<commit_before>export { init, testCorrectness, disposeResources };

import * as Pool from "../util/Pool.js";
import * as Recyclable from "../util/Recyclable.js";
import * as Pool_Asserter from "../util/Pool_Asserter.js";
import * as ValueDesc from "../Unpacker/ValueDesc.js";
import * as BatchIdCalculator from "./BatchIdCalculator.js";
import * as Stage from "../Conv/Stage.js";
import * as Stage_Reference from "./Ref/Stage_Reference.js";
import * as Stage_TestParams from "./Ref/Stage_TestParams.js";
import * as ImageSourceBag from "./Ref/ImageSourceBag.js";
import * as NumberImage from "./Ref/NumberImage.js";

/**
 * Test CNN Stage.
 *
 * @see {@link https://www.measurethat.net/Benchmarks/Show/15055/90/colorfulcakechen-cnn-block-a4bc81be0a8974ea17d20da05fdf}
 */

/**
 * A test set.
 */
class HeightWidthDepth {

  /**
   * @param {number} height            image height
   * @param {number} width             image width
   * @param {number} depth             image channel count
   */
  constructor( height, width, depth ) {

    this.disposeResources();

    this.height = height;
    this.width = width;
    this.depth = depth;

    this.valueCount = height * width * depth;
  }

  disposeResources() {
    if ( this.dataTensor3dArray ) {
      tf.dispose( this.dataTensor3dArray );
      this.dataTensor3dArray = null;
    }

    this.block_PerformanceTest_release();
  }

  block_PerformanceTest_init() {

    // Release dataTensor3d too. Because perofrmance testing uses larger different input image from correctness testing.
    this.disposeResources();

    // Larger input image for performance testing.
    let inputTensorCount = 1;
    this.testPerformance_NumberImageArray = Recyclable.OwnerArray.Pool.get_or_create_by( inputTensorCount );
    this.dataTensor3dArray = tf.tidy( () => {
      let inputScaleBoundsArray = ActivationEscaping.ScaleBoundsArray.Pool.get_or_create_by( this.depth );

      let dataTensor3dArray = new Array( inputTensorCount );

      let shape = [ this.height, this.width, this.depth ];
      let elementCount = tf.util.sizeFromShape( shape );

      for ( let i = 0; i < dataTensor3dArray.length; ++i ) {
        let numberBegin = ( i * elementCount );
        let numberEnd = numberBegin + elementCount;

        let image = this.testPerformance_NumberImageArray[ i ] = NumberImage.Base.Pool.get_or_create_by(
          this.height, this.width, this.depth, undefined,
          inputScaleBoundsArray, null, BoundsArraySet.InputsOutputs, Weights.Base.ValueBounds );

        for ( let j = 0; j < elementCount; ++j ) {
          image.dataArray[ j ] = numberBegin + j;
        }

        dataTensor3dArray[ i ] = tf.tensor( image.dataArray, shape );
      }

      inputScaleBoundsArray.disposeResources_and_recycleToPool();
      inputScaleBoundsArray = null;

      return dataTensor3dArray;
    });

//!!!
    let stepCountRequested = 10;

    // sourceHeight, sourceWidth, sourceChannelCount, stepCountRequested, bPointwise1,
    // depthwiseFilterHeight, depthwiseFilterWidth, nActivationId, bPointwise2ActivatedAtStageEnd,
    // nConvStageType, bKeepInputTensor
    //
    //
    // The block performance testing should:
    //   - ( bKeepInputTensor == true ). Otherwise, the this.dataTensor3d will be destroyed.
    //

    this.testCaseMap = new Map();

    // Test Case 1: (MobileNetV1, ( bPointwise1 == true ))
    this.testCaseMap.set( "MobileNetV1_bPointwise1_true", { testParams:
      ( new Stage_TestParams.Base() ).set_By_ParamsScattered(
        this.height, this.width, this.depth, stepCountRequested, true,
        3, 3, ValueDesc.ActivationFunction.Singleton.Ids.CLIP_BY_VALUE_N3_P3, true,
        ValueDesc.ConvStageType.Singleton.Ids.MOBILE_NET_V1,
        true
      ) } );

    // Test Case 2: (MobileNetV1_padValid, ( bPointwise1 == true ))
    this.testCaseMap.set( "MobileNetV1_padValid_bPointwise1_true", { testParams:
      ( new Stage_TestParams.Base() ).set_By_ParamsScattered(
        this.height, this.width, this.depth, stepCountRequested, true,
        3, 3, ValueDesc.ActivationFunction.Singleton.Ids.CLIP_BY_VALUE_N3_P3, true,
        ValueDesc.ConvStageType.Singleton.Ids.MOBILE_NET_V1_PAD_VALID,
        true
      ) } );

    // Test Case 3: (MobileNetV2_Thin, ( bPointwise1 == true ))
    this.testCaseMap.set( "MobileNetV2_Thin_bPointwise1_true", { testParams:
      ( new Stage_TestParams.Base() ).set_By_ParamsScattered(
        this.height, this.width, this.depth, stepCountRequested, true,
        3, 3, ValueDesc.ActivationFunction.Singleton.Ids.CLIP_BY_VALUE_N3_P3, true,
        ValueDesc.ConvStageType.Singleton.Ids.MOBILE_NET_V2_THIN,
        true
      ) } );

    // Test Case 4: (MobileNetV2, ( bPointwise1 == true ))
    this.testCaseMap.set( "MobileNetV2_bPointwise1_true", { testParams:
      ( new Stage_TestParams.Base() ).set_By_ParamsScattered(
        this.height, this.width, this.depth, stepCountRequested, true,
        3, 3, ValueDesc.ActivationFunction.Singleton.Ids.CLIP_BY_VALUE_N3_P3, true,
        ValueDesc.ConvStageType.Singleton.Ids.MOBILE_NET_V2,
        true
      ) } );

    // Test Case 5: (ShuffleNetV2, ( bPointwise1 == true ))
    this.testCaseMap.set( "ShuffleV2_bPointwise1_true", { testParams:
      ( new Stage_TestParams.Base() ).set_By_ParamsScattered(
        this.height, this.width, this.depth, stepCountRequested, true,
        3, 3, ValueDesc.ActivationFunction.Singleton.Ids.CLIP_BY_VALUE_N3_P3, true,
        ValueDesc.ConvStageType.Singleton.Ids.SHUFFLE_NET_V2,
        true
      ) } );

    // Test Case 6: (ShuffleNetV2_byPointwise22, ( bPointwise1 == true ))
    this.testCaseMap.set( "ShuffleV2_byPointwise22_bPointwise1_true", { testParams:
      ( new Stage_TestParams.Base() ).set_By_ParamsScattered(
        this.height, this.width, this.depth, stepCountRequested, true,
        3, 3, ValueDesc.ActivationFunction.Singleton.Ids.CLIP_BY_VALUE_N3_P3, true,
        ValueDesc.ConvStageType.Singleton.Ids.SHUFFLE_NET_V2_BY_POINTWISE22,
        true
      ) } );

    // Test Case 7: (ShuffleNetV2_byMobileNetV1, ( bPointwise1 == true ))
    this.testCaseMap.set( "ShuffleNetV2_byMobileNetV1_bPointwise1_true", { testParams:
      ( new Stage_TestParams.Base() ).set_By_ParamsScattered(
        this.height, this.width, this.depth, stepCountRequested, true,
        3, 3, ValueDesc.ActivationFunction.Singleton.Ids.CLIP_BY_VALUE_N3_P3, true,
        ValueDesc.ConvStageType.Singleton.Ids.SHUFFLE_NET_V2_BY_MOBILE_NET_V1,
        true
      ) } );

    // Test Case 8: (ShuffleNetV2_byMobileNetV1_padValid, ( bPointwise1 == true ))
    this.testCaseMap.set( "ShuffleNetV2_byMobileNetV1_padValid_bPointwise1_true", { testParams:
      ( new Stage_TestParams.Base() ).set_By_ParamsScattered(
        this.height, this.width, this.depth, stepCountRequested, true,
        3, 3, ValueDesc.ActivationFunction.Singleton.Ids.CLIP_BY_VALUE_N3_P3, true,
        ValueDesc.ConvStageType.Singleton.Ids.SHUFFLE_NET_V2_BY_MOBILE_NET_V1_PAD_VALID,
        true
      ) } );

    // Create the different Stage objects for performance testing.
    for ( let name_testCase of this.testCaseMap.entries() ) {
      let name = name_testCase[ 0 ];
      let testCase = name_testCase[ 1 ];
      if ( !testCase.block ) {
        testCase.block = Stage_Reference.Base.Stage_create( testCase.testParams );
      }

      console.log( `Stage.${name}: tensorWeightCount = { Extracted: ${testCase.block.tensorWeightCountExtracted}, `
        + `Total: ${testCase.block.tensorWeightCountTotal} }` );
    }

  }

  block_PerformanceTest_release() {
    if ( this.testCaseMap ) {
      for ( let name_testCase of this.testCaseMap.entries() ) {
        let name = name_testCase[ 0 ];
        let testCase = name_testCase[ 1 ];
        if ( testCase.block ) {
          testCase.block.disposeResources();
        }
      }
      this.testCaseMap = null;
    }

//!!!
    if ( this.testPerformance_NumberImageArray ) {
      this.testPerformance_NumberImageArray.disposeResources_and_recycleToPool();
      this.testPerformance_NumberImageArray = null;
    }
  }

  /** Test apply by Xxx */
  testStage_ByName( testCaseName ) {
    let testCase = this.testCaseMap.get( testCaseName );
    let block = testCase.block;
    let outputTensor3d = block.apply( this.dataTensor3dArray[ 0 ] );
    tf.dispose( outputTensor3d );
  }

  /** Testing whether the results of different implementation are the same. */
  testCorrectness() {

    tf.tidy( () => {

      let memoryInfo_testCorrectness_before = tf.memory(); // Test memory leakage of imageSourceBag.

      {
        // Note: imageSourceBag should not be created outside tidy() because tidy() will dispose tensors
        //       dynamically created in them.
        let imageSourceBag = new ImageSourceBag.Base();

        let testParams = new Stage_TestParams.Base();
        let testParamsGenerator = testParams.ParamsGenerator();
        let testReference = new Stage_Reference.Base();

        let batchIdCalculator = new BatchIdCalculator.Base( 50 * 1000 );

        try {
          for ( testParams of testParamsGenerator ) {
            batchIdCalculator.checkAndDisplay( testParams.id );
            testReference.testCorrectness( imageSourceBag, testParams );
          }

        } catch ( e ) {
          let backendName = tf.getBackend();
          console.log( `jsPerf_Stage.js: testCorrectness(): backendName=${backendName}, `
            + `Stage testParams.id == ${testParams.id}` );
          throw e;
        }

        imageSourceBag.disposeResources();
      }

      let memoryInfo_testCorrectness_after = tf.memory();

      if ( memoryInfo_testCorrectness_after.numTensors != memoryInfo_testCorrectness_before.numTensors )
        throw Error( `testCorrectness() memory leak. `
          + `result tensor count (${memoryInfo_testCorrectness_after.numTensors}) `
          + `should be (${memoryInfo_testCorrectness_before.numTensors} `
          + `` );
    });

    // After correctness testing done, create all Stage for performance testing.
    this.block_PerformanceTest_init();
  }

}

function init() {
  //console.log("jsPerf_Stage.js, init()");

  disposeResources();

  let depth = 4;

  // Using mobile phone's resolution ( 1080 * 2160 ) will crash the computer.
  // Using ( 1 / 10 ) of computer screen ( 1080 * 1920 ).
  globalThis.testSet_108x192x4 = new HeightWidthDepth( 108, 192, depth ); // height, width, depth

  globalThis.testSet_All = [
    globalThis.testSet_108x192x4
  ];
}

function testCorrectness() {
  for ( let i = 0; i < globalThis.testSet_All.length; ++i ) {
    let testSet = globalThis.testSet_All[ i ];
    testSet.testCorrectness();
  }
}

function disposeResources() {
  if ( globalThis.testSet_All ) {
    for ( let i = 0; i < globalThis.testSet_All.length; ++i ) {
      let testSet = globalThis.testSet_All[ i ];
      if ( testSet )
        testSet.disposeResources();
    }

    globalThis.testSet_All = null;
  }

  globalThis.testSet_108x192x4
    = null;
}
<commit_msg>Update jsPerf_Stage.js<commit_after>export { init, testCorrectness, disposeResources };

import * as Pool from "../util/Pool.js";
import * as Recyclable from "../util/Recyclable.js";
import * as Pool_Asserter from "../util/Pool_Asserter.js";
import * as ValueDesc from "../Unpacker/ValueDesc.js";
import * as BatchIdCalculator from "./BatchIdCalculator.js";
import * as Stage from "../Conv/Stage.js";
import * as Stage_Reference from "./Ref/Stage_Reference.js";
import * as Stage_TestParams from "./Ref/Stage_TestParams.js";
import * as ImageSourceBag from "./Ref/ImageSourceBag.js";
import * as NumberImage from "./Ref/NumberImage.js";

/**
 * Test CNN Stage.
 *
 * @see {@link https://www.measurethat.net/Benchmarks/Show/15055/90/colorfulcakechen-cnn-block-a4bc81be0a8974ea17d20da05fdf}
 */

/**
 * A test set.
 */
class HeightWidthDepth {

  /**
   * @param {number} height            image height
   * @param {number} width             image width
   * @param {number} depth             image channel count
   */
  constructor( height, width, depth ) {

    this.disposeResources();

    this.height = height;
    this.width = width;
    this.depth = depth;

    this.valueCount = height * width * depth;
  }

  disposeResources() {
    if ( this.dataTensor3dArray ) {
      tf.dispose( this.dataTensor3dArray );
      this.dataTensor3dArray = null;
    }

    this.block_PerformanceTest_release();
  }

  block_PerformanceTest_init() {

    // Release dataTensor3d too. Because perofrmance testing uses larger different input image from correctness testing.
    this.disposeResources();

    // Larger input image for performance testing.
    let inputTensorCount = 1;
    this.testPerformance_NumberImageArray = Recyclable.OwnerArray.Pool.get_or_create_by( inputTensorCount );
    this.dataTensor3dArray = tf.tidy( () => {
      let inputScaleBoundsArray = ActivationEscaping.ScaleBoundsArray.Pool.get_or_create_by( this.depth );

      let dataTensor3dArray = new Array( inputTensorCount );

      let shape = [ this.height, this.width, this.depth ];
      let elementCount = tf.util.sizeFromShape( shape );

      for ( let i = 0; i < dataTensor3dArray.length; ++i ) {
        let numberBegin = ( i * elementCount );
        let numberEnd = numberBegin + elementCount;

        let image = this.testPerformance_NumberImageArray[ i ] = NumberImage.Base.Pool.get_or_create_by(
          this.height, this.width, this.depth, undefined,
          inputScaleBoundsArray, null, BoundsArraySet.InputsOutputs, Weights.Base.ValueBounds );

        for ( let j = 0; j < elementCount; ++j ) {
          image.dataArray[ j ] = numberBegin + j;
        }

        dataTensor3dArray[ i ] = tf.tensor( image.dataArray, shape );
      }

      inputScaleBoundsArray.disposeResources_and_recycleToPool();
      inputScaleBoundsArray = null;

      return dataTensor3dArray;
    });

    let stepCountRequested = 10;
    let nSqueezeExcitationChannelCountDivisor = 4;

    // sourceHeight, sourceWidth, sourceChannelCount,
    // nConvStageTypeId,
    // blockCountRequested,
    // bPointwise1,
    // depthwiseFilterHeight, depthwiseFilterWidth,
    // bPointwise2ActivatedAtStageEnd,
    // nSqueezeExcitationChannelCountDivisor,
    // nActivationId,
    // bKeepInputTensor
    //
    // The stage performance testing should:
    //   - ( bKeepInputTensor == true ). Otherwise, the this.dataTensor3d will be destroyed.
    //

    this.testCaseMap = new Map();

    // Test Case 1: (MobileNetV1, ( bPointwise1 == true ))
    this.testCaseMap.set( "MobileNetV1_bPointwise1_true", { testParams:
      ( new Stage_TestParams.Base() ).set_By_ParamsScattered(
        this.height, this.width, this.depth,
        ValueDesc.ConvStageType.Singleton.Ids.MOBILE_NET_V1,
        stepCountRequested, true,
        3, 3, true, nSqueezeExcitationChannelCountDivisor, ValueDesc.ActivationFunction.Singleton.Ids.CLIP_BY_VALUE_N3_P3,
        true
      ) } );

//!!!
    // Test Case 2: (MobileNetV1_padValid, ( bPointwise1 == true ))
    this.testCaseMap.set( "MobileNetV1_padValid_bPointwise1_true", { testParams:
      ( new Stage_TestParams.Base() ).set_By_ParamsScattered(
        this.height, this.width, this.depth,
        ValueDesc.ConvStageType.Singleton.Ids.MOBILE_NET_V1_PAD_VALID,
        stepCountRequested, true,
        3, 3, true, nSqueezeExcitationChannelCountDivisor, ValueDesc.ActivationFunction.Singleton.Ids.CLIP_BY_VALUE_N3_P3,
        true
      ) } );

    // Test Case 3: (MobileNetV2_Thin, ( bPointwise1 == true ))
    this.testCaseMap.set( "MobileNetV2_Thin_bPointwise1_true", { testParams:
      ( new Stage_TestParams.Base() ).set_By_ParamsScattered(
        this.height, this.width, this.depth,
        ValueDesc.ConvStageType.Singleton.Ids.MOBILE_NET_V2_THIN,
        stepCountRequested, true,
        3, 3, true, nSqueezeExcitationChannelCountDivisor, ValueDesc.ActivationFunction.Singleton.Ids.CLIP_BY_VALUE_N3_P3,
        true
      ) } );

    // Test Case 4: (MobileNetV2, ( bPointwise1 == true ))
    this.testCaseMap.set( "MobileNetV2_bPointwise1_true", { testParams:
      ( new Stage_TestParams.Base() ).set_By_ParamsScattered(
        this.height, this.width, this.depth,
        ValueDesc.ConvStageType.Singleton.Ids.MOBILE_NET_V2,
        stepCountRequested, true,
        3, 3, true, nSqueezeExcitationChannelCountDivisor, ValueDesc.ActivationFunction.Singleton.Ids.CLIP_BY_VALUE_N3_P3,
        true
      ) } );

    // Test Case 5: (ShuffleNetV2, ( bPointwise1 == true ))
    this.testCaseMap.set( "ShuffleV2_bPointwise1_true", { testParams:
      ( new Stage_TestParams.Base() ).set_By_ParamsScattered(
        this.height, this.width, this.depth,
        ValueDesc.ConvStageType.Singleton.Ids.SHUFFLE_NET_V2,
        stepCountRequested, true,
        3, 3, true, nSqueezeExcitationChannelCountDivisor, ValueDesc.ActivationFunction.Singleton.Ids.CLIP_BY_VALUE_N3_P3,
        true
      ) } );

    // Test Case 6: (ShuffleNetV2_byPointwise22, ( bPointwise1 == true ))
    this.testCaseMap.set( "ShuffleV2_byPointwise22_bPointwise1_true", { testParams:
      ( new Stage_TestParams.Base() ).set_By_ParamsScattered(
        this.height, this.width, this.depth,
        ValueDesc.ConvStageType.Singleton.Ids.SHUFFLE_NET_V2_BY_POINTWISE22,
        stepCountRequested, true,
        3, 3, true, nSqueezeExcitationChannelCountDivisor, ValueDesc.ActivationFunction.Singleton.Ids.CLIP_BY_VALUE_N3_P3,
        true
      ) } );

    // Test Case 7: (ShuffleNetV2_byMobileNetV1, ( bPointwise1 == true ))
    this.testCaseMap.set( "ShuffleNetV2_byMobileNetV1_bPointwise1_true", { testParams:
      ( new Stage_TestParams.Base() ).set_By_ParamsScattered(
        this.height, this.width, this.depth,
        ValueDesc.ConvStageType.Singleton.Ids.SHUFFLE_NET_V2_BY_MOBILE_NET_V1,
        stepCountRequested, true,
        3, 3, true, nSqueezeExcitationChannelCountDivisor, ValueDesc.ActivationFunction.Singleton.Ids.CLIP_BY_VALUE_N3_P3,
        true
      ) } );

    // Test Case 8: (ShuffleNetV2_byMobileNetV1_padValid, ( bPointwise1 == true ))
    this.testCaseMap.set( "ShuffleNetV2_byMobileNetV1_padValid_bPointwise1_true", { testParams:
      ( new Stage_TestParams.Base() ).set_By_ParamsScattered(
        this.height, this.width, this.depth,
        ValueDesc.ConvStageType.Singleton.Ids.SHUFFLE_NET_V2_BY_MOBILE_NET_V1_PAD_VALID,
        stepCountRequested, true,
        3, 3, true, nSqueezeExcitationChannelCountDivisor, ValueDesc.ActivationFunction.Singleton.Ids.CLIP_BY_VALUE_N3_P3,
        true
      ) } );

    // Create the different Stage objects for performance testing.
    for ( let name_testCase of this.testCaseMap.entries() ) {
      let name = name_testCase[ 0 ];
      let testCase = name_testCase[ 1 ];
      if ( !testCase.block ) {
        testCase.block = Stage_Reference.Base.Stage_create( testCase.testParams );
      }

      console.log( `Stage.${name}: tensorWeightCount = { Extracted: ${testCase.block.tensorWeightCountExtracted}, `
        + `Total: ${testCase.block.tensorWeightCountTotal} }` );
    }

  }

  block_PerformanceTest_release() {
    if ( this.testCaseMap ) {
      for ( let name_testCase of this.testCaseMap.entries() ) {
        let name = name_testCase[ 0 ];
        let testCase = name_testCase[ 1 ];
        if ( testCase.block ) {
          testCase.block.disposeResources();
        }
      }
      this.testCaseMap = null;
    }

//!!!
    if ( this.testPerformance_NumberImageArray ) {
      this.testPerformance_NumberImageArray.disposeResources_and_recycleToPool();
      this.testPerformance_NumberImageArray = null;
    }
  }

  /** Test apply by Xxx */
  testStage_ByName( testCaseName ) {
    let testCase = this.testCaseMap.get( testCaseName );
    let block = testCase.block;
    let outputTensor3d = block.apply( this.dataTensor3dArray[ 0 ] );
    tf.dispose( outputTensor3d );
  }

  /** Testing whether the results of different implementation are the same. */
  testCorrectness() {

    tf.tidy( () => {

      let memoryInfo_testCorrectness_before = tf.memory(); // Test memory leakage of imageSourceBag.

      {
        // Note: imageSourceBag should not be created outside tidy() because tidy() will dispose tensors
        //       dynamically created in them.
        let imageSourceBag = new ImageSourceBag.Base();

        let testParams = new Stage_TestParams.Base();
        let testParamsGenerator = testParams.ParamsGenerator();
        let testReference = new Stage_Reference.Base();

        let batchIdCalculator = new BatchIdCalculator.Base( 50 * 1000 );

        try {
          for ( testParams of testParamsGenerator ) {
            batchIdCalculator.checkAndDisplay( testParams.id );
            testReference.testCorrectness( imageSourceBag, testParams );
          }

        } catch ( e ) {
          let backendName = tf.getBackend();
          console.log( `jsPerf_Stage.js: testCorrectness(): backendName=${backendName}, `
            + `Stage testParams.id == ${testParams.id}` );
          throw e;
        }

        imageSourceBag.disposeResources();
      }

      let memoryInfo_testCorrectness_after = tf.memory();

      if ( memoryInfo_testCorrectness_after.numTensors != memoryInfo_testCorrectness_before.numTensors )
        throw Error( `testCorrectness() memory leak. `
          + `result tensor count (${memoryInfo_testCorrectness_after.numTensors}) `
          + `should be (${memoryInfo_testCorrectness_before.numTensors} `
          + `` );
    });

    // After correctness testing done, create all Stage for performance testing.
    this.block_PerformanceTest_init();
  }

}

function init() {
  //console.log("jsPerf_Stage.js, init()");

  disposeResources();

  let depth = 4;

  // Using mobile phone's resolution ( 1080 * 2160 ) will crash the computer.
  // Using ( 1 / 10 ) of computer screen ( 1080 * 1920 ).
  globalThis.testSet_108x192x4 = new HeightWidthDepth( 108, 192, depth ); // height, width, depth

  globalThis.testSet_All = [
    globalThis.testSet_108x192x4
  ];
}

function testCorrectness() {
  for ( let i = 0; i < globalThis.testSet_All.length; ++i ) {
    let testSet = globalThis.testSet_All[ i ];
    testSet.testCorrectness();
  }
}

function disposeResources() {
  if ( globalThis.testSet_All ) {
    for ( let i = 0; i < globalThis.testSet_All.length; ++i ) {
      let testSet = globalThis.testSet_All[ i ];
      if ( testSet )
        testSet.disposeResources();
    }

    globalThis.testSet_All = null;
  }

  globalThis.testSet_108x192x4
    = null;
}
<|endoftext|>


<commit_before>var fControllers = angular.module('fControllers', ['ngSanitize']);

fControllers.controller("messagebarCtrl", ['$scope', '$http', '$timeout', 'makeRequest',
                                                function($scope, $timeout, $http, makeRequest){

    $scope.animated = false;

    $scope.min_bar = false;

    $scope.pre_login = true;

    $scope.user = USER_ID;
    $scope.nickname = USER_NICKNAME;
    $scope.page = PAGE_ID;
    $scope.hash = USER_HASH;
    $scope.role = parseInt(USER_ROLE);
//    $scope.avatar_size = '150';
    $scope.avatar_size = $scope.min_bar ? '50' : '150';

    $scope.min_barToggle = function() {
        console.log("anything?");
        $scope.min_bar = !$scope.min_bar;
        if ($scope.min_bar) {
            $scope.avatar_size = 50;
            $scope.gravatar = 'http://www.gravatar.com/avatar/' + USER_HASH + '?d=mm&s=' + $scope.avatar_size;
        }
        else {
            $scope.avatar_size = 150;
            $scope.gravatar = 'http://www.gravatar.com/avatar/' + USER_HASH + '?d=mm&s=' + $scope.avatar_size;
        }
    };

    $scope.gravatar = 'http://www.gravatar.com/avatar/' + USER_HASH + '?d=mm&s=' + $scope.avatar_size;
    $scope.entry_progress = 0;
    $scope.total_entry = 0;

    //progress bar stuff:
    $scope.bar = {'progress': 100, 'remaining': 0};

    //listener for regular entries changing:
    $scope.$on('progressbarUpdate', function(e, data) {
       console.log("received progressbarupdate notice", data);

        var portion = (parseInt(PAGE_ID))*data.page_len;
        var left_section = (portion/data.total_entry_count)*100;

        if (left_section >= 100) {
            $scope.bar.progress = 100;
            $scope.bar.remaining = 0;
        }
        else {
            $scope.bar.progress = left_section;
            $scope.bar.remaining = 100 - $scope.bar.progress;
        }

        $scope.total_entry = data.total_entry_count;
        $scope.entry_progress = portion;

        //page*len = number of total entries we've looked at
        //total = total number of entries

        //(page*len)/total * 100 = left side
        //100 - left side = right side
        //

    });

    $scope.$on('refreshUpdate', function(e, data) {
       console.log("progressbar receieved refresh update", data);

        $scope.animated = true;

        var portion = (data.current_fin.length/data.feed_ids.length)*100;  //percentage done
        var remains = 100 - portion;
        $scope.bar.progress = portion;
        $scope.bar.remaining = remains;
    });

    $scope.$on('entryRefreshInit', function() {

        console.log('messagebar: I have detected a refresh occurring.  I should probably deal with it.');

//        $timeout(function() {
//            console.log('I am a timeout!!');
//        }, 1000);

    });

}]);

fControllers.controller("EntriesCtrl", ['$scope', '$http', 'makeRequest', function($scope, $http, makeRequest){

    $scope.user = USER_ID;
    $scope.page = PAGE_ID;

    $scope.message = 'this is the entries controller...';
    $scope.no_entries = false;
    $scope.compressed = "entrycontent fullview";  // "entrycontent compview"

    $scope.myData = {};
    $scope.myData.getEntries = function() {

        makeRequest.pullEntryData(PAGE_ID, false)
            .then(function(data){
                console.log(data);
            });
    };

    $scope.markAsRead = function(id) {
        makeRequest.markAsRead(id);

        //and when complete, probably change the entry object unread status of the entry.
        //loop over entries array and find out e_id; - set to unread-false.
//        console.log($scope.myData.fromServer);
        var entry_array = $scope.myData.fromServer.e;

        for (var i=0; i< entry_array.length; i++) {
            if (entry_array[i].entry_id == id) {
                entry_array[i].entry_unread = false;
            }
        }
    };

    $scope.myData.requestCategoryFeed = function(category) {
        console.log('-- request for category initiated, data: ', category);
        makeRequest.requestCategoryFeed(category);
    };

    //listeners:
    $scope.$on('entriesUpdated', function(){
                var Edata = makeRequest.getUpdatedEntries();
                $scope.myData.fromServer = Edata;
                $scope.no_entries = Edata.e.length == 0;
                $scope.pager = Edata.pager;
    });

    $scope.$on('feedChange', function(){
                $scope.myData.getEntries();
    });

    //init our entries:
    $scope.myData.getEntries();  // initialize entries on load

}]);

fControllers.controller("ToolbarCtrl", ['$scope', '$modal', '$timeout', 'makeRequest',
                                            function($scope, $modal, $timeout, makeRequest){

    $scope.userId = parseInt(USER_ID);

    $scope.message = "I am the toolbar Ctrl!";

    $scope.refreshFeeds = function() {
      console.log("refreshFeeds activated, page: ", PAGE_ID);
      var feed_ids = makeRequest.getFeedIds();
      console.log('controller says feed ids are: ', feed_ids);

        makeRequest.refreshFeeds()
        .then(function(){
                //this should fire when the response back from server is that refresh has started
                //from here, we should start the polling of done, update progressbar, etc.
                console.log("refresh feeds completed! Let's update Entries!");
            });

    };

    $scope.all_feeds = function() {
      console.log("allFeeds activated, page: ", PAGE_ID);

        makeRequest.allFeeds()
        .then(function(){
                console.log("all feeds completed! Let's update Entries!");
            });

    };

    $scope.inputText = '';
    $scope.errorMessage = false;
    $scope.infoMessage = false;
    $scope.successMessage = false;
    $scope.messageText = '';

    //if multiple feeds are returned, display modal to choose which one:
    $scope.modal_many_feeds = function() {

        var modalInstance = $modal.open({
            templateUrl: 'modalContent.html',
            controller: modalInstanceCtrl,
            resolve: {feeds: function() {return $scope.feeds;}}
        });

        modalInstance.result.then(function(item){
            console.log('result receieved: ', item, 'adding feed...');
            $scope.add_feed(item);
        });

    };

    var modalInstanceCtrl = function($scope, $modalInstance, feeds) {
        $scope.feeds = feeds;
        $scope.choice = function(item) {
            console.log('user chose', item);
            $modalInstance.close(item);
        };
    };

    //sending notifications to the user
    $scope.userNotifications = function(result) {
        console.log("controller received: ", result);
        switch (result.category){
            case "error":
                $scope.errorMessage = true;
                $scope.messageText = result.msg;
                break;

            case "info":
                $scope.infoMessage = true;
                $scope.messageText = result.msg;
                break;

            case "success":
                $scope.successMessage = true;
                $scope.messageText = result.msg;
                makeRequest.requestSubUpdate();  //notify subs that feeds have changed
                makeRequest.notifyPageChange();  //notify entries that feeds have updated
                break;

            // deal with multiple feeds returned
            case "multi":
                $scope.infoMessage = true;
                $scope.messageText = result.msg;
                $scope.feeds = result.f;
                $scope.modal_many_feeds();
                break;

            default:
                console.log("message type was not understood by controller");
            }

            $timeout(function() {
                $scope.errorMessage = false;
                $scope.infoMessage = false;
                $scope.successMessage = false;
                $scope.messageText = '';
            }, 3000);

    };

    $scope.add_feed = function(item) {
      console.log("add_feed has begun!");
      var feed_item = item || this.inputText;
      console.log('input text: ', feed_item);
      makeRequest.addFeed(feed_item);

    };

    // listeners:
    $scope.$on("entriesUpdated", function() {

        console.log("toolbar heard about entries!");
        $scope.user_preferences = makeRequest.getUserPreferences();
        console.log("view status: ", $scope.user_preferences.compressed);

    });

    $scope.$on('notificationBroadcast', function(e, result){
        $scope.userNotifications(result);
    });

}]);

fControllers.controller("SubCtrl", ['$scope', '$http', 'makeRequest', function($scope, $http, makeRequest){

    $scope.user = USER_ID;
    $scope.page = PAGE_ID;

    $scope.subData = {};

    $scope.subData.getSubs = function() {
        makeRequest.getSubs()
            .then(function(data) {
               $scope.subData.fromServer = data;
            });
    };

    $scope.isOn = function(cat) {

        var sub_array = $scope.subData.fromServer.subs;
        var category_on = false;
        var result;

        if (typeof sub_array === 'undefined') {
            result = 'label-default';
            return result;
        }

        for (var i=0; i<sub_array.length; i++) {
            if (sub_array[i].category == cat) {
                if (sub_array[i].active === true) {
                    category_on = true;
                    break;
                }
            }
        }

        if (category_on) {
            result = 'label-success';
        }

        return result;

    };

    $scope.subData.toggleFeed = function(userFeedId) {

        makeRequest.toggleFeed(userFeedId)
            .then(function(data){
                console.log(data);

                var sub_array = $scope.subData.fromServer.subs;

                for (var i=0; i<sub_array.length; i++) {
                    if (sub_array[i].uf_id == userFeedId) {
                        sub_array[i].active = !sub_array[i].active;
                    }
                }
            });

    };

    $scope.subData.singleFeed = function(userFeedId) {

        makeRequest.singleFeed(userFeedId)
            .then(function(data){
                console.log(data);

                var sub_array = $scope.subData.fromServer.subs;

                for (var i=0; i<sub_array.length; i++) {
                    if (sub_array[i].uf_id != userFeedId) {
                        sub_array[i].active = false;
                    }
                    else {
                        sub_array[i].active = true;
                    }
                }
            });
    };

    $scope.subData.categoryFeed = function(category) {

        console.log('categoryFeed function started: ', category);

        var sub_array = $scope.subData.fromServer.subs;

        makeRequest.categoryFeed(category)
            .then(function(data){
                console.log('categoryFeed request finished: ', data);

                for (var i=0; i<sub_array.length; i++) {
                    if (sub_array[i].category == category) {
                        sub_array[i].active = data.all;
                    }
                }
            });
    };

    $scope.subData.unsubscribeFeed = function(userFeedId){
        console.log('unsubscribing feed...', userFeedId);
        makeRequest.unsubscribeFeed(userFeedId);
    };

    //listeners:
    $scope.$on("requestCategoryFeed", function(event, c) {
       console.log("i detect a requestCategory broadcast! data: ", c);
        $scope.subData.categoryFeed(c);
    });

    $scope.$on("toolbarFeedChange", function() {

        var sub_array = $scope.subData.fromServer.subs;
        for (var i=0; i<sub_array.length; i++) {
            sub_array[i].active = true;
        }
    });

    $scope.$on("requestSubUpdate", function() {
       console.log("sub update was requested");
        $scope.subData.getSubs();
    });

    //init our subs:
    $scope.subData.getSubs();

}]);

fControllers.controller("PagerCtrl", ['$scope', 'makeRequest', 'hotkeys', '$document',
                                                function($scope, makeRequest, hotkeys, $document){

    //define hotkey events:
    hotkeys.add({
       combo: 'left',
       description: 'Move backward one page.',
       callback: function() {
           console.log('left pressed');

           //pager is coming back undefined in some cases:
           if (typeof($scope.pager) != "undefined" && $scope.pager.has_prev) {
               $scope.pager_functions.advance_page(-1);
           }
       }
    });

    hotkeys.add({
       combo: 'right',
       description: 'Move forward one page.',
       callback: function() {
           console.log('right pressed');
           if (typeof($scope.pager) != "undefined" && $scope.pager.has_next) {
               $scope.pager_functions.advance_page(1);
           }
       }
    });

    hotkeys.add({
       combo: 'down',
       description: 'Move to next entry',
        callback: function() {
            console.log('down was pressed');
            $scope.pager_functions.advance_entry(1);
//            testScroll();
        }
    });

    hotkeys.add({
       combo: 'up',
       description: 'Move to previous entry',
        callback: function() {
            console.log('up was pressed');
            $scope.pager_functions.advance_entry(-1);
        }
    });

    hotkeys.add({
       combo: 'w',
       description: 'test',
        callback: function() {
            console.log('w was pressed');
            scrollToTop();
        }
    });

    $scope.pager_functions = {};

    $scope.pager_functions.advance_page = function(amount) {

        console.log('this is page id: ');
        console.log(PAGE_ID);
        PAGE_ID = parseInt(PAGE_ID) + amount;

        //switch to next/prev page worth of entries

        makeRequest.notifyPageChange();
        makeRequest.setOffset(0, true);
          scrollToEntry(0);
    };

    //this might not be necessary anymore, but let's keep it for the time being:
    var scrollToTop = function() {
        console.log('scrollToTop active');
        $document.scrollTopAnimated(0,1000).then(function() {console.log('complete');},
                                                 function() {console.log('failed');} );
    };

    $scope.pager_functions.advance_entry = function(amount){
        console.log('advance entry active with amount: ', amount);
        makeRequest.setOffset(amount);

        var progress = makeRequest.getProgress();
        console.log('total number of entries on page: ', progress.progress.page_len);
        console.log('current offset on page: ', progress.progress.current_offset);

        //there's a stupid exception being thrown here and weird behaviour on
        //offset == page_len || offset == 0 to fix up.
        scrollToEntry(progress.progress.current_offset);
    };

    var scrollToEntry = function(entryId) {

      var elementTarget = 'offsetId' + entryId.toString();
      console.log('scrollToEntry active with ID: ', elementTarget, entryId);

       var scrollTarget = angular.element(document.getElementById(elementTarget));
       $document.scrollToElement(scrollTarget, 60, 500);
    };

    //listeners:
    $scope.$on('pagerUpdated', function() {
        console.log('i detected that the pager was updated!');
        $scope.pager = makeRequest.getPager();
    });

}]);<commit_msg>pushing for work.<commit_after>var fControllers = angular.module('fControllers', ['ngSanitize']);

fControllers.controller("messagebarCtrl", ['$scope', '$http', '$timeout', 'makeRequest',
                                                function($scope, $timeout, $http, makeRequest){

    $scope.animated = false;

    $scope.min_bar = false;

    $scope.pre_login = true;

    $scope.user = USER_ID;
    $scope.nickname = USER_NICKNAME;
    $scope.page = PAGE_ID;
    $scope.hash = USER_HASH;
    $scope.role = parseInt(USER_ROLE);
//    $scope.avatar_size = '150';
    $scope.avatar_size = $scope.min_bar ? '50' : '150';

    $scope.min_barToggle = function() {
        console.log("anything?");
        $scope.min_bar = !$scope.min_bar;
        if ($scope.min_bar) {
            $scope.avatar_size = 50;
            $scope.gravatar = 'http://www.gravatar.com/avatar/' + USER_HASH + '?d=mm&s=' + $scope.avatar_size;
        }
        else {
            $scope.avatar_size = 150;
            $scope.gravatar = 'http://www.gravatar.com/avatar/' + USER_HASH + '?d=mm&s=' + $scope.avatar_size;
        }
    };

    $scope.gravatar = 'http://www.gravatar.com/avatar/' + USER_HASH + '?d=mm&s=' + $scope.avatar_size;
    $scope.entry_progress = 0;
    $scope.total_entry = 0;

    //progress bar stuff:
    $scope.bar = {'progress': 100, 'remaining': 0};

    //listener for regular entries changing:
    $scope.$on('progressbarUpdate', function(e, data) {
       console.log("received progressbarupdate notice", data);

        var portion = (parseInt(PAGE_ID))*data.page_len;
        var left_section = (portion/data.total_entry_count)*100;

        if (left_section >= 100) {
            $scope.bar.progress = 100;
            $scope.bar.remaining = 0;
        }
        else {
            $scope.bar.progress = left_section;
            $scope.bar.remaining = 100 - $scope.bar.progress;
        }

        $scope.total_entry = data.total_entry_count;
        $scope.entry_progress = portion;

        //page*len = number of total entries we've looked at
        //total = total number of entries

        //(page*len)/total * 100 = left side
        //100 - left side = right side
        //

    });

    $scope.$on('refreshUpdate', function(e, data) {
       console.log("progressbar receieved refresh update", data);

        $scope.animated = true;

        var portion = (data.current_fin.length/data.feed_ids.length)*100;  //percentage done
        var remains = 100 - portion;
        $scope.bar.progress = portion;
        $scope.bar.remaining = remains;
    });

    $scope.$on('entryRefreshInit', function() {

        console.log('messagebar: I have detected a refresh occurring.  I should probably deal with it.');

//        $timeout(function() {
//            console.log('I am a timeout!!');
//        }, 1000);

    });

}]);

fControllers.controller("EntriesCtrl", ['$scope', '$http', 'makeRequest', function($scope, $http, makeRequest){

    $scope.user = USER_ID;
    $scope.page = PAGE_ID;

    $scope.message = 'this is the entries controller...';
    $scope.no_entries = false;
    $scope.compressed = "entrycontent fullview";  // "entrycontent compview"

    $scope.myData = {};
    $scope.myData.getEntries = function() {

        makeRequest.pullEntryData(PAGE_ID, false)
            .then(function(data){
                console.log(data);
            });
    };

    $scope.markAsRead = function(id) {
        makeRequest.markAsRead(id);

        //and when complete, probably change the entry object unread status of the entry.
        //loop over entries array and find out e_id; - set to unread-false.
//        console.log($scope.myData.fromServer);
        var entry_array = $scope.myData.fromServer.e;

        for (var i=0; i< entry_array.length; i++) {
            if (entry_array[i].entry_id == id) {
                entry_array[i].entry_unread = false;
            }
        }
    };

    $scope.myData.requestCategoryFeed = function(category) {
        console.log('-- request for category initiated, data: ', category);
        makeRequest.requestCategoryFeed(category);
    };

    //listeners:
    $scope.$on('entriesUpdated', function(){
                var Edata = makeRequest.getUpdatedEntries();
                $scope.myData.fromServer = Edata;
                $scope.no_entries = Edata.e.length == 0;
                $scope.pager = Edata.pager;
    });

    $scope.$on('feedChange', function(){
                $scope.myData.getEntries();
    });

    //init our entries:
    $scope.myData.getEntries();  // initialize entries on load

}]);

fControllers.controller("ToolbarCtrl", ['$scope', '$modal', '$timeout', 'makeRequest',
                                            function($scope, $modal, $timeout, makeRequest){

    $scope.userId = parseInt(USER_ID);

    $scope.message = "I am the toolbar Ctrl!";

    $scope.refreshFeeds = function() {
      console.log("refreshFeeds activated, page: ", PAGE_ID);
      var feed_ids = makeRequest.getFeedIds();
      console.log('controller says feed ids are: ', feed_ids);

        makeRequest.refreshFeeds()
        .then(function(){
                //this should fire when the response back from server is that refresh has started
                //from here, we should start the polling of done, update progressbar, etc.
                console.log("refresh feeds completed! Let's update Entries!");
            });

    };

    $scope.all_feeds = function() {
      console.log("allFeeds activated, page: ", PAGE_ID);

        makeRequest.allFeeds()
        .then(function(){
                console.log("all feeds completed! Let's update Entries!");
            });

    };

    $scope.inputText = '';
    $scope.errorMessage = false;
    $scope.infoMessage = false;
    $scope.successMessage = false;
    $scope.messageText = '';

    //if multiple feeds are returned, display modal to choose which one:
    $scope.modal_many_feeds = function() {

        var modalInstance = $modal.open({
            templateUrl: 'modalContent.html',
            controller: modalInstanceCtrl,
            resolve: {feeds: function() {return $scope.feeds;}}
        });

        modalInstance.result.then(function(item){
            console.log('result receieved: ', item, 'adding feed...');
            $scope.add_feed(item);
        });

    };

    var modalInstanceCtrl = function($scope, $modalInstance, feeds) {
        $scope.feeds = feeds;
        $scope.choice = function(item) {
            console.log('user chose', item);
            $modalInstance.close(item);
        };
    };

    //sending notifications to the user
    $scope.userNotifications = function(result) {
        console.log("controller received: ", result);
        switch (result.category){
            case "error":
                $scope.errorMessage = true;
                $scope.messageText = result.msg;
                break;

            case "info":
                $scope.infoMessage = true;
                $scope.messageText = result.msg;
                break;

            case "success":
                $scope.successMessage = true;
                $scope.messageText = result.msg;
                makeRequest.requestSubUpdate();  //notify subs that feeds have changed
                makeRequest.notifyPageChange();  //notify entries that feeds have updated
                break;

            // deal with multiple feeds returned
            case "multi":
                $scope.infoMessage = true;
                $scope.messageText = result.msg;
                $scope.feeds = result.f;
                $scope.modal_many_feeds();
                break;

            default:
                console.log("message type was not understood by controller");
            }

            $timeout(function() {
                $scope.errorMessage = false;
                $scope.infoMessage = false;
                $scope.successMessage = false;
                $scope.messageText = '';
            }, 3000);

    };

    $scope.add_feed = function(item) {
      console.log("add_feed has begun!");
      var feed_item = item || this.inputText;
      console.log('input text: ', feed_item);
      makeRequest.addFeed(feed_item);

    };

    // listeners:
    $scope.$on("entriesUpdated", function() {

        console.log("toolbar heard about entries!");
        $scope.user_preferences = makeRequest.getUserPreferences();
        console.log("view status: ", $scope.user_preferences.compressed);

    });

    $scope.$on('notificationBroadcast', function(e, result){
        $scope.userNotifications(result);
    });

}]);

fControllers.controller("SubCtrl", ['$scope', '$http', 'makeRequest', function($scope, $http, makeRequest){

    $scope.user = USER_ID;
    $scope.page = PAGE_ID;

    $scope.subData = {};

    $scope.subData.getSubs = function() {
        makeRequest.getSubs()
            .then(function(data) {
               $scope.subData.fromServer = data;
            });
    };

    $scope.isOn = function(cat) {

        var sub_array = $scope.subData.fromServer.subs;
        var category_on = false;
        var result;

        if (typeof sub_array === 'undefined') {
            result = 'label-default';
            return result;
        }

        for (var i=0; i<sub_array.length; i++) {
            if (sub_array[i].category == cat) {
                if (sub_array[i].active === true) {
                    category_on = true;
                    break;
                }
            }
        }

        if (category_on) {
            result = 'label-success';
        }

        return result;

    };

    $scope.subData.toggleFeed = function(userFeedId) {

        makeRequest.toggleFeed(userFeedId)
            .then(function(data){
                console.log(data);

                var sub_array = $scope.subData.fromServer.subs;

                for (var i=0; i<sub_array.length; i++) {
                    if (sub_array[i].uf_id == userFeedId) {
                        sub_array[i].active = !sub_array[i].active;
                    }
                }
            });

    };

    $scope.subData.singleFeed = function(userFeedId) {

        makeRequest.singleFeed(userFeedId)
            .then(function(data){
                console.log(data);

                var sub_array = $scope.subData.fromServer.subs;

                for (var i=0; i<sub_array.length; i++) {
                    if (sub_array[i].uf_id != userFeedId) {
                        sub_array[i].active = false;
                    }
                    else {
                        sub_array[i].active = true;
                    }
                }
            });
    };

    $scope.subData.categoryFeed = function(category) {

        console.log('categoryFeed function started: ', category);

        var sub_array = $scope.subData.fromServer.subs;

        makeRequest.categoryFeed(category)
            .then(function(data){
                console.log('categoryFeed request finished: ', data);

                for (var i=0; i<sub_array.length; i++) {
                    if (sub_array[i].category == category) {
                        sub_array[i].active = data.all;
                    }
                }
            });
    };

    $scope.subData.unsubscribeFeed = function(userFeedId){
        console.log('unsubscribing feed...', userFeedId);
        makeRequest.unsubscribeFeed(userFeedId);
    };

    //listeners:
    $scope.$on("requestCategoryFeed", function(event, c) {
       console.log("i detect a requestCategory broadcast! data: ", c);
        $scope.subData.categoryFeed(c);
    });

    $scope.$on("toolbarFeedChange", function() {

        var sub_array = $scope.subData.fromServer.subs;
        for (var i=0; i<sub_array.length; i++) {
            sub_array[i].active = true;
        }
    });

    $scope.$on("requestSubUpdate", function() {
       console.log("sub update was requested");
        $scope.subData.getSubs();
    });

    //init our subs:
    $scope.subData.getSubs();

}]);

fControllers.controller("PagerCtrl", ['$scope', 'makeRequest', 'hotkeys', '$document',
                                                function($scope, makeRequest, hotkeys, $document){

    //define hotkey events:
    hotkeys.add({
       combo: 'left',
       description: 'Move backward one page.',
       callback: function() {
           console.log('left pressed');

           //pager is coming back undefined in some cases:
           if (typeof($scope.pager) != "undefined" && $scope.pager.has_prev) {
               $scope.pager_functions.advance_page(-1);
           }
       }
    });

    hotkeys.add({
       combo: 'right',
       description: 'Move forward one page.',
       callback: function() {
           console.log('right pressed');
           if (typeof($scope.pager) != "undefined" && $scope.pager.has_next) {
               $scope.pager_functions.advance_page(1);
           }
       }
    });

    hotkeys.add({
       combo: 'down',
       description: 'Move to next entry',
        callback: function() {
            console.log('down was pressed');
            $scope.pager_functions.advance_entry(1);
//            testScroll();
        }
    });

    hotkeys.add({
       combo: 'up',
       description: 'Move to previous entry',
        callback: function() {
            console.log('up was pressed');
            $scope.pager_functions.advance_entry(-1);
        }
    });

    hotkeys.add({
       combo: 'w',
       description: 'test',
        callback: function() {
            console.log('w was pressed');
            scrollToTop();
        }
    });

    $scope.pager_functions = {};

    $scope.pager_functions.advance_page = function(amount) {

        console.log('this is page id: ');
        console.log(PAGE_ID);
        PAGE_ID = parseInt(PAGE_ID) + amount;

        //switch to next/prev page worth of entries

        makeRequest.notifyPageChange();
        makeRequest.setOffset(0, true);
          scrollToEntry(0);
    };

    //this might not be necessary anymore, but let's keep it for the time being:
    var scrollToTop = function() {
        console.log('scrollToTop active');
        $document.scrollTopAnimated(0,1000).then(function() {console.log('complete');},
                                                 function() {console.log('failed');} );
    };

    $scope.pager_functions.advance_entry = function(amount){
        console.log('advance entry active with amount: ', amount);
        makeRequest.setOffset(amount);

        var progress = makeRequest.getProgress();
        console.log('total number of entries on page: ', progress.progress.page_len);
        console.log('current offset on page: ', progress.progress.current_offset);

        //there's a stupid exception being thrown here and weird behaviour on
        //offset == page_len || offset == 0 to fix up.
        scrollToEntry(progress.progress.current_offset);
    };

    var scrollToEntry = function(entryId) {

      var elementTarget = 'offsetId' + entryId.toString();
      console.log('scrollToEntry active with ID: ', elementTarget, entryId);

       var scrollTarget = angular.element(document.getElementById(elementTarget));
       $document.scrollToElement(scrollTarget, 60, 500);
    };

    //listeners:
    $scope.$on('pagerUpdated', function() {
        console.log('i detected that the pager was updated!');
        $scope.pager = makeRequest.getPager();
    });

}]);<|endoftext|>


<commit_before>frappe.provide('frappe.ui.form');

frappe.quick_edit = function(doctype, name) {
	frappe.db.get_doc(doctype, name).then(doc => {
		frappe.ui.form.make_quick_entry(doctype, null, null, doc);
	});
};

frappe.ui.form.make_quick_entry = (doctype, after_insert, init_callback, doc) => {
	var trimmed_doctype = doctype.replace(/ /g, '');
	var controller_name = "QuickEntryForm";

	if(frappe.ui.form[trimmed_doctype + "QuickEntryForm"]){
		controller_name = trimmed_doctype + "QuickEntryForm";
	}

	frappe.quick_entry = new frappe.ui.form[controller_name](doctype, after_insert, init_callback, doc);
	return frappe.quick_entry.setup();
};

frappe.ui.form.QuickEntryForm = Class.extend({
	init: function(doctype, after_insert, init_callback, doc) {
		this.doctype = doctype;
		this.after_insert = after_insert;
		this.init_callback = init_callback;
		this.doc = doc;
	},

	setup: function() {
		let me = this;
		return new Promise(resolve => {
			frappe.model.with_doctype(this.doctype, function() {
				me.set_meta_and_mandatory_fields();
				if(me.is_quick_entry()) {
					me.render_dialog();
					resolve(me);
				} else {
					frappe.quick_entry = null;
					frappe.set_route('Form', me.doctype, me.doc.name)
						.then(() => resolve(me));
				}
			});
		});
	},

	set_meta_and_mandatory_fields: function(){
		let fields = frappe.get_meta(this.doctype).fields;
		if (fields.length < 7) {
			// if less than 7 fields, then show everything
			this.mandatory = fields;
		} else {
			// prepare a list of mandatory and bold fields
			this.mandatory = $.map(fields,
				function(d) { return ((d.reqd || d.bold || d.allow_in_quick_entry) && !d.read_only) ? $.extend({}, d) : null; });
		}
		this.meta = frappe.get_meta(this.doctype);
		if (!this.doc) {
			this.doc = frappe.model.get_new_doc(this.doctype, null, null, true);
		}
	},

	is_quick_entry: function(){
		if(this.meta.quick_entry != 1) {
			return false;
		}

		this.validate_for_prompt_autoname();

		if (this.has_child_table()
			|| !this.mandatory.length) {
			return false;
		}

		return true;
	},

	too_many_mandatory_fields: function(){
		if(this.mandatory.length > 7) {
			// too many fields, show form
			return true;
		}
		return false;
	},

	has_child_table: function(){
		if($.map(this.mandatory, function(d) {
			return d.fieldtype==='Table' ? d : null; }).length) {
			// has mandatory table, quit!
			return true;
		}
		return false;
	},

	validate_for_prompt_autoname: function(){
		if(this.meta.autoname && this.meta.autoname.toLowerCase()==='prompt') {
			this.mandatory = [{fieldname:'__newname', label:__('{0} Name', [this.meta.name]),
				reqd: 1, fieldtype:'Data'}].concat(this.mandatory);
		}
	},

	render_dialog: function(){
		var me = this;
		this.dialog = new frappe.ui.Dialog({
			title: __("New {0}", [__(this.doctype)]),
			fields: this.mandatory,
		});
		this.dialog.doc = this.doc;

		this.register_primary_action();
		this.render_edit_in_full_page_link();
		// ctrl+enter to save
		this.dialog.wrapper.keydown(function(e) {
			if((e.ctrlKey || e.metaKey) && e.which==13) {
				if(!frappe.request.ajax_count) {
					// not already working -- double entry
					me.dialog.get_primary_btn().trigger("click");
					e.preventDefault();
					return false;
				}
			}
		});

		this.dialog.onhide = () => frappe.quick_entry = null;
		this.dialog.show();

		this.dialog.refresh_dependency();
		this.set_defaults();

		if (this.init_callback) {
			this.init_callback(this.dialog);
		}
	},

	register_primary_action: function(){
		var me = this;
		this.dialog.set_primary_action(__('Save'), function() {
			if(me.dialog.working) {
				return;
			}
			var data = me.dialog.get_values();

			if(data) {
				me.dialog.working = true;
				me.dialog.set_message(__('Saving...'));
				me.insert().then(() => {
					me.dialog.clear_message();
				});
			}
		});
	},

	insert: function() {
		let me = this;
		return new Promise(resolve => {
			me.update_doc();
			frappe.call({
				method: "frappe.client.save",
				args: {
					doc: me.dialog.doc
				},
				callback: function(r) {

					if (frappe.model.is_submittable(me.doctype)) {
						frappe.run_serially([
							() => me.dialog.working = true,
							() => {
								me.dialog.set_primary_action(__('Submit'), function() {
									me.submit(r.message);
								});
							}
						]);
					} else {
						me.dialog.hide();
						// delete the old doc
						frappe.model.clear_doc(me.dialog.doc.doctype, me.dialog.doc.name);
						me.dialog.doc = r.message;
						if(frappe._from_link) {
							frappe.ui.form.update_calling_link(me.dialog.doc);
						} else {
							if(me.after_insert) {
								me.after_insert(me.dialog.doc);
							} else {
								me.open_form_if_not_list();
							}
						}
					}
				},
				error: function() {
					me.open_doc();
				},
				always: function() {
					me.dialog.working = false;
					resolve(me.dialog.doc);
				},
				freeze: true
			});
		});
	},

	submit: function(doc) {
		var me = this;
		frappe.call({
			method: "frappe.client.submit",
			args : {
				doc: doc
			},
			callback: function(r) {
				me.dialog.hide();
				// delete the old doc
				frappe.model.clear_doc(me.dialog.doc.doctype, me.dialog.doc.name);
				me.dialog.doc = r.message;
				if (frappe._from_link) {
					frappe.ui.form.update_calling_link(me.dialog.doc);
				}
				cur_frm.reload_doc();
			}
		});
	},

	open_form_if_not_list: function() {
		let route = frappe.get_route();
		let doc = this.dialog.doc;
		if (route && !(route[0]==='List' && route[1]===doc.doctype)) {
			frappe.run_serially([
				() => frappe.set_route('Form', doc.doctype, doc.name)
			]);
		}
	},

	update_doc: function(){
		var me = this;
		var data = this.dialog.get_values(true);
		$.each(data, function(key, value) {
			if(key==='__newname') {
				me.dialog.doc.name = value;
			}
			else {
				if(!is_null(value)) {
					me.dialog.doc[key] = value;
				}
			}
		});
		return this.dialog.doc;
	},

	open_doc: function(){
		this.dialog.hide();
		this.update_doc();
		frappe.set_route('Form', this.doctype, this.doc.name);
	},

	render_edit_in_full_page_link: function(){
		var me = this;
		var $link = $('
' +
			'<button class="edit-full btn-default btn-sm">' + __("Edit in full page") + '</button>
').appendTo(this.dialog.body);

		$link.find('.edit-full').on('click', function() {
			// edit in form
			me.open_doc();
		});
	},

	set_defaults: function(){
		var me = this;
		// set defaults
		$.each(this.dialog.fields_dict, function(fieldname, field) {
			field.doctype = me.doc.doctype;
			field.docname = me.doc.name;

			if(!is_null(me.doc[fieldname])) {
				field.set_input(me.doc[fieldname]);
			}
		});
	}
});
<commit_msg>feat: added flag to skip redirect on error for quick entry<commit_after>frappe.provide('frappe.ui.form');

frappe.quick_edit = function(doctype, name) {
	frappe.db.get_doc(doctype, name).then(doc => {
		frappe.ui.form.make_quick_entry(doctype, null, null, doc);
	});
};

frappe.ui.form.make_quick_entry = (doctype, after_insert, init_callback, doc) => {
	var trimmed_doctype = doctype.replace(/ /g, '');
	var controller_name = "QuickEntryForm";

	if(frappe.ui.form[trimmed_doctype + "QuickEntryForm"]){
		controller_name = trimmed_doctype + "QuickEntryForm";
	}

	frappe.quick_entry = new frappe.ui.form[controller_name](doctype, after_insert, init_callback, doc);
	return frappe.quick_entry.setup();
};

frappe.ui.form.QuickEntryForm = Class.extend({
	init: function(doctype, after_insert, init_callback, doc) {
		this.doctype = doctype;
		this.after_insert = after_insert;
		this.init_callback = init_callback;
		this.doc = doc;
	},

	setup: function() {
		let me = this;
		return new Promise(resolve => {
			frappe.model.with_doctype(this.doctype, function() {
				me.set_meta_and_mandatory_fields();
				if(me.is_quick_entry()) {
					me.render_dialog();
					resolve(me);
				} else {
					frappe.quick_entry = null;
					frappe.set_route('Form', me.doctype, me.doc.name)
						.then(() => resolve(me));
				}
			});
		});
	},

	set_meta_and_mandatory_fields: function(){
		let fields = frappe.get_meta(this.doctype).fields;
		if (fields.length < 7) {
			// if less than 7 fields, then show everything
			this.mandatory = fields;
		} else {
			// prepare a list of mandatory and bold fields
			this.mandatory = $.map(fields,
				function(d) { return ((d.reqd || d.bold || d.allow_in_quick_entry) && !d.read_only) ? $.extend({}, d) : null; });
		}
		this.meta = frappe.get_meta(this.doctype);
		if (!this.doc) {
			this.doc = frappe.model.get_new_doc(this.doctype, null, null, true);
		}
	},

	is_quick_entry: function(){
		if(this.meta.quick_entry != 1) {
			return false;
		}

		this.validate_for_prompt_autoname();

		if (this.has_child_table()
			|| !this.mandatory.length) {
			return false;
		}

		return true;
	},

	too_many_mandatory_fields: function(){
		if(this.mandatory.length > 7) {
			// too many fields, show form
			return true;
		}
		return false;
	},

	has_child_table: function(){
		if($.map(this.mandatory, function(d) {
			return d.fieldtype==='Table' ? d : null; }).length) {
			// has mandatory table, quit!
			return true;
		}
		return false;
	},

	validate_for_prompt_autoname: function(){
		if(this.meta.autoname && this.meta.autoname.toLowerCase()==='prompt') {
			this.mandatory = [{fieldname:'__newname', label:__('{0} Name', [this.meta.name]),
				reqd: 1, fieldtype:'Data'}].concat(this.mandatory);
		}
	},

	render_dialog: function(){
		var me = this;
		this.dialog = new frappe.ui.Dialog({
			title: __("New {0}", [__(this.doctype)]),
			fields: this.mandatory,
		});
		this.dialog.doc = this.doc;

		this.register_primary_action();
		this.render_edit_in_full_page_link();
		// ctrl+enter to save
		this.dialog.wrapper.keydown(function(e) {
			if((e.ctrlKey || e.metaKey) && e.which==13) {
				if(!frappe.request.ajax_count) {
					// not already working -- double entry
					me.dialog.get_primary_btn().trigger("click");
					e.preventDefault();
					return false;
				}
			}
		});

		this.dialog.onhide = () => frappe.quick_entry = null;
		this.dialog.show();

		this.dialog.refresh_dependency();
		this.set_defaults();

		if (this.init_callback) {
			this.init_callback(this.dialog);
		}
	},

	register_primary_action: function(){
		var me = this;
		this.dialog.set_primary_action(__('Save'), function() {
			if(me.dialog.working) {
				return;
			}
			var data = me.dialog.get_values();

			if(data) {
				me.dialog.working = true;
				me.dialog.set_message(__('Saving...'));
				me.insert().then(() => {
					me.dialog.clear_message();
				});
			}
		});
	},

	insert: function() {
		let me = this;
		return new Promise(resolve => {
			me.update_doc();
			frappe.call({
				method: "frappe.client.save",
				args: {
					doc: me.dialog.doc
				},
				callback: function(r) {

					if (frappe.model.is_submittable(me.doctype)) {
						frappe.run_serially([
							() => me.dialog.working = true,
							() => {
								me.dialog.set_primary_action(__('Submit'), function() {
									me.submit(r.message);
								});
							}
						]);
					} else {
						me.dialog.hide();
						// delete the old doc
						frappe.model.clear_doc(me.dialog.doc.doctype, me.dialog.doc.name);
						me.dialog.doc = r.message;
						if(frappe._from_link) {
							frappe.ui.form.update_calling_link(me.dialog.doc);
						} else {
							if(me.after_insert) {
								me.after_insert(me.dialog.doc);
							} else {
								me.open_form_if_not_list();
							}
						}
					}
				},
				error: function() {
					if (!me.skip_redirect_on_error) {
						me.open_doc();
					}
				},
				always: function() {
					me.dialog.working = false;
					resolve(me.dialog.doc);
				},
				freeze: true
			});
		});
	},

	submit: function(doc) {
		var me = this;
		frappe.call({
			method: "frappe.client.submit",
			args : {
				doc: doc
			},
			callback: function(r) {
				me.dialog.hide();
				// delete the old doc
				frappe.model.clear_doc(me.dialog.doc.doctype, me.dialog.doc.name);
				me.dialog.doc = r.message;
				if (frappe._from_link) {
					frappe.ui.form.update_calling_link(me.dialog.doc);
				}
				cur_frm.reload_doc();
			}
		});
	},

	open_form_if_not_list: function() {
		let route = frappe.get_route();
		let doc = this.dialog.doc;
		if (route && !(route[0]==='List' && route[1]===doc.doctype)) {
			frappe.run_serially([
				() => frappe.set_route('Form', doc.doctype, doc.name)
			]);
		}
	},

	update_doc: function(){
		var me = this;
		var data = this.dialog.get_values(true);
		$.each(data, function(key, value) {
			if(key==='__newname') {
				me.dialog.doc.name = value;
			}
			else {
				if(!is_null(value)) {
					me.dialog.doc[key] = value;
				}
			}
		});
		return this.dialog.doc;
	},

	open_doc: function(){
		this.dialog.hide();
		this.update_doc();
		frappe.set_route('Form', this.doctype, this.doc.name);
	},

	render_edit_in_full_page_link: function(){
		var me = this;
		var $link = $('
' +
			'<button class="edit-full btn-default btn-sm">' + __("Edit in full page") + '</button>
').appendTo(this.dialog.body);

		$link.find('.edit-full').on('click', function() {
			// edit in form
			me.open_doc();
		});
	},

	set_defaults: function(){
		var me = this;
		// set defaults
		$.each(this.dialog.fields_dict, function(fieldname, field) {
			field.doctype = me.doc.doctype;
			field.docname = me.doc.name;

			if(!is_null(me.doc[fieldname])) {
				field.set_input(me.doc[fieldname]);
			}
		});
	}
});
<|endoftext|>


<commit_before>/*global logger*/
/*
    DataviewLoader
    ========================

    @file      : DataviewLoader.js
    @version   : 1.3.1
    @author    : JvdGraaf
    @date      : Mon, 24 Apr 2017 15:02:42 GMT
    @copyright : Appronto
    @license   : Apache2

    Documentation
    ========================
    Describe your widget here.
*/

// Required module list. Remove unnecessary modules, you can always get them back from the boilerplate.

define([
  "dojo/_base/declare",
  "mxui/widget/_WidgetBase",
  "dijit/_TemplatedMixin",

  "mxui/dom",
  "dojo/dom",
  "dojo/dom-style",
  "dojo/dom-class",
  "dojo/_base/lang",
  "dojo/_base/event",

  "dojo/text!DataviewLoader/widget/template/DataviewLoader.html"
], function (declare, _WidgetBase, _TemplatedMixin, dom, dojoDom, dojoStyle, dojoClass, dojoLang, dojoEvent, widgetTemplate) {
    "use strict";

    // Declare widget's prototype.
    return declare("DataviewLoader.widget.DataviewLoader", [_WidgetBase, _TemplatedMixin], {
        // _TemplatedMixin will create our dom node using this HTML template.
        templateString: widgetTemplate,

        // DOM elements
        divContent: null,
        divLoader: null,

        // Internal variables. Non-primitives created in the prototype are shared between all widget instances.
        _handles: null,
        _contextObj: null,
        _loadingStarted: false,
        _pageInitiated: false,
        _form: null,
        active: true,
        prevForm: null,
        refreshHandler: null,
        // dojo.declare.constructor is called to construct the widget instance. Implement to initialize non-primitive properties.
        constructor: function () {
            //logger.debug(this.id + ".constructor");
            this._handles = [];
        },

        // dijit._WidgetBase.postCreate is called after constructing the widget. Implement to do extra setup work.
        postCreate: function () {
            logger.debug(this.id + ".postCreate");

            this._updateRendering();
            this._setupEvents();
        },

        // mxui.widget._WidgetBase.update is called when context is changed or initialized. Implement to re-render and / or fetch data.
        update: function (obj, callback) {
            console.log(this.id + ".update on new object");
            this._loadingStarted = false;
            this._pageInitiated = false;
            this._contextObj = obj;
            this._resetSubscriptions();
            this._updateRendering();
            if (callback) {
                callback();
            }
        },

        // mxui.widget._WidgetBase.enable is called when the widget should enable editing. Implement to enable editing if widget is input widget.
        enable: function () {
            logger.debug(this.id + ".enable");
        },

        // mxui.widget._WidgetBase.enable is called when the widget should disable editing. Implement to disable editing if widget is input widget.
        disable: function () {
            logger.debug(this.id + ".disable");
        },

        // mxui.widget._WidgetBase.resize is called when the page's layout is recalculated. Implement to do sizing calculations. Prefer using CSS instead.
        resize: function (box) {
            console.log(this.id + ".resize");
            // TODO: How to handle tabs and conditional visibility
            if (this.domNode.offsetParent !== null && this.visibilityCheck) {
                if (this.refreshAction == "Attribute" && this.refreshtAttr && this._contextObj) {
                    if (this._contextObj.get(this.refreshtAttr))
                        this._loadAndShowcontent();
                    else
                        console.log(this.id + ".resize Skip because " + this.refreshtAttr + " is false");
                } else {
                    this._loadAndShowcontent();
                }
            }
        },

        // mxui.widget._WidgetBase.uninitialize is called when the widget is destroyed. Implement to do special tear-down work.
        uninitialize: function () {
            logger.debug(this.id + ".uninitialize");
            // Clean up listeners, helper objects, etc. There is no need to remove listeners added with this.connect / this.subscribe / this.own.
            this.active = false;
            if (this.refreshHandler) {
                clearInterval(this.refreshHandler);
                this.refreshHandler = null;
            }
            if (this._form != null) {
                this._form.destroy();
            }
        },

        // We want to stop events on a mobile device
        _stopBubblingEventOnMobile: function (e) {
            logger.debug(this.id + "._stopBubblingEventOnMobile");
            if (typeof document.ontouchstart !== "undefined") {
                dojoEvent.stop(e);
            }
        },

        // Attach events to HTML dom elements
        _setupEvents: function () {
            // Set fading of content
            if (this.fadeContent) {
                dojoClass.add(this.divContent, "loaderfade");
            }

            // Set refreshing each time
            if (this.refreshTime > 0) {
                this.refreshHandler = setInterval(dojoLang.hitch(this, function () {
                    if (this._loadingStarted == false) {
                        if (this.pageMF) {
                            this._pageInitiated = false;
                        }
                        this._updateRendering();
                    } else {
                        console.log(this.id + ".Refresh skip because of loading started.");
                    }
                }), this.refreshTime * 1000);
            }
        },

        // Rerender the interface.
        _updateRendering: function () {
            logger.debug(this.id + "._updateRendering");

            try{
                if (this._contextObj) {
                    if (this.loadingText && this.divLoader.innerHTML.indexOf(this.loadingText) === -1) {
                        var text = "

" + this.loadingText + "

";
                        this.divLoader.innerHTML = text + this.divLoader.innerHTML;
                    }

                    dojoStyle.set(this.divContent, "display", "none");
                    dojoStyle.set(this.divLoader, "display", "block");

                    if (this.domNode.offsetParent !== null || !this.visibilityCheck) {
                        this._loadAndShowcontent();
                    }
                }
            } catch(error){
                console.log(this.id + "._updateRendering error occurred: " + JSON.stringify(error));
            }
        },

        _loadAndShowcontent: function () {
            logger.debug(this.id + "._loadAndShowcontent");
            if (this._loadingStarted == false) {
                this._loadingStarted = true;
                if (this._contextObj && this.loadingMF) {
                    this._execMf(this.loadingMF, this._contextObj.getGuid(), this._processMicroflowCallback, this._processMicroflowFailure);
                } else if (this._contextObj) {
                    this._setPage(this._contextObj);
                }
            }
        },

        _processMicroflowCallback: function (objs) {
            logger.debug(this.id + '._processMicroflowCallback');
            if (this.active) {
                if (this.asyncCall)
                    this._setPage(this._contextObj);
                else
                    this._setPage(objs[0]);
            } else {
                console.info(this.id + "._processMicroflowCallback Skip loading because widget is destroyed.");
            }
        },
        _processMicroflowFailure: function () {
            if (this.errorText) {
                if (this._pageInitiated)
                    this._form.close();
                this._pageInitiated = false;
                this.divContent.innerHTML = "

" + this.errorText + "

";

                this._showPage();
            }
        },

        _openFormByFormProp: function (pageContext) {
            var props = {
                location: "node",
                domNode: this.divContent,
                callback: dojoLang.hitch(this, this._showPage),
                error: function (error) {
                    console.log(error.description);
                }
            };

            if (pageContext)
                props.context = pageContext;
            this._form = mx.ui.openForm(this.pageContent, props);
        },

        _openFormByMF: function (pageObj, pageContext) {
            this._execMf(this.pageMF, pageObj.getGuid(), function (response) {
                this._form = mx.ui.openForm(response, {
                    context: pageContext,
                    location: "node",
                    domNode: this.divContent,
                    callback: dojoLang.hitch(this, this._showPage),
                    error: function (error) {
                        console.log(error.description);
                    }
                });
            }, function () {
                mx.ui.error("Error calling custom form microflow");
            });
        },

        _setPage: function (pageObj) {
            logger.debug(this.id + '._setPage');

            if (this._pageInitiated) {
                if (this._loadingStarted) {
                    this._showPage();
                } else {
                    console.log(this.id + "_setPage skip because already set.");
                }
            } else {
                this._pageInitiated = true;
                this.divContent.innerHTML = "";
                console.log(this.id + "_setPage");
                if (pageObj) {
                    var pageContext = new mendix.lib.MxContext();
                    pageContext.setTrackObject(pageObj);

                    if (this.pageMF != null && this.pageMF != '')
                        this._openFormByMF(pageObj, pageContext);
                    else
                        this._openFormByFormProp(pageContext);

                } else {
                    if (this.pageMF != null && this.pageMF != '')
                    mx.ui.error("Page microflow is not supported without context");
                    else
                        this._openFormByFormProp();

                }
            }
        },

        _showPage: function (mxform) {
            logger.debug(this.id + "._showPage on form");

            if (this.prevForm != null && mxform != null) {
                this.prevForm.destroy();
            }
            this.prevForm = mxform;

            dojoStyle.set(this.divContent, "display", "block");
            dojoStyle.set(this.divLoader, "display", "none");

            this._loadingStarted = false;
        },

        // Reset subscriptions.
        _resetSubscriptions: function () {
            logger.debug(this.id + "._resetSubscriptions");
            // Release handles on previous object, if any.
            this.unsubscribeAll();

            // When a mendix object exists create subscribtions.
            if (this._contextObj && (this.refreshAction === "Object" || this.refreshAction === "Attribute")) {
                console.log(this.id + "._resetSubscriptions setup refresh handler: " + this.refreshAction);
                this.subscribe({
                    guid: this._contextObj.getGuid(),
                    callback: dojoLang.hitch(this, function (guid) {
                        if (this._loadingStarted == false) {
                            if (this.pageMF) {
                                this._pageInitiated = false;
                            }
                            if (this.refreshAction == "Attribute" && this.refreshtAttr && this._contextObj.get(this.refreshtAttr)) {
                                console.log(this.id + ".Refresh triggered on attribute change.");
                                this._updateRendering();
                            } else if (this.refreshAction == "Object") {
                                console.log(this.id + ".Refresh triggered on object change.");
                                this._updateRendering();
                            }
                        } else {
                            console.log(this.id + ".Refresh skip because of loading started.");
                        }
                    })
                });
            }
        },

        _execMf: function (mf, guid, cb, cbfailure) {
            logger.debug(this.id + "._execMf" + (mf ? ": " + mf : ""));
            if (mf && guid) {
                mx.data.action({
                    params: {
                        applyto: "selection",
                        actionname: mf,
                        guids: [guid]
                    },
                    async: this.asyncCall,
                    origin: this.mxform,
                    callback: (cb && typeof cb === "function" ? dojoLang.hitch(this, cb) : null),
                    error: (cbfailure && typeof cbfailure === "function" ? dojoLang.hitch(this, cbfailure) : null)
                });
            }
        }
    });
});

require(["DataviewLoader/widget/DataviewLoader"]);<commit_msg>Removed superfluous code and comments<commit_after>/*global logger*/
/*
    DataviewLoader
    ========================

    @file      : DataviewLoader.js
    @version   : 1.3.1
    @author    : JvdGraaf
    @date      : Mon, 24 Apr 2017 15:02:42 GMT
    @copyright : Appronto
    @license   : Apache2

*/
define([
  "dojo/_base/declare",
  "mxui/widget/_WidgetBase",
  "dijit/_TemplatedMixin",
  "dojo/dom-style",
  "dojo/dom-class",
  "dojo/_base/lang",
  "dojo/_base/event",

  "dojo/text!DataviewLoader/widget/template/DataviewLoader.html"
], function (declare, _WidgetBase, _TemplatedMixin, dojoStyle, dojoClass, dojoLang, dojoEvent, widgetTemplate) {
    "use strict";

    return declare("DataviewLoader.widget.DataviewLoader", [_WidgetBase, _TemplatedMixin], {
        templateString: widgetTemplate,

        // DOM elements
        divContent: null,
        divLoader: null,

        // Internal variables. Non-primitives created in the prototype are shared between all widget instances.
        _handles: null,
        _contextObj: null,
        _loadingStarted: false,
        _pageInitiated: false,
        _form: null,
        active: true,
        prevForm: null,
        refreshHandler: null,

        postCreate: function () {
            logger.debug(this.id + ".postCreate");

            this._updateRendering();
            this._setupEvents();
        },
        update: function (obj, callback) {
            console.log(this.id + ".update on new object");
            this._loadingStarted = false;
            this._pageInitiated = false;
            this._contextObj = obj;
            this._resetSubscriptions();
            this._updateRendering();
            if (callback) {
                callback();
            }
        },

        resize: function (box) {
            console.log(this.id + ".resize");
            // TODO: How to handle tabs and conditional visibility
            if (this.domNode.offsetParent !== null && this.visibilityCheck) {
                if (this.refreshAction == "Attribute" && this.refreshtAttr && this._contextObj) {
                    if (this._contextObj.get(this.refreshtAttr))
                        this._loadAndShowcontent();
                    else
                        console.log(this.id + ".resize Skip because " + this.refreshtAttr + " is false");
                } else {
                    this._loadAndShowcontent();
                }
            }
        },

        uninitialize: function () {
            logger.debug(this.id + ".uninitialize");
            this.active = false;
            if (this.refreshHandler) {
                clearInterval(this.refreshHandler);
                this.refreshHandler = null;
            }
            if (this._form != null) {
                this._form.destroy();
            }
        },

        _setupEvents: function () {
            if (this.fadeContent) {
                dojoClass.add(this.divContent, "loaderfade");
            }

            if (this.refreshTime > 0) {
                this.refreshHandler = setInterval(dojoLang.hitch(this, function () {
                    if (this._loadingStarted == false) {
                        if (this.pageMF) {
                            this._pageInitiated = false;
                        }
                        this._updateRendering();
                    } else {
                        console.log(this.id + ".Refresh skip because of loading started.");
                    }
                }), this.refreshTime * 1000);
            }
        },

        _updateRendering: function () {
            logger.debug(this.id + "._updateRendering");

            try{
                if (this._contextObj) {
                    if (this.loadingText && this.divLoader.innerHTML.indexOf(this.loadingText) === -1) {
                        var text = "

" + this.loadingText + "

";
                        this.divLoader.innerHTML = text + this.divLoader.innerHTML;
                    }

                    dojoStyle.set(this.divContent, "display", "none");
                    dojoStyle.set(this.divLoader, "display", "block");

                    if (this.domNode.offsetParent !== null || !this.visibilityCheck) {
                        this._loadAndShowcontent();
                    }
                }
            } catch(error){
                console.log(this.id + "._updateRendering error occurred: " + JSON.stringify(error));
            }
        },

        _loadAndShowcontent: function () {
            logger.debug(this.id + "._loadAndShowcontent");
            if (this._loadingStarted == false) {
                this._loadingStarted = true;
                if (this._contextObj && this.loadingMF) {
                    this._execMf(this.loadingMF, this._contextObj.getGuid(), this._processMicroflowCallback, this._processMicroflowFailure);
                } else if (this._contextObj) {
                    this._setPage(this._contextObj);
                }
            }
        },

        _processMicroflowCallback: function (objs) {
            logger.debug(this.id + '._processMicroflowCallback');
            if (this.active) {
                if (this.asyncCall)
                    this._setPage(this._contextObj);
                else
                    this._setPage(objs[0]);
            } else {
                console.info(this.id + "._processMicroflowCallback Skip loading because widget is destroyed.");
            }
        },
        _processMicroflowFailure: function () {
            if (this.errorText) {
                if (this._pageInitiated)
                    this._form.close();
                this._pageInitiated = false;
                this.divContent.innerHTML = "

" + this.errorText + "

";

                this._showPage();
            }
        },

        _openFormByFormProp: function (pageContext) {
            var props = {
                location: "node",
                domNode: this.divContent,
                callback: dojoLang.hitch(this, this._showPage),
                error: function (error) {
                    console.log(error.description);
                }
            };

            if (pageContext)
                props.context = pageContext;
            this._form = mx.ui.openForm(this.pageContent, props);
        },

        _openFormByMF: function (pageObj, pageContext) {
            this._execMf(this.pageMF, pageObj.getGuid(), function (response) {
                this._form = mx.ui.openForm(response, {
                    context: pageContext,
                    location: "node",
                    domNode: this.divContent,
                    callback: dojoLang.hitch(this, this._showPage),
                    error: function (error) {
                        console.log(error.description);
                    }
                });
            }, function () {
                mx.ui.error("Error calling custom form microflow");
            });
        },

        _setPage: function (pageObj) {
            logger.debug(this.id + '._setPage');

            if (this._pageInitiated) {
                if (this._loadingStarted) {
                    this._showPage();
                } else {
                    console.log(this.id + "_setPage skip because already set.");
                }
            } else {
                this._pageInitiated = true;
                this.divContent.innerHTML = "";
                console.log(this.id + "_setPage");
                if (pageObj) {
                    var pageContext = new mendix.lib.MxContext();
                    pageContext.setTrackObject(pageObj);

                    if (this.pageMF != null && this.pageMF != '')
                        this._openFormByMF(pageObj, pageContext);
                    else
                        this._openFormByFormProp(pageContext);

                } else {
                    if (this.pageMF != null && this.pageMF != '')
                    mx.ui.error("Page microflow is not supported without context");
                    else
                        this._openFormByFormProp();

                }
            }
        },

        _showPage: function (mxform) {
            logger.debug(this.id + "._showPage on form");

            if (this.prevForm != null && mxform != null) {
                this.prevForm.destroy();
            }
            this.prevForm = mxform;

            dojoStyle.set(this.divContent, "display", "block");
            dojoStyle.set(this.divLoader, "display", "none");

            this._loadingStarted = false;
        },

        _resetSubscriptions: function () {
            logger.debug(this.id + "._resetSubscriptions");
            this.unsubscribeAll();

            if (this._contextObj && (this.refreshAction === "Object" || this.refreshAction === "Attribute")) {
                console.log(this.id + "._resetSubscriptions setup refresh handler: " + this.refreshAction);
                this.subscribe({
                    guid: this._contextObj.getGuid(),
                    callback: dojoLang.hitch(this, function (guid) {
                        if (this._loadingStarted == false) {
                            if (this.pageMF) {
                                this._pageInitiated = false;
                            }
                            if (this.refreshAction == "Attribute" && this.refreshtAttr && this._contextObj.get(this.refreshtAttr)) {
                                console.log(this.id + ".Refresh triggered on attribute change.");
                                this._updateRendering();
                            } else if (this.refreshAction == "Object") {
                                console.log(this.id + ".Refresh triggered on object change.");
                                this._updateRendering();
                            }
                        } else {
                            console.log(this.id + ".Refresh skip because of loading started.");
                        }
                    })
                });
            }
        },

        _execMf: function (mf, guid, cb, cbfailure) {
            logger.debug(this.id + "._execMf" + (mf ? ": " + mf : ""));
            if (mf && guid) {
                mx.data.action({
                    params: {
                        applyto: "selection",
                        actionname: mf,
                        guids: [guid]
                    },
                    async: this.asyncCall,
                    origin: this.mxform,
                    callback: (cb && typeof cb === "function" ? dojoLang.hitch(this, cb) : null),
                    error: (cbfailure && typeof cbfailure === "function" ? dojoLang.hitch(this, cbfailure) : null)
                });
            }
        }
    });
});

require(["DataviewLoader/widget/DataviewLoader"]);
<|endoftext|>


<commit_before>/*jshint node: true */
'use strict';

/*
* Name :  MapChartViewSpec.js
* Module : UnitTest
* Location : /frontend/test/unit/View
*
* History :
* Version       Date        Programmer                  Description
* =================================================================================================
* 0.1.0         2015-06-13  Maria Giovanna Chinellato   Add all attributes and all methods
*
* 0.0.1         2015-06-13  Maria Giovanna Chinellato   Initial code
* =================================================================================================
*
*/

describe('MapChartView', function(){

	beforeEach(angular.mock.module('norris-nrti'));

	var html, element, scope, MapChartFactory;

	beforeEach(inject(function($rootScope, $compile, $injector) {
		MapChartFactory = $injector.get('MapChartFactory');
		var map = MapChartFactory.build();
    	scope = $rootScope.$new();
    	html = angular.element('<map-chart url="http://example/map.com"></map-chart>');

    	/*scope.graphs = [
    		{ 'id' : 1, 'type' : 'MapChart', 'url' : 'http://example/map.com'},
    		{ 'id' : 2, 'type' : 'LineChart', 'url' : 'http://example/line.com'},
    		{ 'id' : 3, 'type' : 'BarChart', 'url' : 'http://example/bar.com'},
    		{ 'id' : 4, 'type' : 'Table', 'url' : 'http://example/table.com'}
    	];*/

    	scope.mapChart = map;

    	element = $compile(html)(scope);
    	scope.$digest();

  	}));

  	describe('template', function() {
		it('works fine', function() {
			var map = element.find('map-chart');
			expect(map).toBeDefined();
		});
	});

});<commit_msg>some fixes<commit_after>/*jshint node: true */
'use strict';

/*
* Name :  MapChartViewSpec.js
* Module : UnitTest
* Location : /frontend/test/unit/View
*
* History :
* Version       Date        Programmer                  Description
* =================================================================================================
* 0.1.0         2015-06-13  Maria Giovanna Chinellato   Add all attributes and all methods
*
* 0.0.1         2015-06-13  Maria Giovanna Chinellato   Initial code
* =================================================================================================
*
*/

describe('MapChartView', function(){

	beforeEach(angular.mock.module('norris-nrti'));

	var html, element, scope, map, MapChartFactory;

	beforeEach(inject(function($rootScope, $compile, $injector) {
		MapChartFactory = $injector.get('MapChartFactory');
		map = MapChartFactory.build();
    	scope = $rootScope.$new();
    	html = angular.element('<map-chart url="http://example/map.com"></map-chart>');

    	/*scope.graphs = [
    		{ 'id' : 1, 'type' : 'MapChart', 'url' : 'http://example/map.com'},
    		{ 'id' : 2, 'type' : 'LineChart', 'url' : 'http://example/line.com'},
    		{ 'id' : 3, 'type' : 'BarChart', 'url' : 'http://example/bar.com'},
    		{ 'id' : 4, 'type' : 'Table', 'url' : 'http://example/table.com'}
    	];*/

    	scope.mapChart = map;

    	element = $compile(html)(scope);
    	scope.$digest();

  	}));

  	describe('template', function() {
		it('works fine', function() {
			var mapChart = element.find('map-chart');
			expect(mapChart).toBeDefined();
			var div = element.find('div');
			expect(div).toBeDefined();
			expect(div.text()).toEqual(map.getTitle());
		});
	});

});<|endoftext|>