<commit_before>export { Params, Base };

import * as ValueMax from "../ValueMax.js";
import * as Weights from "../Weights.js";

/**
 * Embedding (2d) layer parameters.
 *
 * @member {number} outChannels
 *   Output channel count. It is always depending on channelMultiplier and equals to ( inChannels * channelMultiplier ).
 */
class Params extends Weights.Params {

  /**
   * @param {number} channelMultiplier
   *   Every vocabulary will have how many embedding channels. Every input channel will be expanded into so many
   * embedding channels. If null, it will be extracted from inputFloat32Array (i.e. by evolution).
   *
   * @return {boolean} Return false, if initialization failed.
   *
   * @override
   */
  init( inputFloat32Array, byteOffsetBegin, inChannels, channelMultiplier = null ) {

//!!! ...unfinished...
// inverted residual connection (by add or by concatenate) ? (dense net)
// squeeze-and-excitation ?
// Shuffled Grouped Pointwise Convolution ... ? (by tf.gather() ?)

    let parameterMap = new Map( [
      [ Weights.Params.Keys.inChannels,        inChannels ],
      [ Weights.Params.Keys.channelMultiplier, channelMultiplier ],

      // For an embedding layer, its output channel count always depends on channelMultiplier.
      [ Weights.Params.Keys.outChannels,       Infinity ],
    ] );

    return super.init( inputFloat32Array, byteOffsetBegin, parameterMap );
  }

}

/**
 * Embedding could achieve non-linear mapping (just like any perceptron). But it is achieved by lookup table (instead
 * of weighted sum, bias and activation function). This implies:
 *   - It may use more (CPU or GPU) memory, but may use less (CPU or GPU) computation.
 *   - It can only achieve channel expansion, and can not achieve channel aggregation. (because no weighted sum)
 *   - It can only represent context-independent (not context-dependent) information. (because no weighted sum)
 *   - It can only handle integer input (i.e. int32, not float32).
 *
 * It is useful as the first layer of text or image processing because their inputs are all integer (e.g. character codes,
 * word indices, color codes, etc). And, the first layer only needs carry context-independent information (and all the other
 * layers after it will produce context-dependent information).
 *
 * This object always accepts tensor3d (dtype = int32).
 *   - The axis 0 is height. (Image height) (Text lines and usually only 1 line.)
 *   - The axis 1 is width. (Image width) (Text length (e.g. character count).)
 *   - The axis 2 is channel. (Image color channel) (Text character code channel and usually only 1 channel.)
 *
 * An embedding layer contains one params (this.params) and inChannels embedding vocabulary tables.
 *   - Every input channel has one embedding vocabulary table.
 *   - Every embedding vocabulary table has vocabularyCountPerInputChannel vocabularies.
 *   - Every vocabulary has channelMultiplier embedding channels.
 *
 *
 *
 * @member {number} vocabularyCountPerInputChannel
 *   Every input channel will have how many vocabularies. This is also vocabulary count per vocabulary table (because
 * every input channel has a vocabulary table). For an image data (R-G-B-A four channels), there will be 256
 * vocabularies per input channel because every channel is represented by one byte (8 bits) which has 2^8 = 256 kinds
 * of possible values.
 *
 * @member {boolean} bEmbedVocabularyId
 *   If true, one of embedding channels will be an auto-generated vocabulary id (i.e. 0, 1, 2, ...). So only
 * ( channelMultiplier - 1 ) embedding channels will be extracted from inputFloat32Array. The extra vocabulary id
 * channel achieves residual connection. Residual connection means apply_and_destroy_or_keep() will append (concatenate)
 * input to output. Since apply_and_destroy_or_keep()'s input is just vocabulary id (one channel or multiple channels),
 * pre-embedded vocabulary id inside the embedding table acheives the same effect by less computation (but more memory).
 *
 * @member {number} channelMultiplier
 *   Every vocabulary will have how many embedding channels. Every input channel will be expanded into so many
 * embedding channels. It could be viewed as embeddingChannelCountPerInputChannel.
 *
 * @member {number} outChannels
 *   Output channel count. It is always depending on channelMultiplier and equals to ( inChannels * channelMultiplier ).
 */
class Base {

  /**
   * Generator for initializing this object.
   *
   * @param {ValueMax.Percentage.Aggregate} progressParent
   *   Some new progressToAdvance will be created and added to progressParent. The created progressToAdvance will be
   * increased when every time advanced. The progressParent.getRoot() will be returned when every time yield.
   *
   * @param {Float32Array} inputFloat32Array
   *   A Float32Array whose values will be interpret as weights.
   *
   * @param {number} byteOffsetBegin
   *   The position to start to decode from the inputFloat32Array. This is relative to the inputFloat32Array.buffer
   * (not to the inputFloat32Array.byteOffset).
   *
   * @param {number} inChannels
   *   The input channel count.
   *
   * @param {number} channelMultiplier
   *   Every vocabulary will have how many embedding channels. Every input channel will be expanded into so many
   * embedding channels. If null, it will be extracted from inputFloat32Array (i.e. by evolution).
   *
   * @param {boolean} bKeepInputTensor
   *   If true, apply_and_destroy_or_keep() will not dispose inputTensor (i.e. keep). For example, for the branch of step 0 of ShuffleNetV2.
   * For another example, the input image should be shared across many neural networks.
   *
   * @yield {ValueMax.Percentage.Aggregate}
   *   Yield ( value = progressParent.getRoot() ) when ( done = false ).
   *
   * @yield {boolean}
   *   Yield ( value = true ) when ( done = true ) successfully.
   *   Yield ( value = false ) when ( done = true ) failed.
   */
  * initer(
    progressParent,
    inputFloat32Array, byteOffsetBegin,
    inChannels, channelMultiplier = null, vocabularyCountPerInputChannel = 256, bEmbedVocabularyId = true,
    bKeepInputTensor
  ) {

    // Estimate the maximum value of progress.
    let progressMax =
      1             // for extracting parameters from inputFloat32Array.
      + inChannels  // for extracting vocabulary table of every input channel from inputFloat32Array.
      + inChannels; // for building vocabulary table tensor2d of every input channel.

    let progressRoot = progressParent.getRoot();
    let progressToAdvance = progressParent.addChild( new ValueMax.Percentage.Concrete( progressMax ) );

//!!! ...unfinished...
// inverted residual connection (by add or by concatenate) ? (dense net)
// squeeze-and-excitation ?
// Shuffled Grouped Pointwise Convolution ... ? (by tf.gather() ?)

    disposeTensors();
    this.params = this.vocabularyTables = null; // So that distinguishable if re-initialization failed.

    this.vocabularyCountPerInputChannel = vocabularyCountPerInputChannel;
    this.bEmbedVocabularyId = bEmbedVocabularyId;
    this.bKeepInputTensor = bKeepInputTensor;

    this.params = new Params();
    if ( !this.params.init( inputFloat32Array, byteOffsetBegin, inChannels, channelMultiplier ) )
      return false;

    ++progressToAdvance.value;
    yield progressRoot;  // Parameters extracted. Report progress.

    let channelMultiplier = this.channelMultiplier; // The real channelMultiplier. May be specified or extracted.
    if ( channelMultiplier < 1 )
      return false; // At least, there should be one embedding channel.

    let vocabularyTableShape_toExtract;
    if ( bEmbedVocabularyId ) {
      // If there will be an auto-generated vocabulary id embedding channel, extract one less channels from data.
      vocabularyTableShape_toExtract = [ vocabularyCountPerInputChannel, channelMultiplier - 1 ];
    } else {
      // Otherwise, all embedding channels are extracted from data.
      vocabularyTableShape_toExtract = [ vocabularyCountPerInputChannel, channelMultiplier ];
    }

    // Extract data of vocabulary tables from inputFloat32Array.
    this.vocabularyTables = new Array( inChannels );
    {
      let nextByteOffsetBegin = this.params.defaultByteOffsetEnd;
      for ( let i = 0; i < inChannels; ++i ) {
        this.vocabularyTables[ i ] = new Weights.Base();
        if ( !this.vocabularyTables[ i ].init( inputFloat32Array, nextByteOffsetBegin, null, 0, vocabularyTableShape_toExtract ) )
          return false;  // e.g. input array do not have enough data.
        nextByteOffsetBegin = this.vocabularyTables[ i ].defaultByteOffsetEnd;

        ++progressToAdvance.value;
        yield progressRoot;  // One vocabulary table extracted. Report progress.
      }
    }

    // Build tf.tensor of vocabulary tables.
    try {
      this.vocabularyTablesTensor3dArray = new Array( this.vocabularyTables.length );

      // Need to prefix vocabulary id channel.
      if ( bEmbedVocabularyId ) {

        // Create vocabulary id list (tensor3d). (for concatenating with vocabulary table)
        let numberSequencer = new Array( vocabularyCountPerInputChannel ).keys(); // Generator: 0, 1, 2, ..., ( vocabularyCountPerInputChannel - 1 )
        const idsTensor3d = tf.tensor3d( [ ...numberSequencer ], [ vocabularyCountPerInputChannel, 1, 1 ] );

        try {
          let theLastAxisId = ( vocabularyTableShape_toExtract.length - 1 ); // e.g. will be 2 for tensor3d.
          for ( let i = 0; i < this.vocabularyTables.length; ++i ) {

            // Create an embedding vocabulary table (without vocabulary id).
            const vocabularyTableTensor3dWithoutIds = tf.tensor3d( this.vocabularyTables[ i ], vocabularyTableShape_toExtract );

            try { // Concatenate vocabulary id prefix vocabulary table (as residual connection).
              this.vocabularyTablesTensor3dArray[ i ] = idsTensor3d.concat( vocabularyTableTensor3dWithoutIds, theLastAxisId );

            } finally {
              vocabularyTableTensor3dWithoutIds.dispose();
            }

            ++progressToAdvance.value;
            yield progressRoot;  // One vocabulary table tensor3d built. Report progress.
          }

        } finally {
          idsTensor3d.dispose();
        }

      } else { // No need to prefix vocabulary id channel.

        for ( let i = 0; i < this.vocabularyTables.length; ++i ) {
          // Create an embedding vocabulary table (without vocabulary id).
          this.vocabularyTablesTensor3dArray[ i ] = tf.tensor3d( this.vocabularyTables[ i ], vocabularyTableShape_toExtract );

          ++progressToAdvance.value;
          yield progressRoot;  // One vocabulary table tensor2d built. Report progress.
        }
      }

    } catch ( e ) {
      return false; // e.g. out of (GPU) memory.
    }

    return true; // Initialized successfully.
  }

  /** @return {boolean} Return true if this object initialized (i.e. initer()) successfully. */
  isValid() {
    if ( this.vocabularyTablesTensor3dArray )
      if ( this.vocabularyTablesTensor3dArray[ this.params.inChannels - 1 ] ) // At least, there should be one vocabulary table.
        if ( this.vocabularyTablesTensor3dArray[ this.params.inChannels - 1 ].isValid() )  // the last vocabulary table is valid.
          return true;
    return false;
  }

  /** Release tf.tensor. */
  disposeTensors() {
    if ( this.vocabularyTablesTensor3dArray ) {
      Base.disposeTensorArray_NotNull( this.vocabularyTablesTensor3dArray );
      this.vocabularyTablesTensor3dArray = null;
    }
  }

  /**
   * Process the input and produce output by using the weights of this neural network layer.
   *
   * @param {tf.tensor3d} inputTensor3d
   *   A tensor3d data (e.g. height-width-color for color image, or 1-width-1 for text) with this.inChannels
   * (e.g. 4 for r-g-b-a, or 1 for text) channels. The inputTensor3d.dtype must be int32 (i.e. can not be float32)
   * so that they can be used as tf.gather()'s indices. If ( this.bKeepInputTensor == false ), this inputTensor3d
   * will be disposed. If ( this.bKeepInputTensor == true ), this inputTensor3d will be kept.
   *
   * @return {tf.tensor3d} The predicted output as tensor3d. Return null, if failed (e.g. out of GPU memory).
   */
  apply_and_destroy_or_keep( inputTensor3d ) {

//!!! ...unfinished... could use gahter, gather, concat instead of split, gather, concat?

    // For example, suppose input is a color image (i.e. height-width-color tensor3d). The last
    // axis is a 4 color (r-g-b-a) channel. Splitting along the last axis (the color channel)
    // results in an array [ r, g, b, a ] which has 4 tensor3d (in fact, they should be
    // viewed as tensor1d).
    let theLastAxisId = ( inputTensor3d.shape.length - 1 );  // Or, ( inputTensor3d.rank - 1 )

    // For a 4 color (r-g-b-a) channel image, splitCount will be 4.
    //
    // This should be the same as this.inChannels.
    let splitCount = inputTensor3d.shape[ theLastAxisId ];

    // Extract vocabulary indices from input.
    //
    // Split the last axis (of input) as many as the shape size (of the last axis) (i.e. become tensor2d).
    // In fact, the result is still tensor3d but has only one channel.
    const vocabularyIndicesOneChannelTensor3dArray = inputTensor3d.split( splitCount, theLastAxisId );

//!!! ...unfinished... could using function pointer to avoid conditional-branching?
    if ( !this.bKeepInputTensor )
      inputTensor3d.dispose();

    try {
//!!! ...unfinished... could re-use this array shell (without re-allocating every time apply_and_destroy_or_keep() is called)?
      let embeddedTensor3dArray = new Array( vocabularyIndicesOneChannelTensor3dArray.length );

      try {

        // Embedding (looking up different vocabulary tables according to channel index of vocabulary indices).
        // Every tensor3d (one channel) will be expanded to tensor3d (multiple channels).
        for ( let channelIndex = 0; channelIndex < vocabularyIndicesOneChannelTensor3dArray.length; ++channelIndex ) {
          let oneChannelTensor3d = vocabularyIndicesOneChannelTensor3dArray[ channelIndex ];
          embeddedTensor3dArray[ channelIndex ] = this.vocabularyTablesTensor3dArray[ channelIndex ].gather( oneChannelTensor3d );
        }

        // Concatenate along the last axis, so that it is still tensor3d but with embedded (more) channels in the last axis.
        let predictResult = tf.concat( embeddedTensor3dArray, theLastAxisId );
        return predictResult;

      } catch ( e ) {
        throw e;

      } finally {
        Base.disposeTensorArray_NotNull( embeddedTensor3dArray ); // Avoid tensors' memory leakage.
        //embeddedTensor3dArray = null;
      }

    } catch ( e ) {
      throw e;

    } finally {
      Base.disposeTensorArray_NotNull( vocabularyIndicesOneChannelTensor3dArray ); // Avoid tensors' memory leakage.
      //vocabularyIndicesOneChannelTensor3dArray = null;
    }

//!!! ...unfinished... squeeze-and-excitation.
  }

  /**
   * Release an array of tf.tensor.
   *
   * This method is a little like tf.dispose() but can only handle one-layer array. But it should be faster than
   * tf.dispose( an_array ) because no dynamic memory allocation.
   *
   * @param {tf.tensor[]} tensorArray
   *   An array contains tf.tensor to be disposed. The tensorArray itself can not be null (for reducing conditional-branch
   * to improve performance). It can not have nested array. Its element must be either tf.tensor or null.
   */
  static disposeTensorArray_NotNull( tensorArray ) {
    for ( let i = 0; i < tensorArray.length; ++i ) {
      let t = tensorArray[ i ];
      if ( t )
        t.dispose();
    }
  }

  get byteOffsetBegin() { return this.params.defaultByteOffsetBegin; }
  get byteOffsetEnd()   { return this.vocabularyTables[ this.params.inChannels - 1 ].defaultByteOffsetEnd; }

  get inChannels()        { return this.params.inChannels; }
  get channelMultiplier() { return this.params.channelMultiplier; }
  get outChannels()       { return this.params.outChannels; }
}
<commit_msg>Update Embedding2d.js<commit_after>export { Params, Base };

import * as ValueMax from "../ValueMax.js";
import * as Weights from "../Weights.js";

/**
 * Embedding (2d) layer parameters.
 *
 * @member {number} outChannels
 *   Output channel count. It is always depending on channelMultiplier and equals to ( inChannels * channelMultiplier ).
 */
class Params extends Weights.Params {

  /**
   * @param {number} channelMultiplier
   *   Every vocabulary will have how many embedding channels. Every input channel will be expanded into so many
   * embedding channels. If null, it will be extracted from inputFloat32Array (i.e. by evolution).
   *
   * @return {boolean} Return false, if initialization failed.
   *
   * @override
   */
  init( inputFloat32Array, byteOffsetBegin, inChannels, channelMultiplier = null ) {

//!!! ...unfinished...
// inverted residual connection (by add or by concatenate) ? (dense net)
// squeeze-and-excitation ?
// Shuffled Grouped Pointwise Convolution ... ? (by tf.gather() ?)

    let parameterMap = new Map( [
      [ Weights.Params.Keys.inChannels,        inChannels ],
      [ Weights.Params.Keys.channelMultiplier, channelMultiplier ],

      // For an embedding layer, its output channel count always depends on channelMultiplier.
      [ Weights.Params.Keys.outChannels,       Infinity ],
    ] );

    return super.init( inputFloat32Array, byteOffsetBegin, parameterMap );
  }

}

/**
 * Embedding could achieve non-linear mapping (just like any perceptron). But it is achieved by lookup table (instead
 * of weighted sum, bias and activation function). This implies:
 *   - It may use more (CPU or GPU) memory, but may use less (CPU or GPU) computation.
 *   - It can only achieve channel expansion, and can not achieve channel aggregation. (because no weighted sum)
 *   - It can only represent context-independent (not context-dependent) information. (because no weighted sum)
 *   - It can only handle integer input (i.e. int32, not float32).
 *
 * It is useful as the first layer of text or image processing because their inputs are all integer (e.g. character codes,
 * word indices, color codes, etc). And, the first layer only needs carry context-independent information (and all the other
 * layers after it will produce context-dependent information).
 *
 * This object always accepts tensor3d (dtype = int32).
 *   - The axis 0 is height. (Image height) (Text lines and usually only 1 line.)
 *   - The axis 1 is width. (Image width) (Text length (e.g. character count).)
 *   - The axis 2 is channel. (Image color channel) (Text character code channel and usually only 1 channel.)
 *
 * An embedding layer contains one params (this.params) and inChannels embedding vocabulary tables.
 *   - Every input channel has one embedding vocabulary table.
 *   - Every embedding vocabulary table has vocabularyCountPerInputChannel vocabularies.
 *   - Every vocabulary has channelMultiplier embedding channels.
 *
 *
 *
 * @member {number} vocabularyCountPerInputChannel
 *   Every input channel will have how many vocabularies. This is also vocabulary count per vocabulary table (because
 * every input channel has a vocabulary table). For an image data (R-G-B-A four channels), there will be 256
 * vocabularies per input channel because every channel is represented by one byte (8 bits) which has 2^8 = 256 kinds
 * of possible values.
 *
 * @member {boolean} bEmbedVocabularyId
 *   If true, one of embedding channels will be an auto-generated vocabulary id (i.e. 0, 1, 2, ...). So only
 * ( channelMultiplier - 1 ) embedding channels will be extracted from inputFloat32Array. The extra vocabulary id
 * channel achieves residual connection. Residual connection means apply_and_destroy_or_keep() will append (concatenate)
 * input to output. Since apply_and_destroy_or_keep()'s input is just vocabulary id (one channel or multiple channels),
 * pre-embedded vocabulary id inside the embedding table acheives the same effect by less computation (but more memory).
 *
 * @member {number} channelMultiplier
 *   Every vocabulary will have how many embedding channels. Every input channel will be expanded into so many
 * embedding channels. It could be viewed as embeddingChannelCountPerInputChannel.
 *
 * @member {number} outChannels
 *   Output channel count. It is always depending on channelMultiplier and equals to ( inChannels * channelMultiplier ).
 */
class Base {

  /**
   * Generator for initializing this object.
   *
   * @param {ValueMax.Percentage.Aggregate} progressParent
   *   Some new progressToAdvance will be created and added to progressParent. The created progressToAdvance will be
   * increased when every time advanced. The progressParent.getRoot() will be returned when every time yield.
   *
   * @param {Float32Array} inputFloat32Array
   *   A Float32Array whose values will be interpret as weights.
   *
   * @param {number} byteOffsetBegin
   *   The position to start to decode from the inputFloat32Array. This is relative to the inputFloat32Array.buffer
   * (not to the inputFloat32Array.byteOffset).
   *
   * @param {number} inChannels
   *   The input channel count.
   *
   * @param {number} channelMultiplier
   *   Every vocabulary will have how many embedding channels. Every input channel will be expanded into so many
   * embedding channels. If null, it will be extracted from inputFloat32Array (i.e. by evolution).
   *
   * @param {boolean} bKeepInputTensor
   *   If true, apply_and_destroy_or_keep() will not dispose inputTensor (i.e. keep). For example, for the branch of step 0 of ShuffleNetV2.
   * For another example, the input image should be shared across many neural networks.
   *
   * @yield {ValueMax.Percentage.Aggregate}
   *   Yield ( value = progressParent.getRoot() ) when ( done = false ).
   *
   * @yield {boolean}
   *   Yield ( value = true ) when ( done = true ) successfully.
   *   Yield ( value = false ) when ( done = true ) failed.
   */
  * initer(
    progressParent,
    inputFloat32Array, byteOffsetBegin,
    inChannels, channelMultiplier = null, vocabularyCountPerInputChannel = 256, bEmbedVocabularyId = true,
    bKeepInputTensor
  ) {

    // Estimate the maximum value of progress.
    let progressMax =
      1             // for extracting parameters from inputFloat32Array.
      + inChannels  // for extracting vocabulary table of every input channel from inputFloat32Array.
      + inChannels; // for building vocabulary table tensor2d of every input channel.

    let progressRoot = progressParent.getRoot();
    let progressToAdvance = progressParent.addChild( new ValueMax.Percentage.Concrete( progressMax ) );

//!!! ...unfinished...
// inverted residual connection (by add or by concatenate) ? (dense net)
// squeeze-and-excitation ?
// Shuffled Grouped Pointwise Convolution ... ? (by tf.gather() ?)

    disposeTensors();
    this.params = this.vocabularyTables = null; // So that distinguishable if re-initialization failed.

    this.vocabularyCountPerInputChannel = vocabularyCountPerInputChannel;
    this.bEmbedVocabularyId = bEmbedVocabularyId;
    this.bKeepInputTensor = bKeepInputTensor;

    this.params = new Params();
    if ( !this.params.init( inputFloat32Array, byteOffsetBegin, inChannels, channelMultiplier ) )
      return false;

    ++progressToAdvance.value;
    yield progressRoot;  // Parameters extracted. Report progress.

    let channelMultiplier = this.channelMultiplier; // The real channelMultiplier. May be specified or extracted.
    if ( channelMultiplier < 1 )
      return false; // At least, there should be one embedding channel.

    let vocabularyTableShape_toExtract;
    if ( bEmbedVocabularyId ) {
      // If there will be an auto-generated vocabulary id embedding channel, extract one less channels from data.
      vocabularyTableShape_toExtract = [ vocabularyCountPerInputChannel, 1, channelMultiplier - 1 ];
    } else {
      // Otherwise, all embedding channels are extracted from data.
      vocabularyTableShape_toExtract = [ vocabularyCountPerInputChannel, 1, channelMultiplier ];
    }

    // Extract data of vocabulary tables from inputFloat32Array.
    this.vocabularyTables = new Array( inChannels );
    {
      let nextByteOffsetBegin = this.params.defaultByteOffsetEnd;
      for ( let i = 0; i < inChannels; ++i ) {
        this.vocabularyTables[ i ] = new Weights.Base();
        if ( !this.vocabularyTables[ i ].init( inputFloat32Array, nextByteOffsetBegin, null, 0, vocabularyTableShape_toExtract ) )
          return false;  // e.g. input array do not have enough data.
        nextByteOffsetBegin = this.vocabularyTables[ i ].defaultByteOffsetEnd;

        ++progressToAdvance.value;
        yield progressRoot;  // One vocabulary table extracted. Report progress.
      }
    }

    // Build tf.tensor of vocabulary tables.
    try {
      this.vocabularyTablesTensor3dArray = new Array( this.vocabularyTables.length );

      // Need to prefix vocabulary id channel.
      if ( bEmbedVocabularyId ) {

        // Create vocabulary id list (tensor3d). (for concatenating with vocabulary table)
        let numberSequencer = new Array( vocabularyCountPerInputChannel ).keys(); // Generator: 0, 1, 2, ..., ( vocabularyCountPerInputChannel - 1 )
        const idsTensor3d = tf.tensor3d( [ ...numberSequencer ], [ vocabularyCountPerInputChannel, 1, 1 ] );

        try {
          let theLastAxisId = ( vocabularyTableShape_toExtract.length - 1 ); // e.g. will be 2 for tensor3d.
          for ( let i = 0; i < this.vocabularyTables.length; ++i ) {

            // Create an embedding vocabulary table (without vocabulary id).
            const vocabularyTableTensor3dWithoutIds = tf.tensor3d( this.vocabularyTables[ i ], vocabularyTableShape_toExtract );

            try { // Concatenate vocabulary id prefix vocabulary table (as residual connection).
              this.vocabularyTablesTensor3dArray[ i ] = idsTensor3d.concat( vocabularyTableTensor3dWithoutIds, theLastAxisId );

            } finally {
              vocabularyTableTensor3dWithoutIds.dispose();
            }

            ++progressToAdvance.value;
            yield progressRoot;  // One vocabulary table tensor3d built. Report progress.
          }

        } finally {
          idsTensor3d.dispose();
        }

      } else { // No need to prefix vocabulary id channel.

        for ( let i = 0; i < this.vocabularyTables.length; ++i ) {
          // Create an embedding vocabulary table (without vocabulary id).
          this.vocabularyTablesTensor3dArray[ i ] = tf.tensor3d( this.vocabularyTables[ i ], vocabularyTableShape_toExtract );

          ++progressToAdvance.value;
          yield progressRoot;  // One vocabulary table tensor2d built. Report progress.
        }
      }

    } catch ( e ) {
      return false; // e.g. out of (GPU) memory.
    }

    return true; // Initialized successfully.
  }

  /** @return {boolean} Return true if this object initialized (i.e. initer()) successfully. */
  isValid() {
    if ( this.vocabularyTablesTensor3dArray )
      if ( this.vocabularyTablesTensor3dArray[ this.params.inChannels - 1 ] ) // At least, there should be one vocabulary table.
        if ( this.vocabularyTablesTensor3dArray[ this.params.inChannels - 1 ].isValid() )  // the last vocabulary table is valid.
          return true;
    return false;
  }

  /** Release tf.tensor. */
  disposeTensors() {
    if ( this.vocabularyTablesTensor3dArray ) {
      Base.disposeTensorArray_NotNull( this.vocabularyTablesTensor3dArray );
      this.vocabularyTablesTensor3dArray = null;
    }
  }

  /**
   * Process the input and produce output by using the weights of this neural network layer.
   *
   * @param {tf.tensor3d} inputTensor3d
   *   A tensor3d data (e.g. height-width-color for color image, or 1-width-1 for text) with this.inChannels
   * (e.g. 4 for r-g-b-a, or 1 for text) channels. The inputTensor3d.dtype must be int32 (i.e. can not be float32)
   * so that they can be used as tf.gather()'s indices. If ( this.bKeepInputTensor == false ), this inputTensor3d
   * will be disposed. If ( this.bKeepInputTensor == true ), this inputTensor3d will be kept.
   *
   * @return {tf.tensor3d} The predicted output as tensor3d. Return null, if failed (e.g. out of GPU memory).
   */
  apply_and_destroy_or_keep( inputTensor3d ) {

//!!! ...unfinished... could use gahter, gather, concat instead of split, gather, concat?

    // For example, suppose input is a color image (i.e. height-width-color tensor3d). The last
    // axis is a 4 color (r-g-b-a) channel. Splitting along the last axis (the color channel)
    // results in an array [ r, g, b, a ] which has 4 tensor3d (in fact, they should be
    // viewed as tensor1d).
    let theLastAxisId = ( inputTensor3d.shape.length - 1 );  // Or, ( inputTensor3d.rank - 1 )

    // For a 4 color (r-g-b-a) channel image, splitCount will be 4.
    //
    // This should be the same as this.inChannels.
    let splitCount = inputTensor3d.shape[ theLastAxisId ];

    // Extract vocabulary indices from input.
    //
    // Split the last axis (of input) as many as the shape size (of the last axis) (i.e. become tensor2d).
    // In fact, the result is still tensor3d but has only one channel.
    const vocabularyIndicesOneChannelTensor3dArray = inputTensor3d.split( splitCount, theLastAxisId );

//!!! ...unfinished... could using function pointer to avoid conditional-branching?
    if ( !this.bKeepInputTensor )
      inputTensor3d.dispose();

    try {
//!!! ...unfinished... could re-use this array shell (without re-allocating every time apply_and_destroy_or_keep() is called)?
      let embeddedTensor3dArray = new Array( vocabularyIndicesOneChannelTensor3dArray.length );

      try {

        // Embedding (looking up different vocabulary tables according to channel index of vocabulary indices).
        // Every tensor3d (one channel) will be expanded to tensor3d (multiple channels).
        for ( let channelIndex = 0; channelIndex < vocabularyIndicesOneChannelTensor3dArray.length; ++channelIndex ) {
          let oneChannelTensor3d = vocabularyIndicesOneChannelTensor3dArray[ channelIndex ];
          embeddedTensor3dArray[ channelIndex ] = this.vocabularyTablesTensor3dArray[ channelIndex ].gather( oneChannelTensor3d );
        }

        // Concatenate along the last axis, so that it is still tensor3d but with embedded (more) channels in the last axis.
        let predictResult = tf.concat( embeddedTensor3dArray, theLastAxisId );
        return predictResult;

      } catch ( e ) {
        throw e;

      } finally {
        Base.disposeTensorArray_NotNull( embeddedTensor3dArray ); // Avoid tensors' memory leakage.
        //embeddedTensor3dArray = null;
      }

    } catch ( e ) {
      throw e;

    } finally {
      Base.disposeTensorArray_NotNull( vocabularyIndicesOneChannelTensor3dArray ); // Avoid tensors' memory leakage.
      //vocabularyIndicesOneChannelTensor3dArray = null;
    }

//!!! ...unfinished... squeeze-and-excitation.
  }

  /**
   * Release an array of tf.tensor.
   *
   * This method is a little like tf.dispose() but can only handle one-layer array. But it should be faster than
   * tf.dispose( an_array ) because no dynamic memory allocation.
   *
   * @param {tf.tensor[]} tensorArray
   *   An array contains tf.tensor to be disposed. The tensorArray itself can not be null (for reducing conditional-branch
   * to improve performance). It can not have nested array. Its element must be either tf.tensor or null.
   */
  static disposeTensorArray_NotNull( tensorArray ) {
    for ( let i = 0; i < tensorArray.length; ++i ) {
      let t = tensorArray[ i ];
      if ( t )
        t.dispose();
    }
  }

  get byteOffsetBegin() { return this.params.defaultByteOffsetBegin; }
  get byteOffsetEnd()   { return this.vocabularyTables[ this.params.inChannels - 1 ].defaultByteOffsetEnd; }

  get inChannels()        { return this.params.inChannels; }
  get channelMultiplier() { return this.params.channelMultiplier; }
  get outChannels()       { return this.params.outChannels; }
}
<|endoftext|>


<commit_before>var AddOffer = React.createClass({

  getInitialState: function() {
    return {
      type: 'info',
      message: ''
    }
  },

  // form submit callback
  handleSubmit: function(e) {
    e.preventDefault();

    this.setState({ type: 'info', message: 'Sending...' }, this.sendFormData);
  },

  sendFormData: function() {

    // prepare form data for submitting it
    var formData = {
      company_name: ReactDOM.findDOMNode(this.refs.budget).value,
      position: ReactDOM.findDOMNode(this.refs.position).value,
      salary: ReactDOM.findDOMNode(this.refs.salary).value,
      equity: ReactDOM.findDOMNode(this.refs.equity).value,
      vesting_start_date: ReactDOM.findDOMNode(this.refs.vesting_start_date).value,
      vesting_end_date: ReactDOM.findDOMNode(this.refs.vesting_end_date).value,
      vesting_cliff_date: ReactDOM.findDOMNode(this.refs.vesting_cliff_date).value,
      vesting_cliff_percent: ReactDOM.findDOMNode(this.refs.vesting_cliff_percent).value,
      other_benefits: ReactDOM.findDOMNode(this.refs.other_benefits).value,
      last_financing_round_valuation: ReactDOM.findDOMNode(this.refs.last_financing_round_valuation).value,
      estimated_eit_valuation: ReactDOM.findDOMNode(this.refs.estimated_eit_valuation).value
    };

    // extract the checkbox values
    formData.benefits = this.getSelected('benefits');

    // send the form data
    var xmlhttp = new XMLHttpRequest();
    var _this = this;
    xmlhttp.onreadystatechange = function() {
      if (xmlhttp.readyState === 4) {
        var response = JSON.parse(xmlhttp.responseText);
        if (xmlhttp === 200 && response.status === 'OK') {
          _this.setState({ type: 'success', message: 'Offer submitted...'});
        }
        else {
          _this.setState({ type: 'danger', message: 'Offer not submitted...'});
        }
      }
    };
    xmlhttp.open('POST', 'send', true);
    xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    xmlhttp.send(this.requestBuildQueryString(formData));
  },

  requestBuildQueryString: function(params) {
    var queryString = [];
    for (var property in params) {
      if (params.hasOwnProperty(property)) {
        queryString.push(encodeURIComponent(property) + '=' + encodeURIComponent(params[property]));
      }
    }
    return queryString.join('&');
  },

  getSelected: function(fieldName) {
    var i;
    var fields = document.getElementByName(fieldName);
    var selectedFields = [];
    for (i = 0; i < fields.length; i++) {
      if (fields[i].checked === true) {
        selectedFields.push(fields[i].value);
      }
    }
    return selectedFields.join(', ');
  },

  render: function() {
    if (this.state.type && this.state.message) {
      var classString = 'alert alert-' + this.state.type;
      var status =
{this.state.message}

    }
    return (

Add Your Offer

        {status}
        <form action="" onSubmit={this.handleSubmit}>

            Startup Name *
            <input className="form-control" name="company_name" ref="company_name" type="text" />

            Position *
            <input className="form-control" name="position" ref="position" type="text" />

            Salary *
            <input className="form-control" name="salary" ref="salary" type="text" />

            Equity *
            <input className="form-control" name="equity" ref="equity" type="text" />

            Vesting Start Date *
            <input className="form-control" name="vesting_start_date" ref="vesting_start_date" type="text" />

            Vesting End Date *
            <input className="form-control" name="vesting_end_date" ref="vesting_end_date" type="text" />

            Vesting Cliff Date *
            <input className="form-control" name="vesting_cliff_date" ref="vesting_cliff_date" type="text" />

            Vesting Cliff Percent *
            <input className="form-control" name="vesting_cliff_percent" ref="vesting_cliff_percent" type="text" />

            Most Recent Valuation *
            <input className="form-control" name="last_financing_round_valuation" ref="last_financing_round_valuation" type="text" />

            Estimated Exit Valuation *
            <input className="form-control" name="estimated_eit_valuation" ref="estimated_eit_valuation" type="text" />

What additional benefits do you receive?

            <input name="benefits" type="checkbox" value="food" />Food
            <input name="healthcare" type="checkbox" value="healthcare" />Healthcare

            <button className="btn btn-primary" type="submit">Add Offer</button>

        </form>

    )
  }
});

// POST request to server with all info listed above
// update tables: users, offers, companies

// GET request for metrics about the offer
// so we can display the offer results on the page

// var AddOffer = React.createClass({

//   handleSubmit: function(e) {
//     $.ajax({
//       url: this.props.url,
//       dataType: 'json',
//       type: 'POST',
//       data: comment,
//       success: function(data) {
//         this.setState({data: data});
//         console.log('POST')
//       }.bind(this),
//       error: function(xhr, status, err) {
//         console.error(this.props.url, status, err.toString());
//       }.bind(this)
//     });

//     e.preventDefault();
//     var company = this.refs.company.value.trim();
//     if (!company) {
//       return;
//     }

//     this.refs.company.value = '';
//     return;
//   },

//   render: function() {
//     return (

//       <form className="form-inline" onSubmit={this.handleSubmit}>
//

//

//             Startup:
//             <input type="text" className="form-control" id="company" placeholder="Which Startup?" ref="company" />
//

//           Salary (in dollars)

//

//
$

//             <input type="number" className="form-control" id="salary" />
//
.00

//

//           Equity Percentage

//

//             <input type="number" className="form-control" id="equity" />
//
%

//

//           Benefits:
//

//
//               <input type="checkbox" value="" /> Food
//
//
//               <input type="checkbox" /> Healthcare
//
//

//

//         <button type="submit" className="btn btn-primary" value="Post">Submit Offer</button>
//       </form>
//     )
//   }
// });

// var Offers = React.createClass({
//   getOffers: function() {
//     $.ajax({
//       url: this.props.url,
//       dataType: 'json',
//       cache: false,
//       success: function(data) {
//         this.setState({data: data});
//       }.bind(this),
//       error: function(xhr, status, err) {
//         console.error(this.props.url, status, err.toString());
//       }.bind(this)
//     });
//   }
// });

// var PostOffer = React.createClass()

    // note: in our formula to calculate the value of the offer, we should also include federal and state taxes

module.exports = {
  AddOffer: AddOffer
}
<commit_msg>refactoring into jquery<commit_after>var AddOffer = React.createClass({

  sendFormData: function(formData) {
    $.ajax({
      url: '/api/offer',
      dataType: 'json',
      type: 'POST',
      data: formData,
      success: function(data) {
        console.log('Success!!!!')
        this.setState({data: data});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error(this.props.url, status, err.toString());
      }.bind(this)
    });
  },

  render: function() {
    return (

Add Your Offer

        {status}
        <form action="" onSubmit={this.handleSubmit}>

            Startup Name *
            <input className="form-control" name="company_name" ref="company_name" type="text" />

            Position *
            <input className="form-control" name="position" ref="position" type="text" />

            Salary *
            <input className="form-control" name="salary" ref="salary" type="text" />

            Equity *
            <input className="form-control" name="equity" ref="equity" type="text" />

            Vesting Start Date *
            <input className="form-control" name="vesting_start_date" ref="vesting_start_date" type="text" />

            Vesting End Date *
            <input className="form-control" name="vesting_end_date" ref="vesting_end_date" type="text" />

            Vesting Cliff Date *
            <input className="form-control" name="vesting_cliff_date" ref="vesting_cliff_date" type="text" />

            Vesting Cliff Percent *
            <input className="form-control" name="vesting_cliff_percent" ref="vesting_cliff_percent" type="text" />

            Most Recent Valuation *
            <input className="form-control" name="last_financing_round_valuation" ref="last_financing_round_valuation" type="text" />

            Estimated Exit Valuation *
            <input className="form-control" name="estimated_eit_valuation" ref="estimated_eit_valuation" type="text" />

What additional benefits do you receive?

            <input name="benefits" type="checkbox" value="food" />Food
            <input name="healthcare" type="checkbox" value="healthcare" />Healthcare

            <button className="btn btn-primary" type="submit">Add Offer</button>

        </form>

    )
  }
});

module.exports = {
  AddOffer: AddOffer
}

<|endoftext|>


<commit_before>var tools = require(__dirname + '/../tools.js');

function detect(ip, device, options, callback) {
    // options.newInstances
    // options.existingInstances
    // device - additional info about device
    // options.log - logger
    // options.enums - {
    //      enum.rooms: {
    //          enum.rooms.ROOM1: {
    //              common: name
    //          }
    //      },
    //      enum.functions: {}
    // }

    // try to test TCP ports 2000, 2001, 2002
    var count = 0;
    // test hs485d
    var someFound = false;
    var name = ip + (device._name ? (' - ' + device._name) : '');

    // InfluxDB HTTP
    //TODO: HTTPS and Auth
    count++;
    tools.testPort(ip, 8086, {
        onConnect: function (ip, port, client) {
            //options.log.debug('Got connection to InfluxDB on ' + ip + ':' + port);
            client.write(
                'GET /query?q=SHOW%20DATABASES HTTP/1.1\r\n' +
                'User-Agent: NodeJS XML-RPC Client\r\n' +
                'Accept: */*\r\n' +
                'Host: ' + ip + ':' + port + '\r\n' +
                '\r\n');
        },
        onReceive: function (data, ip, port, client) {
            //options.log.debug('Got data from InfluxDB on ' + ip + ':' + port + ': ' + data);
            return data && !!data.toString().match(/X-Influxdb-Version:[.\d]+/);
        }
    }, function (err, found, ip, port) {
        if (found) {
            var instance = tools.findInstance(options, 'influxdb', function (obj) {
                return obj.native.host === ip;
            });
            if (instance) found = false;

            if (found) {
                options.newInstances.push({
                    _id: tools.getNextInstanceID('influxdb', options),
                    common: {
                        name: 'influxdb',
                        title: 'InfluxDB (' + name + ')'
                    },
                    native: {
                        host: ip,
                    },
                    comment: {
                        add: ['InfluxDB (' + name + ')'],
/*                        inputs: [
                            {
                                name: 'native.user',
                                def: '',
                                type: 'text', // text, checkbox, number, select, password. Select requires
                                title: 'user' // see translation in words.js
                            },
                            {
                                name: 'native.password',
                                def: '',
                                type: 'password',
                                title: 'password' // see translation in words.js
                            }
                        ]*/
                    }
                });
                someFound = true;
            }
        }
        if (!--count) callback(null, someFound, ip);
    });

}

exports.detect  = detect;
exports.type    = 'ip';// make type=serial for USB sticks
exports.timeout = 700;
<commit_msg>fix InfluxDB detection again<commit_after>var tools = require(__dirname + '/../tools.js');

function detect(ip, device, options, callback) {
    // options.newInstances
    // options.existingInstances
    // device - additional info about device
    // options.log - logger
    // options.enums - {
    //      enum.rooms: {
    //          enum.rooms.ROOM1: {
    //              common: name
    //          }
    //      },
    //      enum.functions: {}
    // }

    // try to test TCP ports 2000, 2001, 2002
    var count = 0;
    // test hs485d
    var someFound = false;
    var name = ip + (device._name ? (' - ' + device._name) : '');

    // InfluxDB HTTP
    //TODO: HTTPS and Auth
    count++;
    tools.testPort(ip, 8086, {
        onConnect: function (ip, port, client) {
            //options.log.debug('Got connection to InfluxDB on ' + ip + ':' + port);
            client.write(
                'GET /query?q=SHOW%20DATABASES HTTP/1.1\r\n' +
                'User-Agent: NodeJS XML-RPC Client\r\n' +
                'Accept: */*\r\n' +
                'Host: ' + ip + ':' + port + '\r\n' +
                '\r\n');
        },
        onReceive: function (data, ip, port, client) {
            //options.log.debug('Got data from InfluxDB on ' + ip + ':' + port + ': ' + data);
            return data && !!data.toString().match(/X-Influxdb-Version:[.\d]+/g);
        }
    }, function (err, found, ip, port) {
        if (found) {
            var instance = tools.findInstance(options, 'influxdb', function (obj) {
                return obj.native.host === ip;
            });
            if (instance) found = false;

            if (found) {
                options.newInstances.push({
                    _id: tools.getNextInstanceID('influxdb', options),
                    common: {
                        name: 'influxdb',
                        title: 'InfluxDB (' + name + ')'
                    },
                    native: {
                        host: ip,
                    },
                    comment: {
                        add: ['InfluxDB (' + name + ')'],
/*                        inputs: [
                            {
                                name: 'native.user',
                                def: '',
                                type: 'text', // text, checkbox, number, select, password. Select requires
                                title: 'user' // see translation in words.js
                            },
                            {
                                name: 'native.password',
                                def: '',
                                type: 'password',
                                title: 'password' // see translation in words.js
                            }
                        ]*/
                    }
                });
                someFound = true;
            }
        }
        if (!--count) callback(null, someFound, ip);
    });

}

exports.detect  = detect;
exports.type    = 'ip';// make type=serial for USB sticks
exports.timeout = 700;
<|endoftext|>


<commit_before>// @flow

import moment from 'moment'

import { getFeedId } from '../../common/util/modules'

import type {Alert, AlertEntity, Project, RtdAlert, RtdEntity} from '../../types'

export const FILTERS = ['ACTIVE', 'FUTURE', 'ARCHIVED', 'DRAFT', 'ALL']

export const CAUSES = [
  'UNKNOWN_CAUSE',
  'TECHNICAL_PROBLEM',
  'STRIKE',
  'DEMONSTRATION',
  'ACCIDENT',
  'HOLIDAY',
  'WEATHER',
  'MAINTENANCE',
  'CONSTRUCTION',
  'POLICE_ACTIVITY',
  'MEDICAL_EMERGENCY',
  'OTHER_CAUSE'
]

export const modes = [
  {gtfsType: 0, name: 'Tram/LRT'},
  {gtfsType: 1, name: 'Subway/Metro'},
  {gtfsType: 2, name: 'Rail'},
  {gtfsType: 3, name: 'Bus'},
  {gtfsType: 4, name: 'Ferry'},
  {gtfsType: 5, name: 'Cable Car'},
  {gtfsType: 6, name: 'Gondola'},
  {gtfsType: 7, name: 'Funicular'}
]

export const SORT_OPTIONS = [
  {value: 'id:asc', label: 'Oldest'},
  {value: 'id:desc', label: 'Newest'},
  {value: 'title:asc', label: 'Title'},
  {value: 'title:desc', label: 'Title (reverse)'},
  {value: 'start:asc', label: 'Starts earliest'},
  {value: 'start:desc', label: 'Starts latest'},
  {value: 'end:asc', label: 'Ends earliest'},
  {value: 'end:desc', label: 'Ends latest'}
]

export const EFFECTS = [
  'UNKNOWN_EFFECT',
  'NO_SERVICE',
  'REDUCED_SERVICE',
  'SIGNIFICANT_DELAYS',
  'DETOUR',
  'ADDITIONAL_SERVICE',
  'MODIFIED_SERVICE',
  'STOP_MOVED',
  'OTHER_EFFECT'
]

export function filterAlertsByCategory (alerts: Array<Alert>, filter: string) {
  const now = moment()
  switch (filter) {
    case 'ALL':
      return alerts
    case 'ACTIVE':
      return alerts.filter((alert: Alert) =>
        moment(alert.start).isBefore(now) &&
        moment(alert.end).isAfter(now) &&
        alert.published
      )
    case 'FUTURE':
      return alerts.filter((alert: Alert) => moment(alert.start).isAfter(now))
    case 'ARCHIVED':
      return alerts.filter((alert: Alert) => moment(alert.end).isBefore(now))
    case 'DRAFT':
      return alerts.filter((alert: Alert) => !alert.published)
    default:
      return alerts
  }
}

export function mapRtdAlert (rtdAlert: RtdAlert, project: Project): Alert {
  const {
    Id: id,
    HeaderText: title,
    DescriptionText: description,
    Cause: cause,
    Effect: effect,
    EditedBy: editedBy,
    EditedDate: editedDate,
    Url: url,
    StartDateTime: start,
    EndDateTime: end,
    Published: published,
    ServiceAlertEntities: entities
  } = rtdAlert
  const alert = {
    id,
    // Alert title and description are fields we expect to have values, i.e. they are required fields.
    // If for some reason they are null, change them to empty strings so as not to upset the application
    // downstream.
    title: title || '',
    // RTD server sends back two-char new lines, which can mess up character limit counts
    // Here, we replace any of those occurrences with a single new line char.
    description: description
      ? description.replace(/(\r\n)/g, '\n')
      : '',
    cause,
    effect,
    editedBy,
    editedDate,
    url,
    start: start * 1000,
    end: end * 1000,
    published: published === 'Yes',
    affectedEntities: entities.map(ent => mapRtdEntity(ent, project))
  }
  return alert
}

function mapRtdEntity (rtdEntity: RtdEntity, project: Project): AlertEntity {
  const {
    Id: id,
    AgencyId,
    StopId,
    RouteId,
    RouteType
  } = rtdEntity
  let agency, type, mode
  // Hierarchically examine RTD entity contents and assign type based on what is
  // found. For example, the type is AGENCY if that is all the entity contains.
  if (AgencyId) {
    if (!project.feedSources) {
      console.warn('could not map Rtd Entity because feedSources is missing from project')
    } else {
      agency = project.feedSources.find(f => getFeedId(f) === AgencyId)
    }
    if (!agency) console.warn('Could not find agency for id', AgencyId)
    type = 'AGENCY'
  }
  // Stop goes ahead of route type and route because it's an optional field in
  // the below
  if (StopId) {
    type = 'STOP'
  }
  if (RouteId) {
    type = 'ROUTE'
  }
  if (RouteType !== null) {
    const defaultMode = modes.find(m => m.gtfsType === 0)
    // Catch any integers outside of 0 -7 range with default mode
    mode = modes.find(m => m.gtfsType === RouteType) || defaultMode
    type = 'MODE'
  }
  return {
    // FIXME: For now RTD incorrectly returns id as an int.
    // See https://github.com/ibi-group/datatools-ui/issues/459
    id: `${id}`,
    agency,
    type,
    mode,
    stop_id: StopId,
    route_id: RouteId
  }
}

export const NEW_ALERT_ID = 'NEW_ALERT_ID_PLACEHOLDER'

export function isNew (alert: Alert) {
  return alert.id === NEW_ALERT_ID
}
<commit_msg>feat(alerts): Add 2 alert effects (MTC)<commit_after>// @flow

import moment from 'moment'

import { getFeedId } from '../../common/util/modules'

import type {Alert, AlertEntity, Project, RtdAlert, RtdEntity} from '../../types'

export const FILTERS = ['ACTIVE', 'FUTURE', 'ARCHIVED', 'DRAFT', 'ALL']

export const CAUSES = [
  'UNKNOWN_CAUSE',
  'TECHNICAL_PROBLEM',
  'STRIKE',
  'DEMONSTRATION',
  'ACCIDENT',
  'HOLIDAY',
  'WEATHER',
  'MAINTENANCE',
  'CONSTRUCTION',
  'POLICE_ACTIVITY',
  'MEDICAL_EMERGENCY',
  'OTHER_CAUSE'
]

export const modes = [
  {gtfsType: 0, name: 'Tram/LRT'},
  {gtfsType: 1, name: 'Subway/Metro'},
  {gtfsType: 2, name: 'Rail'},
  {gtfsType: 3, name: 'Bus'},
  {gtfsType: 4, name: 'Ferry'},
  {gtfsType: 5, name: 'Cable Car'},
  {gtfsType: 6, name: 'Gondola'},
  {gtfsType: 7, name: 'Funicular'}
]

export const SORT_OPTIONS = [
  {value: 'id:asc', label: 'Oldest'},
  {value: 'id:desc', label: 'Newest'},
  {value: 'title:asc', label: 'Title'},
  {value: 'title:desc', label: 'Title (reverse)'},
  {value: 'start:asc', label: 'Starts earliest'},
  {value: 'start:desc', label: 'Starts latest'},
  {value: 'end:asc', label: 'Ends earliest'},
  {value: 'end:desc', label: 'Ends latest'}
]

export const EFFECTS = [
  'UNKNOWN_EFFECT',
  'NO_SERVICE',
  'REDUCED_SERVICE',
  'SIGNIFICANT_DELAYS',
  'DETOUR',
  'ADDITIONAL_SERVICE',
  'MODIFIED_SERVICE',
  'STOP_MOVED',
  'OTHER_EFFECT',
  // Items introduced by MTC.
  'NO_EFFECT',
  'ACCESSIBILITY_ISSUE'
]

export function filterAlertsByCategory (alerts: Array<Alert>, filter: string) {
  const now = moment()
  switch (filter) {
    case 'ALL':
      return alerts
    case 'ACTIVE':
      return alerts.filter((alert: Alert) =>
        moment(alert.start).isBefore(now) &&
        moment(alert.end).isAfter(now) &&
        alert.published
      )
    case 'FUTURE':
      return alerts.filter((alert: Alert) => moment(alert.start).isAfter(now))
    case 'ARCHIVED':
      return alerts.filter((alert: Alert) => moment(alert.end).isBefore(now))
    case 'DRAFT':
      return alerts.filter((alert: Alert) => !alert.published)
    default:
      return alerts
  }
}

export function mapRtdAlert (rtdAlert: RtdAlert, project: Project): Alert {
  const {
    Id: id,
    HeaderText: title,
    DescriptionText: description,
    Cause: cause,
    Effect: effect,
    EditedBy: editedBy,
    EditedDate: editedDate,
    Url: url,
    StartDateTime: start,
    EndDateTime: end,
    Published: published,
    ServiceAlertEntities: entities
  } = rtdAlert
  const alert = {
    id,
    // Alert title and description are fields we expect to have values, i.e. they are required fields.
    // If for some reason they are null, change them to empty strings so as not to upset the application
    // downstream.
    title: title || '',
    // RTD server sends back two-char new lines, which can mess up character limit counts
    // Here, we replace any of those occurrences with a single new line char.
    description: description
      ? description.replace(/(\r\n)/g, '\n')
      : '',
    cause,
    effect,
    editedBy,
    editedDate,
    url,
    start: start * 1000,
    end: end * 1000,
    published: published === 'Yes',
    affectedEntities: entities.map(ent => mapRtdEntity(ent, project))
  }
  return alert
}

function mapRtdEntity (rtdEntity: RtdEntity, project: Project): AlertEntity {
  const {
    Id: id,
    AgencyId,
    StopId,
    RouteId,
    RouteType
  } = rtdEntity
  let agency, type, mode
  // Hierarchically examine RTD entity contents and assign type based on what is
  // found. For example, the type is AGENCY if that is all the entity contains.
  if (AgencyId) {
    if (!project.feedSources) {
      console.warn('could not map Rtd Entity because feedSources is missing from project')
    } else {
      agency = project.feedSources.find(f => getFeedId(f) === AgencyId)
    }
    if (!agency) console.warn('Could not find agency for id', AgencyId)
    type = 'AGENCY'
  }
  // Stop goes ahead of route type and route because it's an optional field in
  // the below
  if (StopId) {
    type = 'STOP'
  }
  if (RouteId) {
    type = 'ROUTE'
  }
  if (RouteType !== null) {
    const defaultMode = modes.find(m => m.gtfsType === 0)
    // Catch any integers outside of 0 -7 range with default mode
    mode = modes.find(m => m.gtfsType === RouteType) || defaultMode
    type = 'MODE'
  }
  return {
    // FIXME: For now RTD incorrectly returns id as an int.
    // See https://github.com/ibi-group/datatools-ui/issues/459
    id: `${id}`,
    agency,
    type,
    mode,
    stop_id: StopId,
    route_id: RouteId
  }
}

export const NEW_ALERT_ID = 'NEW_ALERT_ID_PLACEHOLDER'

export function isNew (alert: Alert) {
  return alert.id === NEW_ALERT_ID
}
<|endoftext|>


<commit_before>// Node.js API.
var http = require('http')

// Control-flow utilities.
var cadence = require('cadence')
var delta = require('delta')

// Route Sencha Connect middleware based on request method and URL patterns.
var dispatch = require('dispatch')

// Exceptions that you can catch by type.
var interrupt = require('interrupt').createInterrupter('reactor')

// Contextualized callbacks and event handlers.
var Operation = require('operation/variadic')

// Evented work queue.
var Turnstile = require('turnstile')
Turnstile.Queue = require('turnstile/queue')

// Catch exceptions based on a regex match of an error message or property.
var rescue = require('rescue')

// Return the first not null-like value.
var coalesce = require('extant')

// Do nothing.
var nop = require('nop')

function Constructor (object, dispatch) {
    this._object = object
    this._dispatch = dispatch
    this._use = []
    this._defaultUse = true
}

Constructor.prototype.useDefault = function () {
    this.use(require('express-auth-parser'))
    this.use(require('body-parser').urlencoded({ extended: false, limit: '64mb' }))
    this.use(require('body-parser').json({ limit: '64mb' }))
}

Constructor.prototype.use = function () {
    var vargs = Array.prototype.slice.call(arguments)
    this._defaultUse = false
    while (vargs.length) {
        var varg = vargs.shift()
        if (Array.isArray(varg)) {
            vargs.unshift(varg)
        } else {
            this._use.push(varg)
        }
    }
}

Constructor.prototype.dispatch = function () {
    var vargs = Array.prototype.slice.call(arguments)
    this._dispatch[vargs.shift()] = Operation(vargs, { object: this._object })
}

function handler (queue, before, operation) {
    return function (request, response, next) {
        var vargs = Array.prototype.slice.call(arguments, 3)
        request.entry = {
            when: {
                push: Date.now(),
                start: null,
                headers: null,
                finish: null
            },
            health: {
                push: JSON.parse(JSON.stringify(queue.turnstile.health)),
                start: null,
                finish: null
            },
            duration: {
                start: null,
                headers: null,
                finish: null,
            },
            request: {
                method: request.method,
                header: request.headers,
                url: request.url,
                vargs: vargs
            }
        }
        before(request, response, function (error) {
            queue.push({
                error: coalesce(error),
                operation: operation,
                request: request,
                response: response,
                vargs: vargs
            })
        })
    }
}

function Reactor (object, configurator) {
    var constructor = new Constructor(object, this._dispatch = {})
    configurator(constructor)
    this.turnstile = new Turnstile({
        Date: coalesce(constructor.Date, Date),
        turnstiles: coalesce(constructor.turnstiles, 24),
        timeout: coalesce(constructor.timeout)
    })
    this._queue = new Turnstile.Queue(this, '_respond', this.turnstile)
    this._logger = coalesce(constructor.logger, nop)
    this._completed = coalesce(constructor.completed, nop)
    this._object = object
    if (constructor._defaultUse) {
        constructor.useDefault()
    }
    var before = require('connect')()
    constructor._use.forEach(function (middleware) {
        before.use(middleware)
    })
    var dispatcher = {}
    for (var pattern in this._dispatch) {
        dispatcher[pattern] = handler(this._queue, before, this._dispatch[pattern])
    }
    this.middleware = require('connect')().use(dispatch(dispatcher))
}

Reactor.prototype._timeout = cadence(function () { throw 503 })

function createProperties (properties) {
    return {
        statusCode: properties.statusCode,
        headers: coalesce(properties.headers, {}),
        description: coalesce(properties.description)
    }
}

function raise (statusCode, description, headers) {
    throw interrupt('http', createProperties({
        statusCode: statusCode,
        description: description,
        headers: headers
    }))
}

Reactor.prototype._respond = cadence(function (async, envelope) {
    var work = envelope.body
    var next = work.next

    var entry = work.request.entry

    entry.when.start = Date.now()
    entry.health.start = JSON.parse(JSON.stringify(this.turnstile.health))

    work.request.entry = entry
    work.request.raise = raise

    if (envelope.timedout) {
        work.operation = Operation([ this, '_timeout' ])
    }

    var finish

    async(function () {
        async(function () {
            async([function () {
                async(function () {
                    if (work.error) {
                        throw { cause: work.error, statusCode: coalesce(work.error.statusCode) }
                    }
                    work.operation.apply(null, [ work.request ].concat(work.vargs, async()))
                }, function () {
                    var vargs = Array.prototype.slice.call(arguments)

                    var result = vargs.shift()
                    var statusCode = (typeof vargs[0] == 'number') ? vargs.shift() : 200
                    var description = coalesce(http.STATUS_CODES[statusCode])
                    if (typeof vargs[0] == 'string') {
                        description = vargs.shift()
                    }
                    if (vargs[0] == null) {
                        vargs.shift()
                    }
                    var headers = coalesce(vargs.shift(), {})

                    interrupt.assert(description != null, 'unknown.http.status', { statusCode: statusCode })

                    return [ result, statusCode, description, headers ]
                })
            }, function (caught) {
                for (;;) {
                    try {
                        return rescue(/^reactor#http$/m, function (error) {
                            var statusCode = error.statusCode
                            var description = coalesce(error.description, http.STATUS_CODES[statusCode])
                            var headers = coalesce(error.headers, {})

                            entry.error = coalesce(error.cause)

                            interrupt.assert(description != null, 'unknown.http.status', { statusCode: statusCode })

                            headers['content-type'] = 'application/json'

                            return [ description, statusCode, description, headers ]
                        })(caught)
                    } catch (error) {
                        if (
                            typeof error == 'number' &&
                            !isNaN(error) &&
                            (error | 0) === error &&
                            Math.floor(error / 100) <= 5 &&
                            Math.floor(error / 100) >= 3
                        ) {
                            error = { statusCode: error }
                        } else if (typeof error == 'string') {
                            error = { statusCode: 307, location: error }
                        }
                        if (
                            ! (error instanceof Error) &&
                            typeof error == 'object' &&
                            typeof error.statusCode == 'number' &&
                            !isNaN(error.statusCode) &&
                            (error.statusCode | 0) === error.statusCode &&
                            Math.floor(error.statusCode / 100) <= 5 &&
                            Math.floor(error.statusCode / 100) >= 3
                        ) {
                            var properties = createProperties(error)
                            if (error.location) {
                                properties.headers.location = error.location
                            }
                            error = interrupt('http', properties, { cause: coalesce(error.cause) })
                        } else {
                            error = { statusCode: 500, cause: error }
                        }
                        caught = error
                    }
                }
            }])
        }, function (result, statusCode, description, headers) {
            var body, f
            if (typeof result == 'function') {
                f = result
            } else {
                if (typeof result == 'string') {
                    if (!('content-type' in headers)) {
                        headers['content-type'] = 'text/plain'
                        result = new Buffer(result)
                    } else if (headers['content-type'] == 'application/json') {
                        result = new Buffer(JSON.stringify(result) + '\n')
                    }
                } else if (!('content-type' in headers)) {
                    headers['content-type'] = 'application/json'
                    result = new Buffer(JSON.stringify(result) + '\n')
                }
                f = function (response) {
                    response.end(result)
                }
            }

            work.response.writeHead(statusCode, description, headers)

            entry.when.headers = Date.now()

            entry.statusCode = statusCode
            entry.description = description
            entry.headers = headers

            var finish = delta(async()).ee(work.response).on('finish')

            async([function () {
                async(function () {
                    if (f.length == 2) {
                        f.call(work.operation.object, work.response, async())
                    } else {
                        return [ f.call(work.operation.object, work.response) ]
                    }
                }, function () {
                    return []
                })
            }, function (error) {
                entry.error = error
                work.response.end()
                finish.cancel()
            }])
        })
    }, function () {
        entry.health.done = JSON.parse(JSON.stringify(this.turnstile.health))
        entry.when.finish = Date.now()
        entry.duration = {
            start: entry.when.start - entry.when.push,
            headers: entry.when.headers - entry.when.push,
            finish: entry.when.finish - entry.when.push
        }
        this._logger.call(null, entry)
        this._completed.call(null, entry)
    })
})

Reactor.resend = function (statusCode, headers, body) {
    return Reactor.send({ statusCode: statusCode, headers: headers }, body)
}

Reactor.send = function (properties, buffer) {
    var headers = JSON.parse(JSON.stringify(properties.headers))
    delete headers['content-length']
    delete headers['transfer-encoding']
    return [ buffer, properties.statusCode, properties.statusMessage, headers ]
}

Reactor.string = function (statusCode, contentType, text) {
    return [ new Buffer(text), statusCode, { 'content-type': contentType } ]
}

Reactor.stream = function (properties, stream) {
    var headers = JSON.parse(JSON.stringify(properties.headers))
    delete headers['content-length']
    headers['transfer-encoding'] = 'chunked'
    return [ function (response) {
        stream.pipe(response)
    }, properties.statusCode, properties.statusMessage, headers ]
}

module.exports = Reactor
<commit_msg>Add a default logger for the common case.<commit_after>// Node.js API.
var http = require('http')

// Control-flow utilities.
var cadence = require('cadence')
var delta = require('delta')

// Route Sencha Connect middleware based on request method and URL patterns.
var dispatch = require('dispatch')

// Exceptions that you can catch by type.
var interrupt = require('interrupt').createInterrupter('reactor')

// Contextualized callbacks and event handlers.
var Operation = require('operation/variadic')

// Evented work queue.
var Turnstile = require('turnstile')
Turnstile.Queue = require('turnstile/queue')

// Catch exceptions based on a regex match of an error message or property.
var rescue = require('rescue')

// Return the first not null-like value.
var coalesce = require('extant')

// Do nothing.
var nop = require('nop')

function Constructor (object, dispatch) {
    this._object = object
    this._dispatch = dispatch
    this._use = []
    this._defaultUse = true
}

Constructor.prototype.useDefault = function () {
    this.use(require('express-auth-parser'))
    this.use(require('body-parser').urlencoded({ extended: false, limit: '64mb' }))
    this.use(require('body-parser').json({ limit: '64mb' }))
}

Constructor.prototype.use = function () {
    var vargs = Array.prototype.slice.call(arguments)
    this._defaultUse = false
    while (vargs.length) {
        var varg = vargs.shift()
        if (Array.isArray(varg)) {
            vargs.unshift(varg)
        } else {
            this._use.push(varg)
        }
    }
}

Constructor.prototype.dispatch = function () {
    var vargs = Array.prototype.slice.call(arguments)
    this._dispatch[vargs.shift()] = Operation(vargs, { object: this._object })
}

function handler (queue, before, operation) {
    return function (request, response, next) {
        var vargs = Array.prototype.slice.call(arguments, 3)
        request.entry = {
            when: {
                push: Date.now(),
                start: null,
                headers: null,
                finish: null
            },
            health: {
                push: JSON.parse(JSON.stringify(queue.turnstile.health)),
                start: null,
                finish: null
            },
            duration: {
                start: null,
                headers: null,
                finish: null,
            },
            request: {
                method: request.method,
                header: request.headers,
                url: request.url,
                vargs: vargs
            }
        }
        before(request, response, function (error) {
            queue.push({
                error: coalesce(error),
                operation: operation,
                request: request,
                response: response,
                vargs: vargs
            })
        })
    }
}

function Reactor (object, configurator) {
    var constructor = new Constructor(object, this._dispatch = {})
    configurator(constructor)
    this.turnstile = new Turnstile({
        Date: coalesce(constructor.Date, Date),
        turnstiles: coalesce(constructor.turnstiles, 24),
        timeout: coalesce(constructor.timeout)
    })
    this._queue = new Turnstile.Queue(this, '_respond', this.turnstile)
    this._logger = coalesce(constructor.logger, function (entry) {
        if (entry.error) {
            console.log(entry.error.stack)
        }
    })
    this._completed = coalesce(constructor.completed, nop)
    this._object = object
    if (constructor._defaultUse) {
        constructor.useDefault()
    }
    var before = require('connect')()
    constructor._use.forEach(function (middleware) {
        before.use(middleware)
    })
    var dispatcher = {}
    for (var pattern in this._dispatch) {
        dispatcher[pattern] = handler(this._queue, before, this._dispatch[pattern])
    }
    this.middleware = require('connect')().use(dispatch(dispatcher))
}

Reactor.prototype._timeout = cadence(function () { throw 503 })

function createProperties (properties) {
    return {
        statusCode: properties.statusCode,
        headers: coalesce(properties.headers, {}),
        description: coalesce(properties.description)
    }
}

function raise (statusCode, description, headers) {
    throw interrupt('http', createProperties({
        statusCode: statusCode,
        description: description,
        headers: headers
    }))
}

Reactor.prototype._respond = cadence(function (async, envelope) {
    var work = envelope.body
    var next = work.next

    var entry = work.request.entry

    entry.when.start = Date.now()
    entry.health.start = JSON.parse(JSON.stringify(this.turnstile.health))

    work.request.entry = entry
    work.request.raise = raise

    if (envelope.timedout) {
        work.operation = Operation([ this, '_timeout' ])
    }

    var finish

    async(function () {
        async(function () {
            async([function () {
                async(function () {
                    if (work.error) {
                        throw { cause: work.error, statusCode: coalesce(work.error.statusCode) }
                    }
                    work.operation.apply(null, [ work.request ].concat(work.vargs, async()))
                }, function () {
                    var vargs = Array.prototype.slice.call(arguments)

                    var result = vargs.shift()
                    var statusCode = (typeof vargs[0] == 'number') ? vargs.shift() : 200
                    var description = coalesce(http.STATUS_CODES[statusCode])
                    if (typeof vargs[0] == 'string') {
                        description = vargs.shift()
                    }
                    if (vargs[0] == null) {
                        vargs.shift()
                    }
                    var headers = coalesce(vargs.shift(), {})

                    interrupt.assert(description != null, 'unknown.http.status', { statusCode: statusCode })

                    return [ result, statusCode, description, headers ]
                })
            }, function (caught) {
                for (;;) {
                    try {
                        return rescue(/^reactor#http$/m, function (error) {
                            var statusCode = error.statusCode
                            var description = coalesce(error.description, http.STATUS_CODES[statusCode])
                            var headers = coalesce(error.headers, {})

                            entry.error = coalesce(error.cause)

                            interrupt.assert(description != null, 'unknown.http.status', { statusCode: statusCode })

                            headers['content-type'] = 'application/json'

                            return [ description, statusCode, description, headers ]
                        })(caught)
                    } catch (error) {
                        if (
                            typeof error == 'number' &&
                            !isNaN(error) &&
                            (error | 0) === error &&
                            Math.floor(error / 100) <= 5 &&
                            Math.floor(error / 100) >= 3
                        ) {
                            error = { statusCode: error }
                        } else if (typeof error == 'string') {
                            error = { statusCode: 307, location: error }
                        }
                        if (
                            ! (error instanceof Error) &&
                            typeof error == 'object' &&
                            typeof error.statusCode == 'number' &&
                            !isNaN(error.statusCode) &&
                            (error.statusCode | 0) === error.statusCode &&
                            Math.floor(error.statusCode / 100) <= 5 &&
                            Math.floor(error.statusCode / 100) >= 3
                        ) {
                            var properties = createProperties(error)
                            if (error.location) {
                                properties.headers.location = error.location
                            }
                            error = interrupt('http', properties, { cause: coalesce(error.cause) })
                        } else {
                            error = { statusCode: 500, cause: error }
                        }
                        caught = error
                    }
                }
            }])
        }, function (result, statusCode, description, headers) {
            var body, f
            if (typeof result == 'function') {
                f = result
            } else {
                if (typeof result == 'string') {
                    if (!('content-type' in headers)) {
                        headers['content-type'] = 'text/plain'
                        result = new Buffer(result)
                    } else if (headers['content-type'] == 'application/json') {
                        result = new Buffer(JSON.stringify(result) + '\n')
                    }
                } else if (!('content-type' in headers)) {
                    headers['content-type'] = 'application/json'
                    result = new Buffer(JSON.stringify(result) + '\n')
                }
                f = function (response) {
                    response.end(result)
                }
            }

            work.response.writeHead(statusCode, description, headers)

            entry.when.headers = Date.now()

            entry.statusCode = statusCode
            entry.description = description
            entry.headers = headers

            var finish = delta(async()).ee(work.response).on('finish')

            async([function () {
                async(function () {
                    if (f.length == 2) {
                        f.call(work.operation.object, work.response, async())
                    } else {
                        return [ f.call(work.operation.object, work.response) ]
                    }
                }, function () {
                    return []
                })
            }, function (error) {
                entry.error = error
                work.response.end()
                finish.cancel()
            }])
        })
    }, function () {
        entry.health.done = JSON.parse(JSON.stringify(this.turnstile.health))
        entry.when.finish = Date.now()
        entry.duration = {
            start: entry.when.start - entry.when.push,
            headers: entry.when.headers - entry.when.push,
            finish: entry.when.finish - entry.when.push
        }
        this._logger.call(null, entry)
        this._completed.call(null, entry)
    })
})

Reactor.resend = function (statusCode, headers, body) {
    return Reactor.send({ statusCode: statusCode, headers: headers }, body)
}

Reactor.send = function (properties, buffer) {
    var headers = JSON.parse(JSON.stringify(properties.headers))
    delete headers['content-length']
    delete headers['transfer-encoding']
    return [ buffer, properties.statusCode, properties.statusMessage, headers ]
}

Reactor.string = function (statusCode, contentType, text) {
    return [ new Buffer(text), statusCode, { 'content-type': contentType } ]
}

Reactor.stream = function (properties, stream) {
    var headers = JSON.parse(JSON.stringify(properties.headers))
    delete headers['content-length']
    headers['transfer-encoding'] = 'chunked'
    return [ function (response) {
        stream.pipe(response)
    }, properties.statusCode, properties.statusMessage, headers ]
}

module.exports = Reactor
<|endoftext|>


<commit_before>/*
==================================================================================================
  Lowland - JavaScript low level functions
  Copyright (C) 2012 Sebatian Fastner
==================================================================================================
*/

core.Class("lowland.events.DataEvent", {
  properties : {
    data : {}
  }
});<commit_msg>Add constructor and olddata to data event<commit_after>/*
==================================================================================================
  Lowland - JavaScript low level functions
  Copyright (C) 2012 Sebatian Fastner
==================================================================================================
*/

core.Class("lowland.events.DataEvent", {
  properties : {
    data : {},
    oldData : {}
  },

  construct : function(value, oldValue) {
    if (value) {
      this.setData(value);
    }
    if (oldValue) {
      this.setOldData(oldValue);
    }
  }
});<|endoftext|>


<commit_before>module.exports = {
  toc: [
    {
      title: 'Get Started',
      prefix: 'get-started',
      pages: ['introduction', 'install', 'whats-a-story', 'browse-stories', 'setup', 'conclusion'],
    },
    {
      title: 'Writing Stories',
      prefix: 'writing-stories',
      pages: [
        'introduction',
        'args',
        'parameters',
        'decorators',
        'naming-components-and-hierarchy',
      ],
    },
    {
      title: 'Writing Docs',
      prefix: 'writing-docs',
      pages: ['introduction', 'docs-page', 'mdx', 'doc-blocks'],
    },
    {
      title: 'Essentials',
      prefix:'essentials',
      pages:['introduction','controls','actions','viewports','backgrounds','toolbars-and-globals']
    },
    {
      title: 'Configure',
      prefix: 'configure',
      pages: ['overview', 'integration', 'story-rendering', 'user-interface'],
    },
    {
      title:'Workflows',
      prefix:'workflows',
      pages:[
        'publish-storybook',
        'build-pages-with-storybook',
        'stories-for-multiple-components',
        'testing-with-storybook',
        'unit-testing',
        'visual-testing',
        'interaction-testing',
        'snapshot-testing',
        'storybook-composition',
        'package-composition'
      ]
    },
    {

      title: 'API',
      prefix:'api',
      pages:['stories','addons','new-frameworks','cli-options','frameworks-feature-support']
    },
  ],
};<commit_msg>Update TOC format, setup with currently ready pages<commit_after>module.exports = {
  toc: [
    {
      title: 'Get Started',
      type: 'menu',
      children: [
        {
          path: '/docs/get-started/introduction/',
          title: 'Introduction',
          type: 'bullet-link',
        },
        {
          path: '/docs/get-started/install/',
          title: 'Install',
          type: 'bullet-link',
        },
        {
          path: '/docs/get-started/whats-a-story/',
          title: "What's a story?",
          type: 'bullet-link',
        },
        {
          path: '/docs/get-started/browse-stories/',
          title: 'Browse stories',
          type: 'bullet-link',
        },
        {
          path: '/docs/get-started/setup/',
          title: 'Setup',
          type: 'bullet-link',
        },
        {
          path: '/docs/get-started/conclusion/',
          title: 'Conclusion',
          type: 'bullet-link',
        },
      ],
    },
    {
      title: 'Writing Stories',
      type: 'menu',
      children: [
        {
          path: '/docs/writing-stories/introduction/',
          title: 'Introduction',
          type: 'link',
        },
        {
          path: '/docs/writing-stories/args/',
          title: 'Args',
          type: 'link',
        },
        {
          path: '/docs/writing-stories/parameters/',
          title: 'Parameters',
          type: 'link',
        },
        {
          path: '/docs/writing-stories/decorators/',
          title: 'Decorators',
          type: 'link',
        },
        {
          path: '/docs/writing-stories/naming-components-and-hierarchy/',
          title: 'Naming components and hierarchy',
          type: 'link',
        },
      ],
    },
    {
      title: 'Writing Docs',
      type: 'menu',
      children: [
        {
          path: '/docs/writing-docs/introduction/',
          title: 'Introduction',
          type: 'link',
        },
        {
          path: '/docs/writing-docs/docs-page/',
          title: 'Docs Page',
          type: 'link',
        },
        {
          path: '/docs/writing-docs/mdx/',
          title: 'MDX',
          type: 'link',
        },
        {
          path: '/docs/writing-docs/doc-blocks/',
          title: 'Docs Blocks',
          type: 'link',
        },
      ],
    },
    {
      title: 'Essentials',
      type: 'menu',
      children: [
        {
          path: '/docs/essentials/introduction/',
          title: 'Introduction',
          type: 'link',
        },
        {
          path: '/docs/essentials/controls/',
          title: 'Controls',
          type: 'link',
        },
        {
          path: '/docs/essentials/actions/',
          title: 'Actions',
          type: 'link',
        },
        {
          path: '/docs/essentials/viewports/',
          title: 'Viewports',
          type: 'link',
        },
        {
          path: '/docs/essentials/backgrounds/',
          title: 'Backgrounds',
          type: 'link',
        },
        {
          path: '/docs/essentials/toolbars-and-globals/',
          title: 'Toolbars & globals',
          type: 'link',
        },
      ],
    },
    {
      title: 'Configure',
      type: 'menu',
      children: [
        {
          path: '/docs/configure/overview/',
          title: 'Overview',
          type: 'link',
        },
        {
          path: '/docs/configure/integration/',
          title: 'Integration',
          type: 'link',
        },
        {
          path: '/docs/configure/story-rendering/',
          title: 'Story rendering',
          type: 'link',
        },
        {
          path: '/docs/configure/user-interface/',
          title: 'User interface',
          type: 'link',
        },
      ],
    },
    {
      title: 'Workflows',
      type: 'menu',
      children: [
        {
          path: '/docs/workflows/publish-storybook/',
          title: 'Publish Storybook',
          type: 'link',
        },
        {
          path: '/docs/workflows/build-pages-with-storybook/',
          title: 'Building pages with Storybook',
          type: 'link',
        },
        {
          path: '/docs/workflows/stories-for-multiple-components/',
          title: 'Stories for multiple components',
          type: 'link',
        },
        {
          title: 'Testing with Storybook',
          type: 'menu',
          children: [
            {
              path: '/docs/workflows/testing-with-storybook/',
              title: 'Introduction',
              type: 'link',
            },
            {
              path: '/docs/workflows/unit-testing/',
              title: 'Unit testing',
              type: 'link',
            },
            {
              path: '/docs/workflows/visual-testing/',
              title: 'Visual testing',
              type: 'link',
            },
            {
              path: '/docs/workflows/interaction-testing/',
              title: 'Interaction testing',
              type: 'link',
            },
            {
              path: '/docs/workflows/snapshot-testing/',
              title: 'Snapshot testing',
              type: 'link',
            },
          ],
        },
        {
          path: '/docs/workflows/storybook-composition/',
          title: 'Storybook Composition',
          type: 'link',
        },
        {
          path: '/docs/workflows/package-composition/',
          title: 'Package Composition',
          type: 'link',
        },
      ],
    },
    {
      title: 'API',
      type: 'menu',
      children: [
        {
          path: '/docs/api/stories/',
          title: 'Stories',
          type: 'link',
        },
        {
          path: '/docs/api/addons/',
          title: 'Addons',
          type: 'link',
        },
        {
          path: '/docs/api/new-frameworks/',
          title: 'Frameworks',
          type: 'link',
        },
        {
          path: '/docs/api/cli-options/',
          title: 'CLI Options',
          type: 'link',
        },
        {
          path: '/docs/api/frameworks-feature-support/',
          title: 'Feature support for frameworks',
          type: 'link',
        },
      ],
    },
  ],
};
<|endoftext|>


<commit_before>import Fetch from 'js-fetch';
import btoa from '../Tools/btoa';
import localStorageService from './LocalStorageService';

const API = `https://${API_HOST}/`; // eslint-disable-line no-undef
const WS = `wss://${API_HOST}/ws/`; // eslint-disable-line no-undef
export const authStorage = 'auth';

/**
 * Generate FetchBuilder for given URL with auth and error handler.
 * @param  {String} url   Wanted URL
 * @return {FetchBuilder} FetchBuilder pre-configured
 */
function auth(url) {
  return Fetch.url(url)
    .auth(localStorageService.getItem(authStorage));
}

export default class DockerService {
  /**
   * Check if User is already logged.
   * @return {Boolean} True if User has authentification token, false  otherwise.
   */
  static isLogged() {
    return !!localStorageService.getItem(authStorage);
  }

  /**
   * Login User given username and password. If success, store auth token in LocalStorage.
   * @param  {String} username User's username
   * @param  {String} password User's password
   * @return {Promise}         Token wrapped in a Promise
   */
  static login(username, password) {
    const hash = `Basic ${btoa(`${username}:${password}`)}`;

    return Fetch.url(`${API}auth`).auth(hash).get()
      .then((result) => {
        localStorageService.setItem(authStorage, hash);
        return result;
      });
  }

  /**
   * Logout User by removing User's token.
   * @return {Promise} Resolved promise
   */
  static logout() {
    localStorageService.removeItem(authStorage);
    return Promise.resolve();
  }

  /**
   * List Docker's containers.
   * @return {Promise} Array of informations wrapped in a Promise
   */
  static containers() {
    return auth(`${API}containers`)
      .get()
      .then(({ results }) => results);
  }

  /**
   * Retrieve informations about a Docker's container.
   * @param  {String} containerId Container's id
   * @return {Promise}            Information wrapped in a Promise
   */
  static infos(containerId) {
    return auth(`${API}containers/${containerId}/`)
      .get();
  }

  /**
   * Create Docker's container from Docker Compose file.
   * @param  {String} name        Name of the stack
   * @param  {String} composeFile Docker Compose file content
   * @return {Promise}            Array of created containers wrapped in a Promise
   */
  static create(name, composeFile) {
    return auth(`${API}containers/${name}/`)
      .post(composeFile);
  }

  /**
   * Start Docker's container.
   * @param  {String} containerId Container's id
   * @return {Promise}            Resolved promise
   */
  static start(containerId) {
    return auth(`${API}containers/${containerId}/start`)
      .post();
  }

  /**
   * Stop Docker's container.
   * @param  {String} containerId Container's id
   * @return {Promise}            Resolved promise
   */
  static stop(containerId) {
    return auth(`${API}containers/${containerId}/stop`)
      .post();
  }

  /**
   * Restart Docker's container.
   * @param  {String} containerId Container's id
   * @return {Promise}            Resolved promise
   */
  static restart(containerId) {
    return auth(`${API}containers/${containerId}/restart`)
      .post();
  }

  /**
   * Delete Docker's container.
   * @param  {String} containerId Container's id
   * @return {Promise}            Resolved promise
   */
  static delete(containerId) {
    return auth(`${API}containers/${containerId}/`).delete();
  }

  /**
   * Open WebSocket with auth token for reading logs of a Docker's container.
   * @param  {String} containerId Container's id
   * @param  {Function} onMessage Callback for each input receive from socket
   * @return {Websocket}          Opened and authentified Websocket
   */
  static logs(containerId, onMessage) {
    const socket = new WebSocket(`${WS}containers/${containerId}/logs`);

    socket.onmessage = event => onMessage(event.data);
    socket.onopen = () => socket.send(localStorageService.getItem(authStorage));

    return socket;
  }

  /**
   * Open WebSocket with auth token for reading events of a Docker's daemon.
   * @param  {Function} onMessage Callback for each input event from socket
   * @return {Websocket}          Opened and authentified Websocket
   */
  static events(onMessage) {
    const socket = new WebSocket(`${WS}events`);

    socket.onmessage = event => onMessage(event.data);
    socket.onopen = () => socket.send(localStorageService.getItem(authStorage));

    return socket;
  }
}
<commit_msg>Update DockerService.js<commit_after>import Fetch from 'js-fetch';
import btoa from '../Tools/btoa';
import localStorageService from './LocalStorageService';

const API = 'https://API_HOST/';
const WS = 'wss://API_HOST/ws/';
export const authStorage = 'auth';

/**
 * Generate FetchBuilder for given URL with auth and error handler.
 * @param  {String} url   Wanted URL
 * @return {FetchBuilder} FetchBuilder pre-configured
 */
function auth(url) {
  return Fetch.url(url)
    .auth(localStorageService.getItem(authStorage));
}

export default class DockerService {
  /**
   * Check if User is already logged.
   * @return {Boolean} True if User has authentification token, false  otherwise.
   */
  static isLogged() {
    return !!localStorageService.getItem(authStorage);
  }

  /**
   * Login User given username and password. If success, store auth token in LocalStorage.
   * @param  {String} username User's username
   * @param  {String} password User's password
   * @return {Promise}         Token wrapped in a Promise
   */
  static login(username, password) {
    const hash = `Basic ${btoa(`${username}:${password}`)}`;

    return Fetch.url(`${API}auth`).auth(hash).get()
      .then((result) => {
        localStorageService.setItem(authStorage, hash);
        return result;
      });
  }

  /**
   * Logout User by removing User's token.
   * @return {Promise} Resolved promise
   */
  static logout() {
    localStorageService.removeItem(authStorage);
    return Promise.resolve();
  }

  /**
   * List Docker's containers.
   * @return {Promise} Array of informations wrapped in a Promise
   */
  static containers() {
    return auth(`${API}containers`)
      .get()
      .then(({ results }) => results);
  }

  /**
   * Retrieve informations about a Docker's container.
   * @param  {String} containerId Container's id
   * @return {Promise}            Information wrapped in a Promise
   */
  static infos(containerId) {
    return auth(`${API}containers/${containerId}/`)
      .get();
  }

  /**
   * Create Docker's container from Docker Compose file.
   * @param  {String} name        Name of the stack
   * @param  {String} composeFile Docker Compose file content
   * @return {Promise}            Array of created containers wrapped in a Promise
   */
  static create(name, composeFile) {
    return auth(`${API}containers/${name}/`)
      .post(composeFile);
  }

  /**
   * Start Docker's container.
   * @param  {String} containerId Container's id
   * @return {Promise}            Resolved promise
   */
  static start(containerId) {
    return auth(`${API}containers/${containerId}/start`)
      .post();
  }

  /**
   * Stop Docker's container.
   * @param  {String} containerId Container's id
   * @return {Promise}            Resolved promise
   */
  static stop(containerId) {
    return auth(`${API}containers/${containerId}/stop`)
      .post();
  }

  /**
   * Restart Docker's container.
   * @param  {String} containerId Container's id
   * @return {Promise}            Resolved promise
   */
  static restart(containerId) {
    return auth(`${API}containers/${containerId}/restart`)
      .post();
  }

  /**
   * Delete Docker's container.
   * @param  {String} containerId Container's id
   * @return {Promise}            Resolved promise
   */
  static delete(containerId) {
    return auth(`${API}containers/${containerId}/`).delete();
  }

  /**
   * Open WebSocket with auth token for reading logs of a Docker's container.
   * @param  {String} containerId Container's id
   * @param  {Function} onMessage Callback for each input receive from socket
   * @return {Websocket}          Opened and authentified Websocket
   */
  static logs(containerId, onMessage) {
    const socket = new WebSocket(`${WS}containers/${containerId}/logs`);

    socket.onmessage = event => onMessage(event.data);
    socket.onopen = () => socket.send(localStorageService.getItem(authStorage));

    return socket;
  }

  /**
   * Open WebSocket with auth token for reading events of a Docker's daemon.
   * @param  {Function} onMessage Callback for each input event from socket
   * @return {Websocket}          Opened and authentified Websocket
   */
  static events(onMessage) {
    const socket = new WebSocket(`${WS}events`);

    socket.onmessage = event => onMessage(event.data);
    socket.onopen = () => socket.send(localStorageService.getItem(authStorage));

    return socket;
  }
}
<|endoftext|>