<commit_before>// Copyright 2022, University of Colorado Boulder

/**
 * Type to implement disposal strategies, with a disposer option and a disposeEmitter.
 *
 * @author <NAME> (PhET Interactive Simulations)
 * @author <NAME> (PhET Interactive Simulations)
 */

import axon from './axon.js';
import TEmitter from './TEmitter.js';
import TinyEmitter from './TinyEmitter.js';

type TDisposable = {
  disposeEmitter: TEmitter;
};

export type Disposer = TEmitter | TDisposable;

export type DisposableOptions = {

  // When set, this is the object that is responsible for triggering disposal for this object. This Disposable will listen
  // for when the disposer calls dispose (via the disposeEmitter), and then will dispose itself.
  disposer?: Disposer | null;
};

class Disposable implements TDisposable {

  public readonly disposeEmitter = new TinyEmitter();
  public isDisposed = false;
  private _disposer: Disposer | null = null;
  private boundOnDisposer: ( () => void ) | null = null;

  public constructor( providedOptions?: DisposableOptions ) {

    if ( providedOptions && providedOptions.disposer ) {
      this.disposer = providedOptions.disposer;
    }

    if ( assert ) {

      // Wrap the prototype dispose method with a check. NOTE: We will not catch devious cases where the dispose() is
      // overridden after the Node constructor (which may happen).
      const protoDispose = this.dispose;
      this.dispose = () => {
        assert && assert( !this.isDisposed, 'This PhetioObject has already been disposed, and cannot be disposed again' );
        protoDispose.call( this );
        assert && assert( this.isDisposed, 'PhetioObject.dispose() call is missing from an overridden dispose method' );
      };
    }
  }

  private onDisposer(): void {
    this.dispose();

    // @ts-ignore instanceof check isn't flexible enough to check for all that implement TDisposable
    const previousDisposeEmitter: TEmitter = this._disposer instanceof Disposable ? this._disposer.disposeEmitter : this._disposer;

    assert && assert( this.boundOnDisposer, 'Must have set boundOnDisposer before calling onDisposer' );
    previousDisposeEmitter.removeListener( this.boundOnDisposer! );
  }

  public getDisposer(): Disposer | null {
    return this._disposer;
  }

  public get disposer(): Disposer | null { return this.getDisposer(); }

  public set disposer( disposer: Disposer | null ) { this.setDisposer( disposer ); }

  public setDisposer( disposer: Disposer | null ): void {

    if ( disposer !== this._disposer ) {

      // nullable
      this.boundOnDisposer = this.boundOnDisposer || this.onDisposer.bind( this );

      if ( this._disposer ) {

        // @ts-ignore instanceof check isn't flexible enough to check for all that implement TDisposable
        const previousDisposeEmitter: TEmitter = this._disposer instanceof Disposable ? this._disposer.disposeEmitter : this._disposer;

        assert && assert( this.boundOnDisposer );
        previousDisposeEmitter.removeListener( this.boundOnDisposer );
      }

      this._disposer = disposer;
      if ( this._disposer ) {

        // @ts-ignore instanceof check isn't flexible enough to check for all that implement TDisposable
        const otherDisposeEmitter: TEmitter = this._disposer instanceof Disposable ? this._disposer.disposeEmitter : this._disposer;
        otherDisposeEmitter.addListener( this.boundOnDisposer );
      }
    }
  }

  public dispose(): void {
    assert && assert( !this.isDisposed, 'Disposable can only be disposed once' );
    this.disposeEmitter.emit();
    this.isDisposed = true;
  }
}

axon.register( 'Disposable', Disposable );
export default Disposable;
<commit_msg>don't expose TinyEmitter implementation, https://github.com/phetsims/scenery/issues/1494<commit_after>// Copyright 2022, University of Colorado Boulder

/**
 * Type to implement disposal strategies, with a disposer option and a disposeEmitter.
 *
 * @author <NAME> (PhET Interactive Simulations)
 * @author <NAME> (PhET Interactive Simulations)
 */

import axon from './axon.js';
import TEmitter from './TEmitter.js';
import TinyEmitter from './TinyEmitter.js';

type TDisposable = {
  disposeEmitter: TEmitter;
};

export type Disposer = TEmitter | TDisposable;

export type DisposableOptions = {

  // When set, this is the object that is responsible for triggering disposal for this object. This Disposable will listen
  // for when the disposer calls dispose (via the disposeEmitter), and then will dispose itself.
  disposer?: Disposer | null;
};

class Disposable implements TDisposable {

  public readonly disposeEmitter: TEmitter = new TinyEmitter();
  public isDisposed = false;
  private _disposer: Disposer | null = null;
  private boundOnDisposer: ( () => void ) | null = null;

  public constructor( providedOptions?: DisposableOptions ) {

    if ( providedOptions && providedOptions.disposer ) {
      this.disposer = providedOptions.disposer;
    }

    if ( assert ) {

      // Wrap the prototype dispose method with a check. NOTE: We will not catch devious cases where the dispose() is
      // overridden after the Node constructor (which may happen).
      const protoDispose = this.dispose;
      this.dispose = () => {
        assert && assert( !this.isDisposed, 'This PhetioObject has already been disposed, and cannot be disposed again' );
        protoDispose.call( this );
        assert && assert( this.isDisposed, 'PhetioObject.dispose() call is missing from an overridden dispose method' );
      };
    }
  }

  private onDisposer(): void {
    this.dispose();

    // @ts-ignore instanceof check isn't flexible enough to check for all that implement TDisposable
    const previousDisposeEmitter: TEmitter = this._disposer instanceof Disposable ? this._disposer.disposeEmitter : this._disposer;

    assert && assert( this.boundOnDisposer, 'Must have set boundOnDisposer before calling onDisposer' );
    previousDisposeEmitter.removeListener( this.boundOnDisposer! );
  }

  public getDisposer(): Disposer | null {
    return this._disposer;
  }

  public get disposer(): Disposer | null { return this.getDisposer(); }

  public set disposer( disposer: Disposer | null ) { this.setDisposer( disposer ); }

  public setDisposer( disposer: Disposer | null ): void {

    if ( disposer !== this._disposer ) {

      // nullable
      this.boundOnDisposer = this.boundOnDisposer || this.onDisposer.bind( this );

      if ( this._disposer ) {

        // @ts-ignore instanceof check isn't flexible enough to check for all that implement TDisposable
        const previousDisposeEmitter: TEmitter = this._disposer instanceof Disposable ? this._disposer.disposeEmitter : this._disposer;

        assert && assert( this.boundOnDisposer );
        previousDisposeEmitter.removeListener( this.boundOnDisposer );
      }

      this._disposer = disposer;
      if ( this._disposer ) {

        // @ts-ignore instanceof check isn't flexible enough to check for all that implement TDisposable
        const otherDisposeEmitter: TEmitter = this._disposer instanceof Disposable ? this._disposer.disposeEmitter : this._disposer;
        otherDisposeEmitter.addListener( this.boundOnDisposer );
      }
    }
  }

  public dispose(): void {
    assert && assert( !this.isDisposed, 'Disposable can only be disposed once' );
    this.disposeEmitter.emit();
    this.isDisposed = true;
  }
}

axon.register( 'Disposable', Disposable );
export default Disposable;

{'fraction_non_alphanumeric': 0.09412237247288793, 'fraction_numerical': 0.001606640781898514, 'mean_word_length': 3.530018192844148, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3692942', 'n_tokens_mistral': 2145, 'n_tokens_neox': 2034, 'n_words': 665}