<commit_before>export { InputsOutputs };

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

/**
 * Element value bounds (per channel) for inputs and outputs of an operation. *
 *
 * @member {FloatValue.BoundsArray[]} inputs
 *   The element value bounds (per channel) of all inputs. Or say, the domain of the operation.
 *
 * @member {number} inputTensorCount
 *   How many input tensors (i.e. this.inputs.length). Default is 1.
 *
 * @member {FloatValue.BoundsArray} input0
 *   The element value bounds (per channel) of 1st input. It is just the this.outputs[ 0 ].
 *
 * @member {FloatValue.BoundsArray} input1
 *   The element value bounds (per channel) of 2nd input. It is just the this.outputs[ 1 ].
 *
 * @member {FloatValue.BoundsArray[]} outputs
 *   The element value bounds (per channel) of all outputs. Or say, the range of the operation.
 *
 * @member {number} outputTensorCount
 *   How many output tensors (i.e. this.outputs.length). Default is 1.
 *
 * @member {FloatValue.BoundsArray} output0
 *   The element value bounds (per channel) of 1st output. It is just the this.outputs[ 0 ].
 *
 * @member {FloatValue.BoundsArray} output1
 *   The element value bounds (per channel) of 2nd output. It is just the this.outputs[ 1 ].
 */
class ConvBiasActivation {

  /**
   */

//!!! ...unfinished... (2022/04/11)
// Perhaps, inputChannelCount0, inputChannelCount1, outputChannelCount0, outputChannelCount1
  constructor( inputChannelCount, outputChannelCount, inputTensorCount = 1, outputTensorCount = 1 ) {
    this.input = new FloatValue.BoundsArray( inputChannelCount );
    this.afterUndoPreviousActivationEscaping = new FloatValue.BoundsArray( inputChannelCount );
    this.afterFilter = new FloatValue.BoundsArray( outputChannelCount );
    this.afterBias = new FloatValue.BoundsArray( outputChannelCount );
    this.afterActivationEscaping = new FloatValue.BoundsArray( outputChannelCount );
    this.afterActivation = new FloatValue.BoundsArray( outputChannelCount ); // i.e. .output

    this.activationEscaping_ScaleArraySet = new ActivationEscaping.ScaleArraySet( outputChannelCount );
    this.bPassThrough = new Array( outputChannelCount );

    //this.set_all_byBounds.set_all_byBounds( Weights.Base.ValueBounds );
  }

  /**
   * @return {BoundsArraySet} Return a newly created BoundsArraySet which is a copy of this BoundsArraySet.
   */
  clone() {
    let result = new BoundsArraySet( this.input.length, this.output.length );
    result.set_byBoundsArraySet( this );
    return result;
  }

  /**
   * Set:
   *   - this.activationEscaping_ScaleArraySet to scale 1 (i.e. all are no scale).
   *   - this.bPassThrough[] to false (i.e. all are not pass-through).
   *
   * @return {BoundsArraySet}
   *   Return this (modified) object.
   */
  set_all_activationEscaping_bPassThrough_none() {
    this.activationEscaping_ScaleArraySet.set_all_byN( 1 );
    this.bPassThrough.fill( false );
    return this;
  }

  /**
   * @param {FloatValue.Bounds} aBounds
   *   Set all BoundsArray to the same as the specified aBounds. Set the this.activationEscaping_ScaleArraySet to default ( 1 );
   *
   * @return {BoundsArraySet}
   *   Return this (modified) object.
   */
  set_all_byBounds( aBounds ) {
    this.input.set_all_byBounds( aBounds );
    this.afterUndoPreviousActivationEscaping.set_all_byBounds( aBounds );
    this.afterFilter.set_all_byBounds( aBounds );
    this.afterBias.set_all_byBounds( aBounds );
    this.afterActivationEscaping.set_all_byBounds( aBounds );
    this.afterActivation.set_all_byBounds( aBounds );
    this.set_all_activationEscaping_bPassThrough_none();
    return this;
  }

  /**
   * @param {BoundsArray} inputBoundsArray
   *   The BoundsArray to be copied to .input and .afterUndoPreviousActivationEscaping.
   *
   * @param {BoundsArray} outputBoundsArray
   *   The BoundsArray to be copied to .afterFilter, .afterBias, .afterActivationEscaping, .afterActivation.
   *
   * @return {BoundsArraySet}
   *   Return this (modified) object.
   */
  set_all_byBoundsArray_input_output( inputBoundsArray, outputBoundsArray ) {
    this.input                              .set_all_byBoundsArray( inputBoundsArray );
    this.afterUndoPreviousActivationEscaping.set_all_byBoundsArray( inputBoundsArray );
    this.afterFilter                        .set_all_byBoundsArray( outputBoundsArray );
    this.afterBias                          .set_all_byBoundsArray( outputBoundsArray );
    this.afterActivationEscaping            .set_all_byBoundsArray( outputBoundsArray );
    this.afterActivation                    .set_all_byBoundsArray( outputBoundsArray );
    this.set_all_activationEscaping_bPassThrough_none();
    return this;
  }

  /**
   * @param {BoundsArray} aBoundsArray
   *   The BoundsArray to be copied. It will be copied to all BoundsArray in this object.
   *
   * @return {BoundsArraySet}
   *   Return this (modified) object.
   */
  set_all_byBoundsArray( aBoundsArray ) {
    return this.set_all_byBoundsArray_input_output( aBoundsArray, aBoundsArray );
  }

  /**
   * @param {BoundsArray} inputBoundsArray0
   *   The BoundsArray of the 1st input.
   *
   * @param {BoundsArray} inputBoundsArray1
   *   The BoundsArray of the 2nd input.
   *
   * @return {BoundsArraySet}
   *   Return a newly created object.
   */
  static create_byBoundsArray_concat_input0_input1( inputBoundsArray0, inputBoundsArray1 ) {
    let rLength = inputBoundsArray0.length + inputBoundsArray1.length;
    let rBoundsArraySet = new BoundsArraySet( rLength, rLength );

    // Concat value bounds array.
    rBoundsArraySet.input.set_all_byBoundsArray_concat_input0_input1( inputBoundsArray0, inputBoundsArray1 );

    // Spread to all value bounds array.
    return rBoundsArraySet.set_all_byBoundsArray_input_output( rBoundsArraySet.input, rBoundsArraySet.input );
  }

  /**
   * @param {BoundsArraySet} aBoundsArraySet
   *   The BoundsArraySet to be copied.
   *
   * @return {BoundsArraySet}
   *   Return this (modified) object which is copied from aBoundsArraySet.
   */
  set_all_byBoundsArraySet( aBoundsArraySet ) {
    this.input                              .set_all_byBoundsArray( aBoundsArraySet.input );
    this.afterUndoPreviousActivationEscaping.set_all_byBoundsArray( aBoundsArraySet.afterUndoPreviousActivationEscaping );
    this.afterFilter                        .set_all_byBoundsArray( aBoundsArraySet.afterFilter );
    this.afterBias                          .set_all_byBoundsArray( aBoundsArraySet.afterBias );
    this.afterActivationEscaping            .set_all_byBoundsArray( aBoundsArraySet.afterActivationEscaping );
    this.afterActivation                    .set_all_byBoundsArray( aBoundsArraySet.afterActivation );
    this.activationEscaping_ScaleArraySet.set_byScaleArraySet( aBoundsArraySet.activationEscaping_ScaleArraySet );

    for ( let i = 0; i < this.bPassThrough.length; ++i ) {
      this.bPassThrough[ i ] = aBoundsArraySet.bPassThrough[ i ];
    }

    return this;
  }

  /**
   * Determine .activationEscaping_ScaleArraySet and .afterActivationEscaping and .afterActivation, by .afterBias and .bPassThrough and nActivationId.
   *
   * The following properties will be used:
   *   - this.afterBias
   *   - this.bPassThrough
   *
   * The following properties will be modified:
   *   - this.activationEscaping_ScaleArraySet
   *   - this.afterActivationEscaping
   *   - this.afterActivation
   *
   * @param {number} nActivationId
   *   The activation function id (ValueDesc.ActivationFunction.Singleton.Ids.Xxx) of this depthwise convolution.
   *
   * @return {BoundsArraySet}
   *   Return this (modified) object.
   */
  set_activationEscaping_afterActivationEscaping_afterActivation_by_afterBias_bPassThrough_nActivationId( nActivationId ) {
    let theActivationFunctionInfo = ValueDesc.ActivationFunction.Singleton.getInfoById( nActivationId );

    for ( let outChannel = 0; outChannel < this.afterBias.length; ++outChannel ) {

      // 1. Determine .activationEscaping_ScaleArraySet
      {
        // 1.1 Determine .do

        if ( nActivationId == ValueDesc.ActivationFunction.Singleton.Ids.NONE ) {

          // Since no activation function, no need to escape. (i.e. scale = 1 for no scale)
          this.activationEscaping_ScaleArraySet.do.set_one_byN( outChannel, 1 );

        } else {

          if ( this.bPassThrough[ outChannel ] ) { // For pass-through half channels.

            // Calculate the scale for escaping bias result from activation function's non-linear domain into linear domain.
            //
            // Note: This does not work for avg/max pooling.
            this.activationEscaping_ScaleArraySet.do.set_one_by_fromLowerUpper_toLowerUpper( outChannel,
              this.afterBias.lowers[ outChannel ], this.afterBias.uppers[ outChannel ],
              theActivationFunctionInfo.inputDomainLinear.lower, theActivationFunctionInfo.inputDomainLinear.upper
            );

            let doEscapingScale = this.activationEscaping_ScaleArraySet.do.scales[ outChannel ];
            tf.util.assert( ( Number.isNaN( doEscapingScale ) == false ),
              `ConvBiasActivation.BoundsArraySet.`
                + `set_activationEscaping_afterActivationEscaping_afterActivation_by_afterBias_bPassThrough_nActivationId(): `
                + `this.activationEscaping_ScaleArraySet.do.scales[ ${outChannel} ] ( ${doEscapingScale} ) `
                + `should not be NaN. `
                + `Please use activation function (e.g. tanh()) which has both negative and positive parts near origin point.`
            );

          } else { // Non pass-through half channels.
            // Since non-pass-through, no need to escape. (i.e. scale = 1 for no scale)
            this.activationEscaping_ScaleArraySet.do.set_one_byN( outChannel, 1 );
          }
        }

        // 1.2 Determine .undo (Prepared for the next convolution-bias-activation. Not for this.)
        this.activationEscaping_ScaleArraySet.undo.set_one_byUndo_ScaleArray(
          outChannel, this.activationEscaping_ScaleArraySet.do, outChannel );
      }

      // 2. Determine .afterActivationEscaping
      this.afterActivationEscaping
        .set_one_byBoundsArray( outChannel, this.afterBias, outChannel )
        .multiply_one_byNs( outChannel, this.activationEscaping_ScaleArraySet.do.scales, outChannel );

      // 3. Determine .afterActivation
      {
        // If no activation function, the output range is determined by .afterActivationEscaping.
        if ( nActivationId == ValueDesc.ActivationFunction.Singleton.Ids.NONE ) {
          this.afterActivation.set_one_byBoundsArray( outChannel, this.afterActivationEscaping, outChannel )

        // Otherwise, the activation function dominates the output range.
        //
        // Note: Consider the implementation of ScaleArray.set_one_by_fromLowerUpper_toLowerUpper(), they are all not so good no
        //       matter using set_one_byXxx() or clamp_one_byXxx() of this.afterActivation. However, when using clamp_one_byXxx(),
        //       even if the activation function output range has Infinity (e.g. RELU is [ 0, +Infinity ]), the result bounds
        //       is more feasible (at least, will not become another bounds with Infinity).
        } else {
          if ( this.bPassThrough[ outChannel ] ) { // For pass-through half channels, it is clamped by the output range for linearDomainLinear.
            //this.afterActivation.set_one_byBounds( outChannel, theActivationFunctionInfo.outputRangeLinear );
            this.afterActivation.clamp_one_byBounds( outChannel, theActivationFunctionInfo.outputRangeLinear );

          } else { // Non pass-through half channels, it is clamped by the output range for the whole input domain.
            //this.afterActivation.set_one_byBounds( outChannel, theActivationFunctionInfo.outputRange );
            this.afterActivation.clamp_one_byBounds( outChannel, theActivationFunctionInfo.outputRange );
          }
        }
      }
    }

    return this;
  }

  /**
   * Rearrange output related channel information (.afterFilter, .afterBias, .afterActivationEscaping, .afterActivation,
   * .activationEscaping_ScaleArraySet, .bPassThrough) by interleaving as ( groupCount == 2 ). This element count must be even
   * (i.e. divisible by 2).
   *
   * @param {Array} arrayTemp
   *   A temporary array for placing the original elements temporarily. Provide this array could reduce memory re-allocation
   * and improve performance.
   *
   * @return {BoundsArraySet}
   *   Return this (modified) object.
   */
  output_interleave_asGrouptTwo( arrayTemp ) {
    this.afterFilter.interleave_asGrouptTwo( arrayTemp );
    this.afterBias.interleave_asGrouptTwo( arrayTemp );
    this.afterActivationEscaping.interleave_asGrouptTwo( arrayTemp );
    this.afterActivation.interleave_asGrouptTwo( arrayTemp );
    this.activationEscaping_ScaleArraySet.interleave_asGrouptTwo( arrayTemp );
    FloatValue.ArrayInterleaver.interleave_asGrouptTwo( this.bPassThrough, 0, this.bPassThrough.length, arrayTemp );
    return this;
  }

  get intput0() {
    return this.inputs[ 0 ];
  }

  get output0() {
    return this.outputs[ 0 ];
  }

}

<commit_msg>Update InputsOutputs.js<commit_after>export { InputsOutputs };

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

/**
 * Element value bounds (per channel) for inputs and outputs of an operation. *
 *
 * @member {FloatValue.BoundsArray[]} inputs
 *   The element value bounds (per channel) of all inputs (i.e. the domain of the operation). This array (which is past into
 * constructor) will be kept (not cloned) directly. So caller should not modify them.
 *
 * @member {number} inputTensorCount
 *   How many input tensors (i.e. this.inputs.length).
 *
 * @member {FloatValue.BoundsArray} input0
 *   The element value bounds (per channel) of 1st input (i.e. this.inputs[ 0 ]).
 *
 * @member {FloatValue.BoundsArray} input1
 *   The element value bounds (per channel) of 2nd input (i.e. this.inputs[ 1 ]).
 *
 * @member {number} inputChannelCount0
 *   The channel count of 1st input (i.e. this.input0.length).
 *
 * @member {number} inputChannelCount1
 *   The channel count of 2nd input (i.e. this.input1.length).
 *
 * @member {FloatValue.BoundsArray[]} outputs
 *   The element value bounds (per channel) of all outputs (i.e. the range of the operation). The array is created by constructor
 * according to outputChannelCount0 and outputChannelCount1.
 *
 * @member {number} outputTensorCount
 *   How many output tensors (i.e. this.outputs.length). Default is 1.
 *
 * @member {FloatValue.BoundsArray} output0
 *   The element value bounds (per channel) of 1st output (i.e. this.outputs[ 0 ]).
 *
 * @member {FloatValue.BoundsArray} output1
 *   The element value bounds (per channel) of 2nd output (i.e. this.outputs[ 1 ]).
 *
 * @member {number} outputChannelCount0
 *   The channel count of 1st output (i.e. this.output0.length).
 *
 * @member {number} outputChannelCount1
 *   The channel count of 2nd output (i.e. this.output1.length).
 */
class ConvBiasActivation {

  /**
   */

//!!! ...unfinished... (2022/04/11)
// Perhaps, inputChannelCount0, inputChannelCount1, outputChannelCount0, outputChannelCount1
  constructor( inputs, outputChannelCount0, outputChannelCount1 ) {
    this.input = new FloatValue.BoundsArray( inputChannelCount );
    this.afterUndoPreviousActivationEscaping = new FloatValue.BoundsArray( inputChannelCount );
    this.afterFilter = new FloatValue.BoundsArray( outputChannelCount );
    this.afterBias = new FloatValue.BoundsArray( outputChannelCount );
    this.afterActivationEscaping = new FloatValue.BoundsArray( outputChannelCount );
    this.afterActivation = new FloatValue.BoundsArray( outputChannelCount ); // i.e. .output

    this.activationEscaping_ScaleArraySet = new ActivationEscaping.ScaleArraySet( outputChannelCount );
    this.bPassThrough = new Array( outputChannelCount );

    //this.set_all_byBounds.set_all_byBounds( Weights.Base.ValueBounds );
  }

  /**
   * @return {BoundsArraySet} Return a newly created BoundsArraySet which is a copy of this BoundsArraySet.
   */
  clone() {
    let result = new BoundsArraySet( this.input.length, this.output.length );
    result.set_byBoundsArraySet( this );
    return result;
  }

  /**
   * Set:
   *   - this.activationEscaping_ScaleArraySet to scale 1 (i.e. all are no scale).
   *   - this.bPassThrough[] to false (i.e. all are not pass-through).
   *
   * @return {BoundsArraySet}
   *   Return this (modified) object.
   */
  set_all_activationEscaping_bPassThrough_none() {
    this.activationEscaping_ScaleArraySet.set_all_byN( 1 );
    this.bPassThrough.fill( false );
    return this;
  }

  /**
   * @param {FloatValue.Bounds} aBounds
   *   Set all BoundsArray to the same as the specified aBounds. Set the this.activationEscaping_ScaleArraySet to default ( 1 );
   *
   * @return {BoundsArraySet}
   *   Return this (modified) object.
   */
  set_all_byBounds( aBounds ) {
    this.input.set_all_byBounds( aBounds );
    this.afterUndoPreviousActivationEscaping.set_all_byBounds( aBounds );
    this.afterFilter.set_all_byBounds( aBounds );
    this.afterBias.set_all_byBounds( aBounds );
    this.afterActivationEscaping.set_all_byBounds( aBounds );
    this.afterActivation.set_all_byBounds( aBounds );
    this.set_all_activationEscaping_bPassThrough_none();
    return this;
  }

  /**
   * @param {BoundsArray} inputBoundsArray
   *   The BoundsArray to be copied to .input and .afterUndoPreviousActivationEscaping.
   *
   * @param {BoundsArray} outputBoundsArray
   *   The BoundsArray to be copied to .afterFilter, .afterBias, .afterActivationEscaping, .afterActivation.
   *
   * @return {BoundsArraySet}
   *   Return this (modified) object.
   */
  set_all_byBoundsArray_input_output( inputBoundsArray, outputBoundsArray ) {
    this.input                              .set_all_byBoundsArray( inputBoundsArray );
    this.afterUndoPreviousActivationEscaping.set_all_byBoundsArray( inputBoundsArray );
    this.afterFilter                        .set_all_byBoundsArray( outputBoundsArray );
    this.afterBias                          .set_all_byBoundsArray( outputBoundsArray );
    this.afterActivationEscaping            .set_all_byBoundsArray( outputBoundsArray );
    this.afterActivation                    .set_all_byBoundsArray( outputBoundsArray );
    this.set_all_activationEscaping_bPassThrough_none();
    return this;
  }

  /**
   * @param {BoundsArray} aBoundsArray
   *   The BoundsArray to be copied. It will be copied to all BoundsArray in this object.
   *
   * @return {BoundsArraySet}
   *   Return this (modified) object.
   */
  set_all_byBoundsArray( aBoundsArray ) {
    return this.set_all_byBoundsArray_input_output( aBoundsArray, aBoundsArray );
  }

  /**
   * @param {BoundsArray} inputBoundsArray0
   *   The BoundsArray of the 1st input.
   *
   * @param {BoundsArray} inputBoundsArray1
   *   The BoundsArray of the 2nd input.
   *
   * @return {BoundsArraySet}
   *   Return a newly created object.
   */
  static create_byBoundsArray_concat_input0_input1( inputBoundsArray0, inputBoundsArray1 ) {
    let rLength = inputBoundsArray0.length + inputBoundsArray1.length;
    let rBoundsArraySet = new BoundsArraySet( rLength, rLength );

    // Concat value bounds array.
    rBoundsArraySet.input.set_all_byBoundsArray_concat_input0_input1( inputBoundsArray0, inputBoundsArray1 );

    // Spread to all value bounds array.
    return rBoundsArraySet.set_all_byBoundsArray_input_output( rBoundsArraySet.input, rBoundsArraySet.input );
  }

  /**
   * @param {BoundsArraySet} aBoundsArraySet
   *   The BoundsArraySet to be copied.
   *
   * @return {BoundsArraySet}
   *   Return this (modified) object which is copied from aBoundsArraySet.
   */
  set_all_byBoundsArraySet( aBoundsArraySet ) {
    this.input                              .set_all_byBoundsArray( aBoundsArraySet.input );
    this.afterUndoPreviousActivationEscaping.set_all_byBoundsArray( aBoundsArraySet.afterUndoPreviousActivationEscaping );
    this.afterFilter                        .set_all_byBoundsArray( aBoundsArraySet.afterFilter );
    this.afterBias                          .set_all_byBoundsArray( aBoundsArraySet.afterBias );
    this.afterActivationEscaping            .set_all_byBoundsArray( aBoundsArraySet.afterActivationEscaping );
    this.afterActivation                    .set_all_byBoundsArray( aBoundsArraySet.afterActivation );
    this.activationEscaping_ScaleArraySet.set_byScaleArraySet( aBoundsArraySet.activationEscaping_ScaleArraySet );

    for ( let i = 0; i < this.bPassThrough.length; ++i ) {
      this.bPassThrough[ i ] = aBoundsArraySet.bPassThrough[ i ];
    }

    return this;
  }

  /**
   * Determine .activationEscaping_ScaleArraySet and .afterActivationEscaping and .afterActivation, by .afterBias and .bPassThrough and nActivationId.
   *
   * The following properties will be used:
   *   - this.afterBias
   *   - this.bPassThrough
   *
   * The following properties will be modified:
   *   - this.activationEscaping_ScaleArraySet
   *   - this.afterActivationEscaping
   *   - this.afterActivation
   *
   * @param {number} nActivationId
   *   The activation function id (ValueDesc.ActivationFunction.Singleton.Ids.Xxx) of this depthwise convolution.
   *
   * @return {BoundsArraySet}
   *   Return this (modified) object.
   */
  set_activationEscaping_afterActivationEscaping_afterActivation_by_afterBias_bPassThrough_nActivationId( nActivationId ) {
    let theActivationFunctionInfo = ValueDesc.ActivationFunction.Singleton.getInfoById( nActivationId );

    for ( let outChannel = 0; outChannel < this.afterBias.length; ++outChannel ) {

      // 1. Determine .activationEscaping_ScaleArraySet
      {
        // 1.1 Determine .do

        if ( nActivationId == ValueDesc.ActivationFunction.Singleton.Ids.NONE ) {

          // Since no activation function, no need to escape. (i.e. scale = 1 for no scale)
          this.activationEscaping_ScaleArraySet.do.set_one_byN( outChannel, 1 );

        } else {

          if ( this.bPassThrough[ outChannel ] ) { // For pass-through half channels.

            // Calculate the scale for escaping bias result from activation function's non-linear domain into linear domain.
            //
            // Note: This does not work for avg/max pooling.
            this.activationEscaping_ScaleArraySet.do.set_one_by_fromLowerUpper_toLowerUpper( outChannel,
              this.afterBias.lowers[ outChannel ], this.afterBias.uppers[ outChannel ],
              theActivationFunctionInfo.inputDomainLinear.lower, theActivationFunctionInfo.inputDomainLinear.upper
            );

            let doEscapingScale = this.activationEscaping_ScaleArraySet.do.scales[ outChannel ];
            tf.util.assert( ( Number.isNaN( doEscapingScale ) == false ),
              `ConvBiasActivation.BoundsArraySet.`
                + `set_activationEscaping_afterActivationEscaping_afterActivation_by_afterBias_bPassThrough_nActivationId(): `
                + `this.activationEscaping_ScaleArraySet.do.scales[ ${outChannel} ] ( ${doEscapingScale} ) `
                + `should not be NaN. `
                + `Please use activation function (e.g. tanh()) which has both negative and positive parts near origin point.`
            );

          } else { // Non pass-through half channels.
            // Since non-pass-through, no need to escape. (i.e. scale = 1 for no scale)
            this.activationEscaping_ScaleArraySet.do.set_one_byN( outChannel, 1 );
          }
        }

        // 1.2 Determine .undo (Prepared for the next convolution-bias-activation. Not for this.)
        this.activationEscaping_ScaleArraySet.undo.set_one_byUndo_ScaleArray(
          outChannel, this.activationEscaping_ScaleArraySet.do, outChannel );
      }

      // 2. Determine .afterActivationEscaping
      this.afterActivationEscaping
        .set_one_byBoundsArray( outChannel, this.afterBias, outChannel )
        .multiply_one_byNs( outChannel, this.activationEscaping_ScaleArraySet.do.scales, outChannel );

      // 3. Determine .afterActivation
      {
        // If no activation function, the output range is determined by .afterActivationEscaping.
        if ( nActivationId == ValueDesc.ActivationFunction.Singleton.Ids.NONE ) {
          this.afterActivation.set_one_byBoundsArray( outChannel, this.afterActivationEscaping, outChannel )

        // Otherwise, the activation function dominates the output range.
        //
        // Note: Consider the implementation of ScaleArray.set_one_by_fromLowerUpper_toLowerUpper(), they are all not so good no
        //       matter using set_one_byXxx() or clamp_one_byXxx() of this.afterActivation. However, when using clamp_one_byXxx(),
        //       even if the activation function output range has Infinity (e.g. RELU is [ 0, +Infinity ]), the result bounds
        //       is more feasible (at least, will not become another bounds with Infinity).
        } else {
          if ( this.bPassThrough[ outChannel ] ) { // For pass-through half channels, it is clamped by the output range for linearDomainLinear.
            //this.afterActivation.set_one_byBounds( outChannel, theActivationFunctionInfo.outputRangeLinear );
            this.afterActivation.clamp_one_byBounds( outChannel, theActivationFunctionInfo.outputRangeLinear );

          } else { // Non pass-through half channels, it is clamped by the output range for the whole input domain.
            //this.afterActivation.set_one_byBounds( outChannel, theActivationFunctionInfo.outputRange );
            this.afterActivation.clamp_one_byBounds( outChannel, theActivationFunctionInfo.outputRange );
          }
        }
      }
    }

    return this;
  }

  /**
   * Rearrange output related channel information (.afterFilter, .afterBias, .afterActivationEscaping, .afterActivation,
   * .activationEscaping_ScaleArraySet, .bPassThrough) by interleaving as ( groupCount == 2 ). This element count must be even
   * (i.e. divisible by 2).
   *
   * @param {Array} arrayTemp
   *   A temporary array for placing the original elements temporarily. Provide this array could reduce memory re-allocation
   * and improve performance.
   *
   * @return {BoundsArraySet}
   *   Return this (modified) object.
   */
  output_interleave_asGrouptTwo( arrayTemp ) {
    this.afterFilter.interleave_asGrouptTwo( arrayTemp );
    this.afterBias.interleave_asGrouptTwo( arrayTemp );
    this.afterActivationEscaping.interleave_asGrouptTwo( arrayTemp );
    this.afterActivation.interleave_asGrouptTwo( arrayTemp );
    this.activationEscaping_ScaleArraySet.interleave_asGrouptTwo( arrayTemp );
    FloatValue.ArrayInterleaver.interleave_asGrouptTwo( this.bPassThrough, 0, this.bPassThrough.length, arrayTemp );
    return this;
  }

  get intput0() {
    return this.inputs[ 0 ];
  }

  get output0() {
    return this.outputs[ 0 ];
  }

}

<|endoftext|>


<commit_before>'use strict';
var userMangas = {};
var api = api;
var user = user;
var token = token;
var userObj = {};

/* Clear mangas from view
 * Removes manga from all views and lists.
 */
function cleanMangas() {
    $('.list').empty();
    $('.list2').empty();
    $('.list3').empty();
    $('.list4').empty();
}

/* Clear User Info from view
 * Removes user info from all views and lists.
 */
function cleanUser() {
    $('.user-name').empty();
    $('.user-email').empty();
    $('.user-count').empty();
}

/* Manga panels generator
 * Takes a manga object and creates custom html
 * to be added to the dom.
 */
function mangaInfo(manga) {
    var inputClass = 'input-' + window.s.slugify(manga.title);
    var panelClass = 'panel-' + window.s.slugify(manga.title);
    var dataChapter = [inputClass, manga.title];
    var dataDel = [panelClass, manga.title];

    var title = window.s.titleize(manga.title);

    var photo = '<img class="activator thumbnail" src="' + manga.thumbnail + '"';

    var author = ' Author: ' + window.s.titleize(manga.author) + '
';

    var status = ' Series Status: ' + window.s.humanize(manga.seriesStatus) + '  ';

    var userStats = ' My Status: ' + window.s.humanize(manga.userStatus) + '';

    var chapter = ' Current Chapter: ' + '' + manga.chapter + '   ';

    var type = ' Type: ' + window.s.humanize(manga.type) + '
';

    var direction = 'Reading Direction:' + ' ' + window.s.titleize(manga.direction) + '
';

    var altName = ' Other Names: ' + window.s.titleize(window.s.toSentence(manga.altName, ', ', ', ')) + '

';

    var categories = ' Categories: ' + window.s.titleize(window.s.toSentence(manga.categories, ', ', ', ')) + '';

    var plot = '
 Plot: ' + window.s.humanize(manga.plot) + '

';

    var addOne = '' + 'plus_one';

    var del = 'delete';

    var upd = 'update';

    var buttons = '
' + '
' + del + addOne + upd + '

';

    var html = '

' + photo + '

 ' + title + 'more_vert
' + chapter + direction + '

' + buttons + '

' + title + '

close ' + altName + author + status + userStats + type + categories + plot + '

';

    return html;
}

/* Manga object constructor
 * Used to create a curated manga without saving
 * sensitive information like _id and user id.
 */
function createManga(title, author, url, userStatus, type, categories, chapter,
    seriesStatus, plot, altName, direction, thumbnail) {
    this.title = title,
        this.author = author,
        this.url = url,
        this.userStatus = userStatus,
        this.type = type,
        this.categories = categories,
        this.chapter = chapter,
        this.seriesStatus = seriesStatus,
        this.plot = plot,
        this.altName = altName,
        this.direction = direction,
        this.thumbnail = thumbnail;
}

/* Get list of all user's mangas
 * Sends a GET request to the API and generates an oject
 * directory with curated manga information, sensitive data
 * is not kept.
 */

function getMangas() {
    var settings = {
        'async': true,
        'crossDomain': true,
        'url': api + '/mangas/' + user.toLowerCase(),
        'method': 'GET',
        'headers': {
            'x-access-token': token
        }
    };

    $.ajax(settings).done(function(mangas) {
        cleanMangas();
        mangas.data.map(function(manga) {
            var newManga = new createManga(manga.title, manga.author, manga.url,
                manga.userStatus, manga.type, manga.categories, manga.chapter,
                manga.seriesStatus, manga.plot, manga.altName, manga.direction,
                manga.thumbnail);

            userMangas[manga.title] = newManga;
            var html = mangaInfo(newManga);

            if (newManga.userStatus === 'reading') {
                $('.list2').append(html);
            } else if (newManga.userStatus === 'finished') {
                $('.list3').append(html);
            } else if (newManga.userStatus === 'will read') {
                $('.list4').append(html);
            }
            $('.list').append(html);
        });
        $('.tooltipped').tooltip({
            delay: 50
        });
    });
}

// Get User Information
function getUserInfo() {
    var settings = {
        'async': true,
        'crossDomain': true,
        'url': api + '/users/' + user.toLowerCase(),
        'method': 'GET',
        'headers': {
            'x-access-token': token
        }
    };

    $.ajax(settings).done(function(response) {
        var userInfo = response.data[0];
        userObj.firstname = window.s.titleize(userInfo.firstname);
        userObj.lastname = window.s.titleize(userInfo.lastname);
        userObj.email = userInfo.email;
        userObj.count = Object.keys(userMangas).length;
        var userName = '
' + 'Full Name:

 ' + userObj.firstname + ' ' + userObj.lastname + '
';
        var userEmail = '
E-Mail:

 ' + userInfo.email + '
';
        var userCount = '
' + 'Total Manga Count:

 ' + userObj.count + '
';
        cleanUser();
        $('.user-name').append(userName);
        $('.user-email').append(userEmail);
        $('.user-count').append(userCount);

    });
}
/* Load Manga info When Page Is Loaded
 * When the profile page is read it will
 * call the function to get all mangas.
 */
$(document).ready(function() {
    $('.button-collapse').sideNav();
    $('.modal-trigger').leanModal({
        dismissible: true, // Modal can be dismissed by clicking outside of it
        opacity: .5, // Opacity of modal background
        in_duration: 300, // Transition in duration
        ready: getUserInfo, // Callback for Modal open
        out_duration: 200 // Transition out duration
    });
    if (window.location.pathname === '/user/' + user.toLowerCase()) {
        getMangas();
    }
});

/* Increment chapter number
 * Sends a PUT to the API with the new chapter number.
 */
function oneUp(info) {
    var manga = info.split(','); // Split string into array
    var mangaClass = '.' + manga[0]; // Select the class
    var mangaTitle = manga[1];
    var currentChapter = +$(mangaClass + ':first').text(); // Get value as int
    var newChapter = currentChapter + 1; // increment chapter

    var settings = {
        'async': true,
        'crossDomain': true,
        'url': api + '/mangas/' + user.toLowerCase() + '/' +
            encodeURIComponent(mangaTitle),
        'method': 'PUT',
        'headers': {
            'x-access-token': token,
            'content-type': 'application/x-www-form-urlencoded'
        },
        'data': {
            'chapter': newChapter
        }
    };

    $.ajax(settings).done(function() {
        // Update chapter number in place
        userMangas[mangaTitle].chapter = newChapter;
        $(mangaClass).text(newChapter); // updates chapter for all catagories
    });
}

/* Sends request to delete manga from DB
 * and from the DOM itself
 */

function delManga(info) {
    // Sends delete request and removes the manga from the DOM
    var manga = info.split(','); // Split string into array
    var mangaClass = '.' + manga[0]; // Select the class
    var mangaTitle = manga[1];

    var settings = {
        'async': true,
        'crossDomain': true,
        'url': api + '/mangas/' + user.toLowerCase() + '/' +
            encodeURIComponent(mangaTitle),
        'method': 'DELETE',
        'headers': {
            'x-access-token': token,
            'content-type': 'application/x-www-form-urlencoded'
        }
    };

    // When the DEL request is done, delete from DOM
    $.ajax(settings).done(function() {
        $(mangaClass).remove();
    });
}

/* Search Bar Functionality
 * Search for mangas on keypress.
 * Search by author, title, alt name, and categories.
 */

function search() {
    if ($('#search').val().length > 0) {
        $('.manga-panel').css('display', 'none');
        // Display the results
        displayResults();
    } else {
        // display everything again
        $('.manga-panel').css('display', 'block');
    }

    $('#search').unbind('keyup');
    $('#search').keyup(search);
}

/* Display Search Results
 * part of the search function.
 */
function displayResults() {
    var reg = new RegExp($('#search').val(), 'ig');
    for (var manga in userMangas) {
        if (reg.test(userMangas[manga].title) ||
            reg.test(userMangas[manga].altName) ||
            reg.test(userMangas[manga].categories) ||
            reg.test(userMangas[manga].author) ||
            reg.test(userMangas[manga].type)) {
            $('.panel-' + window.s.slugify(userMangas[manga].title))
                .css('display', 'block');
        }
    }
}
/* Start Search
 * When a key is released call the function.
 */

$('#search').keyup(search);

/* Live Thubnail Preview
 * When a key is release and the image input has text
 * then use that text as the new src attribute for the image.
 */
function previewThumbnail() {
    var img = $('#img-input').val();
    if ($('#img-input').val().length > 4) {
        $('#img').attr('src', img);
    } else {
        $('#img').attr('src', '../../images/kaneki1.jpg');
    }

    $('#img-input').unbind('keyup');
    $('#img-input').keyup(previewThumbnail);
}

$('#img-input').keyup(previewThumbnail);<commit_msg>Shave lines off<commit_after>'use strict';
var userMangas = {};
var api = api;
var user = user;
var token = token;
var userObj = {};

/* Clear mangas from view Removes manga from all views and lists. */
function cleanMangas() {
    $('.list').empty();
    $('.list2').empty();
    $('.list3').empty();
    $('.list4').empty();
}

/* Clear User Info from view Removes user info from all views and lists. */
function cleanUser() {
    $('.user-name').empty();
    $('.user-email').empty();
    $('.user-count').empty();
}

/* Manga panels generator: Takes a manga object and creates custom html to be added to the dom. */
function mangaInfo(manga) {
    var inputClass = 'input-' + window.s.slugify(manga.title);
    var panelClass = 'panel-' + window.s.slugify(manga.title);
    var dataChapter = [inputClass, manga.title];
    var dataDel = [panelClass, manga.title];
    var title = window.s.titleize(manga.title);
    var photo = '<img class="activator thumbnail" src="' + manga.thumbnail + '"';
    var author = ' Author: ' + window.s.titleize(manga.author) + '
';
    var status = ' Series Status: ' + window.s.humanize(manga.seriesStatus) + '  ';
    var userStats = ' My Status: ' + window.s.humanize(manga.userStatus) + '';
    var chapter = ' Current Chapter: ' + '' + manga.chapter + '   ';
    var type = ' Type: ' + window.s.humanize(manga.type) + '
';
    var direction = 'Reading Direction:' + ' ' + window.s.titleize(manga.direction) + '
';
    var altName = ' Other Names: ' + window.s.titleize(window.s.toSentence(manga.altName, ', ', ', ')) + '

';
    var categories = ' Categories: ' + window.s.titleize(window.s.toSentence(manga.categories, ', ', ', ')) + '';
    var plot = '
 Plot: ' + window.s.humanize(manga.plot) + '

';
    var addOne = '' + 'plus_one';
    var del = 'delete';
    var upd = 'update';
    var buttons = '
' + '
' + del + addOne + upd + '

';
    var html = '

' + photo + '

 ' + title + 'more_vert
' + chapter + direction + '

' + buttons + '

' + title + '

close ' + altName + author + status + userStats + type + categories + plot + '

';
    return html;
}

/* Manga object constructor: Used to create a curated manga without saving sensitive information like _id and user id. */
function createManga(title, author, url, userStatus, type, categories, chapter,
    seriesStatus, plot, altName, direction, thumbnail) {
    this.title = title,
        this.author = author,
        this.url = url,
        this.userStatus = userStatus,
        this.type = type,
        this.categories = categories,
        this.chapter = chapter,
        this.seriesStatus = seriesStatus,
        this.plot = plot,
        this.altName = altName,
        this.direction = direction,
        this.thumbnail = thumbnail;
}

/* Get list of all user's mangas: Sends a GET request to the API and generates an oject directory with curated manga information, sensitive data is not kept. */
function getMangas() {
    var settings = {
        'async': true,
        'crossDomain': true,
        'url': api + '/mangas/' + user.toLowerCase(),
        'method': 'GET',
        'headers': {
            'x-access-token': token
        }
    };
    $.ajax(settings).done(function(mangas) {
        cleanMangas();
        mangas.data.map(function(manga) {
            var newManga = new createManga(manga.title, manga.author, manga.url,
                manga.userStatus, manga.type, manga.categories, manga.chapter,
                manga.seriesStatus, manga.plot, manga.altName, manga.direction,
                manga.thumbnail);
            userMangas[manga.title] = newManga;
            var html = mangaInfo(newManga);
            if (newManga.userStatus === 'reading') {
                $('.list2').append(html);
            } else if (newManga.userStatus === 'finished') {
                $('.list3').append(html);
            } else if (newManga.userStatus === 'will read') {
                $('.list4').append(html);
            }
            $('.list').append(html);
        });
        $('.tooltipped').tooltip({
            delay: 50
        });
    });
}

// Get User Information
function getUserInfo() {
    var settings = {
        'async': true,
        'crossDomain': true,
        'url': api + '/users/' + user.toLowerCase(),
        'method': 'GET',
        'headers': {
            'x-access-token': token
        }
    };
    $.ajax(settings).done(function(response) {
        var userInfo = response.data[0];
        userObj.firstname = window.s.titleize(userInfo.firstname);
        userObj.lastname = window.s.titleize(userInfo.lastname);
        userObj.email = userInfo.email;
        userObj.count = Object.keys(userMangas).length;
        var userName = '
' + 'Full Name:

 ' + userObj.firstname + ' ' + userObj.lastname + '
';
        var userEmail = '
E-Mail:

 ' + userInfo.email + '
';
        var userCount = '
' + 'Total Manga Count:

 ' + userObj.count + '
';
        cleanUser();
        $('.user-name').append(userName);
        $('.user-email').append(userEmail);
        $('.user-count').append(userCount);
    });
}
/* Load Manga info When Page Is Loaded: When the profile page is read it will call the function to get all mangas. */
$(document).ready(function() {
    $('.button-collapse').sideNav();
    $('.modal-trigger').leanModal({
        dismissible: true, // Modal can be dismissed by clicking outside of it
        opacity: .5, // Opacity of modal background
        in_duration: 300, // Transition in duration
        ready: getUserInfo, // Callback for Modal open
        out_duration: 200 // Transition out duration
    });
    if (window.location.pathname === '/user/' + user.toLowerCase()) {
        getMangas();
    }
});

/* Increment chapter number: Sends a PUT to the API with the new chapter number. */
function oneUp(info) {
    var manga = info.split(','); // Split string into array
    var mangaClass = '.' + manga[0]; // Select the class
    var mangaTitle = manga[1];
    var currentChapter = +$(mangaClass + ':first').text(); // Get value as int
    var newChapter = currentChapter + 1; // increment chapter
    var settings = {
        'async': true,
        'crossDomain': true,
        'url': api + '/mangas/' + user.toLowerCase() + '/' +
            encodeURIComponent(mangaTitle),
        'method': 'PUT',
        'headers': {
            'x-access-token': token,
            'content-type': 'application/x-www-form-urlencoded'
        },
        'data': {
            'chapter': newChapter
        }
    };
    $.ajax(settings).done(function() {
        userMangas[mangaTitle].chapter = newChapter;
        $(mangaClass).text(newChapter); // updates chapter for all catagories
    });
}

/* Sends request to delete manga from DB and from the DOM itself */
function delManga(info) {
    var manga = info.split(','); // Split string into array
    var mangaClass = '.' + manga[0]; // Select the class
    var mangaTitle = manga[1];
    var settings = {
        'async': true,
        'crossDomain': true,
        'url': api + '/mangas/' + user.toLowerCase() + '/' +
            encodeURIComponent(mangaTitle),
        'method': 'DELETE',
        'headers': {
            'x-access-token': token,
            'content-type': 'application/x-www-form-urlencoded'
        }
    };
    $.ajax(settings).done(function() {
        $(mangaClass).remove();
    });
}

/* Search Bar Functionality: Search for mangas on keypress. Search by author, title, alt name, and categories. */
function search() {
    if ($('#search').val().length > 0) {
        $('.manga-panel').css('display', 'none');
        displayResults();
    } else {
        $('.manga-panel').css('display', 'block');
    }
    $('#search').unbind('keyup');
    $('#search').keyup(search);
}

/* Display Search Results, part of the search function.
 */
function displayResults() {
    var reg = new RegExp($('#search').val(), 'ig');
    for (var manga in userMangas) {
        if (reg.test(userMangas[manga].title) ||
            reg.test(userMangas[manga].altName) ||
            reg.test(userMangas[manga].categories) ||
            reg.test(userMangas[manga].author) ||
            reg.test(userMangas[manga].type)) {
            $('.panel-' + window.s.slugify(userMangas[manga].title))
                .css('display', 'block');
        }
    }
}
/* Start Search when a key is released call the function. */
$('#search').keyup(search);

/* Live Thubnail Preview: When a key is release and the image input has text then use that text as the new src attribute for the image. */
function previewThumbnail() {
    var img = $('#img-input').val();
    if ($('#img-input').val().length > 4) {
        $('#img').attr('src', img);
    } else {
        $('#img').attr('src', '../../images/kaneki1.jpg');
    }
    $('#img-input').unbind('keyup');
    $('#img-input').keyup(previewThumbnail);
}

$('#img-input').keyup(previewThumbnail);<|endoftext|>


<commit_before>// TODO: transfer Save, Print, Close functions from older forms. *needs server access
app.service('$eea', ['$http', '$compile', function($http, $compile) {
	return {
		test: function($scope) {
			return 'test' + $scope;
		},
		init: function($scope) {
			//preserialisation: all form fields will be available on filed.form name.field name
			$scope.field = {};
			//Multiple Rows counter
			$scope.multipleIndex = 1;
			//Multiple Forms/Groups/Rows placeholder Object
			$scope.groups = groups;
			//Validation labels switch
			$scope.ValidationDisabled = false;
			//Default language
			$scope.selectedLanguage = "en";
		},
		group: {
			addRow: function(frm, group, $scope) {
				$scope.multipleIndex = $scope.groups[frm][group].length;
				var id = ++$scope.multipleIndex;
				$scope.groups[frm][group].push(id);
				setTimeout(function() {
					$scope.$apply();
					$('#group-area-' + $scope.multipleIndex + '-' + group).prepend($compile('
<button type="button"  title="{{\'deleterow\' | translate}}"class="deleterowbutton" ng-click="deleteRow(\'' + frm + '\', \'' + group + '\', ' + id + ')"></button>
')($scope));
				}, 10);
			},
			deleteRow: function(frm, group, id, $scope) {
				$scope.groups[frm][group][$scope.groups[frm][group].indexOf(id)] = -1;
				$("#group-area-" + id + "-" + group).html("");
			}
		},
		form: {
			save : function ($scope) {
				$("form.eeaform").each(function(index, form){
					console.log("Testing form - Valid:", $scope[form.name].$valid);
					//TODO: Maybe a DB status flag (same Table or Lookup for activity logging? or Status in main table and Activity Table for action/date/user logging)
					//(i.e. 0 created/saved, 1 edited (+dateedited? extra field), 2 commited - leave it "open" for future statuses). In order to be Commited it needs to be valid. It can be saved without all elements completed but not commited.
					// Maybe an extra Commit button appearing in case server respond to last edit/save (and on form loading with saved data from server) that data are accepted and checked.
					// Is it possible to autogenerate the DB and REST services based on the XSD (or the other way around) so front-end and back-end automatch/autofunctioning together since they are constructed based on the same XSD file?
					// A kind of enviromental behaviour to tasks. If it can not be automated and just coexists transparently together with the other processes, then it doesn't do. Extra work and tickets every time.
					if (!$scope[form.name].$valid) {
						$("#" + form.name + " .ng-invalid").first().focus();
					}
				});
			},
			submit : function (frm, test, $scope) {
				for (var form in $scope.field) {
					if (frm) {
						if (frm.$name !== form) continue;
					}

					if (test) return false;

					var frmObj = $scope.field[form];
					var postContent = '';
					for (var element in frmObj) {
						postContent += "&" + encodeURIComponent(element) + "=" + encodeURIComponent(frmObj[element]);
					}

					$http({
						method: 'POST',
						url: $("#" + form).attr("eea-action"),
						headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
						data: postContent
					}).then(
						function(response) {
							console.log("response", response.status);
						},
						function(error) {
							console.log("error", error.status);
						});
				}

				return false;
			}
		}
	};
}]);<commit_msg>Updated Comments in EEA Toolbar Component<commit_after>// TODO: transfer Save, Print, Close functions from older forms. *needs server access
app.service('$eea', ['$http', '$compile', function($http, $compile) {
	return {
		test: function($scope) {
			return 'test' + $scope;
		},
		init: function($scope) {
			//preserialisation: all form fields will be available on filed.form name.field name
			$scope.field = {};
			//Multiple Rows counter - It works only In case of different $scope for each form. TODO: same scope
			$scope.multipleIndex = 1;
			//Multiple Forms/Groups/Rows placeholder Object
			$scope.groups = groups;
			//Validation labels switch
			$scope.ValidationDisabled = false;
			//Default language
			$scope.selectedLanguage = "en";
		},
		group: {
			addRow: function(frm, group, $scope) {
				$scope.multipleIndex = $scope.groups[frm][group].length;
				var id = ++$scope.multipleIndex;
				$scope.groups[frm][group].push(id);
				setTimeout(function() {
					$scope.$apply();
					$('#group-area-' + $scope.multipleIndex + '-' + group).prepend($compile('
<button type="button"  title="{{\'deleterow\' | translate}}"class="deleterowbutton" ng-click="deleteRow(\'' + frm + '\', \'' + group + '\', ' + id + ')"></button>
')($scope));
				}, 10);
			},
			deleteRow: function(frm, group, id, $scope) {
				$scope.groups[frm][group][$scope.groups[frm][group].indexOf(id)] = -1;
				$("#group-area-" + id + "-" + group).html("");
			}
		},
		form: {
			save : function ($scope) {
				$("form.eeaform").each(function(index, form){
					console.log("Testing form - Valid:", $scope[form.name].$valid);
					//TODO: Maybe a DB status flag (same Table or Lookup for activity logging? or Status in main table and Activity Table for action/date/user logging)
					//(i.e. 0 created/saved, 1 edited (+dateedited? extra field), 2 commited - leave it "open" for future statuses). In order to be Commited it needs to be valid. It can be saved without all elements completed but not commited.
					// Maybe an extra Commit button appearing in case server respond to last edit/save (and on form loading with saved data from server) that data are accepted and checked.
					// Is it possible to autogenerate the DB and REST services based on the XSD (or the other way around) so front-end and back-end automatch/autofunctioning together since they are constructed based on the same XSD file?
					// A kind of enviromental behaviour to tasks. If it can not be automated and just coexists transparently together with the other processes, then it doesn't do. Extra work and tickets every time.
					if (!$scope[form.name].$valid) {
						$("#" + form.name + " .ng-invalid").first().focus();
					}
				});
			},
			submit : function (frm, test, $scope) {
				for (var form in $scope.field) {
					if (frm) {
						if (frm.$name !== form) continue;
					}

					if (test) return false;

					var frmObj = $scope.field[form];
					var postContent = '';
					for (var element in frmObj) {
						postContent += "&" + encodeURIComponent(element) + "=" + encodeURIComponent(frmObj[element]);
					}

					$http({
						method: 'POST',
						url: $("#" + form).attr("eea-action"),
						headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
						data: postContent
					}).then(
						function(response) {
							console.log("response", response.status);
						},
						function(error) {
							console.log("error", error.status);
						});
				}

				return false;
			}
		}
	};
}]);<|endoftext|>


<commit_before>var test_helper = require('../support/test_helper');
var RedisPool = require('redis-mpool');
var querystring = require('querystring');

var assert = require('../support/assert');
var mapnik = require('windshaft').mapnik;
var CartodbWindshaft = require(__dirname + '/../../lib/cartodb/server');
var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
var server = new CartodbWindshaft(serverOptions);
var TemplateMaps = require('../../lib/cartodb/backends/template_maps.js');
var NamedMapsCacheEntry = require('../../lib/cartodb/cache/model/named_maps_entry');

describe('named maps authentication', function() {
    // configure redis pool instance to use in tests
    var redisPool = new RedisPool(global.environment.redis);

    var templateMaps = new TemplateMaps(redisPool, {
        max_user_templates: global.environment.maxUserTemplates
    });

    var wadusLayer = {
        type: 'cartodb',
        options: {
            sql: 'select 1 cartodb_id, null::geometry the_geom_webmercator',
            cartocss: '#layer { marker-fill: <%= color %>; }',
            cartocss_version: '2.3.0'
        }
    };

    var username = 'localhost';

    var templateName = 'valid_template';
    var template = {
        version: '0.0.1',
        name: templateName,
        auth: {
            method: 'open'
        },
        "placeholders": {
            "color": {
                "type": "css_color",
                "default": "#cc3300"
            }
        },
        layergroup: {
            layers: [
                wadusLayer
            ]
        }
    };

    var tokenAuthTemplateName = 'auth_valid_template';
    var tokenAuthTemplate = {
        version: '0.0.1',
        name: tokenAuthTemplateName,
        auth: {
            method: 'token',
            valid_tokens: ['valid1', 'valid2']
        },
        placeholders: {
            color: {
                "type": "css_color",
                "default": "#cc3300"
            }
        },
        layergroup: {
            layers: [
                wadusLayer
            ]
        }
    };

    var namedMapLayer = {
        type: 'named',
        options: {
            name: templateName,
            config: {},
            auth_tokens: []
        }
    };

    var nestedNamedMapTemplateName = 'nested_template';
    var nestedNamedMapTemplate = {
        version: '0.0.1',
        name: nestedNamedMapTemplateName,
        auth: {
            method: 'open'
        },
        layergroup: {
            layers: [
                namedMapLayer
            ]
        }
    };

    before(function (done) {
        templateMaps.addTemplate(username, nestedNamedMapTemplate, function (err) {
            if (err) {
                return done(err);
            }
            templateMaps.addTemplate(username, tokenAuthTemplate, function (err) {
                if (err) {
                    return done(err);
                }
                templateMaps.addTemplate(username, template, function (err) {
                    return done(err);
                });
            });
        });
    });

    after(function (done) {
        templateMaps.delTemplate(username, nestedNamedMapTemplateName, function (err) {
            if (err) {
                return done(err);
            }
            templateMaps.delTemplate(username, tokenAuthTemplateName, function (err) {
                if (err) {
                    return done(err);
                }
                templateMaps.delTemplate(username, templateName, function (err) {
                    return done(err);
                });
            });
        });
    });

    function getNamedTile(name, z, x, y, options, callback) {

        var url = '/api/v1/map/named/' + name + '/all/' + [z,x,y].join('/') + '.png';
        if (options.params) {
            url = url + '?' + querystring.stringify(options.params);
        }
        var requestOptions = {
            url: url,
            method: 'GET',
            headers: {
                host: username
            },
            encoding: 'binary'
        };

        var statusCode = options.status || 200;

        var expectedResponse = {
            status: statusCode,
            headers: {
                'Content-Type': statusCode === 200 ? 'image/png' : 'application/json; charset=utf-8'
            }
        };

        assert.response(server,
            requestOptions,
            expectedResponse,
            function (res, err) {
                var img;
                if (!err && res.headers['content-type'] === 'image/png') {
                    img = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
                }
                return callback(err, res, img);
            }
        );
    }

    describe('tiles', function() {
        it('should return a 404 error for nonexistent template name', function (done) {
            var nonexistentName = 'nonexistent';
            getNamedTile(nonexistentName, 0, 0, 0, { status: 404 }, function(err, res) {
                assert.ok(!err);
                assert.deepEqual(
                    JSON.parse(res.body),
                    { errors: ["Template '" + nonexistentName + "' of user '" + username + "' not found"] }
                );
                done();
            });
        });

        it('should return 403 if not properly authorized', function(done) {
            getNamedTile(tokenAuthTemplateName, 0, 0, 0, { status: 403 }, function(err, res) {
                assert.ok(!err);
                assert.deepEqual(JSON.parse(res.body), { errors: ['Unauthorized template instantiation'] });
                done();
            });
        });

        it('should return 200 if properly authorized', function(done) {
            getNamedTile(tokenAuthTemplateName, 0, 0, 0, { params: { auth_token: 'valid1' } }, function(err, res, img) {
                assert.equal(img.width(), 256);
                assert.equal(img.height(), 256);

                assert.ok(!err);
                test_helper.checkSurrogateKey(res, new NamedMapsCacheEntry(username, tokenAuthTemplateName).key());
                done();
            });
        });
    });

    function getStaticMap(name, options, callback) {

        var url = '/api/v1/map/static/named/' + name + '/640/480.png';
        if (options.params) {
            url = url + '?' + querystring.stringify(options.params);
        }
        var requestOptions = {
            url: url,
            method: 'GET',
            headers: {
                host: username
            },
            encoding: 'binary'
        };

        var statusCode = options.status || 200;

        var expectedResponse = {
            status: statusCode,
            headers: {
                'Content-Type': statusCode === 200 ? 'image/png' : 'application/json; charset=utf-8'
            }
        };

        assert.response(server,
            requestOptions,
            expectedResponse,
            function (res, err) {
                var img;
                if (!err && res.headers['content-type'] === 'image/png') {
                    img = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
                }
                return callback(err, res, img);
            }
        );
    }
    describe('static maps', function() {
        it('should return a 404 error for nonexistent template name', function (done) {
            var nonexistentName = 'nonexistent';
            getStaticMap(nonexistentName, { status: 404 }, function(err, res) {
                assert.ok(!err);
                assert.deepEqual(
                    JSON.parse(res.body),
                    { errors: ["Template '" + nonexistentName + "' of user '" + username + "' not found"] }
                );
                done();
            });
        });

        it('should return 403 if not properly authorized', function(done) {
            getStaticMap(tokenAuthTemplateName, { status: 403 }, function(err, res) {
                assert.ok(!err);
                assert.deepEqual(JSON.parse(res.body), { errors: ['Unauthorized template instantiation'] });
                done();
            });
        });

        it('should return 200 if properly authorized', function(done) {
            getStaticMap(tokenAuthTemplateName, { params: { auth_token: 'valid1' } }, function(err, res, img) {
                assert.ok(!err);

                assert.equal(img.width(), 640);
                assert.equal(img.height(), 480);

                test_helper.checkSurrogateKey(res, new NamedMapsCacheEntry(username, tokenAuthTemplateName).key());
                done();
            });
        });
    });

});
<commit_msg>Remove redis keys after each test<commit_after>var test_helper = require('../support/test_helper');
var RedisPool = require('redis-mpool');
var querystring = require('querystring');

var assert = require('../support/assert');
var mapnik = require('windshaft').mapnik;
var CartodbWindshaft = require(__dirname + '/../../lib/cartodb/server');
var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
var server = new CartodbWindshaft(serverOptions);
var TemplateMaps = require('../../lib/cartodb/backends/template_maps.js');
var NamedMapsCacheEntry = require('../../lib/cartodb/cache/model/named_maps_entry');

describe('named maps authentication', function() {
    // configure redis pool instance to use in tests
    var redisPool = new RedisPool(global.environment.redis);

    var templateMaps = new TemplateMaps(redisPool, {
        max_user_templates: global.environment.maxUserTemplates
    });

    var wadusLayer = {
        type: 'cartodb',
        options: {
            sql: 'select 1 cartodb_id, null::geometry the_geom_webmercator',
            cartocss: '#layer { marker-fill: <%= color %>; }',
            cartocss_version: '2.3.0'
        }
    };

    var username = 'localhost';

    var templateName = 'valid_template';
    var template = {
        version: '0.0.1',
        name: templateName,
        auth: {
            method: 'open'
        },
        "placeholders": {
            "color": {
                "type": "css_color",
                "default": "#cc3300"
            }
        },
        layergroup: {
            layers: [
                wadusLayer
            ]
        }
    };

    var tokenAuthTemplateName = 'auth_valid_template';
    var tokenAuthTemplate = {
        version: '0.0.1',
        name: tokenAuthTemplateName,
        auth: {
            method: 'token',
            valid_tokens: ['valid1', 'valid2']
        },
        placeholders: {
            color: {
                "type": "css_color",
                "default": "#cc3300"
            }
        },
        layergroup: {
            layers: [
                wadusLayer
            ]
        }
    };

    var namedMapLayer = {
        type: 'named',
        options: {
            name: templateName,
            config: {},
            auth_tokens: []
        }
    };

    var nestedNamedMapTemplateName = 'nested_template';
    var nestedNamedMapTemplate = {
        version: '0.0.1',
        name: nestedNamedMapTemplateName,
        auth: {
            method: 'open'
        },
        layergroup: {
            layers: [
                namedMapLayer
            ]
        }
    };

    beforeEach(function (done) {
        templateMaps.addTemplate(username, nestedNamedMapTemplate, function (err) {
            if (err) {
                return done(err);
            }
            templateMaps.addTemplate(username, tokenAuthTemplate, function (err) {
                if (err) {
                    return done(err);
                }
                templateMaps.addTemplate(username, template, function (err) {
                    return done(err);
                });
            });
        });
    });

    afterEach(function (done) {
        templateMaps.delTemplate(username, nestedNamedMapTemplateName, function (err) {
            if (err) {
                return done(err);
            }
            templateMaps.delTemplate(username, tokenAuthTemplateName, function (err) {
                if (err) {
                    return done(err);
                }
                templateMaps.delTemplate(username, templateName, function (err) {
                    return done(err);
                });
            });
        });
    });

    function getNamedTile(name, z, x, y, options, callback) {

        var url = '/api/v1/map/named/' + name + '/all/' + [z,x,y].join('/') + '.png';
        if (options.params) {
            url = url + '?' + querystring.stringify(options.params);
        }
        var requestOptions = {
            url: url,
            method: 'GET',
            headers: {
                host: username
            },
            encoding: 'binary'
        };

        var statusCode = options.status || 200;

        var expectedResponse = {
            status: statusCode,
            headers: {
                'Content-Type': statusCode === 200 ? 'image/png' : 'application/json; charset=utf-8'
            }
        };

        assert.response(server,
            requestOptions,
            expectedResponse,
            function (res, err) {
                var img;
                if (!err && res.headers['content-type'] === 'image/png') {
                    img = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
                }
                return callback(err, res, img);
            }
        );
    }

    describe('tiles', function() {
        it('should return a 404 error for nonexistent template name', function (done) {
            var nonexistentName = 'nonexistent';
            getNamedTile(nonexistentName, 0, 0, 0, { status: 404 }, function(err, res) {
                assert.ok(!err);
                assert.deepEqual(
                    JSON.parse(res.body),
                    { errors: ["Template '" + nonexistentName + "' of user '" + username + "' not found"] }
                );
                done();
            });
        });

        it('should return 403 if not properly authorized', function(done) {
            getNamedTile(tokenAuthTemplateName, 0, 0, 0, { status: 403 }, function(err, res) {
                assert.ok(!err);
                assert.deepEqual(JSON.parse(res.body), { errors: ['Unauthorized template instantiation'] });
                done();
            });
        });

        it('should return 200 if properly authorized', function(done) {
            getNamedTile(tokenAuthTemplateName, 0, 0, 0, { params: { auth_token: 'valid1' } }, function(err, res, img) {
                assert.equal(img.width(), 256);
                assert.equal(img.height(), 256);

                assert.ok(!err);
                test_helper.checkSurrogateKey(res, new NamedMapsCacheEntry(username, tokenAuthTemplateName).key());
                done();
            });
        });
    });

    function getStaticMap(name, options, callback) {

        var url = '/api/v1/map/static/named/' + name + '/640/480.png';
        if (options.params) {
            url = url + '?' + querystring.stringify(options.params);
        }
        var requestOptions = {
            url: url,
            method: 'GET',
            headers: {
                host: username
            },
            encoding: 'binary'
        };

        var statusCode = options.status || 200;

        var expectedResponse = {
            status: statusCode,
            headers: {
                'Content-Type': statusCode === 200 ? 'image/png' : 'application/json; charset=utf-8'
            }
        };

        assert.response(server,
            requestOptions,
            expectedResponse,
            function (res, err) {
                var img;
                if (!err && res.headers['content-type'] === 'image/png') {
                    img = mapnik.Image.fromBytes(new Buffer(res.body, 'binary'));
                }
                return callback(err, res, img);
            }
        );
    }
    describe('static maps', function() {
        it('should return a 404 error for nonexistent template name', function (done) {
            var nonexistentName = 'nonexistent';
            getStaticMap(nonexistentName, { status: 404 }, function(err, res) {
                assert.ok(!err);
                assert.deepEqual(
                    JSON.parse(res.body),
                    { errors: ["Template '" + nonexistentName + "' of user '" + username + "' not found"] }
                );
                done();
            });
        });

        it('should return 403 if not properly authorized', function(done) {
            getStaticMap(tokenAuthTemplateName, { status: 403 }, function(err, res) {
                assert.ok(!err);
                assert.deepEqual(JSON.parse(res.body), { errors: ['Unauthorized template instantiation'] });
                done();
            });
        });

        it('should return 200 if properly authorized', function(done) {
            getStaticMap(tokenAuthTemplateName, { params: { auth_token: 'valid1' } }, function(err, res, img) {
                assert.ok(!err);

                assert.equal(img.width(), 640);
                assert.equal(img.height(), 480);

                test_helper.checkSurrogateKey(res, new NamedMapsCacheEntry(username, tokenAuthTemplateName).key());
                done();
            });
        });
    });

});
<|endoftext|>