<commit_before>export { init, testResultSame, testDifferentDisposeStrategy, disposeTensors };

import * as ChannelShuffler from "../Conv/ChannelShuffler.js";
import * as TensorTools from "../util/TensorTools.js";

/**
 * Test different channel shuffle implementation for CNN ShuffleNet.
 *
 * @see {@link https://www.measurethat.net/Benchmarks/Show/10945/16/colorfulcakechen-cnn-channel-shuffler-6cb696633cf508477}
 * @see {@link https://www.measurethat.net/Benchmarks/Show/10973/0/colorfulcakechen-cnn-channel-shuffler-concatpointwiseco}
 */

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

  /**
   * @param {number} height      image height
   * @param {number} width       image width
   * @param {number} depth       image channel count
   * @param {number} groupCount  Split the data into how many groups. ( depth / groupCount ) should be an integer.
   */
  constructor( height, width, depth, groupCount ) {

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

    this.valueCount = height * width * depth;

    this.concatenatedShape = [ height, width, depth ];

    this.dataTensor3dArray = tf.tidy( () => {
      let dataTensor1d = tf.linspace( 0, this.valueCount - 1, this.valueCount );

      let dataTensor3d = dataTensor1d.reshape( [ height, width, depth ] );
      return dataTensor3d.split( groupCount, dataTensor3d.rank - 1 );  // Along the last axis.
    });

    this.shuffleInfo = new ChannelShuffler.ShuffleInfo( this.concatenatedShape, groupCount );
    ( this.concatGatherUnsorted = new ChannelShuffler.ConcatGather() ).init( this.concatenatedShape, groupCount );
    ( this.splitConcatSortedShared = new ChannelShuffler.SplitConcat() ).init( this.concatenatedShape, groupCount );
    ( this.concatPointwiseConv = new ChannelShuffler.ConcatPointwiseConv() ).init( this.concatenatedShape, groupCount );
  }

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

    if ( this.concatGatherUnsorted )
      this.concatGatherUnsorted.disposeTensors();

    if ( this.concatPointwiseConv )
      this.concatPointwiseConv.disposeTensors();
  }

  // Test concat-reshape-transpose-reshape-split
  test_ConcatReshapeTransposeReshapeSplit() {
    let shuffledArray = this.shuffleInfo.concatReshapeTransposeReshapeSplit( this.dataTensor3dArray );
    tf.dispose( shuffledArray );
  }

  // Test concat-gather (Unsorted)
  test_ConcatGatherUnsorted() {
    let shuffledArray = this.concatGatherUnsorted.concatGather( this.dataTensor3dArray );
    tf.dispose( shuffledArray );
  }

  // Test split-concat (Sorted Shared)
  test_SplitConcatSortedShared() {
    let shuffledArray = this.splitConcatSortedShared.splitConcat( this.dataTensor3dArray );
    tf.dispose( shuffledArray );
  }

  // Test concat-pointwise-convolution
  test_ConcatPointwiseConv() {
    let shuffledArray = this.concatPointwiseConv.concatGather( this.dataTensor3dArray );
    tf.dispose( shuffledArray );
  }

  test_ConcatPointwiseConv_dispose_direct_call_for_dispose()  { tf.dispose( this.concatPointwiseConv.concatGather_dispose_direct_call_for_dispose( this.dataTensor3dArray ) ); }
  test_ConcatPointwiseConv_dispose_finally_call_for_dispose() { tf.dispose( this.concatPointwiseConv.concatGather_dispose_finally_call_for_dispose( this.dataTensor3dArray ) ); }

  test_ConcatPointwiseConv_dispose_direct_call_map_dispose()  { tf.dispose( this.concatPointwiseConv.concatGather_dispose_direct_call_map_dispose( this.dataTensor3dArray ) ); }
  test_ConcatPointwiseConv_dispose_finally_call_map_dispose() { tf.dispose( this.concatPointwiseConv.concatGather_dispose_finally_call_map_dispose( this.dataTensor3dArray ) ); }

  test_ConcatPointwiseConv_dispose_direct_call_map_tidy()  { tf.dispose( this.concatPointwiseConv.concatGather_dispose_direct_call_map_tidy( this.dataTensor3dArray ) ); }
  test_ConcatPointwiseConv_dispose_finally_call_map_tidy() { tf.dispose( this.concatPointwiseConv.concatGather_dispose_finally_call_map_tidy( this.dataTensor3dArray ) ); }

  test_ConcatPointwiseConv_dispose_direct_for() { tf.dispose( this.concatPointwiseConv.concatGather_dispose_direct_for( this.dataTensor3dArray ) ); }
  test_ConcatPointwiseConv_dispose_finally_for() { tf.dispose( this.concatPointwiseConv.concatGather_dispose_finally_for( this.dataTensor3dArray ) ); }

  test_ConcatPointwiseConv_dispose_direct_map() { tf.dispose( this.concatPointwiseConv.concatGather_dispose_direct_map( this.dataTensor3dArray ) ); }
  test_ConcatPointwiseConv_dispose_finally_map() { tf.dispose( this.concatPointwiseConv.concatGather_dispose_finally_map( this.dataTensor3dArray ) ); }

  test_ConcatPointwiseConv_tidy_map() { tf.dispose( this.concatPointwiseConv.concatGather_tidy_map( this.dataTensor3dArray ) ); }

  // Testing whether the results of different implementation are the same.
  testResultSame() {
    tf.tidy( () => {
      let memoryInfo0 = tf.memory();

      let t1Array = this.shuffleInfo.concatReshapeTransposeReshapeSplit( this.dataTensor3dArray );
      let memoryInfo1 = tf.memory();
      tf.util.assert( memoryInfo1.numTensors == ( memoryInfo0.numTensors + t1Array.length ), `ConcatReshapeTransposeReshapeSplit() memory leak`);

      let t2Array = this.concatGatherUnsorted.concatGather( this.dataTensor3dArray );
      let memoryInfo2 = tf.memory();
      tf.util.assert( memoryInfo2.numTensors == ( memoryInfo1.numTensors + t2Array.length ), `ConcatGatherUnsorted() memory leak`);

      let t3Array = this.splitConcatSortedShared.splitConcat( this.dataTensor3dArray );
      let memoryInfo3 = tf.memory();
      tf.util.assert( memoryInfo3.numTensors == ( memoryInfo2.numTensors + t3Array.length ), `SplitConcatSortedShared() memory leak`);

      let t4Array = this.concatPointwiseConv.concatGather( this.dataTensor3dArray );
      let memoryInfo4 = tf.memory();
      tf.util.assert( memoryInfo4.numTensors == ( memoryInfo3.numTensors + t4Array.length ), `PointwiseConv() memory leak`);

      tf.util.assert(
        TensorTools.Comparator.isTensorArrayEqual( t1Array, t2Array ),
        `ConcatReshapeTransposeReshapeSplit() != ConcatGatherUnsorted()`);

//!!! Sorted never equal to Unsorted.
//       tf.util.assert(
//         ChannelShuffler.Layer.isTensorArrayEqual( t2Array, t3Array ),
//         `ConcatGatherUnsorted() != SplitConcatSortedShared()`);

      // Because the sorted will never equal to unsorted, try to compare their sum.
      // (Using average may be have some floating-point error.)
      tf.tidy( () => {
//         let t2MeanArray = t2Array.map( t => t.mean() );
//         let t3MeanArray = t3Array.map( t => t.mean() );
        let lastAxisId = t2Array[ 0 ].rank - 1;
        let t2SumArray = t2Array.map( t => t.sum( lastAxisId ) );
        let t3SumArray = t3Array.map( t => t.sum( lastAxisId ) );

        tf.util.assert(
          TensorTools.Comparator.isTensorArrayEqual( t2SumArray, t3SumArray ),
          `ConcatGatherUnsorted() != SplitConcatSortedShared()`);
      });

      tf.util.assert(
        TensorTools.Comparator.isTensorArrayEqual( t2Array, t4Array ),
        `ConcatGatherUnsorted() != PointwiseConv()`);
    });
  }

  testDifferentDisposeStrategy() {
    let functionTable = [
      this.concatPointwiseConv.concatGather_dispose_direct_call_for_dispose,
      this.concatPointwiseConv.concatGather_dispose_finally_call_for_dispose,

      this.concatPointwiseConv.concatGather_dispose_direct_call_map_dispose,
      this.concatPointwiseConv.concatGather_dispose_finally_call_map_dispose,

      this.concatPointwiseConv.concatGather_dispose_direct_call_map_tidy,
      this.concatPointwiseConv.concatGather_dispose_finally_call_map_tidy,

      this.concatPointwiseConv.concatGather_dispose_direct_for,
      this.concatPointwiseConv.concatGather_dispose_finally_for,

      this.concatPointwiseConv.concatGather_dispose_direct_map,
      this.concatPointwiseConv.concatGather_dispose_finally_map,

      this.concatPointwiseConv.concatGather_tidy_map,
    ];

    tf.tidy( () => {
      let memoryInfoPrev = tf.memory();

      let funcPrev;
      let tArrayPrev;

      for ( let i = 0; i < functionTable.length; ++i ) {
        let func = functionTable[ i ];
        let tArray = func( this.dataTensor3dArray );

        let memoryInfo = tf.memory();
        tf.util.assert( memoryInfo.numTensors == ( memoryInfoPrev.numTensors + tArray.length ), `${func.name}() memory leak`);

        if ( tArrayPrev ) {
          tf.util.assert(
            TensorTools.Comparator.isTensorArrayEqual( tArrayPrev, tArray ),
            `${funcPrev.name}() != ${func.name}()`);
        }

        memoryInfoPrev = memoryInfo;
        funcPrev = func;
        tArrayPrev = tArray;
      }
    });
}

function init() {
  globalThis.testSet_110x110x24_g8 = new HeightWidthDepthGroup( 110, 110, 24, 8 ); // height, width, depth, groupCount
  globalThis.testSet_110x110x24_g4 = new HeightWidthDepthGroup( 110, 110, 24, 4 );
  globalThis.testSet_110x110x24_g3 = new HeightWidthDepthGroup( 110, 110, 24, 3 );
  globalThis.testSet_110x110x24_g2 = new HeightWidthDepthGroup( 110, 110, 24, 2 );
  globalThis.testSet_110x110x24_g1 = new HeightWidthDepthGroup( 110, 110, 24, 1 );
}

function testResultSame() {
  globalThis.testSet_110x110x24_g8.testResultSame();
  globalThis.testSet_110x110x24_g4.testResultSame();
  globalThis.testSet_110x110x24_g3.testResultSame();
  globalThis.testSet_110x110x24_g2.testResultSame();
  globalThis.testSet_110x110x24_g1.testResultSame();
}

function testDifferentDisposeStrategy() {
  globalThis.testSet_110x110x24_g8.testDifferentDisposeStrategy();
  globalThis.testSet_110x110x24_g4.testDifferentDisposeStrategy();
  globalThis.testSet_110x110x24_g3.testDifferentDisposeStrategy();
  globalThis.testSet_110x110x24_g2.testDifferentDisposeStrategy();
  globalThis.testSet_110x110x24_g1.testDifferentDisposeStrategy();
}

function disposeTensors() {
  if ( globalThis.testSet_110x110x24_g8 ) {
    globalThis.testSet_110x110x24_g8.disposeTensors();
    globalThis.testSet_110x110x24_g8 = null;
  }

  if ( globalThis.testSet_110x110x24_g4 ) {
    globalThis.testSet_110x110x24_g4.disposeTensors();
    globalThis.testSet_110x110x24_g4 = null;
  }

  if ( globalThis.testSet_110x110x24_g3 ) {
    globalThis.testSet_110x110x24_g3.disposeTensors();
    globalThis.testSet_110x110x24_g3 = null;
  }

  if ( globalThis.testSet_110x110x24_g2 ) {
    globalThis.testSet_110x110x24_g2.disposeTensors();
    globalThis.testSet_110x110x24_g2 = null;
  }

  if ( globalThis.testSet_110x110x24_g1 ) {
    globalThis.testSet_110x110x24_g1.disposeTensors();
    globalThis.testSet_110x110x24_g1 = null;
  }
}

//tf.ready().then( init );
<commit_msg>Update jsPerf_CNNChannelShuffle.js<commit_after>export { init, testResultSame, testDifferentDisposeStrategy, disposeTensors };

import * as ChannelShuffler from "../Conv/ChannelShuffler.js";
import * as TensorTools from "../util/TensorTools.js";

/**
 * Test different channel shuffle implementation for CNN ShuffleNet.
 *
 * @see {@link https://www.measurethat.net/Benchmarks/Show/10945/16/colorfulcakechen-cnn-channel-shuffler-6cb696633cf508477}
 * @see {@link https://www.measurethat.net/Benchmarks/Show/10973/0/colorfulcakechen-cnn-channel-shuffler-concatpointwiseco}
 */

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

  /**
   * @param {number} height      image height
   * @param {number} width       image width
   * @param {number} depth       image channel count
   * @param {number} groupCount  Split the data into how many groups. ( depth / groupCount ) should be an integer.
   */
  constructor( height, width, depth, groupCount ) {

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

    this.valueCount = height * width * depth;

    this.concatenatedShape = [ height, width, depth ];

    this.dataTensor3dArray = tf.tidy( () => {
      let dataTensor1d = tf.linspace( 0, this.valueCount - 1, this.valueCount );

      let dataTensor3d = dataTensor1d.reshape( [ height, width, depth ] );
      return dataTensor3d.split( groupCount, dataTensor3d.rank - 1 );  // Along the last axis.
    });

    this.shuffleInfo = new ChannelShuffler.ShuffleInfo( this.concatenatedShape, groupCount );
    ( this.concatGatherUnsorted = new ChannelShuffler.ConcatGather() ).init( this.concatenatedShape, groupCount );
    ( this.splitConcatSortedShared = new ChannelShuffler.SplitConcat() ).init( this.concatenatedShape, groupCount );
    ( this.concatPointwiseConv = new ChannelShuffler.ConcatPointwiseConv() ).init( this.concatenatedShape, groupCount );
  }

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

    if ( this.concatGatherUnsorted )
      this.concatGatherUnsorted.disposeTensors();

    if ( this.concatPointwiseConv )
      this.concatPointwiseConv.disposeTensors();
  }

  // Test concat-reshape-transpose-reshape-split
  test_ConcatReshapeTransposeReshapeSplit() {
    let shuffledArray = this.shuffleInfo.concatReshapeTransposeReshapeSplit( this.dataTensor3dArray );
    tf.dispose( shuffledArray );
  }

  // Test concat-gather (Unsorted)
  test_ConcatGatherUnsorted() {
    let shuffledArray = this.concatGatherUnsorted.concatGather( this.dataTensor3dArray );
    tf.dispose( shuffledArray );
  }

  // Test split-concat (Sorted Shared)
  test_SplitConcatSortedShared() {
    let shuffledArray = this.splitConcatSortedShared.splitConcat( this.dataTensor3dArray );
    tf.dispose( shuffledArray );
  }

  // Test concat-pointwise-convolution
  test_ConcatPointwiseConv() {
    let shuffledArray = this.concatPointwiseConv.concatGather( this.dataTensor3dArray );
    tf.dispose( shuffledArray );
  }

  test_ConcatPointwiseConv_dispose_direct_call_for_dispose()  { tf.dispose( this.concatPointwiseConv.concatGather_dispose_direct_call_for_dispose( this.dataTensor3dArray ) ); }
  test_ConcatPointwiseConv_dispose_finally_call_for_dispose() { tf.dispose( this.concatPointwiseConv.concatGather_dispose_finally_call_for_dispose( this.dataTensor3dArray ) ); }

  test_ConcatPointwiseConv_dispose_direct_call_map_dispose()  { tf.dispose( this.concatPointwiseConv.concatGather_dispose_direct_call_map_dispose( this.dataTensor3dArray ) ); }
  test_ConcatPointwiseConv_dispose_finally_call_map_dispose() { tf.dispose( this.concatPointwiseConv.concatGather_dispose_finally_call_map_dispose( this.dataTensor3dArray ) ); }

  test_ConcatPointwiseConv_dispose_direct_call_map_tidy()  { tf.dispose( this.concatPointwiseConv.concatGather_dispose_direct_call_map_tidy( this.dataTensor3dArray ) ); }
  test_ConcatPointwiseConv_dispose_finally_call_map_tidy() { tf.dispose( this.concatPointwiseConv.concatGather_dispose_finally_call_map_tidy( this.dataTensor3dArray ) ); }

  test_ConcatPointwiseConv_dispose_direct_for() { tf.dispose( this.concatPointwiseConv.concatGather_dispose_direct_for( this.dataTensor3dArray ) ); }
  test_ConcatPointwiseConv_dispose_finally_for() { tf.dispose( this.concatPointwiseConv.concatGather_dispose_finally_for( this.dataTensor3dArray ) ); }

  test_ConcatPointwiseConv_dispose_direct_map() { tf.dispose( this.concatPointwiseConv.concatGather_dispose_direct_map( this.dataTensor3dArray ) ); }
  test_ConcatPointwiseConv_dispose_finally_map() { tf.dispose( this.concatPointwiseConv.concatGather_dispose_finally_map( this.dataTensor3dArray ) ); }

  test_ConcatPointwiseConv_tidy_map() { tf.dispose( this.concatPointwiseConv.concatGather_tidy_map( this.dataTensor3dArray ) ); }

  // Testing whether the results of different implementation are the same.
  testResultSame() {
    tf.tidy( () => {
      let memoryInfo0 = tf.memory();

      let t1Array = this.shuffleInfo.concatReshapeTransposeReshapeSplit( this.dataTensor3dArray );
      let memoryInfo1 = tf.memory();
      tf.util.assert( memoryInfo1.numTensors == ( memoryInfo0.numTensors + t1Array.length ), `ConcatReshapeTransposeReshapeSplit() memory leak`);

      let t2Array = this.concatGatherUnsorted.concatGather( this.dataTensor3dArray );
      let memoryInfo2 = tf.memory();
      tf.util.assert( memoryInfo2.numTensors == ( memoryInfo1.numTensors + t2Array.length ), `ConcatGatherUnsorted() memory leak`);

      let t3Array = this.splitConcatSortedShared.splitConcat( this.dataTensor3dArray );
      let memoryInfo3 = tf.memory();
      tf.util.assert( memoryInfo3.numTensors == ( memoryInfo2.numTensors + t3Array.length ), `SplitConcatSortedShared() memory leak`);

      let t4Array = this.concatPointwiseConv.concatGather( this.dataTensor3dArray );
      let memoryInfo4 = tf.memory();
      tf.util.assert( memoryInfo4.numTensors == ( memoryInfo3.numTensors + t4Array.length ), `PointwiseConv() memory leak`);

      tf.util.assert(
        TensorTools.Comparator.isTensorArrayEqual( t1Array, t2Array ),
        `ConcatReshapeTransposeReshapeSplit() != ConcatGatherUnsorted()`);

//!!! Sorted never equal to Unsorted.
//       tf.util.assert(
//         ChannelShuffler.Layer.isTensorArrayEqual( t2Array, t3Array ),
//         `ConcatGatherUnsorted() != SplitConcatSortedShared()`);

      // Because the sorted will never equal to unsorted, try to compare their sum.
      // (Using average may be have some floating-point error.)
      tf.tidy( () => {
//         let t2MeanArray = t2Array.map( t => t.mean() );
//         let t3MeanArray = t3Array.map( t => t.mean() );
        let lastAxisId = t2Array[ 0 ].rank - 1;
        let t2SumArray = t2Array.map( t => t.sum( lastAxisId ) );
        let t3SumArray = t3Array.map( t => t.sum( lastAxisId ) );

        tf.util.assert(
          TensorTools.Comparator.isTensorArrayEqual( t2SumArray, t3SumArray ),
          `ConcatGatherUnsorted() != SplitConcatSortedShared()`);
      });

      tf.util.assert(
        TensorTools.Comparator.isTensorArrayEqual( t2Array, t4Array ),
        `ConcatGatherUnsorted() != PointwiseConv()`);
    });
  }

  testDifferentDisposeStrategy() {
    let functionTable = [
      this.concatPointwiseConv.concatGather_dispose_direct_call_for_dispose,
      this.concatPointwiseConv.concatGather_dispose_finally_call_for_dispose,

      this.concatPointwiseConv.concatGather_dispose_direct_call_map_dispose,
      this.concatPointwiseConv.concatGather_dispose_finally_call_map_dispose,

      this.concatPointwiseConv.concatGather_dispose_direct_call_map_tidy,
      this.concatPointwiseConv.concatGather_dispose_finally_call_map_tidy,

      this.concatPointwiseConv.concatGather_dispose_direct_for,
      this.concatPointwiseConv.concatGather_dispose_finally_for,

      this.concatPointwiseConv.concatGather_dispose_direct_map,
      this.concatPointwiseConv.concatGather_dispose_finally_map,

      this.concatPointwiseConv.concatGather_tidy_map,
    ];

    tf.tidy( () => {
      let memoryInfoPrev = tf.memory();

      let funcPrev;
      let tArrayPrev;

      for ( let i = 0; i < functionTable.length; ++i ) {
        let func = functionTable[ i ];
        let tArray = func( this.dataTensor3dArray );

        let memoryInfo = tf.memory();
        tf.util.assert( memoryInfo.numTensors == ( memoryInfoPrev.numTensors + tArray.length ), `${func.name}() memory leak`);

        if ( tArrayPrev ) {
          tf.util.assert(
            TensorTools.Comparator.isTensorArrayEqual( tArrayPrev, tArray ),
            `${funcPrev.name}() != ${func.name}()`);
        }

        memoryInfoPrev = memoryInfo;
        funcPrev = func;
        tArrayPrev = tArray;
      }
    });
  }
}

function init() {
  globalThis.testSet_110x110x24_g8 = new HeightWidthDepthGroup( 110, 110, 24, 8 ); // height, width, depth, groupCount
  globalThis.testSet_110x110x24_g4 = new HeightWidthDepthGroup( 110, 110, 24, 4 );
  globalThis.testSet_110x110x24_g3 = new HeightWidthDepthGroup( 110, 110, 24, 3 );
  globalThis.testSet_110x110x24_g2 = new HeightWidthDepthGroup( 110, 110, 24, 2 );
  globalThis.testSet_110x110x24_g1 = new HeightWidthDepthGroup( 110, 110, 24, 1 );
}

function testResultSame() {
  globalThis.testSet_110x110x24_g8.testResultSame();
  globalThis.testSet_110x110x24_g4.testResultSame();
  globalThis.testSet_110x110x24_g3.testResultSame();
  globalThis.testSet_110x110x24_g2.testResultSame();
  globalThis.testSet_110x110x24_g1.testResultSame();
}

function testDifferentDisposeStrategy() {
  globalThis.testSet_110x110x24_g8.testDifferentDisposeStrategy();
  globalThis.testSet_110x110x24_g4.testDifferentDisposeStrategy();
  globalThis.testSet_110x110x24_g3.testDifferentDisposeStrategy();
  globalThis.testSet_110x110x24_g2.testDifferentDisposeStrategy();
  globalThis.testSet_110x110x24_g1.testDifferentDisposeStrategy();
}

function disposeTensors() {
  if ( globalThis.testSet_110x110x24_g8 ) {
    globalThis.testSet_110x110x24_g8.disposeTensors();
    globalThis.testSet_110x110x24_g8 = null;
  }

  if ( globalThis.testSet_110x110x24_g4 ) {
    globalThis.testSet_110x110x24_g4.disposeTensors();
    globalThis.testSet_110x110x24_g4 = null;
  }

  if ( globalThis.testSet_110x110x24_g3 ) {
    globalThis.testSet_110x110x24_g3.disposeTensors();
    globalThis.testSet_110x110x24_g3 = null;
  }

  if ( globalThis.testSet_110x110x24_g2 ) {
    globalThis.testSet_110x110x24_g2.disposeTensors();
    globalThis.testSet_110x110x24_g2 = null;
  }

  if ( globalThis.testSet_110x110x24_g1 ) {
    globalThis.testSet_110x110x24_g1.disposeTensors();
    globalThis.testSet_110x110x24_g1 = null;
  }
}

//tf.ready().then( init );
<|endoftext|>


<commit_before>var path = require('path');
var MakePlatform = require('enb/lib/make');
var Logger = require('enb/lib/logger');
var dropRequireCache = require('enb/lib/fs/drop-require-cache');
var makePlatform = new MakePlatform();
var logger = new Logger();
var fixturesRelativePath = path.join('..', 'fixtures');
var fixturesAbsolutePath = path.join(__dirname, fixturesRelativePath);

logger.setEnabled(false);

function TestTarget (target) {
    this._target = target;
    this._targetRelativePath = path.join(fixturesRelativePath, target);
    this._targetAbsolutePath = path.join(fixturesAbsolutePath, target);
}

TestTarget.prototype.build = function () {
    var targets = [this._target];

    return makePlatform.init(fixturesAbsolutePath)
        .then(function () {
            makePlatform.loadCache();
            makePlatform.setLogger(logger);
            makePlatform.buildTargets(targets);
        })
        .then(function () {
            makePlatform.saveCache();
            makePlatform.destruct();
        });
};

TestTarget.prototype.require = function () {
    global.REQUIRED_TECHS = [];

    dropRequireCache(require, this._targetAbsolutePath);
    require(this._targetRelativePath);

    return global.REQUIRED_TECHS;
};

exports.TestTarget = TestTarget;
<commit_msg>Remove unused test module<commit_after><|endoftext|>


<commit_before>var LocalPair = require('../local-pair')

function listen (tracker, connection) {
  var actions = []
  connection.on('connecting', function () {
    actions.push(['connecting'])
    if (tracker.waiting) tracker.waiting()
  })
  connection.on('connect', function () {
    actions.push(['connect'])
    if (tracker.waiting) tracker.waiting()
  })
  connection.on('disconnect', function (reason) {
    actions.push(['disconnect', reason])
    if (tracker.waiting) tracker.waiting()
  })
  connection.on('message', function (msg) {
    actions.push(msg)
    if (tracker.waiting) tracker.waiting()
  })
  return actions
}

function createTracker (delay) {
  var result = {
    pair: new LocalPair(delay),
    wait: function () {
      return new Promise(function (resolve) {
        result.waiting = function () {
          result.waiting = false
          resolve()
        }
      })
    }
  }
  result.left = listen(result, result.pair.left)
  result.right = listen(result, result.pair.right)
  return result
}

it('has right link between connections', function () {
  var pair = new LocalPair()
  expect(pair.left.other()).toBe(pair.right)
  expect(pair.right.other()).toBe(pair.left)
})

it('throws a error on disconnection in disconnected state', function () {
  var pair = new LocalPair()
  expect(function () {
    pair.left.disconnect()
  }).toThrowError(/already finished/)
})

it('throws a error on message in disconnected state', function () {
  var pair = new LocalPair()
  expect(function () {
    pair.left.send(['test'])
  }).toThrowError(/started before sending/)
})

it('throws a error on connection in connected state', function () {
  var pair = new LocalPair()
  return pair.left.connect().then(function () {
    expect(function () {
      pair.left.connect()
    }).toThrowError(/already established/)
  })
})

it('sends a connect event', function () {
  var tracker = createTracker()
  expect(tracker.left).toEqual([])

  var connecting = tracker.pair.left.connect()
  expect(tracker.left).toEqual([['connecting']])
  expect(tracker.right).toEqual([])

  return connecting.then(function () {
    expect(tracker.left).toEqual([['connecting'], ['connect']])
    expect(tracker.right).toEqual([['connect']])
  })
})

it('sends a disconnect event', function () {
  var tracker = createTracker()
  return tracker.pair.left.connect().then(function () {
    tracker.pair.right.disconnect('error')
    expect(tracker.left).toEqual([['connecting'], ['connect']])
    expect(tracker.right).toEqual([['connect'], ['disconnect', 'error']])
    return tracker.wait()
  }).then(function () {
    expect(tracker.left).toEqual([
      ['connecting'],
      ['connect'],
      ['disconnect', undefined]
    ])
    expect(tracker.right).toEqual([['connect'], ['disconnect', 'error']])
  })
})

it('sends a message event', function () {
  var tracker = createTracker()
  return tracker.pair.left.connect().then(function () {
    tracker.pair.left.send(['test'])
    expect(tracker.right).toEqual([['connect']])
    return tracker.wait()
  }).then(function () {
    expect(tracker.left).toEqual([['connecting'], ['connect']])
    expect(tracker.right).toEqual([['connect'], ['test']])
  })
})

it('emulates delay', function () {
  var tracker = createTracker(50)
  expect(tracker.pair.delay).toEqual(50)

  var prevTime = Date.now()
  return tracker.pair.left.connect().then(function () {
    expect(Date.now() - prevTime).toBeGreaterThanOrEqual(50)

    prevTime = Date.now()
    tracker.pair.left.send(['test'])
    return tracker.wait()
  }).then(function () {
    expect(Date.now() - prevTime).toBeGreaterThanOrEqual(48)
  })
})
<commit_msg>Make tests more stable<commit_after>var LocalPair = require('../local-pair')

function listen (tracker, connection) {
  var actions = []
  connection.on('connecting', function () {
    actions.push(['connecting'])
    if (tracker.waiting) tracker.waiting()
  })
  connection.on('connect', function () {
    actions.push(['connect'])
    if (tracker.waiting) tracker.waiting()
  })
  connection.on('disconnect', function (reason) {
    actions.push(['disconnect', reason])
    if (tracker.waiting) tracker.waiting()
  })
  connection.on('message', function (msg) {
    actions.push(msg)
    if (tracker.waiting) tracker.waiting()
  })
  return actions
}

function createTracker (delay) {
  var result = {
    pair: new LocalPair(delay),
    wait: function () {
      return new Promise(function (resolve) {
        result.waiting = function () {
          result.waiting = false
          resolve()
        }
      })
    }
  }
  result.left = listen(result, result.pair.left)
  result.right = listen(result, result.pair.right)
  return result
}

it('has right link between connections', function () {
  var pair = new LocalPair()
  expect(pair.left.other()).toBe(pair.right)
  expect(pair.right.other()).toBe(pair.left)
})

it('throws a error on disconnection in disconnected state', function () {
  var pair = new LocalPair()
  expect(function () {
    pair.left.disconnect()
  }).toThrowError(/already finished/)
})

it('throws a error on message in disconnected state', function () {
  var pair = new LocalPair()
  expect(function () {
    pair.left.send(['test'])
  }).toThrowError(/started before sending/)
})

it('throws a error on connection in connected state', function () {
  var pair = new LocalPair()
  return pair.left.connect().then(function () {
    expect(function () {
      pair.left.connect()
    }).toThrowError(/already established/)
  })
})

it('sends a connect event', function () {
  var tracker = createTracker()
  expect(tracker.left).toEqual([])

  var connecting = tracker.pair.left.connect()
  expect(tracker.left).toEqual([['connecting']])
  expect(tracker.right).toEqual([])

  return connecting.then(function () {
    expect(tracker.left).toEqual([['connecting'], ['connect']])
    expect(tracker.right).toEqual([['connect']])
  })
})

it('sends a disconnect event', function () {
  var tracker = createTracker()
  return tracker.pair.left.connect().then(function () {
    tracker.pair.right.disconnect('error')
    expect(tracker.left).toEqual([['connecting'], ['connect']])
    expect(tracker.right).toEqual([['connect'], ['disconnect', 'error']])
    return tracker.wait()
  }).then(function () {
    expect(tracker.left).toEqual([
      ['connecting'],
      ['connect'],
      ['disconnect', undefined]
    ])
    expect(tracker.right).toEqual([['connect'], ['disconnect', 'error']])
  })
})

it('sends a message event', function () {
  var tracker = createTracker()
  return tracker.pair.left.connect().then(function () {
    tracker.pair.left.send(['test'])
    expect(tracker.right).toEqual([['connect']])
    return tracker.wait()
  }).then(function () {
    expect(tracker.left).toEqual([['connecting'], ['connect']])
    expect(tracker.right).toEqual([['connect'], ['test']])
  })
})

it('emulates delay', function () {
  var tracker = createTracker(50)
  expect(tracker.pair.delay).toEqual(50)

  var prevTime = Date.now()
  return tracker.pair.left.connect().then(function () {
    expect(Date.now() - prevTime).toBeGreaterThanOrEqual(48)

    prevTime = Date.now()
    tracker.pair.left.send(['test'])
    return tracker.wait()
  }).then(function () {
    expect(Date.now() - prevTime).toBeGreaterThanOrEqual(48)
  })
})
<|endoftext|>


<commit_before>/*
 * net-dolisten-test.js: Basic tests for the lib/net.js module
 *
 * (C) 2011 stolsma
 * MIT LICENCE
 *
 */

var assert = require('assert'),
    path = require('path'),
    fork = require('node-fork').fork,
    vows = require('vows'),
    helper = require('../helper/macros.js'),
    carapace = require('../../lib/carapace');

var script = path.join(__dirname, '..', 'fixtures', 'eacces.js'),
    argv = ['--plugin', 'net', '--setuid', 'nobody', script];

vows.describe('carapace/net/dolisten').addBatch({
  "When using haibu-carapace": {
    "spawning the eacces.js script the child carapace": {
      topic: function () {
        var callback = this.callback;
        var that = this,
            result,
            child;

        result = {
          events: [],
          exitCode: -1
        };

        child = fork(carapace.bin, argv, { silent: true });

        child.on('message', function onPort (info) {
          if (info.event == 'port') {
            result.events.push({
               event: info.event,
               info: info.data
             });
            child.kill();
            callback(null, result);
          }
        });
      },
      "should exit": {
        topic: function (info, child) {
          this.callback(null, info, child);
        },
        "with the correct exit code": function (_, info, child) {
          assert.equal(info.exitCode, -1);
        },
        "and emit the `port` event with the correct port": function (_, info, child) {
          var desired = 80;

          assert.equal(info.events.length, 1);
          info.events.forEach(function (event, index) {
            assert.equal(event.info.desired, desired);
          });
        }
      }
    }
  }
}).export(module);
<commit_msg>[test] test that the net plugin will use any address<commit_after>/*
 * net-dolisten-test.js: Basic tests for the lib/net.js module
 *
 * (C) 2011 stolsma
 * MIT LICENCE
 *
 */

var assert = require('assert'),
    path = require('path'),
    fork = require('node-fork').fork,
    vows = require('vows'),
    helper = require('../helper/macros.js'),
    carapace = require('../../lib/carapace');

var script = path.join(__dirname, '..', 'fixtures', 'eacces.js'),
    argv = ['--plugin', 'net', '--setuid', 'nobody', script];

vows.describe('carapace/net/dolisten').addBatch({
  "When using haibu-carapace": {
    "spawning the eacces.js script the child carapace": {
      topic: function () {
        var callback = this.callback;
        var that = this,
            result,
            child;

        result = {
          events: [],
          exitCode: -1
        };

        child = fork(carapace.bin, argv, { silent: true });

        child.on('message', function onPort (info) {
          if (info.event == 'port') {
            result.events.push({
               event: info.event,
               info: info.data
             });
            child.kill();
            callback(null, result);
          }
        });
      },
      "should exit": {
        topic: function (info, child) {
          this.callback(null, info, child);
        },
        "with the correct exit code": function (_, info, child) {
          assert.equal(info.exitCode, -1);
        },
        "and emit the `port` event with the correct port": function (_, info, child) {
          assert.equal(info.events.length, 1);
          info.events.forEach(function (event, index) {
            assert.equal(event.info.addr, '0.0.0.0');
            assert.equal(event.info.desired, 80);
          });
        }
      }
    }
  }
}).export(module);
<|endoftext|>


<commit_before>"use babel";

import { CompositeDisposable } from "atom";
import { allowUnsafeNewFunction } from "loophole";
import {
  standardTransforms,
  standardDisplayOrder,
  registerTransform,
  richestMimetype
} from "@nteract/transforms";
import React from "react";
import ReactDOM from "react-dom";
import { Map as ImmutableMap } from "immutable";

import { log } from "./utils";

// Due to Atom's CSP we need to use loophole :(
let PlotlyTransform;
allowUnsafeNewFunction(() => {
  PlotlyTransform = require("@nteract/transform-plotly").default;
});

// We can easily add other transforms here:
const additionalTransforms = [PlotlyTransform];

const {
  transforms,
  displayOrder
} = additionalTransforms.reduce(registerTransform, {
  transforms: standardTransforms,
  displayOrder: standardDisplayOrder
});

export default class ResultViewOld {
  constructor(marker) {
    this.marker = marker;
    this.element = document.createElement("div");
    this.element.classList.add("hydrogen", "output-bubble", "hidden");

    const outputContainer = document.createElement("div");
    outputContainer.classList.add("bubble-output-container");
    const onWheel = event => {
      const clientHeight = outputContainer.clientHeight;
      const scrollHeight = outputContainer.scrollHeight;
      const scrollTop = outputContainer.scrollTop;
      const atTop = scrollTop !== 0 && event.deltaY < 0;
      const atBottom =
        scrollTop !== scrollHeight - clientHeight && event.deltaY > 0;

      if (clientHeight < scrollHeight && (atTop || atBottom)) {
        event.stopPropagation();
      }
    };
    outputContainer.addEventListener("wheel", onWheel, { passive: true });
    this.element.appendChild(outputContainer);

    this.resultContainer = document.createElement("div");
    this.resultContainer.classList.add("bubble-result-container");
    outputContainer.appendChild(this.resultContainer);

    this.errorContainer = document.createElement("div");
    this.errorContainer.classList.add("bubble-error-container");
    outputContainer.appendChild(this.errorContainer);

    this.statusContainer = document.createElement("div");
    this.statusContainer.classList.add("bubble-status-container");
    this.spinner = this.buildSpinner();
    this.statusContainer.appendChild(this.spinner);
    outputContainer.appendChild(this.statusContainer);

    const richCloseButton = document.createElement("div");
    richCloseButton.classList.add("rich-close-button", "icon", "icon-x");
    richCloseButton.onclick = () => this.destroy();
    this.element.appendChild(richCloseButton);

    const actionPanel = document.createElement("div");
    actionPanel.classList.add("bubble-action-panel");
    this.element.appendChild(actionPanel);

    const closeButton = document.createElement("div");
    closeButton.classList.add(
      "action-button",
      "close-button",
      "icon",
      "icon-x"
    );
    closeButton.onclick = () => this.destroy();
    actionPanel.appendChild(closeButton);

    const padding = document.createElement("div");
    padding.classList.add("padding");
    actionPanel.appendChild(padding);

    const copyButton = document.createElement("div");
    copyButton.classList.add(
      "action-button",
      "copy-button",
      "icon",
      "icon-clippy"
    );
    copyButton.onclick = () => {
      atom.clipboard.write(this.getAllText());
      atom.notifications.addSuccess("Copied to clipboard");
    };
    actionPanel.appendChild(copyButton);

    const openButton = document.createElement("div");
    openButton.classList.add(
      "action-button",
      "open-button",
      "icon",
      "icon-file-symlink-file"
    );
    openButton.onclick = () => {
      const bubbleText = this.getAllText();
      atom.workspace.open().then(editor => editor.insertText(bubbleText));
    };
    actionPanel.appendChild(openButton);

    this.setMultiline(false);

    this.tooltips = new CompositeDisposable();
    this.addCopyTooltip(copyButton);
    this.tooltips.add(
      atom.tooltips.add(openButton, { title: "Open in new editor" })
    );

    this._hasResult = false;
    this._executionCount = null;

    return this;
  }

  addCopyTooltip(element) {
    this.tooltips.add(
      atom.tooltips.add(element, {
        title: () => {
          if (!this._executionCount) {
            return "Copy to clipboard";
          }
          return `Copy to clipboard (Out[${this._executionCount}])`;
        }
      })
    );
  }

  addResult(result) {
    let container;

    if (result.stream === "execution_count") {
      this._executionCount = result.data;
      return;
    }

    this.spinner.classList.add("hidden");
    this.element.classList.remove("hidden");

    if (result.stream === "status") {
      if (!this._hasResult && result.data === "ok") {
        log("ResultView: Show status container");
        this.statusContainer.classList.add("icon", "icon-check");
      }
      return;
    }

    log("ResultView: Add result", result);

    if (result.stream === "stderr") {
      container = this.errorContainer;
    } else if (result.stream === "stdout") {
      container = this.resultContainer;
    } else if (result.stream === "error") {
      container = this.errorContainer;
    } else {
      container = this.resultContainer;
    }

    const bundle = new ImmutableMap(result.data);
    const mimeType = richestMimetype(bundle, displayOrder, transforms);
    if (!mimeType) {
      log("ResultView: Cannot render", bundle);
      return;
    }
    const Transform = transforms.get(mimeType);

    log("ResultView: Hide status container");
    this._hasResult = true;
    this.statusContainer.classList.add("hidden");

    // If transforms use unsafe eval, we might need to use loophole due to Atom's CSP:
    // allowUnsafeEval(() => {
    //   allowUnsafeNewFunction(() => {
    //     ReactDOM.render(<Transform data={bundle.get(mimeType)} />, div);
    //   });
    // });
    const div = document.createElement("div");
    ReactDOM.render(<Transform data={bundle.get(mimeType)} />, div);

    if (mimeType === "text/plain") {
      this.element.classList.remove("rich");
      const text = result.data["text/plain"];

      if (
        this.resultContainer.innerHTML === "" &&
        this.errorContainer.innerHTML === "" &&
        text.length < 50 &&
        (text.indexOf("\n") === text.length - 1 || text.indexOf("\n") === -1)
      ) {
        this.setMultiline(false);

        this.addCopyTooltip(container);

        container.onclick = () => {
          atom.clipboard.write(this.getAllText());
          atom.notifications.addSuccess("Copied to clipboard");
        };
      } else {
        this.setMultiline(true);
      }
    } else if (mimeType === "application/json") {
      this.setMultiline(true);
    } else {
      this.element.classList.add("rich");
      this.setMultiline(true);
    }

    log("ResultView: Rendering as MIME ", mimeType);
    // this.getAllText must be called after appending the htmlElement
    // in order to obtain innerText
    container.appendChild(div);

    if (mimeType === "text/html") {
      if (this.getAllText() !== "") {
        this.element.classList.remove("rich");
      }
    }

    if (mimeType === "image/svg+xml") {
      container.classList.add("svg");
    }

    if (mimeType === "text/markdown") {
      this.element.classList.add("markdown");
      this.element.classList.remove("rich");
    }

    if (mimeType === "text/latex") {
      this.element.classList.add("latex");
    }

    if (this.errorContainer.getElementsByTagName("span").length === 0) {
      this.errorContainer.classList.add("plain-error");
    } else {
      this.errorContainer.classList.remove("plain-error");
    }
  }

  getAllText() {
    let text = "";

    const resultText = this.resultContainer.innerText.trim();
    if (resultText.length > 0) {
      text += resultText;
    }

    const errorText = this.errorContainer.innerText.trim();
    if (errorText.length > 0) {
      text += `\n${errorText}`;
    }

    return text;
  }

  setMultiline(multiline) {
    if (multiline) {
      this.element.classList.add("multiline");
    } else {
      this.element.classList.remove("multiline");
    }
  }

  buildSpinner() {
    const container = document.createElement("div");
    container.classList.add("spinner");

    const rect1 = document.createElement("div");
    rect1.classList.add("rect1");
    const rect2 = document.createElement("div");
    rect2.classList.add("rect2");
    const rect3 = document.createElement("div");
    rect3.classList.add("rect3");
    const rect4 = document.createElement("div");
    rect4.classList.add("rect4");
    const rect5 = document.createElement("div");
    rect5.classList.add("rect5");

    container.appendChild(rect1);
    container.appendChild(rect2);
    container.appendChild(rect3);
    container.appendChild(rect4);
    container.appendChild(rect5);

    return container;
  }

  spin() {
    this.element.classList.remove("hidden");
    this.spinner.classList.remove("hidden");
  }

  destroy() {
    this.tooltips.dispose();
    if (this.marker) {
      this.marker.destroy();
    }
    [
      ...this.errorContainer.childNodes,
      ...this.resultContainer.childNodes
    ].forEach(item => {
      ReactDOM.unmountComponentAtNode(item);
    });
    this.element.innerHTML = "";
  }
}
<commit_msg>Remove old result-view.js<commit_after><|endoftext|>


<commit_before>/* eslint-env mocha */
import Observable from '../src/observable.js';
import Symbolic from '../src/symbolic.js';

describe('Unit: Observable', () => {
    describe('simple object', () => {
        let changes = [];
        let observable = new Observable({
            firstName: 'Alan',
            lastName: 'Turing',
            birthday: new Date('1912/06/23'),
        });

        observable.on('change', (changset) => {
            changes.push(changset);
        });

        before((done) => setTimeout(done, 100));

        it('should not trigger changes if object is not changed', () => {
            observable.firstName = 'Alan';

            assert.equal(changes.length, 0);
        });

        it('should trigger changes if object has been updated', () => {
            observable.lastName = 'Skywalker';

            let change = changes[0];
            assert.equal(change.value, 'Skywalker');
            assert.equal(change.oldValue, 'Turing');
        });

        it('should trigger changes', () => {
            assert.equal(changes.length, 1);
        });
    });

    describe('simple Array', () => {
        let changes = [];
        let observable = new Observable([
            'Luke',
            'Anakin',
            'Padme',
        ]);

        observable.on('change', (changset) => {
            changes.push(changset);
        });

        before((done) => setTimeout(done, 100));

        it('should not trigger changes if object is not changed', () => {
            observable[0] = 'Luke';

            assert.equal(changes.length, 0);
            assert.equal(observable.length, 3);
        });

        it('should trigger changes if object has been updated', () => {
            observable[1] = 'Darth Vader';

            let change = changes[0];
            assert.equal(change.value, 'Darth Vader');
            assert.equal(change.oldValue, 'Anakin');
        });

        it('should trigger changes for array functions', () => {
            observable[2] = 'Leia';
            observable.pop();
            observable.push('Leia', 'Han Solo');

            assert.equal(changes[1].property, '2');
            assert.equal(changes[1].oldValue, 'Padme');
            assert.equal(changes[1].value, 'Leia');
            assert.equal(changes[2].property, '2');
            assert.equal(changes[2].added.length, 0);
            assert.equal(changes[2].removed.length, 1);
            assert.equal(changes[2].removed[0], 'Leia');
            assert.equal(changes[3].property, '2');
            assert.equal(changes[3].added.length, 2);
            assert.equal(changes[3].removed.length, 0);
            assert.deepEqual(changes[3].added, ['Leia', 'Han Solo']);
            assert.equal(observable.length, 4);
        });

        it('should trigger changes', () => {
            assert.equal(changes.length, 4);
        });
    });

    describe('complex objects', () => {
        let changes1 = [];
        let changes2 = [];
        let observable = new Observable({
            prop1: 1,
            prop2: ['a', 'b'],
            prop3: {
                prop3_1: 1,
                prop3_2: ['c', 'd'],
            },
            prop4: [
                { prop4_1: 1 },
                { prop4_2: [] },
            ],
        });
        let observable2 = new Observable(observable);

        observable.on('change', (changset) => {
            changes1.push(changset);
        });

        observable2.on('change', (changset) => {
            changes2.push(changset);
        });

        before((done) => {
            observable.prop1 = 2;
            observable.prop2.push('c');
            observable.prop3.prop3_1 = 2;
            observable.prop3.prop3_2.pop();
            observable.prop4[0].prop4_1 = 2;
            observable.prop4[1].prop4_2.push({
                prop6: 5,
            });
            observable.prop4[1].prop4_2[0].prop6 = 10;
            setTimeout(done, 100);
        });

        it('should trigger changes', () => {
            assert.equal(changes1.length, 7);
            assert.deepEqual(changes1, changes2);
        });

        it('should compute property name', () => {
            let paths = changes1.map((change) => change.property);
            assert.deepEqual(paths, [
                'prop1',
                'prop2.2',
                'prop3.prop3_1',
                'prop3.prop3_2.1',
                'prop4.0.prop4_1',
                'prop4.1.prop4_2.0',
                'prop4.1.prop4_2.0.prop6',
            ]);
        });
    });

    describe('symbolic keys', () => {
        let changes = [];
        let sym = Symbolic('tags');
        let observable = new Observable({
            name: 'Alan',
            [sym]: ['math'],
        });

        observable.on('change', (changset) => {
            changes.push(changset);
        });

        before((done) => setTimeout(done, 100));

        it('should be ignored', () => {
            observable[sym] = [];
            observable.name = 'Steve';
            assert.equal(changes.length, 1);
        });
    });

    describe('not Observable objects', () => {
        it('should throw an exception', () => {
            assert.throws(() => new Observable(null));
            assert.throws(() => new Observable(2));
            assert.throws(() => new Observable('hello'));
            assert.throws(() => new Observable(undefined));
            assert.throws(() => new Observable(NaN));
        });
    });

    describe('Observable object with thousands of instances', () => {
        it('should not crash in Firefox', () => {
            assert.doesNotThrow(() => {
                let obj = [[], [], [], [], [], [], [], [], []];
                let obs = new Observable(obj);
                for (let i = 0; i < 3000; i++) {
                    obs = new Observable(obs);
                }
            });
        });
    });

    describe('Reobserve object when adding properties', () => {
        const observable = new Observable({ foo: 'foo' });
        const changes = [];

        observable.on('change', changeset => {
            changes.push(changeset);
        });

        before((done) => setTimeout(done, 100));

        it('should trigger changes', () => {
            observable.bar = 'bar';
            observable.baz = 'baz';
            Observable.reobserve(observable);

            assert.deepEqual(changes, [
                {
                    property: 'bar',
                    oldValue: undefined,
                    value: 'bar',
                },
                {
                    property: 'baz',
                    oldValue: undefined,
                    value: 'baz',
                },
            ]);
        });
    });
});
<commit_msg>test: wait change callbacks in tests<commit_after>/* eslint-env mocha */
import Observable from '../src/observable.js';
import Symbolic from '../src/symbolic.js';

describe('Unit: Observable', () => {
    describe('simple object', () => {
        let changes = [];
        let observable = new Observable({
            firstName: 'Alan',
            lastName: 'Turing',
            birthday: new Date('1912/06/23'),
        });

        observable.on('change', (changset) => {
            changes.push(changset);
        });

        before((done) => {
            observable.firstName = 'Alan';
            observable.lastName = 'Skywalker';
            setTimeout(done, 100);
        });

        it('should trigger changes if object has been updated', () => {
            assert.equal(changes[0].value, 'Skywalker');
            assert.equal(changes[0].oldValue, 'Turing');
            assert.equal(changes.length, 1);
        });
    });

    describe('simple Array', () => {
        let changes = [];
        let observable = new Observable([
            'Luke',
            'Anakin',
            'Padme',
        ]);

        observable.on('change', (changset) => {
            changes.push(changset);
        });

        before((done) => {
            observable[0] = 'Luke';
            observable[1] = 'Darth Vader';
            observable[2] = 'Leia';
            observable.pop();
            observable.push('Leia', 'Han Solo');
            setTimeout(done, 100);
        });

        it('should trigger changes if object has been updated', () => {
            assert.equal(changes[0].value, 'Darth Vader');
            assert.equal(changes[0].oldValue, 'Anakin');
            assert.equal(changes[1].property, '2');
            assert.equal(changes[1].oldValue, 'Padme');
            assert.equal(changes[1].value, 'Leia');
            assert.equal(changes[2].property, '2');
            assert.equal(changes[2].added.length, 0);
            assert.equal(changes[2].removed.length, 1);
            assert.equal(changes[2].removed[0], 'Leia');
            assert.equal(changes[3].property, '2');
            assert.equal(changes[3].added.length, 2);
            assert.equal(changes[3].removed.length, 0);
            assert.deepEqual(changes[3].added, ['Leia', 'Han Solo']);
            assert.equal(observable.length, 4);
            assert.equal(changes.length, 4);
        });
    });

    describe('complex objects', () => {
        let changes1 = [];
        let changes2 = [];
        let observable = new Observable({
            prop1: 1,
            prop2: ['a', 'b'],
            prop3: {
                prop3_1: 1,
                prop3_2: ['c', 'd'],
            },
            prop4: [
                { prop4_1: 1 },
                { prop4_2: [] },
            ],
        });
        let observable2 = new Observable(observable);

        observable.on('change', (changset) => {
            changes1.push(changset);
        });

        observable2.on('change', (changset) => {
            changes2.push(changset);
        });

        before((done) => {
            observable.prop1 = 2;
            observable.prop2.push('c');
            observable.prop3.prop3_1 = 2;
            observable.prop3.prop3_2.pop();
            observable.prop4[0].prop4_1 = 2;
            observable.prop4[1].prop4_2.push({
                prop6: 5,
            });
            observable.prop4[1].prop4_2[0].prop6 = 10;
            setTimeout(done, 100);
        });

        it('should trigger changes', () => {
            assert.equal(changes1.length, 7);
            assert.deepEqual(changes1, changes2);
        });

        it('should compute property name', () => {
            let paths = changes1.map((change) => change.property);
            assert.deepEqual(paths, [
                'prop1',
                'prop2.2',
                'prop3.prop3_1',
                'prop3.prop3_2.1',
                'prop4.0.prop4_1',
                'prop4.1.prop4_2.0',
                'prop4.1.prop4_2.0.prop6',
            ]);
        });
    });

    describe('symbolic keys', () => {
        let changes = [];
        let sym = Symbolic('tags');
        let observable = new Observable({
            name: 'Alan',
            [sym]: ['math'],
        });

        observable.on('change', (changset) => {
            changes.push(changset);
        });

        before((done) => {
            observable[sym] = [];
            observable.name = 'Steve';
            setTimeout(done, 100);
        });

        it('should be ignored', () => {
            assert.equal(changes.length, 1);
        });
    });

    describe('not Observable objects', () => {
        it('should throw an exception', () => {
            assert.throws(() => new Observable(null));
            assert.throws(() => new Observable(2));
            assert.throws(() => new Observable('hello'));
            assert.throws(() => new Observable(undefined));
            assert.throws(() => new Observable(NaN));
        });
    });

    describe('Observable object with thousands of instances', () => {
        it('should not crash in Firefox', () => {
            assert.doesNotThrow(() => {
                let obj = [[], [], [], [], [], [], [], [], []];
                let obs = new Observable(obj);
                for (let i = 0; i < 3000; i++) {
                    obs = new Observable(obs);
                }
            });
        });
    });

    describe('Reobserve object when adding properties', () => {
        const observable = new Observable({ foo: 'foo' });
        const changes = [];

        observable.on('change', changeset => {
            changes.push(changeset);
        });

        before((done) => {
            observable.bar = 'bar';
            observable.baz = 'baz';
            Observable.reobserve(observable);
            setTimeout(done, 100);
        });

        it('should trigger changes', () => {
            assert.deepEqual(changes, [
                {
                    property: 'bar',
                    oldValue: undefined,
                    value: 'bar',
                },
                {
                    property: 'baz',
                    oldValue: undefined,
                    value: 'baz',
                },
            ]);
        });
    });
});
<|endoftext|>


<commit_before>function ddg_spice_amazon(apiResult) {
    if(!apiResult || !apiResult.results || !apiResult.results.length){ return; }

    var items = apiResult.results;

    // TODO: this is obviously not ideal, just ported over
    // what was in permenant.js. It loads ratings data for each item,
    // the render the spice plugin:
    var loadRatingsData = function(items,fn){
        var numLeft = items.length;

        var onGotRatingsData = function(r,s,x){

            if (r.stars.match(/stars-(\d)-(\d)/)) {
                console.log("rating: %s.%s", RegExp.$1, RegExp.$2);
                this.rating = RegExp.$1 + "." + RegExp.$2;
            }
            this.reviewCount = r.reviews;

            // add the products_buy template
            // TODO: this is hacky, can it combine with the Spice.templates?
            this.products_buy = 'products_amazon_buy';

            // decrement the numLeft and
            // see if all have returned yet:
            numLeft--;
            if(!numLeft){
                // if done, call the callback:
                fn();
            }
        }

        for(var i=0;i<items.length;i++){
            var item = items[i],
                arg = item.rating,
                url = '/m.js?r=';

            arg = arg.replace(/(?:.com.au|.com.br|.cn|.fr|.de|.in|.it|.co.jp|.mx|.es|.co.uk|.com|.ca?)/i, '');
            arg = arg.replace('http://www.amazon/reviews/iframe?', '');
            $.getJSON(url + encodeURIComponent(arg),onGotRatingsData.bind(item));
        }
    };

    // loading ratings data for all the items
    // then render the spice:
    loadRatingsData(items,function(){
        Spice.add({
            id: 'products',
            name: 'Products',

            data: items,

            meta: {
                count: items.length,
                total: items.length,
                itemType: 'Products',
                sourceName: 'Amazon',
                sourceUrl: apiResult.more_at,
                sourceIcon: true
            },

            templates: {
                item: 'products_item',
                detail: 'products_detail',
                item_detail: 'products_item_detail'
            }
        });
    });
}
<commit_msg>console<commit_after>function ddg_spice_amazon(apiResult) {
    if(!apiResult || !apiResult.results || !apiResult.results.length){ return; }

    var items = apiResult.results;

    // TODO: this is obviously not ideal, just ported over
    // what was in permenant.js. It loads ratings data for each item,
    // the render the spice plugin:
    var loadRatingsData = function(items,fn){
        var numLeft = items.length;

        var onGotRatingsData = function(r,s,x){

            if (r.stars.match(/stars-(\d)-(\d)/)) {
                this.rating = RegExp.$1 + "." + RegExp.$2;
            }
            this.reviewCount = r.reviews;

            // add the products_buy template
            // TODO: this is hacky, can it combine with the Spice.templates?
            this.products_buy = 'products_amazon_buy';

            // decrement the numLeft and
            // see if all have returned yet:
            numLeft--;
            if(!numLeft){
                // if done, call the callback:
                fn();
            }
        }

        for(var i=0;i<items.length;i++){
            var item = items[i],
                arg = item.rating,
                url = '/m.js?r=';

            arg = arg.replace(/(?:.com.au|.com.br|.cn|.fr|.de|.in|.it|.co.jp|.mx|.es|.co.uk|.com|.ca?)/i, '');
            arg = arg.replace('http://www.amazon/reviews/iframe?', '');
            $.getJSON(url + encodeURIComponent(arg),onGotRatingsData.bind(item));
        }
    };

    // loading ratings data for all the items
    // then render the spice:
    loadRatingsData(items,function(){
        Spice.add({
            id: 'products',
            name: 'Products',

            data: items,

            meta: {
                count: items.length,
                total: items.length,
                itemType: 'Products',
                sourceName: 'Amazon',
                sourceUrl: apiResult.more_at,
                sourceIcon: true
            },

            templates: {
                item: 'products_item',
                detail: 'products_detail',
                item_detail: 'products_item_detail'
            }
        });
    });
}
<|endoftext|>


<commit_before>function ddg_spice_amazon(api_response) {
    "use strict";

    if (!api_response || !api_response.results ||
	!api_response.results.length || api_response.results.length == 0) {
	return;
    }

    var items = api_response.results;

    // TODO: this is obviously not ideal, just ported over
    // what was in permenant.js. It loads ratings data for each item,
    // the render the spice plugin:
    var loadRatingsData = function(items,fn){
        var numLeft = items.length;

        var onGotRatingsData = function(r,s,x){
            // add the ratings data:
            this.ratingData = r;

            // add the products_buy template
            // TODO: this is hacky, can it combine with the Spice.templates?
            this.products_buy = DDG.templates.products_amazon_buy;

            // decrement the numLeft and
            // see if all have returned yet:
            numLeft--;
            if(!numLeft){
                // if done, call the callback:
                fn();
            }
        }

        for(var i=0;i<items.length;i++){
            var item = items[i],
                arg = item.rating,
                url = '/m.js?r=';

            arg = arg.replace(/(?:.com.au|.com.br|.cn|.fr|.de|.in|.it|.co.jp|.mx|.es|.co.uk|.com|.ca?)/i, '');
            arg = arg.replace('http://www.amazon/reviews/iframe?', '');
            $.getJSON(url + encodeURIComponent(arg),onGotRatingsData.bind(item));
        }
    };

    // loading ratings data for all the items
    // then render the spice:
    loadRatingsData(items,function(){
        Spice.add({
            id: 'products',
            name: 'Products',

            data: items,

            meta: {
                count: items.length,
                total: items.length,
                itemType: 'Products',
                sourceName: 'Amazon',
                sourceUrl: api_response.more_at,
                sourceIcon: true
            },

            templates: {
                item: DDG.templates.products_item,
                detail: DDG.templates.products_detail,
                item_detail: DDG.templates.products_item_detail
            }
        });
    });
}
<commit_msg>amazon.js: replace api_response to api_result.<commit_after>function ddg_spice_amazon(api_result) {
    "use strict";

    if (!api_result || !api_result.results ||
	!api_result.results.length || api_result.results.length == 0) {
	return;
    }

    var items = api_result.results;

    // TODO: this is obviously not ideal, just ported over
    // what was in permenant.js. It loads ratings data for each item,
    // the render the spice plugin:
    var loadRatingsData = function(items,fn){
        var numLeft = items.length;

        var onGotRatingsData = function(r,s,x){
            // add the ratings data:
            this.ratingData = r;

            // add the products_buy template
            // TODO: this is hacky, can it combine with the Spice.templates?
            this.products_buy = DDG.templates.products_amazon_buy;

            // decrement the numLeft and
            // see if all have returned yet:
            numLeft--;
            if(!numLeft){
                // if done, call the callback:
                fn();
            }
        }

        for(var i=0;i<items.length;i++){
            var item = items[i],
                arg = item.rating,
                url = '/m.js?r=';

            arg = arg.replace(/(?:.com.au|.com.br|.cn|.fr|.de|.in|.it|.co.jp|.mx|.es|.co.uk|.com|.ca?)/i, '');
            arg = arg.replace('http://www.amazon/reviews/iframe?', '');
            $.getJSON(url + encodeURIComponent(arg),onGotRatingsData.bind(item));
        }
    };

    // loading ratings data for all the items
    // then render the spice:
    loadRatingsData(items,function(){
        Spice.add({
            id: 'products',
            name: 'Products',

            data: items,

            meta: {
                count: items.length,
                total: items.length,
                itemType: 'Products',
                sourceName: 'Amazon',
                sourceUrl: api_result.more_at,
                sourceIcon: true
            },

            templates: {
                item: DDG.templates.products_item,
                detail: DDG.templates.products_detail,
                item_detail: DDG.templates.products_item_detail
            }
        });
    });
}
<|endoftext|>


<commit_before>var _ = require('lodash');

function parseId(postData, type) {
  type = type || 'tags';
  return postData[type].data.map(function(t) { return t._id; });
}

function getSimilarityScore(arrA, arrB) {
  if (!arrA.length || !arrB.length) return 0;
  return _.intersection(arrA, arrB).length / (arrA.length + arrB.length);
}

function getPostSimilarity(postA, postB) {
  return getSimilarityScore(
    parseId(postA, 'tags'),
    parseId(postB, 'tags')
  ) + getSimilarityScore(
    parseId(postA, 'categories'),
    parseId(postB, 'categories')
  );
}

/**
 * Related Posts helper
 * @description Find related posts.
 * @example
 *      related_posts(post, 3);
 */

hexo.extend.helper.register('related_posts', function (target, max) {
  var posts, relatedPosts;

  max = max || 3;
  posts = hexo.locals.get('posts').data;

  return posts.map(function(p) {
    p.similarityScore = getPostSimilarity(target, p);
    return p;
  }).sort(function(a, b) {
    return b.similarityScore - a.similarityScore;
  }).filter(function(p) {
    return (p._id !== target._id) && (p.similarityScore > 0);
  }).slice(0, max);
});
<commit_msg>Improve precision of related post s<commit_after>var _ = require('lodash');

function parseId(postData, type) {
  type = type || 'tags';
  return postData[type].data.map(function(t) { return t._id; });
}

function getSimilarityScore(arrA, arrB) {
  if (!arrA.length || !arrB.length) return 0;
  return Math.sqrt(
    _.intersection(arrA, arrB).length / Math.max(arrA.length, arrB.length)
  );
}

function getPostSimilarity(postA, postB) {
  return getSimilarityScore(
    parseId(postA, 'tags'),
    parseId(postB, 'tags')
  ) * 2 + getSimilarityScore(
    parseId(postA, 'categories'),
    parseId(postB, 'categories')
  );
}

/**
 * Related Posts helper
 * @description Find related posts.
 * @example
 *      related_posts(post, 3);
 */

hexo.extend.helper.register('related_posts', function (target, max) {
  var posts, relatedPosts;

  max = max || 3;
  posts = hexo.locals.get('posts').data;

  return posts.map(function(p) {
    p.similarityScore = getPostSimilarity(target, p);
    return p;
  }).sort(function(a, b) {
    return b.similarityScore - a.similarityScore;
  }).filter(function(p) {
    return (p._id !== target._id) && (p.similarityScore > 0);
  }).slice(0, max);
});
<|endoftext|>


<commit_before>let radio       = [
    { title : 'Dataset', value : 'dataset' },
    { title : 'Rows', value : 'rows' },
    { title : 'Filesize (in MB)', value : 'filesize' },
];

let baseURL     = "http://localhost:3010/";

let frequencies = [];
let freqColors  = ['#BBCDA3', '#055C81', '#B13C3D', '#CCB40C', '#DA9F93'];
let activeFreq  = [];
let activeSec   = [];
let filter      = {
    type        : null,
}

let exclude     = ['China'];

$(' #tagselector-container ').on('sector-change', (event, state, sector) => {
    let spinner     = new Spinner().spin(document.getElementById('root'));
    $(' #spinnerOverlay ').show();

    if (state == 'add') {
        activeSec.push(sector);
    } else if (state == 'remove') {
        _.pull(activeSec, sector);
    } else if (state == 'write') {
        activeSec   = [sector];
        createSwimlane(rangeData, activeSec, activeFreq, startDate, endDate);
    }

    let startDate   = $.datepicker.formatDate('yy-mm-dd', $(' #startpicker ').datepicker('getDate'));
    let endDate     = $.datepicker.formatDate('yy-mm-dd', $(' #endpicker ').datepicker('getDate'));
    fetchData(startDate, endDate, false, false, true, true, () => {
        $(' #spinnerOverlay ').hide();
        spinner.stop();
    });
});

$(document).on('click', '.type-button', (e) => {
    let spinner     = new Spinner().spin(document.getElementById('root'));
    $(' #spinnerOverlay ').show();

    filter.type     = $(e.target).attr('value');
    $(' .type-active ').removeClass('type-active');
    $('#type-' + filter.type).addClass('type-active');

    let startDate   = $.datepicker.formatDate('yy-mm-dd', $(' #startpicker ').datepicker('getDate'));
    let endDate     = $.datepicker.formatDate('yy-mm-dd', $(' #endpicker ').datepicker('getDate'));
    fetchData(startDate, endDate, true, false, true, false, () => {
        $(' #spinnerOverlay ').hide();
        spinner.stop();
    });
});

$(document).on('click', '.freq-button', (e) => {
    let spinner     = new Spinner().spin(document.getElementById('root'));
    $(' #spinnerOverlay ').show();

    let selected    = _.toInteger($(e.target).attr('value'));
    if ($('#freq-' + selected).hasClass('freq-unactive')) {
        $('#freq-' + selected).removeClass('freq-unactive');
        activeFreq.push(selected);
    } else {
        $('#freq-' + selected).addClass('freq-unactive');
        _.pull(activeFreq, selected);
    }

    let startDate   = $.datepicker.formatDate('yy-mm-dd', $(' #startpicker ').datepicker('getDate'));
    let endDate     = $.datepicker.formatDate('yy-mm-dd', $(' #endpicker ').datepicker('getDate'));
    fetchData(startDate, endDate, true, true, true, true, () => {
        $(' #spinnerOverlay ').hide();
        spinner.stop();
    });
});

$(document).on('click', '#button-changer', (e) => {
    if ($(' #swimlane-chart ').is(":visible")) {
        $(' #swimlane-chart ').hide();
        $(' #datasets-wrapper ').show();

        $(' #button-changer ').html('See swimlane');
    } else {
        $(' #swimlane-chart ').show();
        $(' #datasets-wrapper ').hide();

        $(' #button-changer ').html('See datasets');
    }
});

function drawListDataset(data) {
    $(' #datasets-container ').html(
        _.chain(data).filter((o) => (_.intersection(o.tags, activeSec).length > 0)).map((o, idx) => (
            "
" +
                "
" + o._id + "
" +
                "
" + _.chain(o.tags).intersection(activeSec).map((t) => ("
" + t + "
")).sortBy(_.size).value().join('') + "
" +
                "

" +
             "
"
        )).value()
    );
}

function fetchData(startDate, endDate, isForce, isSwimlane, isStacked, isRedraw, callback) {
    $.get( baseURL + 'selector', { frequencies : JSON.stringify(activeFreq), datatype : filter.type, startDate, endDate }, (response) => {
        tagChain    = _.chain(response.result).flatMap('tags').uniq();

        if (tagChain.intersection(activeSec).size().value() == 0) { activeSec.push(tagChain.sortBy().head().value()); }

        if (isForce) { createForce(response.result, activeSec); }
        if (isSwimlane) { createSwimlane(response.result, activeSec, startDate, endDate); }
        if (isRedraw) { drawListDataset(response.result); }
    }).always(() => {
        if (isStacked) {
            $.get( baseURL + 'stacked', { frequencies : JSON.stringify(activeFreq), tags : JSON.stringify(activeSec), datatype : filter.type, startDate, endDate }, (response) => {
                createStacked(response.result, frequencies, freqColors);
            }).always(() => {
                callback();
            });
        } else {
            callback();
        }
    });
}

window.onload   = function() {
    let spinner     = new Spinner().spin(document.getElementById('root'));

    let stringType  = _.map(radio, (o) => ("
" + o.title + "
")).join('');
    $(' #types-container ').append(stringType);
    filter.type     = _.head(radio).value;
    $('#type-' + filter.type).addClass('type-active');

    let dateFormat  = 'dd MM yy';
    let dateConfig  = { showOtherMonths: true, selectOtherMonths: true, changeMonth: true, changeYear: true, dateFormat : dateFormat }
    let fromPicker  = $(' #startpicker ').datepicker(dateConfig);
    let untilPicker = $(' #endpicker ').datepicker(dateConfig);

    $(' #filter-wrapper ').height($(' #wrapper ').outerHeight(true) / 2);

    $(' #datasets-wrapper ').width($(' #tagselector-container ').outerWidth(true) * 2 / 3);
    $(' #datasets-wrapper ').height($(' #wrapper ').outerHeight(true) / 2 - 40);

    fromPicker.datepicker( 'setDate', moment().subtract(6, 'year').startOf('year').toDate() );
    untilPicker.datepicker( 'setDate', '0' );
    fromPicker.datepicker( 'option', 'maxDate', untilPicker.datepicker('getDate') );
    untilPicker.datepicker( 'option', 'minDate', fromPicker.datepicker('getDate') );

    fromPicker.on('change', () => {
        untilPicker.datepicker( 'option', 'minDate', fromPicker.datepicker('getDate') );

        redrawOnDatepickerChange();
    });

    untilPicker.on('change', () => {
        fromPicker.datepicker( 'option', 'maxDate', untilPicker.datepicker("getDate") );

        redrawOnDatepickerChange();
    });

    $.get( baseURL + 'config', (response) => {
        frequencies     = response.result.frequency;
        activeFreq      = _.clone(frequencies);

        let stringFreq  = _.map(frequencies, (o, idx) => ("
" + o + "
"));
        $(' #frequency-container ').append(stringFreq);

        let startDate   = $.datepicker.formatDate('yy-mm-dd', fromPicker.datepicker('getDate'));
        let endDate     = $.datepicker.formatDate('yy-mm-dd', untilPicker.datepicker('getDate'));
        fetchData(startDate, endDate, true, true, true, true, () => {
            $(' #spinnerOverlay ').css('opacity', '0.7');
            $(' #spinnerOverlay ').hide();
            spinner.stop();
        });
    });

    function redrawOnDatepickerChange() {
        let spinner     = new Spinner().spin(document.getElementById('root'));
        $(' #spinnerOverlay ').show();

        let startDate   = $.datepicker.formatDate('yy-mm-dd', $(' #startpicker ').datepicker('getDate'));
        let endDate     = $.datepicker.formatDate('yy-mm-dd', $(' #endpicker ').datepicker('getDate'));
        fetchData(startDate, endDate, true, true, true, true, () => {
            $(' #spinnerOverlay ').hide();
            spinner.stop();
        });
    }
};
<commit_msg>fix old frequency<commit_after>let radio       = [
    { title : 'Dataset', value : 'dataset' },
    { title : 'Rows', value : 'rows' },
    { title : 'Filesize (in MB)', value : 'filesize' },
];

let baseURL     = "http://localhost:3010/";

let frequencies = [];
let freqColors  = ['#BBCDA3', '#055C81', '#B13C3D', '#CCB40C', '#DA9F93'];
let activeFreq  = [];
let activeSec   = [];
let filter      = {
    type        : null,
}

let exclude     = ['China'];

$(' #tagselector-container ').on('sector-change', (event, state, sector) => {
    let spinner     = new Spinner().spin(document.getElementById('root'));
    $(' #spinnerOverlay ').show();

    if (state == 'add') {
        activeSec.push(sector);
    } else if (state == 'remove') {
        _.pull(activeSec, sector);
    } else if (state == 'write') {
        activeSec   = [sector];
        createSwimlane(rangeData, activeSec, activeFreq, startDate, endDate);
    }

    let startDate   = $.datepicker.formatDate('yy-mm-dd', $(' #startpicker ').datepicker('getDate'));
    let endDate     = $.datepicker.formatDate('yy-mm-dd', $(' #endpicker ').datepicker('getDate'));
    fetchData(startDate, endDate, false, false, true, true, () => {
        $(' #spinnerOverlay ').hide();
        spinner.stop();
    });
});

$(document).on('click', '.type-button', (e) => {
    let spinner     = new Spinner().spin(document.getElementById('root'));
    $(' #spinnerOverlay ').show();

    filter.type     = $(e.target).attr('value');
    $(' .type-active ').removeClass('type-active');
    $('#type-' + filter.type).addClass('type-active');

    let startDate   = $.datepicker.formatDate('yy-mm-dd', $(' #startpicker ').datepicker('getDate'));
    let endDate     = $.datepicker.formatDate('yy-mm-dd', $(' #endpicker ').datepicker('getDate'));
    fetchData(startDate, endDate, true, false, true, false, () => {
        $(' #spinnerOverlay ').hide();
        spinner.stop();
    });
});

$(document).on('click', '.freq-button', (e) => {
    let spinner     = new Spinner().spin(document.getElementById('root'));
    $(' #spinnerOverlay ').show();

    let selected    = _.toInteger($(e.target).attr('value'));
    if ($('#freq-' + selected).hasClass('freq-unactive')) {
        $('#freq-' + selected).removeClass('freq-unactive');
        activeFreq.push(selected);
    } else {
        $('#freq-' + selected).addClass('freq-unactive');
        _.pull(activeFreq, selected);
    }

    let startDate   = $.datepicker.formatDate('yy-mm-dd', $(' #startpicker ').datepicker('getDate'));
    let endDate     = $.datepicker.formatDate('yy-mm-dd', $(' #endpicker ').datepicker('getDate'));
    fetchData(startDate, endDate, true, true, true, true, () => {
        $(' #spinnerOverlay ').hide();
        spinner.stop();
    });
});

$(document).on('click', '#button-changer', (e) => {
    if ($(' #swimlane-chart ').is(":visible")) {
        $(' #swimlane-chart ').hide();
        $(' #datasets-wrapper ').show();

        $(' #button-changer ').html('See swimlane');
    } else {
        $(' #swimlane-chart ').show();
        $(' #datasets-wrapper ').hide();

        $(' #button-changer ').html('See datasets');
    }
});

function drawListDataset(data) {
    $(' #datasets-container ').html(
        _.chain(data).filter((o) => (_.intersection(o.tags, activeSec).length > 0)).map((o, idx) => (
            "
" +
                "
" + o._id + "
" +
                "
" + _.chain(o.tags).intersection(activeSec).map((t) => ("
" + t + "
")).sortBy(_.size).value().join('') + "
" +
                "

" +
             "
"
        )).value()
    );
}

function fetchData(startDate, endDate, isForce, isSwimlane, isStacked, isRedraw, callback) {
    $.get( baseURL + 'selector', { frequencies : JSON.stringify(activeFreq), datatype : filter.type, startDate, endDate }, (response) => {
        tagChain    = _.chain(response.result).flatMap('tags').uniq();

        if (tagChain.intersection(activeSec).size().value() == 0) { activeSec = [tagChain.sortBy().head().value()]; }

        if (isForce) { createForce(response.result, activeSec); }
        if (isSwimlane) { createSwimlane(response.result, activeSec, startDate, endDate); }
        if (isRedraw) { drawListDataset(response.result); }
    }).always(() => {
        if (isStacked) {
            $.get( baseURL + 'stacked', { frequencies : JSON.stringify(activeFreq), tags : JSON.stringify(activeSec), datatype : filter.type, startDate, endDate }, (response) => {
                createStacked(response.result, frequencies, freqColors);
            }).always(() => {
                callback();
            });
        } else {
            callback();
        }
    });
}

window.onload   = function() {
    let spinner     = new Spinner().spin(document.getElementById('root'));

    let stringType  = _.map(radio, (o) => ("
" + o.title + "
")).join('');
    $(' #types-container ').append(stringType);
    filter.type     = _.head(radio).value;
    $('#type-' + filter.type).addClass('type-active');

    let dateFormat  = 'dd MM yy';
    let dateConfig  = { showOtherMonths: true, selectOtherMonths: true, changeMonth: true, changeYear: true, dateFormat : dateFormat }
    let fromPicker  = $(' #startpicker ').datepicker(dateConfig);
    let untilPicker = $(' #endpicker ').datepicker(dateConfig);

    $(' #filter-wrapper ').height($(' #wrapper ').outerHeight(true) / 2);

    $(' #datasets-wrapper ').width($(' #tagselector-container ').outerWidth(true) * 2 / 3);
    $(' #datasets-wrapper ').height($(' #wrapper ').outerHeight(true) / 2 - 40);

    fromPicker.datepicker( 'setDate', moment().subtract(6, 'year').startOf('year').toDate() );
    untilPicker.datepicker( 'setDate', '0' );
    fromPicker.datepicker( 'option', 'maxDate', untilPicker.datepicker('getDate') );
    untilPicker.datepicker( 'option', 'minDate', fromPicker.datepicker('getDate') );

    fromPicker.on('change', () => {
        untilPicker.datepicker( 'option', 'minDate', fromPicker.datepicker('getDate') );

        redrawOnDatepickerChange();
    });

    untilPicker.on('change', () => {
        fromPicker.datepicker( 'option', 'maxDate', untilPicker.datepicker("getDate") );

        redrawOnDatepickerChange();
    });

    $.get( baseURL + 'config', (response) => {
        frequencies     = response.result.frequency;
        activeFreq      = _.clone(frequencies);

        let stringFreq  = _.map(frequencies, (o, idx) => ("
" + o + "
"));
        $(' #frequency-container ').append(stringFreq);

        let startDate   = $.datepicker.formatDate('yy-mm-dd', fromPicker.datepicker('getDate'));
        let endDate     = $.datepicker.formatDate('yy-mm-dd', untilPicker.datepicker('getDate'));
        fetchData(startDate, endDate, true, true, true, true, () => {
            $(' #spinnerOverlay ').css('opacity', '0.7');
            $(' #spinnerOverlay ').hide();
            spinner.stop();
        });
    });

    function redrawOnDatepickerChange() {
        let spinner     = new Spinner().spin(document.getElementById('root'));
        $(' #spinnerOverlay ').show();

        let startDate   = $.datepicker.formatDate('yy-mm-dd', $(' #startpicker ').datepicker('getDate'));
        let endDate     = $.datepicker.formatDate('yy-mm-dd', $(' #endpicker ').datepicker('getDate'));
        fetchData(startDate, endDate, true, true, true, true, () => {
            $(' #spinnerOverlay ').hide();
            spinner.stop();
        });
    }
};
<|endoftext|>