<commit_before>require('../support/test_helper');

const assert = require('../support/assert');
const mapnik = require('mapnik');
const TestClient = require('../support/test_client');
const INCOMPATIBLE_LAYERS_ERROR =
'The layergroup contains incompatible layers: don\'t mix styled layers with non styled layers (without cartocss)';

describe('vector aggregation', function () {
    it('should create a single layer map w/o cartocss', function (done) {
        this.testClient = new TestClient({
            version: '1.6.0',
            layers: [
                {
                    type: 'cartodb',
                    options: {
                        sql: 'select st_setsrid(st_makepoint(0, 0), 4326) as the_geom'
                    }
                }
            ]
        });

        this.testClient.createLayergroup((err, layergroup) => {
            if (err) {
                return done(err);
            }

            assert.ok(layergroup);
            assert.ok(layergroup.layergroupid);

            done();
        });
    });

    it('should fail whether layergroup cantains incompatible layers', function (done) {
        this.testClient = new TestClient({
            version: '1.6.0',
            layers: [
                {
                    type: 'cartodb',
                    options: {
                        sql: 'select st_setsrid(st_makepoint(0, 0), 4326) as the_geom'
                    }
                },
                {
                    type: 'cartodb',
                    options: {
                        sql: 'select st_setsrid(st_makepoint(0, 0), 4326) as the_geom',
                        cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
                        cartocss_version: '2.0.2',
                    }
                }
            ]
        });

        this.testClient.createLayergroup((err) => {
            assert.ok(err);
            assert.equal(INCOMPATIBLE_LAYERS_ERROR, err.message);
            done();
        });
    });

    it('should get tile 0/0/0 for a vector layergroup', function (done) {
        this.testClient = new TestClient({
            version: '1.6.0',
            layers: [
                {
                    type: 'cartodb',
                    options: {
                        sql: 'select st_setsrid(st_makepoint(0, 0), 4326) as the_geom'
                    }
                }
            ]
        });

        this.testClient.getTile(0, 0, 0, (err, mvtTile) => {
            if (err) {
                return done(err);
            }

            var vtile = new mapnik.VectorTile(0, 0, 0);
            vtile.setData(mvtTile);
            assert.equal(vtile.painted(), true);
            assert.equal(vtile.empty(), false);
        });
    });
});
<commit_msg>Fix test to fetch a vector tile without cartocss<commit_after>require('../support/test_helper');

const assert = require('../support/assert');
const mapnik = require('mapnik');
const TestClient = require('../support/test_client');
const INCOMPATIBLE_LAYERS_ERROR =
'The layergroup contains incompatible layers: don\'t mix styled layers with non styled layers (without cartocss)';

describe('vector aggregation', function () {
    it('should create a single layer map w/o cartocss', function (done) {
        this.testClient = new TestClient({
            version: '1.6.0',
            layers: [
                {
                    type: 'cartodb',
                    options: {
                        sql: 'select st_setsrid(st_makepoint(0, 0), 4326) as the_geom'
                    }
                }
            ]
        });

        this.testClient.createLayergroup((err, layergroup) => {
            if (err) {
                return done(err);
            }

            assert.ok(layergroup);
            assert.ok(layergroup.layergroupid);

            done();
        });
    });

    it('should fail whether layergroup cantains incompatible layers', function (done) {
        this.testClient = new TestClient({
            version: '1.6.0',
            layers: [
                {
                    type: 'cartodb',
                    options: {
                        sql: 'select st_setsrid(st_makepoint(0, 0), 4326) as the_geom'
                    }
                },
                {
                    type: 'cartodb',
                    options: {
                        sql: 'select st_setsrid(st_makepoint(0, 0), 4326) as the_geom',
                        cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
                        cartocss_version: '2.0.2',
                    }
                }
            ]
        });

        this.testClient.createLayergroup((err) => {
            assert.ok(err);
            assert.equal(INCOMPATIBLE_LAYERS_ERROR, err.message);
            done();
        });
    });

    it('should get tile 0/0/0 for a vector layergroup', function (done) {
        this.testClient = new TestClient({
            version: '1.6.0',
            layers: [
                {
                    type: 'cartodb',
                    options: {
                        sql: 'select st_setsrid(st_makepoint(0, 0), 4326) as the_geom'
                    }
                }
            ]
        });

        this.testClient.getTile(0, 0, 0, { format: 'mvt' }, (err, mvtTile) => {
            if (err) {
                return done(err);
            }

            var vtile = new mapnik.VectorTile(0, 0, 0);
            vtile.setData(mvtTile);
            assert.equal(vtile.painted(), true);
            assert.equal(vtile.empty(), false);
            done();
        });
    });
});
<|endoftext|>


<commit_before>
var expect = require('chai').expect;

// still undecided on how the mixin gets dependencies
_ = require('yobo')._;

var Backbone = require('yobo').Backbone;

var BackbonePolymerAttach = require('../backbone-polymer-mixin');

var PolymerElementMock = function() {

  var spliced = this.spliced = [];

  this.splice = function(path, index, removeCount, items) {
    spliced.push({path: path, index: index, removeCount: removeCount, items: items});
  };

  this.notifyPath = function() {
    console.log('Polymer got notifyPath', arguments);
  };

};

describe("Array modification through Polymer splices, emulate backbone events", function() {

  describe("Backbone events before backbone-polymer", function() {

    it("add model", function() {
      var c = new Backbone.Collection();
      var m = new Backbone.Model({id: 'a1', type: 'testmodel'})
      c.on('add', function(model, collection, options) {
        console.log('add', m === model ? '(model)' : model, c === collection ? '(collection)' : collection, JSON.stringify(options));
      });
      c.add(m);
    });

    it("add model at index", function() {
      var c = new Backbone.Collection();
      c.add({id: 'a2', type: 'testmodel2'});
      c.add({id: 'a3', type: 'testmodel3'});
      var m = new Backbone.Model({id: 'a1', type: 'testmodel'})
      c.on('add', function(model, collection, options) {
        console.log('add', m === model ? '(model)' : model, c === collection ? '(collection)' : collection, JSON.stringify(options));
      });
      var added = c.add(m, {at: 1});
      console.log('add returned', m === added ? '(model)' : model);
    });

  });

  describe("#add", function() {

    it("ATM accepts only a single item", function() {
      expect(function() {
        var c = new Backbone.Collection();
        BackbonePolymerAttach.call(c, new PolymerElementMock(), 'edit.units');
        c.add([]);
      }).to.throw(/single/);
    });

    it("ATM requires that item to be a real model", function() {
      expect(function() {
        var c = new Backbone.Collection();
        BackbonePolymerAttach.call(c, new PolymerElementMock(), 'edit.units');
        c.add({id: 'add1', type: 'test'});
      }).to.throw(/requires model instance/);
    });

    it("Is transparent to backbone add event listener", function() {
      var e = new PolymerElementMock();
      var c = new Backbone.Collection();
      var adds = [];

      c.on('add', function() { adds.push(arguments); });
      BackbonePolymerAttach.call(c, e, 'edit.units');
      var m = c.add(new Backbone.Model({id: 'add1', type: 'test'}));

      expect(m.get('type')).to.equal('test');
      expect(adds).to.have.length(1);
      expect(e.spliced).to.have.length(1);

      //Expects on the options obj.
      expect(adds[0][2]).to.be.an('object');
      expect(adds[0][2]).to.have.property('add').and.equal(true);
      expect(adds[0][2]).to.have.property('merge').and.equal(false);
      expect(adds[0][2]).to.have.property('remove').and.equal(false);
      expect(adds[0][1]).to.have.property('length').and.equal(1);

      var m1 = new Backbone.Model({id: 'add2', type: 'test'});
      c.add(m1, {at: 1});

      //Adding at an index, expects Options to have 'at'
      expect(adds[1][2]).to.have.property('add').and.equal(true);
      expect(adds[1][2]).to.have.property('at').and.equal(1);
      expect(adds[0][1]).to.have.property('length').and.equal(2);
      expect(e.spliced).to.have.length(2);

      //Adding again at index 2
      var m2 = new Backbone.Model({id: 'add3', type: 'test'});
      c.add(m2, {at: 2});

      expect(e.spliced[2]).to.have.property('index').and.equal(2);
      expect(adds[0][1]).to.have.property('length').and.equal(3);
    });
  });
});
<commit_msg>Clarifies some test and uncovers a serious flaw in backbone compatibility<commit_after>
var expect = require('chai').expect;

// still undecided on how the mixin gets dependencies
_ = require('yobo')._;

var Backbone = require('yobo').Backbone;

var BackbonePolymerAttach = require('../backbone-polymer-mixin');

var PolymerElementMock = function() {

  var spliced = this.spliced = [];

  this.splice = function(path, index, removeCount, items) {
    spliced.push({path: path, index: index, removeCount: removeCount, items: items});
  };

  this.notifyPath = function() {
    // our code still does this for attribute changes
  };

};

describe("Array modification through Polymer splices, emulate backbone events", function() {

  describe("Backbone events before backbone-polymer", function() {

    it("add model", function() {
      var c = new Backbone.Collection();
      var m = new Backbone.Model({id: 'a1', type: 'testmodel'})
      c.on('add', function(model, collection, options) {
        console.log('add', m === model ? '(model)' : model, c === collection ? '(collection)' : collection, JSON.stringify(options));
      });
      c.add(m);
    });

    it("add model at index", function() {
      var c = new Backbone.Collection();
      c.add({id: 'a2', type: 'testmodel2'});
      c.add({id: 'a3', type: 'testmodel3'});
      var m = new Backbone.Model({id: 'a1', type: 'testmodel'})
      c.on('add', function(model, collection, options) {
        console.log('add', m === model ? '(model)' : model, c === collection ? '(collection)' : collection, JSON.stringify(options));
      });
      var added = c.add(m, {at: 1});
      console.log('add returned', m === added ? '(model)' : model);
      // common Backbone operations
      expect(c.get('a1')).to.exist.and.have.property('cid');
      expect(c.at(0)).to.exist.and.have.property('cid');
    });

  });

  describe("#add, reduced Backbone functionality", function() {

    it("Accepts only a single item", function() {
      expect(function() {
        var c = new Backbone.Collection();
        BackbonePolymerAttach.call(c, new PolymerElementMock(), 'edit.units');
        c.add([]);
      }).to.throw(/single/);
    });

    it("Requires item to be a real model", function() {
      expect(function() {
        var c = new Backbone.Collection();
        BackbonePolymerAttach.call(c, new PolymerElementMock(), 'edit.units');
        c.add({id: 'add1', type: 'test'});
      }).to.throw(/requires model instance/);
    });

    it("Is transparent to backbone add event listener", function() {
      var e = new PolymerElementMock();
      var c = new Backbone.Collection();

      var adds = [];
      c.on('add', function(m, c, o) {
        adds.push({model:m, collection:c, options:o});
      });

      BackbonePolymerAttach.call(c, e, 'edit.units');
      var m = c.add(new Backbone.Model({id: 'add1', type: 'test'}));

      expect(m.get('type')).to.equal('test');
      expect(adds).to.have.length(1);
      expect(e.spliced).to.have.length(1);

      //Expects on the options obj.
      expect(adds[0].options).to.be.an('object');
      expect(adds[0].options).to.have.property('add').and.equal(true);
      expect(adds[0].options).to.have.property('merge').and.equal(false);
      expect(adds[0].options).to.have.property('remove').and.equal(false);
      expect(adds[0].options).to.not.have.property('at');
      expect(adds[0].collection).to.have.property('length').and.equal(1);

      var m1 = new Backbone.Model({id: 'add2', type: 'test'});
      c.add(m1, {at: 1});

      //Adding at an index, expects Options to have 'at'
      expect(adds[1].options).to.have.property('add').and.equal(true);
      expect(adds[1].options).to.have.property('at').and.equal(1);
      expect(adds[0].collection).to.have.property('length').and.equal(2);
      expect(e.spliced).to.have.length(2);

      //Adding again at index 1, i.e. insert
      var m2 = new Backbone.Model({id: 'add3', type: 'test'});
      c.add(m2, {at: 1});

      expect(e.spliced[2]).to.have.property('index').and.equal(1);
      expect(adds[0].collection).to.have.property('length').and.equal(3);

      // common Backbone operations
      expect(c.get('add1')).to.exist.and.have.property('cid');
      expect(c.at(1)).to.exist.and.have.property('cid');
    });
  });
});
<|endoftext|>


<commit_before>'use strict'

import test from 'tape'
import Grid from '../../'
import _ from 'lodash'

const WIDTH = 1600
const HEIGHT = 900

function StubWindow (id, width, height) {
  this.id = id
  this.width = width,
  this.height = height
}

test('can increase window size', t => {
  t.plan(2)
  try {
    const grid = new Grid(WIDTH, HEIGHT)
    const stubWindow = new StubWindow(1, 400, 600)
    grid.add(stubWindow)
    grid.getWindow(1).changeSize(450, 650)
    t.equals(grid.windows.length, 1, 'grid has one window')
    t.deepEquals(_.pick(grid.getWindow(1), ['x', 'y', 'width', 'height']), {
      x: 0,
      y: 0,
      width: 450,
      height: 650
    }, 'window size changed')
  } catch (e) {
    t.fail(e.toString())
    t.end()
  }
})

test('can decrease window size', t => {
  t.plan(2)
  try {
    const grid = new Grid(WIDTH, HEIGHT)
    const stubWindow = new StubWindow(1, 400, 600)
    grid.add(stubWindow)
    grid.getWindow(1).changeSize(350, 550)
    t.equals(grid.windows.length, 1, 'grid has one window')
    t.deepEquals(_.pick(grid.getWindow(1), ['x', 'y', 'width', 'height']), {
      x: 0,
      y: 0,
      width: 350,
      height: 550
    }, 'window size changed')
  } catch (e) {
    t.fail(e.toString())
    t.end()
  }
})

test('cannot resize window over another window', t => {
  t.plan(3)
  try {
    const grid = new Grid(WIDTH, HEIGHT)
    const stubWindow1 = new StubWindow(1, 400, 100)
    const stubWindow2 = new StubWindow(2, 400, 100)
    const stubWindow3 = new StubWindow(3, 400, 100)
    grid.add(stubWindow1)
    grid.add(stubWindow2, 400)
    grid.add(stubWindow3, 0, 100)
    t.throws(
      () => grid.getWindow(1).changeSize(401, 100),
      Error,
      'cannot resize window horizontally over another'
    )
    t.throws(
      () => grid.getWindow(1).changeSize(400, 101),
      Error,
      'cannot resize window vertically over another'
    )
    t.equals(grid.windows.length, 3, 'grid windows still present')
  } catch (e) {
    t.fail(e.toString())
    t.end()
  }
})

test('can change window location', t => {
  t.plan(2)
  try {
    const grid = new Grid(WIDTH, HEIGHT)
    const stubWindow = new StubWindow(1, 400, 600)
    grid.add(stubWindow)
    grid.getWindow(1).changeLocation(1, 1)
    t.equals(grid.windows.length, 1, 'grid has one window')
    t.deepEquals(_.pick(grid.getWindow(1), ['x', 'y', 'width', 'height']), {
      x: 1,
      y: 1,
      width: 400,
      height: 600
    }, 'window location changed')
  } catch (e) {
    t.fail(e.toString())
    t.end()
  }
})

test('cannot move window over another window', t => {
  t.plan(3)
  try {
    const grid = new Grid(WIDTH, HEIGHT)
    const stubWindow1 = new StubWindow(1, 400, 100)
    const stubWindow2 = new StubWindow(2, 400, 100)
    const stubWindow3 = new StubWindow(3, 400, 100)
    grid.add(stubWindow1)
    grid.add(stubWindow2, 400)
    grid.add(stubWindow3, 0, 100)
    t.throws(
      () => grid.getWindow(1).changeLocation(1, 0),
      Error,
      'cannot move window horizontally over another'
    )
    t.throws(
      () => grid.getWindow(1).changeLocation(0, 1),
      Error,
      'cannot move window vertically over another'
    )
    t.equals(grid.windows.length, 3, 'grid windows still present')
  } catch (e) {
    t.fail(e.toString())
    t.end()
  }
})

test('cannot resize window outside grid', t => {
  t.plan(3)
  try {
    const grid = new Grid(WIDTH, HEIGHT)
    const stubWindow1 = new StubWindow(1, 400, 100)
    grid.add(stubWindow1)
    t.throws(
      () => grid.getWindow(1).changeSize(1601, 100),
      Error,
      'cannot resize window horizontally outside grid'
    )
    t.throws(
      () => grid.getWindow(1).changeSize(400, 901),
      Error,
      'cannot resize window vertically over another'
    )
    t.equals(grid.windows.length, 1, 'grid window still present')
  } catch (e) {
    t.fail(e.toString())
    t.end()
  }
})

test.skip('cannot move window outside grid', t => {
  // TBD
})

test.skip('can move window into location vacated by another', t => {
  // TBD
})
<commit_msg>fix copy-paste error<commit_after>'use strict'

import test from 'tape'
import Grid from '../../'
import _ from 'lodash'

const WIDTH = 1600
const HEIGHT = 900

function StubWindow (id, width, height) {
  this.id = id
  this.width = width,
  this.height = height
}

test('can increase window size', t => {
  t.plan(2)
  try {
    const grid = new Grid(WIDTH, HEIGHT)
    const stubWindow = new StubWindow(1, 400, 600)
    grid.add(stubWindow)
    grid.getWindow(1).changeSize(450, 650)
    t.equals(grid.windows.length, 1, 'grid has one window')
    t.deepEquals(_.pick(grid.getWindow(1), ['x', 'y', 'width', 'height']), {
      x: 0,
      y: 0,
      width: 450,
      height: 650
    }, 'window size changed')
  } catch (e) {
    t.fail(e.toString())
    t.end()
  }
})

test('can decrease window size', t => {
  t.plan(2)
  try {
    const grid = new Grid(WIDTH, HEIGHT)
    const stubWindow = new StubWindow(1, 400, 600)
    grid.add(stubWindow)
    grid.getWindow(1).changeSize(350, 550)
    t.equals(grid.windows.length, 1, 'grid has one window')
    t.deepEquals(_.pick(grid.getWindow(1), ['x', 'y', 'width', 'height']), {
      x: 0,
      y: 0,
      width: 350,
      height: 550
    }, 'window size changed')
  } catch (e) {
    t.fail(e.toString())
    t.end()
  }
})

test('cannot resize window over another window', t => {
  t.plan(3)
  try {
    const grid = new Grid(WIDTH, HEIGHT)
    const stubWindow1 = new StubWindow(1, 400, 100)
    const stubWindow2 = new StubWindow(2, 400, 100)
    const stubWindow3 = new StubWindow(3, 400, 100)
    grid.add(stubWindow1)
    grid.add(stubWindow2, 400)
    grid.add(stubWindow3, 0, 100)
    t.throws(
      () => grid.getWindow(1).changeSize(401, 100),
      Error,
      'cannot resize window horizontally over another'
    )
    t.throws(
      () => grid.getWindow(1).changeSize(400, 101),
      Error,
      'cannot resize window vertically over another'
    )
    t.equals(grid.windows.length, 3, 'grid windows still present')
  } catch (e) {
    t.fail(e.toString())
    t.end()
  }
})

test('can change window location', t => {
  t.plan(2)
  try {
    const grid = new Grid(WIDTH, HEIGHT)
    const stubWindow = new StubWindow(1, 400, 600)
    grid.add(stubWindow)
    grid.getWindow(1).changeLocation(1, 1)
    t.equals(grid.windows.length, 1, 'grid has one window')
    t.deepEquals(_.pick(grid.getWindow(1), ['x', 'y', 'width', 'height']), {
      x: 1,
      y: 1,
      width: 400,
      height: 600
    }, 'window location changed')
  } catch (e) {
    t.fail(e.toString())
    t.end()
  }
})

test('cannot move window over another window', t => {
  t.plan(3)
  try {
    const grid = new Grid(WIDTH, HEIGHT)
    const stubWindow1 = new StubWindow(1, 400, 100)
    const stubWindow2 = new StubWindow(2, 400, 100)
    const stubWindow3 = new StubWindow(3, 400, 100)
    grid.add(stubWindow1)
    grid.add(stubWindow2, 400)
    grid.add(stubWindow3, 0, 100)
    t.throws(
      () => grid.getWindow(1).changeLocation(1, 0),
      Error,
      'cannot move window horizontally over another'
    )
    t.throws(
      () => grid.getWindow(1).changeLocation(0, 1),
      Error,
      'cannot move window vertically over another'
    )
    t.equals(grid.windows.length, 3, 'grid windows still present')
  } catch (e) {
    t.fail(e.toString())
    t.end()
  }
})

test('cannot resize window outside grid', t => {
  t.plan(3)
  try {
    const grid = new Grid(WIDTH, HEIGHT)
    const stubWindow1 = new StubWindow(1, 400, 100)
    grid.add(stubWindow1)
    t.throws(
      () => grid.getWindow(1).changeSize(1601, 100),
      Error,
      'cannot resize window horizontally outside grid'
    )
    t.throws(
      () => grid.getWindow(1).changeSize(400, 901),
      Error,
      'cannot resize window vertically outside grid'
    )
    t.equals(grid.windows.length, 1, 'grid window still present')
  } catch (e) {
    t.fail(e.toString())
    t.end()
  }
})

test.skip('cannot move window outside grid', t => {
  // TBD
})

test.skip('can move window into location vacated by another', t => {
  // TBD
})
<|endoftext|>


<commit_before>'use strict';
var d3 = require('d3');
require('d3-multiaxis-zoom')(d3);
var _ = require('lodash');
var utils = require('lightning-client-utils');
var simplify = require('simplify-js');
var TooltipPlugin = require('d3-tip');
TooltipPlugin(d3);

var margin = {
    top: 40,
    right: 10,
    bottom: 20,
    left: 10
};

var LineStackedGraph = function(selector, data, images, opts) {

    var defaults = {
        tooltips: false
    };

    opts = _.defaults(opts || {}, defaults);
    this.opts = opts;

    var tip;

    if(this.opts.tooltips) {
        var format = d3.format('.02f');
        tip = d3.tip()
            .attr('class', 'd3-tip')
            .html(function(d, i) {
                return 'Series: ' + i;
            });
    }

    var colors = utils.getColors(data.length);

    this.$el = $(selector);
    this.lid = utils.getUniqueId();

    if(_.isArray(data[0])) {
        data = _.map(data, function(d) {
            if(_.isNumber(d[0])) {
                return _.map(d, function(datum, i) {
                    return {
                        x: i,
                        y: datum
                    };
                });
            }
            return d;
        });
    } else {
        data = [_.map(data, function(d, i) {
            if(_.isNumber(d)) {
                return {
                    x: i,
                    y: d
                };
            }
            return d;
        })];
    }

    var nestedExtent = function(arrays, map) {
        var max = d3.max(arrays, function(arr) {
            return d3.max(_.map(arr, map));
        });
        var min = d3.min(arrays, function(arr) {
            return d3.min(_.map(arr, map));
        });

        return [min, max];
    };

    var yDomain = nestedExtent(data, function(d) {
        return d.y;
    });
    var xDomain = nestedExtent(data, function(d) {
        return d.x;
    });

    var xSpread = Math.abs(xDomain[0] - xDomain[1]);
    var ySpread = Math.abs(yDomain[0] - yDomain[1]);

    var simpleData = _.map(data, function(d) {
        return simplify(d, 0.1);
    });

    var chartWidth = $(selector).width();
    var chartHeight = (opts.height || (chartWidth * 0.6));

    // do everything for the minimap
    var minimapWidth = 0.2 * chartWidth;

    chartWidth -= minimapWidth;

    var minimapLineHeight = 20;
    var minimapLinePadding = 5;

    var minimapX = d3.scale.linear()
                    .domain([xDomain[0] - xSpread * 0.05, xDomain[1] + xSpread * 0.05])
                    .range([0, minimapWidth]);

    var minimapY = d3.scale.linear()
                    .domain([yDomain[0] - ySpread * 0.05, yDomain[1] + ySpread * 0.05])
                    .range([minimapLineHeight, 0]);

    var minimapLine = d3.svg.line()
                        .x(function(d) {
                            return minimapX(d.x);
                        })
                        .y(function(d) {
                            return minimapY(d.y);
                        });

    var selectedLinesLength = 0;
    var getChartData = function(dataObjArr) {

        return _.map(dataObjArr, function(dataObj, i) {

            dataObj.data =  _.map(dataObj.data, function(point) {
                var p = {
                    x: point.x
                };

                p.y = chartY(point.y) + i * (chartHeight / selectedLinesLength);

                return p;
            });

            return dataObj;
        });

    };

    var chartX = d3.scale.linear()
                    .domain([xDomain[0] - xSpread * 0.05, xDomain[1] + xSpread * 0.05])
                    .range([0, chartWidth]);

    var chartY = d3.scale.linear()
                    .domain([yDomain[0] - ySpread * 0.05, yDomain[1] + ySpread * 0.05]);

    var zoomY = d3.scale.linear();

    var chartLine = d3.svg.line()
                        .x(function(d) {
                            return chartX(d.x);
                        })
                        .y(function(d) {
                            return zoomY(d.y);
                        });

    var minimapDiv = d3.select(selector).append('div').attr('class', 'minimap').attr('id', this.lid);

    _.each(simpleData, function(d, i) {

        var minimapSvg = minimapDiv.append('div').attr('class', 'miniline-container')
            .append('svg')
            .attr('class', 'stacked-line-plot-minimap')
            .attr('width', minimapWidth)
            .attr('height', minimapLineHeight);

        var minimap = minimapSvg.append('g')
            .attr('class', 'minimap');

        minimap.append('svg:clipPath')
            .attr('id', 'minimapClip')
            .append('svg:rect')
            .attr('x', 0)
            .attr('y', 0)
            .attr('width', minimapWidth)
            .attr('height', (minimapLineHeight + minimapLinePadding) * data.length);

        var chartBody = minimap.append('g')
            .attr('clip-path', 'url(#minimapClip)');

        chartBody.append('path')
            .datum(d)
            .attr('class', 'line')
            .attr('d', minimapLine)
            .style('stroke', colors[i]);

    });

    var $minilines = $(selector).find('#' + this.lid + ' .miniline-container');

    $minilines.click(function() {
        $(this).toggleClass('active');
        updateChart();
    });

    var panExtent = {x: [xDomain[0], xDomain[1]], y: [-Infinity,Infinity] };

    var zoom = d3.behavior.zoom()
        .x(chartX)
        .on('zoom', zoomed);

    var chartDiv = d3.select(selector).append('div').attr('class', 'chart').style('width', chartWidth + 'px');

    var chartSvg = chartDiv.append('svg')
        .attr('class', 'stacked-line-plot')
        .attr('width', chartWidth)
        .attr('height', chartHeight)
        .call(zoom);

    var chart = chartSvg.append('g')
        .attr('class', 'chart');

    var xGrid = d3.svg.axis()
                    .scale(chartX)
                    .orient('bottom')
                    .ticks(5)
                    .tickSize(-chartHeight, 0, 0)
                    .tickFormat('');

    chart.append('svg:clipPath')
        .attr('id', 'chartClip')
        .append('svg:rect')
        .attr('x', 0)
        .attr('y', 0)
        .attr('width', chartWidth)
        .attr('height', chartHeight);

    var chartBody = chart.append('g')
        .attr('clip-path', 'url(#chartClip)');

    if(this.opts.tooltips) {
        chartBody.call(tip);
    }

    chartBody.append('g')
        .attr('class', 'grid x')
        .attr('transform', 'translate(0,' + chartHeight + ')')
        .call(xGrid
        );

    var updateChart = function() {

        var tempData = [], domainData = [];
        $minilines.each(function(i) {
            if($(this).hasClass('active')) {
                tempData.push({
                    data: data[i],
                    index: i
                });

                domainData.push(data[i]);
            }
        });

        selectedLinesLength = tempData.length;

        var yDomain = nestedExtent(domainData, function(d) {
            return d.y;
        });
        var ySpread = Math.abs(yDomain[0] - yDomain[1]);

        chartY.domain([yDomain[0] - ySpread * 0.05, yDomain[1] + ySpread * 0.05]).range([chartHeight / selectedLinesLength, 0]);

        tempData = getChartData(tempData);

        var lineContainer = chartBody.selectAll('.line')
                            .data(tempData, function(d) {
                                return d.index;
                            });

        lineContainer
            .enter()
            .append('path')
            .attr('class', 'line')
            .attr('d', function(d) {
                return chartLine(d.data);
            })
            .attr('index', function(d) {
                return d.index;
            })
            .style('stroke', function(d, i) {
                return colors[d.index];
            });

        if(opts.tooltips) {
            var t;
            lineContainer
                .on('mouseover', function(d, i) {
                    clearTimeout(t);
                    tip.show(d, i);
                })
                .on('mouseout', function(d, i) {
                    t = setTimeout(function() {
                        tip.hide(d, i);
                    }, 500);
                });
        }

        lineContainer.exit().remove();

        chart.selectAll('.line')
            .transition()
            .duration(750)
            .attr('d', function(d) {
                return chartLine(d.data);
            });
    }

    function zoomed() {

        chartBody.select('.x.grid')
            .call(xGrid);

        chart.selectAll('.line')
            .attr('d', function(d) {
                return chartLine(d.data);
            });
    }
};

module.exports = LineStackedGraph;

LineStackedGraph.prototype.updateData = function(data) {
    this.svg.select('.line')
        .datum(data)
        .transition()
        .attr('d', this.line);
};

<commit_msg>Stray line<commit_after>'use strict';
var d3 = require('d3');
require('d3-multiaxis-zoom')(d3);
var _ = require('lodash');
var utils = require('lightning-client-utils');
var simplify = require('simplify-js');
var TooltipPlugin = require('d3-tip');
TooltipPlugin(d3);

var margin = {
    top: 40,
    right: 10,
    bottom: 20,
    left: 10
};

var LineStackedGraph = function(selector, data, images, opts) {

    var defaults = {
        tooltips: false
    };

    opts = _.defaults(opts || {}, defaults);
    this.opts = opts;

    var tip;

    if(this.opts.tooltips) {
        var format = d3.format('.02f');
        tip = d3.tip()
            .attr('class', 'd3-tip')
            .html(function(d, i) {
                return 'Series: ' + i;
            });
    }

    var colors = utils.getColors(data.length);

    this.$el = $(selector);
    this.lid = utils.getUniqueId();

    if(_.isArray(data[0])) {
        data = _.map(data, function(d) {
            if(_.isNumber(d[0])) {
                return _.map(d, function(datum, i) {
                    return {
                        x: i,
                        y: datum
                    };
                });
            }
            return d;
        });
    } else {
        data = [_.map(data, function(d, i) {
            if(_.isNumber(d)) {
                return {
                    x: i,
                    y: d
                };
            }
            return d;
        })];
    }

    var nestedExtent = function(arrays, map) {
        var max = d3.max(arrays, function(arr) {
            return d3.max(_.map(arr, map));
        });
        var min = d3.min(arrays, function(arr) {
            return d3.min(_.map(arr, map));
        });

        return [min, max];
    };

    var yDomain = nestedExtent(data, function(d) {
        return d.y;
    });
    var xDomain = nestedExtent(data, function(d) {
        return d.x;
    });

    var xSpread = Math.abs(xDomain[0] - xDomain[1]);
    var ySpread = Math.abs(yDomain[0] - yDomain[1]);

    var simpleData = _.map(data, function(d) {
        return simplify(d, 0.1);
    });

    var chartWidth = $(selector).width();
    var chartHeight = (opts.height || (chartWidth * 0.6));

    // do everything for the minimap
    var minimapWidth = 0.2 * chartWidth;

    chartWidth -= minimapWidth;

    var minimapLineHeight = 20;
    var minimapLinePadding = 5;

    var minimapX = d3.scale.linear()
                    .domain([xDomain[0] - xSpread * 0.05, xDomain[1] + xSpread * 0.05])
                    .range([0, minimapWidth]);

    var minimapY = d3.scale.linear()
                    .domain([yDomain[0] - ySpread * 0.05, yDomain[1] + ySpread * 0.05])
                    .range([minimapLineHeight, 0]);

    var minimapLine = d3.svg.line()
                        .x(function(d) {
                            return minimapX(d.x);
                        })
                        .y(function(d) {
                            return minimapY(d.y);
                        });

    var selectedLinesLength = 0;
    var getChartData = function(dataObjArr) {

        return _.map(dataObjArr, function(dataObj, i) {

            dataObj.data =  _.map(dataObj.data, function(point) {
                var p = {
                    x: point.x
                };

                p.y = chartY(point.y) + i * (chartHeight / selectedLinesLength);

                return p;
            });

            return dataObj;
        });

    };

    var chartX = d3.scale.linear()
                    .domain([xDomain[0] - xSpread * 0.05, xDomain[1] + xSpread * 0.05])
                    .range([0, chartWidth]);

    var chartY = d3.scale.linear()
                    .domain([yDomain[0] - ySpread * 0.05, yDomain[1] + ySpread * 0.05]);

    var zoomY = d3.scale.linear();

    var chartLine = d3.svg.line()
                        .x(function(d) {
                            return chartX(d.x);
                        })
                        .y(function(d) {
                            return zoomY(d.y);
                        });

    var minimapDiv = d3.select(selector).append('div').attr('class', 'minimap').attr('id', this.lid);

    _.each(simpleData, function(d, i) {

        var minimapSvg = minimapDiv.append('div').attr('class', 'miniline-container')
            .append('svg')
            .attr('class', 'stacked-line-plot-minimap')
            .attr('width', minimapWidth)
            .attr('height', minimapLineHeight);

        var minimap = minimapSvg.append('g')
            .attr('class', 'minimap');

        minimap.append('svg:clipPath')
            .attr('id', 'minimapClip')
            .append('svg:rect')
            .attr('x', 0)
            .attr('y', 0)
            .attr('width', minimapWidth)
            .attr('height', (minimapLineHeight + minimapLinePadding) * data.length);

        var chartBody = minimap.append('g')
            .attr('clip-path', 'url(#minimapClip)');

        chartBody.append('path')
            .datum(d)
            .attr('class', 'line')
            .attr('d', minimapLine)
            .style('stroke', colors[i]);

    });

    var $minilines = $(selector).find('#' + this.lid + ' .miniline-container');

    $minilines.click(function() {
        $(this).toggleClass('active');
        updateChart();
    });

    var panExtent = {x: [xDomain[0], xDomain[1]], y: [-Infinity,Infinity] };

    var zoom = d3.behavior.zoom()
        .x(chartX)
        .on('zoom', zoomed);

    var chartDiv = d3.select(selector).append('div').attr('class', 'chart').style('width', chartWidth + 'px');

    var chartSvg = chartDiv.append('svg')
        .attr('class', 'stacked-line-plot')
        .attr('width', chartWidth)
        .attr('height', chartHeight)
        .call(zoom);

    var chart = chartSvg.append('g')
        .attr('class', 'chart');

    var xGrid = d3.svg.axis()
                    .scale(chartX)
                    .orient('bottom')
                    .ticks(5)
                    .tickSize(-chartHeight, 0, 0)
                    .tickFormat('');

    chart.append('svg:clipPath')
        .attr('id', 'chartClip')
        .append('svg:rect')
        .attr('x', 0)
        .attr('y', 0)
        .attr('width', chartWidth)
        .attr('height', chartHeight);

    var chartBody = chart.append('g')
        .attr('clip-path', 'url(#chartClip)');

    if(this.opts.tooltips) {
        chartBody.call(tip);
    }

    chartBody.append('g')
        .attr('class', 'grid x')
        .attr('transform', 'translate(0,' + chartHeight + ')')
        .call(xGrid
        );

    var updateChart = function() {

        var tempData = [], domainData = [];
        $minilines.each(function(i) {
            if($(this).hasClass('active')) {
                tempData.push({
                    data: data[i],
                    index: i
                });

                domainData.push(data[i]);
            }
        });

        selectedLinesLength = tempData.length;

        var yDomain = nestedExtent(domainData, function(d) {
            return d.y;
        });
        var ySpread = Math.abs(yDomain[0] - yDomain[1]);

        chartY.domain([yDomain[0] - ySpread * 0.05, yDomain[1] + ySpread * 0.05]).range([chartHeight / selectedLinesLength, 0]);

        tempData = getChartData(tempData);

        var lineContainer = chartBody.selectAll('.line')
                            .data(tempData, function(d) {
                                return d.index;
                            });

        lineContainer
            .enter()
            .append('path')
            .attr('class', 'line')
            .attr('d', function(d) {
                return chartLine(d.data);
            })
            .attr('index', function(d) {
                return d.index;
            })
            .style('stroke', function(d, i) {
                return colors[d.index];
            });

        if(opts.tooltips) {
            var t;
            lineContainer
                .on('mouseover', function(d, i) {
                    clearTimeout(t);
                    tip.show(d, i);
                })
                .on('mouseout', function(d, i) {
                    t = setTimeout(function() {
                        tip.hide(d, i);
                    }, 500);
                });
        }

        lineContainer.exit().remove();

        chart.selectAll('.line')
            .transition()
            .duration(750)
            .attr('d', function(d) {
                return chartLine(d.data);
            });
    }

    function zoomed() {

        chartBody.select('.x.grid')
            .call(xGrid);

        chart.selectAll('.line')
            .attr('d', function(d) {
                return chartLine(d.data);
            });
    }
};

module.exports = LineStackedGraph;

LineStackedGraph.prototype.updateData = function(data) {
    this.svg.select('.line')
        .datum(data)
        .transition()
        .attr('d', this.line);
};

<|endoftext|>


<commit_before>'use strict';

const rendy = require('rendy');
const currify = require('currify');
const store = require('fullstore');
const {encode} = require('./entity');
const btoa = require('./btoa');

const getHeaderField = currify(_getHeaderField);

/* КОНСТАНТЫ (общие для клиента и сервера)*/

/* название программы */
const NAME = 'Cloud Commander';
const FS = '/fs';
const Path = store();

Path('/');

module.exports.FS = FS;
module.exports.apiURL = '/api/v1';
module.exports.MAX_FILE_SIZE = 500 * 1024;
module.exports.getHeaderField = getHeaderField;
module.exports.getPathLink = getPathLink;
module.exports.getDotDot = getDotDot;

module.exports.formatMsg = (msg, name, status) => {
    status = status || 'ok';
    name = name || '';

    if (name)
        name = '("' + name + '")';

    return msg + ': ' + status + name;
};

/**
 * Функция возвращает заголовок веб страницы
 * @path
 */
module.exports.getTitle = (options) => {
    options = options || {};

    const {
        path = Path(),
        name,
    } = options;

    const array = [
        name || NAME,
        path,
    ];

    return array
        .filter(Boolean)
        .join(' - ');
};

/** Функция получает адреса каждого каталога в пути
 * возвращаеться массив каталогов
 * @param url -  адрес каталога
 */
function getPathLink(url, prefix, template) {
    if (!url)
        throw Error('url could not be empty!');

    if (!template)
        throw Error('template could not be empty!');

    const names = url
        .split('/')
        .slice(1, -1);

    const allNames = ['/', ...names];
    const length = allNames.length - 1;

    let path = '/';

    const pathHTML = allNames.map((name, index) => {
        const isLast = index === length;

        if (index)
            path += name + '/';

        if (index && isLast)
            return name + '/';

        const slash = index ? '/' : '';

        return rendy(template, {
            path,
            name,
            slash,
            prefix,
        });
    }).join('');

    return pathHTML;
}

const getDataName = (name) => {
    const encoded = btoa(encodeURI(name));
    return `data-name="js-file-${encoded}" `;
};

/**
 * Функция строит таблицу файлв из JSON-информации о файлах
 * @param params - информация о файлах
 *
 */
module.exports.buildFromJSON = (params) => {
    const {
        prefix,
        template,
        sort = 'name',
        order = 'asc',
    } = params;

    const templateFile = template.file;
    const templateLink = template.link;
    const json = params.data;

    const path = encode(json.path);

    const {files} = json;

    /*
     * Строим путь каталога в котором мы находимся
     * со всеми подкаталогами
     */
    const htmlPath = getPathLink(path, prefix, template.pathLink);

    let fileTable = rendy(template.path, {
        link        : prefix + FS + path,
        fullPath    : path,
        path        : htmlPath,
    });

    const owner = 'owner';
    const mode = 'mode';

    const getFieldName = getHeaderField(sort, order);

    const name = getFieldName('name');
    const size = getFieldName('size');
    const date = getFieldName('date');

    const header = rendy(templateFile, {
        tag         : 'div',
        attribute   : 'data-name="js-fm-header" ',
        className   : 'fm-header',
        type        : '',
        name,
        size,
        date,
        owner,
        mode,
    });

    /* сохраняем путь */
    Path(path);

    fileTable += header + '
';

    /* Если мы не в корне */
    if (path !== '/') {
        const dotDot = getDotDot(path);
        const link = prefix + FS + dotDot;

        const linkResult = rendy(template.link, {
            link,
            title       : '..',
            name        : '..',
        });

        const dataName = 'data-name="js-file-.." ';
        const attribute = 'draggable="true" ' + dataName;

        /* Сохраняем путь к каталогу верхнего уровня*/
        fileTable += rendy(template.file, {
            tag         : 'li',
            attribute,
            className   : '',
            type        : 'directory',
            name        : linkResult,
            size        : '<dir>',
            date        : '--.--.----',
            owner       : '.',
            mode        : '--- --- ---',
        });
    }

    fileTable += files
        .map(updateField)
        .map((file) => {
            const name = encode(file.name);
            const link = prefix + FS + path + name;

            const {
                type,
                mode,
                date,
                owner,
                size,
            } = file;

            const linkResult = rendy(templateLink, {
                link,
                title: name,
                name,
                attribute: getAttribute(file.type),
            });

            const dataName = getDataName(file.name);
            const attribute = `draggable="true" ${dataName}`;

            return rendy(templateFile, {
                tag: 'li',
                attribute,
                className: '',
                type,
                name: linkResult,
                size,
                date,
                owner,
                mode,
            });
        }).join('');

    fileTable += '
';

    return fileTable;
};

function updateField(file) {
    return {
        ...file,
        date: file.date || '--.--.----',
        owner: file.owner || 'root',
        size: getSize(file),
    };
}

function getAttribute(type) {
    if (type === 'directory')
        return '';

    return 'target="_blank" ';
}

module.exports._getSize = getSize;
function getSize(file) {
    const {
        size,
        type,
    } = file;

    if (/^directory$/.test(type))
        return '<dir>';

    if (/link/.test(type))
        return '<link>';

    return size;
}

function _getHeaderField(sort, order, name) {
    const arrow = order === 'asc' ? '↑' : '↓';

    if (sort !== name)
        return name;

    if (sort === 'name' && order === 'asc')
        return name;

    return `${name}${arrow}`;
}

function getDotDot(path) {
    // убираем последний слеш и каталог в котором мы сейчас находимся
    const lastSlash = path.substr(path, path.lastIndexOf('/'));
    const dotDot = lastSlash.substr(lastSlash, lastSlash.lastIndexOf('/'));

    if (!dotDot)
        return '/';

    return dotDot;
}

<commit_msg>refactor(cloudfunc) getPathLink: map -> for<commit_after>'use strict';

const rendy = require('rendy');
const currify = require('currify');
const store = require('fullstore');
const {encode} = require('./entity');
const btoa = require('./btoa');

const getHeaderField = currify(_getHeaderField);

/* КОНСТАНТЫ (общие для клиента и сервера)*/

/* название программы */
const NAME = 'Cloud Commander';
const FS = '/fs';
const Path = store();

Path('/');

module.exports.FS = FS;
module.exports.apiURL = '/api/v1';
module.exports.MAX_FILE_SIZE = 500 * 1024;
module.exports.getHeaderField = getHeaderField;
module.exports.getPathLink = getPathLink;
module.exports.getDotDot = getDotDot;

module.exports.formatMsg = (msg, name, status) => {
    status = status || 'ok';
    name = name || '';

    if (name)
        name = '("' + name + '")';

    return msg + ': ' + status + name;
};

/**
 * Функция возвращает заголовок веб страницы
 * @path
 */
module.exports.getTitle = (options) => {
    options = options || {};

    const {
        path = Path(),
        name,
    } = options;

    const array = [
        name || NAME,
        path,
    ];

    return array
        .filter(Boolean)
        .join(' - ');
};

/** Функция получает адреса каждого каталога в пути
 * возвращаеться массив каталогов
 * @param url -  адрес каталога
 */
function getPathLink(url, prefix, template) {
    if (!url)
        throw Error('url could not be empty!');

    if (!template)
        throw Error('template could not be empty!');

    const names = url
        .split('/')
        .slice(1, -1);

    const allNames = ['/', ...names];
    const lines = [];
    const n = allNames.length;

    let path = '/';

    for (let i = 0; i < n; i++) {
        const name = allNames[i];
        const isLast = i === n - 1;

        if (i)
            path += name + '/';

        if (i && isLast) {
            lines.push(name + '/');
            continue;
        }

        const slash = i ? '/' : '';

        lines.push(rendy(template, {
            path,
            name,
            slash,
            prefix,
        }));
    }

    return lines.join('');
}

const getDataName = (name) => {
    const encoded = btoa(encodeURI(name));
    return `data-name="js-file-${encoded}" `;
};

/**
 * Функция строит таблицу файлв из JSON-информации о файлах
 * @param params - информация о файлах
 *
 */
module.exports.buildFromJSON = (params) => {
    const {
        prefix,
        template,
        sort = 'name',
        order = 'asc',
    } = params;

    const templateFile = template.file;
    const templateLink = template.link;
    const json = params.data;

    const path = encode(json.path);

    const {files} = json;

    /*
     * Строим путь каталога в котором мы находимся
     * со всеми подкаталогами
     */
    const htmlPath = getPathLink(path, prefix, template.pathLink);

    let fileTable = rendy(template.path, {
        link        : prefix + FS + path,
        fullPath    : path,
        path        : htmlPath,
    });

    const owner = 'owner';
    const mode = 'mode';

    const getFieldName = getHeaderField(sort, order);

    const name = getFieldName('name');
    const size = getFieldName('size');
    const date = getFieldName('date');

    const header = rendy(templateFile, {
        tag         : 'div',
        attribute   : 'data-name="js-fm-header" ',
        className   : 'fm-header',
        type        : '',
        name,
        size,
        date,
        owner,
        mode,
    });

    /* сохраняем путь */
    Path(path);

    fileTable += header + '
';

    /* Если мы не в корне */
    if (path !== '/') {
        const dotDot = getDotDot(path);
        const link = prefix + FS + dotDot;

        const linkResult = rendy(template.link, {
            link,
            title       : '..',
            name        : '..',
        });

        const dataName = 'data-name="js-file-.." ';
        const attribute = 'draggable="true" ' + dataName;

        /* Сохраняем путь к каталогу верхнего уровня*/
        fileTable += rendy(template.file, {
            tag         : 'li',
            attribute,
            className   : '',
            type        : 'directory',
            name        : linkResult,
            size        : '<dir>',
            date        : '--.--.----',
            owner       : '.',
            mode        : '--- --- ---',
        });
    }

    fileTable += files
        .map(updateField)
        .map((file) => {
            const name = encode(file.name);
            const link = prefix + FS + path + name;

            const {
                type,
                mode,
                date,
                owner,
                size,
            } = file;

            const linkResult = rendy(templateLink, {
                link,
                title: name,
                name,
                attribute: getAttribute(file.type),
            });

            const dataName = getDataName(file.name);
            const attribute = `draggable="true" ${dataName}`;

            return rendy(templateFile, {
                tag: 'li',
                attribute,
                className: '',
                type,
                name: linkResult,
                size,
                date,
                owner,
                mode,
            });
        }).join('');

    fileTable += '
';

    return fileTable;
};

function updateField(file) {
    return {
        ...file,
        date: file.date || '--.--.----',
        owner: file.owner || 'root',
        size: getSize(file),
    };
}

function getAttribute(type) {
    if (type === 'directory')
        return '';

    return 'target="_blank" ';
}

module.exports._getSize = getSize;
function getSize(file) {
    const {
        size,
        type,
    } = file;

    if (/^directory$/.test(type))
        return '<dir>';

    if (/link/.test(type))
        return '<link>';

    return size;
}

function _getHeaderField(sort, order, name) {
    const arrow = order === 'asc' ? '↑' : '↓';

    if (sort !== name)
        return name;

    if (sort === 'name' && order === 'asc')
        return name;

    return `${name}${arrow}`;
}

function getDotDot(path) {
    // убираем последний слеш и каталог в котором мы сейчас находимся
    const lastSlash = path.substr(path, path.lastIndexOf('/'));
    const dotDot = lastSlash.substr(lastSlash, lastSlash.lastIndexOf('/'));

    if (!dotDot)
        return '/';

    return dotDot;
}

<|endoftext|>


<commit_before>/*global chai, sinon, ports:true, Port, PortCollection, handlers,
  _currentUserData:true, currentConversation:true, UserData,
  _presenceSocket:true, browserPort:true, currentUsers:true,
  Conversation, _config:true, _loginPending:true, _autologinPending: true,
  _cookieNickname:true, Server, server:true, _signinCallback */
/* jshint expr:true */

var expect = chai.expect;

describe('handlers', function() {
  var sandbox;

  beforeEach(function() {
    sandbox = sinon.sandbox.create();
    server = new Server({});
  });

  afterEach(function() {
    sandbox.restore();
  });

  describe("social.port-closing", function() {
    var port;

    beforeEach(function() {
      ports = new PortCollection();
      port = new Port({_portid: 42});
      ports.add(port);

      browserPort = {postEvent: sandbox.spy()};
    });

    afterEach(function() {
      currentConversation = undefined;
    });

    it("should remove a closed port on receiving social.port-closing",
      function() {
        handlers['social.port-closing'].bind(port)();
        expect(Object.keys(ports.ports)).to.have.length.of(0);
      });

    it("should clear the current conversation on receiving " +
       "social.port-closing for the conversation port", function() {
        currentConversation = new Conversation();
        currentConversation.port = port;

        handlers['social.port-closing'].bind(port)();
        expect(currentConversation).to.be.equal(undefined);
      });
  });

  describe("social.cookies-get-response", function() {
    var oldConfig;

    beforeEach(function() {
      oldConfig = _config;
      _cookieNickname = undefined;
    });

    afterEach(function() {
      _config = oldConfig;
      _cookieNickname = undefined;
    });

    it("should try to connect the presence socket",
      function() {
        _currentUserData = {};
        sandbox.stub(server, "autoconnect");
        var event = {
          data: [ {name: "nick", value: "Boriss"} ]
        };

        handlers['social.cookies-get-response'](event);

        sinon.assert.calledOnce(server.autoconnect);
        sinon.assert.calledWithExactly(server.autoconnect, "Boriss");
      });

    it("should NOT try to connect if there is no nick provided",
      function () {
        sandbox.stub(server, "autoconnect");

        handlers['social.cookies-get-response']({
          topic: "social.cookies-get-response",
          data: []
        });

        sinon.assert.notCalled(server.autoconnect);
      });

  });

  describe("talkilla.login", function() {
    var xhr, rootURL, socketStub, requests;

    beforeEach(function() {
      socketStub = sinon.stub(server, "connect");
      // XXX For some reason, sandbox.useFakeXMLHttpRequest doesn't want to work
      // nicely so we have to manually xhr.restore for now.
      xhr = sinon.useFakeXMLHttpRequest();
      requests = [];
      xhr.onCreate = function (req) { requests.push(req); };

      rootURL = 'http://fake';
      _currentUserData = new UserData({}, {
        ROOTURL: rootURL
      });
      sandbox.stub(_currentUserData, "send");
      _loginPending = _autologinPending = false;
    });

    afterEach(function() {
      _currentUserData = undefined;
      xhr.restore();
      socketStub.restore();
    });

    it("should call postEvent with a pending message if I pass in valid data",
      function() {
        handlers.postEvent = sandbox.spy();
        handlers['talkilla.login']({
          topic: "talkilla.login",
          data: {assertion: "fake assertion"}
        });
        sinon.assert.calledOnce(handlers.postEvent);
        sinon.assert.calledWith(handlers.postEvent, "talkilla.login-pending");
      });

    it("should post an ajax message to the server if I pass valid login data",
      function() {
        handlers['talkilla.login']({
          topic: "talkilla.login",
          data: {assertion: "fake assertion"}
        });
        expect(requests.length).to.equal(1);
        expect(requests[0].url).to.equal('/signin');
        expect(requests[0].requestBody).to.be.not.empty;
        expect(requests[0].requestBody)
          .to.be.equal('{"assertion":"fake assertion"}');
      });

    it("should not do anything if a login is already pending", function() {
      _loginPending = true;
      sandbox.stub(window, "_signinCallback");
      handlers.postEvent = sandbox.spy();

      handlers['talkilla.login']({
        topic: "talkilla.login",
        data: {assertion: "fake assertion"}
      });

      sinon.assert.notCalled(handlers.postEvent);
      sinon.assert.notCalled(_signinCallback);
    });

    it("should not do anything if an auto login is already pending",
      function() {
        _autologinPending = true;
        sandbox.stub(window, "_signinCallback");
        handlers.postEvent = sandbox.spy();

        handlers['talkilla.login']({
          topic: "talkilla.login",
          data: {assertion: "fake assertion"}
        });

        sinon.assert.notCalled(handlers.postEvent);
        sinon.assert.notCalled(_signinCallback);
      });

    describe("Failed login", function() {
      it("should call postEvent with a failure message if I pass in bad data",
        function() {
          handlers.postEvent = sandbox.spy();
          handlers['talkilla.login']({topic: "talkilla.login", data: null});
          sinon.assert.calledOnce(handlers.postEvent);
          sinon.assert.calledWith(handlers.postEvent, "talkilla.login-failure");
        });

      it("should post a fail message if the server rejected login",
        function() {
          handlers.postEvent = sinon.spy();
          handlers['talkilla.login']({
            topic: "talkilla.login",
            data: {assertion: "fake assertion"}
          });
          expect(requests.length).to.equal(1);

          requests[0].respond(401, {'Content-Type': 'text/plain'},
                              JSON.stringify({error: "some error"}));

          sinon.assert.calledTwice(handlers.postEvent);
          sinon.assert.calledWith(handlers.postEvent, "talkilla.login-failure");
        });
    });

    describe("Accepted Login", function() {
      var port;

      beforeEach(function() {
        port = {id: "tests", postEvent: sandbox.spy()};
        ports.add(port);

        handlers['talkilla.login']({
          topic: "talkilla.login",
          data: {assertion: "fake assertion"}
        });
        expect(requests.length).to.equal(1);

        requests[0].respond(200, { 'Content-Type': 'application/json' },
          '{"nick":"jb"}' );
      });

      afterEach(function() {
        ports.remove(port);
      });

      it("should store the userName if the server accepted login", function() {
        expect(_currentUserData.userName).to.be.equal("jb");
      });

      it("should set the current user name if the server accepted login",
        function() {
          sinon.assert.calledOnce(_currentUserData.send);
        });

      it("should store the username if the server accepted login",
        function() {
          expect(_currentUserData.userName).to.equal('jb');
        });
    });
  });

  describe("talkilla.logout", function() {
    var xhr, requests;

    beforeEach(function() {
      // XXX For some reason, sandbox.useFakeXMLHttpRequest doesn't want to work
      // nicely so we have to manually xhr.restore for now.
      xhr = sinon.useFakeXMLHttpRequest();
      requests = [];
      xhr.onCreate = function (req) { requests.push(req); };

      sandbox.stub(UserData.prototype, "send");
      _currentUserData = new UserData({userName: 'romain'}, {});
      _presenceSocket = { close: sandbox.stub() };
    });

    afterEach(function() {
      _presenceSocket = undefined;
      _currentUserData = undefined;
      xhr.restore();
    });

    it("should post an ajax message to the server",
      function() {
        sandbox.stub(server, "signout");
        handlers['talkilla.logout']({
          topic: 'talkilla.logout'
        });
        sinon.assert.calledOnce(server.signout);
      });

    describe("Success logout", function() {
      var port;

      beforeEach(function () {
        port = {id: "tests", postEvent: sandbox.spy()};
        ports.add(port);

        sandbox.stub(_currentUserData, "reset");

        handlers['talkilla.logout']({
          topic: 'talkilla.logout'
        });

        requests[0].respond(200, { 'Content-Type': 'text/plain' },
          'OK' );
      });

      afterEach(function() {
        ports.remove(port);
      });

      it("should post a success message",
        function() {
          sinon.assert.calledOnce(port.postEvent);
          sinon.assert.calledWith(port.postEvent, 'talkilla.logout-success');
          ports.remove(port);
        });

      it("should reset the current user data", function() {
        sinon.assert.calledOnce(_currentUserData.reset);
      });
    });

    it("should log failure, if the server failed to sign the user out",
      function() {
        handlers.postEvent = sandbox.spy();
        handlers['talkilla.logout']({
          topic: 'talkilla.logout'
        });

        expect(requests.length).to.equal(1);

        requests[0].respond(401, { 'Content-Type': 'text/plain' },
                            'Not Authorised' );

        sinon.assert.calledOnce(handlers.postEvent);
        sinon.assert.calledWith(handlers.postEvent, 'talkilla.error');
      });
  });

  describe("talkilla.conversation-open", function() {
    beforeEach(function() {
      browserPort = {postEvent: sandbox.spy()};
    });

    afterEach(function() {
      currentConversation = undefined;
      browserPort = undefined;
    });

    it("should create a new conversation object when receiving a " +
       "talkilla.conversation-open event", function() {
        handlers.postEvent = sinon.spy();
        handlers['talkilla.conversation-open']({
          topic: "talkilla.conversation-open",
          data: {}
        });

        expect(currentConversation).to.be.an.instanceOf(Conversation);
      });
  });

  describe("talkilla.chat-window-ready", function() {
    beforeEach(function() {
      browserPort = {postEvent: sandbox.spy()};
      _currentUserData = new UserData();
      currentConversation = {
        windowOpened: sandbox.spy()
      };
    });

    afterEach(function() {
      currentConversation = undefined;
      _currentUserData = undefined;
      browserPort = undefined;
    });

    it("should tell the conversation the window has opened when " +
      "receiving a talkilla.chat-window-ready",
      function () {
        var chatAppPort = {postEvent: sinon.spy()};
        _currentUserData.userName = "bob";

        handlers['talkilla.chat-window-ready'].bind(chatAppPort)({
          topic: "talkilla.chat-window-ready",
          data: {}
        });

        sinon.assert.called(currentConversation.windowOpened);
        sinon.assert.calledWithExactly(currentConversation.windowOpened,
          chatAppPort);
      });
  });

  describe("talkilla.sidebar-ready", function() {

    beforeEach(function() {
      _currentUserData = new UserData();
      sandbox.stub(_currentUserData, "send");
    });

    afterEach(function() {
      _currentUserData = undefined;
    });

    it("should notify new sidebars of the logged in user",
      function() {
        _currentUserData.userName = "jb";
        handlers.postEvent = sinon.spy();
        handlers['talkilla.sidebar-ready']({
          topic: "talkilla.sidebar-ready",
          data: {}
        });

        sinon.assert.calledOnce(handlers.postEvent);
        sinon.assert.calledWith(handlers.postEvent, "talkilla.login-success");
      });

    it("should notify new sidebars only if there's a logged in user",
      function() {
        handlers.postEvent = sinon.spy();
        handlers['talkilla.sidebar-ready']({
          topic: "talkilla.sidebar-ready",
          data: {}
        });

        sinon.assert.notCalled(handlers.postEvent);
      });

  });

  describe("talkilla.presence-request", function () {
    beforeEach(function() {
      _currentUserData = new UserData();
      sandbox.stub(_currentUserData, "send");
    });

    afterEach(function() {
      _currentUserData = null;
    });

    it("should notify new sidebars of current users",
      function() {
        _currentUserData.userName = "jb";
        _presenceSocket = {send: sinon.spy()};
        currentUsers = {};
        handlers.postEvent = sinon.spy();
        handlers['talkilla.presence-request']({
          topic: "talkilla.presence-request",
          data: {}
        });

        sinon.assert.calledWith(handlers.postEvent, "talkilla.users");
      });

    it("should request for the initial presence state" +
       "if there is no current users", function() {
        currentUsers = undefined;
        sandbox.stub(server, "send");
        handlers['talkilla.presence-request']({
          topic: "talkilla.presence-request",
          data: {}
        });

        sinon.assert.calledOnce(server.send);
        sinon.assert.calledWithExactly(server.send,
                                       {"presence_request": null});
      });
  });

  describe("talkilla.call-offer", function() {
    it("should send a websocket message when receiving talkilla.call-offer",
      function() {
        sandbox.stub(server, "send");
        var data = {
          peer: "tom",
          offer: { sdp: "sdp", type: "type" }
        };

        handlers['talkilla.call-offer']({
          topic: "talkilla.call-offer",
          data: data
        });

        sinon.assert.calledOnce(server.send);
        sinon.assert.calledWithExactly(server.send, {'call_offer': data });
      });
  });

  describe("talkilla.call-answer", function() {
    it("should send a websocket message when receiving talkilla.call-answer",
      function() {
        sandbox.stub(server, "send");
        var data = {
          peer: "fred",
          offer: { sdp: "sdp", type: "type" }
        };

        handlers['talkilla.call-answer']({
          topic: "talkilla.call-answer",
          data: data
        });

        sinon.assert.calledOnce(server.send);
        sinon.assert.calledWithExactly(server.send, { 'call_accepted': data });
      });
  });

  describe("talkilla.call-hangup", function() {
    afterEach(function() {
      currentConversation = undefined;
    });

    it("should send a websocket message when receiving talkilla.call-hangup",
      function() {
        sandbox.stub(server, "send");
        var data = {
          peer: "florian"
        };

        handlers['talkilla.call-hangup']({
          topic: "talkilla.call-hangup",
          data: data
        });

        sinon.assert.calledOnce(server.send);
        sinon.assert.calledWithExactly(server.send, { 'call_hangup': data });
      });
  });

});
<commit_msg>Add a comment about postEvent being called twice for login-failure<commit_after>/*global chai, sinon, ports:true, Port, PortCollection, handlers,
  _currentUserData:true, currentConversation:true, UserData,
  _presenceSocket:true, browserPort:true, currentUsers:true,
  Conversation, _config:true, _loginPending:true, _autologinPending: true,
  _cookieNickname:true, Server, server:true, _signinCallback */
/* jshint expr:true */

var expect = chai.expect;

describe('handlers', function() {
  var sandbox;

  beforeEach(function() {
    sandbox = sinon.sandbox.create();
    server = new Server({});
  });

  afterEach(function() {
    sandbox.restore();
  });

  describe("social.port-closing", function() {
    var port;

    beforeEach(function() {
      ports = new PortCollection();
      port = new Port({_portid: 42});
      ports.add(port);

      browserPort = {postEvent: sandbox.spy()};
    });

    afterEach(function() {
      currentConversation = undefined;
    });

    it("should remove a closed port on receiving social.port-closing",
      function() {
        handlers['social.port-closing'].bind(port)();
        expect(Object.keys(ports.ports)).to.have.length.of(0);
      });

    it("should clear the current conversation on receiving " +
       "social.port-closing for the conversation port", function() {
        currentConversation = new Conversation();
        currentConversation.port = port;

        handlers['social.port-closing'].bind(port)();
        expect(currentConversation).to.be.equal(undefined);
      });
  });

  describe("social.cookies-get-response", function() {
    var oldConfig;

    beforeEach(function() {
      oldConfig = _config;
      _cookieNickname = undefined;
    });

    afterEach(function() {
      _config = oldConfig;
      _cookieNickname = undefined;
    });

    it("should try to connect the presence socket",
      function() {
        _currentUserData = {};
        sandbox.stub(server, "autoconnect");
        var event = {
          data: [ {name: "nick", value: "Boriss"} ]
        };

        handlers['social.cookies-get-response'](event);

        sinon.assert.calledOnce(server.autoconnect);
        sinon.assert.calledWithExactly(server.autoconnect, "Boriss");
      });

    it("should NOT try to connect if there is no nick provided",
      function () {
        sandbox.stub(server, "autoconnect");

        handlers['social.cookies-get-response']({
          topic: "social.cookies-get-response",
          data: []
        });

        sinon.assert.notCalled(server.autoconnect);
      });

  });

  describe("talkilla.login", function() {
    var xhr, rootURL, socketStub, requests;

    beforeEach(function() {
      socketStub = sinon.stub(server, "connect");
      // XXX For some reason, sandbox.useFakeXMLHttpRequest doesn't want to work
      // nicely so we have to manually xhr.restore for now.
      xhr = sinon.useFakeXMLHttpRequest();
      requests = [];
      xhr.onCreate = function (req) { requests.push(req); };

      rootURL = 'http://fake';
      _currentUserData = new UserData({}, {
        ROOTURL: rootURL
      });
      sandbox.stub(_currentUserData, "send");
      _loginPending = _autologinPending = false;
    });

    afterEach(function() {
      _currentUserData = undefined;
      xhr.restore();
      socketStub.restore();
    });

    it("should call postEvent with a pending message if I pass in valid data",
      function() {
        handlers.postEvent = sandbox.spy();
        handlers['talkilla.login']({
          topic: "talkilla.login",
          data: {assertion: "fake assertion"}
        });
        sinon.assert.calledOnce(handlers.postEvent);
        sinon.assert.calledWith(handlers.postEvent, "talkilla.login-pending");
      });

    it("should post an ajax message to the server if I pass valid login data",
      function() {
        handlers['talkilla.login']({
          topic: "talkilla.login",
          data: {assertion: "fake assertion"}
        });
        expect(requests.length).to.equal(1);
        expect(requests[0].url).to.equal('/signin');
        expect(requests[0].requestBody).to.be.not.empty;
        expect(requests[0].requestBody)
          .to.be.equal('{"assertion":"fake assertion"}');
      });

    it("should not do anything if a login is already pending", function() {
      _loginPending = true;
      sandbox.stub(window, "_signinCallback");
      handlers.postEvent = sandbox.spy();

      handlers['talkilla.login']({
        topic: "talkilla.login",
        data: {assertion: "fake assertion"}
      });

      sinon.assert.notCalled(handlers.postEvent);
      sinon.assert.notCalled(_signinCallback);
    });

    it("should not do anything if an auto login is already pending",
      function() {
        _autologinPending = true;
        sandbox.stub(window, "_signinCallback");
        handlers.postEvent = sandbox.spy();

        handlers['talkilla.login']({
          topic: "talkilla.login",
          data: {assertion: "fake assertion"}
        });

        sinon.assert.notCalled(handlers.postEvent);
        sinon.assert.notCalled(_signinCallback);
      });

    describe("Failed login", function() {
      it("should call postEvent with a failure message if I pass in bad data",
        function() {
          handlers.postEvent = sandbox.spy();
          handlers['talkilla.login']({topic: "talkilla.login", data: null});
          sinon.assert.calledOnce(handlers.postEvent);
          sinon.assert.calledWith(handlers.postEvent, "talkilla.login-failure");
        });

      it("should post a fail message if the server rejected login",
        function() {
          handlers.postEvent = sinon.spy();
          handlers['talkilla.login']({
            topic: "talkilla.login",
            data: {assertion: "fake assertion"}
          });
          expect(requests.length).to.equal(1);

          requests[0].respond(401, {'Content-Type': 'text/plain'},
                              JSON.stringify({error: "some error"}));

          // This gets called twice - once for login-pending, tested elsewhere,
          // and once for the actual login-failure.
          sinon.assert.calledTwice(handlers.postEvent);
          sinon.assert.calledWith(handlers.postEvent, "talkilla.login-failure");
        });
    });

    describe("Accepted Login", function() {
      var port;

      beforeEach(function() {
        port = {id: "tests", postEvent: sandbox.spy()};
        ports.add(port);

        handlers['talkilla.login']({
          topic: "talkilla.login",
          data: {assertion: "fake assertion"}
        });
        expect(requests.length).to.equal(1);

        requests[0].respond(200, { 'Content-Type': 'application/json' },
          '{"nick":"jb"}' );
      });

      afterEach(function() {
        ports.remove(port);
      });

      it("should store the userName if the server accepted login", function() {
        expect(_currentUserData.userName).to.be.equal("jb");
      });

      it("should set the current user name if the server accepted login",
        function() {
          sinon.assert.calledOnce(_currentUserData.send);
        });

      it("should store the username if the server accepted login",
        function() {
          expect(_currentUserData.userName).to.equal('jb');
        });
    });
  });

  describe("talkilla.logout", function() {
    var xhr, requests;

    beforeEach(function() {
      // XXX For some reason, sandbox.useFakeXMLHttpRequest doesn't want to work
      // nicely so we have to manually xhr.restore for now.
      xhr = sinon.useFakeXMLHttpRequest();
      requests = [];
      xhr.onCreate = function (req) { requests.push(req); };

      sandbox.stub(UserData.prototype, "send");
      _currentUserData = new UserData({userName: 'romain'}, {});
      _presenceSocket = { close: sandbox.stub() };
    });

    afterEach(function() {
      _presenceSocket = undefined;
      _currentUserData = undefined;
      xhr.restore();
    });

    it("should post an ajax message to the server",
      function() {
        sandbox.stub(server, "signout");
        handlers['talkilla.logout']({
          topic: 'talkilla.logout'
        });
        sinon.assert.calledOnce(server.signout);
      });

    describe("Success logout", function() {
      var port;

      beforeEach(function () {
        port = {id: "tests", postEvent: sandbox.spy()};
        ports.add(port);

        sandbox.stub(_currentUserData, "reset");

        handlers['talkilla.logout']({
          topic: 'talkilla.logout'
        });

        requests[0].respond(200, { 'Content-Type': 'text/plain' },
          'OK' );
      });

      afterEach(function() {
        ports.remove(port);
      });

      it("should post a success message",
        function() {
          sinon.assert.calledOnce(port.postEvent);
          sinon.assert.calledWith(port.postEvent, 'talkilla.logout-success');
          ports.remove(port);
        });

      it("should reset the current user data", function() {
        sinon.assert.calledOnce(_currentUserData.reset);
      });
    });

    it("should log failure, if the server failed to sign the user out",
      function() {
        handlers.postEvent = sandbox.spy();
        handlers['talkilla.logout']({
          topic: 'talkilla.logout'
        });

        expect(requests.length).to.equal(1);

        requests[0].respond(401, { 'Content-Type': 'text/plain' },
                            'Not Authorised' );

        sinon.assert.calledOnce(handlers.postEvent);
        sinon.assert.calledWith(handlers.postEvent, 'talkilla.error');
      });
  });

  describe("talkilla.conversation-open", function() {
    beforeEach(function() {
      browserPort = {postEvent: sandbox.spy()};
    });

    afterEach(function() {
      currentConversation = undefined;
      browserPort = undefined;
    });

    it("should create a new conversation object when receiving a " +
       "talkilla.conversation-open event", function() {
        handlers.postEvent = sinon.spy();
        handlers['talkilla.conversation-open']({
          topic: "talkilla.conversation-open",
          data: {}
        });

        expect(currentConversation).to.be.an.instanceOf(Conversation);
      });
  });

  describe("talkilla.chat-window-ready", function() {
    beforeEach(function() {
      browserPort = {postEvent: sandbox.spy()};
      _currentUserData = new UserData();
      currentConversation = {
        windowOpened: sandbox.spy()
      };
    });

    afterEach(function() {
      currentConversation = undefined;
      _currentUserData = undefined;
      browserPort = undefined;
    });

    it("should tell the conversation the window has opened when " +
      "receiving a talkilla.chat-window-ready",
      function () {
        var chatAppPort = {postEvent: sinon.spy()};
        _currentUserData.userName = "bob";

        handlers['talkilla.chat-window-ready'].bind(chatAppPort)({
          topic: "talkilla.chat-window-ready",
          data: {}
        });

        sinon.assert.called(currentConversation.windowOpened);
        sinon.assert.calledWithExactly(currentConversation.windowOpened,
          chatAppPort);
      });
  });

  describe("talkilla.sidebar-ready", function() {

    beforeEach(function() {
      _currentUserData = new UserData();
      sandbox.stub(_currentUserData, "send");
    });

    afterEach(function() {
      _currentUserData = undefined;
    });

    it("should notify new sidebars of the logged in user",
      function() {
        _currentUserData.userName = "jb";
        handlers.postEvent = sinon.spy();
        handlers['talkilla.sidebar-ready']({
          topic: "talkilla.sidebar-ready",
          data: {}
        });

        sinon.assert.calledOnce(handlers.postEvent);
        sinon.assert.calledWith(handlers.postEvent, "talkilla.login-success");
      });

    it("should notify new sidebars only if there's a logged in user",
      function() {
        handlers.postEvent = sinon.spy();
        handlers['talkilla.sidebar-ready']({
          topic: "talkilla.sidebar-ready",
          data: {}
        });

        sinon.assert.notCalled(handlers.postEvent);
      });

  });

  describe("talkilla.presence-request", function () {
    beforeEach(function() {
      _currentUserData = new UserData();
      sandbox.stub(_currentUserData, "send");
    });

    afterEach(function() {
      _currentUserData = null;
    });

    it("should notify new sidebars of current users",
      function() {
        _currentUserData.userName = "jb";
        _presenceSocket = {send: sinon.spy()};
        currentUsers = {};
        handlers.postEvent = sinon.spy();
        handlers['talkilla.presence-request']({
          topic: "talkilla.presence-request",
          data: {}
        });

        sinon.assert.calledWith(handlers.postEvent, "talkilla.users");
      });

    it("should request for the initial presence state" +
       "if there is no current users", function() {
        currentUsers = undefined;
        sandbox.stub(server, "send");
        handlers['talkilla.presence-request']({
          topic: "talkilla.presence-request",
          data: {}
        });

        sinon.assert.calledOnce(server.send);
        sinon.assert.calledWithExactly(server.send,
                                       {"presence_request": null});
      });
  });

  describe("talkilla.call-offer", function() {
    it("should send a websocket message when receiving talkilla.call-offer",
      function() {
        sandbox.stub(server, "send");
        var data = {
          peer: "tom",
          offer: { sdp: "sdp", type: "type" }
        };

        handlers['talkilla.call-offer']({
          topic: "talkilla.call-offer",
          data: data
        });

        sinon.assert.calledOnce(server.send);
        sinon.assert.calledWithExactly(server.send, {'call_offer': data });
      });
  });

  describe("talkilla.call-answer", function() {
    it("should send a websocket message when receiving talkilla.call-answer",
      function() {
        sandbox.stub(server, "send");
        var data = {
          peer: "fred",
          offer: { sdp: "sdp", type: "type" }
        };

        handlers['talkilla.call-answer']({
          topic: "talkilla.call-answer",
          data: data
        });

        sinon.assert.calledOnce(server.send);
        sinon.assert.calledWithExactly(server.send, { 'call_accepted': data });
      });
  });

  describe("talkilla.call-hangup", function() {
    afterEach(function() {
      currentConversation = undefined;
    });

    it("should send a websocket message when receiving talkilla.call-hangup",
      function() {
        sandbox.stub(server, "send");
        var data = {
          peer: "florian"
        };

        handlers['talkilla.call-hangup']({
          topic: "talkilla.call-hangup",
          data: data
        });

        sinon.assert.calledOnce(server.send);
        sinon.assert.calledWithExactly(server.send, { 'call_hangup': data });
      });
  });

});
<|endoftext|>


<commit_before>var LsClass = function() {
}
LsClass.prototype = new Process();
LsClass.prototype.main = function(args) {
	var stdout = this.files['stdout'];
	var env = this.Environment.array;
	var folder = "";
	if (args.length == 1) {
		folder = env['PWD'];
	} else {
		folder = args[1];
	}
	var files = Kernel.Filesystem.getDirectory(folder);
	if (files.error) {
		stdout.write("ls: cannot access " + folder + ": " + files.error + "\n");
		this.exit(2);
		return; // ugly, change this
	}
	for (var i = 0; i < files.length; i++) {
		if (files[i].substring(0, 1) != ".")
			stdout.write(files[i] + "\n");
	}
	this.exit(0);
}
<commit_msg>fix fix<commit_after>var LsClass = function() {
}
LsClass.prototype = new Process();
LsClass.prototype.main = function(args) {
	var stdout = this.files['stdout'];
	var env = this.Environment.array;
	var folder = "";
	if (args.length == 1) {
		folder = env['PWD'];
	} else {
		folder = args[1];
	}
	var files = Kernel.Filesystem.getDirectory(folder);
	if (files.error) {
		stdout.write("ls: cannot access " + folder + ": " + files.error + "\n");
		this.exit(2);
	}
	for (var i = 0; i < files.length; i++) {
		if (files[i].substring(0, 1) != ".")
			stdout.write(files[i] + "\n");
	}
	this.exit(0);
}
<|endoftext|>


<commit_before>

import SimpleMDE from 'simplemde';

import {addSentenceMode} from './sentence';
import {addReplaceMode} from './replace';
import {addHardWordMode} from './hardWords';

export const setup = () => {
  captureLanguageXHR();
  var simplemde = new SimpleMDE({
  	autofocus: true,
  	autosave: {
  		enabled: false,
  		uniqueId: "MyUniqueID",
  		delay: 1000,
  	},
  	blockStyles: {
  		bold: "__",
  		italic: "_"
  	},
  	element: document.getElementById("main_editor"),
  	forceSync: true,
  	hideIcons: ["fullscreen","table","code","side-by-side"],
  	indentWithTabs: false,
  	initialValue: "",
  	insertTexts: {
  		horizontalRule: ["", "\n\n-----\n\n"],
  		image: ["![](http://", ")"],
  		link: ["[", "](http://)"],
  		table: ["", "\n\n| Column 1 | Column 2 | Column 3 |\n| -------- | -------- | -------- |\n| Text     | Text      | Text     |\n\n"],
  	},
  	lineWrapping: true,
  	parsingConfig: {
  		allowAtxHeaderWithoutSpace: true,
  		strikethrough: false,
  		underscoresBreakWords: true,
  	},
  	placeholder: "",

  	promptURLs: true,
  	renderingConfig: {
  		singleLineBreaks: false,
  		codeSyntaxHighlighting: false,
  	},
  	shortcuts: {
  		drawTable: "Cmd-Alt-T"
  	},
  	showIcons: ["code", "table"],
  	spellChecker: true,
  	status: ["words"],
  	styleSelectedText: false,
  	tabSize: 4,
    //toolbar: false,
  	toolbarTips: true
  });
  addSentenceMode(simplemde);
  addReplaceMode(simplemde);
  addHardWordMode(simplemde);

  simplemde.codemirror.on("update", function() {
    setupReplaceHandler();
    setupReadabilityHandler();
  });

  return simplemde;
};

function captureLanguageXHR(){
  (function(open) {

    XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {

        console.log(url);
        if(url == 'https://cdn.jsdelivr.net/codemirror.spell-checker/latest/en_US.aff'){
          url = 'https://gitcdn.xyz/cdn/colugo/jameson/master/lang/en_AU.aff';
        }
        if(url == 'https://cdn.jsdelivr.net/codemirror.spell-checker/latest/en_US.dic'){
          url = 'https://gitcdn.xyz/cdn/colugo/jameson/master/lang/en_AU.dic';
        }
        open.call(this, method, url, async, user, pass);
    };

  })(XMLHttpRequest.prototype.open);
}
<commit_msg>Removed logging from XHR hook<commit_after>

import SimpleMDE from 'simplemde';

import {addSentenceMode} from './sentence';
import {addReplaceMode} from './replace';
import {addHardWordMode} from './hardWords';

export const setup = () => {
  captureLanguageXHR();
  var simplemde = new SimpleMDE({
  	autofocus: true,
  	autosave: {
  		enabled: false,
  		uniqueId: "MyUniqueID",
  		delay: 1000,
  	},
  	blockStyles: {
  		bold: "__",
  		italic: "_"
  	},
  	element: document.getElementById("main_editor"),
  	forceSync: true,
  	hideIcons: ["fullscreen","table","code","side-by-side"],
  	indentWithTabs: false,
  	initialValue: "",
  	insertTexts: {
  		horizontalRule: ["", "\n\n-----\n\n"],
  		image: ["![](http://", ")"],
  		link: ["[", "](http://)"],
  		table: ["", "\n\n| Column 1 | Column 2 | Column 3 |\n| -------- | -------- | -------- |\n| Text     | Text      | Text     |\n\n"],
  	},
  	lineWrapping: true,
  	parsingConfig: {
  		allowAtxHeaderWithoutSpace: true,
  		strikethrough: false,
  		underscoresBreakWords: true,
  	},
  	placeholder: "",

  	promptURLs: true,
  	renderingConfig: {
  		singleLineBreaks: false,
  		codeSyntaxHighlighting: false,
  	},
  	shortcuts: {
  		drawTable: "Cmd-Alt-T"
  	},
  	showIcons: ["code", "table"],
  	spellChecker: true,
  	status: ["words"],
  	styleSelectedText: false,
  	tabSize: 4,
    //toolbar: false,
  	toolbarTips: true
  });
  addSentenceMode(simplemde);
  addReplaceMode(simplemde);
  addHardWordMode(simplemde);

  simplemde.codemirror.on("update", function() {
    setupReplaceHandler();
    setupReadabilityHandler();
  });

  return simplemde;
};

function captureLanguageXHR(){
  (function(open) {

    XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {

        if(url == 'https://cdn.jsdelivr.net/codemirror.spell-checker/latest/en_US.aff'){
          url = 'https://gitcdn.xyz/cdn/colugo/jameson/master/lang/en_AU.aff';
        }
        if(url == 'https://cdn.jsdelivr.net/codemirror.spell-checker/latest/en_US.dic'){
          url = 'https://gitcdn.xyz/cdn/colugo/jameson/master/lang/en_AU.dic';
        }
        open.call(this, method, url, async, user, pass);
    };

  })(XMLHttpRequest.prototype.open);
}
<|endoftext|>