export type Listener < Data , Key extends keyof Data > = ( data : Data [ Key ] , key : Key ) => void ; export type AnyListener < Data > = ( data : Partial < Data > , key : string ) => void ; export type Unsubscribe = ( ) => void ; export interface PropertyChange < Source , Value > { source : Source ; previous : Value ; } export interface AnyEvent < Data > { key : string ; data : Partial < Data > ; } export interface Events < Data > { on < Key extends keyof Data > ( eventKey , listener ) : void ; off < Key extends keyof Data > ( eventKey , listener ) : void ; onAny ( listener ) : void ; offAny ( listener ) : void ; } export class EventSource < Data > implements Events < Data > { private listeners = new Map < keyof Data , Array < Listener < Data , any > > > ( ) ; private anyListeners : Array < AnyListener < Data > > | undefined ; on < Key extends keyof Data > ( eventKey , listener ) { let listeners = this . listeners . get ( eventKey ) ; if ( ! listeners ) { listeners = [ ] ; this . listeners . set ( eventKey , listeners ) ; } listeners . push ( listener ) ; } onAny ( listener ) { let listeners = this . anyListeners ; if ( ! listeners ) { listeners = [ ] ; this . anyListeners = listeners ; } listeners . push ( listener ) ; } off < Key extends keyof Data > ( eventKey , listener ) { const listeners = this . listeners . get ( eventKey ) ; if ( ! listeners ) { return ; } const index = listeners . indexOf ( listener ) ; if ( index >= 0 ) { listeners . splice ( index , 1 ) ; } } offAny ( listener ) { const listeners = this . anyListeners ; if ( ! listeners ) { return ; } const index = listeners . indexOf ( listener ) ; if ( index >= 0 ) { listeners . splice ( index , 1 ) ; } } trigger < Key extends keyof Data > ( eventKey , data ) { const listeners = this . listeners . get ( eventKey ) ; if ( listeners ) { for ( const listener of listeners ) { listener ( data , eventKey ) ; } } if ( this . anyListeners ) { for ( const anyListener of this . anyListeners ) { anyListener ( { [ eventKey ] : data } as any , eventKey as string ) ; } } } } export class EventObserver { private unsubscribeByKey = new Map < string , Unsubscribe [ ] > ( ) ; private onDispose : Array < Unsubscribe > = [ ] ; listen < Data , Key extends keyof Data > ( events , eventKey , listener ) { events . on ( eventKey , listener ) ; this . onDispose . push ( ( ) => events . off ( eventKey , listener ) ) ; } listenAny < Data > ( events , listener ) { events . onAny ( listener ) ; this . onDispose . push ( ( ) => events . offAny ( listener ) ) ; } listenOnce < Data , Key extends keyof Data > ( events , eventKey , listener ) { let handled = false ; const onceListener = ( data , key ) => { handled = true ; events . off ( eventKey , onceListener ) ; listener ( data , key ) ; } ; events . on ( eventKey , onceListener ) ; this . onDispose . push ( ( ) => { if ( handled ) { return ; } events . off ( eventKey , onceListener ) ; } ) ; } stopListening ( ) { for ( const unsubscribe of this . onDispose ) { unsubscribe ( ) ; } this . onDispose . length = 0 ; this . unsubscribeByKey . forEach ( ( unsubscribers ) => { for ( const unsubscribe of unsubscribers ) { unsubscribe ( ) ; } } ) ; this . unsubscribeByKey . clear ( ) ; } }