import core = require ( './interfaces' ) ; import lang = require ( './lang' ) ; import Scheduler = require ( './Scheduler' ) ; interface ICallbackObject { removed ? : boolean ; callback : core . IObserver < any > ; } interface INotification { newValue : any ; oldValue : any ; callbacks : Array < ICallbackObject > ; } class Observable implements core . IObservable { private _callbacks : { [ property ] : ICallbackObject [ ] ; } ; private _notifications : { [ property ] : INotification ; } ; private _timer : core . IHandle ; constructor ( props ? ) { if ( props ) { lang . mixin ( this , props ) ; } Object . defineProperties ( this , { _callbacks : { value : { } } , _dispatch : { configurable : true , value : this . _dispatch . bind ( this ) , writable : true } , _notifications : { value : Object . create ( null ) , writable : true } , _timer : { value : null , writable : true } } ) ; } private _dispatch ( ) { if ( this . _timer ) { this . _timer . remove ( ) ; this . _timer = null ; } var notifications = this . _notifications ; this . _notifications = Object . create ( null ) ; for ( var property in notifications ) { var notification = notifications [ property ] ; if ( this . _isEqual ( notification . oldValue , notification . newValue ) ) { continue ; } var callback ; for ( var i = 0 ; ( callback = notification . callbacks [ i ] ) ; i ++ ) { if ( ! callback . removed ) { callback . callback . call ( this , notification . newValue , notification . oldValue ) ; } } } } _isEqual ( a , b ) { return lang . isEqual ( a , b ) ; } private _notify < T > ( property , newValue , oldValue ) { var callbacks = this . _callbacks [ property ] ; if ( ! callbacks || ! callbacks . length ) { return ; } var notification = this . _notifications [ property ] ; if ( notification ) { notification . newValue = newValue ; } else { this . _notifications [ property ] = { newValue : newValue , oldValue : oldValue , callbacks : callbacks . slice ( 0 ) } ; } this . _schedule ( ) ; } observe < T > ( property , callback ) { var callbackObject = { callback : callback } ; if ( ! this . _callbacks [ property ] ) { var oldDescriptor = lang . getPropertyDescriptor ( this , property ) , currentValue = ( < any > this ) [ property ] , descriptor = { configurable : true , enumerable : true } ; if ( oldDescriptor && ! ( 'value' in oldDescriptor ) ) { descriptor . get = oldDescriptor . get ; if ( oldDescriptor . set ) { descriptor . set = function ( value ) { oldDescriptor . set . apply ( this , arguments ) ; var newValue = descriptor . get . call ( this ) ; this . _notify ( property , newValue , currentValue ) ; currentValue = newValue ; } ; } } else { descriptor . get = ( ) => { return currentValue ; } ; if ( oldDescriptor . writable ) { descriptor . set = function ( newValue ) { this . _notify ( property , newValue , currentValue ) ; currentValue = newValue ; } ; } } Object . defineProperty ( this , property , descriptor ) ; this . _callbacks [ property ] = [ callbackObject ] ; } else { this . _callbacks [ property ] . push ( callbackObject ) ; } var self = this ; return { remove : function ( ) { this . remove = ( ) => { } ; callbackObject . removed = true ; lang . pullFromArray ( self . _callbacks [ property ] , callbackObject ) ; } } ; } _schedule ( ) { if ( ! this . _timer ) { this . _timer = Scheduler . schedule ( this . _dispatch ) ; } } } export = Observable ;