namespace drunk { import util = drunk . util ; import config = drunk . config ; import Parser = drunk . Parser ; import observable = drunk . observable ; export class Watcher { param expression param isDeepWatch static getNameOfKey ( expression , isDeepWatch ? ) { return ! ! isDeepWatch ? expression + '<deep>' : expression ; } private _isInterpolate : boolean ; private _actions : IBindingAction [ ] = [ ] ; private _observers : { [ id ] : observable . Observer ; } = { } ; private _properties : { [ number ] : { [ property ] : boolean ; } ; } = { } ; private _tmpObservers : { [ id ] : observable . Observer ; } ; private _tmpProperties : { [ number ] : { [ property ] : boolean ; } ; } ; private _isActived : boolean = true ; private _throttle : number ; private _getter : Parser . IGetter ; /**
         * 表达式求值的结果
         */ value : any ; param viewModel param expression param isDeepWatch constructor ( public viewModel , public expression , public isDeepWatch ? ) { this . _isInterpolate = Parser . hasInterpolation ( expression ) ; this . _getter = this . _isInterpolate ? Parser . parseInterpolate ( expression ) : Parser . parseGetter ( expression ) ; if ( ! this . _getter . dynamic ) { throw new Error ( `不能watch不包含任何变量的表达式: "${ expression }"` ) ; } this . _propertyChanged = this . _propertyChanged . bind ( this ) ; this . value = this . _getValue ( ) ; } param action addAction ( action ) { if ( ! this . _isActived ) { return ; } util . addArrayItem ( this . _actions , action ) ; } param action removeAction ( action ) { if ( ! this . _isActived ) { return ; } util . removeArrayItem ( this . _actions , action ) ; if ( ! this . _actions . length ) { this . dispose ( ) ; } } /**
         * 销毁实例和移除所有应用
         */ dispose ( ) { if ( ! this . _isActived ) { return ; } Object . keys ( this . _observers ) . forEach ( ( id ) => { Object . keys ( this . _properties [ id ] ) . forEach ( property => { this . _observers [ id ] . $removeListener ( property , this . _propertyChanged ) ; } ) ; } ) ; if ( this . _throttle ) { util . cancelAnimationFrame ( this . _throttle ) ; } let key = Watcher . getNameOfKey ( this . expression , this . isDeepWatch ) ; this . viewModel . _watchers [ key ] = this . _propertyChanged = this . value = this . viewModel = this . expression = this . _getter = null ; this . _actions = this . _observers = this . _properties = this . _tmpProperties = this . _tmpObservers = null ; this . _isActived = false ; } /**
         * 数据更新派发，会先做缓冲，防止在同一时刻对此出发更新操作，等下一次系统轮训时再真正执行更新操作
         */ private _propertyChanged ( ) { if ( ! config . renderOptimization ) { if ( this . _throttle ) { util . cancelAnimationFrame ( this . _throttle ) ; } this . _flush ( ) ; } else if ( ! this . _throttle ) { this . _throttle = util . requestAnimationFrame ( ( ) => this . _flush ( ) ) ; } } /**
         * 立即获取最新的数据判断并判断是否已经更新，如果已经更新，执行所有的回调
         */ private _flush ( ) { if ( ! this . _isActived ) { return ; } this . _throttle = null ; let oldValue = this . value ; let newValue = this . _getValue ( ) ; if ( ( typeof newValue === 'object' && newValue != null ) || newValue !== oldValue ) { this . value = newValue ; this . _actions . slice ( ) . forEach ( action => { if ( this . _isActived ) { action ( newValue , oldValue ) ; } } ) ; } } /**
         * 执行表达式函数获取最新的数据
         */ private _getValue ( ) { this . _beforeAccess ( ) ; let newValue = this . _getter . call ( this . viewModel ) ; if ( this . isDeepWatch ) { visit ( newValue ) ; } if ( this . _getter . filters ) { newValue = Filter . pipeFor ( newValue , this . _getter . filters , this . viewModel . $filter , this . _isInterpolate , this . viewModel ) ; } this . _accessed ( ) ; return newValue ; } /**
         * 设置observable的属性访问回调为当前watcher实例的订阅方法,当访问某个属性是就会对该属性进行订阅
         */ private _beforeAccess ( ) { this . _tmpObservers = { } ; this . _tmpProperties = { } ; observable . onPropertyAccessing = this . _subscribePropertyChanged . bind ( this ) ; } /**
         * 表达式求解完后的收尾工作,取消注册onPropertyAccessed回调,并对比旧的observer表和新的表看有哪些实例已经不需要订阅
         */ private _accessed ( ) { observable . onPropertyAccessing = null ; let { _observers , _properties , _tmpObservers , _tmpProperties , _propertyChanged } = this ; Object . keys ( _observers ) . forEach ( id => { let observer = _observers [ id ] ; if ( ! _tmpObservers [ id ] ) { Object . keys ( _properties [ id ] ) . forEach ( property => { observer . $removeListener ( property , _propertyChanged ) ; } ) ; } else { Object . keys ( _properties [ id ] ) . forEach ( property => { if ( ! _tmpProperties [ id ] [ property ] ) { observer . $removeListener ( property , _propertyChanged ) ; } } ) ; } } ) ; this . _observers = _tmpObservers ; this . _properties = _tmpProperties ; } param observer param property private _subscribePropertyChanged ( observer , property ) { let { _observers , _properties , _tmpObservers , _tmpProperties , _propertyChanged } = this ; let id = util . uniqueId ( observer ) ; if ( ! _tmpObservers [ id ] ) { _tmpObservers [ id ] = observer ; _tmpProperties [ id ] = { [ property ] : true } ; if ( ! _observers [ id ] ) { _observers [ id ] = observer ; _properties [ id ] = { [ property ] : true } ; observer . $addListener ( property , _propertyChanged ) ; } else if ( ! _properties [ id ] [ property ] ) { _properties [ id ] [ property ] = true ; observer . $addListener ( property , _propertyChanged ) ; } } else if ( ! _tmpProperties [ id ] [ property ] ) { _tmpProperties [ id ] [ property ] = true ; if ( ! _properties [ id ] [ property ] ) { observer . $addListener ( property , _propertyChanged ) ; _properties [ id ] [ property ] = true ; } } } } function visit ( target ) { if ( util . isPlainObjectOrObservableObject ( target ) ) { Object . keys ( target ) . forEach ( key => { visit ( target [ key ] ) ; } ) ; } else if ( Array . isArray ( target ) ) { target . forEach ( item => { visit ( item ) ; } ) ; } } }