import { Observable , EventData } from '../../../data/observable' ; const handlersForEventName = new Map < string , ( eventData : EventData ) => void > ( ) ; const sourcesMap = new WeakMap < Partial < Observable > , Map < string , Array < TargetHandlerPair > > > ( ) ; class TargetHandlerPair { tagetRef : WeakRef < Object > ; handler : ( eventData : EventData ) => void ; constructor ( target , handler ) { this . tagetRef = new WeakRef ( target ) ; this . handler = handler ; } } function getHandlerForEventName ( eventName ) { let handler = handlersForEventName . get ( eventName ) ; if ( ! handler ) { handler = function ( eventData ) { const source = eventData . object ; const sourceEventMap = sourcesMap . get ( source ) ; if ( ! sourceEventMap ) { source . removeEventListener ( eventName , handlersForEventName . get ( eventName ) ) ; return ; } const targetHandlerPairList = sourceEventMap . get ( eventName ) ; if ( ! targetHandlerPairList ) { return ; } const deadPairsIndexes = [ ] ; let pair ; let target ; for ( let i = 0 ; i < targetHandlerPairList . length ; i ++ ) { pair = targetHandlerPairList [ i ] ; target = pair . tagetRef . get ( ) ; if ( target ) { pair . handler . call ( target , eventData ) ; } else { deadPairsIndexes . push ( i ) ; } } if ( deadPairsIndexes . length === targetHandlerPairList . length ) { source . removeEventListener ( eventName , handlersForEventName . get ( eventName ) ) ; sourceEventMap . delete ( eventName ) ; } else { for ( let j = deadPairsIndexes . length - 1 ; j >= 0 ; j -- ) { targetHandlerPairList . splice ( deadPairsIndexes [ j ] , 1 ) ; } } } ; handlersForEventName . set ( eventName , handler ) ; } return handler ; } function validateArgs ( source , eventName , handler , target ) { if ( ! source ) { throw new Error ( 'source is null or undefined' ) ; } if ( ! target ) { throw new Error ( 'target is null or undefined' ) ; } if ( typeof eventName !== 'string' ) { throw new Error ( 'eventName is not a string' ) ; } if ( typeof handler !== 'function' ) { throw new Error ( 'handler is not a function' ) ; } } export function addWeakEventListener ( source , eventName , handler , target ) { validateArgs ( source , eventName , handler , target ) ; let shouldAttach = false ; let sourceEventMap = sourcesMap . get ( source ) ; if ( ! sourceEventMap ) { sourceEventMap = new Map < string , Array < TargetHandlerPair > > ( ) ; sourcesMap . set ( source , sourceEventMap ) ; shouldAttach = true ; } let pairList = sourceEventMap . get ( eventName ) ; if ( ! pairList ) { pairList = new Array < TargetHandlerPair > ( ) ; sourceEventMap . set ( eventName , pairList ) ; shouldAttach = true ; } pairList . push ( new TargetHandlerPair ( target , handler ) ) ; if ( shouldAttach ) { source . addEventListener ( eventName , getHandlerForEventName ( eventName ) ) ; } } export function removeWeakEventListener ( source , eventName , handler , target ) { validateArgs ( source , eventName , handler , target ) ; const handlerForEventWithName = handlersForEventName . get ( eventName ) ; if ( ! handlerForEventWithName ) { return ; } const sourceEventMap = sourcesMap . get ( source ) ; if ( ! sourceEventMap ) { return ; } const targetHandlerPairList = sourceEventMap . get ( eventName ) ; if ( ! targetHandlerPairList ) { return ; } const targetHandlerPairsToRemove = [ ] ; let pair ; let registeredTarget ; for ( let i = 0 ; i < targetHandlerPairList . length ; i ++ ) { pair = targetHandlerPairList [ i ] ; registeredTarget = pair . tagetRef . get ( ) ; if ( ! registeredTarget || ( registeredTarget === target && handler === pair . handler ) ) { targetHandlerPairsToRemove . push ( i ) ; } } if ( targetHandlerPairsToRemove . length === targetHandlerPairList . length ) { source . removeEventListener ( eventName , handlerForEventWithName ) ; sourceEventMap . delete ( eventName ) ; } else { for ( let j = targetHandlerPairsToRemove . length - 1 ; j >= 0 ; j -- ) { targetHandlerPairList . splice ( targetHandlerPairsToRemove [ j ] , 1 ) ; } } }