license import { coerceBooleanProperty , coerceNumberProperty , coerceElement , BooleanInput , NumberInput , } from '@angular/cdk/coercion' ; import { AfterContentInit , Directive , ElementRef , EventEmitter , Injectable , Input , NgModule , NgZone , OnDestroy , Output , } from '@angular/core' ; import { Observable , Subject , Subscription , Observer } from 'rxjs' ; import { debounceTime } from 'rxjs/operators' ; docs-private @ Injectable ( { providedIn : 'root' } ) export class MutationObserverFactory { create ( callback ) { return typeof MutationObserver === 'undefined' ? null : new MutationObserver ( callback ) ; } } /** An injectable service that allows watching elements for changes to their content. */ @ Injectable ( { providedIn : 'root' } ) export class ContentObserver implements OnDestroy { /** Keeps track of the existing MutationObservers so they can be reused. */ private _observedElements = new Map < Element , { observer : MutationObserver | null ; readonly stream : Subject < MutationRecord [ ] > ; count : number ; } > ( ) ; constructor ( private _mutationObserverFactory ) { } ngOnDestroy ( ) { this . _observedElements . forEach ( ( _ , element ) => this . _cleanupObserver ( element ) ) ; } param element observe ( element ) ; param element observe ( element ) ; observe ( elementOrRef ) { const element = coerceElement ( elementOrRef ) ; return new Observable ( ( observer ) => { const stream = this . _observeElement ( element ) ; const subscription = stream . subscribe ( observer ) ; return ( ) => { subscription . unsubscribe ( ) ; this . _unobserveElement ( element ) ; } ; } ) ; } /**
     * Observes the given element by using the existing MutationObserver if available, or creating a
     * new one if not.
     */ private _observeElement ( element ) { if ( ! this . _observedElements . has ( element ) ) { const stream = new Subject < MutationRecord [ ] > ( ) ; const observer = this . _mutationObserverFactory . create ( mutations => stream . next ( mutations ) ) ; if ( observer ) { observer . observe ( element , { characterData : true , childList : true , subtree : true , } ) ; } this . _observedElements . set ( element , { observer , stream , count : 1 } ) ; } else { this . _observedElements . get ( element ) ! . count ++ ; } return this . _observedElements . get ( element ) ! . stream ; } /**
     * Un-observes the given element and cleans up the underlying MutationObserver if nobody else is
     * observing this element.
     */ private _unobserveElement ( element ) { if ( this . _observedElements . has ( element ) ) { this . _observedElements . get ( element ) ! . count -- ; if ( ! this . _observedElements . get ( element ) ! . count ) { this . _cleanupObserver ( element ) ; } } } /** Clean up the underlying MutationObserver for the specified element. */ private _cleanupObserver ( element ) { if ( this . _observedElements . has ( element ) ) { const { observer , stream } = this . _observedElements . get ( element ) ! ; if ( observer ) { observer . disconnect ( ) ; } stream . complete ( ) ; this . _observedElements . delete ( element ) ; } } } /**
 * Directive that triggers a callback whenever the content of
 * its associated element has changed.
 */ @ Directive ( { selector : '[cdkObserveContent]' , exportAs : 'cdkObserveContent' , } ) export class CdkObserveContent implements AfterContentInit , OnDestroy { /** Event emitted for each change in the element's content. */ @ Output ( 'cdkObserveContent' ) readonly event = new EventEmitter < MutationRecord [ ] > ( ) ; /**
     * Whether observing content is disabled. This option can be used
     * to disconnect the underlying MutationObserver until it is needed.
     */ @ Input ( 'cdkObserveContentDisabled' ) get disabled ( ) : boolean { return this . _disabled ; } set disabled ( value ) { this . _disabled = coerceBooleanProperty ( value ) ; this . _disabled ? this . _unsubscribe ( ) : this . _subscribe ( ) ; } private _disabled = false ; /** Debounce interval for emitting the changes. */ @ Input ( ) get debounce ( ) : number { return this . _debounce ; } set debounce ( value ) { this . _debounce = coerceNumberProperty ( value ) ; this . _subscribe ( ) ; } private _debounce : number ; private _currentSubscription : Subscription | null = null ; constructor ( private _contentObserver , private _elementRef , private _ngZone ) { } ngAfterContentInit ( ) { if ( ! this . _currentSubscription && ! this . disabled ) { this . _subscribe ( ) ; } } ngOnDestroy ( ) { this . _unsubscribe ( ) ; } private _subscribe ( ) { this . _unsubscribe ( ) ; const stream = this . _contentObserver . observe ( this . _elementRef ) ; this . _ngZone . runOutsideAngular ( ( ) => { this . _currentSubscription = ( this . debounce ? stream . pipe ( debounceTime ( this . debounce ) ) : stream ) . subscribe ( this . event ) ; } ) ; } private _unsubscribe ( ) { this . _currentSubscription ?. unsubscribe ( ) ; } } @ NgModule ( { exports : [ CdkObserveContent ] , declarations : [ CdkObserveContent ] , providers : [ MutationObserverFactory ] , } ) export class ObserversModule { }