<commit_before>export { Base };

import * as TensorPlaceholder from "../TensorPlaceholder.js";

//!!! ...unfinished... (2022/05/30) Used by Operation.apply()
// class OperationApplyArgs {
//
// //   constructor() {
// //     this.blockInput0 =
// //     this.blockInput1 =
// //
// //     this.operationInput0 =
// //     this.operationInput1 =
// //     this.operationOutput0 =
// //     this.operationOutput1 =;
//   }
//
// }
//

/**
 * An object operates several TensorPlaceholder.Base.
 *
 *
 * @member {TensorPlaceholder.Base} input0
 *   The TensorPlaceholder object which represents this operation's first input. It (from constructor) will be kept (not cloned)
 * directly. So caller should not modify them.
 *
 * @member {TensorPlaceholder.Base} input1
 *   The TensorPlaceholder object which represents this operation's second input. It could be null which means this operation
 * does not have second input tensor. It (from constructor) will be kept (not cloned) directly. So caller should not modify them.
 *
 * @member {TensorPlaceholder.Base} output0
 *   The TensorPlaceholder object which represents this operation's first output. It will be created by constructor if
 * outputTensorCount (of constructor) is >= 1.
 *
 * @member {TensorPlaceholder.Base} output1
 *   The TensorOpCounter object which represents this operation's second output. It is only created by constructor if
 * outputTensorCount (of constructor) is >= 2.
 *

//!!! ...unfinished... (2022/06/02)

 * @member {function} apply
 *   This is a method. It processes this.input0.realTensor and this.input1.realTensor as inputTensors. It puts to this.output0.realTensor
 * as outputTensor. Both inputTensors are tf.tensor3d and represents an images ( height x width x channel ) which will be added. They
 * should have the same channel count. Their ( height x width ) should be either the same or one is ( 1 x 1 ). The outputTensor
 * (tf.tensor3d) represents the result of adding the two inputs. The inputTensors may or may not be disposed. In fact, this method
 * calls one of Add_and_keep0_keep1(), Add_and_keep0_destroy1(), Add_and_destroy0_keep1(), Add_and_destroy0_destroy1() according to
 * the parameters.
 */
let Base = ( ParentClass = Object ) => class extends ParentClass {

  /**
   * This constructor will register this operation as the input TensorHolder's last operation. So the construction order is important
   * because the last constructed Operation object will become the real last operation of the inputs.
   *
   *
   * @param {number} outputTensorCount
   *   If 0, no this.outputX will be created. If 1, only the this.output0 will be created. If 2, both the this.output0 and this.output1
   * will be created.
   */
  constructor( input0, input1, outputTensorCount ) {
    this.input0 = input0;
    this.input1 = input1;

    // Register as the input TensorPlaceholder's final user.
    {
      if ( this.input0 )
        this.input0.lastOperation = this;

      if ( this.input1 )
        this.input1.lastOperation = this;
    }

    // Create output TensorPlaceholder.
    {
      if ( outputTensorCount >= 1 ) {
        this.output0 = new TensorPlaceholder.Base();

        if ( outputTensorCount >= 2 ) {
          this.output1 = new TensorPlaceholder.Base();
        }
      }
    }
  }

  /**
   * Sub-class should override this method.
   *
   * Release all tensors.
   */
  disposeTensors() {
  }

  /**
   * Sub-class should override this method.
   *
   * The this.setKeepInputTensor_IfNotLastOperation_Or_In() will call this method. This method should adjust
   * this.apply so that this.apply() will or will not dispose its inputTensors.
   *
   * @param {boolean} bKeepInputTensor0
   *   Whether the .input0's tensor should be destroyed by this operation. It is ignored if .input0 does not exist.
   *
   * @param {boolean} bKeepInputTensor1
   *   Whether the .input1's tensor should be destroyed by this operation. It is ignored if .input1 does not exist.
   */
  setKeepInputTensor( bKeepInputTensor0, bKeepInputTensor1 ) {
    Base.setup_apply.call( this, bKeepInputTensor0, bKeepInputTensor1 );
  }

  /**
   * This method will call this.setKeepInputTensor() according to：
   *   - whether the operation is the last operation of the this.input0 / this.input1.
   *   - whether the this.input0 / this.input1 is in alwaysKeepSet.
   *
   * @param {Set<TensorPlaceholder.Base>} alwaysKeepSet
   *   A set object. Its every element is TensorPlaceholder.Base object. They represent tensors never be disposed. The this.input0
   * and this.input1 will be compared with them.
   */
  setKeepInputTensor_IfNotLastOperation_Or_In( alwaysKeepSet ) {

    // Note: If an input appears multiple times (i.e. ( this.input0 == this.input1 ); multiple inputs of this operation are the same),
    //       the input will be disposed multiple times.
    //
    tf.util.assert( ( this.input0 != this.input1 ),
      `Operation.Base.setKeepInputTensor_IfNotLastOperation_Or_In(): `
        + `input0 ( ${this.input0} ) and input1 ( ${this.input1} ) should be different objects.`
    );

    // If this operation is the last operation of the input tensor, this operation is responsible for disposing it.

    let input0_bNeedDispose;
    if (   ( this.input0 )
        && ( !alwaysKeepSet?.has( this.input0 ) ) // input in alwaysKeepSet should always be kept (always not to be disposed).
        && ( this.input0.lastOperation == this )
       ) {
      input0_bNeedDispose = true;

    } else {
      input0_bNeedDispose = false;
    }

    let input1_bNeedDispose;
    if (   ( this.input1 )
        && ( !alwaysKeepSet?.has( this.input1 ) ) // input in alwaysKeepSet should always be kept (always not to be disposed).
        && ( this.input1.lastOperation == this )
       ) {
      input1_bNeedDispose = true;

    } else {
      input1_bNeedDispose = false;
    }

    // Configure the operation to keep or dispose its inputs.
    this.setKeepInputTensor( input0_bNeedDispose, input1_bNeedDispose );
  }

  /**
   * Sub-class should override this method.
   *
   * This method should:
   *   - Use this.input0.realTensor (and this.input1.realTensor) to compute.
   *   - Place the result in this.output0.realTensor (and this.output1.realTensor)
   *
   */
  apply() {
  }

  /**
   * Sub-class should override this property.
   *
   * @return {number}
   *   The wieght count extracted from inputFloat32Array and used in tensors. Not including Params, because they are not used in
   * tensors. Not including inferenced weights (even if they are used in tensors), because they are not extracted from inputFloat32Array.
   */
  get tensorWeightCountExtracted() {
    return 0;
  }

  /**
   * Sub-class should override this property.
   *
   * @return {number}
   *   The total wieght count used in tensors. Not including Params, because they are not used in tensors. Including inferenced
   * weights, if they are used in tensors.
   */
  get tensorWeightCountTotal() {
    return 0;
  }

  /**
   * @return {number} inputTensorCount
   *   How many input tensor placeholders.
   */
  get inputTensorCount() {
    if ( this.input0 )
      if ( this.input1 )
        return 2;
      else
        return 1;
    else
      if ( this.input1 )
        return 1;
      else
        return 0; // (should not happen)
  }

  /**
   * @return {number}
   *   How many output tensor placeholders. It's value is between [ 0, 2 ].
   */
  get outputTensorCount() {
    if ( this.output0 )
      if ( this.output1 )
        return 2;
      else
        return 1;
    else
      if ( this.output1 )
        return 1;
      else
        return 0;
  }

//!!! ...unfinished... (2022/06/02)
  /** Determine this.apply data members according to whether .inputX and .outputX exist.
   *
   * @param {Base} this  The Base object to be determined and modified.
   *
   * @param {boolean} bKeepInputTensor0
   *   Whether the .input0's tensor should be destroyed by this operation. It is ignored if .input0 does not exist.
   *
   * @param {boolean} bKeepInputTensor1
   *   Whether the .input1's tensor should be destroyed by this operation. It is ignored if .input1 does not exist.
   */
  static setup_apply( bKeepInputTensor0, bKeepInputTensor1 ) {

    if ( this.input0 ) {
      if ( this.input1 ) {

        if ( this.output0 ) {
          if ( this.output1 ) { //  1. ( .input0, .input1 ) => ( .output0, .output1 )

            if ( bKeepInputTensor0 ) {
              if ( bKeepInputTensor1 )
                this.apply = () => { this.output0.realTensor = this.input0.realTensor.clone(); this.output1.realTensor = this.input1.realTensor.clone(); }
                //Base.input0_to_output0_keep0__input1_to_output1_keep1;
              else
                this.apply = () => { this.output0.realTensor = this.input0.realTensor.clone(); this.output1.realTensor = this.input1.realTensor; }
                //Base.input0_to_output0_keep0__input1_to_output1_destroy1;
            } else {
              if ( bKeepInputTensor1 )
                this.apply = () => { this.output0.realTensor = this.input0.realTensor; this.output1.realTensor = this.input1.realTensor.clone(); }
                //Base.input0_to_output0_destroy0__input1_to_output1_keep1;
              else
                this.apply = () => { this.output0.realTensor = this.input0.realTensor; this.output1.realTensor = this.input1.realTensor; }
                //Base.input0_to_output0_destroy0__input1_to_output1_destroy1;
            }

          } else {              //  2. ( .input0, .input1 ) => ( .output0 )

            if ( bKeepInputTensor0 ) {
              if ( bKeepInputTensor1 )
                this.apply = () => { this.output0.realTensor = this.input0.realTensor.clone(); }
                //Base.input0_to_output0_keep0__input1_keep1;
              else
                this.apply = () => { this.output0.realTensor = this.input0.realTensor.clone(); this.input1.realTensor.dispose(); }
                //Base.input0_to_output0_keep0__input1_destroy1;
            } else {
              if ( bKeepInputTensor1 )
                this.apply = () => { this.output0.realTensor = this.input0.realTensor; }
                //Base.input0_to_output0_destroy0__input1_keep1;
              else
                this.apply = () => { this.output0.realTensor = this.input0.realTensor; this.input1.realTensor.dispose(); }
                //Base.input0_to_output0_destroy0__input1_destroy1;
            }

          }
        } else {
          if ( this.output1 ) { //  3. ( .input0, .input1 ) => ( , .output1 )

            if ( bKeepInputTensor0 ) {
              if ( bKeepInputTensor1 )
                this.apply = Base.input0_keep0__input1_to_output1_keep1;
              else
                this.apply = Base.input0_keep0__input1_to_output1_destroy1;
            } else {
              if ( bKeepInputTensor1 )
                this.apply = Base.input0_destroy0__input1_to_output1_keep1;
              else
                this.apply = Base.input0_destroy0__input1_to_output1_destroy1;
            }

          } else {              //  4. ( .input0, .input1 ) => (  )

            if ( bKeepInputTensor0 ) {
              if ( bKeepInputTensor1 )
                this.apply = Base.input0_keep0__input1__keep1;
              else
                this.apply = Base.input0_keep0__input1_destroy1;
            } else {
              if ( bKeepInputTensor1 )
                this.apply = Base.input0_destroy0__input1_keep1;
              else
                this.apply = Base.input0_destroy0__input1_destroy1;
            }

          }
        }

      } else {

        if ( this.output0 ) {
          if ( this.output1 ) { //  5. ( .input0 ) => ( .output0, .output1 )

            if ( bKeepInputTensor0 ) {
              this.apply = Base.input0_to_output0_keep0__input0_to_output1_keep0;
            } else {
              this.apply = Base.input0_to_output0_keep0__input0_to_output1_destroy0;
            }

          } else {              //  6. ( .input0 ) => ( .output0 )
          }
        } else {
          if ( this.output1 ) { //  7. ( .input0 ) => ( , .output1 )
          } else {              //  8. ( .input0 ) => (  )
          }
        }

      }

    } else {

      if ( this.input1 ) {

        if ( this.output0 ) {
          if ( this.output1 ) { //  9. ( , .input1 ) => ( .output0, .output1 )
          } else {              // 10. ( , .input1 ) => ( .output0 )
          }
        } else {
          if ( this.output1 ) { // 11. ( , .input1 ) => ( , .output1 )
          } else {              // 12. ( , .input1 ) => (  )
          }
        }

      } else { // 13. no input0, no input1. Not supported.

        tf.util.assert( ( this.input0 != this.input1 ),
          `Operation.Base.setup_apply(): `
            + `input0 ( ${this.input0} ) and input1 ( ${this.input1} ) should at least one is non-null.`
        );

      }

//!!! ...unfinished... (2022/06/02)
    }
  }

  /**
   * Pass the input0 as output0 directly. Used for ( bKeepInputTensor == false ).
   *
   * @param {Base} this
   *   The this.input0.realTensor should be viewed as already disposed by this method. However, in fact, it is returned as
   * this.output0.realTensor directly.
   */
  static output0_return_input0_directly() {
    this.output0.realTensor = this.input0.realTensor;
  }

  /**
   * Pass the cloned input0 as output0. Used for ( bKeepInputTensor == true ).
   *
   * @param {Base} this
   *   The this.input0.realTensor should be viewed as kept (i.e. not disposed) by this method. However, in fact, it is cloned
   * and returned as this.output0.realTensor.
   */
  static output0_return_input0_cloned() {
    this.output0.realTensor = this.input0.realTensor.clone();
  }

//!!! ...unfinished... (2022/06/02)

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

import * as TensorPlaceholder from "../TensorPlaceholder.js";

//!!! ...unfinished... (2022/05/30) Used by Operation.apply()
// class OperationApplyArgs {
//
// //   constructor() {
// //     this.blockInput0 =
// //     this.blockInput1 =
// //
// //     this.operationInput0 =
// //     this.operationInput1 =
// //     this.operationOutput0 =
// //     this.operationOutput1 =;
//   }
//
// }
//

/**
 * An object operates several TensorPlaceholder.Base.
 *
 *
 * @member {TensorPlaceholder.Base} input0
 *   The TensorPlaceholder object which represents this operation's first input. It (from constructor) will be kept (not cloned)
 * directly. So caller should not modify them.
 *
 * @member {TensorPlaceholder.Base} input1
 *   The TensorPlaceholder object which represents this operation's second input. It could be null which means this operation
 * does not have second input tensor. It (from constructor) will be kept (not cloned) directly. So caller should not modify them.
 *
 * @member {TensorPlaceholder.Base} output0
 *   The TensorPlaceholder object which represents this operation's first output. It will be created by constructor if
 * outputTensorCount (of constructor) is >= 1.
 *
 * @member {TensorPlaceholder.Base} output1
 *   The TensorOpCounter object which represents this operation's second output. It is only created by constructor if
 * outputTensorCount (of constructor) is >= 2.
 *

//!!! ...unfinished... (2022/06/02)

 * @member {function} apply
 *   This is a method. It processes this.input0.realTensor and this.input1.realTensor as inputTensors. It puts to this.output0.realTensor
 * as outputTensor. Both inputTensors are tf.tensor3d and represents an images ( height x width x channel ) which will be added. They
 * should have the same channel count. Their ( height x width ) should be either the same or one is ( 1 x 1 ). The outputTensor
 * (tf.tensor3d) represents the result of adding the two inputs. The inputTensors may or may not be disposed. In fact, this method
 * calls one of Add_and_keep0_keep1(), Add_and_keep0_destroy1(), Add_and_destroy0_keep1(), Add_and_destroy0_destroy1() according to
 * the parameters.
 */
let Base = ( ParentClass = Object ) => class extends ParentClass {

  /**
   * This constructor will register this operation as the input TensorHolder's last operation. So the construction order is important
   * because the last constructed Operation object will become the real last operation of the inputs.
   *
   *
   * @param {number} outputTensorCount
   *   If 0, no this.outputX will be created. If 1, only the this.output0 will be created. If 2, both the this.output0 and this.output1
   * will be created.
   */
  constructor( input0, input1, outputTensorCount ) {
    this.input0 = input0;
    this.input1 = input1;

    // Register as the input TensorPlaceholder's final user.
    {
      if ( this.input0 )
        this.input0.lastOperation = this;

      if ( this.input1 )
        this.input1.lastOperation = this;
    }

    // Create output TensorPlaceholder.
    {
      if ( outputTensorCount >= 1 ) {
        this.output0 = new TensorPlaceholder.Base();

        if ( outputTensorCount >= 2 ) {
          this.output1 = new TensorPlaceholder.Base();
        }
      }
    }
  }

  /**
   * Sub-class should override this method.
   *
   * Release all tensors.
   */
  disposeTensors() {
  }

  /**
   * Sub-class should override this method.
   *
   * The this.setKeepInputTensor_IfNotLastOperation_Or_In() will call this method. This method should adjust
   * this.apply so that this.apply() will or will not dispose its inputTensors.
   *
   * @param {boolean} bKeepInputTensor0
   *   Whether the .input0's tensor should be destroyed by this operation. It is ignored if .input0 does not exist.
   *
   * @param {boolean} bKeepInputTensor1
   *   Whether the .input1's tensor should be destroyed by this operation. It is ignored if .input1 does not exist.
   */
  setKeepInputTensor( bKeepInputTensor0, bKeepInputTensor1 ) {
    Base.setup_apply.call( this, bKeepInputTensor0, bKeepInputTensor1 );
  }

  /**
   * This method will call this.setKeepInputTensor() according to：
   *   - whether the operation is the last operation of the this.input0 / this.input1.
   *   - whether the this.input0 / this.input1 is in alwaysKeepSet.
   *
   * @param {Set<TensorPlaceholder.Base>} alwaysKeepSet
   *   A set object. Its every element is TensorPlaceholder.Base object. They represent tensors never be disposed. The this.input0
   * and this.input1 will be compared with them.
   */
  setKeepInputTensor_IfNotLastOperation_Or_In( alwaysKeepSet ) {

    // Note: If an input appears multiple times (i.e. ( this.input0 == this.input1 ); multiple inputs of this operation are the same),
    //       the input will be disposed multiple times.
    //
    tf.util.assert( ( this.input0 != this.input1 ),
      `Operation.Base.setKeepInputTensor_IfNotLastOperation_Or_In(): `
        + `input0 ( ${this.input0} ) and input1 ( ${this.input1} ) should be different objects.`
    );

    // If this operation is the last operation of the input tensor, this operation is responsible for disposing it.

    let input0_bNeedDispose;
    if (   ( this.input0 )
        && ( !alwaysKeepSet?.has( this.input0 ) ) // input in alwaysKeepSet should always be kept (always not to be disposed).
        && ( this.input0.lastOperation == this )
       ) {
      input0_bNeedDispose = true;

    } else {
      input0_bNeedDispose = false;
    }

    let input1_bNeedDispose;
    if (   ( this.input1 )
        && ( !alwaysKeepSet?.has( this.input1 ) ) // input in alwaysKeepSet should always be kept (always not to be disposed).
        && ( this.input1.lastOperation == this )
       ) {
      input1_bNeedDispose = true;

    } else {
      input1_bNeedDispose = false;
    }

    // Configure the operation to keep or dispose its inputs.
    this.setKeepInputTensor( input0_bNeedDispose, input1_bNeedDispose );
  }

  /**
   * Sub-class should override this method.
   *
   * This method should:
   *   - Use this.input0.realTensor (and this.input1.realTensor) to compute.
   *   - Place the result in this.output0.realTensor (and this.output1.realTensor)
   *
   */
  apply() {
  }

  /**
   * Sub-class should override this property.
   *
   * @return {number}
   *   The wieght count extracted from inputFloat32Array and used in tensors. Not including Params, because they are not used in
   * tensors. Not including inferenced weights (even if they are used in tensors), because they are not extracted from inputFloat32Array.
   */
  get tensorWeightCountExtracted() {
    return 0;
  }

  /**
   * Sub-class should override this property.
   *
   * @return {number}
   *   The total wieght count used in tensors. Not including Params, because they are not used in tensors. Including inferenced
   * weights, if they are used in tensors.
   */
  get tensorWeightCountTotal() {
    return 0;
  }

  /**
   * @return {number} inputTensorCount
   *   How many input tensor placeholders.
   */
  get inputTensorCount() {
    if ( this.input0 )
      if ( this.input1 )
        return 2;
      else
        return 1;
    else
      if ( this.input1 )
        return 1;
      else
        return 0; // (should not happen)
  }

  /**
   * @return {number}
   *   How many output tensor placeholders. It's value is between [ 0, 2 ].
   */
  get outputTensorCount() {
    if ( this.output0 )
      if ( this.output1 )
        return 2;
      else
        return 1;
    else
      if ( this.output1 )
        return 1;
      else
        return 0;
  }

//!!! ...unfinished... (2022/06/02)
  /** Determine this.apply data members according to whether .inputX and .outputX exist.
   *
   * @param {Base} this  The Base object to be determined and modified.
   *
   * @param {boolean} bKeepInputTensor0
   *   Whether the .input0's tensor should be destroyed by this operation. It is ignored if .input0 does not exist.
   *
   * @param {boolean} bKeepInputTensor1
   *   Whether the .input1's tensor should be destroyed by this operation. It is ignored if .input1 does not exist.
   */
  static setup_apply( bKeepInputTensor0, bKeepInputTensor1 ) {

    if ( this.input0 ) {
      if ( this.input1 ) {

        if ( this.output0 ) {
          if ( this.output1 ) { //  1. ( .input0, .input1 ) => ( .output0, .output1 )

            if ( bKeepInputTensor0 ) {
              if ( bKeepInputTensor1 )
                this.apply = () => { this.output0.realTensor = this.input0.realTensor.clone(); this.output1.realTensor = this.input1.realTensor.clone(); }
                //Base.input0_to_output0_keep0__input1_to_output1_keep1;
              else
                this.apply = () => { this.output0.realTensor = this.input0.realTensor.clone(); this.output1.realTensor = this.input1.realTensor; }
                //Base.input0_to_output0_keep0__input1_to_output1_destroy1;
            } else {
              if ( bKeepInputTensor1 )
                this.apply = () => { this.output0.realTensor = this.input0.realTensor; this.output1.realTensor = this.input1.realTensor.clone(); }
                //Base.input0_to_output0_destroy0__input1_to_output1_keep1;
              else
                this.apply = () => { this.output0.realTensor = this.input0.realTensor; this.output1.realTensor = this.input1.realTensor; }
                //Base.input0_to_output0_destroy0__input1_to_output1_destroy1;
            }

          } else {              //  2. ( .input0, .input1 ) => ( .output0 )

            if ( bKeepInputTensor0 ) {
              if ( bKeepInputTensor1 )
                this.apply = () => { this.output0.realTensor = this.input0.realTensor.clone(); }
              else
                this.apply = () => { this.output0.realTensor = this.input0.realTensor.clone(); this.input1.realTensor.dispose(); }
            } else {
              if ( bKeepInputTensor1 )
                this.apply = () => { this.output0.realTensor = this.input0.realTensor; }
              else
                this.apply = () => { this.output0.realTensor = this.input0.realTensor; this.input1.realTensor.dispose(); }
            }

          }
        } else {
          if ( this.output1 ) { //  3. ( .input0, .input1 ) => ( , .output1 )

            if ( bKeepInputTensor0 ) {
              if ( bKeepInputTensor1 )
                this.apply = () => { this.output1.realTensor = this.input1.realTensor.clone(); }
              else
                this.apply = () => { this.output1.realTensor = this.input1.realTensor; }
            } else {
              if ( bKeepInputTensor1 )
                this.apply = () => { this.input0.realTensor.dispose(); this.output1.realTensor = this.input1.realTensor.clone(); }
              else
                this.apply = () => { this.input0.realTensor.dispose(); this.output1.realTensor = this.input1.realTensor; }
            }

          } else {              //  4. ( .input0, .input1 ) => (  )

//!!! ...unfinished... (2022/06/02)

            if ( bKeepInputTensor0 ) {
              if ( bKeepInputTensor1 )
                this.apply = Base.input0_keep0__input1__keep1;
              else
                this.apply = Base.input0_keep0__input1_destroy1;
            } else {
              if ( bKeepInputTensor1 )
                this.apply = Base.input0_destroy0__input1_keep1;
              else
                this.apply = Base.input0_destroy0__input1_destroy1;
            }

          }
        }

      } else {

        if ( this.output0 ) {
          if ( this.output1 ) { //  5. ( .input0 ) => ( .output0, .output1 )

            if ( bKeepInputTensor0 ) {
              this.apply = Base.input0_to_output0_keep0__input0_to_output1_keep0;
            } else {
              this.apply = Base.input0_to_output0_keep0__input0_to_output1_destroy0;
            }

          } else {              //  6. ( .input0 ) => ( .output0 )
          }
        } else {
          if ( this.output1 ) { //  7. ( .input0 ) => ( , .output1 )
          } else {              //  8. ( .input0 ) => (  )
          }
        }

      }

    } else {

      if ( this.input1 ) {

        if ( this.output0 ) {
          if ( this.output1 ) { //  9. ( , .input1 ) => ( .output0, .output1 )
          } else {              // 10. ( , .input1 ) => ( .output0 )
          }
        } else {
          if ( this.output1 ) { // 11. ( , .input1 ) => ( , .output1 )
          } else {              // 12. ( , .input1 ) => (  )
          }
        }

      } else { // 13. no input0, no input1. Not supported.

        tf.util.assert( ( this.input0 != this.input1 ),
          `Operation.Base.setup_apply(): `
            + `input0 ( ${this.input0} ) and input1 ( ${this.input1} ) should at least one is non-null.`
        );

      }

//!!! ...unfinished... (2022/06/02)
    }
  }

  /**
   * Pass the input0 as output0 directly. Used for ( bKeepInputTensor == false ).
   *
   * @param {Base} this
   *   The this.input0.realTensor should be viewed as already disposed by this method. However, in fact, it is returned as
   * this.output0.realTensor directly.
   */
  static output0_return_input0_directly() {
    this.output0.realTensor = this.input0.realTensor;
  }

  /**
   * Pass the cloned input0 as output0. Used for ( bKeepInputTensor == true ).
   *
   * @param {Base} this
   *   The this.input0.realTensor should be viewed as kept (i.e. not disposed) by this method. However, in fact, it is cloned
   * and returned as this.output0.realTensor.
   */
  static output0_return_input0_cloned() {
    this.output0.realTensor = this.input0.realTensor.clone();
  }

//!!! ...unfinished... (2022/06/02)

}
<|endoftext|>


<commit_before>// These exist in shared because the Bluetooth service needs access to them
// Services do not have "Actions" they're directly associated with, so they use the shared creator

import { NativeModules } from 'react-native';
import BackgroundTimer from 'react-native-background-timer';
import DeviceInfo from 'react-native-device-info';

import {
    START_DEVICE_SCAN,
    STOP_DEVICE_SCAN,
    FOUND_DEVICE,
    BLUETOOTH_OFF,
    DISCONNECTED_FROM_DEVICE,
    CONNECTING_TO_DEVICE,
    CONNECTED_TO_DEVICE,
    ADD_REP_DATA,
    RECONNECTING_TO_DEVICE,
    DISCONNECT_DEVICE,
    CONNECT_DEVICE,
    RECONNECT_DEVICE
} from 'app/ActionTypes';
import * as TimerActionCreators from './TimerActionCreators';
import * as SetsActionCreators from './SetsActionCreators';
import * as ConnectedDeviceStatusSelectors from 'app/redux/selectors/ConnectedDeviceStatusSelectors';
import * as SettingsSelectors from 'app/redux/selectors/SettingsSelectors';
import * as Analytics from 'app/utility/Analytics';

const RFDuinoLib = NativeModules.RFDuinoLib;

var connectTimeoutTimer = null;
var reconnectTimeoutTimer = null;
var reconnectTimer = null;
const clearTimers = () => {
    BackgroundTimer.clearTimeout(connectTimeoutTimer);
    BackgroundTimer.clearTimeout(reconnectTimeoutTimer);
    BackgroundTimer.clearTimeout(reconnectTimer);
    connectTimeoutTimer = null;
    reconnectTimeoutTimer = null;
    reconnectTimer = null;
};

// SCANNING

export const startDeviceScan = () => {
    RFDuinoLib.startScan();

    // analytics
    const uniqueID = DeviceInfo.getUniqueId();
    Analytics.setUserProp('mobile_identifier', uniqueID);

    return {
        type: START_DEVICE_SCAN
    };
};

export const stopDeviceScan = () => (dispatch, getState) => {
    RFDuinoLib.stopScan();

    // analytics
    const state = getState();
    const scanned_devices = state.scannedDevices.devices.join();
    const num_scanned_devices = state.scannedDevices.devices.length;
    Analytics.setUserProp('scanned_devices', scanned_devices);
    Analytics.setUserProps('num_scanned_devices', num_scanned_devices);

    dispatch({
        type: STOP_DEVICE_SCAN
    });
};

export const foundDevice = (name, identifier) => {
    Analytics.setUserProp('is_bluetooth_on', true);

    return {
        type: FOUND_DEVICE,
        device: name,
        deviceIdentifier: identifier
    }
};

// DEVICE

export const connectDevice = (device) => (dispatch, getState) => {
    Analytics.setUserProp('is_bluetooth_on', true);
    RFDuinoLib.connectDevice(device);

    // HACK: ideally this is a connect timeout saga
    // but it requires both background timer and access to actions
    // therefore putting it here
    connectTimeoutTimer = BackgroundTimer.setTimeout(() => {
        // check to see if stuck connecting
        const state = getState();
        const status = ConnectedDeviceStatusSelectors.getConnectedDeviceStatus(state);
        if (status === 'CONNECTING') {
            // disconnect
            dispatch(disconnectDevice()); // in case it's trying to connect, ensure it's actually disconnecting
            dispatch(disconnectedFromDevice()); // in case it can never find it, visually update
        }
    }, 5000);

    dispatch({
        type: CONNECT_DEVICE,
        device: device
    });
};

export const reconnectDevice = (device, identifier) => (dispatch, getState) => {
    // reconnect after a second as V2s have issues
    reconnectTimer = BackgroundTimer.setTimeout(() => {
        RFDuinoLib.connectDevice(device);
    }, 2000);

    // HACK: ideally this is a connect timeout saga
    // but it requires both background timer and access to actions
    // therefore putting it here
    reconnectTimeoutTimer = BackgroundTimer.setTimeout(() => {
        // check to see if stuck connecting
        const state = getState();
        const status = ConnectedDeviceStatusSelectors.getConnectedDeviceStatus(state);
        if (status !== 'CONNECTED') {
            // disconnect
            dispatch(disconnectDevice()); // in case it's trying to connect, ensure it's actually disconnecting
            dispatch(disconnectedFromDevice(device, identifier)); // in case it can never find it, visually update and trigger another reconnect
        }
    }, 7000);

    dispatch({
        type: RECONNECT_DEVICE,
        device: device
    });
};

export const disconnectDevice = () => {
    RFDuinoLib.disconnectDevice();

    return {
        type: DISCONNECT_DEVICE
    };
};

// DEVICE STATUS

export const bluetoothIsOff = () => {
    Analytics.setUserProp('is_bluetooth_on', false);

    return {
        type: BLUETOOTH_OFF
    };
};

export const disconnectedFromDevice = (name=null, identifier=null) => {
<<<<<<< HEAD
    Analytics.setUserProp('connected_device_id', null);
    clearTimers();
=======
    Analytics.setUserProp('connected_device_id', null);
    Analytics.setUserProp('is_v2', false);
    Analytics.setUserProps('is_v3', false);
>>>>>>> added start new set events

    return {
        type: DISCONNECTED_FROM_DEVICE,
        device: name,
        deviceIdentifier: identifier
    };
};

export const connectingToDevice = (name, identifier) => {
    Analytics.setUserProp('is_bluetooth_on', true);

    return {
        type: CONNECTING_TO_DEVICE,
        device: name,
        deviceIdentifier: identifier
    };
};

export const connectedToDevice = (name, identifier) => {
    Analytics.setUserProp('connected_device_id', identifier);
<<<<<<< HEAD
    clearTimers();

=======
    Analytics.setUserProp('is_bluetooth_on', true);

    if (name.charAt(3) === '2') {
        Analytics.setUserProp('is_v2', true);
        Analytics.setUserProps('is_v3', false);
    } else if (name.charAt(3) === '3') {
        Analytics.setUserProp('is_v3', true);
        Analytics.setUserProp('is_v2', false);
    }

>>>>>>> added start new set events
    return {
        type: CONNECTED_TO_DEVICE,
        deviceName: name,
        deviceIdentifier: identifier
    };
};

export const reconnectingToDevice = (name, identifier) => {
    Analytics.setUserProp('connected_device_id', identifier);
    Analytics.setUserProp('is_bluetooth_on', true);

    if (name.charAt(3) === '2') {
        Analytics.setUserProp('is_v2', true);
        Analytics.setUserProps('is_v3', false);
    } else if (name.charAt(3) === '3') {
        Analytics.setUserProp('is_v3', true);
        Analytics.setUserProp('is_v2', false);
    }
    return {
        type: RECONNECTING_TO_DEVICE,
        deviceName: name,
        deviceIdentifier: identifier
    };
};

// DATA

export const receivedLiftData = (isValid, data, time=new Date()) => (dispatch, getState) => {
    var state = getState();
    Analytics.setUserProp('is_bluetooth_on', true);

    dispatch(TimerActionCreators.sanityCheckTimer());

    dispatch({
        type: ADD_REP_DATA,
        isValid: isValid,
        data: data,
        deviceName: state.connectedDevice.deviceName,
        deviceIdentifier: state.connectedDevice.deviceIdentifier,
        time: time
    });

    dispatch(TimerActionCreators.startEndSetTimer());
};
<commit_msg>fixed merge conflicts<commit_after>// These exist in shared because the Bluetooth service needs access to them
// Services do not have "Actions" they're directly associated with, so they use the shared creator

import { NativeModules } from 'react-native';
import BackgroundTimer from 'react-native-background-timer';
import DeviceInfo from 'react-native-device-info';

import {
    START_DEVICE_SCAN,
    STOP_DEVICE_SCAN,
    FOUND_DEVICE,
    BLUETOOTH_OFF,
    DISCONNECTED_FROM_DEVICE,
    CONNECTING_TO_DEVICE,
    CONNECTED_TO_DEVICE,
    ADD_REP_DATA,
    RECONNECTING_TO_DEVICE,
    DISCONNECT_DEVICE,
    CONNECT_DEVICE,
    RECONNECT_DEVICE
} from 'app/ActionTypes';
import * as TimerActionCreators from './TimerActionCreators';
import * as SetsActionCreators from './SetsActionCreators';
import * as ConnectedDeviceStatusSelectors from 'app/redux/selectors/ConnectedDeviceStatusSelectors';
import * as SettingsSelectors from 'app/redux/selectors/SettingsSelectors';
import * as Analytics from 'app/utility/Analytics';

const RFDuinoLib = NativeModules.RFDuinoLib;

var connectTimeoutTimer = null;
var reconnectTimeoutTimer = null;
var reconnectTimer = null;
const clearTimers = () => {
    BackgroundTimer.clearTimeout(connectTimeoutTimer);
    BackgroundTimer.clearTimeout(reconnectTimeoutTimer);
    BackgroundTimer.clearTimeout(reconnectTimer);
    connectTimeoutTimer = null;
    reconnectTimeoutTimer = null;
    reconnectTimer = null;
};

// SCANNING

export const startDeviceScan = () => {
    RFDuinoLib.startScan();

    // analytics
    const uniqueID = DeviceInfo.getUniqueId();
    Analytics.setUserProp('mobile_identifier', uniqueID);

    return {
        type: START_DEVICE_SCAN
    };
};

export const stopDeviceScan = () => (dispatch, getState) => {
    RFDuinoLib.stopScan();

    // analytics
    const state = getState();
    const scanned_devices = state.scannedDevices.devices.join();
    const num_scanned_devices = state.scannedDevices.devices.length;
    Analytics.setUserProp('scanned_devices', scanned_devices);
    Analytics.setUserProps('num_scanned_devices', num_scanned_devices);

    dispatch({
        type: STOP_DEVICE_SCAN
    });
};

export const foundDevice = (name, identifier) => {
    Analytics.setUserProp('is_bluetooth_on', true);

    return {
        type: FOUND_DEVICE,
        device: name,
        deviceIdentifier: identifier
    }
};

// DEVICE

export const connectDevice = (device) => (dispatch, getState) => {
    Analytics.setUserProp('is_bluetooth_on', true);
    RFDuinoLib.connectDevice(device);

    // HACK: ideally this is a connect timeout saga
    // but it requires both background timer and access to actions
    // therefore putting it here
    connectTimeoutTimer = BackgroundTimer.setTimeout(() => {
        // check to see if stuck connecting
        const state = getState();
        const status = ConnectedDeviceStatusSelectors.getConnectedDeviceStatus(state);
        if (status === 'CONNECTING') {
            // disconnect
            dispatch(disconnectDevice()); // in case it's trying to connect, ensure it's actually disconnecting
            dispatch(disconnectedFromDevice()); // in case it can never find it, visually update
        }
    }, 5000);

    dispatch({
        type: CONNECT_DEVICE,
        device: device
    });
};

export const reconnectDevice = (device, identifier) => (dispatch, getState) => {
    // reconnect after a second as V2s have issues
    reconnectTimer = BackgroundTimer.setTimeout(() => {
        RFDuinoLib.connectDevice(device);
    }, 2000);

    // HACK: ideally this is a connect timeout saga
    // but it requires both background timer and access to actions
    // therefore putting it here
    reconnectTimeoutTimer = BackgroundTimer.setTimeout(() => {
        // check to see if stuck connecting
        const state = getState();
        const status = ConnectedDeviceStatusSelectors.getConnectedDeviceStatus(state);
        if (status !== 'CONNECTED') {
            // disconnect
            dispatch(disconnectDevice()); // in case it's trying to connect, ensure it's actually disconnecting
            dispatch(disconnectedFromDevice(device, identifier)); // in case it can never find it, visually update and trigger another reconnect
        }
    }, 7000);

    dispatch({
        type: RECONNECT_DEVICE,
        device: device
    });
};

export const disconnectDevice = () => {
    RFDuinoLib.disconnectDevice();

    return {
        type: DISCONNECT_DEVICE
    };
};

// DEVICE STATUS

export const bluetoothIsOff = () => {
    Analytics.setUserProp('is_bluetooth_on', false);

    return {
        type: BLUETOOTH_OFF
    };
};

export const disconnectedFromDevice = (name=null, identifier=null) => {
    clearTimers();

    Analytics.setUserProp('connected_device_id', null);
    Analytics.setUserProp('is_v2', false);
    Analytics.setUserProps('is_v3', false);

    return {
        type: DISCONNECTED_FROM_DEVICE,
        device: name,
        deviceIdentifier: identifier
    };
};

export const connectingToDevice = (name, identifier) => {
    Analytics.setUserProp('is_bluetooth_on', true);

    return {
        type: CONNECTING_TO_DEVICE,
        device: name,
        deviceIdentifier: identifier
    };
};

export const connectedToDevice = (name, identifier) => {
    clearTimers();

    Analytics.setUserProp('connected_device_id', identifier);
    Analytics.setUserProp('is_bluetooth_on', true);

    if (name.charAt(3) === '2') {
        Analytics.setUserProp('is_v2', true);
        Analytics.setUserProps('is_v3', false);
    } else if (name.charAt(3) === '3') {
        Analytics.setUserProp('is_v3', true);
        Analytics.setUserProp('is_v2', false);
    }

    return {
        type: CONNECTED_TO_DEVICE,
        deviceName: name,
        deviceIdentifier: identifier
    };
};

export const reconnectingToDevice = (name, identifier) => {
    Analytics.setUserProp('connected_device_id', identifier);
    Analytics.setUserProp('is_bluetooth_on', true);

    if (name.charAt(3) === '2') {
        Analytics.setUserProp('is_v2', true);
        Analytics.setUserProps('is_v3', false);
    } else if (name.charAt(3) === '3') {
        Analytics.setUserProp('is_v3', true);
        Analytics.setUserProp('is_v2', false);
    }
    return {
        type: RECONNECTING_TO_DEVICE,
        deviceName: name,
        deviceIdentifier: identifier
    };
};

// DATA

export const receivedLiftData = (isValid, data, time=new Date()) => (dispatch, getState) => {
    var state = getState();
    Analytics.setUserProp('is_bluetooth_on', true);

    dispatch(TimerActionCreators.sanityCheckTimer());

    dispatch({
        type: ADD_REP_DATA,
        isValid: isValid,
        data: data,
        deviceName: state.connectedDevice.deviceName,
        deviceIdentifier: state.connectedDevice.deviceIdentifier,
        time: time
    });

    dispatch(TimerActionCreators.startEndSetTimer());
};
<|endoftext|>


<commit_before>export { Base };

import * as ReturnOrClone from "./ReturnOrClone.js";
import * as ValueDesc from "../Unpacker/ValueDesc.js";

/**
 * Shared common base class for Pointwise and Depthwise.
 *
 */
class Base extends ReturnOrClone.Base {

  /**
   * Convert activation function id to function object.
   *
   * @param {number} nActivationId
   *   It should be one of ValueDesc.ActivationFunction.Singleton.Ids.Xxx. (e.g. ValueDesc.ActivationFunction.Singleton.Ids.NONE,
   * ValueDesc.ActivationFunction.Singleton.Ids.RELU6, ValueDesc.ActivationFunction.Singleton.Ids.COS, ...)
   *
   * @return {function}
   *   It should be one of ValueDesc.ActivationFunction.Singleton.integerToObjectMap according to the nActivationId. (e.g. null,
   * tf.relu6, tf.cos, ...)
   */
  static getActivationFunctionById( nActivationId ) {
    let pfn = ValueDesc.ActivationFunction.Singleton.integerToObjectMap.get( nActivationId );
    return pfn;
  }

  /**
   * Extract tf.tensor from inputFloat32Array (at this.byteOffsetEnd). The following data members will be modified:
   *   - this.byteOffsetEnd
   *   - this.tensorWeightCountExtracted
   *   - this.tensorWeightCountTotal
   *
   * @param {Base} this                       The Base object to be modified.
   * @param {Float32Array} inputFloat32Array  A Float32Array whose values will be interpreted as weights.
   * @param {number[]} tensorShape            The shape of the tensor to be extracted.
   *
   * @return {tf.tensor}                      The extracted tensor. Return null, if failed.
   */
  static extractTensor( inputFloat32Array, tensorShape ) {
    let tensorWeights = new Weights.Base( inputFloat32Array, this.byteOffsetEnd, tensorShape );
    if ( !tensorWeights.extract() )
      return null;  // e.g. input array does not have enough data.
    this.byteOffsetEnd = tensorWeights.defaultByteOffsetEnd;

    try {
      let t = tf.tensor( tensorWeights.weights, tensorShape );
      let elementCount = tf.util.sizeFromShape( t.shape );
      this.tensorWeightCountExtracted += elementCount;
      this.tensorWeightCountTotal += elementCount;
      return t;

    // If failed (e.g. memory not enough), return null.
    } catch ( e ) {
      return null;
    }
  }

  /**
   * Extract biases from inputFloat32Array (at this.byteOffsetEnd). The following data members will be modified:
   *   - this.byteOffsetEnd
   *   - this.tensorWeightCountExtracted
   *   - this.tensorWeightCountTotal
   *
   * @param {Base} this                       The Base object to be modified.
   * @param {Float32Array} inputFloat32Array  A Float32Array whose values will be interpreted as weights.
   * @param {number} channelCount             The (input and output) channel count of the biases.
   *
   * @return {tf.tensor3d}                    The extracted biases. Return null, if failed.
   */
  static extractBiases( inputFloat32Array, channelCount ) {
    let biasesShape = [ 1, 1, channelCount ];
    return Base.extractTensor.call( inputFloat32Array, biasesShape );
  }

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

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

/**
 * Shared common base class for Pointwise and Depthwise.
 *
 */
class Base extends ReturnOrClone.Base {

  /**
   * Convert activation function id to function object.
   *
   * @param {number} nActivationId
   *   It should be one of ValueDesc.ActivationFunction.Singleton.Ids.Xxx. (e.g. ValueDesc.ActivationFunction.Singleton.Ids.NONE,
   * ValueDesc.ActivationFunction.Singleton.Ids.RELU6, ValueDesc.ActivationFunction.Singleton.Ids.COS, ...)
   *
   * @return {function}
   *   It should be one of ValueDesc.ActivationFunction.Singleton.integerToObjectMap according to the nActivationId. (e.g. null,
   * tf.relu6, tf.cos, ...)
   */
  static getActivationFunctionById( nActivationId ) {
    let pfn = ValueDesc.ActivationFunction.Singleton.integerToObjectMap.get( nActivationId );
    return pfn;
  }

  /**
   * Extract tf.tensor from inputFloat32Array (at this.byteOffsetEnd). The following data members will be modified:
   *   - this.byteOffsetEnd
   *   - this.tensorWeightCountExtracted
   *   - this.tensorWeightCountTotal
   *
   * @param {Base} this                       The Base object to be modified.
   * @param {Float32Array} inputFloat32Array  A Float32Array whose values will be interpreted as weights.
   * @param {number[]} tensorShape            The shape of the tensor to be extracted.
   *
   * @return {tf.tensor}                      The extracted tensor. Return null, if failed.
   */
  static extractTensor( inputFloat32Array, tensorShape ) {
    let tensorWeights = new Weights.Base( inputFloat32Array, this.byteOffsetEnd, tensorShape );
    if ( !tensorWeights.extract() )
      return null;  // e.g. input array does not have enough data.
    this.byteOffsetEnd = tensorWeights.defaultByteOffsetEnd;

    try {
      let t = tf.tensor( tensorWeights.weights, tensorShape );
      let elementCount = tf.util.sizeFromShape( t.shape );
      this.tensorWeightCountExtracted += elementCount;
      this.tensorWeightCountTotal += elementCount;
      return t;

    // If failed (e.g. memory not enough), return null.
    } catch ( e ) {
      return null;
    }
  }

  /**
   * Extract biases from inputFloat32Array (at this.byteOffsetEnd). The following data members will be modified:
   *   - this.byteOffsetEnd
   *   - this.tensorWeightCountExtracted
   *   - this.tensorWeightCountTotal
   *
   * @param {Base} this                       The Base object to be modified.
   * @param {Float32Array} inputFloat32Array  A Float32Array whose values will be interpreted as weights.
   * @param {number} channelCount             The (input and output) channel count of the biases.
   *
   * @return {tf.tensor3d}                    The extracted biases. Return null, if failed.
   */
  static extractBiases( inputFloat32Array, channelCount ) {
    let biasesShape = [ 1, 1, channelCount ];
    return Base.extractTensor.call( inputFloat32Array, biasesShape );
  }

}
<|endoftext|>


<commit_before>'use strict';

angular.module('openSenseMapApp')
.controller('InterpolationCtrl',
	["$scope", "$stateParams", "$http", "OpenSenseBoxAPI", "leafletData", "$timeout", "moment", function($scope, $stateParams, $http, OpenSenseBoxAPI, leafletData, $timeout, moment){

    $scope.calculating = false;
    $scope.interpolationPickerStart = {
      date: moment().toDate(),
      open: false,
      buttonBar: {
        show: false
      },
      timepickerOptions: {
        readonlyInput: false,
        showMeridian: false,
        max: moment().toDate()
      }
    };
    $scope.interpolationPickerEnd = {
      date: moment().toDate(),
      open: false,
      buttonBar: {
        show: false
      },
      timepickerOptions: {
        readonlyInput: false,
        showMeridian: false,
        max: moment().toDate()
      }
    };

    $scope.visible = false;
    var dates = [];
    $scope.selectedTimeStep = Date.now();
    $scope.slider = {
      options: {
        stepsArray: dates,
        showTicks: true,
        hidePointerLabels: true,
        hideLimitLabels: true,
        onChange: function(id, value) {
          $scope.selectedTimeStep = value;
          $scope.map.removeLayer($scope.layer);
          $scope.layer = idwLayers[value.toISOString()];
          $scope.map.addLayer($scope.layer);
        }
      }
    };
    $scope.refreshSlider = function () {
      $timeout(function () {
        $scope.$broadcast('rzSliderForceRender');
      });
    };

    $scope.$watch('interpolationPickerEnd.open', function(newValue) {
      console.log($scope.interpolationPickerEnd.open);
    });

    activate();

    function activate() {
      L.LayerGroup.include({
        customGetLayer: function (id) {
          for (var i in this._layers) {
            if (this._layers[i].id == id) {
              return this._layers[i];
            }
          }
        }
      });

      leafletData.getMap('map_main').then(function (map) {
        $scope.map = map;
        map.eachLayer(function(layer){
          if (layer instanceof L.LayerGroup) {
            var layerGroup = layer.customGetLayer('interpolation');
            if (!angular.isUndefined(layerGroup)) {
              $scope.layerGroup = layerGroup;
            }
          }
         });
      });
    };

    $scope.legendTitle = '';
    $scope.legendEntries = [];

    $scope.alerts = [];
    $scope.closeAlert = function(index) {
      $scope.alerts.splice(index, 1);
    };

    $scope.idwPower = 3;
    $scope.cellWidth = 1;
    $scope.numTimeSteps = 1;
    $scope.exposure = "outdoor";
    $scope.layerGroup;
    $scope.selectedPhenomenon = "";
    $scope.map;

    $scope.minIDWPower = 1;
    $scope.maxIDWPower = 9;

    $scope.changeIDWPower = function(number) {
      var newValue = $scope.idwPower + number;
      if (newValue >= $scope.minIDWPower && newValue <= $scope.maxIDWPower) {
        $scope.idwPower += number;
      }
    }

    $scope.minNumTimeSteps = 1;
    $scope.maxNumTimeSteps = 10;

    $scope.changeNumTimeSteps = function(number) {
      var newValue = $scope.numTimeSteps + number;
      if (newValue >= $scope.minNumTimeSteps && newValue <= $scope.maxNumTimeSteps) {
        $scope.numTimeSteps += number;
      }
    }

    $scope.layer;
    var idwLayers = [];
    var featureCollections = [];
    $scope.calculateInterpolation = function () {
      $scope.calculating = true;
      $scope.alerts.length = 0;
      $scope.legendEntries.length = 0;
      $scope.legendTitle = '';

      leafletData.getMap('map_main').then(function (map) {
        if (!angular.isUndefined($scope.layerGroup)) {
          $scope.layerGroup.clearLayers();
        }
        $scope.map = map;
        return map.getBounds().toBBoxString();
      }).then(function (bbox) {
        $http.get(OpenSenseBoxAPI.url+'/statistics/idw', {
          params: {
            'phenomenon': $scope.selectedPhenomenon,
            'from-date': moment($scope.interpolationPickerStart.date).toISOString(),
            'to-date': moment($scope.interpolationPickerEnd.date).toISOString(),
            'exposure': $scope.exposure,
            'cellWidth': $scope.cellWidth,
            'power': $scope.idwPower,
            'numClasses': 6,
            'bbox': bbox,
            'numTimeSteps': $scope.numTimeSteps
          }
        })
        .then(function (response) {
          var colors = "#A2F689,#B1E36F,#BCD05B,#C4BD4C,#C8AA44,#C99840".split(",");
          if (response.data.code === 'NotFoundError') {
            return response;
          }

          dates = [];
          response.data.data.timesteps.forEach(function (element, index) {
            dates.push(new Date(element));
            featureCollections.push({type: "FeatureCollection", features: []});
          });

          $scope.slider.value = dates[0];
          $scope.slider.options.stepsArray = dates;
          $scope.selectedTimeStep = dates[0];

          var breaks = response.data.data.breaks;

          // Set legend title
          switch ($scope.selectedPhenomenon) {
            case 'Temperatur':
              $scope.legendTitle = $scope.selectedPhenomenon + " in °C"
              break;
            case 'rel. Luftfeuchte':
              $scope.legendTitle = $scope.selectedPhenomenon + " in %"
              break;
            case 'Luftdruck':
              $scope.legendTitle = $scope.selectedPhenomenon + " in hPa"
              break;
            case 'Beleuchtungsstärke':
              $scope.legendTitle = $scope.selectedPhenomenon + " in lx"
              break;
            case 'UV-Intensität':
              $scope.legendTitle = $scope.selectedPhenomenon + " in μW/cm²"
              break;
          }

          // Generate legend
          for (var j = 0; j < breaks.length; j++) {
            var caption = "";
            if (j === breaks.length-1) {
              caption = "> " + breaks[j].toFixed(2);
            } else {
              caption = breaks[j].toFixed(2) +' - '+ breaks[j+1].toFixed(2);
            }
            $scope.legendEntries.push({
              caption: caption,
              style:{
                'background': colors[j]
              }
            });
          }

          response.data.data.featureCollection.features.map(function (feature) {
            var newFeature = {};
            angular.copy(feature, newFeature);
            for (var i = 0; i < feature.properties.idwValues.length; i++) {
              newFeature.properties.idwValues = [];
              var value = feature.properties.idwValues[i];
              newFeature.properties.idwValues.push(value);
              featureCollections[i].features.push(newFeature);
            }
          });

          for (var i = 0; i < featureCollections.length; i++) {
            var idwLayer = createGeoJsonLayer(featureCollections[i], breaks);
            idwLayers[response.data.data.timesteps[i]] = idwLayer;
          }

          // $scope.layerGroup = L.layerGroup([idwLayers[dates[0].toISOString()]]);
          // $scope.layerGroup.eachLayer(function (layer) {
          //   layer.id = 'interpolation';
          // });
          $scope.layer = idwLayers[dates[0].toISOString()];
          $scope.map.addLayer($scope.layer);

          $scope.visible = true;
          $scope.refreshSlider();

          return;
        }, function(error) {
          return error;
        })
        .then(function (error) {
          if (angular.isUndefined(error)) {
            return;
          }

          switch (error.data.message) {
            case "planned computation too expensive ((area in square kilometers / cellWidth) > 2500)":
              $scope.alerts.push({msg: 'Der gewählte Kartenausschnitt ist zu groß!'});
              break;
            case "Invalid time frame specified: to-date is in the future":
              $scope.alerts.push({msg: 'Das gewählte Datum darf nicht in der Zukunft liegen!'});
              break;
            case "no senseBoxes found":
              $scope.alerts.push({msg: 'Es konnten keine senseBoxen für die Filtereinstellungen gefunden werden!'});
              break;
            case "no measurements found":
              $scope.alerts.push({msg: 'Es wurden keine Messungen für den angegebenen Zeitpunkt gefunden!'});
              break;
            default:
              $scope.alerts.push({msg: 'Bei der Interpolation ist ein unbekannter Fehler aufgetreten! Bitte überprüfe die Filtereinstellungen.'});
              break;
          }
        })
        .finally(function () {
          $scope.calculating = false;
        });
      });
    };

    function createGeoJsonLayer (featureCollection, breaks) {
      var colors = "#A2F689,#B1E36F,#BCD05B,#C4BD4C,#C8AA44,#C99840".split(",");
      var idwLayer = L.geoJson(featureCollection, {
        style: function (feature) {
          var props = feature.properties;
          for (var key in props) {
            var z = props[key];
            if (!Number.isNaN(z)) {
              var fillColor = colors[0];
              for (var i = 0; i < breaks.length; i++) {
                if (z >= breaks[i]) {
                  fillColor = colors[i];
                } else {
                  break;
                }
              }
              return {
                weight: 0.1,
                fillOpacity: 0.6,
                fillColor: fillColor
              }
            }
            return {
              weight: 0,
              fillColor: "red",
              fillOpacity: 1
            };
          }
        }
      });
      return idwLayer;
    }

    $scope.$on("slideEnded", function() {
      console.log("slide endend");
    });

    $scope.removeInterpolation = function () {
      if (!angular.isUndefined($scope.map) && !angular.isUndefined($scope.layerGroup)) {
        $scope.layerGroup.clearLayers();
        $scope.visible = false;
      }
    }

    $scope.showRemoveInterpolation = function () {
      if (!angular.isUndefined($scope.map) && !angular.isUndefined($scope.layerGroup)) {
        if ($scope.layerGroup.toGeoJSON().features.length > 0) {
          return true;
        }
      }
      return false;
    }

    $scope.openCalendar = function(e, picker) {
      e.preventDefault();
      e.stopPropagation();
      switch(picker) {
        case 'interpolationPickerStart':
          $scope.interpolationPickerStart.open = true;
          $scope.interpolationPickerStart.timepickerOptions.max = moment($scope.interpolationPickerEnd.date).toISOString();
          break;
        case 'interpolationPickerEnd':
          $scope.interpolationPickerEnd.open = true;
          $scope.interpolationPickerEnd.timepickerOptions.max = moment().toDate();
          break;
      }
    };

    $scope.selectExposure = function (exposure) {
      $scope.exposure = exposure;
    };

    $scope.closeSidebar = function () {
      if (!angular.isUndefined($scope.map) && !angular.isUndefined($scope.layerGroup)) {
        $scope.layerGroup.clearLayers();
      }
    }
}]);
<commit_msg>working interpolation slider<commit_after>'use strict';

angular.module('openSenseMapApp')
.controller('InterpolationCtrl',
	["$scope", "$stateParams", "$http", "OpenSenseBoxAPI", "leafletData", "$timeout", "moment", function($scope, $stateParams, $http, OpenSenseBoxAPI, leafletData, $timeout, moment){

    $scope.calculating = false;
    $scope.interpolationPickerStart = {
      date: moment().toDate(),
      open: false,
      buttonBar: {
        show: false
      },
      timepickerOptions: {
        readonlyInput: false,
        showMeridian: false,
        max: moment().toDate()
      }
    };
    $scope.interpolationPickerEnd = {
      date: moment().toDate(),
      open: false,
      buttonBar: {
        show: false
      },
      timepickerOptions: {
        readonlyInput: false,
        showMeridian: false,
        max: moment().toDate()
      }
    };

    $scope.visible = false;
    var dates = [];
    $scope.selectedTimeStep = Date.now();
    $scope.slider = {
      options: {
        stepsArray: dates,
        showTicks: true,
        hidePointerLabels: true,
        hideLimitLabels: true,
        onChange: function(id, value) {
          $scope.selectedTimeStep = value;
          $scope.map.removeLayer($scope.layer);
          $scope.layer = createGeoJsonLayer(featureCollections[dates.indexOf(value)], breaks);
          $scope.map.addLayer($scope.layer);
        }
      }
    };
    $scope.refreshSlider = function () {
      $timeout(function () {
        $scope.$broadcast('rzSliderForceRender');
      });
    };

    $scope.$watch('interpolationPickerEnd.open', function(newValue) {
      console.log($scope.interpolationPickerEnd.open);
    });

    $scope.legendTitle = '';
    $scope.legendEntries = [];

    $scope.alerts = [];
    $scope.closeAlert = function(index) {
      $scope.alerts.splice(index, 1);
    };

    $scope.idwPower = 3;
    $scope.cellWidth = 1;
    $scope.numTimeSteps = 1;
    $scope.exposure = "outdoor";
    $scope.layerGroup;
    $scope.selectedPhenomenon = "";
    $scope.map;

    $scope.minIDWPower = 1;
    $scope.maxIDWPower = 9;

    $scope.changeIDWPower = function(number) {
      var newValue = $scope.idwPower + number;
      if (newValue >= $scope.minIDWPower && newValue <= $scope.maxIDWPower) {
        $scope.idwPower += number;
      }
    }

    $scope.minNumTimeSteps = 1;
    $scope.maxNumTimeSteps = 9;

    $scope.changeNumTimeSteps = function(number) {
      var newValue = $scope.numTimeSteps + number;
      if (newValue >= $scope.minNumTimeSteps && newValue <= $scope.maxNumTimeSteps) {
        $scope.numTimeSteps += number;
      }
    }

    $scope.layer;
    var colors = "#A2F689,#B1E36F,#BCD05B,#C4BD4C,#C8AA44,#C99840".split(",");
    var breaks = [];
    var idwLayers = [];
    var featureCollections = [];
    $scope.calculateInterpolation = function () {
      clearInterpolation();
      $scope.calculating = true;

      leafletData.getMap('map_main').then(function (map) {
        if (!angular.isUndefined($scope.layer)) {
          $scope.map.removeLayer($scope.layer);
        }
        $scope.map = map;
        return map.getBounds().toBBoxString();
      }).then(function (bbox) {
        $http.get(OpenSenseBoxAPI.url+'/statistics/idw', {
          params: {
            'phenomenon': $scope.selectedPhenomenon,
            'from-date': moment($scope.interpolationPickerStart.date).toISOString(),
            'to-date': moment($scope.interpolationPickerEnd.date).toISOString(),
            'exposure': $scope.exposure,
            'cellWidth': $scope.cellWidth,
            'power': $scope.idwPower,
            'numClasses': 6,
            'bbox': bbox,
            'numTimeSteps': $scope.numTimeSteps
          }
        })
        .then(function (response) {
          if (response.data.code === 'NotFoundError') {
            return response;
          }

          dates = [];
          response.data.data.timesteps.forEach(function (element, index) {
            dates.push(new Date(element));
            featureCollections.push({type: "FeatureCollection", features: []});
          });

          $scope.slider.value = dates[0];
          $scope.slider.options.stepsArray = dates;
          $scope.selectedTimeStep = dates[0];

          breaks = response.data.data.breaks;

          // Set legend title
          switch ($scope.selectedPhenomenon) {
            case 'Temperatur':
              $scope.legendTitle = $scope.selectedPhenomenon + " in °C"
              break;
            case 'rel. Luftfeuchte':
              $scope.legendTitle = $scope.selectedPhenomenon + " in %"
              break;
            case 'Luftdruck':
              $scope.legendTitle = $scope.selectedPhenomenon + " in hPa"
              break;
            case 'Beleuchtungsstärke':
              $scope.legendTitle = $scope.selectedPhenomenon + " in lx"
              break;
            case 'UV-Intensität':
              $scope.legendTitle = $scope.selectedPhenomenon + " in μW/cm²"
              break;
          }

          // Generate legend
          for (var j = 0; j < breaks.length; j++) {
            var caption = "";
            if (j === breaks.length-1) {
              caption = "> " + breaks[j].toFixed(2);
            } else {
              caption = breaks[j].toFixed(2) +' - '+ breaks[j+1].toFixed(2);
            }
            $scope.legendEntries.push({
              caption: caption,
              style:{
                'background': colors[j]
              }
            });
          }

          response.data.data.featureCollection.features.map(function (feature) {
            for (var i = 0; i < feature.properties.idwValues.length; i++) {
              var newFeature = {};
              angular.copy(feature, newFeature);
              newFeature.properties.idwValues = [];
              var value = feature.properties.idwValues[i];
              newFeature.properties.idwValues.push(value);
              featureCollections[i].features.push(newFeature);
            }
          });

          $scope.layer = createGeoJsonLayer(featureCollections[0], breaks);
          $scope.map.addLayer($scope.layer);

          if (featureCollections.length > 1) {
            $scope.visible = true;
          }
          $scope.refreshSlider();

          return;
        }, function(error) {
          return error;
        })
        .then(function (error) {
          if (angular.isUndefined(error)) {
            return;
          }

          switch (error.data.message) {
            case "planned computation too expensive ((area in square kilometers / cellWidth) > 2500)":
              $scope.alerts.push({msg: 'Der gewählte Kartenausschnitt ist zu groß!'});
              break;
            case "Invalid time frame specified: to-date is in the future":
              $scope.alerts.push({msg: 'Das gewählte Datum darf nicht in der Zukunft liegen!'});
              break;
            case "no senseBoxes found":
              $scope.alerts.push({msg: 'Es konnten keine senseBoxen für die Filtereinstellungen gefunden werden!'});
              break;
            case "no measurements found":
              $scope.alerts.push({msg: 'Es wurden keine Messungen für den angegebenen Zeitpunkt gefunden!'});
              break;
            default:
              $scope.alerts.push({msg: 'Bei der Interpolation ist ein unbekannter Fehler aufgetreten! Bitte überprüfe die Filtereinstellungen.'});
              break;
          }
        })
        .finally(function () {
          $scope.calculating = false;
        });
      });
    };

    function createGeoJsonLayer (featureCollection, breaks) {
      var idwLayer = L.geoJson(featureCollection, {
        style: function (feature) {
          var props = feature.properties;
          for (var key in props) {
            var z = props[key];
            if (!Number.isNaN(z)) {
              var fillColor = colors[0];
              for (var i = 0; i < breaks.length; i++) {
                if (z >= breaks[i]) {
                  fillColor = colors[i];
                } else {
                  break;
                }
              }
              return {
                weight: 0.1,
                fillOpacity: 0.6,
                fillColor: fillColor
              }
            }
            return {
              weight: 0,
              fillColor: "red",
              fillOpacity: 1
            };
          }
        }
      });
      return idwLayer;
    }

    function clearInterpolation () {
      $scope.alerts.length = 0;
      $scope.legendEntries.length = 0;
      $scope.legendTitle = '';
      idwLayers = [];
      featureCollections = [];
      breaks = [];
      if (!angular.isUndefined($scope.map) && !angular.isUndefined($scope.layer)) {
        $scope.map.removeLayer($scope.layer);
        delete $scope.layer;
        $scope.visible = false;
      }
    }

    $scope.removeInterpolation = function () {
      clearInterpolation();
    }

    $scope.showRemoveInterpolation = function () {
      if (!angular.isUndefined($scope.map) && !angular.isUndefined($scope.layer)) {
        return true;
      }
      return false;
    }

    $scope.openCalendar = function(e, picker) {
      e.preventDefault();
      e.stopPropagation();
      switch(picker) {
        case 'interpolationPickerStart':
          $scope.interpolationPickerStart.open = true;
          $scope.interpolationPickerStart.timepickerOptions.max = moment($scope.interpolationPickerEnd.date).toISOString();
          break;
        case 'interpolationPickerEnd':
          console.log(angular.element('#interpolationPickerEnd').parent());
          $scope.interpolationPickerEnd.open = true;
          $scope.interpolationPickerEnd.timepickerOptions.max = moment().toDate();
          break;
      }
    };

    $scope.selectExposure = function (exposure) {
      $scope.exposure = exposure;
    };

    $scope.closeSidebar = function () {
      if (!angular.isUndefined($scope.map) && !angular.isUndefined($scope.layer)) {
        clearInterpolation()
      }
    }
}]);
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc function
 * @name groongaAdminApp.controller:TableShowController
 * @description
 * # TableShowController
 * Controller of the groongaAdminApp
 */
angular.module('groongaAdminApp')
  .controller('TableShowController', [
    '$scope', '$routeParams', '$filter', 'schemaLoader',
    function ($scope, $routeParams, $filter, schemaLoader) {
      var schema;

      function initialize() {
        $scope.table = {
          name: $routeParams.table,
          properties: [],
          columnPropertyNames: [],
          columns: []
        };
        $scope.tables = [];
      }

      initialize();
      schemaLoader()
        .then(function(_schema) {
          schema = _schema;

          var table = schema.tables[$scope.table.name];
          angular.forEach(table.properties, function(value, key) {
            $scope.table.properties.push({name: key, value: value});
          });
          var columns = [];
          angular.forEach(table.columns, function(value) {
            columns.push(value);
          });
          $scope.table.columns = columns.sort(function(column1, column2) {
            return (column1.name > column2.name) ? 1 : -1;
          });
          if (columns.length > 0) {
            var column = columns[columns.length - 1];
            angular.forEach(column.properties, function(value, key) {
              $scope.table.columnPropertyNames.push(key);
            });
          }

          var tables = [];
          angular.forEach(schema.tables, function(value) {
            tables.push(value);
          });
          $scope.tables = tables.sort(function(table1, table2) {
            return (table1.name > table2.name) ? 1 : -1;
          });
        });
    }]);
<commit_msg>table show: use normal column to get property names<commit_after>'use strict';

/**
 * @ngdoc function
 * @name groongaAdminApp.controller:TableShowController
 * @description
 * # TableShowController
 * Controller of the groongaAdminApp
 */
angular.module('groongaAdminApp')
  .controller('TableShowController', [
    '$scope', '$routeParams', '$filter', 'schemaLoader',
    function ($scope, $routeParams, $filter, schemaLoader) {
      var schema;

      function initialize() {
        $scope.table = {
          name: $routeParams.table,
          properties: [],
          columnPropertyNames: [],
          columns: []
        };
        $scope.tables = [];
      }

      initialize();
      schemaLoader()
        .then(function(_schema) {
          schema = _schema;

          var table = schema.tables[$scope.table.name];
          angular.forEach(table.properties, function(value, key) {
            $scope.table.properties.push({name: key, value: value});
          });
          var columns = [];
          angular.forEach(table.columns, function(value) {
            columns.push(value);
          });
          $scope.table.columns = columns.sort(function(column1, column2) {
            return (column1.name > column2.name) ? 1 : -1;
          });
          var representingColumn = columns.find(function(column) {
            return column.name[0] !== '_';
          });
          representingColumn = representingColumn || columns[0];
          if (representingColumn) {
            angular.forEach(representingColumn.properties, function(value, key) {
              $scope.table.columnPropertyNames.push(key);
            });
          }

          var tables = [];
          angular.forEach(schema.tables, function(value) {
            tables.push(value);
          });
          $scope.tables = tables.sort(function(table1, table2) {
            return (table1.name > table2.name) ? 1 : -1;
          });
        });
    }]);
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var _firefox_ReaderMode = {
    // If this is http or https content, and not an index page, then try to run Readability. If anything
    // fails, the app will never be notified and we don't show the button. That is ok for now since there
    // is no error feedback possible anyway.

    readabilityResult: null,

    DEBUG: false,

    debug: function(s) {
        if (!this.DEBUG) {
            return;
        }
        console.log(s);
    },

    checkReadability: function() {
        if (document.location.href.match(/^http:\/\/localhost:\d+\/reader-mode\/page/)) {
            this.debug({Type: "ReaderModeStateChange", Value: "Active"});
            __cliqzjs___readerModeMessageHandler({Type: "ReaderModeStateChange", Value: "Active"});
            return;
        }

        if ((document.location.protocol === "http:" || document.location.protocol === "https:") && document.location.pathname !== "/") {
            // Short circuit in case we already ran Readability. This mostly happens when going
            // back/forward: the page will be cached and the result will still be there.
            if (_firefox_ReaderMode.readabilityResult && _firefox_ReaderMode.readabilityResult.content) {
                this.debug({Type: "ReaderModeStateChange", Value: "Available"});
                __cliqzjs___readerModeMessageHandler({Type: "ReaderModeStateChange", Value: "Available"});
                return;
            }

            var uri = {
                spec: document.location.href,
                host: document.location.host,
                prePath: document.location.protocol + "//" + document.location.host, // TODO This is incomplete, needs username/password and port
                scheme: document.location.protocol.substr(0, document.location.protocol.indexOf(":")),
                pathBase: document.location.protocol + "//" + document.location.host + location.pathname.substr(0, location.pathname.lastIndexOf("/") + 1)
            }

            // document.cloneNode() can cause the webview to break (bug 1128774).
            // Serialize and then parse the document instead.
            var docStr = new XMLSerializer().serializeToString(document);
            var doc = new DOMParser().parseFromString(docStr, "text/html");
            var readability = new Readability(uri, doc);
            _firefox_ReaderMode.readabilityResult = readability.parse();

            this.debug({Type: "ReaderModeStateChange", Value: _firefox_ReaderMode.readabilityResult !== null ? "Available" : "Unavailable"});
            __cliqzjs___readerModeMessageHandler({Type: "ReaderModeStateChange", Value: _firefox_ReaderMode.readabilityResult !== null ? "Available" : "Unavailable"});

            return;
        }

        this.debug({Type: "ReaderModeStateChange", Value: "Unavailable"});
        __cliqzjs___readerModeMessageHandler({Type: "ReaderModeStateChange", Value: "Unavailable"});
    },

    // Readerize the document. Since we did the actual readerization already in checkReadability, we
    // can simply return the results we already have.

    readerize: function() {
        return _firefox_ReaderMode.readabilityResult;
    },

    // TODO The following code only makes sense in about:reader context. It may be a good idea to move
    //   it out of this file and into for example a Reader.js.

    currentStyle: null,

    setStyle: function(style) {
        // Configure the theme (light, dark)
        if (_firefox_ReaderMode.currentStyle != null) {
            document.body.classList.remove(_firefox_ReaderMode.currentStyle.theme);
        }
        document.body.classList.add(style.theme);

        // Configure the font size (1-5)
        if (_firefox_ReaderMode.currentStyle != null) {
            document.body.classList.remove("font-size" + _firefox_ReaderMode.currentStyle.fontSize);
        }
        document.body.classList.add("font-size" + style.fontSize);

        // Configure the font type
        if (_firefox_ReaderMode.currentStyle != null) {
            document.body.classList.remove(_firefox_ReaderMode.currentStyle.fontType);
        }
        document.body.classList.add(style.fontType);

        // Remember the style
        _firefox_ReaderMode.currentStyle = style;
    },

    _BLOCK_IMAGES_SELECTOR: ".content p > img:only-child, " +
        ".content p > a:only-child > img:only-child, " +
        ".content .wp-caption img, " +
        ".content figure img",

    updateImageMargins: function() {
        var contentElement = document.getElementById('reader-content');

        var windowWidth = window.innerWidth;
        var contentWidth = contentElement.offsetWidth;
        var maxWidthStyle = windowWidth + "px !important";

        var setImageMargins = function(img) {
            if (!img._originalWidth) {
                img._originalWidth = img.offsetWidth;
            }

            var imgWidth = img._originalWidth;

            // If the image is taking more than half of the screen, just make
            // it fill edge-to-edge.
            if (imgWidth < contentWidth && imgWidth > windowWidth * 0.55) {
                imgWidth = windowWidth;
            }

            var sideMargin = Math.max((contentWidth - windowWidth) / 2, (contentWidth - imgWidth) / 2);

            var imageStyle = sideMargin + "px !important";
            var widthStyle = imgWidth + "px !important";

            var cssText = "max-width: " + maxWidthStyle + ";" +
                "width: " + widthStyle + ";" +
                "margin-left: " + imageStyle + ";" +
                "margin-right: " + imageStyle + ";";

            img.style.cssText = cssText;
        }

        var imgs = document.querySelectorAll(_firefox_ReaderMode._BLOCK_IMAGES_SELECTOR);
        for (var i = imgs.length; --i >= 0;) {
            var img = imgs[i];
            if (img.width > 0) {
                setImageMargins(img);
            } else {
                img.onload = function() {
                    setImageMargins(img);
                }
            }
        }
    },

    showContent: function() {
        // Make the reader visible
        var messageElement = document.getElementById('reader-message');
        messageElement.style.display = "none";
        var headerElement = document.getElementById('reader-header');
        headerElement.style.display = "block"
        var contentElement = document.getElementById('reader-content');
        contentElement.style.display = "block";
    },

    configureReader: function() {
        // Configure the reader with the initial style that was injected in the page.
        var style = JSON.parse(document.body.getAttribute("data-readerStyle"));
        _firefox_ReaderMode.setStyle(style);

        // The order here is important. Because updateImageMargins depends on contentElement.offsetWidth which
        // will not be set until contentElement is visible. If this leads to annoying content reflowing then we
        // need to look at an alternative way to do this.
        _firefox_ReaderMode.showContent();
        _firefox_ReaderMode.updateImageMargins();
    }
};

window.addEventListener('load', function(event) {
    // If this is an about:reader page that we are loading, apply the initial style to the page.
    if (document.location.href.match(/^http:\/\/localhost:\d+\/reader-mode\/page/)) {
        _firefox_ReaderMode.configureReader();
    }
});

window.addEventListener('pageshow', function(event) {
    // If this is an about:reader page that we are showing, fire an event to the native code
    if (document.location.href.match(/^http:\/\/localhost:\d+\/reader-mode\/page/)) {
        __cliqzjs___readerModeMessageHandler({Type: "ReaderPageEvent", Value: "PageShow"});
    }
});
<commit_msg>[IB-1384] Disabled showing reader mode button in Youtube pages<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var _firefox_ReaderMode = {
    // If this is http or https content, and not an index page, then try to run Readability. If anything
    // fails, the app will never be notified and we don't show the button. That is ok for now since there
    // is no error feedback possible anyway.

    readabilityResult: null,

    DEBUG: false,

    debug: function(s) {
        if (!this.DEBUG) {
            return;
        }
        console.log(s);
    },

    checkReadability: function() {
        if (document.location.href.match(/^http:\/\/localhost:\d+\/reader-mode\/page/)) {
            this.debug({Type: "ReaderModeStateChange", Value: "Active"});
            __cliqzjs___readerModeMessageHandler({Type: "ReaderModeStateChange", Value: "Active"});
            return;
        }
        // CLIQZ: Added check to prevent showing reader mode in youtube pages
        if ((document.location.protocol === "http:" || document.location.protocol === "https:") && document.location.pathname !== "/" && document.location.hostname.indexOf("youtube.com") == -1) {
            // Short circuit in case we already ran Readability. This mostly happens when going
            // back/forward: the page will be cached and the result will still be there.
            if (_firefox_ReaderMode.readabilityResult && _firefox_ReaderMode.readabilityResult.content) {
                this.debug({Type: "ReaderModeStateChange", Value: "Available"});
                __cliqzjs___readerModeMessageHandler({Type: "ReaderModeStateChange", Value: "Available"});
                return;
            }

            var uri = {
                spec: document.location.href,
                host: document.location.host,
                prePath: document.location.protocol + "//" + document.location.host, // TODO This is incomplete, needs username/password and port
                scheme: document.location.protocol.substr(0, document.location.protocol.indexOf(":")),
                pathBase: document.location.protocol + "//" + document.location.host + location.pathname.substr(0, location.pathname.lastIndexOf("/") + 1)
            }

            // document.cloneNode() can cause the webview to break (bug 1128774).
            // Serialize and then parse the document instead.
            var docStr = new XMLSerializer().serializeToString(document);
            var doc = new DOMParser().parseFromString(docStr, "text/html");
            var readability = new Readability(uri, doc);
            _firefox_ReaderMode.readabilityResult = readability.parse();

            this.debug({Type: "ReaderModeStateChange", Value: _firefox_ReaderMode.readabilityResult !== null ? "Available" : "Unavailable"});
            __cliqzjs___readerModeMessageHandler({Type: "ReaderModeStateChange", Value: _firefox_ReaderMode.readabilityResult !== null ? "Available" : "Unavailable"});

            return;
        }

        this.debug({Type: "ReaderModeStateChange", Value: "Unavailable"});
        __cliqzjs___readerModeMessageHandler({Type: "ReaderModeStateChange", Value: "Unavailable"});
    },

    // Readerize the document. Since we did the actual readerization already in checkReadability, we
    // can simply return the results we already have.

    readerize: function() {
        return _firefox_ReaderMode.readabilityResult;
    },

    // TODO The following code only makes sense in about:reader context. It may be a good idea to move
    //   it out of this file and into for example a Reader.js.

    currentStyle: null,

    setStyle: function(style) {
        // Configure the theme (light, dark)
        if (_firefox_ReaderMode.currentStyle != null) {
            document.body.classList.remove(_firefox_ReaderMode.currentStyle.theme);
        }
        document.body.classList.add(style.theme);

        // Configure the font size (1-5)
        if (_firefox_ReaderMode.currentStyle != null) {
            document.body.classList.remove("font-size" + _firefox_ReaderMode.currentStyle.fontSize);
        }
        document.body.classList.add("font-size" + style.fontSize);

        // Configure the font type
        if (_firefox_ReaderMode.currentStyle != null) {
            document.body.classList.remove(_firefox_ReaderMode.currentStyle.fontType);
        }
        document.body.classList.add(style.fontType);

        // Remember the style
        _firefox_ReaderMode.currentStyle = style;
    },

    _BLOCK_IMAGES_SELECTOR: ".content p > img:only-child, " +
        ".content p > a:only-child > img:only-child, " +
        ".content .wp-caption img, " +
        ".content figure img",

    updateImageMargins: function() {
        var contentElement = document.getElementById('reader-content');

        var windowWidth = window.innerWidth;
        var contentWidth = contentElement.offsetWidth;
        var maxWidthStyle = windowWidth + "px !important";

        var setImageMargins = function(img) {
            if (!img._originalWidth) {
                img._originalWidth = img.offsetWidth;
            }

            var imgWidth = img._originalWidth;

            // If the image is taking more than half of the screen, just make
            // it fill edge-to-edge.
            if (imgWidth < contentWidth && imgWidth > windowWidth * 0.55) {
                imgWidth = windowWidth;
            }

            var sideMargin = Math.max((contentWidth - windowWidth) / 2, (contentWidth - imgWidth) / 2);

            var imageStyle = sideMargin + "px !important";
            var widthStyle = imgWidth + "px !important";

            var cssText = "max-width: " + maxWidthStyle + ";" +
                "width: " + widthStyle + ";" +
                "margin-left: " + imageStyle + ";" +
                "margin-right: " + imageStyle + ";";

            img.style.cssText = cssText;
        }

        var imgs = document.querySelectorAll(_firefox_ReaderMode._BLOCK_IMAGES_SELECTOR);
        for (var i = imgs.length; --i >= 0;) {
            var img = imgs[i];
            if (img.width > 0) {
                setImageMargins(img);
            } else {
                img.onload = function() {
                    setImageMargins(img);
                }
            }
        }
    },

    showContent: function() {
        // Make the reader visible
        var messageElement = document.getElementById('reader-message');
        messageElement.style.display = "none";
        var headerElement = document.getElementById('reader-header');
        headerElement.style.display = "block"
        var contentElement = document.getElementById('reader-content');
        contentElement.style.display = "block";
    },

    configureReader: function() {
        // Configure the reader with the initial style that was injected in the page.
        var style = JSON.parse(document.body.getAttribute("data-readerStyle"));
        _firefox_ReaderMode.setStyle(style);

        // The order here is important. Because updateImageMargins depends on contentElement.offsetWidth which
        // will not be set until contentElement is visible. If this leads to annoying content reflowing then we
        // need to look at an alternative way to do this.
        _firefox_ReaderMode.showContent();
        _firefox_ReaderMode.updateImageMargins();
    }
};

window.addEventListener('load', function(event) {
    // If this is an about:reader page that we are loading, apply the initial style to the page.
    if (document.location.href.match(/^http:\/\/localhost:\d+\/reader-mode\/page/)) {
        _firefox_ReaderMode.configureReader();
    }
});

window.addEventListener('pageshow', function(event) {
    // If this is an about:reader page that we are showing, fire an event to the native code
    if (document.location.href.match(/^http:\/\/localhost:\d+\/reader-mode\/page/)) {
        __cliqzjs___readerModeMessageHandler({Type: "ReaderPageEvent", Value: "PageShow"});
    }
});
<|endoftext|>


<commit_before>Function.prototype.bind||(Function.prototype.bind=function(a){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var b=Array.prototype.slice.call(arguments,1),c=this,d=function(){},e=function(){return c.apply(this instanceof d?this:a,b.concat(Array.prototype.slice.call(arguments)))};return this.prototype&&(d.prototype=this.prototype),e.prototype=new d,e}),define(["dojo/_base/declare","dijit/_Widget","dijit/_TemplatedMixin","dijit/_WidgetsInTemplateMixin","esri/dijit/Search","dojo/dom-construct","dijit/layout/ContentPane","dijit/TitlePane","dojox/widget/TitleGroup","dojo/on","dojo/Deferred","dojo/query","dojo/NodeList-data"],function(a,b,c,d,e,f,g,h,i,j,k,l){var m=function(a){return void 0===a||null===a||""===a},n=function(a,b){var c=f.toDom(""+a+"");return l(c).data("result",b),c},o=function(a,b){var c=0,d=0,e=new i,f=a.Addresses;for(c=0,d=f.length;d>c;c+=1)e.addChild(new g({content:n(f[c].address,f[c])}));b.addChild(new h({title:a.Description,content:e,open:!1}))},p=function(a){var b,c,d=0,e=0,f=new i;if(!m(a.Addresses)&&a.Addresses.length>1){for(b=a.Addresses,d=0,e=b.length;e>d;d++)!m(b[d].Addresses)&&b[d].Addresses.length>1?o(b[d],f):(c=m(b[d].address)?n(b[d].Addresses[0].address,b[d].Addresses[0]):n(b[d].address,b[d]),f.addChild(new g({content:c})));f.startup()}else m(a.Addresses[0].address)?!m(a.Addresses[0].Addresses)&&a.Addresses[0].Addresses.length>0&&o(a.Addresses[0],f):f=new g({content:n(a.Addresses[0].address,a.Addresses[0])});return f},q=function(a){this.get("contentSet")===!1&&(this.set("content",p(a)),this.set("contentSet",!0))};return a([b,c,d,e],{baseClass:"drilldown",widgetsInTemplate:!0,resultsElement:null,_titleGroups:[],constructor:function(b){a.safeMixin(this,b)},destroy:function(){this._clearPicklist(),this.inherited(arguments)},search:function(){var a=this,b=new k;return this.inherited(arguments).then(function(c){a._buildPickListUi(c),b.resolve()}),b.promise},clear:function(){this._clearPicklist(),this.inherited(arguments)},_hydrateResults:function(a){return a.PickListItems?a:this.inherited(arguments)},_formatResults:function(a,b,c){var d={activeSourceIndex:b,value:c,numResults:0,numErrors:0,errors:null,results:null},e={},f={},g=0;if(a){if(b===this._allIndex){for(g=0;g0){for(a=0,b=this._titleGroups.length;b>a;a++)this._titleGroups[a].destroy();this._titleGroups=[]}},_showNoResults:function(){this._noResults(this.value),this._showNoResultsMenu()},_isSingleResult:function(a){var b,c,d,e=0;for(d in a)a.hasOwnProperty(d)&&(e++,c=d);if(1===e){if(b=a[c].PickListItems,1===b.length&&1===b[0].Addresses.length){if(m(b[0].Addresses[0].Addresses))return!0;if(!m(b[0].Addresses[0].Addresses)&&1===b[0].Addresses[0].Addresses.length)return!0}return!1}return!1},_buildPickListUi:function(a){var b,c,d,e,g,n,o=this,r=0,s=0,t=!1,u=new k;if(this._clearPicklist(),f.destroy(this.resultsElement),this.resultsElement=f.create("div",{"class":"arcgisSearch searchGroup picklistResults"},this.domNode,"last"),o.activeSourceIndex!==this._allIndex&&this._isSingleResult(a))e=this._hydrateResult(a[o.activeSourceIndex].PickListItems[0].Addresses[0],o.activeSourceIndex,!1),this.select(e);else{if(!m(a)){for(d in a)if(a.hasOwnProperty(d))if(m(a[d])||m(a[d].PickListItems))t=!0;else if(b=a[d].PickListItems,s=b.length,s>0)for(g=f.create("div",{id:d},this.resultsElement,"last"),c=new i(null,g),this._titleGroups.push(c),r=0;s>r;r+=1)n=new h({title:b[r].Description,open:!1,contentSet:!1}),1===s?(n.set("open",!0),n.set("contentSet",!0),n.set("content",p(b[r]))):n.own(n.on("click",q.bind(n,b[r]))),c.addChild(n),t=!1;else t=!0;u.resolve()}t&&this._showNoResults(),m(this.resultsElement)||j(this.resultsElement,".drilldownResult:click",function(){var a=l(this).data()[0],b=o._hydrateResult(a.result,o.activeSourceIndex,!1);o.select(b),o._clearPicklist()})}return u.promise}})});<commit_msg>Update from travis build of arcgis-dijit-drilldown<commit_after>Function.prototype.bind||(Function.prototype.bind=function(a){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var b=Array.prototype.slice.call(arguments,1),c=this,d=function(){},e=function(){return c.apply(this instanceof d?this:a,b.concat(Array.prototype.slice.call(arguments)))};return this.prototype&&(d.prototype=this.prototype),e.prototype=new d,e}),define(["dojo/_base/declare","dijit/_Widget","dijit/_TemplatedMixin","dijit/_WidgetsInTemplateMixin","esri/dijit/Search","dojo/dom-construct","dijit/layout/ContentPane","dijit/TitlePane","dojox/widget/TitleGroup","dojo/on","dojo/Deferred","dojo/query","dojo/NodeList-data"],function(a,b,c,d,e,f,g,h,i,j,k,l){var m=function(a){return void 0===a||null===a||""===a},n=function(a,b){var c=f.toDom(""+a+"");return l(c).data("result",b),c},o=function(a,b){return!m(a)&&a.length>0&&b?""+a.length+"":""},p=function(a,b,c){var d=0,e=0,f=new i,j=a.Addresses;for(d=0,e=j.length;e>d;d+=1)f.addChild(new g({content:n(j[d].address,j[d])}));b.addChild(new h({title:a.Description+o(j,c),content:f,open:!1}))},q=function(a,b){var c,d,e=0,f=0,h=new i;if(!m(a.Addresses)&&a.Addresses.length>1){for(c=a.Addresses,e=0,f=c.length;f>e;e++)!m(c[e].Addresses)&&c[e].Addresses.length>1?p(c[e],h,b):(d=m(c[e].address)?n(c[e].Addresses[0].address,c[e].Addresses[0]):n(c[e].address,c[e]),h.addChild(new g({content:d})));h.startup()}else m(a.Addresses[0].address)?!m(a.Addresses[0].Addresses)&&a.Addresses[0].Addresses.length>0&&p(a.Addresses[0],h):h=new g({content:n(a.Addresses[0].address,a.Addresses[0])});return h},r=function(a,b){this.get("contentSet")===!1&&(this.set("content",q(a,b)),this.set("contentSet",!0))};return a([b,c,d,e],{baseClass:"drilldown",widgetsInTemplate:!0,resultsElement:null,_titleGroups:[],showCounts:!1,constructor:function(b){a.safeMixin(this,b)},destroy:function(){this._clearPicklist(),this.inherited(arguments)},search:function(){var a=this,b=new k;return this.inherited(arguments).then(function(c){a._buildPickListUi(c),b.resolve()}),b.promise},clear:function(){this._clearPicklist(),this.inherited(arguments)},_hydrateResults:function(a){return a.PickListItems?a:this.inherited(arguments)},_formatResults:function(a,b,c){var d={activeSourceIndex:b,value:c,numResults:0,numErrors:0,errors:null,results:null},e={},f={},g=0;if(a){if(b===this._allIndex){for(g=0;g0){for(a=0,b=this._titleGroups.length;b>a;a++)this._titleGroups[a].destroy();this._titleGroups=[]}},_showNoResults:function(){this._noResults(this.value),this._showNoResultsMenu()},_isSingleResult:function(a){var b,c,d,e=0;for(d in a)a.hasOwnProperty(d)&&(e++,c=d);if(1===e){if(b=a[c].PickListItems,1===b.length&&1===b[0].Addresses.length){if(m(b[0].Addresses[0].Addresses))return!0;if(!m(b[0].Addresses[0].Addresses)&&1===b[0].Addresses[0].Addresses.length)return!0}return!1}return!1},_buildPickListUi:function(a){var b,c,d,e,g,n,p=this,s=0,t=0,u=!1,v=new k;if(this._clearPicklist(),f.destroy(this.resultsElement),this.resultsElement=f.create("div",{"class":"arcgisSearch searchGroup picklistResults"},this.domNode,"last"),p.activeSourceIndex!==this._allIndex&&this._isSingleResult(a))e=this._hydrateResult(a[p.activeSourceIndex].PickListItems[0].Addresses[0],p.activeSourceIndex,!1),this.select(e);else{if(!m(a)){for(d in a)if(a.hasOwnProperty(d))if(m(a[d])||m(a[d].PickListItems))u=!0;else if(b=a[d].PickListItems,t=b.length,t>0)for(g=f.create("div",{id:d},this.resultsElement,"last"),c=new i(null,g),this._titleGroups.push(c),s=0;t>s;s+=1)n=new h({title:b[s].Description+o(b[s].Addresses,this.showCounts),open:!1,contentSet:!1}),1===t?(n.set("open",!0),n.set("contentSet",!0),n.set("content",q(b[s],this.showCounts))):n.own(n.on("click",r.bind(n,b[s],this.showCounts))),c.addChild(n),u=!1;else u=!0;v.resolve()}u&&this._showNoResults(),m(this.resultsElement)||j(this.resultsElement,".drilldownResult:click",function(){var a=l(this).data()[0],b=p._hydrateResult(a.result,p.activeSourceIndex,!1);p.select(b),p._clearPicklist()})}return v.promise}})});<|endoftext|>