<commit_before>'use strict';

// Immediately invoke all dependencies,
// because of the `copy` module use `lazy-cache` (it can't work with `mock-fs`).
process.env.UNLAZY = true;

const fs = require('fs');
const path = require('path');

const test = require('ava');
const mockFs = require('mock-fs');
const streamify = require('stream-array');

const CopyFileStream = require('../../lib/streams/copy-stream');

const cwd = process.cwd();
const base = path.resolve('source-dir');
const dest = path.resolve('dest-dir');

test.afterEach(() => mockFs.restore());

test('should not copy empty dir', async t => {
    mockFs({
        'source-dir': {}
    });

    await copyFiles([]);

    const dirs = fs.readdirSync('./');

    t.deepEqual(dirs, ['source-dir']);
});

test('should copy files', async t => {
    mockFs({
        'source-dir': {
            'file-1.txt': 'Hi!',
            'file-2.txt': 'Hello!'
        }
    });

    await copyFiles(['file-1.txt', 'file-2.txt']);

    const files = fs.readdirSync(path.join(dest, 'source-dir'));

    t.deepEqual(files, ['file-1.txt', 'file-2.txt']);
});

test('should copy file with contents', async t => {
    mockFs({
        'source-dir': {
            'file-1.txt': 'Hi!'
        }
    });

    await copyFiles(['file-1.txt']);

    const str = fs.readFileSync(path.join(dest, 'source-dir', 'file-1.txt'), 'utf-8');

    t.is(str, 'Hi!');
});

test('should copy dir with subdirs', async t => {
    mockFs({
        'source-dir': {
            'sub-dir': {
                'file-1.txt': 'Hi!',
                'file-2.txt': 'Hello!'
            }
        }
    });

    await copyFiles(['sub-dir/file-1.txt', 'sub-dir/file-2.txt']);

    const files = fs.readdirSync(path.join(dest, 'source-dir', 'sub-dir'));

    t.deepEqual(files, ['file-1.txt', 'file-2.txt']);
});

test('should copy directory without files', async t => {
    mockFs({
        'source-dir': {
            'sub-dir': { 'file-2.txt': 'Hello!' }
        }
    });

    await copyFiles(['sub-dir/']);

    const files = fs.readdirSync(path.join(dest, 'source-dir'));

    t.deepEqual(files, ['sub-dir']);
});

test('should copy source file of symlink', async t => {
    mockFs({
        'file-1.txt': 'Hi!',
        'source-dir': {
            'symlink.txt': mockFs.symlink({
                path: '../file-1.txt'
            })
        }
    });

    await copyFiles(['symlink.txt']);
    fs.unlinkSync('./file-1.txt');

    const str = fs.readFileSync(path.join(dest, 'source-dir', 'symlink.txt'), 'utf-8');

    t.deepEqual(str, 'Hi!');
});

test('should ignore broken symlinks', async t => {
    mockFs({
        'source-dir': {
            'file-1.txt': 'Hi!',
            'symlink.txt': mockFs.symlink({
                path: 'no-file'
            })
        }
    });

    await copyFiles(['file-1.txt', 'symlink.txt']);

    const files = fs.readdirSync(path.join(dest, 'source-dir'));

    t.deepEqual(files, ['file-1.txt']);
});

test('should ignore empty file', async t => {
    mockFs({
        'source-dir': {
            'file-1.txt': 'Hi!',
            'empty-file.txt': mockFs.file()
        }
    });

    await copyFiles(['file-1.txt', 'empty-file.txt']);

    const files = fs.readdirSync(path.join(dest, 'source-dir'));

    t.deepEqual(files, ['file-1.txt']);
});

test('should copy empty file', async t => {
    mockFs({
        'source-dir': {
            'empty-file.txt': mockFs.file(),
            'file-1.txt': 'Hi!'
        }
    });

    await copyFiles(['empty-file.txt', 'file-1.txt'], { emptyFiles: true });

    const files = fs.readdirSync(path.join(dest, 'source-dir'));

    t.deepEqual(files, ['empty-file.txt', 'file-1.txt']);
});

test('should emit error if file file does not exist', t => {
    mockFs({
        'source-dir': {}
    });

    t.throws(copyFiles(['no-file.txt']), /no such file or directory/);
});

function copyFiles(filenames, options) {
    return new Promise((resolve, reject) => {
        findFiles(filenames)
            .on('error', reject)
            .pipe(new CopyFileStream(dest, options))
            .on('error', reject)
            .on('finish', resolve);
    });
}

function findFiles(filenames) {
    const files = filenames.map(basename => {
        return { path: path.join(base, basename), base, cwd };
    });

    return streamify(files);
}
<commit_msg>test(copy): should copy empty files by default<commit_after>'use strict';

// Immediately invoke all dependencies,
// because of the `copy` module use `lazy-cache` (it can't work with `mock-fs`).
process.env.UNLAZY = true;

const fs = require('fs');
const path = require('path');

const test = require('ava');
const mockFs = require('mock-fs');
const streamify = require('stream-array');

const CopyFileStream = require('../../lib/streams/copy-stream');

const cwd = process.cwd();
const base = path.resolve('source-dir');
const dest = path.resolve('dest-dir');

test.afterEach(() => mockFs.restore());

test('should not copy empty dir', async t => {
    mockFs({
        'source-dir': {}
    });

    await copyFiles([]);

    const dirs = fs.readdirSync('./');

    t.deepEqual(dirs, ['source-dir']);
});

test('should copy files', async t => {
    mockFs({
        'source-dir': {
            'file-1.txt': 'Hi!',
            'file-2.txt': 'Hello!'
        }
    });

    await copyFiles(['file-1.txt', 'file-2.txt']);

    const files = fs.readdirSync(path.join(dest, 'source-dir'));

    t.deepEqual(files, ['file-1.txt', 'file-2.txt']);
});

test('should copy file with contents', async t => {
    mockFs({
        'source-dir': {
            'file-1.txt': 'Hi!'
        }
    });

    await copyFiles(['file-1.txt']);

    const str = fs.readFileSync(path.join(dest, 'source-dir', 'file-1.txt'), 'utf-8');

    t.is(str, 'Hi!');
});

test('should copy dir with subdirs', async t => {
    mockFs({
        'source-dir': {
            'sub-dir': {
                'file-1.txt': 'Hi!',
                'file-2.txt': 'Hello!'
            }
        }
    });

    await copyFiles(['sub-dir/file-1.txt', 'sub-dir/file-2.txt']);

    const files = fs.readdirSync(path.join(dest, 'source-dir', 'sub-dir'));

    t.deepEqual(files, ['file-1.txt', 'file-2.txt']);
});

test('should copy directory without files', async t => {
    mockFs({
        'source-dir': {
            'sub-dir': { 'file-2.txt': 'Hello!' }
        }
    });

    await copyFiles(['sub-dir/']);

    const files = fs.readdirSync(path.join(dest, 'source-dir'));

    t.deepEqual(files, ['sub-dir']);
});

test('should copy source file of symlink', async t => {
    mockFs({
        'file-1.txt': 'Hi!',
        'source-dir': {
            'symlink.txt': mockFs.symlink({
                path: '../file-1.txt'
            })
        }
    });

    await copyFiles(['symlink.txt']);
    fs.unlinkSync('./file-1.txt');

    const str = fs.readFileSync(path.join(dest, 'source-dir', 'symlink.txt'), 'utf-8');

    t.deepEqual(str, 'Hi!');
});

test('should ignore broken symlinks', async t => {
    mockFs({
        'source-dir': {
            'file-1.txt': 'Hi!',
            'symlink.txt': mockFs.symlink({
                path: 'no-file'
            })
        }
    });

    await copyFiles(['file-1.txt', 'symlink.txt']);

    const files = fs.readdirSync(path.join(dest, 'source-dir'));

    t.deepEqual(files, ['file-1.txt']);
});

test('should copy empty file', async t => {
    mockFs({
        'source-dir': {
            'empty-file.txt': mockFs.file(),
            'file-1.txt': 'Hi!'
        }
    });

    await copyFiles(['empty-file.txt', 'file-1.txt']);

    const files = fs.readdirSync(path.join(dest, 'source-dir'));

    t.deepEqual(files, ['empty-file.txt', 'file-1.txt']);
});

test('should ignore empty file', async t => {
    mockFs({
        'source-dir': {
            'file-1.txt': 'Hi!',
            'empty-file.txt': mockFs.file()
        }
    });

    await copyFiles(['file-1.txt', 'empty-file.txt'], { emptyFiles: false });

    const files = fs.readdirSync(path.join(dest, 'source-dir'));

    t.deepEqual(files, ['file-1.txt']);
});

test('should emit error if file file does not exist', t => {
    mockFs({
        'source-dir': {}
    });

    t.throws(copyFiles(['no-file.txt']), /no such file or directory/);
});

function copyFiles(filenames, options) {
    return new Promise((resolve, reject) => {
        findFiles(filenames)
            .on('error', reject)
            .pipe(new CopyFileStream(dest, options))
            .on('error', reject)
            .on('finish', resolve);
    });
}

function findFiles(filenames) {
    const files = filenames.map(basename => {
        return { path: path.join(base, basename), base, cwd };
    });

    return streamify(files);
}
<|endoftext|>


<commit_before>var assert = require('chai').assert,
    ltsv_to_json_stream = require('../lib/ltsv_to_json_stream');

suite('ltsv_to_json_streamのテスト', function() {

  suite('コンストラクタのテスト', function() {

    test('引数を渡さずに生成できること', function() {
      var ltjs = ltsv_to_json_stream.createLtsvToJsonStream();

      assert.strictEqual(ltjs.toObject_, false,
          'toObject_ should be false');
      assert.strictEqual(ltjs.isStrict_, false,
          'isStrict_ should be false');
    });

    test('引数を渡して生成できること', function() {
      var ltjs = ltsv_to_json_stream.createLtsvToJsonStream({
        toObject: true,
        strict: true
      });

      assert.strictEqual(ltjs.toObject_, true,
          'toObject_ should be true');
      assert.strictEqual(ltjs.isStrict_, true,
          'isStrict_ should be true');
    });

  });

  suite('destroyメソッドのテスト', function() {

    test('closeイベントが送信されること', function(done) {
      var ltjs = ltsv_to_json_stream.createLtsvToJsonStream();

      ltjs.on('close', function() {
        done();
      });

      ltjs.destroy();
    });

    test('destroyの後に各メソッドを呼ぶとerrorイベントが送信されること',
        function() {
          var ltjs = ltsv_to_json_stream.createLtsvToJsonStream(),
              errorCount = 0;

          ltjs.on('error', function(err) {
            ++errorCount;
          });
          ltjs.destroy();

          ltjs.destroy();
          ltjs.setEncoding();
          ltjs.pause();
          ltjs.resume();
          ltjs.write();
          ltjs.end();

          assert.strictEqual(errorCount, 6, 'errorCount should be 6');
        });

  });

  suite('dataイベントのテスト', function() {

    test('LTSVがJSONに変換されて渡されること', function(done) {
      var ltjs = ltsv_to_json_stream.createLtsvToJsonStream(),
          json = [];

      ltjs.on('data', function(data) {
        json.push(data);
      });
      ltjs.on('end', function() {
        assert.deepEqual(json, [
          '{"l1":"v1","l2":"v2"}',
          '{"l3":"v3","l4":"v4"}'
        ],
        'LtsvToJsonStream should be sent data events per ltsv record');
        done();
      });

      ltjs.write('l1:v1\tl2:v2');
      ltjs.end('\nl3:v3\tl4:v4');
    });

    test('LTSVがオブジェクトに変換されて渡されること', function(done) {
      var ltjs = ltsv_to_json_stream.createLtsvToJsonStream({ toObject: true }),
          json = [];

      ltjs.on('data', function(data) {
        json.push(data);
      });
      ltjs.on('end', function() {
        assert.deepEqual(json, [
          { l1: 'v1', l2: 'v2' },
          { l3: 'v3', l4: 'v4' }
        ],
        'LtsvToJsonStream should be sent data events per ltsv record');
        done();
      });

      ltjs.write('l1:v1\tl2:v2');
      ltjs.end('\nl3:v3\tl4:v4');
    });

    test('LTSVがparseLine関数で変換されていること', function(done) {
      var ltjs = ltsv_to_json_stream.createLtsvToJsonStream(),
          json = [];

      ltjs.on('data', function(data) {
        json.push(data);
      });
      ltjs.on('end', function() {
        assert.deepEqual(json, [
          '{"+++":"---"}',
          '{"***":"///"}'
        ],
        'LtsvToJsonStream should be sent data events per ltsv record');
        done();
      });

      ltjs.write('+++:---');
      ltjs.end('\n***:///');
    });

    test('LTSVがparseLineStrict関数で変換されていること', function(done) {
      var ltjs = ltsv_to_json_stream.createLtsvToJsonStream({ strict: true }),
          json = [],
          errorCount = 0;

      ltjs.on('error', function(err) {
        ++errorCount;
      });
      ltjs.on('data', function(data) {
        json.push(data);
      });
      ltjs.on('end', function() {
        assert.deepEqual(json, [],
            'LtsvToJsonStream should be sent data events per ltsv record');
        ltjs.destroy();
      });
      ltjs.on('close', function() {
        assert.strictEqual(errorCount, 2,
            'LtsvToJsonStream should be sent error events per error record');
        done();
      });

      ltjs.write('+++:---');
      ltjs.end('\n***:///');
    });

  });

});
<commit_msg>テスト失敗時のメッセージを修正<commit_after>var assert = require('chai').assert,
    ltsv_to_json_stream = require('../lib/ltsv_to_json_stream');

suite('ltsv_to_json_streamのテスト', function() {

  suite('コンストラクタのテスト', function() {

    test('引数を渡さずに生成できること', function() {
      var ltjs = ltsv_to_json_stream.createLtsvToJsonStream();

      assert.strictEqual(ltjs.toObject_, false,
          'toObject_ should be false');
      assert.strictEqual(ltjs.isStrict_, false,
          'isStrict_ should be false');
    });

    test('引数を渡して生成できること', function() {
      var ltjs = ltsv_to_json_stream.createLtsvToJsonStream({
        toObject: true,
        strict: true
      });

      assert.strictEqual(ltjs.toObject_, true,
          'toObject_ should be true');
      assert.strictEqual(ltjs.isStrict_, true,
          'isStrict_ should be true');
    });

  });

  suite('destroyメソッドのテスト', function() {

    test('closeイベントが送信されること', function(done) {
      var ltjs = ltsv_to_json_stream.createLtsvToJsonStream();

      ltjs.on('close', function() {
        done();
      });

      ltjs.destroy();
    });

    test('destroyの後に各メソッドを呼ぶとerrorイベントが送信されること',
        function() {
          var ltjs = ltsv_to_json_stream.createLtsvToJsonStream(),
              errorCount = 0;

          ltjs.on('error', function(err) {
            ++errorCount;
          });
          ltjs.destroy();

          ltjs.destroy();
          ltjs.setEncoding();
          ltjs.pause();
          ltjs.resume();
          ltjs.write();
          ltjs.end();

          assert.strictEqual(errorCount, 6, 'errorCount should be 6');
        });

  });

  suite('dataイベントのテスト', function() {

    test('LTSVがJSONに変換されて渡されること', function(done) {
      var ltjs = ltsv_to_json_stream.createLtsvToJsonStream(),
          json = [];

      ltjs.on('data', function(data) {
        json.push(data);
      });
      ltjs.on('end', function() {
        assert.deepEqual(json, [
          '{"l1":"v1","l2":"v2"}',
          '{"l3":"v3","l4":"v4"}'
        ],
        'LtsvToJsonStream should be sent data event per ltsv records');
        done();
      });

      ltjs.write('l1:v1\tl2:v2');
      ltjs.end('\nl3:v3\tl4:v4');
    });

    test('LTSVがオブジェクトに変換されて渡されること', function(done) {
      var ltjs = ltsv_to_json_stream.createLtsvToJsonStream({ toObject: true }),
          json = [];

      ltjs.on('data', function(data) {
        json.push(data);
      });
      ltjs.on('end', function() {
        assert.deepEqual(json, [
          { l1: 'v1', l2: 'v2' },
          { l3: 'v3', l4: 'v4' }
        ],
        'LtsvToJsonStream should be sent data event per ltsv records');
        done();
      });

      ltjs.write('l1:v1\tl2:v2');
      ltjs.end('\nl3:v3\tl4:v4');
    });

    test('LTSVがparseLine関数で変換されていること', function(done) {
      var ltjs = ltsv_to_json_stream.createLtsvToJsonStream(),
          json = [];

      ltjs.on('data', function(data) {
        json.push(data);
      });
      ltjs.on('end', function() {
        assert.deepEqual(json, [
          '{"+++":"---"}',
          '{"***":"///"}'
        ],
        'LtsvToJsonStream should be sent data event per ltsv records');
        done();
      });

      ltjs.write('+++:---');
      ltjs.end('\n***:///');
    });

    test('LTSVがparseLineStrict関数で変換されていること', function(done) {
      var ltjs = ltsv_to_json_stream.createLtsvToJsonStream({ strict: true }),
          json = [],
          errorCount = 0;

      ltjs.on('error', function(err) {
        ++errorCount;
      });
      ltjs.on('data', function(data) {
        json.push(data);
      });
      ltjs.on('end', function() {
        assert.deepEqual(json, [],
            'LtsvToJsonStream should be sent data event per ltsv records');
        ltjs.destroy();
      });
      ltjs.on('close', function() {
        assert.strictEqual(errorCount, 2,
            'LtsvToJsonStream should be sent error events per error record');
        done();
      });

      ltjs.write('+++:---');
      ltjs.end('\n***:///');
    });

  });

});
<|endoftext|>


<commit_before>/**
 * Selection Handles Plugin
 *
 *
 * Options
 *  show - True enables the handles plugin.
 *  drag - Left and Right drag handles
 *  scroll - Scrolling handle
 */
(function () {

function isLeftClick (e, type) {
  return (e.which ? (e.which === 1) : (e.button === 0 || e.button === 1));
}

function boundX(x, graph) {
  return Math.min(Math.max(0, x), graph.plotWidth - 1);
}

function boundY(y, graph) {
  return Math.min(Math.max(0, y), graph.plotHeight);
}

var
  D = Flotr.DOM,
  E = Flotr.EventAdapter,
  _ = Flotr._;

Flotr.addPlugin('selection', {

  options: {
    mode: null,            // => one of null, 'x', 'y' or 'xy'
    color: '#B6D9FF',      // => selection box color
    fps: 20                // => frames-per-second
  },

  callbacks: {
    'flotr:mouseup' : function (event) {
      if (!this.options.selection || !this.options.selection.mode) return;
      if (this.selection.interval) clearInterval(this.selection.interval);

      var pointer = E.eventPointer(event);
      this.selection.setSelectionPos(this.selection.selection.second, {pageX:pointer.x, pageY:pointer.y});
      this.selection.clearSelection();

      if(this.selection.selectionIsSane()){
        this.selection.drawSelection();
        this.selection.fireSelectEvent();
        this.ignoreClick = true;
      }
    },
    'flotr:mousedown' : function (event) {
      if (!this.options.selection || !this.options.selection.mode) return;
      if (!this.options.selection.mode || (!isLeftClick(event) && _.isUndefined(event.touches))) return;

      var pointer = E.eventPointer(event);

      this.selection.setSelectionPos(this.selection.selection.first, {pageX:pointer.x, pageY:pointer.y});

      if (this.selection.interval) clearInterval(this.selection.interval);

      this.lastMousePos.pageX = null;
      this.selection.interval = setInterval(
        _.bind(this.selection.updateSelection, this),
        1000/this.options.selection.fps
      );
    }
  },

  getArea: function() {

    var s = this.selection.selection,
      first = s.first,
      second = s.second;

    return {
      x1: Math.min(first.x, second.x),
      x2: Math.max(first.x, second.x),
      y1: Math.min(first.y, second.y),
      y2: Math.max(first.y, second.y)
    };
  },

  selection: {first: {x: -1, y: -1}, second: {x: -1, y: -1}},
  prevSelection: null,
  interval: null,

  /**
   * Fires the 'flotr:select' event when the user made a selection.
   */
  fireSelectEvent: function(){
    var a = this.axes,
        s = this.selection.selection,
        x1, x2, y1, y2;

    x1 = a.x.p2d(s.first.x);
    x2 = a.x.p2d(s.second.x);
    y1 = a.y.p2d(s.first.y);
    y2 = a.y.p2d(s.second.y);

    E.fire(this.el, 'flotr:select', [{
      x1:Math.min(x1, x2),
      y1:Math.min(y1, y2),
      x2:Math.max(x1, x2),
      y2:Math.max(y1, y2),
      xfirst:x1, xsecond:x2, yfirst:y1, ysecond:y2
    }, this]);
  },

  /**
   * Allows the user the manually select an area.
   * @param {Object} area - Object with coordinates to select.
   */
  setSelection: function(area, preventEvent){
    var options = this.options,
      xa = this.axes.x,
      ya = this.axes.y,
      vertScale = ya.scale,
      hozScale = xa.scale,
      selX = options.selection.mode.indexOf('x') != -1,
      selY = options.selection.mode.indexOf('y') != -1,
      s = this.selection.selection;

    this.selection.clearSelection();

    s.first.y  = boundY((selX && !selY) ? 0 : (ya.max - area.y1) * vertScale, this);
    s.second.y = boundY((selX && !selY) ? this.plotHeight - 1: (ya.max - area.y2) * vertScale, this);
    s.first.x  = boundX((selY && !selX) ? 0 : area.x1, this);
    s.second.x = boundX((selY && !selX) ? this.plotWidth : area.x2, this);

    this.selection.drawSelection();
    if (!preventEvent)
      this.selection.fireSelectEvent();
  },

  /**
   * Calculates the position of the selection.
   * @param {Object} pos - Position object.
   * @param {Event} event - Event object.
   */
  setSelectionPos: function(pos, pointer) {
    var options = this.options,
        offset = D.position(this.overlay),
        s = this.selection.selection;

    if(options.selection.mode.indexOf('x') == -1){
      pos.x = (pos == s.first) ? 0 : this.plotWidth;
    }else{
      pos.x = boundX(pointer.pageX - offset.left - this.plotOffset.left, this);
    }

    if (options.selection.mode.indexOf('y') == -1){
      pos.y = (pos == s.first) ? 0 : this.plotHeight - 1;
    }else{
      pos.y = boundY(pointer.pageY - offset.top - this.plotOffset.top, this);
    }
  },
  /**
   * Draws the selection box.
   */
  drawSelection: function() {

    var s = this.selection.selection,
      octx = this.octx,
      options = this.options,
      plotOffset = this.plotOffset,
      prevSelection = this.selection.prevSelection;

    if (prevSelection &&
      s.first.x == prevSelection.first.x &&
      s.first.y == prevSelection.first.y &&
      s.second.x == prevSelection.second.x &&
      s.second.y == prevSelection.second.y) {
      return;
    }

    octx.save();
    octx.strokeStyle = this.processColor(options.selection.color, {opacity: 0.8});
    octx.lineWidth = 1;
    octx.lineJoin = 'miter';
    octx.fillStyle = this.processColor(options.selection.color, {opacity: 0.4});

    this.selection.prevSelection = {
      first: { x: s.first.x, y: s.first.y },
      second: { x: s.second.x, y: s.second.y }
    };

    var x = Math.min(s.first.x, s.second.x),
        y = Math.min(s.first.y, s.second.y),
        w = Math.abs(s.second.x - s.first.x),
        h = Math.abs(s.second.y - s.first.y);

    octx.fillRect(x + plotOffset.left+0.5, y + plotOffset.top+0.5, w, h);
    octx.strokeRect(x + plotOffset.left+0.5, y + plotOffset.top+0.5, w, h);
    octx.restore();
  },

  /**
   * Updates (draws) the selection box.
   */
  updateSelection: function(){
    if (!this.lastMousePos.pageX) return;

    this.selection.setSelectionPos(this.selection.selection.second, this.lastMousePos);

    this.selection.clearSelection();

    if(this.selection.selectionIsSane()) this.selection.drawSelection();
  },

  /**
   * Removes the selection box from the overlay canvas.
   */
  clearSelection: function() {
    if (!this.selection.prevSelection) return;

    var prevSelection = this.selection.prevSelection,
      lw = this.octx.lineWidth,
      plotOffset = this.plotOffset,
      x = Math.min(prevSelection.first.x, prevSelection.second.x),
      y = Math.min(prevSelection.first.y, prevSelection.second.y),
      w = Math.abs(prevSelection.second.x - prevSelection.first.x),
      h = Math.abs(prevSelection.second.y - prevSelection.first.y);

    this.octx.clearRect(x + plotOffset.left - lw/2+0.5,
                        y + plotOffset.top - lw/2+0.5,
                        w + lw,
                        h + lw);

    this.selection.prevSelection = null;
  },
  /**
   * Determines whether or not the selection is sane and should be drawn.
   * @return {Boolean} - True when sane, false otherwise.
   */
  selectionIsSane: function(){
    var s = this.selection.selection;
    return Math.abs(s.second.x - s.first.x) >= 5 &&
           Math.abs(s.second.y - s.first.y) >= 5;
  }

});

})();
<commit_msg>Fire selection event on update selection.<commit_after>/**
 * Selection Handles Plugin
 *
 *
 * Options
 *  show - True enables the handles plugin.
 *  drag - Left and Right drag handles
 *  scroll - Scrolling handle
 */
(function () {

function isLeftClick (e, type) {
  return (e.which ? (e.which === 1) : (e.button === 0 || e.button === 1));
}

function boundX(x, graph) {
  return Math.min(Math.max(0, x), graph.plotWidth - 1);
}

function boundY(y, graph) {
  return Math.min(Math.max(0, y), graph.plotHeight);
}

var
  D = Flotr.DOM,
  E = Flotr.EventAdapter,
  _ = Flotr._;

Flotr.addPlugin('selection', {

  options: {
    mode: null,            // => one of null, 'x', 'y' or 'xy'
    color: '#B6D9FF',      // => selection box color
    fps: 20                // => frames-per-second
  },

  callbacks: {
    'flotr:mouseup' : function (event) {
      if (!this.options.selection || !this.options.selection.mode) return;
      if (this.selection.interval) clearInterval(this.selection.interval);

      var pointer = E.eventPointer(event);
      this.selection.setSelectionPos(this.selection.selection.second, {pageX:pointer.x, pageY:pointer.y});
      this.selection.clearSelection();

      if(this.selection.selectionIsSane()){
        this.selection.drawSelection();
        this.selection.fireSelectEvent();
        this.ignoreClick = true;
      }
    },
    'flotr:mousedown' : function (event) {
      if (!this.options.selection || !this.options.selection.mode) return;
      if (!this.options.selection.mode || (!isLeftClick(event) && _.isUndefined(event.touches))) return;

      var pointer = E.eventPointer(event);

      this.selection.setSelectionPos(this.selection.selection.first, {pageX:pointer.x, pageY:pointer.y});

      if (this.selection.interval) clearInterval(this.selection.interval);

      this.lastMousePos.pageX = null;
      this.selection.interval = setInterval(
        _.bind(this.selection.updateSelection, this),
        1000/this.options.selection.fps
      );
    }
  },

  getArea: function() {

    var s = this.selection.selection,
      first = s.first,
      second = s.second;

    return {
      x1: Math.min(first.x, second.x),
      x2: Math.max(first.x, second.x),
      y1: Math.min(first.y, second.y),
      y2: Math.max(first.y, second.y)
    };
  },

  selection: {first: {x: -1, y: -1}, second: {x: -1, y: -1}},
  prevSelection: null,
  interval: null,

  /**
   * Fires the 'flotr:select' event when the user made a selection.
   */
  fireSelectEvent: function(){
    var a = this.axes,
        s = this.selection.selection,
        x1, x2, y1, y2;

    x1 = a.x.p2d(s.first.x);
    x2 = a.x.p2d(s.second.x);
    y1 = a.y.p2d(s.first.y);
    y2 = a.y.p2d(s.second.y);

    E.fire(this.el, 'flotr:select', [{
      x1:Math.min(x1, x2),
      y1:Math.min(y1, y2),
      x2:Math.max(x1, x2),
      y2:Math.max(y1, y2),
      xfirst:x1, xsecond:x2, yfirst:y1, ysecond:y2
    }, this]);
  },

  /**
   * Allows the user the manually select an area.
   * @param {Object} area - Object with coordinates to select.
   */
  setSelection: function(area, preventEvent){
    var options = this.options,
      xa = this.axes.x,
      ya = this.axes.y,
      vertScale = ya.scale,
      hozScale = xa.scale,
      selX = options.selection.mode.indexOf('x') != -1,
      selY = options.selection.mode.indexOf('y') != -1,
      s = this.selection.selection;

    this.selection.clearSelection();

    s.first.y  = boundY((selX && !selY) ? 0 : (ya.max - area.y1) * vertScale, this);
    s.second.y = boundY((selX && !selY) ? this.plotHeight - 1: (ya.max - area.y2) * vertScale, this);
    s.first.x  = boundX((selY && !selX) ? 0 : area.x1, this);
    s.second.x = boundX((selY && !selX) ? this.plotWidth : area.x2, this);

    this.selection.drawSelection();
    if (!preventEvent)
      this.selection.fireSelectEvent();
  },

  /**
   * Calculates the position of the selection.
   * @param {Object} pos - Position object.
   * @param {Event} event - Event object.
   */
  setSelectionPos: function(pos, pointer) {
    var options = this.options,
        offset = D.position(this.overlay),
        s = this.selection.selection;

    if(options.selection.mode.indexOf('x') == -1){
      pos.x = (pos == s.first) ? 0 : this.plotWidth;
    }else{
      pos.x = boundX(pointer.pageX - offset.left - this.plotOffset.left, this);
    }

    if (options.selection.mode.indexOf('y') == -1){
      pos.y = (pos == s.first) ? 0 : this.plotHeight - 1;
    }else{
      pos.y = boundY(pointer.pageY - offset.top - this.plotOffset.top, this);
    }
  },
  /**
   * Draws the selection box.
   */
  drawSelection: function() {

    var s = this.selection.selection,
      octx = this.octx,
      options = this.options,
      plotOffset = this.plotOffset,
      prevSelection = this.selection.prevSelection;

    if (prevSelection &&
      s.first.x == prevSelection.first.x &&
      s.first.y == prevSelection.first.y &&
      s.second.x == prevSelection.second.x &&
      s.second.y == prevSelection.second.y) {
      return;
    }

    octx.save();
    octx.strokeStyle = this.processColor(options.selection.color, {opacity: 0.8});
    octx.lineWidth = 1;
    octx.lineJoin = 'miter';
    octx.fillStyle = this.processColor(options.selection.color, {opacity: 0.4});

    this.selection.prevSelection = {
      first: { x: s.first.x, y: s.first.y },
      second: { x: s.second.x, y: s.second.y }
    };

    var x = Math.min(s.first.x, s.second.x),
        y = Math.min(s.first.y, s.second.y),
        w = Math.abs(s.second.x - s.first.x),
        h = Math.abs(s.second.y - s.first.y);

    octx.fillRect(x + plotOffset.left+0.5, y + plotOffset.top+0.5, w, h);
    octx.strokeRect(x + plotOffset.left+0.5, y + plotOffset.top+0.5, w, h);
    octx.restore();
  },

  /**
   * Updates (draws) the selection box.
   */
  updateSelection: function(){
    if (!this.lastMousePos.pageX) return;

    this.selection.setSelectionPos(this.selection.selection.second, this.lastMousePos);

    this.selection.clearSelection();

    if(this.selection.selectionIsSane()) {
      this.selection.fireSelectEvent();
      this.selection.drawSelection();
    }
  },

  /**
   * Removes the selection box from the overlay canvas.
   */
  clearSelection: function() {
    if (!this.selection.prevSelection) return;

    var prevSelection = this.selection.prevSelection,
      lw = this.octx.lineWidth,
      plotOffset = this.plotOffset,
      x = Math.min(prevSelection.first.x, prevSelection.second.x),
      y = Math.min(prevSelection.first.y, prevSelection.second.y),
      w = Math.abs(prevSelection.second.x - prevSelection.first.x),
      h = Math.abs(prevSelection.second.y - prevSelection.first.y);

    this.octx.clearRect(x + plotOffset.left - lw/2+0.5,
                        y + plotOffset.top - lw/2+0.5,
                        w + lw,
                        h + lw);

    this.selection.prevSelection = null;
  },
  /**
   * Determines whether or not the selection is sane and should be drawn.
   * @return {Boolean} - True when sane, false otherwise.
   */
  selectionIsSane: function(){
    var s = this.selection.selection;
    return Math.abs(s.second.x - s.first.x) >= 5 &&
           Math.abs(s.second.y - s.first.y) >= 5;
  }

});

})();
<|endoftext|>


<commit_before><commit_msg>correlation analysis improvement..<commit_after><|endoftext|>


<commit_before>var common = require("./common.js"),
    expect = require("expect.js"),
    googleapis = require("googleapis"),
    models = require("../lib/server-models"),
    webdriver = require("selenium-webdriver"),
    request = require("superagent");

describe("CREATE HOA", function() {
    var browser;

    if (process.env.SKIP_SELENIUM_TESTS) {
        return;
    }
    this.timeout(60000); // Extra long timeout for selenium :(
    before(function(done) {
        common.getSeleniumBrowser(function(theBrowser) {
            browser = theBrowser;
            common.standardSetup(done);
        });

        //
        // Set up mocks for googleapis, so we can dance right past them.
        //

        googleapis.OAuth2Client = function() {
            this.generateAuthUrl = function() {
                return common.URL + "/event/create-hoa/callback";
            };
            this.getToken = function(code, callback) {
                return callback(null, "mock-token");
            };
        }

        googleapis.discover = function(thingy, version) {
            return {
                execute: function(callback) {
                    // Callback with a fake 'client'
                    return callback(null, {
                        youtube: {
                            channels: {
                                list: function(opts) {
                                    return {
                                        withAuthClient: function(client) {
                                            return {
                                                execute: function(callback) {
                                                    return callback(null, {items: []});
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    });
                }
            }
        };
    });
    after(function(done) {
        browser.quit().then(function() {
            common.standardShutdown(done);
        });
    });

    it("creates an hoa with event page", function(done) {
        var event = common.server.db.events.findWhere({shortName: "writers-at-work"});
        event.set("open", true);
        var user = common.server.db.users.findWhere({"sock-key": "admin1"});
        expect(user.isAdminOf(event)).to.be(true);

        var windowHandle;

        browser.get(common.URL);
        browser.mockAuthenticate(user.get("sock-key"));
        browser.get(common.URL + "/event/" + event.id);
        browser.waitForEventReady(event, user.get("sock-key"));
        browser.waitForSelector(".create-hoa");
        browser.byCss(".create-hoa").click();
        // Switch to the hangout creation window.
        // Seems that browser.getWindowHandles is not implemented.  Doing it raw.
        browser.schedule(
            new webdriver.Command(webdriver.CommandName.GET_WINDOW_HANDLES)
        ).then(function(handles) {
            browser.switchTo().window(handles[1]);
        });
        browser.waitWithTimeout(function() {
            return !!event.get("hoa");
        });
        browser.getCurrentUrl().then(function(url) {
            expect(event.get("hoa")).to.not.be(null);
            // This is the test URL -- not the URL used in production.. not
            // sure how valuable it is to test here, but at least we know that
            // the session redirect is running.
            expect(url).to.eql(
                common.URL + "/test/hangout/" + event.get("hoa").id + "/?isHoA=1"
            );
        });

        // Switch back to the event window.
        browser.schedule(
            new webdriver.Command(webdriver.CommandName.GET_WINDOW_HANDLES)
        ).then(function(handles) {
            browser.switchTo().window(handles[0]);
        });

        // Wait for the hangout broadcast video to be embedded.
        browser.waitForScript("$");
        var embedSrcScript = "return $('.video-player iframe').attr('src');";
        browser.waitWithTimeout(function() {
            return browser.executeScript(embedSrcScript).then(function(src) {
                return (
                    src && event.get("hoa") &&
                    src.indexOf("http://www.youtube.com/embed/" +
                                event.get("hoa").get("hangout-broadcast-id")) === 0
                );
            });
        });
        browser.executeScript("return $('.join-hoa').attr('href');").then(function(href) {
            expect(href).to.eql(event.get("hoa").getParticipationLink());
        });

        // Now make sure that a regular user sees the embedded video, but does
        // not see the "join current hangout" link.
        browser.mockAuthenticate("regular1");
        browser.get(common.URL + "/event/" + event.id);
        browser.waitForEventReady(event, "regular1");
        browser.byCsss(".join-hoa").then(function(els) {
            expect(els.length).to.be(0);
        });
        browser.waitForSelector("iframe");
        browser.waitWithTimeout(function() {
            return browser.executeScript(embedSrcScript).then(function(src) {
                return src !== null && src.indexOf(
                    "http://www.youtube.com/embed/" +
                    event.get("hoa").get("hangout-broadcast-id")) === 0;
            });
        }, 45000);

        browser.then(function() { done(); });
    });

    it("shows correct links for hoa statuses", function(done) {
        var event = common.server.db.events.findWhere({shortName: "writers-at-work"});
        event.set("hoa", null);
        event.set("open", true);
        var hoa;
        browser.get(common.URL);
        browser.mockAuthenticate("superuser1");

        // Get the page once -- the rest are live updates triggered by model
        // changes.
        browser.get(common.URL + event.getEventUrl());
        browser.waitForEventReady(event, "superuser1");

        function hasHoA(has) {
            browser.waitWithTimeout(function() {
                return browser.byCsss("a.create-hoa").then(function(els) {
                    return els.length === (has ? 0 : 1);
                });
            });
            return browser.waitWithTimeout(function() {
                return browser.byCsss("a.join-hoa").then(function(els) {
                    return els.length === (has ? 1 : 0);
                });
            });
        };

        hasHoA(false).then(function() {
            // Add an hoa.
            hoa = new models.ServerHoASession();
            event.set("hoa", hoa);
            hoa.markHangoutPending();
        });
        hasHoA(true).then(function() {
            hoa.set("hangout-pending", null);
        });
        hasHoA(false).then(function() {
            hoa.set("hangout-url", "http://example.com/hoa");
        });
        hasHoA(true);
        browser.byCss(".remove-hoa").click();
        hasHoA(false).then(function() {
            done();
        });
    });

    it("shows pending page for pending hoa's", function(done) {
        var event = common.server.db.events.findWhere({shortName: "writers-at-work"});
        var hoa = new models.ServerHoASession();
        var user = common.server.db.users.findWhere({"sock-key": "admin1"});
        expect(user).to.not.be(undefined);
        event.set("hoa", hoa);
        hoa.markHangoutPending(user);
        expect(hoa.getParticipationLink()).to.eql("/hoa-session/" + hoa.get("session-key"));
        request.get(common.URL + hoa.getParticipationLink())
            .set("x-mock-user", "superuser1")
            .redirects(0)
            .end(function(res) {
                expect(res.status).to.be(200);
                expect(res.text).to.contain("started creating a hangout-on-air");
                done();
            });
    });

    it("shows 'create new one' page for stale hoa's", function(done) {
        var event = common.server.db.events.findWhere({shortName: "writers-at-work"});
        var hoa = new models.ServerHoASession()
        event.set("hoa", hoa);
        // not marking it pending -- nor giving it a URL.

        expect(hoa.getParticipationLink()).to.eql("/hoa-session/" + hoa.get("session-key"));
        request.get(common.URL + hoa.getParticipationLink())
            .set("x-mock-user", "superuser1")
            .redirects(0)
            .end(function(res) {
                expect(res.status).to.be(200);
                expect(res.text).to.contain("looks like everyone has left the old");
                done();
            });
    });
});
<commit_msg>Add regression test for issue #330<commit_after>var common = require("./common.js"),
    expect = require("expect.js"),
    googleapis = require("googleapis"),
    models = require("../lib/server-models"),
    webdriver = require("selenium-webdriver"),
    request = require("superagent");

describe("CREATE HOA", function() {
    var browser;

    if (process.env.SKIP_SELENIUM_TESTS) {
        return;
    }
    this.timeout(60000); // Extra long timeout for selenium :(
    before(function(done) {
        common.getSeleniumBrowser(function(theBrowser) {
            browser = theBrowser;
            common.standardSetup(done);
        });

        //
        // Set up mocks for googleapis, so we can dance right past them.
        //

        googleapis.OAuth2Client = function() {
            this.generateAuthUrl = function() {
                return common.URL + "/event/create-hoa/callback";
            };
            this.getToken = function(code, callback) {
                return callback(null, "mock-token");
            };
        }

        googleapis.discover = function(thingy, version) {
            return {
                execute: function(callback) {
                    // Callback with a fake 'client'
                    return callback(null, {
                        youtube: {
                            channels: {
                                list: function(opts) {
                                    return {
                                        withAuthClient: function(client) {
                                            return {
                                                execute: function(callback) {
                                                    return callback(null, {items: []});
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    });
                }
            }
        };
    });
    after(function(done) {
        browser.quit().then(function() {
            common.standardShutdown(done);
        });
    });

    it("creates an hoa with event page", function(done) {
        var event = common.server.db.events.findWhere({shortName: "writers-at-work"});
        event.set("open", true);
        var user = common.server.db.users.findWhere({"sock-key": "admin1"});
        expect(user.isAdminOf(event)).to.be(true);

        var windowHandle;

        browser.get(common.URL);
        browser.mockAuthenticate(user.get("sock-key"));
        browser.get(common.URL + "/event/" + event.id);
        browser.waitForEventReady(event, user.get("sock-key"));
        browser.waitForSelector(".create-hoa");
        browser.byCss(".create-hoa").click();
        // Switch to the hangout creation window.
        // Seems that browser.getWindowHandles is not implemented.  Doing it raw.
        browser.schedule(
            new webdriver.Command(webdriver.CommandName.GET_WINDOW_HANDLES)
        ).then(function(handles) {
            browser.switchTo().window(handles[1]);
        });
        browser.waitWithTimeout(function() {
            return !!event.get("hoa");
        });
        browser.getCurrentUrl().then(function(url) {
            expect(event.get("hoa")).to.not.be(null);
            // This is the test URL -- not the URL used in production.. not
            // sure how valuable it is to test here, but at least we know that
            // the session redirect is running.
            expect(url).to.eql(
                common.URL + "/test/hangout/" + event.get("hoa").id + "/?isHoA=1"
            );
        });

        // Switch back to the event window.
        browser.schedule(
            new webdriver.Command(webdriver.CommandName.GET_WINDOW_HANDLES)
        ).then(function(handles) {
            browser.switchTo().window(handles[0]);
        });

        // Wait for the hangout broadcast video to be embedded.
        browser.waitForScript("$");
        var embedSrcScript = "return $('.video-player iframe').attr('src');";
        browser.waitWithTimeout(function() {
            return browser.executeScript(embedSrcScript).then(function(src) {
                return (
                    src && event.get("hoa") &&
                    src.indexOf("http://www.youtube.com/embed/" +
                                event.get("hoa").get("hangout-broadcast-id")) === 0
                );
            });
        });
        browser.executeScript("return $('.join-hoa').attr('href');").then(function(href) {
            expect(href).to.eql(event.get("hoa").getParticipationLink());
        });

        // Now make sure that a regular user sees the embedded video, but does
        // not see the "join current hangout" link.
        browser.mockAuthenticate("regular1");
        browser.get(common.URL + "/event/" + event.id);
        browser.waitForEventReady(event, "regular1");
        browser.byCsss(".join-hoa").then(function(els) {
            expect(els.length).to.be(0);
        });
        browser.waitForSelector("iframe");
        browser.waitWithTimeout(function() {
            return browser.executeScript(embedSrcScript).then(function(src) {
                return src !== null && src.indexOf(
                    "http://www.youtube.com/embed/" +
                    event.get("hoa").get("hangout-broadcast-id")) === 0;
            });
        }, 45000);

        browser.then(function() { done(); });
    });

    it("shows correct links for hoa statuses", function(done) {
        var event = common.server.db.events.findWhere({shortName: "writers-at-work"});
        event.set("hoa", null);
        event.set("open", true);
        var hoa;
        browser.get(common.URL);
        browser.mockAuthenticate("superuser1");

        // Get the page once -- the rest are live updates triggered by model
        // changes.
        browser.get(common.URL + event.getEventUrl());
        browser.waitForEventReady(event, "superuser1");

        function hasHoA(has) {
            browser.waitWithTimeout(function() {
                return browser.byCsss("a.create-hoa").then(function(els) {
                    return els.length === (has ? 0 : 1);
                });
            });
            return browser.waitWithTimeout(function() {
                return browser.byCsss("a.join-hoa").then(function(els) {
                    return els.length === (has ? 1 : 0);
                });
            });
        };

        hasHoA(false).then(function() {
            // Add an hoa.
            hoa = new models.ServerHoASession();
            event.set("hoa", hoa);
            hoa.markHangoutPending();
        });
        hasHoA(true).then(function() {
            hoa.set("hangout-pending", null);
        });
        hasHoA(false).then(function() {
            hoa.set("hangout-url", "http://example.com/hoa");
        });
        hasHoA(true);
        browser.byCss(".remove-hoa").click();
        hasHoA(false).then(function() {
            done();
        });
    });

    it("shows pending page for pending hoa's", function(done) {
        var event = common.server.db.events.findWhere({shortName: "writers-at-work"});
        var hoa = new models.ServerHoASession();
        var user = common.server.db.users.findWhere({"sock-key": "admin1"});
        expect(user).to.not.be(undefined);
        event.set("hoa", hoa);
        hoa.markHangoutPending(user);
        expect(hoa.getParticipationLink()).to.eql("/hoa-session/" + hoa.get("session-key"));
        request.get(common.URL + hoa.getParticipationLink())
            .set("x-mock-user", "superuser1")
            .redirects(0)
            .end(function(res) {
                expect(res.status).to.be(200);
                expect(res.text).to.contain("started creating a hangout-on-air");
                done();
            });
    });

    it("shows 'create new one' page for stale hoa's", function(done) {
        var event = common.server.db.events.findWhere({shortName: "writers-at-work"});
        var hoa = new models.ServerHoASession()
        event.set("hoa", hoa);
        // not marking it pending -- nor giving it a URL.

        expect(hoa.getParticipationLink()).to.eql("/hoa-session/" + hoa.get("session-key"));
        request.get(common.URL + hoa.getParticipationLink())
            .set("x-mock-user", "superuser1")
            .redirects(0)
            .end(function(res) {
                expect(res.status).to.be(200);
                expect(res.text).to.contain("looks like everyone has left the old");
                done();
            });
    });

    it("Doesn't error when creating a new hoa for events with a stale one (issue #330)", function(done) {
        var event = common.server.db.events.findWhere({shortName: "writers-at-work"});
        var hoa = new models.ServerHoASession()
        hoa.event = event;
        event.set("hoa", hoa);
        hoa.save({}, {
            success: function() {
                request.get(common.URL + "/event/" + event.id + "/create-hoa/")
                    .set("x-mock-user", "superuser1")
                    .end(function(res) {
                        expect(event.get("hoa").id).to.not.eql(hoa.id);
                        expect(res.status).to.be(200);
                        done();
                    });
            }
        });
    });
});
<|endoftext|>


<commit_before>describe( "windowManager", function() {

    it( "should exist as jQuery namespace", function() {

        assert.isFunction( $.fn.windowManager, "no windowManager plugin in jQuery object" );

    } );

    it( "should have a method for appointing an element as mainframe parent", function() {

    	assert.lengthOf( $( '#window_manager' ).windowManager(), 1, "no method for initializing the windowManager" );

    } );

} );<commit_msg>tests for initialization an window creation<commit_after>describe( "windowManager", function() {

    it( "should exist as jQuery namespace",

    function() {

        assert.isFunction(

        	$.fn.windowManager,

        	"no windowManager plugin in jQuery object" );

    } );

    it( "should have a method for appointing an element as mainframe parent",

    function() {

    	assert.lengthOf(

    		$( '#window_manager' )
    			.windowManager(),

    		1,

    		"no method for initializing the windowManager" );

    } );

    it( "should insert a root HTML element",

    function() {

        assert.lengthOf(

        	$( '#window_manager' )
        		.windowManager()
        		.find( '.__window__manager__root__' ),

        	1,

        	"no root element found" );

    } );

    it( "should display a new window when 'open_window' is called",

    function() {

    	var
    		winman =
    			$( '#window_manager' )
    				.windowManager();

    	assert.lengthOf(

    		winman
    			.windowManager( 'open_window' )
    			.find( '.__window__manager__root__' )
    			.children(),

    		1,

    		"expected windowManager to have one child window" );

    } );

    it( "should give new window an unique ID",

    function() {

    	var
    		winman =
    			$( '#window_manager' )
    				.windowManager()
    				.windowManager( 'open_window' );

        assert.match(

            winman
            	.find( '.window' )
            	.attr( 'id' ),

            /^win_[0-9a-f]+/,

            "new window did not have a correctly formatted ID" );

    } );

    it( "should size the new window according to the settings",

    function() {

    	var
    		winman =
    			$( '#window_manager' )
    				.windowManager(),

    		wndclass = {
    			width: 250,
    			height: 250
    		};

    	assert.equal(

    		winman
    			.windowManager( 'open_window', wndclass )
    			.find( '.__window__manager__root__' )
    			.children( '.window' )
    			.width(),

    		250,

    		"wrong width of new window" );

    } );

} );<|endoftext|>


<commit_before>import widgets from 'jupyter-js-widgets'
import _ from 'underscore'

import GoogleMapsLoader from 'google-maps'

function reloadGoogleMaps(configuration) {
    GoogleMapsLoader.release();
    GoogleMapsLoader.LIBRARIES = ["visualization"] ;
    if (configuration["api_key"] !== null &&
        configuration["api_key"] !== undefined) {
            GoogleMapsLoader.KEY = configuration["api_key"];
    };
}

reloadGoogleMaps({}) ;

function gPointToList(gpoint) {
    return [gpoint.lat(), gpoint.lng()]
}

function gBoundsToList(gbounds) {
    const sw = gPointToList(gbounds.getSouthWest())
    const ne = gPointToList(gbounds.getNorthEast())
    return [sw, ne]
}

// Mixins

const ConfigurationMixin = {
    loadConfiguration() {
        const modelConfiguration = this.model.get("configuration")
        reloadGoogleMaps(modelConfiguration)
    }
}

// Views

const GMapsLayerView = widgets.WidgetView.extend({
    initialize(parameters) {
        GMapsLayerView.__super__.initialize.apply(this, arguments)
        this.mapView = this.options.mapView
    }
})

export const DirectionsLayerView = GMapsLayerView.extend({
    render() {
        const rendererOptions = { map: this.mapView.map }

        this.directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions)

        const modelData = this.model.get("data");

        const request = {
            origin: this.getOrigin(modelData),
            destination: this.getDestination(modelData),
            waypoints: this.getWaypoints(modelData),
            travelMode: google.maps.TravelMode.DRIVING
        };

        const directionsService = new google.maps.DirectionsService();

        directionsService.route(request, (response, status) => {
            // print to the browser console (mostly for debugging)
            console.log(`Direction service returned: ${status}`) ;
            // set a flag in the model
            this.model.set("layer_status", status) ;
            this.touch() ; // push `layer_status` changes to the model
            if (status == google.maps.DirectionsStatus.OK) {
                this.response = this.directionsDisplay ;
                this.directionsDisplay.setDirections(response);
            }
        });
    },

    addToMapView(mapView) { },

    getOrigin(modelData) {
        const [lat, lng] = _.first(modelData)
        return new google.maps.LatLng(lat, lng)
    },

    getDestination(modelData) {
        const [lat, lng] = _.last(modelData)
        return new google.maps.LatLng(lat, lng)
    },

    getWaypoints(modelData) {
        const withoutFirst = _.tail(modelData)
        const withoutLast = _.initial(withoutFirst)
        const dataAsGoogle = withoutLast.map(([lat, lng]) => {
            return {location: new google.maps.LatLng(lat, lng)}
        })
        return dataAsGoogle
    }
})

const HeatmapLayerBaseView = GMapsLayerView.extend({
    render() {
        this.modelEvents() ;
        GoogleMapsLoader.load((google) => {
            this.heatmap = new google.maps.visualization.HeatmapLayer({
                data: this.getData(),
                radius: this.model.get("point_radius"),
                maxIntensity: this.model.get("max_intensity"),
                dissipating: this.model.get("dissipating"),
                opacity: this.model.get("opacity"),
                gradient: this.model.get("gradient")
            }) ;
        });
    },

    addToMapView(mapView) {
        this.heatmap.setMap(mapView.map)
    },

    modelEvents() {
        // Simple properties:
        // [nameInView, nameInModel]
        const properties = [
            ['maxIntensity', 'max_intensity'],
            ['opacity', 'opacity'],
            ['radius', 'point_radius'],
            ['dissipating', 'dissipating'],
            ['gradient', 'gradient']
        ]
        properties.forEach(([nameInView, nameInModel]) => {
            const callback = (
                () => this.heatmap.set(nameInView, this.model.get(nameInModel))
            )
            this.model.on(`change:${nameInModel}`, callback, this)
        })
    },

    get_data() {},

})

export const SimpleHeatmapLayerView = HeatmapLayerBaseView.extend({
    getData() {
        const data = this.model.get("data")
        const dataAsGoogle = new google.maps.MVCArray(
            data.map(([lat, lng]) => new google.maps.LatLng(lat, lng))
        )
        return dataAsGoogle
    }
})

export const WeightedHeatmapLayerView = HeatmapLayerBaseView.extend({
    getData() {
        const data = this.model.get("data")
        const dataAsGoogle = new google.maps.MVCArray(
            data.map(([lat, lng, weight]) => {
                const location = new google.maps.LatLng(lat, lng)
                return { location: location, weight: weight }
            })
        );
        return dataAsGoogle
    }
})

/* Base class for markers.
 * This sets options common to the different types of markers.
 *
 * Subclasses are responsible for implementing the `getStyleOptions`
 * method, which must return an object of additional options
 * to add to the marker, and `setStyleEvents`, which must set
 * up events for those styles.
 */
export const BaseMarkerView = widgets.WidgetView.extend({
    render() {
        const [lat, lng] = this.model.get("location")
        const title = this.model.get("hover_text")
        const infoBoxHtml = this.model.get("info_html")
        const styleOptions = this.getStyleOptions()
        const markerOptions = {
            position: {lat, lng},
            draggable: false,
            title,
            ...styleOptions
        }
        this.marker = new google.maps.Marker(markerOptions)

        this.infoWindow=new google.maps.InfoWindow({
            content: infoBoxHtml
        });
        this.modelEvents()
    },

    addToMapView(mapView) {
        let marker= this.marker;
        let infoWindow=this.infoWindow;
        marker.setMap(mapView.map);
        marker.addListener('click', function() {
            infoWindow.open(mapView.map, marker);
        });
    },

    modelEvents() {
        // Simple properties:
        const properties = [
            ['title', 'hover_text','info_html']
        ]
        properties.forEach(([nameInView, nameInModel]) => {
            const callback = (
                () => {
                  this.marker.set(
                  nameInView, this.model.get(nameInModel))
                }
            )
            this.model.on(`change:${nameInModel}`, callback, this)
        })

        this.setStyleEvents()
    }

})

export const SymbolView = BaseMarkerView.extend({

    getStyleOptions() {
        const fillColor = this.model.get("fill_color")
        const strokeColor = this.model.get("stroke_color")
        const fillOpacity = this.model.get("fill_opacity")
        const strokeOpacity = this.model.get("stroke_opacity")
        const scale = this.model.get("scale")
        return {
            icon: {
                path: google.maps.SymbolPath.CIRCLE,
                scale,
                fillColor,
                strokeColor,
                fillOpacity,
                strokeOpacity
            }
        }
    },

    setStyleEvents() {
        const iconProperties = [
            ['strokeColor', 'stroke_color'],
            ['fillColor', 'fill_color'],
            ['scale', 'scale'],
            ['stroke_opacity', 'stroke_opacity'],
            ['fillOpacity', 'fill_opacity']
        ]
        iconProperties.forEach(([nameInView, nameInModel]) => {
            const callback = ( () => {
                const newIcon = Object.assign({}, this.marker.getIcon())
                newIcon[nameInView] = this.model.get(nameInModel)
                this.marker.setIcon(newIcon)
            })
            this.model.on(`change:${nameInModel}`, callback, this)
        })
    }
})

export const MarkerView = BaseMarkerView.extend({

    getStyleOptions() {
        this.modelEvents()
        const label = this.model.get("label")
        return { label }
    },

    setStyleEvents() {
        const properties = [
            ['label', 'label']
        ]
        properties.forEach(([nameInView, nameInModel]) => {
            const callback = (
                () => {
                  this.marker.set(
                  nameInView, this.model.get(nameInModel))
                }
            )
            this.model.on(`change:${nameInModel}`, callback, this)
        })
    }

})

export const MarkerLayerView = GMapsLayerView.extend({
    render() {
        this.markerViews = new widgets.ViewList(this.addMarker, null, this)
        this.markerViews.update(this.model.get("markers"))
    },

    addToMapView(mapView) {
        this.markerViews.forEach(view => view.addToMapView(mapView))
    },

    addMarker(childModel) {
        return this.create_child_view(childModel)
            .then((childView) => {
                childView.addToMapView(this.mapView)
                return childView
            })
    }
})

export const PlainmapView = widgets.DOMWidgetView.extend({
    render() {
        this.loadConfiguration();
        this.el.style["width"] = this.model.get("width");
        this.el.style["height"] = this.model.get("height");

        const initialBounds = this.model.get("data_bounds");

        this.layerViews = new widgets.ViewList(this.addLayerModel, null, this);
        this.modelEvents() ;

        this.on("displayed", () => {
            GoogleMapsLoader.load((google) => {
                this.map = new google.maps.Map(this.el) ;
                this.updateBounds(initialBounds);

                this.layerViews.update(this.model.get("layers"));

                // hack to force the map to redraw
                setTimeout(() => {
                    google.maps.event.trigger(this.map, 'resize') ;
                    this.updateBounds(initialBounds);
                }, 500);
            })
        })
    },

    modelEvents() {
        this.model.on("change:data_bounds", this.updateBounds, this);
    },

    updateBounds() {
        const [[latBL, lngBL], [latTR, lngTR]] = this.model.get("data_bounds")
        const boundBL = new google.maps.LatLng(latBL, lngBL)
        const boundTR = new google.maps.LatLng(latTR, lngTR)
        const bounds = new google.maps.LatLngBounds(boundBL, boundTR)
        this.map.fitBounds(bounds);
    },

    addLayerModel(childModel) {
        return this.create_child_view(
            childModel, {mapView: this}
        ).then((childView) => {
            childView.addToMapView(this) ;
            return childView;
        })
    },

})

_.extend(PlainmapView.prototype, ConfigurationMixin);

// Models

export const GMapsLayerModel = widgets.WidgetModel.extend({
    defaults: _.extend({}, widgets.WidgetModel.prototype.defaults, {
        _view_name : 'GMapsLayerView',
        _model_name : 'GMapsLayerModel',
        _view_module : 'jupyter-gmaps',
        _model_module : 'jupyter-gmaps'
    })
});

export const DirectionsLayerModel = GMapsLayerModel.extend({
    defaults: _.extend({}, GMapsLayerModel.prototype.defaults, {
        _view_name: "DirectionsLayerView",
        _model_name: "DirectionsLayerModel"
    })
});

export const SimpleHeatmapLayerModel = GMapsLayerModel.extend({
    defaults: _.extend({}, GMapsLayerModel.prototype.defaults, {
        _view_name: "SimpleHeatmapLayerView",
        _model_name: "SimpleHeatmapLayerModel"
    })
});

export const WeightedHeatmapLayerModel = GMapsLayerModel.extend({
    defaults: _.extend({}, GMapsLayerModel.prototype.defaults, {
        _view_name: "WeightedHeatmapLayerView",
        _model_name: "WeightedHeatmapLayerModel"
    })
});

export const SymbolModel = GMapsLayerModel.extend({
    defaults: _.extend({}, GMapsLayerModel.prototype.defaults, {
        _view_name: "SymbolView",
        _model_name: "SymbolModel"
    })
})

export const MarkerModel = GMapsLayerModel.extend({
    defaults: _.extend({}, GMapsLayerModel.prototype.defaults, {
        _view_name: "MarkerView",
        _model_name: "MarkerModel"
    })
})

export const MarkerLayerModel = GMapsLayerModel.extend({
    defaults: _.extend({}, GMapsLayerModel.prototype.defaults, {
        _view_name: "MarkerLayerView",
        _model_name: "MarkerLayerModel"
    }),
}, {
    serializers: _.extend({
            markers: {deserialize: widgets.unpack_models}
    }, widgets.DOMWidgetModel.serializers)
})

export const PlainmapModel = widgets.DOMWidgetModel.extend({
    defaults: _.extend({}, widgets.DOMWidgetModel.prototype.defaults, {
        _view_name: "PlainmapView",
        _model_name: "PlainmapModel",
        _view_module : 'jupyter-gmaps',
        _model_module : 'jupyter-gmaps',
        width: "600px",
        height: "400px"

    })
}, {
    serializers: _.extend({
            layers: {deserialize: widgets.unpack_models}
    }, widgets.DOMWidgetModel.serializers)
});
<commit_msg>Fix: Inserts spaces after and before each equal signs<commit_after>import widgets from 'jupyter-js-widgets'
import _ from 'underscore'

import GoogleMapsLoader from 'google-maps'

function reloadGoogleMaps(configuration) {
    GoogleMapsLoader.release();
    GoogleMapsLoader.LIBRARIES = ["visualization"] ;
    if (configuration["api_key"] !== null &&
        configuration["api_key"] !== undefined) {
            GoogleMapsLoader.KEY = configuration["api_key"];
    };
}

reloadGoogleMaps({}) ;

function gPointToList(gpoint) {
    return [gpoint.lat(), gpoint.lng()]
}

function gBoundsToList(gbounds) {
    const sw = gPointToList(gbounds.getSouthWest())
    const ne = gPointToList(gbounds.getNorthEast())
    return [sw, ne]
}

// Mixins

const ConfigurationMixin = {
    loadConfiguration() {
        const modelConfiguration = this.model.get("configuration")
        reloadGoogleMaps(modelConfiguration)
    }
}

// Views

const GMapsLayerView = widgets.WidgetView.extend({
    initialize(parameters) {
        GMapsLayerView.__super__.initialize.apply(this, arguments)
        this.mapView = this.options.mapView
    }
})

export const DirectionsLayerView = GMapsLayerView.extend({
    render() {
        const rendererOptions = { map: this.mapView.map }

        this.directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions)

        const modelData = this.model.get("data");

        const request = {
            origin: this.getOrigin(modelData),
            destination: this.getDestination(modelData),
            waypoints: this.getWaypoints(modelData),
            travelMode: google.maps.TravelMode.DRIVING
        };

        const directionsService = new google.maps.DirectionsService();

        directionsService.route(request, (response, status) => {
            // print to the browser console (mostly for debugging)
            console.log(`Direction service returned: ${status}`) ;
            // set a flag in the model
            this.model.set("layer_status", status) ;
            this.touch() ; // push `layer_status` changes to the model
            if (status == google.maps.DirectionsStatus.OK) {
                this.response = this.directionsDisplay ;
                this.directionsDisplay.setDirections(response);
            }
        });
    },

    addToMapView(mapView) { },

    getOrigin(modelData) {
        const [lat, lng] = _.first(modelData)
        return new google.maps.LatLng(lat, lng)
    },

    getDestination(modelData) {
        const [lat, lng] = _.last(modelData)
        return new google.maps.LatLng(lat, lng)
    },

    getWaypoints(modelData) {
        const withoutFirst = _.tail(modelData)
        const withoutLast = _.initial(withoutFirst)
        const dataAsGoogle = withoutLast.map(([lat, lng]) => {
            return {location: new google.maps.LatLng(lat, lng)}
        })
        return dataAsGoogle
    }
})

const HeatmapLayerBaseView = GMapsLayerView.extend({
    render() {
        this.modelEvents() ;
        GoogleMapsLoader.load((google) => {
            this.heatmap = new google.maps.visualization.HeatmapLayer({
                data: this.getData(),
                radius: this.model.get("point_radius"),
                maxIntensity: this.model.get("max_intensity"),
                dissipating: this.model.get("dissipating"),
                opacity: this.model.get("opacity"),
                gradient: this.model.get("gradient")
            }) ;
        });
    },

    addToMapView(mapView) {
        this.heatmap.setMap(mapView.map)
    },

    modelEvents() {
        // Simple properties:
        // [nameInView, nameInModel]
        const properties = [
            ['maxIntensity', 'max_intensity'],
            ['opacity', 'opacity'],
            ['radius', 'point_radius'],
            ['dissipating', 'dissipating'],
            ['gradient', 'gradient']
        ]
        properties.forEach(([nameInView, nameInModel]) => {
            const callback = (
                () => this.heatmap.set(nameInView, this.model.get(nameInModel))
            )
            this.model.on(`change:${nameInModel}`, callback, this)
        })
    },

    get_data() {},

})

export const SimpleHeatmapLayerView = HeatmapLayerBaseView.extend({
    getData() {
        const data = this.model.get("data")
        const dataAsGoogle = new google.maps.MVCArray(
            data.map(([lat, lng]) => new google.maps.LatLng(lat, lng))
        )
        return dataAsGoogle
    }
})

export const WeightedHeatmapLayerView = HeatmapLayerBaseView.extend({
    getData() {
        const data = this.model.get("data")
        const dataAsGoogle = new google.maps.MVCArray(
            data.map(([lat, lng, weight]) => {
                const location = new google.maps.LatLng(lat, lng)
                return { location: location, weight: weight }
            })
        );
        return dataAsGoogle
    }
})

/* Base class for markers.
 * This sets options common to the different types of markers.
 *
 * Subclasses are responsible for implementing the `getStyleOptions`
 * method, which must return an object of additional options
 * to add to the marker, and `setStyleEvents`, which must set
 * up events for those styles.
 */
export const BaseMarkerView = widgets.WidgetView.extend({
    render() {
        const [lat, lng] = this.model.get("location")
        const title = this.model.get("hover_text")
        const infoBoxHtml = this.model.get("info_html")
        const styleOptions = this.getStyleOptions()
        const markerOptions = {
            position: {lat, lng},
            draggable: false,
            title,
            ...styleOptions
        }
        this.marker = new google.maps.Marker(markerOptions)

        this.infoWindow = new google.maps.InfoWindow({
            content: infoBoxHtml
        });
        this.modelEvents()
    },

    addToMapView(mapView) {
        let marker = this.marker;
        let infoWindow = this.infoWindow;
        marker.setMap(mapView.map);
        marker.addListener('click', function() {
            infoWindow.open(mapView.map, marker);
        });
    },

    modelEvents() {
        // Simple properties:
        const properties = [
            ['title', 'hover_text', 'info_html']
        ]
        properties.forEach(([nameInView, nameInModel]) => {
            const callback = (
                () => {
                  this.marker.set(
                  nameInView, this.model.get(nameInModel))
                }
            )
            this.model.on(`change:${nameInModel}`, callback, this)
        })

        this.setStyleEvents()
    }

})

export const SymbolView = BaseMarkerView.extend({

    getStyleOptions() {
        const fillColor = this.model.get("fill_color")
        const strokeColor = this.model.get("stroke_color")
        const fillOpacity = this.model.get("fill_opacity")
        const strokeOpacity = this.model.get("stroke_opacity")
        const scale = this.model.get("scale")
        return {
            icon: {
                path: google.maps.SymbolPath.CIRCLE,
                scale,
                fillColor,
                strokeColor,
                fillOpacity,
                strokeOpacity
            }
        }
    },

    setStyleEvents() {
        const iconProperties = [
            ['strokeColor', 'stroke_color'],
            ['fillColor', 'fill_color'],
            ['scale', 'scale'],
            ['stroke_opacity', 'stroke_opacity'],
            ['fillOpacity', 'fill_opacity']
        ]
        iconProperties.forEach(([nameInView, nameInModel]) => {
            const callback = ( () => {
                const newIcon = Object.assign({}, this.marker.getIcon())
                newIcon[nameInView] = this.model.get(nameInModel)
                this.marker.setIcon(newIcon)
            })
            this.model.on(`change:${nameInModel}`, callback, this)
        })
    }
})

export const MarkerView = BaseMarkerView.extend({

    getStyleOptions() {
        this.modelEvents()
        const label = this.model.get("label")
        return { label }
    },

    setStyleEvents() {
        const properties = [
            ['label', 'label']
        ]
        properties.forEach(([nameInView, nameInModel]) => {
            const callback = (
                () => {
                  this.marker.set(
                  nameInView, this.model.get(nameInModel))
                }
            )
            this.model.on(`change:${nameInModel}`, callback, this)
        })
    }

})

export const MarkerLayerView = GMapsLayerView.extend({
    render() {
        this.markerViews = new widgets.ViewList(this.addMarker, null, this)
        this.markerViews.update(this.model.get("markers"))
    },

    addToMapView(mapView) {
        this.markerViews.forEach(view => view.addToMapView(mapView))
    },

    addMarker(childModel) {
        return this.create_child_view(childModel)
            .then((childView) => {
                childView.addToMapView(this.mapView)
                return childView
            })
    }
})

export const PlainmapView = widgets.DOMWidgetView.extend({
    render() {
        this.loadConfiguration();
        this.el.style["width"] = this.model.get("width");
        this.el.style["height"] = this.model.get("height");

        const initialBounds = this.model.get("data_bounds");

        this.layerViews = new widgets.ViewList(this.addLayerModel, null, this);
        this.modelEvents() ;

        this.on("displayed", () => {
            GoogleMapsLoader.load((google) => {
                this.map = new google.maps.Map(this.el) ;
                this.updateBounds(initialBounds);

                this.layerViews.update(this.model.get("layers"));

                // hack to force the map to redraw
                setTimeout(() => {
                    google.maps.event.trigger(this.map, 'resize') ;
                    this.updateBounds(initialBounds);
                }, 500);
            })
        })
    },

    modelEvents() {
        this.model.on("change:data_bounds", this.updateBounds, this);
    },

    updateBounds() {
        const [[latBL, lngBL], [latTR, lngTR]] = this.model.get("data_bounds")
        const boundBL = new google.maps.LatLng(latBL, lngBL)
        const boundTR = new google.maps.LatLng(latTR, lngTR)
        const bounds = new google.maps.LatLngBounds(boundBL, boundTR)
        this.map.fitBounds(bounds);
    },

    addLayerModel(childModel) {
        return this.create_child_view(
            childModel, {mapView: this}
        ).then((childView) => {
            childView.addToMapView(this) ;
            return childView;
        })
    },

})

_.extend(PlainmapView.prototype, ConfigurationMixin);

// Models

export const GMapsLayerModel = widgets.WidgetModel.extend({
    defaults: _.extend({}, widgets.WidgetModel.prototype.defaults, {
        _view_name : 'GMapsLayerView',
        _model_name : 'GMapsLayerModel',
        _view_module : 'jupyter-gmaps',
        _model_module : 'jupyter-gmaps'
    })
});

export const DirectionsLayerModel = GMapsLayerModel.extend({
    defaults: _.extend({}, GMapsLayerModel.prototype.defaults, {
        _view_name: "DirectionsLayerView",
        _model_name: "DirectionsLayerModel"
    })
});

export const SimpleHeatmapLayerModel = GMapsLayerModel.extend({
    defaults: _.extend({}, GMapsLayerModel.prototype.defaults, {
        _view_name: "SimpleHeatmapLayerView",
        _model_name: "SimpleHeatmapLayerModel"
    })
});

export const WeightedHeatmapLayerModel = GMapsLayerModel.extend({
    defaults: _.extend({}, GMapsLayerModel.prototype.defaults, {
        _view_name: "WeightedHeatmapLayerView",
        _model_name: "WeightedHeatmapLayerModel"
    })
});

export const SymbolModel = GMapsLayerModel.extend({
    defaults: _.extend({}, GMapsLayerModel.prototype.defaults, {
        _view_name: "SymbolView",
        _model_name: "SymbolModel"
    })
})

export const MarkerModel = GMapsLayerModel.extend({
    defaults: _.extend({}, GMapsLayerModel.prototype.defaults, {
        _view_name: "MarkerView",
        _model_name: "MarkerModel"
    })
})

export const MarkerLayerModel = GMapsLayerModel.extend({
    defaults: _.extend({}, GMapsLayerModel.prototype.defaults, {
        _view_name: "MarkerLayerView",
        _model_name: "MarkerLayerModel"
    }),
}, {
    serializers: _.extend({
            markers: {deserialize: widgets.unpack_models}
    }, widgets.DOMWidgetModel.serializers)
})

export const PlainmapModel = widgets.DOMWidgetModel.extend({
    defaults: _.extend({}, widgets.DOMWidgetModel.prototype.defaults, {
        _view_name: "PlainmapView",
        _model_name: "PlainmapModel",
        _view_module : 'jupyter-gmaps',
        _model_module : 'jupyter-gmaps',
        width: "600px",
        height: "400px"

    })
}, {
    serializers: _.extend({
            layers: {deserialize: widgets.unpack_models}
    }, widgets.DOMWidgetModel.serializers)
});
<|endoftext|>