<commit_before>export { Base };

import * as ValueDesc from "../Unpacker/ValueDesc.js";
import * as Weights from "../Unpacker/Weights.js";
import * as ReturnOrClone_Activation from "./ReturnOrClone_Activation.js";

/**
 * Handle depthwise convolution, bias and activation.
 *
 * @member {number} byteOffsetBegin
 *   The position which is started (inclusive) to extract from inputFloat32Array.buffer by init().
 *
 * @member {number} byteOffsetEnd
 *   The position which is ended to (non-inclusive) extract from inputFloat32Array.buffer by init(). Where to extract next weights.
 * Only meaningful when ( this.bInitOk == true ).
 *
 * @member {boolean} bExisted
 *   If true, this pointwise convolution exist.
 *
 * @member {boolean} bInitOk
 *   If true, the init() is successful.
 *
 * @member {function} pfnOperation
 *   This is a method. It has one parameter inputTensor and return a outputTensor. The inputTensor (tf.tensor3d) represents the image
 * ( height x width x channel ) which will be processed. The outputTensor (tf.tensor3d) represents the result.
 * All intermediate tensors will be disposed. The inputTensor may or may not be disposed. In fact, this method calls one of
 * Base.return_input_directly(), Base.keep_input_return_copy(), Avg_and_destroy(), Avg_and_keep(), Max_and_destroy(), Max_and_keep(),
 * Conv_and_destroy(), Conv_and_keep() according to the parameters.
 *
 * @member {function} pfnOperationBiasActivation
 *   This is a method. It has one parameter inputTensor and return a outputTensor. The inputTensor (tf.tensor3d) represents the image
 * ( height x width x channel ) which will be processed. The outputTensor (tf.tensor3d) represents the result.
 * All intermediate tensors will be disposed. The inputTensors may or may not be disposed. In fact, this method calls one of
 * Base.return_input_directly(), Base.keep_input_return_copy(), Operation_and_destroy_or_keep(), OperationBias_and_destroy_or_keep(),
 * OperationActivation_and_destroy_or_keep(), OperationBiasActivation_and_destroy_or_keep() according to the parameters.
 */
class Base extends ReturnOrClone_Activation.Base {

  constructor(
    inputChannelCount,
    AvgMax_Or_ChannelMultiplier, filterHeight, stridesPad, bBias, nActivationId,
    inputFloat32Array, byteOffsetBegin ) {

    this.inputChannelCount = inputChannelCount;
    this.AvgMax_Or_ChannelMultiplier = AvgMax_Or_ChannelMultiplier;
    this.filterHeight = filterHeight;
    this.stridesPad = stridesPad;
    this.bBias = bBias;
    this.nActivationId = nActivationId;
    this.inputFloat32Array = inputFloat32Array;
    this.byteOffsetBegin = byteOffsetBegin;
  }

  init() {
    this.disposeTensors();

    this.bDepthwise = this.bDepthwiseAvg = this.bDepthwiseMax = this.bDepthwiseConv = false; // Assume no depthwise.
    this.outputChannelCount = this.inputChannelCount; // Assume no channel multiplier.

    this.filterWidth = this.filterHeight;  // Assume depthwise filter's width equals its height.

    if ( this.AvgMax_Or_ChannelMultiplier < 0 ) { // Depthwise by AVG or MAX pooling (so no channel multiplier).

      // if 1x1 AVG pooling, or 1x1 MAX pooling, or illegal pooling type (i.e. not AVG, not MAX):
      //   - As no depthwise operation (i.e. ( this.bDepthwise == true ) )
      //   - Just return input (i.e. ( this.pfn_depthwiseOperation == Base.return_input_directly ) )

      if ( ( 1 == this.filterHeight ) && ( 1 == this.filterWidth ) ) {
        // Do nothing, because the result of 1x1 AVG or MAX pooling is just the same as input.
      } else {
        switch ( this.AvgMax_Or_ChannelMultiplier ) {
          case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG:
            this.bDepthwise = this.bDepthwiseAvg = true;
            this.pfnOperation = Base.Avg_and_destroy;
            break;

          case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX:
            this.bDepthwise = this.bDepthwiseMax = true;
            this.pfnOperation = Base.Max_and_destroy;
            break;
        }
      }

    } else {
      if ( this.AvgMax_Or_ChannelMultiplier >= 1 ) { // Depthwise by convolution (with channel multiplier).
        this.bDepthwise = this.bDepthwiseConv = true;

        this.outputChannelCount = this.inputChannelCount * this.AvgMax_Or_ChannelMultiplier;

        this.filtersShape = [ this.filterHeight, this.filterWidth, this.inputChannelCount, this.AvgMax_Or_ChannelMultiplier ];

        this.filtersWeights = new Weights.Base( this.inputFloat32Array, this.byteOffsetEnd, this.filtersShape );
        if ( !this.filtersWeights.extract() )
          return false;  // e.g. input array does not have enough data.

        this.byteOffsetEnd = this.filtersWeights.defaultByteOffsetEnd;

        this.filtersTensor4d = tf.tensor4d( this.filtersWeights.weights, this.filtersShape );
        this.pfnOperation = Base.Conv_and_destroy; // will dispose inputTensor.

      } else { // No depthwise (e.g. zero or negative number) (so no channel multiplier).
      }
    }

    switch ( this.stridesPad ) {
      case 0:  this.strides = 1; this.pad = "valid"; break;
      default:
      case 1:  this.strides = 1; this.pad = "same";  break;
      case 2:  this.strides = 2; this.pad = "same";  break;
    }

    this.pfnActivation = Base.getActivationFunction( this.nActivationId );

    this.filterHeightWidth = [ this.filterHeight, this.filterWidth ];
    this.biasesShape =       [ 1, 1, this.outputChannelCount ];

    if ( this.bDepthwise ) {

      if ( this.bBias ) {
        this.biasesWeights = new Weights.Base( this.inputFloat32Array, this.byteOffsetEnd, this.biasesShape );
        if ( !this.biasesWeights.extract() )
          return false;  // e.g. input array does not have enough data.
        this.byteOffsetEnd = this.biasesWeights.defaultByteOffsetEnd;

        this.biasesTensor3d = tf.tensor3d( this.biasesWeights.weights, this.biasesShape );

        if ( this.pfnActivation )
          this.pfnOperationBiasActivation = Base.OperationBiasActivation_and_destroy_or_keep;
        else
          this.pfnOperationBiasActivation = Base.OperationBias_and_destroy_or_keep;

      } else {

        if ( this.pfnActivation )
          this.pfnOperationBiasActivation = Base.OperationActivation_and_destroy_or_keep;
         else
          this.pfnOperationBiasActivation = Base.Operation_and_destroy_or_keep;

      }

    } else {
      // Since there is no operation at all, let pfnOperationBiasActivation ignore pfnOperation completely.
      this.pfnOperationBiasActivation = this.pfnOperation = Base.return_input_directly;
    }

    this.bInitOk = true;
  }

  disposeTensors() {
    if ( this.filtersTensor4d ) {
      this.filtersTensor4d.dispose();
      this.filtersTensor4d = null;
    }

    if ( this.biasesTensor3d ) {
      this.biasesTensor3d.dispose();
      this.biasesTensor3d = null;
    }

    this.filtersWeights = this.biasesWeights = this.pfnOperationBiasActivation = this.pfnOperation = this.pfnActivation = null;
    this.byteOffsetEnd = -1;
    this.bKeepInputTensor = false;  // Default will dispose input tensor.
    this.bInitOk = false;
  }

  /**
   * Adjust this.pfnOperation (and this.pfnOperationBiasActivation if need) so that this.pfnOperation() and this.pfnOperationBiasActivation()
   * will or will not dispose its inputTensor.
   */
  setKeepInputTensor( bKeepInputTensor ) {
    this.bKeepInputTensor = bKeepInputTensor;

    if ( bKeepInputTensor ) {

      switch ( this.AvgMax_Or_ChannelMultiplier ) {
        case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG: this.pfnOperation = Base.Avg_and_keep; break;
        case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX: this.pfnOperation = Base.Max_and_keep; break;

        // i.e. ( false == this.bExisted ). Since there is no operation at all, let pfnOperationBiasActivation ignore pfnOperation completely.
        case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.NONE:
          this.pfnOperationBiasActivation = this.pfnOperation = Base.keep_input_return_copy;
          break;

        default: this.pfnOperation = Base.Conv_and_keep; break;
      }

    } else {

      switch ( this.AvgMax_Or_ChannelMultiplier ) {
        case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG: this.pfnOperation = Base.Avg_and_destroy; break;
        case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX: this.pfnOperation = Base.Max_and_destroy; break;

        // i.e. ( false == this.bExisted ). Since there is no operation at all, let pfnOperationBiasActivation ignore pfnOperation completely.
        case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.NONE:
          this.pfnOperationBiasActivation = this.pfnOperation = Base.return_input_directly;
          break;

        default: this.pfnOperation = Base.Conv_and_destroy; break;
      }

    }
  }

  /** Depthwise Average Pooling. */
  static Avg_and_keep( inputTensor ) {
    return tf.pool( inputTensor, this.filterHeightWidth, "avg", this.pad, 1, this.strides ); // dilations = 1
  }

  static Avg_and_destroy( inputTensor ) {
    let t = tf.pool( inputTensor, this.filterHeightWidth, "avg", this.pad, 1, this.strides ); // dilations = 1
    inputTensor.dispose();
    return t;
  }

  /** Depthwise Max Pooling. */
  static Max_and_keep( inputTensor ) {
    return tf.pool( inputTensor, this.filterHeightWidth, "max", this.pad, 1, this.strides ); // dilations = 1
  }

  static Max_and_destroy( inputTensor ) {
    let t = tf.pool( inputTensor, this.filterHeightWidth, "max", this.pad, 1, this.strides ); // dilations = 1
    inputTensor.dispose();
    return t;
  }

  /** Depthwise Convolution. */
  static Conv_and_keep( inputTensor ) {
    return tf.depthwiseConv2d( inputTensor, this.filtersTensor4d, this.strides, this.pad );
  }

  static Conv_and_destroy( inputTensor ) {
    let t = tf.depthwiseConv2d( inputTensor, this.filtersTensor4d, this.strides, this.pad );
    inputTensor.dispose();
    return t;
  }

  /** Depthwise Operation, Bias and Activation. */
  static Operation_and_destroy_or_keep( inputTensor ) {
    return this.pfnOperation( inputTensor ); // may destroy or keep.
  }

  static OperationBias_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = tf.add( t0, this.biasesTensor3d );
    t0.dispose();

    return t1;
  }

  static OperationActivation_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = this.pfnActivation( t0 );
    t0.dispose();

    return t1;
  }

  static OperationBiasActivation_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = tf.add( t0, this.biasesTensor3d );
    t0.dispose();

    t0 = this.pfnActivation( t1 );
    t1.dispose();

    return t0;
  }

}
<commit_msg>Update Depthwise.js<commit_after>export { Base };

import * as ValueDesc from "../Unpacker/ValueDesc.js";
import * as Weights from "../Unpacker/Weights.js";
import * as ReturnOrClone_Activation from "./ReturnOrClone_Activation.js";

/**
 * Handle depthwise convolution, bias and activation.
 *
 * @member {number} byteOffsetBegin
 *   The position which is started (inclusive) to extract from inputFloat32Array.buffer by init().
 *
 * @member {number} byteOffsetEnd
 *   The position which is ended to (non-inclusive) extract from inputFloat32Array.buffer by init(). Where to extract next weights.
 * Only meaningful when ( this.bInitOk == true ).
 *
 * @member {boolean} bExisted
 *   If true, this pointwise convolution exist.
 *
 * @member {boolean} bInitOk
 *   If true, the init() is successful.
 *
 * @member {function} pfnOperation
 *   This is a method. It has one parameter inputTensor and return a outputTensor. The inputTensor (tf.tensor3d) represents the image
 * ( height x width x channel ) which will be processed. The outputTensor (tf.tensor3d) represents the result.
 * All intermediate tensors will be disposed. The inputTensor may or may not be disposed. In fact, this method calls one of
 * Base.return_input_directly(), Base.keep_input_return_copy(), Avg_and_destroy(), Avg_and_keep(), Max_and_destroy(), Max_and_keep(),
 * Conv_and_destroy(), Conv_and_keep() according to the parameters.
 *
 * @member {function} pfnOperationBiasActivation
 *   This is a method. It has one parameter inputTensor and return a outputTensor. The inputTensor (tf.tensor3d) represents the image
 * ( height x width x channel ) which will be processed. The outputTensor (tf.tensor3d) represents the result.
 * All intermediate tensors will be disposed. The inputTensors may or may not be disposed. In fact, this method calls one of
 * Base.return_input_directly(), Base.keep_input_return_copy(), Operation_and_destroy_or_keep(), OperationBias_and_destroy_or_keep(),
 * OperationActivation_and_destroy_or_keep(), OperationBiasActivation_and_destroy_or_keep() according to the parameters.
 */
class Base extends ReturnOrClone_Activation.Base {

  constructor(
    inputChannelCount,
    AvgMax_Or_ChannelMultiplier, filterHeight, stridesPad, bBias, nActivationId,
    inputFloat32Array, byteOffsetBegin ) {

    this.inputChannelCount = inputChannelCount;
    this.AvgMax_Or_ChannelMultiplier = AvgMax_Or_ChannelMultiplier;
    this.filterHeight = filterHeight;
    this.stridesPad = stridesPad;
    this.bBias = bBias;
    this.nActivationId = nActivationId;
    this.inputFloat32Array = inputFloat32Array;
    this.byteOffsetBegin = byteOffsetBegin;
  }

  init() {
    this.disposeTensors();

    this.bDepthwise = this.bDepthwiseAvg = this.bDepthwiseMax = this.bDepthwiseConv = false; // Assume no depthwise.
    this.outputChannelCount = this.inputChannelCount; // Assume no channel multiplier.

    this.filterWidth = this.filterHeight;  // Assume depthwise filter's width equals its height.

    if ( this.AvgMax_Or_ChannelMultiplier < 0 ) { // Depthwise by AVG or MAX pooling (so no channel multiplier).

      // if 1x1 AVG pooling, or 1x1 MAX pooling, or illegal pooling type (i.e. not AVG, not MAX):
      //   - As no depthwise operation (i.e. ( this.bDepthwise == true ) )
      //   - Just return input (i.e. ( this.pfnOperation == Base.return_input_directly ) )

      if ( ( 1 == this.filterHeight ) && ( 1 == this.filterWidth ) ) {
        this.pfnOperation = Base.return_input_directly; // Do nothing, because the result of 1x1 AVG or MAX pooling is just the same as input.
      } else {
        switch ( this.AvgMax_Or_ChannelMultiplier ) {
          case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG:
            this.bDepthwise = this.bDepthwiseAvg = true;
            this.pfnOperation = Base.Avg_and_destroy;
            break;

          case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX:
            this.bDepthwise = this.bDepthwiseMax = true;
            this.pfnOperation = Base.Max_and_destroy;
            break;
        }
      }

    } else {
      if ( this.AvgMax_Or_ChannelMultiplier >= 1 ) { // Depthwise by convolution (with channel multiplier).
        this.bDepthwise = this.bDepthwiseConv = true;

        this.outputChannelCount = this.inputChannelCount * this.AvgMax_Or_ChannelMultiplier;

        this.filtersShape = [ this.filterHeight, this.filterWidth, this.inputChannelCount, this.AvgMax_Or_ChannelMultiplier ];

        this.filtersWeights = new Weights.Base( this.inputFloat32Array, this.byteOffsetEnd, this.filtersShape );
        if ( !this.filtersWeights.extract() )
          return false;  // e.g. input array does not have enough data.

        this.byteOffsetEnd = this.filtersWeights.defaultByteOffsetEnd;

        this.filtersTensor4d = tf.tensor4d( this.filtersWeights.weights, this.filtersShape );
        this.pfnOperation = Base.Conv_and_destroy; // will dispose inputTensor.

      } else { // No depthwise (e.g. zero or negative number) (so no channel multiplier).
      }
    }

    switch ( this.stridesPad ) {
      case 0:  this.strides = 1; this.pad = "valid"; break;
      default:
      case 1:  this.strides = 1; this.pad = "same";  break;
      case 2:  this.strides = 2; this.pad = "same";  break;
    }

    this.pfnActivation = Base.getActivationFunction( this.nActivationId );

    this.filterHeightWidth = [ this.filterHeight, this.filterWidth ];
    this.biasesShape =       [ 1, 1, this.outputChannelCount ];

    if ( this.bDepthwise ) {

      if ( this.bBias ) {
        this.biasesWeights = new Weights.Base( this.inputFloat32Array, this.byteOffsetEnd, this.biasesShape );
        if ( !this.biasesWeights.extract() )
          return false;  // e.g. input array does not have enough data.
        this.byteOffsetEnd = this.biasesWeights.defaultByteOffsetEnd;

        this.biasesTensor3d = tf.tensor3d( this.biasesWeights.weights, this.biasesShape );

        if ( this.pfnActivation )
          this.pfnOperationBiasActivation = Base.OperationBiasActivation_and_destroy_or_keep;
        else
          this.pfnOperationBiasActivation = Base.OperationBias_and_destroy_or_keep;

      } else {

        if ( this.pfnActivation )
          this.pfnOperationBiasActivation = Base.OperationActivation_and_destroy_or_keep;
         else
          this.pfnOperationBiasActivation = Base.Operation_and_destroy_or_keep;

      }

    } else {
      // Since there is no operation at all, let pfnOperationBiasActivation ignore pfnOperation completely.
      this.pfnOperationBiasActivation = this.pfnOperation = Base.return_input_directly;
    }

    this.bInitOk = true;
  }

  disposeTensors() {
    if ( this.filtersTensor4d ) {
      this.filtersTensor4d.dispose();
      this.filtersTensor4d = null;
    }

    if ( this.biasesTensor3d ) {
      this.biasesTensor3d.dispose();
      this.biasesTensor3d = null;
    }

    this.filtersWeights = this.biasesWeights = this.pfnOperationBiasActivation = this.pfnOperation = this.pfnActivation = null;
    this.byteOffsetEnd = -1;
    this.bKeepInputTensor = false;  // Default will dispose input tensor.
    this.bInitOk = false;
  }

  /**
   * Adjust this.pfnOperation (and this.pfnOperationBiasActivation if need) so that this.pfnOperation() and this.pfnOperationBiasActivation()
   * will or will not dispose its inputTensor.
   */
  setKeepInputTensor( bKeepInputTensor ) {
    this.bKeepInputTensor = bKeepInputTensor;

    if ( bKeepInputTensor ) {

      switch ( this.pfnOperation ) {

        // Just clone input if 1x1 AVG/MAX pooling or illegal pooling type (i.e. not AVG, not MAX).
        // Since there is no operation at all, let pfnOperationBiasActivation ignore pfnOperation completely.
        case Base.return_input_directly: this.pfnOperation = this.pfnOperationBiasActivation = Base.keep_input_return_copy; break;

        case Base.Avg_and_destroy:       this.pfnOperation = Base.Avg_and_keep;  break;
        case Base.Max_and_destroy:       this.pfnOperation = Base.Max_and_keep;  break;
        case Base.Conv_and_destroy:      this.pfnOperation = Base.Conv_and_keep; break;

        // Just clone input if unknown depthwise operation.
        // Since there is no operation at all, let pfnOperationBiasActivation ignore pfnOperation completely.
        default:                         this.pfnOperation = this.pfnOperationBiasActivation = Base.keep_input_return_copy;
          tf.util.assert( false, `Unknown depthwise operation. (${this.pfnOperation}) when setKeepInputTensor( ${bKeepInputTensor} )` );
          break;
      }

//!!!
//       switch ( this.AvgMax_Or_ChannelMultiplier ) {
//
// //!!! (2021/04/19) What if Avg/Max but filter size is 1x1 (i.e. ( this.pfnOperation == Base.return_input_directly ) )?
//
// //           // Just clone input if 1x1 AVG/MAX pooling or illegal pooling type (i.e. not AVG, not MAX).
// //           case Base.return_input_directly:     this.pfnOperation = Base.keep_input_return_copy; break;
//
//         case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG: this.pfnOperation = Base.Avg_and_keep; break;
//         case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX: this.pfnOperation = Base.Max_and_keep; break;
//
//         // i.e. ( false == this.bExisted ). Since there is no operation at all, let pfnOperationBiasActivation ignore pfnOperation completely.
//         case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.NONE:
//           this.pfnOperationBiasActivation = this.pfnOperation = Base.keep_input_return_copy;
//           break;
//
//         default: this.pfnOperation = Base.Conv_and_keep; break;
//       }

    } else {

      switch ( this.pfnOperation ) {

        // Just return input if 1x1 AVG/MAX pooling or illegal pooling type (i.e. not AVG, not MAX).
        // Since there is no operation at all, let pfnOperationBiasActivation ignore pfnOperation completely.
        case Base.keep_input_return_copy: this.pfnOperation = this.pfnOperationBiasActivation = Base.return_input_directly; break;

        case Base.Avg_and_keep:           this.pfnOperation = Base.Avg_and_destroy;  break;
        case Base.Max_and_keep:           this.pfnOperation = Base.Max_and_destroy;  break;
        case Base.Conv_and_keep:          this.pfnOperation = Base.Conv_and_destroy; break;

        // Just return input if unknown depthwise operation.
        // Since there is no operation at all, let pfnOperationBiasActivation ignore pfnOperation completely.
        default:                          this.pfnOperation = this.pfnOperationBiasActivation = Base.return_input_directly;
          tf.util.assert( false, `Unknown depthwise operation. (${this.pfnOperation}) when setKeepInputTensor( ${bKeepInputTensor} )` );
          break;
      }

//!!!
//       switch ( this.AvgMax_Or_ChannelMultiplier ) {
//
// //!!! (2021/04/19) What if Avg/Max but filter size is 1x1 (i.e. ( this.pfnOperation == Base.return_input_directly ) )?
//
//         case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG: this.pfnOperation = Base.Avg_and_destroy; break;
//         case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX: this.pfnOperation = Base.Max_and_destroy; break;
//
//         // i.e. ( false == this.bExisted ). Since there is no operation at all, let pfnOperationBiasActivation ignore pfnOperation completely.
//         case ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.NONE:
//           this.pfnOperationBiasActivation = this.pfnOperation = Base.return_input_directly;
//           break;
//
//         default: this.pfnOperation = Base.Conv_and_destroy; break;
//       }

    }
  }

  /** Depthwise Average Pooling. */
  static Avg_and_keep( inputTensor ) {
    return tf.pool( inputTensor, this.filterHeightWidth, "avg", this.pad, 1, this.strides ); // dilations = 1
  }

  static Avg_and_destroy( inputTensor ) {
    let t = tf.pool( inputTensor, this.filterHeightWidth, "avg", this.pad, 1, this.strides ); // dilations = 1
    inputTensor.dispose();
    return t;
  }

  /** Depthwise Max Pooling. */
  static Max_and_keep( inputTensor ) {
    return tf.pool( inputTensor, this.filterHeightWidth, "max", this.pad, 1, this.strides ); // dilations = 1
  }

  static Max_and_destroy( inputTensor ) {
    let t = tf.pool( inputTensor, this.filterHeightWidth, "max", this.pad, 1, this.strides ); // dilations = 1
    inputTensor.dispose();
    return t;
  }

  /** Depthwise Convolution. */
  static Conv_and_keep( inputTensor ) {
    return tf.depthwiseConv2d( inputTensor, this.filtersTensor4d, this.strides, this.pad );
  }

  static Conv_and_destroy( inputTensor ) {
    let t = tf.depthwiseConv2d( inputTensor, this.filtersTensor4d, this.strides, this.pad );
    inputTensor.dispose();
    return t;
  }

  /** Depthwise Operation, Bias and Activation. */
  static Operation_and_destroy_or_keep( inputTensor ) {
    return this.pfnOperation( inputTensor ); // may destroy or keep.
  }

  static OperationBias_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = tf.add( t0, this.biasesTensor3d );
    t0.dispose();

    return t1;
  }

  static OperationActivation_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = this.pfnActivation( t0 );
    t0.dispose();

    return t1;
  }

  static OperationBiasActivation_and_destroy_or_keep( inputTensor ) {
    let t0 = this.pfnOperation( inputTensor ); // may destroy or keep.

    let t1 = tf.add( t0, this.biasesTensor3d );
    t0.dispose();

    t0 = this.pfnActivation( t1 );
    t1.dispose();

    return t0;
  }

}
<|endoftext|>


<commit_before>const {
  GraphQLObjectType,
  GraphQLList,
  GraphQLString,
  GraphQLInt,
  GraphQLEnumType,
} = require(`graphql`)
const Remark = require(`remark`)
const select = require(`unist-util-select`)
const sanitizeHTML = require(`sanitize-html`)
const _ = require(`lodash`)
const visit = require(`unist-util-visit`)
const toHAST = require(`mdast-util-to-hast`)
const hastToHTML = require(`hast-util-to-html`)
const mdastToToc = require(`mdast-util-toc`)
const Promise = require(`bluebird`)
const prune = require(`underscore.string/prune`)
const unified = require(`unified`)
const parse = require(`remark-parse`)
const stringify = require(`remark-stringify`)
const english = require(`retext-english`)
const remark2retext = require(`remark-retext`)
const GraphQlJson = require(`graphql-type-json`)
const stripPosition = require(`unist-util-remove-position`)
const hastReparseRaw = require(`hast-util-raw`)

let pluginsCacheStr = ``
let pathPrefixCacheStr = ``
const astCacheKey = node =>
  `transformer-remark-markdown-ast-${
    node.internal.contentDigest
  }-${pluginsCacheStr}-${pathPrefixCacheStr}`
const htmlCacheKey = node =>
  `transformer-remark-markdown-html-${
    node.internal.contentDigest
  }-${pluginsCacheStr}-${pathPrefixCacheStr}`
const htmlAstCacheKey = node =>
  `transformer-remark-markdown-html-ast-${
    node.internal.contentDigest
  }-${pluginsCacheStr}-${pathPrefixCacheStr}`
const headingsCacheKey = node =>
  `transformer-remark-markdown-headings-${
    node.internal.contentDigest
  }-${pluginsCacheStr}-${pathPrefixCacheStr}`
const tableOfContentsCacheKey = node =>
  `transformer-remark-markdown-toc-${
    node.internal.contentDigest
  }-${pluginsCacheStr}-${pathPrefixCacheStr}`

// ensure only one `/` in new url
const withPathPrefix = (url, pathPrefix) =>
  (pathPrefix + url).replace(/\/\//, `/`)

/**
 * Map that keeps track of generation of AST to not generate it multiple
 * times in parallel.
 *
 * @type {Map<string,Promise>}
 */
const ASTPromiseMap = new Map()

module.exports = (
  { type, store, pathPrefix, getNode, cache, reporter },
  pluginOptions
) => {
  if (type.name !== `MarkdownRemark`) {
    return {}
  }

  pluginsCacheStr = pluginOptions.plugins.map(p => p.name).join(``)
  pathPrefixCacheStr = pathPrefix || ``

  return new Promise((resolve, reject) => {
    // Setup Remark.
    let remark = new Remark().data(`settings`, {
      commonmark: true,
      footnotes: true,
      pedantic: true,
    })

    for (let plugin of pluginOptions.plugins) {
      const requiredPlugin = require(plugin.resolve)
      if (_.isFunction(requiredPlugin.setParserPlugins)) {
        for (let parserPlugin of requiredPlugin.setParserPlugins(
          plugin.pluginOptions
        )) {
          if (_.isArray(parserPlugin)) {
            const [parser, options] = parserPlugin
            remark = remark.use(parser, options)
          } else {
            remark = remark.use(parserPlugin)
          }
        }
      }
    }

    async function getAST(markdownNode) {
      const cacheKey = astCacheKey(markdownNode)
      const cachedAST = await cache.get(cacheKey)
      if (cachedAST) {
        return cachedAST
      } else if (ASTPromiseMap.has(cacheKey)) {
        // We are already generating AST, so let's wait for it
        return await ASTPromiseMap.get(cacheKey)
      } else {
        const ASTGenerationPromise = new Promise(async resolve => {
          const files = _.values(store.getState().nodes).filter(
            n => n.internal.type === `File`
          )
          const ast = await new Promise((resolve, reject) => {
            // Use Bluebird's Promise function "each" to run remark plugins serially.
            Promise.each(pluginOptions.plugins, plugin => {
              const requiredPlugin = require(plugin.resolve)
              if (_.isFunction(requiredPlugin.mutateSource)) {
                return requiredPlugin.mutateSource(
                  {
                    markdownNode,
                    files,
                    getNode,
                    reporter,
                  },
                  plugin.pluginOptions
                )
              } else {
                return Promise.resolve()
              }
            }).then(() => {
              const markdownAST = remark.parse(markdownNode.internal.content)

              if (pathPrefix) {
                // Ensure relative links include `pathPrefix`
                visit(markdownAST, `link`, node => {
                  if (
                    node.url &&
                    node.url.startsWith(`/`) &&
                    !node.url.startsWith(`//`)
                  ) {
                    node.url = withPathPrefix(node.url, pathPrefix)
                  }
                })
              }

              // source => parse (can order parsing for dependencies) => typegen
              //
              // source plugins identify nodes, provide id, initial parse, know
              // when nodes are created/removed/deleted
              // get passed cached DataTree and return list of clean and dirty nodes.
              // Also get passed `dirtyNodes` function which they can call with an array
              // of node ids which will then get re-parsed and the inferred schema
              // recreated (if inferring schema gets too expensive, can also
              // cache the schema until a query fails at which point recreate the
              // schema).
              //
              // parse plugins take data from source nodes and extend it, never mutate
              // it. Freeze all nodes once done so typegen plugins can't change it
              // this lets us save off the DataTree at that point as well as create
              // indexes.
              //
              // typegen plugins identify further types of data that should be lazily
              // computed due to their expense, or are hard to infer graphql type
              // (markdown ast), or are need user input in order to derive e.g.
              // markdown headers or date fields.
              //
              // wrap all resolve functions to (a) auto-memoize and (b) cache to disk any
              // resolve function that takes longer than ~10ms (do research on this
              // e.g. how long reading/writing to cache takes), and (c) track which
              // queries are based on which source nodes. Also if connection of what
              // which are always rerun if their underlying nodes change..
              //
              // every node type in DataTree gets a schema type automatically.
              // typegen plugins just modify the auto-generated types to add derived fields
              // as well as computationally expensive fields.
              const files = _.values(store.getState().nodes).filter(
                n => n.internal.type === `File`
              )
              // Use Bluebird's Promise function "each" to run remark plugins serially.
              Promise.each(pluginOptions.plugins, plugin => {
                const requiredPlugin = require(plugin.resolve)
                if (_.isFunction(requiredPlugin)) {
                  return requiredPlugin(
                    {
                      markdownAST,
                      markdownNode,
                      getNode,
                      files,
                      pathPrefix,
                      reporter,
                    },
                    plugin.pluginOptions
                  )
                } else {
                  return Promise.resolve()
                }
              }).then(() => {
                resolve(markdownAST)
              })
            })
          })

          // Save new AST to cache and return
          cache.set(cacheKey, ast)
          // We can now release promise, as we cached result
          ASTPromiseMap.delete(cacheKey)
          return resolve(ast)
        })
        ASTPromiseMap.set(cacheKey, ASTGenerationPromise)
        return await ASTGenerationPromise
      }
    }

    async function getHeadings(markdownNode) {
      const cachedHeadings = await cache.get(headingsCacheKey(markdownNode))
      if (cachedHeadings) {
        return cachedHeadings
      } else {
        const ast = await getAST(markdownNode)
        const headings = select(ast, `heading`).map(heading => {
          return {
            value: _.first(select(heading, `text`).map(text => text.value)),
            depth: heading.depth,
          }
        })

        cache.set(headingsCacheKey(markdownNode), headings)
        return headings
      }
    }

    async function getTableOfContents(markdownNode) {
      const cachedToc = await cache.get(tableOfContentsCacheKey(markdownNode))
      if (cachedToc) {
        return cachedToc
      } else {
        const ast = await getAST(markdownNode)
        const tocAst = mdastToToc(ast)

        let toc
        if (tocAst.map) {
          const addSlugToUrl = function(node) {
            if (node.url) {
              node.url = [pathPrefix, markdownNode.fields.slug, node.url]
                .join(`/`)
                .replace(/\/\//g, `/`)
            }
            if (node.children) {
              node.children = node.children.map(node => addSlugToUrl(node))
            }

            return node
          }
          tocAst.map = addSlugToUrl(tocAst.map)

          toc = hastToHTML(toHAST(tocAst.map))
        } else {
          toc = ``
        }
        cache.set(tableOfContentsCacheKey(markdownNode), toc)
        return toc
      }
    }

    async function getHTMLAst(markdownNode) {
      const cachedAst = await cache.get(htmlAstCacheKey(markdownNode))
      if (cachedAst) {
        return cachedAst
      } else {
        const ast = await getAST(markdownNode)
        const htmlAst = toHAST(ast, { allowDangerousHTML: true })

        // Save new HTML AST to cache and return
        cache.set(htmlAstCacheKey(markdownNode), htmlAst)
        return htmlAst
      }
    }

    async function getHTML(markdownNode) {
      const cachedHTML = await cache.get(htmlCacheKey(markdownNode))
      if (cachedHTML) {
        return cachedHTML
      } else {
        const ast = await getHTMLAst(markdownNode)
        // Save new HTML to cache and return
        const html = hastToHTML(ast, {
          allowDangerousHTML: true,
        })

        // Save new HTML to cache and return
        cache.set(htmlCacheKey(markdownNode), html)
        return html
      }
    }

    const HeadingType = new GraphQLObjectType({
      name: `MarkdownHeading`,
      fields: {
        value: {
          type: GraphQLString,
          resolve(heading) {
            return heading.value
          },
        },
        depth: {
          type: GraphQLInt,
          resolve(heading) {
            return heading.depth
          },
        },
      },
    })

    const HeadingLevels = new GraphQLEnumType({
      name: `HeadingLevels`,
      values: {
        h1: { value: 1 },
        h2: { value: 2 },
        h3: { value: 3 },
        h4: { value: 4 },
        h5: { value: 5 },
        h6: { value: 6 },
      },
    })

    return resolve({
      html: {
        type: GraphQLString,
        resolve(markdownNode) {
          return getHTML(markdownNode)
        },
      },
      htmlAst: {
        type: GraphQlJson,
        resolve(markdownNode) {
          return getHTMLAst(markdownNode).then(ast => {
            const strippedAst = stripPosition(_.clone(ast), true)
            return hastReparseRaw(strippedAst)
          })
        },
      },
      excerpt: {
        type: GraphQLString,
        args: {
          pruneLength: {
            type: GraphQLInt,
            defaultValue: 140,
          },
        },
        resolve(markdownNode, { pruneLength }) {
          if (markdownNode.excerpt) {
            return Promise.resolve(markdownNode.excerpt)
          }
          return getAST(markdownNode).then(ast => {
            const excerptNodes = []
            visit(ast, node => {
              if (node.type === `text` || node.type === `inlineCode`) {
                excerptNodes.push(node.value)
              }
              return
            })

            return prune(excerptNodes.join(` `), pruneLength, `…`)
          })
        },
      },
      headings: {
        type: new GraphQLList(HeadingType),
        args: {
          depth: {
            type: HeadingLevels,
          },
        },
        resolve(markdownNode, { depth }) {
          return getHeadings(markdownNode).then(headings => {
            if (typeof depth === `number`) {
              headings = headings.filter(heading => heading.depth === depth)
            }
            return headings
          })
        },
      },
      timeToRead: {
        type: GraphQLInt,
        resolve(markdownNode) {
          return getHTML(markdownNode).then(html => {
            let timeToRead = 0
            const pureText = sanitizeHTML(html, { allowTags: [] })
            const avgWPM = 265
            const wordCount = _.words(pureText).length
            timeToRead = Math.round(wordCount / avgWPM)
            if (timeToRead === 0) {
              timeToRead = 1
            }
            return timeToRead
          })
        },
      },
      tableOfContents: {
        type: GraphQLString,
        resolve(markdownNode) {
          return getTableOfContents(markdownNode)
        },
      },
      // TODO add support for non-latin languages https://github.com/wooorm/remark/issues/251#issuecomment-296731071
      wordCount: {
        type: new GraphQLObjectType({
          name: `wordCount`,
          fields: {
            paragraphs: {
              type: GraphQLInt,
            },
            sentences: {
              type: GraphQLInt,
            },
            words: {
              type: GraphQLInt,
            },
          },
        }),
        resolve(markdownNode) {
          let counts = {}

          unified()
            .use(parse)
            .use(
              remark2retext,
              unified()
                .use(english)
                .use(count)
            )
            .use(stringify)
            .processSync(markdownNode.internal.content)

          return {
            paragraphs: counts.ParagraphNode,
            sentences: counts.SentenceNode,
            words: counts.WordNode,
          }

          function count() {
            return counter
            function counter(tree) {
              visit(tree, visitor)
              function visitor(node) {
                counts[node.type] = (counts[node.type] || 0) + 1
              }
            }
          }
        },
      },
    })
  })
}
<commit_msg>Make cache available to the plugins (#5840)<commit_after>const {
  GraphQLObjectType,
  GraphQLList,
  GraphQLString,
  GraphQLInt,
  GraphQLEnumType,
} = require(`graphql`)
const Remark = require(`remark`)
const select = require(`unist-util-select`)
const sanitizeHTML = require(`sanitize-html`)
const _ = require(`lodash`)
const visit = require(`unist-util-visit`)
const toHAST = require(`mdast-util-to-hast`)
const hastToHTML = require(`hast-util-to-html`)
const mdastToToc = require(`mdast-util-toc`)
const Promise = require(`bluebird`)
const prune = require(`underscore.string/prune`)
const unified = require(`unified`)
const parse = require(`remark-parse`)
const stringify = require(`remark-stringify`)
const english = require(`retext-english`)
const remark2retext = require(`remark-retext`)
const GraphQlJson = require(`graphql-type-json`)
const stripPosition = require(`unist-util-remove-position`)
const hastReparseRaw = require(`hast-util-raw`)

let pluginsCacheStr = ``
let pathPrefixCacheStr = ``
const astCacheKey = node =>
  `transformer-remark-markdown-ast-${
    node.internal.contentDigest
  }-${pluginsCacheStr}-${pathPrefixCacheStr}`
const htmlCacheKey = node =>
  `transformer-remark-markdown-html-${
    node.internal.contentDigest
  }-${pluginsCacheStr}-${pathPrefixCacheStr}`
const htmlAstCacheKey = node =>
  `transformer-remark-markdown-html-ast-${
    node.internal.contentDigest
  }-${pluginsCacheStr}-${pathPrefixCacheStr}`
const headingsCacheKey = node =>
  `transformer-remark-markdown-headings-${
    node.internal.contentDigest
  }-${pluginsCacheStr}-${pathPrefixCacheStr}`
const tableOfContentsCacheKey = node =>
  `transformer-remark-markdown-toc-${
    node.internal.contentDigest
  }-${pluginsCacheStr}-${pathPrefixCacheStr}`

// ensure only one `/` in new url
const withPathPrefix = (url, pathPrefix) =>
  (pathPrefix + url).replace(/\/\//, `/`)

/**
 * Map that keeps track of generation of AST to not generate it multiple
 * times in parallel.
 *
 * @type {Map<string,Promise>}
 */
const ASTPromiseMap = new Map()

module.exports = (
  { type, store, pathPrefix, getNode, cache, reporter },
  pluginOptions
) => {
  if (type.name !== `MarkdownRemark`) {
    return {}
  }

  pluginsCacheStr = pluginOptions.plugins.map(p => p.name).join(``)
  pathPrefixCacheStr = pathPrefix || ``

  return new Promise((resolve, reject) => {
    // Setup Remark.
    let remark = new Remark().data(`settings`, {
      commonmark: true,
      footnotes: true,
      pedantic: true,
    })

    for (let plugin of pluginOptions.plugins) {
      const requiredPlugin = require(plugin.resolve)
      if (_.isFunction(requiredPlugin.setParserPlugins)) {
        for (let parserPlugin of requiredPlugin.setParserPlugins(
          plugin.pluginOptions
        )) {
          if (_.isArray(parserPlugin)) {
            const [parser, options] = parserPlugin
            remark = remark.use(parser, options)
          } else {
            remark = remark.use(parserPlugin)
          }
        }
      }
    }

    async function getAST(markdownNode) {
      const cacheKey = astCacheKey(markdownNode)
      const cachedAST = await cache.get(cacheKey)
      if (cachedAST) {
        return cachedAST
      } else if (ASTPromiseMap.has(cacheKey)) {
        // We are already generating AST, so let's wait for it
        return await ASTPromiseMap.get(cacheKey)
      } else {
        const ASTGenerationPromise = new Promise(async resolve => {
          const files = _.values(store.getState().nodes).filter(
            n => n.internal.type === `File`
          )
          const ast = await new Promise((resolve, reject) => {
            // Use Bluebird's Promise function "each" to run remark plugins serially.
            Promise.each(pluginOptions.plugins, plugin => {
              const requiredPlugin = require(plugin.resolve)
              if (_.isFunction(requiredPlugin.mutateSource)) {
                return requiredPlugin.mutateSource(
                  {
                    markdownNode,
                    files,
                    getNode,
                    reporter,
                    cache,
                  },
                  plugin.pluginOptions
                )
              } else {
                return Promise.resolve()
              }
            }).then(() => {
              const markdownAST = remark.parse(markdownNode.internal.content)

              if (pathPrefix) {
                // Ensure relative links include `pathPrefix`
                visit(markdownAST, `link`, node => {
                  if (
                    node.url &&
                    node.url.startsWith(`/`) &&
                    !node.url.startsWith(`//`)
                  ) {
                    node.url = withPathPrefix(node.url, pathPrefix)
                  }
                })
              }

              // source => parse (can order parsing for dependencies) => typegen
              //
              // source plugins identify nodes, provide id, initial parse, know
              // when nodes are created/removed/deleted
              // get passed cached DataTree and return list of clean and dirty nodes.
              // Also get passed `dirtyNodes` function which they can call with an array
              // of node ids which will then get re-parsed and the inferred schema
              // recreated (if inferring schema gets too expensive, can also
              // cache the schema until a query fails at which point recreate the
              // schema).
              //
              // parse plugins take data from source nodes and extend it, never mutate
              // it. Freeze all nodes once done so typegen plugins can't change it
              // this lets us save off the DataTree at that point as well as create
              // indexes.
              //
              // typegen plugins identify further types of data that should be lazily
              // computed due to their expense, or are hard to infer graphql type
              // (markdown ast), or are need user input in order to derive e.g.
              // markdown headers or date fields.
              //
              // wrap all resolve functions to (a) auto-memoize and (b) cache to disk any
              // resolve function that takes longer than ~10ms (do research on this
              // e.g. how long reading/writing to cache takes), and (c) track which
              // queries are based on which source nodes. Also if connection of what
              // which are always rerun if their underlying nodes change..
              //
              // every node type in DataTree gets a schema type automatically.
              // typegen plugins just modify the auto-generated types to add derived fields
              // as well as computationally expensive fields.
              const files = _.values(store.getState().nodes).filter(
                n => n.internal.type === `File`
              )
              // Use Bluebird's Promise function "each" to run remark plugins serially.
              Promise.each(pluginOptions.plugins, plugin => {
                const requiredPlugin = require(plugin.resolve)
                if (_.isFunction(requiredPlugin)) {
                  return requiredPlugin(
                    {
                      markdownAST,
                      markdownNode,
                      getNode,
                      files,
                      pathPrefix,
                      reporter,
                      cache,
                    },
                    plugin.pluginOptions
                  )
                } else {
                  return Promise.resolve()
                }
              }).then(() => {
                resolve(markdownAST)
              })
            })
          })

          // Save new AST to cache and return
          cache.set(cacheKey, ast)
          // We can now release promise, as we cached result
          ASTPromiseMap.delete(cacheKey)
          return resolve(ast)
        })
        ASTPromiseMap.set(cacheKey, ASTGenerationPromise)
        return await ASTGenerationPromise
      }
    }

    async function getHeadings(markdownNode) {
      const cachedHeadings = await cache.get(headingsCacheKey(markdownNode))
      if (cachedHeadings) {
        return cachedHeadings
      } else {
        const ast = await getAST(markdownNode)
        const headings = select(ast, `heading`).map(heading => {
          return {
            value: _.first(select(heading, `text`).map(text => text.value)),
            depth: heading.depth,
          }
        })

        cache.set(headingsCacheKey(markdownNode), headings)
        return headings
      }
    }

    async function getTableOfContents(markdownNode) {
      const cachedToc = await cache.get(tableOfContentsCacheKey(markdownNode))
      if (cachedToc) {
        return cachedToc
      } else {
        const ast = await getAST(markdownNode)
        const tocAst = mdastToToc(ast)

        let toc
        if (tocAst.map) {
          const addSlugToUrl = function(node) {
            if (node.url) {
              node.url = [pathPrefix, markdownNode.fields.slug, node.url]
                .join(`/`)
                .replace(/\/\//g, `/`)
            }
            if (node.children) {
              node.children = node.children.map(node => addSlugToUrl(node))
            }

            return node
          }
          tocAst.map = addSlugToUrl(tocAst.map)

          toc = hastToHTML(toHAST(tocAst.map))
        } else {
          toc = ``
        }
        cache.set(tableOfContentsCacheKey(markdownNode), toc)
        return toc
      }
    }

    async function getHTMLAst(markdownNode) {
      const cachedAst = await cache.get(htmlAstCacheKey(markdownNode))
      if (cachedAst) {
        return cachedAst
      } else {
        const ast = await getAST(markdownNode)
        const htmlAst = toHAST(ast, { allowDangerousHTML: true })

        // Save new HTML AST to cache and return
        cache.set(htmlAstCacheKey(markdownNode), htmlAst)
        return htmlAst
      }
    }

    async function getHTML(markdownNode) {
      const cachedHTML = await cache.get(htmlCacheKey(markdownNode))
      if (cachedHTML) {
        return cachedHTML
      } else {
        const ast = await getHTMLAst(markdownNode)
        // Save new HTML to cache and return
        const html = hastToHTML(ast, {
          allowDangerousHTML: true,
        })

        // Save new HTML to cache and return
        cache.set(htmlCacheKey(markdownNode), html)
        return html
      }
    }

    const HeadingType = new GraphQLObjectType({
      name: `MarkdownHeading`,
      fields: {
        value: {
          type: GraphQLString,
          resolve(heading) {
            return heading.value
          },
        },
        depth: {
          type: GraphQLInt,
          resolve(heading) {
            return heading.depth
          },
        },
      },
    })

    const HeadingLevels = new GraphQLEnumType({
      name: `HeadingLevels`,
      values: {
        h1: { value: 1 },
        h2: { value: 2 },
        h3: { value: 3 },
        h4: { value: 4 },
        h5: { value: 5 },
        h6: { value: 6 },
      },
    })

    return resolve({
      html: {
        type: GraphQLString,
        resolve(markdownNode) {
          return getHTML(markdownNode)
        },
      },
      htmlAst: {
        type: GraphQlJson,
        resolve(markdownNode) {
          return getHTMLAst(markdownNode).then(ast => {
            const strippedAst = stripPosition(_.clone(ast), true)
            return hastReparseRaw(strippedAst)
          })
        },
      },
      excerpt: {
        type: GraphQLString,
        args: {
          pruneLength: {
            type: GraphQLInt,
            defaultValue: 140,
          },
        },
        resolve(markdownNode, { pruneLength }) {
          if (markdownNode.excerpt) {
            return Promise.resolve(markdownNode.excerpt)
          }
          return getAST(markdownNode).then(ast => {
            const excerptNodes = []
            visit(ast, node => {
              if (node.type === `text` || node.type === `inlineCode`) {
                excerptNodes.push(node.value)
              }
              return
            })

            return prune(excerptNodes.join(` `), pruneLength, `…`)
          })
        },
      },
      headings: {
        type: new GraphQLList(HeadingType),
        args: {
          depth: {
            type: HeadingLevels,
          },
        },
        resolve(markdownNode, { depth }) {
          return getHeadings(markdownNode).then(headings => {
            if (typeof depth === `number`) {
              headings = headings.filter(heading => heading.depth === depth)
            }
            return headings
          })
        },
      },
      timeToRead: {
        type: GraphQLInt,
        resolve(markdownNode) {
          return getHTML(markdownNode).then(html => {
            let timeToRead = 0
            const pureText = sanitizeHTML(html, { allowTags: [] })
            const avgWPM = 265
            const wordCount = _.words(pureText).length
            timeToRead = Math.round(wordCount / avgWPM)
            if (timeToRead === 0) {
              timeToRead = 1
            }
            return timeToRead
          })
        },
      },
      tableOfContents: {
        type: GraphQLString,
        resolve(markdownNode) {
          return getTableOfContents(markdownNode)
        },
      },
      // TODO add support for non-latin languages https://github.com/wooorm/remark/issues/251#issuecomment-296731071
      wordCount: {
        type: new GraphQLObjectType({
          name: `wordCount`,
          fields: {
            paragraphs: {
              type: GraphQLInt,
            },
            sentences: {
              type: GraphQLInt,
            },
            words: {
              type: GraphQLInt,
            },
          },
        }),
        resolve(markdownNode) {
          let counts = {}

          unified()
            .use(parse)
            .use(
              remark2retext,
              unified()
                .use(english)
                .use(count)
            )
            .use(stringify)
            .processSync(markdownNode.internal.content)

          return {
            paragraphs: counts.ParagraphNode,
            sentences: counts.SentenceNode,
            words: counts.WordNode,
          }

          function count() {
            return counter
            function counter(tree) {
              visit(tree, visitor)
              function visitor(node) {
                counts[node.type] = (counts[node.type] || 0) + 1
              }
            }
          }
        },
      },
    })
  })
}
<|endoftext|>


<commit_before>const date = 14e11; // 14e11 is about 5pm on 13th may 2014. it just easy to remember its a valid date and short.

export default (client) => [
    {
        name: 'me',
        fn: () => client.me(),
    },
    {
        name: 'get',
        fn: () => client.get('some-id'),
    },
    {
        name: 'create',
        fn: () => client.create('some-type', { some: 'prop' }),
    },
    {
        name: 'create (waitForIndex)',
        fn: () =>
            client.create(
                'some-type',
                { some: 'prop' },
                { waitForIndex: true },
            ),
    },
    {
        name: 'update',
        fn: () => client.update('some-id', { '/foo': 'bar' }),
    },
    {
        name: 'update (waitForIndex)',
        fn: () =>
            client.update('some-id', { '/foo': 'bar' }, { waitForIndex: true }),
    },
    {
        name: 'replace',
        fn: () => client.replace('some-id', { '/foo': 'bar' }),
    },
    {
        name: 'replace (createIfNotExists)',
        fn: () =>
            client.replace(
                'some-id',
                { '/foo': 'bar' },
                { createIfNotExists: 'some-type' },
            ),
    },
    {
        name: 'replace (waitForIndex)',
        fn: () =>
            client.replace(
                'some-id',
                { '/foo': 'bar' },
                { waitForIndex: true },
            ),
    },
    {
        name: 'delete',
        fn: () => client.delete('some-id'),
    },
    {
        name: 'delete (waitForIndex)',
        fn: () => client.delete('some-id', { waitForIndex: true }),
    },
    {
        name: 'ids',
        fn: () => client.ids(['a', 'b', 'c']),
    },
    {
        name: 'connect',
        fn: () => client.connect('some-verb', 'the-in-id', 'the-out-id'),
    },
    {
        name: 'disconnect',
        fn: () => client.disconnect('some-verb', 'the-in-id', 'the-out-id'),
    },
    {
        name: 'lucene (basic)',
        fn: () => client.lucene('*:*'),
    },
    {
        name: 'lucene (limits)',
        fn: () => client.lucene('*:*', { limit: -1, offset: 1000 }),
    },
    {
        name: 'lucene (placeholders)',
        fn: () => client.lucene('/foo:$bar', { bar: '123' }),
    },
    {
        name: 'lucene (count)',
        fn: () => client.lucene('*:*', { count: true }),
    },
    {
        name: 'lucene (order)',
        fn: () =>
            client.lucene('*:*', { order: ['/field1 asc', '/field2 desc'] }),
    },
    {
        name: 'lucene (fields)',
        fn: () => client.lucene('*:*', { fields: ['/field1', '/field2'] }),
    },
    {
        name: 'gremlin',
        fn: () => client.gremlin('root-id', 'pow()'),
    },
    {
        name: 'writets',
        fn: () =>
            client.writets('ts-id', [{ timestamp: date, value: 'value1' }]),
    },
    {
        name: 'streamts',
        fn: () => client.streamts('ts-id'),
    },
    {
        name: 'streamts (from only)',
        fn: () => client.streamts('ts-id', { from: date - 1 }),
    },
    {
        name: 'streamts (to only)',
        fn: () => client.streamts('ts-id', { to: date + 1 }),
    },
    {
        name: 'streamts (to + from)',
        fn: () => client.streamts('ts-id', { from: date - 1, to: date + 1 }),
    },
    {
        name: 'history',
        fn: () => client.history('some-id'),
    },
    {
        name: 'history (with offset)',
        fn: () => client.history('some-id', { offset: 10 }),
    },
    {
        name: 'history (with limit)',
        fn: () => client.history('some-id', { limit: 500 }),
    },
    {
        name: 'history (with offset + limit)',
        fn: () => client.history('some-id', { limit: 5, offset: 100 }),
    },
    {
        name: 'history (with from)',
        fn: () => client.history('some-id', { from: 0 }),
    },
    {
        name: 'history (with to)',
        fn: () => client.history('some-id', { to: 1550597976759 }),
    },
    {
        name: 'history (with from + to)',
        fn: () => client.history('some-id', { from: 0, to: 1550597976759 }),
    },
    {
        name: 'history (with from + to + offset + limit)',
        fn: () =>
            client.history('some-id', {
                from: 0,
                to: 1550597976759,
                limit: 5,
                offset: 100,
            }),
    },
    {
        name: 'history (with version)',
        fn: () => client.history('some-id', { limit: 500, version: 123 }),
    },
];
<commit_msg>Remove Eslint Warnings<commit_after>const date = 14e11; // 14e11 is about 5pm on 13th may 2014. it just easy to remember its a valid date and short.

const clientTestHelper = (client) => [
    {
        name: 'me',
        fn: () => client.me(),
    },
    {
        name: 'get',
        fn: () => client.get('some-id'),
    },
    {
        name: 'create',
        fn: () => client.create('some-type', { some: 'prop' }),
    },
    {
        name: 'create (waitForIndex)',
        fn: () =>
            client.create(
                'some-type',
                { some: 'prop' },
                { waitForIndex: true },
            ),
    },
    {
        name: 'update',
        fn: () => client.update('some-id', { '/foo': 'bar' }),
    },
    {
        name: 'update (waitForIndex)',
        fn: () =>
            client.update('some-id', { '/foo': 'bar' }, { waitForIndex: true }),
    },
    {
        name: 'replace',
        fn: () => client.replace('some-id', { '/foo': 'bar' }),
    },
    {
        name: 'replace (createIfNotExists)',
        fn: () =>
            client.replace(
                'some-id',
                { '/foo': 'bar' },
                { createIfNotExists: 'some-type' },
            ),
    },
    {
        name: 'replace (waitForIndex)',
        fn: () =>
            client.replace(
                'some-id',
                { '/foo': 'bar' },
                { waitForIndex: true },
            ),
    },
    {
        name: 'delete',
        fn: () => client.delete('some-id'),
    },
    {
        name: 'delete (waitForIndex)',
        fn: () => client.delete('some-id', { waitForIndex: true }),
    },
    {
        name: 'ids',
        fn: () => client.ids(['a', 'b', 'c']),
    },
    {
        name: 'connect',
        fn: () => client.connect('some-verb', 'the-in-id', 'the-out-id'),
    },
    {
        name: 'disconnect',
        fn: () => client.disconnect('some-verb', 'the-in-id', 'the-out-id'),
    },
    {
        name: 'lucene (basic)',
        fn: () => client.lucene('*:*'),
    },
    {
        name: 'lucene (limits)',
        fn: () => client.lucene('*:*', { limit: -1, offset: 1000 }),
    },
    {
        name: 'lucene (placeholders)',
        fn: () => client.lucene('/foo:$bar', { bar: '123' }),
    },
    {
        name: 'lucene (count)',
        fn: () => client.lucene('*:*', { count: true }),
    },
    {
        name: 'lucene (order)',
        fn: () =>
            client.lucene('*:*', { order: ['/field1 asc', '/field2 desc'] }),
    },
    {
        name: 'lucene (fields)',
        fn: () => client.lucene('*:*', { fields: ['/field1', '/field2'] }),
    },
    {
        name: 'gremlin',
        fn: () => client.gremlin('root-id', 'pow()'),
    },
    {
        name: 'writets',
        fn: () =>
            client.writets('ts-id', [{ timestamp: date, value: 'value1' }]),
    },
    {
        name: 'streamts',
        fn: () => client.streamts('ts-id'),
    },
    {
        name: 'streamts (from only)',
        fn: () => client.streamts('ts-id', { from: date - 1 }),
    },
    {
        name: 'streamts (to only)',
        fn: () => client.streamts('ts-id', { to: date + 1 }),
    },
    {
        name: 'streamts (to + from)',
        fn: () => client.streamts('ts-id', { from: date - 1, to: date + 1 }),
    },
    {
        name: 'history',
        fn: () => client.history('some-id'),
    },
    {
        name: 'history (with offset)',
        fn: () => client.history('some-id', { offset: 10 }),
    },
    {
        name: 'history (with limit)',
        fn: () => client.history('some-id', { limit: 500 }),
    },
    {
        name: 'history (with offset + limit)',
        fn: () => client.history('some-id', { limit: 5, offset: 100 }),
    },
    {
        name: 'history (with from)',
        fn: () => client.history('some-id', { from: 0 }),
    },
    {
        name: 'history (with to)',
        fn: () => client.history('some-id', { to: 1550597976759 }),
    },
    {
        name: 'history (with from + to)',
        fn: () => client.history('some-id', { from: 0, to: 1550597976759 }),
    },
    {
        name: 'history (with from + to + offset + limit)',
        fn: () =>
            client.history('some-id', {
                from: 0,
                to: 1550597976759,
                limit: 5,
                offset: 100,
            }),
    },
    {
        name: 'history (with version)',
        fn: () => client.history('some-id', { limit: 500, version: 123 }),
    },
];

export default clientTestHelper;
<|endoftext|>


<commit_before>/* global assert, process, setup, suite, teardown, test */
var entityFactory = require('../helpers').entityFactory;

suite('geometry system', function () {
  setup(function (done) {
    var el = entityFactory();
    var self = this;
    el.addEventListener('loaded', function () {
      self.system = el.sceneEl.systems.geometry;
      done();
    });
  });

  suite('getOrCreateGeometry', function () {
    teardown(function () {
      this.system.clearCache();
    });

    test('sets hash on cache', function () {
      var data = {primitive: 'box'};
      var system = this.system;
      var hash = system.hash(data);

      assert.notOk(system.cache[hash]);
      system.getOrCreateGeometry(data);
      assert.ok(system.cache[hash]);
    });

    test('does not hash on cache if skipCache', function () {
      var data = {primitive: 'box', skipCache: true};
      var system = this.system;
      var hash = system.hash(data);
      system.getOrCreateGeometry({primitive: 'box'});
      assert.notOk(system.cache[hash]);
    });

    test('caches identical geometries', function () {
      var data = {primitive: 'box', width: 5};
      var geometry1;
      var geometry2;
      var system = this.system;
      var hash = system.hash(data);

      geometry1 = system.getOrCreateGeometry(data);
      assert.ok(geometry1);

      geometry2 = system.getOrCreateGeometry(data);
      assert.ok(geometry2);

      assert.equal(geometry1, geometry2);
      assert.equal(system.cacheCount[hash], 2);
    });

    test('preserves original metadata on BufferGeometry', function () {
      var data = {primitive: 'box', width: 5, buffer: true};
      var geometry = this.system.getOrCreateGeometry(data);
      assert.equal(geometry.metadata.type, 'BoxGeometry');
      assert.equal(geometry.metadata.width, 5);
    });
  });

  suite('unuseGeometry', function () {
    teardown(function () {
      this.system.clearCache();
    });

    test('disposes geometry if no longer used', function () {
      var data = {primitive: 'box'};
      var system = this.system;
      var hash = system.hash(data);
      var sinon = this.sinon;

      var geometry = system.getOrCreateGeometry(data);
      var disposeSpy = sinon.spy(geometry, 'dispose');
      system.unuseGeometry(data);

      assert.ok(disposeSpy.called);
      assert.notOk(system.cache[hash]);
      assert.notOk(system.cacheCount[hash]);
    });

    test('does not dispose geometry if still used', function () {
      var data = {primitive: 'box'};
      var system = this.system;
      var hash = system.hash(data);
      var sinon = this.sinon;

      var geometry = system.getOrCreateGeometry(data);
      var disposeSpy = sinon.spy(geometry, 'dispose');

      system.getOrCreateGeometry(data);
      system.unuseGeometry(data);
      assert.notOk(disposeSpy.called);
      assert.ok(system.cache[hash]);
      assert.equal(system.cacheCount[hash], 1);
    });
  });
});
<commit_msg>Fix for buffer geometry metadata test<commit_after>/* global assert, process, setup, suite, teardown, test */
var entityFactory = require('../helpers').entityFactory;

suite('geometry system', function () {
  setup(function (done) {
    var el = entityFactory();
    var self = this;
    el.addEventListener('loaded', function () {
      self.system = el.sceneEl.systems.geometry;
      done();
    });
  });

  suite('getOrCreateGeometry', function () {
    teardown(function () {
      this.system.clearCache();
    });

    test('sets hash on cache', function () {
      var data = {primitive: 'box'};
      var system = this.system;
      var hash = system.hash(data);

      assert.notOk(system.cache[hash]);
      system.getOrCreateGeometry(data);
      assert.ok(system.cache[hash]);
    });

    test('does not hash on cache if skipCache', function () {
      var data = {primitive: 'box', skipCache: true};
      var system = this.system;
      var hash = system.hash(data);
      system.getOrCreateGeometry({primitive: 'box'});
      assert.notOk(system.cache[hash]);
    });

    test('caches identical geometries', function () {
      var data = {primitive: 'box', width: 5};
      var geometry1;
      var geometry2;
      var system = this.system;
      var hash = system.hash(data);

      geometry1 = system.getOrCreateGeometry(data);
      assert.ok(geometry1);

      geometry2 = system.getOrCreateGeometry(data);
      assert.ok(geometry2);

      assert.equal(geometry1, geometry2);
      assert.equal(system.cacheCount[hash], 2);
    });

    test('preserves original metadata on BufferGeometry', function () {
      var data = {primitive: 'box', width: 5, buffer: true};
      var geometry = this.system.getOrCreateGeometry(data);
      assert.equal(geometry.metadata.type, 'BoxGeometry');
      assert.equal(geometry.metadata.parameters.width, 5);
    });
  });

  suite('unuseGeometry', function () {
    teardown(function () {
      this.system.clearCache();
    });

    test('disposes geometry if no longer used', function () {
      var data = {primitive: 'box'};
      var system = this.system;
      var hash = system.hash(data);
      var sinon = this.sinon;

      var geometry = system.getOrCreateGeometry(data);
      var disposeSpy = sinon.spy(geometry, 'dispose');
      system.unuseGeometry(data);

      assert.ok(disposeSpy.called);
      assert.notOk(system.cache[hash]);
      assert.notOk(system.cacheCount[hash]);
    });

    test('does not dispose geometry if still used', function () {
      var data = {primitive: 'box'};
      var system = this.system;
      var hash = system.hash(data);
      var sinon = this.sinon;

      var geometry = system.getOrCreateGeometry(data);
      var disposeSpy = sinon.spy(geometry, 'dispose');

      system.getOrCreateGeometry(data);
      system.unuseGeometry(data);
      assert.notOk(disposeSpy.called);
      assert.ok(system.cache[hash]);
      assert.equal(system.cacheCount[hash], 1);
    });
  });
});
<|endoftext|>


<commit_before>const fs = require('fs');
const { readdir, writeFile } = fs.promises;
const { ensureDir } = require('fs-extra');
const path = require('path');
const babel = require('@babel/core');

module.exports = async paths => {
  let componentDirIndex = 0;
  const tempComponentDir = path.join(paths.TMP_DIR, 'components');
  await ensureDir(tempComponentDir);
  for (const dir of paths.COMPONENT_DIRS) {
    const tempComponentSubdir = path.join(
      tempComponentDir,
      `${componentDirIndex}`
    );
    await ensureDir(tempComponentSubdir);
    const files = await readdir(dir);
    for (const file of files) {
      const { code } = await babel.transformFileAsync(path.join(dir, file));
      await writeFile(
        path.join(tempComponentSubdir, path.basename(file)),
        code,
        'utf-8'
      );
    }
    paths.COMPONENT_DIRS[componentDirIndex] = tempComponentSubdir;
    componentDirIndex++;
  }

  return paths;
};
<commit_msg>fix: explicitly include babel react preset<commit_after>const fs = require('fs');
const { readdir, writeFile } = fs.promises;
const { ensureDir } = require('fs-extra');
const path = require('path');
const babel = require('@babel/core');

module.exports = async paths => {
  let componentDirIndex = 0;
  const tempComponentDir = path.join(paths.TMP_DIR, 'components');
  await ensureDir(tempComponentDir);
  for (const dir of paths.COMPONENT_DIRS) {
    const tempComponentSubdir = path.join(
      tempComponentDir,
      `${componentDirIndex}`
    );
    await ensureDir(tempComponentSubdir);
    const files = await readdir(dir);
    for (const file of files) {
      const { code } = await babel.transformFileAsync(path.join(dir, file), {
        presets: ['@babel/preset-react']
      });
      await writeFile(
        path.join(tempComponentSubdir, path.basename(file)),
        code,
        'utf-8'
      );
    }
    paths.COMPONENT_DIRS[componentDirIndex] = tempComponentSubdir;
    componentDirIndex++;
  }

  return paths;
};
<|endoftext|>


<commit_before>const webpack = require('webpack');
const path = require('path');
const pkg = require('../package.json');

if (!process.env.APP_ID) {
  console.log('APP_ID environment variable must be set for webpack build!\n');
  process.exit(1);
}

module.exports = {
  devtool: 'source-map',
  context: path.join(__dirname, '../app'),
  entry: ['./renderer.js', './renderer.html'],
  output: {
    path: path.join(__dirname, '../build'),
    filename: 'bundle.js',
    libraryTarget: 'commonjs2',
  },
  module: {
    rules: [
      {
        test: /\.(js)$/,
        use: ['babel-loader'],
        exclude: [/node_modules/, /__fixtures__/, /__tests__/],
      },
      {
        test: /\.(less|css)$/,
        use: [
          'style-loader',
          { loader: 'css-loader', options: { importLoaders: 1 } },
          { loader: 'less-loader', options: { noIeCompat: true } },
        ],
      },
      {
        test: /\.(html|woff2)$/,
        loader: 'file-loader',
        options: {
          name: '[name].[ext]',
        },
      },
      {
        test: /\.(png|svg)$/,
        loader: 'url-loader',
      },
    ],
  },
  resolve: {
    alias: {
      // Create aliases for react-hot-loader
      // https://github.com/gaearon/react-hot-loader/tree/92961be0b44260d3d3f1b8864aa699766572a67c#linking
      'react-hot-loader': path.resolve(path.join(__dirname, '../node_modules/react-hot-loader')),
      'react': path.resolve(path.join(__dirname, '../node_modules/react')),
      'react-dom': path.resolve(path.join(__dirname, '../node_modules/@hot-loader/react-dom')),
    },
    extensions: ['.js', '.json'],
    mainFields: ['webpack', 'browser', 'web', 'browserify', ['jam', 'main'], 'main'],
  },
  node: {
    __dirname: false, // Use Node __dirname
  },
  externals: [
    // Omit all dependencies in app/package.json (we want them loaded at runtime via NodeJS)
    ...Object.keys(pkg.dependencies).filter(name => !pkg.packedDependencies.includes(name)),

    // To get jsonlint working...
    'file',
    'system',
  ],
  plugins: [
    new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
    new webpack.DefinePlugin({
      'process.env.APP_ID': JSON.stringify(process.env.APP_ID),
      'process.env.RELEASE_DATE': JSON.stringify(new Date()),
    }),
  ],
  target: 'electron-renderer',
};
<commit_msg>Create an alias for styled-components in Webpack for development<commit_after>const webpack = require('webpack');
const path = require('path');
const pkg = require('../package.json');

if (!process.env.APP_ID) {
  console.log('APP_ID environment variable must be set for webpack build!\n');
  process.exit(1);
}

module.exports = {
  devtool: 'source-map',
  context: path.join(__dirname, '../app'),
  entry: ['./renderer.js', './renderer.html'],
  output: {
    path: path.join(__dirname, '../build'),
    filename: 'bundle.js',
    libraryTarget: 'commonjs2',
  },
  module: {
    rules: [
      {
        test: /\.(js)$/,
        use: ['babel-loader'],
        exclude: [/node_modules/, /__fixtures__/, /__tests__/],
      },
      {
        test: /\.(less|css)$/,
        use: [
          'style-loader',
          { loader: 'css-loader', options: { importLoaders: 1 } },
          { loader: 'less-loader', options: { noIeCompat: true } },
        ],
      },
      {
        test: /\.(html|woff2)$/,
        loader: 'file-loader',
        options: {
          name: '[name].[ext]',
        },
      },
      {
        test: /\.(png|svg)$/,
        loader: 'url-loader',
      },
    ],
  },
  resolve: {
    alias: {
      // Create aliases for react-hot-loader
      // https://github.com/gaearon/react-hot-loader/tree/92961be0b44260d3d3f1b8864aa699766572a67c#linking
      'react-hot-loader': path.resolve(path.join(__dirname, '../node_modules/react-hot-loader')),
      'react': path.resolve(path.join(__dirname, '../node_modules/react')),
      'styled-components': path.resolve(path.join(__dirname, '../node_modules/styled-components')),
      'react-dom': path.resolve(path.join(__dirname, '../node_modules/@hot-loader/react-dom')),
    },
    extensions: ['.js', '.json'],
    mainFields: ['webpack', 'browser', 'web', 'browserify', ['jam', 'main'], 'main'],
  },
  node: {
    __dirname: false, // Use Node __dirname
  },
  externals: [
    // Omit all dependencies in app/package.json (we want them loaded at runtime via NodeJS)
    ...Object.keys(pkg.dependencies).filter(name => !pkg.packedDependencies.includes(name)),

    // To get jsonlint working...
    'file',
    'system',
  ],
  plugins: [
    new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
    new webpack.DefinePlugin({
      'process.env.APP_ID': JSON.stringify(process.env.APP_ID),
      'process.env.RELEASE_DATE': JSON.stringify(new Date()),
    }),
  ],
  target: 'electron-renderer',
};
<|endoftext|>


<commit_before>//Lightcurve plot

function LcPlot(id, plotConfig, getDataFromServerFn, onFiltersChangedFn, onPlotReadyFn, toolbar, cssClass, switchable) {

  var currentObj = this;

  Plot.call(this, id, plotConfig, getDataFromServerFn, onFiltersChangedFn, onPlotReadyFn, toolbar, cssClass, switchable);

  //LC plot attributes:
  this.settingsVisible = false;
  this.baselineEnabled = false;

  this.settingsPanel = $('
' +
                            '

Settings:

' +
                            '
' +
                              '
' +
                              '
' +
                              '
' +
                              '
' +
                            '
' +
                          '
');
  this.settingsPanel.hide();
  this.$html.prepend(this.settingsPanel);

  this.btnSettings = $('<button class="btn btn-default btnSettings' + this.id + '"></button>');
  this.$html.find(".plotTools").append(this.btnSettings);
  this.btnSettings.click(function(event){
    currentObj.showSettings();
  });

  this.btnBack = $('<button class="btn btn-default btnBack' + this.id + '"></button>');
  this.btnBack.hide();
  this.$html.find(".plotTools").append(this.btnBack);
  this.btnBack.click(function(event){
    currentObj.hideSettings();
  });

  //LC plot methods:

  this.setSettingsTitle = function (title) {
    this.settingsPanel.find(".title").find("h3").first().html(title);
  }

  this.showSettings = function(){
    if (!this.settingsVisible) {
      this.settingsVisible = true;
      this.setHoverDisablerEnabled(false);
      var height = parseInt(this.$html.find(".plot").height());
      this.$html.find(".plot").hide();
      this.$html.find(".plotTools").children().hide();
      this.btnBack.show();
      this.settingsPanel.show();
      this.settingsPanel.css({ 'height': height + 'px' });

      var title = 'Settings:';
      if (!isNull(this.plotConfig.styles.title)){
        title = this.plotConfig.styles.title + ' Settings:';
      }

      this.setSettingsTitle(title);

      var tab = getTabForSelector(this.id);

      if (this.settingsPanel.find(".baseline").length == 0) {

        var lamValue = 1000;
        var pValue = 0.01;
        var niterValue = 10;

        var $baseline = $('
' +
                            '
' +
                              'Draw baseline:' +
                              '
' +
                                '

' +
                              '
' +
                            '
' +
                            '
' +
                              '
Smoothness: <input id="lam_' + this.id + '" class="inputLam" type="text" name="lam_' + this.id + '" placeholder="' + lamValue + '" value="' + lamValue + '" /> 1-100000
' +
                              '
Asymmetry: <input id="p_' + this.id + '" class="inputP" type="text" name="p_' + this.id + '" placeholder="' + pValue + '" value="' + pValue + '" /> 0.001-1
' +
                              '
Nº iterations: <input id="niter_' + this.id + '" class="inputNiter" type="text" name="niter_' + this.id + '" placeholder="' + niterValue + '" value="' + niterValue + '" /> 1-1000
' +
                              '
Algorithm: Asymmetric Least Squares Smoothing (P. Eilers and H. Boelens, 2005)
' +
                            '
' +
                          '
');

        //Prepares switchBox
        var switchBox = $baseline.find("#switch_" + this.id);
        switchBox.click( function ( event ) {
          currentObj.baselineEnabled = !currentObj.baselineEnabled;
          currentObj.onBaselineValuesChanged();
          if (currentObj.baselineEnabled) {
            $(this).switchClass("fa-plus-square", "fa-minus-square");
            currentObj.settingsPanel.find(".baselineContainer").fadeIn();
          } else {
            $(this).switchClass("fa-minus-square", "fa-plus-square");
            currentObj.settingsPanel.find(".baselineContainer").fadeOut();
          }
        });

        //Prepares input events
        $baseline.find(".baselineContainer").hide();
        $baseline.find("input").on('change', this.onBaselineValuesChanged);

        this.settingsPanel.find(".leftCol").append($baseline);
      }
    }
  }

  this.hideSettings = function(){
    if (this.settingsVisible) {
      this.settingsVisible = false;
      this.setHoverDisablerEnabled(true);
      this.settingsPanel.hide();
      this.$html.find(".plot").show();
      this.$html.find(".plotTools").children().show();
      this.btnBack.hide();
      this.refreshData();
    }
  }

  this.onBaselineValuesChanged = function(){
    if (currentObj.baselineEnabled) {
      currentObj.plotConfig.baseline_opts.lam = getInputIntValueCropped(currentObj.settingsPanel.find(".inputLam"), currentObj.plotConfig.baseline_opts.lam, 1, 100000);
      currentObj.plotConfig.baseline_opts.p = getInputIntValueCropped(currentObj.settingsPanel.find(".inputP"), currentObj.plotConfig.baseline_opts.p, 0.001, 1);
      currentObj.plotConfig.baseline_opts.niter = getInputIntValueCropped(currentObj.settingsPanel.find(".inputNiter"), currentObj.plotConfig.baseline_opts.niter, 1, 1000);
    } else {
      currentObj.disableBaseline();
    }
  }

  this.updateBaselineInputs = function (bsOpts) {
    if (bsOpts.niter > 0) {
      currentObj.settingsPanel.find(".inputLam").val(bsOpts.lam);
      currentObj.settingsPanel.find(".inputP").val(bsOpts.p);
      currentObj.settingsPanel.find(".inputNiter").val(bsOpts.niter);
      currentObj.settingsPanel.find(".baselineContainer").show();
    } else {
      currentObj.settingsPanel.find(".baselineContainer").hide();
    }
  }

  this.disableBaseline = function () {
    currentObj.plotConfig.baseline_opts = { niter: 0 };
  }

  this.getPlotlyConfig = function (data) {
    var coords = currentObj.getSwitchedCoords( { x: 0, y: 1} );
    var plotlyConfig = get_plotdiv_lightcurve(data[0].values, data[1].values,
                                        [], data[2].values,
                                        (data.length > 4) ? currentObj.getWtiRangesFromGtis(data[3].values, data[4].values, data[0].values) : [],
                                        currentObj.plotConfig.styles.labels[coords.x],
                                        currentObj.plotConfig.styles.labels[coords.y],
                                        currentObj.plotConfig.styles.title);

    if (data.length > 5) {
      if (data[5].values.length > 0) {
        //Lightcurve has baseline values
        plotlyConfig.data.push({
                                type : 'scatter',
                                showlegend : false,
                                hoverinfo : 'none',
                                connectgaps : false,
                                x : data[0].values,
                                y : data[5].values,
                                line : {
                                        color : '#DD3333'
                                      }
                              });
      }
    } else {
      currentObj.btnSettings.hide();
    }

    plotlyConfig = currentObj.prepareAxis(plotlyConfig);

    return plotlyConfig;
  }

  this.getPlotDefaultTracesCount = function (){
      return (currentObj.data.length > 5 && currentObj.data[5].values.length > 0) ? 2 : 1;
  }

  //LC BaseLine parameters:
  this.disableBaseline();

  log ("new LcPlot id: " + this.id);

  return this;
}
<commit_msg>DAVE 238: Info in the baseline setting of light curves, code beautified<commit_after>//Lightcurve plot

function LcPlot(id, plotConfig, getDataFromServerFn, onFiltersChangedFn, onPlotReadyFn, toolbar, cssClass, switchable) {

  var currentObj = this;

  Plot.call(this, id, plotConfig, getDataFromServerFn, onFiltersChangedFn, onPlotReadyFn, toolbar, cssClass, switchable);

  //LC plot attributes:
  this.settingsVisible = false;
  this.baselineEnabled = false;

  this.baseline_opts = {};
  this.baseline_opts.lam = { default:1000, min:1, max: 100000}; //Baseline Smoothness ranges
  this.baseline_opts.p = { default:0.01, min:0.001, max: 1}; //Baseline Asymmetry ranges
  this.baseline_opts.niter = { default:10, min:1, max: 1000}; //Baseline N iterations ranges

  this.settingsPanel = $('
' +
                            '

Settings:

' +
                            '
' +
                              '
' +
                              '
' +
                              '
' +
                              '
' +
                            '
' +
                          '
');
  this.settingsPanel.hide();
  this.$html.prepend(this.settingsPanel);

  this.btnSettings = $('<button class="btn btn-default btnSettings' + this.id + '"></button>');
  this.$html.find(".plotTools").append(this.btnSettings);
  this.btnSettings.click(function(event){
    currentObj.showSettings();
  });

  this.btnBack = $('<button class="btn btn-default btnBack' + this.id + '"></button>');
  this.btnBack.hide();
  this.$html.find(".plotTools").append(this.btnBack);
  this.btnBack.click(function(event){
    currentObj.hideSettings();
  });

  //LC plot methods:

  this.setSettingsTitle = function (title) {
    this.settingsPanel.find(".title").find("h3").first().html(title);
  }

  this.showSettings = function(){
    if (!this.settingsVisible) {
      this.settingsVisible = true;
      this.setHoverDisablerEnabled(false);
      var height = parseInt(this.$html.find(".plot").height());
      this.$html.find(".plot").hide();
      this.$html.find(".plotTools").children().hide();
      this.btnBack.show();
      this.settingsPanel.show();
      this.settingsPanel.css({ 'height': height + 'px' });

      var title = 'Settings:';
      if (!isNull(this.plotConfig.styles.title)){
        title = this.plotConfig.styles.title + ' Settings:';
      }

      this.setSettingsTitle(title);

      var tab = getTabForSelector(this.id);

      if (this.settingsPanel.find(".baseline").length == 0) {

        var $baseline = $('
' +
                            '
' +
                              'Draw baseline:' +
                              '
' +
                                '

' +
                              '
' +
                            '
' +
                            '
' +
                              '
Smoothness: <input id="lam_' + this.id + '" class="inputLam" type="text" name="lam_' + this.id + '" placeholder="' + this.baseline_opts.lam.default + '" value="' + this.baseline_opts.lam.default + '" /> ' + this.baseline_opts.lam.min + '-' + this.baseline_opts.lam.max + '
' +
                              '
Asymmetry: <input id="p_' + this.id + '" class="inputP" type="text" name="p_' + this.id + '" placeholder="' + this.baseline_opts.p.default + '" value="' + this.baseline_opts.p.default + '" /> ' + this.baseline_opts.p.min + '-' + this.baseline_opts.p.max + '
' +
                              '
Nº iterations: <input id="niter_' + this.id + '" class="inputNiter" type="text" name="niter_' + this.id + '" placeholder="' + this.baseline_opts.niter.default + '" value="' + this.baseline_opts.niter.default + '" /> ' + this.baseline_opts.niter.min + '-' + this.baseline_opts.niter.max + '
' +
                              '
Algorithm: Asymmetric Least Squares Smoothing (P. Eilers and H. Boelens, 2005)
' +
                            '
' +
                          '
');

        //Prepares switchBox
        var switchBox = $baseline.find("#switch_" + this.id);
        switchBox.click( function ( event ) {
          currentObj.baselineEnabled = !currentObj.baselineEnabled;
          currentObj.onBaselineValuesChanged();
          if (currentObj.baselineEnabled) {
            $(this).switchClass("fa-plus-square", "fa-minus-square");
            currentObj.settingsPanel.find(".baselineContainer").fadeIn();
          } else {
            $(this).switchClass("fa-minus-square", "fa-plus-square");
            currentObj.settingsPanel.find(".baselineContainer").fadeOut();
          }
        });

        //Prepares input events
        $baseline.find(".baselineContainer").hide();
        $baseline.find("input").on('change', this.onBaselineValuesChanged);

        this.settingsPanel.find(".leftCol").append($baseline);
      }
    }
  }

  this.hideSettings = function(){
    if (this.settingsVisible) {
      this.settingsVisible = false;
      this.setHoverDisablerEnabled(true);
      this.settingsPanel.hide();
      this.$html.find(".plot").show();
      this.$html.find(".plotTools").children().show();
      this.btnBack.hide();
      this.refreshData();
    }
  }

  this.onBaselineValuesChanged = function(){
    if (currentObj.baselineEnabled) {
      currentObj.plotConfig.baseline_opts.lam = getInputIntValueCropped(currentObj.settingsPanel.find(".inputLam"), currentObj.plotConfig.baseline_opts.lam, currentObj.baseline_opts.lam.min, currentObj.baseline_opts.lam.max);
      currentObj.plotConfig.baseline_opts.p = getInputIntValueCropped(currentObj.settingsPanel.find(".inputP"), currentObj.plotConfig.baseline_opts.p, currentObj.baseline_opts.p.min, currentObj.baseline_opts.p.max);
      currentObj.plotConfig.baseline_opts.niter = getInputIntValueCropped(currentObj.settingsPanel.find(".inputNiter"), currentObj.plotConfig.baseline_opts.niter, currentObj.baseline_opts.niter.min, currentObj.baseline_opts.niter.max);
    } else {
      currentObj.disableBaseline();
    }
  }

  this.updateBaselineInputs = function (bsOpts) {
    if (bsOpts.niter > 0) {
      currentObj.settingsPanel.find(".inputLam").val(bsOpts.lam);
      currentObj.settingsPanel.find(".inputP").val(bsOpts.p);
      currentObj.settingsPanel.find(".inputNiter").val(bsOpts.niter);
      currentObj.settingsPanel.find(".baselineContainer").show();
    } else {
      currentObj.settingsPanel.find(".baselineContainer").hide();
    }
  }

  this.disableBaseline = function () {
    currentObj.plotConfig.baseline_opts = { niter: 0 };
  }

  this.getPlotlyConfig = function (data) {
    var coords = currentObj.getSwitchedCoords( { x: 0, y: 1} );
    var plotlyConfig = get_plotdiv_lightcurve(data[0].values, data[1].values,
                                        [], data[2].values,
                                        (data.length > 4) ? currentObj.getWtiRangesFromGtis(data[3].values, data[4].values, data[0].values) : [],
                                        currentObj.plotConfig.styles.labels[coords.x],
                                        currentObj.plotConfig.styles.labels[coords.y],
                                        currentObj.plotConfig.styles.title);

    if (data.length > 5) {
      if (data[5].values.length > 0) {
        //Lightcurve has baseline values
        plotlyConfig.data.push({
                                type : 'scatter',
                                showlegend : false,
                                hoverinfo : 'none',
                                connectgaps : false,
                                x : data[0].values,
                                y : data[5].values,
                                line : {
                                        color : '#DD3333'
                                      }
                              });
      }
    } else {
      currentObj.btnSettings.hide();
    }

    plotlyConfig = currentObj.prepareAxis(plotlyConfig);

    return plotlyConfig;
  }

  this.getPlotDefaultTracesCount = function (){
      return (currentObj.data.length > 5 && currentObj.data[5].values.length > 0) ? 2 : 1;
  }

  //LC BaseLine parameters:
  this.disableBaseline();

  log ("new LcPlot id: " + this.id);

  return this;
}
<|endoftext|>