<commit_before>/**
 * test/model.js - Tests for Cantina Redis.
 */
var utils = require('cantina-utils')
  , assert = require('assert')
  , redis = require('haredis')
  , RedisModel = require('../').RedisModel
  , RedisCollection = require('../').RedisCollection
  ;

describe('model/collection', function() {
  var coll, cleanup = [], MyModel;
  function assertModel(model) {
    assert.ok(model, 'model is ok');
    cleanup.push(model);
  }
  function assertValid(err) {
    if (utils.isArray(err)) {
      console.error(err);
      assert.fail('validation errors');
    }
    else {
      assert.ifError(err);
    }
  }
  before(function() {
    MyModel = function MyModel(attrs, options) {
      MyModel.super.call(this, attrs, options);
    }
    utils.inherits(MyModel, RedisModel);
    MyModel.schema = {
      properties: {
        job: {
          type: 'string',
          index: true
        },
        color: {
          type: 'string',
          index: true
        },
        timestamp: {
          type: 'number'
        }
      }
    };
    coll = new RedisCollection({client: redis.createClient(), model: MyModel});
  });

  after(function(done) {
    var model, tasks = [];
    while (model = cleanup.pop()) {
      (function(model) {
        tasks.push(function(cb) {
          model.destroy(cb);
        });
      })(model);
    }
    utils.async.parallel(tasks, function() {
      assert.strictEqual(cleanup.length, 0, 'no models left over');
      done();
    });
  });

  var myId;
  it('can create a model', function(done) {
    var model = new MyModel({name: 'carlos'}, coll);
    model.save(function(err) {
      assertValid(err);
      myId = model.id;
      assert.ok(myId);
      done(err);
    });
  });

  var myModel;
  it('can get the model', function(done) {
    coll.get(myId, function(err, model) {
      assertValid(err);
      assert.ok(model, 'got a model back');
      assert.strictEqual(model.id, myId);
      assert.strictEqual(model.properties.name, 'carlos');
      done();
    });
  });

  it('can update the model', function(done) {
    coll.update(myId, {name: 'scooby'}, function(err, model) {
      assertValid(err);
      assert.ok(model, 'got a model back');
      assert.strictEqual(model.id, myId);
      assert.strictEqual(model.properties.name, 'scooby');
      myModel = model;
      done();
    });
  });

  it('can destroy the model', function(done) {
    myModel.destroy(function(err) {
      assert.ifError(err);
      coll.get(myId, function(err, model) {
        assert.ifError(err);
        assert.strictEqual(model, null, 'model was deleted');
        done();
      });
    });
  });

  it('can run before/after hooks', function(done) {
    var model = new MyModel({name: 'buster'}, coll);
    assertModel(model);
    model.on('save:before', function(model) {
      this.properties.job = 'dog';
    });
    model.save(function(err) {
      assertValid(err);
      coll.get(model.id, function(err, saved) {
        assert.ifError(err);
        assert.strictEqual(saved.properties.job, 'dog', 'property from hook was saved');
        done();
      });
    });
  });

  it('can query an index', function(done) {
    coll.create({name: 'egon', job: 'ghostbuster'}, function(err, model) {
      assertValid(err);
      assertModel(model);
      coll.create({name: 'kobe', job: 'baller'}, function(err, model) {
        assertValid(err);
        assertModel(model);
        coll.find({job: 'ghostbuster'}, function(err, models) {
          assert.ifError(err);
          assert.strictEqual(models.length, 1);
          assert.strictEqual(models[0].properties.name, 'egon');
          done();
        });
      });
    });
  });

  it('can findOne with an indexed query', function(done) {
    coll.create({name: 'egon', job: 'ghostbuster'}, function(err, model) {
      assertValid(err);
      assertModel(model);
      coll.create({name: 'kobe', job: 'baller'}, function(err, model) {
        assertValid(err);
        assertModel(model);
        coll.findOne({job: 'ghostbuster'}, function(err, model) {
          assert.ifError(err);
          assert.strictEqual(model.properties.name, 'egon');
          done();
        });
      });
    });
  });

  it('can sort and limit', function(done) {
    var tasks = [], max = 0;
    for (var i = 0; i < 100; i++) {
      tasks.push(function(cb) {
        var rand = Math.random();
        max = Math.max(rand, max);
        coll.create({timestamp: rand}, function(err, model) {
          assertModel(model);
          cb(err, model);
        });
      })
    }
    utils.async.parallel(tasks, function(err, results) {
      assertValid(err);
      assert.ok(results, 'created some timestamp records');
      coll.find({}, {sort: 'timestamp', desc: true, limit: 70, skip: 1}, function(err, models) {
        assert.ifError(err);
        assert.ok(models[0].properties.timestamp < max, 'max timestamp skipped');
        assert.strictEqual(models.length, 70, 'correct limit');
        var last;
        models.forEach(function(model) {
          if (last) {
            assert.ok(model.properties.timestamp < last, 'timestamp decreasing');
          }
          last = model.properties.timestamp;
        });
        done();
      });
    });
  });

  it('can filter with sort and skip/limit', function(done) {
    var tasks = [], blue_min = 1;
    coll.schema.timestamp = {
      type: 'number'
    };
    for (var i = 0; i < 10; i++) {
      tasks.push(function(cb) {
        var rand = Math.random();
        coll.create({color: 'red', timestamp: rand}, function(err, model) {
          assertModel(model);
          cb(err, model);
        });
      })
    }
    for (var i = 0; i < 10; i++) {
      tasks.push(function(cb) {
        var rand = Math.random();
        blue_min = Math.min(rand, blue_min);
        coll.create({color: 'blue', timestamp: rand}, function(err, model) {
          assertModel(model);
          cb(err, model);
        });
      })
    }
    utils.async.parallel(tasks, function(err, results) {
      assertValid(err);
      assert.ok(results, 'created some records');
      coll.find({color: 'blue'}, {sort: 'timestamp', limit: 10, skip: 1}, function(err, models) {
        assert.ifError(err);
        assert.ok(models[0].properties.timestamp > blue_min, 'blue_min timestamp skipped');
        assert.strictEqual(models.length, 9, 'correct filter/limit/skip');
        var last;
        models.forEach(function(model) {
          if (last) {
            assert.ok(model.properties.timestamp > last, 'timestamp ascending');
          }
          last = model.properties.timestamp;
          assert.strictEqual(model.properties.color, 'blue', 'filtered to blue');
        });
        done();
      });
    });
  });

  it('can store a complex object', function(done) {
    var obj = {
      _id: 1234,
      history: {
        'may 14th 2012': {
          birthdays: ['erin', 'mark', new Date(), /something/],
          hours: 24
        },
        today: {
          date: new Date()
        }
      },
      patterns: [/blah/, /etc/]
    };
    coll.create(obj, function(err, model) {
      assertValid(err);
      assertModel(model);
      assert.deepEqual(model.toJSON(), obj);
      done();
    });
  });

  describe('separate collection', function() {
    var fruit;
    before(function() {
      function Fruit(attrs, options) {
        Fruit.super.call(this, attrs, options);
      }
      utils.inherits(Fruit, RedisModel);
      Fruit.schema = {
        name: 'fruit',
        properties: {
          color: {
            type: 'number',
            index: true
          },
          type: {
            type: 'string'
          }
        }
      };
      fruit = new RedisCollection({model: Fruit, client: redis.createClient()});
    });

    it('can create a new collection', function(done) {
      fruit.find({}, function(err, models) {
        assert.ifError(err);
        assert.strictEqual(models.length, 0, 'new collection has no models');
        done();
      });
    });

    it('can add a model to new collection', function(done) {
      fruit.create({type: 'banana', color: 0xE3CF57}, function(err, model) {
        assertValid(err);
        assert.strictEqual(model.properties.color, 0xE3CF57);
        assertModel(model);
        done();
      });
    });

    it('can add a second model to new collection', function(done) {
      fruit.create({type: 'orange', color: 0xFF7D40}, function(err, model) {
        assertValid(err);
        assert.strictEqual(model.properties.color, 0xFF7D40);
        assertModel(model);
        done();
      });
    });

    it('can query new collection', function(done) {
      fruit.find({color: 0xE3CF57}, function(err, models) {
        assert.ifError(err);
        assert.strictEqual(models.length, 1);
        assert.strictEqual(models[0].properties.type, 'banana');
        done();
      });
    });
  });
});
<commit_msg>Use createModel() in tests.<commit_after>/**
 * test/model.js - Tests for Cantina Redis.
 */
var utils = require('cantina-utils')
  , assert = require('assert')
  , redis = require('haredis')
  , lib = require('../')
  , RedisModel = lib.RedisModel
  , RedisCollection = lib.RedisCollection
  ;

describe('model/collection', function() {
  var coll, cleanup = [], MyModel;
  function assertModel(model) {
    assert.ok(model, 'model is ok');
    cleanup.push(model);
  }
  function assertValid(err) {
    if (utils.isArray(err)) {
      console.error(err);
      assert.fail('validation errors');
    }
    else {
      assert.ifError(err);
    }
  }
  before(function() {
    MyModel = lib.createModel(RedisModel, {
      schema: {
        properties: {
          job: {
            type: 'string',
            index: true
          },
          color: {
            type: 'string',
            index: true
          },
          timestamp: {
            type: 'number'
          }
        }
      }
    });
    coll = new RedisCollection({client: redis.createClient(), model: MyModel});
  });

  after(function(done) {
    var model, tasks = [];
    while (model = cleanup.pop()) {
      (function(model) {
        tasks.push(function(cb) {
          model.destroy(cb);
        });
      })(model);
    }
    utils.async.parallel(tasks, function() {
      assert.strictEqual(cleanup.length, 0, 'no models left over');
      done();
    });
  });

  var myId;
  it('can create a model', function(done) {
    var model = new MyModel({name: 'carlos'}, coll);
    model.save(function(err) {
      assertValid(err);
      myId = model.id;
      assert.ok(myId);
      done(err);
    });
  });

  var myModel;
  it('can get the model', function(done) {
    coll.get(myId, function(err, model) {
      assertValid(err);
      assert.ok(model, 'got a model back');
      assert.strictEqual(model.id, myId);
      assert.strictEqual(model.properties.name, 'carlos');
      done();
    });
  });

  it('can update the model', function(done) {
    coll.update(myId, {name: 'scooby'}, function(err, model) {
      assertValid(err);
      assert.ok(model, 'got a model back');
      assert.strictEqual(model.id, myId);
      assert.strictEqual(model.properties.name, 'scooby');
      myModel = model;
      done();
    });
  });

  it('can destroy the model', function(done) {
    myModel.destroy(function(err) {
      assert.ifError(err);
      coll.get(myId, function(err, model) {
        assert.ifError(err);
        assert.strictEqual(model, null, 'model was deleted');
        done();
      });
    });
  });

  it('can run before/after hooks', function(done) {
    var model = new MyModel({name: 'buster'}, coll);
    assertModel(model);
    model.on('save:before', function(model) {
      this.properties.job = 'dog';
    });
    model.save(function(err) {
      assertValid(err);
      coll.get(model.id, function(err, saved) {
        assert.ifError(err);
        assert.strictEqual(saved.properties.job, 'dog', 'property from hook was saved');
        done();
      });
    });
  });

  it('can query an index', function(done) {
    coll.create({name: 'egon', job: 'ghostbuster'}, function(err, model) {
      assertValid(err);
      assertModel(model);
      coll.create({name: 'kobe', job: 'baller'}, function(err, model) {
        assertValid(err);
        assertModel(model);
        coll.find({job: 'ghostbuster'}, function(err, models) {
          assert.ifError(err);
          assert.strictEqual(models.length, 1);
          assert.strictEqual(models[0].properties.name, 'egon');
          done();
        });
      });
    });
  });

  it('can findOne with an indexed query', function(done) {
    coll.create({name: 'egon', job: 'ghostbuster'}, function(err, model) {
      assertValid(err);
      assertModel(model);
      coll.create({name: 'kobe', job: 'baller'}, function(err, model) {
        assertValid(err);
        assertModel(model);
        coll.findOne({job: 'ghostbuster'}, function(err, model) {
          assert.ifError(err);
          assert.strictEqual(model.properties.name, 'egon');
          done();
        });
      });
    });
  });

  it('can sort and limit', function(done) {
    var tasks = [], max = 0;
    for (var i = 0; i < 100; i++) {
      tasks.push(function(cb) {
        var rand = Math.random();
        max = Math.max(rand, max);
        coll.create({timestamp: rand}, function(err, model) {
          assertModel(model);
          cb(err, model);
        });
      })
    }
    utils.async.parallel(tasks, function(err, results) {
      assertValid(err);
      assert.ok(results, 'created some timestamp records');
      coll.find({}, {sort: 'timestamp', desc: true, limit: 70, skip: 1}, function(err, models) {
        assert.ifError(err);
        assert.ok(models[0].properties.timestamp < max, 'max timestamp skipped');
        assert.strictEqual(models.length, 70, 'correct limit');
        var last;
        models.forEach(function(model) {
          if (last) {
            assert.ok(model.properties.timestamp < last, 'timestamp decreasing');
          }
          last = model.properties.timestamp;
        });
        done();
      });
    });
  });

  it('can filter with sort and skip/limit', function(done) {
    var tasks = [], blue_min = 1;
    coll.schema.timestamp = {
      type: 'number'
    };
    for (var i = 0; i < 10; i++) {
      tasks.push(function(cb) {
        var rand = Math.random();
        coll.create({color: 'red', timestamp: rand}, function(err, model) {
          assertModel(model);
          cb(err, model);
        });
      })
    }
    for (var i = 0; i < 10; i++) {
      tasks.push(function(cb) {
        var rand = Math.random();
        blue_min = Math.min(rand, blue_min);
        coll.create({color: 'blue', timestamp: rand}, function(err, model) {
          assertModel(model);
          cb(err, model);
        });
      })
    }
    utils.async.parallel(tasks, function(err, results) {
      assertValid(err);
      assert.ok(results, 'created some records');
      coll.find({color: 'blue'}, {sort: 'timestamp', limit: 10, skip: 1}, function(err, models) {
        assert.ifError(err);
        assert.ok(models[0].properties.timestamp > blue_min, 'blue_min timestamp skipped');
        assert.strictEqual(models.length, 9, 'correct filter/limit/skip');
        var last;
        models.forEach(function(model) {
          if (last) {
            assert.ok(model.properties.timestamp > last, 'timestamp ascending');
          }
          last = model.properties.timestamp;
          assert.strictEqual(model.properties.color, 'blue', 'filtered to blue');
        });
        done();
      });
    });
  });

  it('can store a complex object', function(done) {
    var obj = {
      _id: 1234,
      history: {
        'may 14th 2012': {
          birthdays: ['erin', 'mark', new Date(), /something/],
          hours: 24
        },
        today: {
          date: new Date()
        }
      },
      patterns: [/blah/, /etc/]
    };
    coll.create(obj, function(err, model) {
      assertValid(err);
      assertModel(model);
      assert.deepEqual(model.toJSON(), obj);
      done();
    });
  });

  describe('separate collection', function() {
    var fruit;
    before(function() {
      var Fruit = lib.createModel(RedisModel, {
        schema: {
          name: 'fruit',
          properties: {
            color: {
              type: 'number',
              index: true
            },
            type: {
              type: 'string'
            }
          }
        }
      });
      fruit = new RedisCollection({model: Fruit, client: redis.createClient()});
    });

    it('can create a new collection', function(done) {
      fruit.find({}, function(err, models) {
        assert.ifError(err);
        assert.strictEqual(models.length, 0, 'new collection has no models');
        done();
      });
    });

    it('can add a model to new collection', function(done) {
      fruit.create({type: 'banana', color: 0xE3CF57}, function(err, model) {
        assertValid(err);
        assert.strictEqual(model.properties.color, 0xE3CF57);
        assertModel(model);
        done();
      });
    });

    it('can add a second model to new collection', function(done) {
      fruit.create({type: 'orange', color: 0xFF7D40}, function(err, model) {
        assertValid(err);
        assert.strictEqual(model.properties.color, 0xFF7D40);
        assertModel(model);
        done();
      });
    });

    it('can query new collection', function(done) {
      fruit.find({color: 0xE3CF57}, function(err, models) {
        assert.ifError(err);
        assert.strictEqual(models.length, 1);
        assert.strictEqual(models[0].properties.type, 'banana');
        done();
      });
    });
  });
});
<|endoftext|>


<commit_before>var visitPreOrder = function(root, callback) {
  callback(root)

  if (root.children) {
    for (var i = root.children.length - 1; i >= 0; i--){
     visitPreOrder(root.children[i], callback)
    };
  }
}

var draw = function(selector, json, server_name, player_name, options) {
  // Handle zooming
  var zoomed = function () {
    g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
  }

  // Zoom to player's node by name
  var zoomTo = function(name) {
    var d = null;

    for(var i = 0; i < nodes.length; i++) {
      if(nodes[i].name == name) {
        d = nodes[i]
      }
    }

    if(d == null) return;

    var translate = [width/2-d.y,  height/2 - d.x];

    svg.transition()
        .call(zoom.translate(translate).event);
  }

  var width = options.width || 300,
     height = options.height || 300;

  var cluster = d3.layout.cluster()
    .size([width, height]);

  var nodes = cluster.nodes(json)
      links = cluster.links(nodes);

  // Calculate root distances (root = 1)
  visitPreOrder(nodes[0], function(node) {
    node.rootDist = (node.parent ? node.parent.rootDist : 0) + 1
  })

  var rootDists = nodes.map(function(n) { return n.rootDist; });

  // Calculate virtual canvas size
  var fixedDepth = 150;
  var virtualSize = [d3.max(rootDists) * fixedDepth, height];

  cluster.size(virtualSize);

  var diagonal = d3.svg.diagonal()
    .projection(function(d) { return [d.y, d.x]; });

  var zoom = d3.behavior.zoom()
    .translate([0,0])
    .scale(1)
    .scaleExtent([.8, 2])
    .on("zoom", zoomed);

  var svg = d3.select(selector).append("svg")
    .attr("width", width)
    .attr("height", height);

  svg.append("rect")
    .attr("class", "overlay")
    .attr("width", width)
    .attr("height", height);

  var g = svg.append("g");

  svg.
    call(zoom).
    call(zoom.event);

  // Normalize so each level is 150px away from the adjacent one
  nodes.forEach(function(d) { d.y = d.rootDist * fixedDepth; });

  var link = g.selectAll(".link")
    .data(links)
    .enter().append("path")
      .attr("class", "link")
      .attr("d", diagonal)
      .attr("fill", function(d) { return "none"; })
      .attr("stroke", function(d) { return "#AAA"; })
      .attr("stroke-width", function(d) { return "1px"; });

  var node = g.selectAll(".node")
    .data(nodes)
    .enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })

  node.append("svg:a")
    .attr("xlink:href", function(d) { return ["/", server_name, "/", d.name].join(""); })
    .append("circle")
      .attr("r", 4.5)
      .style("fill", function(d) { return d.name == player_name ? "gold" : "black"; })
      .style("stroke", function(d) { return "white"; })
      .style("stroke-width", function(d) { return "1px"; });

  node.append("svg:a")
    .attr("xlink:href", function(d) { return ["/", server_name, "/", d.name].join(""); })
    .append("text")
      .attr("dx", function(d) { return d.children ? -8 : 8; })
      .attr("dy", 3)
      .style("text-anchor", function(d) { return d.children ? "end" : "start"; })
      .style("font-family", function(d) { return "'Open Sans', Sans-serif" })
      .style("font-size", function(d) { return "10px" })
      .style("fill", function(d) { return d.name == player_name ? "gold" : "white" })
      .text(function(d) { return d.name; });

  if(player_name) {
    zoomTo(player_name);
  }
};
<commit_msg>Fix text spacing in zoom function<commit_after>var visitPreOrder = function(root, callback) {
  callback(root)

  if (root.children) {
    for (var i = root.children.length - 1; i >= 0; i--){
     visitPreOrder(root.children[i], callback)
    };
  }
}

var draw = function(selector, json, server_name, player_name, options) {
  // Handle zooming
  var zoomed = function () {
    g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
  }

  // Zoom to player's node by name
  var zoomTo = function(name) {
    var d = null;

    for(var i = 0; i < nodes.length; i++) {
      if(nodes[i].name == name) {
        d = nodes[i]
      }
    }

    if(d == null) return;

    var translate = [width/2 - d.y,  height/2 - d.x];

    svg.transition()
        .call(zoom.translate(translate).event);
  }

  var width = options.width || 300,
     height = options.height || 300;

  var cluster = d3.layout.cluster()
    .size([width, height]);

  var nodes = cluster.nodes(json)
      links = cluster.links(nodes);

  // Calculate root distances (root = 1)
  visitPreOrder(nodes[0], function(node) {
    node.rootDist = (node.parent ? node.parent.rootDist : 0) + 1
  })

  var rootDists = nodes.map(function(n) { return n.rootDist; });

  // Calculate virtual canvas size
  var fixedDepth = 150;
  var virtualSize = [d3.max(rootDists) * fixedDepth, height];

  cluster.size(virtualSize);

  var diagonal = d3.svg.diagonal()
    .projection(function(d) { return [d.y, d.x]; });

  var zoom = d3.behavior.zoom()
    .translate([0,0])
    .scale(1)
    .scaleExtent([.8, 2])
    .on("zoom", zoomed);

  var svg = d3.select(selector).append("svg")
    .attr("width", width)
    .attr("height", height);

  svg.append("rect")
    .attr("class", "overlay")
    .attr("width", width)
    .attr("height", height);

  var g = svg.append("g");

  svg.
    call(zoom).
    call(zoom.event);

  // Normalize so each level is 150px away from the adjacent one
  nodes.forEach(function(d) { d.y = d.rootDist * fixedDepth; });

  var link = g.selectAll(".link")
    .data(links)
    .enter().append("path")
      .attr("class", "link")
      .attr("d", diagonal)
      .attr("fill", function(d) { return "none"; })
      .attr("stroke", function(d) { return "#AAA"; })
      .attr("stroke-width", function(d) { return "1px"; });

  var node = g.selectAll(".node")
    .data(nodes)
    .enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })

  node.append("svg:a")
    .attr("xlink:href", function(d) { return ["/", server_name, "/", d.name].join(""); })
    .append("circle")
      .attr("r", 4.5)
      .style("fill", function(d) { return d.name == player_name ? "gold" : "black"; })
      .style("stroke", function(d) { return "white"; })
      .style("stroke-width", function(d) { return "1px"; });

  node.append("svg:a")
    .attr("xlink:href", function(d) { return ["/", server_name, "/", d.name].join(""); })
    .append("text")
      .attr("dx", function(d) { return d.children ? -8 : 8; })
      .attr("dy", 3)
      .style("text-anchor", function(d) { return d.children ? "end" : "start"; })
      .style("font-family", function(d) { return "'Open Sans', Sans-serif" })
      .style("font-size", function(d) { return "10px" })
      .style("fill", function(d) { return d.name == player_name ? "gold" : "white" })
      .text(function(d) { return d.name; });

  if(player_name) {
    zoomTo(player_name);
  }
};
<|endoftext|>


<commit_before>//based loosely on bostock's example and
//http://bl.ocks.org/d3noob/5141278

var width = 960;
var height = 600;

var force = d3.layout.force()
    .charge([-250])
    .linkDistance([50])
    .size([width, height])
    .nodes(nodes)
    .links(links)
    .start();

var colors = d3.scale.category20(); //10 or 20

var svg = d3.select("#vis").append("svg")
    .attr("width", width)
    .attr("height", height);

svg.append("svg:defs").selectAll("marker")
    .data(["end"])// Different path types defined here
      .enter().append("svg:marker")
      .attr("id", String)
      .attr("viewBox", "0 -5 10 10")
      .attr("refX", 15)
      .attr("refY", -3)
      .attr("markerWidth", 6)
      .attr("markerHeight", 6)
      .attr("orient", "auto")
      .append("svg:path")
      .attr("d", "M0,-5L10,0L0,5");

var link = svg.append("svg:g").selectAll("path")
    .data(links)
    .enter().append("svg:path")
    .attr("class", function(d) { return "link " + d.type; })
    .attr("class", "link")
    .attr("marker-end", "url(#end)")
    .style("stroke-width", 1.5)
    .style("stroke", "#666")
    .style("fill", "none")

var node = svg.selectAll(".node")
    .data(nodes)
    .enter().append("g")
    .attr("class", "node")
    .on("mouseover", mouseover)
    .on("mouseout", mouseout)
    .call(force.drag);

node
    .append("circle")
    .attr("r", 7)
    .style("fill", function(d, i) {
        return colors(i);
    })

node
    .append("text")
    .attr("x", 12)
    .attr("dy", ".35em")
    .text(function(d) {
        return d.name;
    });

force.on("tick", function() {
    link
        .attr("d", function(d) {
            var dx = d.target.x - d.source.x,
                dy = d.target.y - d.source.y,
                dr = Math.sqrt(dx * dx + dy * dy);
            return "M" +
                d.source.x + "," +
                d.source.y + "A" +
                dr + "," + dr + " 0 0,1 " +
                d.target.x + "," +
                d.target.y;
        });

    node
        .attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")";
        });
})

function mouseover() {

    d3.select(this).select("text").transition()
        .duration(750)
        .style("font-weight","bold")
    d3.select(this).select("circle").transition()
        .duration(750)
        .attr("r", 16);
}

function mouseout() {

    d3.select(this).select("text").transition()
        .duration(750)
        .style("font-weight","normal")
    d3.select(this).select("circle").transition()
        .duration(750)
        .attr("r", 8);
}
<commit_msg>a more reasonable length for links<commit_after>//based loosely on bostock's example and
//http://bl.ocks.org/d3noob/5141278

var width = 960;
var height = 600;

var force = d3.layout.force()
    .charge([-250])
    .linkDistance([75])
    .size([width, height])
    .nodes(nodes)
    .links(links)
    .start();

var colors = d3.scale.category20(); //10 or 20

var svg = d3.select("#vis").append("svg")
    .attr("width", width)
    .attr("height", height);

svg.append("svg:defs").selectAll("marker")
    .data(["end"])// Different path types defined here
      .enter().append("svg:marker")
      .attr("id", String)
      .attr("viewBox", "0 -5 10 10")
      .attr("refX", 15)
      .attr("refY", -3)
      .attr("markerWidth", 6)
      .attr("markerHeight", 6)
      .attr("orient", "auto")
      .append("svg:path")
      .attr("d", "M0,-5L10,0L0,5");

var link = svg.append("svg:g").selectAll("path")
    .data(links)
    .enter().append("svg:path")
    .attr("class", function(d) { return "link " + d.type; })
    .attr("class", "link")
    .attr("marker-end", "url(#end)")
    .style("stroke-width", 1.5)
    .style("stroke", "#666")
    .style("fill", "none")

var node = svg.selectAll(".node")
    .data(nodes)
    .enter().append("g")
    .attr("class", "node")
    .on("mouseover", mouseover)
    .on("mouseout", mouseout)
    .call(force.drag);

node
    .append("circle")
    .attr("r", 7)
    .style("fill", function(d, i) {
        return colors(i);
    })

node
    .append("text")
    .attr("x", 12)
    .attr("dy", ".35em")
    .text(function(d) {
        return d.name;
    });

force.on("tick", function() {
    link
        .attr("d", function(d) {
            var dx = d.target.x - d.source.x,
                dy = d.target.y - d.source.y,
                dr = Math.sqrt(dx * dx + dy * dy);
            return "M" +
                d.source.x + "," +
                d.source.y + "A" +
                dr + "," + dr + " 0 0,1 " +
                d.target.x + "," +
                d.target.y;
        });

    node
        .attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")";
        });
})

function mouseover() {

    d3.select(this).select("text").transition()
        .duration(750)
        .style("font-weight","bold")
    d3.select(this).select("circle").transition()
        .duration(750)
        .attr("r", 16);
}

function mouseout() {

    d3.select(this).select("text").transition()
        .duration(750)
        .style("font-weight","normal")
    d3.select(this).select("circle").transition()
        .duration(750)
        .attr("r", 8);
}
<|endoftext|>


<commit_before>const assert = require('assert');
const XRegExp = require('xregexp');
const regex = XRegExp('(?:^|\\s|\\>|;)(@[\\p{L}\\d\\-_.]+)', 'g');

// from NodeBB utils.js
const utils = {
	slugify: function (str, preserveCase) {
		if (!str) {
			return '';
		}
		str = str.replace(utils.trimRegex, '');
		if (utils.isLatin.test(str)) {
			str = str.replace(utils.invalidLatinChars, '-');
		} else {
			str = XRegExp.replace(str, utils.invalidUnicodeChars, '-');
		}
		str = !preserveCase ? str.toLocaleLowerCase() : str;
		str = str.replace(utils.collapseWhitespace, '-');
		str = str.replace(utils.collapseDash, '-');
		str = str.replace(utils.trimTrailingDash, '');
		str = str.replace(utils.trimLeadingDash, '');
		return str;
	},
	invalidUnicodeChars: XRegExp('[^\\p{L}\\s\\d\\-_]', 'g'),
	invalidLatinChars: /[^\w\s\d\-_]/g,
	trimRegex: /^\s+|\s+$/g,
	collapseWhitespace: /\s+/g,
	collapseDash: /-+/g,
	trimTrailingDash: /-$/g,
	trimLeadingDash: /^-/g,
	isLatin: /^[\w\d\s.,\-@]+$/,
};

describe('regex', function () {
	const matcher = new RegExp(regex);
	const strings = [
		'@testUser',
		'@testUser some text',
		'some text @testUser',
		'@testUser',
		'@testUser some text',
		'some text @testUser',
		'end of sentence. @testUser',
		'@testUser.',
		'@testUser\'s some text',
	]
	it('failed inside regex', function(done) {
		assert(false);
		done();
	});
	it('should match a mention in all strings', () => {
		strings.forEach(string => {
			const matches = string.match(matcher);
			assert(matches);
			assert.equal(utils.slugify(matches[0]), 'testuser');
		});
	});

	// TODO: Test for unicode/non-latin mention
	// TODO: Ideally the regex matcher should be its own utility function in `lib/`
});

describe('splitter', function () {
	const utility = require('../lib/utility');
	const testHTMLText = 'this is a post with stuff in code and a\n\n
blockquote or two
';
	const testMdText = 'this is a post with `stuff in code` and a \n\n>blockquote or two';

	it('should not error', () => {
		const results = [
			utility.split(testHTMLText, false, true, false),
			utility.split(testHTMLText, false, false, true),
			utility.split(testHTMLText, false, true, true),
			utility.split(testMdText, true, true, false),
			utility.split(testMdText, true, false, true),
			utility.split(testMdText, true, true, true),
		];
		results.forEach(test => assert(test));
	});

	describe('HTML text', () => {
		it('should split blockquotes properly', () => {
			const result = utility.split(testHTMLText, false, true, false);
			assert.equal(result[0], 'this is a post with stuff in code and a\n\n');
			assert.equal(result[1], '
blockquote or two
');
		});

		it('should split inline code properly', () => {
			const result = utility.split(testHTMLText, false, false, true);
			assert.equal(result[0], 'this is a post with ');
			assert.equal(result[1], 'stuff in code');
			assert.equal(result[2], ' and a\n\n
blockquote or two
');
		});

		it('should split both blockquotes and code properly', () => {
			const result = utility.split(testHTMLText, false, true, true);
			assert.equal(result[0], 'this is a post with ');
			assert.equal(result[1], 'stuff in code');
			assert.equal(result[2], ' and a\n\n');
			assert.equal(result[3], '
blockquote or two
');
		});

		it('should split HTML code blocks that are wrapped with only a
', () => {
			const testString = '
test text
\n
var value = \'@admin\';
\nafter text';
			const result = utility.split(testString, false, false, true);
			assert.equal(result[0], '
test text
\n');
			assert.equal(result[1], '
var value = \'@admin\';
');
			assert.equal(result[2], '\nafter text');
		});

		it('should not accidentally split on <annotation> HTML tag', () => {
			const testString = '
wonderful
<annotation>what is an annotation anyway</annotation>what';
			const results = [
				utility.split(testString, false, false, false),
				utility.split(testString, false, false, true),
				utility.split(testString, false, true, false),
				utility.split(testString, false, true, true),
			];

			results.forEach(result => assert.strictEqual(result[0], '
wonderful
<annotation>what is an annotation anyway</annotation>what'));
		});
	});

	describe('Markdown text', () => {
		it('should split blockquotes properly', () => {
			const result = utility.split(testMdText, true, true, false);
			assert.equal(result[0], 'this is a post with `stuff in code` and a \n\n');
			assert.equal(result[1], '>blockquote or two');
		});

		it('should split inline code properly', () => {
			const result = utility.split(testMdText, true, false, true);
			assert.equal(result[0], 'this is a post with ');
			assert.equal(result[1], '`stuff in code`');
			assert.equal(result[2], ' and a \n\n>blockquote or two');
		});

		it('should split both blockquotes and code properly', () => {
			const result = utility.split(testMdText, true, true, true);
			assert.equal(result[0], 'this is a post with ');
			assert.equal(result[1], '`stuff in code`');
			assert.equal(result[2], ' and a \n\n');
			assert.equal(result[3], '>blockquote or two');
		});

		it('should split code fences properly', () => {
			const testString = 'this is some text\n\n```\nvar a = \'@admin\';\n```\nafter text';
			const result = utility.split(testString, true, false, true);
			assert.equal(result[0], 'this is some text\n\n');
			assert.equal(result[1], '```\nvar a = \'@admin\';\n```');
			assert.equal(result[2], '\nafter text');
		});
	});
});
<commit_msg>fix: removed failing test<commit_after>const assert = require('assert');
const XRegExp = require('xregexp');
const regex = XRegExp('(?:^|\\s|\\>|;)(@[\\p{L}\\d\\-_.]+)', 'g');

// from NodeBB utils.js
const utils = {
	slugify: function (str, preserveCase) {
		if (!str) {
			return '';
		}
		str = str.replace(utils.trimRegex, '');
		if (utils.isLatin.test(str)) {
			str = str.replace(utils.invalidLatinChars, '-');
		} else {
			str = XRegExp.replace(str, utils.invalidUnicodeChars, '-');
		}
		str = !preserveCase ? str.toLocaleLowerCase() : str;
		str = str.replace(utils.collapseWhitespace, '-');
		str = str.replace(utils.collapseDash, '-');
		str = str.replace(utils.trimTrailingDash, '');
		str = str.replace(utils.trimLeadingDash, '');
		return str;
	},
	invalidUnicodeChars: XRegExp('[^\\p{L}\\s\\d\\-_]', 'g'),
	invalidLatinChars: /[^\w\s\d\-_]/g,
	trimRegex: /^\s+|\s+$/g,
	collapseWhitespace: /\s+/g,
	collapseDash: /-+/g,
	trimTrailingDash: /-$/g,
	trimLeadingDash: /^-/g,
	isLatin: /^[\w\d\s.,\-@]+$/,
};

describe('regex', function () {
	const matcher = new RegExp(regex);
	const strings = [
		'@testUser',
		'@testUser some text',
		'some text @testUser',
		'@testUser',
		'@testUser some text',
		'some text @testUser',
		'end of sentence. @testUser',
		'@testUser.',
		'@testUser\'s some text',
	]
	it('should match a mention in all strings', () => {
		strings.forEach(string => {
			const matches = string.match(matcher);
			assert(matches);
			assert.equal(utils.slugify(matches[0]), 'testuser');
		});
	});

	// TODO: Test for unicode/non-latin mention
	// TODO: Ideally the regex matcher should be its own utility function in `lib/`
});

describe('splitter', function () {
	const utility = require('../lib/utility');
	const testHTMLText = 'this is a post with stuff in code and a\n\n
blockquote or two
';
	const testMdText = 'this is a post with `stuff in code` and a \n\n>blockquote or two';

	it('should not error', () => {
		const results = [
			utility.split(testHTMLText, false, true, false),
			utility.split(testHTMLText, false, false, true),
			utility.split(testHTMLText, false, true, true),
			utility.split(testMdText, true, true, false),
			utility.split(testMdText, true, false, true),
			utility.split(testMdText, true, true, true),
		];
		results.forEach(test => assert(test));
	});

	describe('HTML text', () => {
		it('should split blockquotes properly', () => {
			const result = utility.split(testHTMLText, false, true, false);
			assert.equal(result[0], 'this is a post with stuff in code and a\n\n');
			assert.equal(result[1], '
blockquote or two
');
		});

		it('should split inline code properly', () => {
			const result = utility.split(testHTMLText, false, false, true);
			assert.equal(result[0], 'this is a post with ');
			assert.equal(result[1], 'stuff in code');
			assert.equal(result[2], ' and a\n\n
blockquote or two
');
		});

		it('should split both blockquotes and code properly', () => {
			const result = utility.split(testHTMLText, false, true, true);
			assert.equal(result[0], 'this is a post with ');
			assert.equal(result[1], 'stuff in code');
			assert.equal(result[2], ' and a\n\n');
			assert.equal(result[3], '
blockquote or two
');
		});

		it('should split HTML code blocks that are wrapped with only a
', () => {
			const testString = '
test text
\n
var value = \'@admin\';
\nafter text';
			const result = utility.split(testString, false, false, true);
			assert.equal(result[0], '
test text
\n');
			assert.equal(result[1], '
var value = \'@admin\';
');
			assert.equal(result[2], '\nafter text');
		});

		it('should not accidentally split on <annotation> HTML tag', () => {
			const testString = '
wonderful
<annotation>what is an annotation anyway</annotation>what';
			const results = [
				utility.split(testString, false, false, false),
				utility.split(testString, false, false, true),
				utility.split(testString, false, true, false),
				utility.split(testString, false, true, true),
			];

			results.forEach(result => assert.strictEqual(result[0], '
wonderful
<annotation>what is an annotation anyway</annotation>what'));
		});
	});

	describe('Markdown text', () => {
		it('should split blockquotes properly', () => {
			const result = utility.split(testMdText, true, true, false);
			assert.equal(result[0], 'this is a post with `stuff in code` and a \n\n');
			assert.equal(result[1], '>blockquote or two');
		});

		it('should split inline code properly', () => {
			const result = utility.split(testMdText, true, false, true);
			assert.equal(result[0], 'this is a post with ');
			assert.equal(result[1], '`stuff in code`');
			assert.equal(result[2], ' and a \n\n>blockquote or two');
		});

		it('should split both blockquotes and code properly', () => {
			const result = utility.split(testMdText, true, true, true);
			assert.equal(result[0], 'this is a post with ');
			assert.equal(result[1], '`stuff in code`');
			assert.equal(result[2], ' and a \n\n');
			assert.equal(result[3], '>blockquote or two');
		});

		it('should split code fences properly', () => {
			const testString = 'this is some text\n\n```\nvar a = \'@admin\';\n```\nafter text';
			const result = utility.split(testString, true, false, true);
			assert.equal(result[0], 'this is some text\n\n');
			assert.equal(result[1], '```\nvar a = \'@admin\';\n```');
			assert.equal(result[2], '\nafter text');
		});
	});
});
<|endoftext|>


<commit_before>/**
 * cacheModule constructor
 * @param config: {
 *    type:                           {string  | 'cache-module'}
 *    verbose:                        {boolean | false},
 *    defaultExpiration:              {integer | 900},
 *    readOnly:                       {boolean | false},
 *    checkOnPreviousEmpty:           {boolean | true},
 *    backgroundRefreshIntervalCheck: {boolean | true},
 *    backgroundRefreshInterval:      {integer | 60000},
 *    backgroundRefreshMinTtl:        {integer | 70000},
 *    storage:                        {string  | null},
 *    storageMock:                    {object  | null}
 * }
 */
function cacheModule(config){
  var self = this;
  config = config || {};
  self.type = config.type || 'cache-module';
  self.verbose = config.verbose || false;
  self.defaultExpiration = config.defaultExpiration || 900;
  self.readOnly = config.readOnly || false;
  self.checkOnPreviousEmpty = (typeof config.checkOnPreviousEmpty === 'boolean') ? config.checkOnPreviousEmpty : true;
  self.backgroundRefreshIntervalCheck = (typeof config.backgroundRefreshIntervalCheck === 'boolean') ? config.backgroundRefreshIntervalCheck : true;
  self.backgroundRefreshInterval = config.backgroundRefreshInterval || 60000;
  self.backgroundRefreshMinTtl = config.backgroundRefreshMinTtl || 70000;
  var store = null;
  var storageMock = config.storageMock || false;
  var backgroundRefreshEnabled = false;
  var browser = (typeof window !== 'undefined');
  var cache = {
    db: {},
    expirations: {},
    refreshKeys: {}
  };
  var storageKey;

  setupBrowserStorage();
  log(false, 'Cache-module client created with the following defaults:', {type: self.type, defaultExpiration: self.defaultExpiration, verbose: self.verbose, readOnly: self.readOnly});

  /**
   * Get the value associated with a given key
   * @param {string} key
   * @param {function} cb
   */
  self.get = function(key, cb){
    throwErrorIf((arguments.length < 2), 'ARGUMENT_EXCEPTION: .get() requires 2 arguments.');
    log(false, 'get() called:', {key: key});
    try {
      var now = Date.now();
      var expiration = cache.expirations[key];
      if(expiration > now){
        cb(null, cache.db[key]);
      }
      else{
        expire(key);
        cb(null, null);
      }
    } catch (err) {
      cb({name: 'GetException', message: err}, null);
    }
  }

  /**
   * Get multiple values given multiple keys
   * @param {array} keys
   * @param {function} cb
   * @param {integer} index
   */
  self.mget = function(keys, cb, index){
    throwErrorIf((arguments.length < 2), 'ARGUMENT_EXCEPTION: .mget() requires 2 arguments.');
    log(false, '.mget() called:', {keys: keys});
    var values = {};
    for(var i = 0; i < keys.length; i++){
      var key = keys[i];
      self.get(key, function(err, response){
        if(response !== null){
          values[key] = response;
        }
      });
    }
    cb(null, values, index);
  }

  /**
   * Associate a key and value and optionally set an expiration
   * @param {string} key
   * @param {string | object} value
   * @param {integer} expiration
   * @param {function} refresh
   * @param {function} cb
   */
  self.set = function(){
    throwErrorIf((arguments.length < 2), 'ARGUMENT_EXCEPTION: .set() requires at least 2 arguments.');
    var key = arguments[0];
    var value = arguments[1];
    var expiration = arguments[2] || null;
    var refresh = (arguments.length == 5) ? arguments[3] : null;
    var cb = (arguments.length == 5) ? arguments[4] : arguments[3];
    log(false, '.set() called:', {key: key, value: value});
    if(!self.readOnly){
      try {
        expiration = (expiration) ? (expiration * 1000) : (self.defaultExpiration * 1000);
        var exp = expiration + Date.now();
        cache.expirations[key] = exp;
        cache.db[key] = value;
        if(cb) cb();
        if(refresh){
          cache.refreshKeys[key] = {expiration: exp, lifeSpan: expiration, refresh: refresh};
          backgroundRefreshInit();
        }
        overwriteBrowserStorage();
      } catch (err) {
        log(true, '.set() failed for cache of type ' + self.type, {name: 'CacheModuleSetException', message: err});
      }
    }
  }

  /**
   * Associate multiple keys with multiple values and optionally set expirations per function and/or key
   * @param {object} obj
   * @param {integer} expiration
   * @param {function} cb
   */
  self.mset = function(obj, expiration, cb){
    throwErrorIf((arguments.length < 1), 'ARGUMENT_EXCEPTION: .mset() requires at least 1 argument.');
    log(false, '.mset() called:', {data: obj});
    for(var key in obj){
      if(obj.hasOwnProperty(key)){
        var tempExpiration = expiration || self.defaultExpiration;
        var value = obj[key];
        if(typeof value === 'object' && value.cacheValue){
          tempExpiration = value.expiration || tempExpiration;
          value = value.cacheValue;
        }
        self.set(key, value, tempExpiration);
      }
    }
    if(cb) cb();
  }

  /**
   * Delete the provided keys and their associated values
   * @param {array} keys
   * @param {function} cb
   */
  self.del = function(keys, cb){
    throwErrorIf((arguments.length < 1), 'ARGUMENT_EXCEPTION: .del() requires at least 1 argument.');
    log(false, '.del() called:', {keys: keys});
    if(typeof keys === 'object'){
      for(var i = 0; i < keys.length; i++){
        var key = keys[i];
        delete cache.db[key];
        delete cache.expirations[key];
        delete cache.refreshKeys[key];
      }
      if(cb) cb(null, keys.length);
    }
    else{
      delete cache.db[keys];
      delete cache.expirations[keys];
      delete cache.refreshKeys[keys];
      if(cb) cb(null, 1);
    }
    overwriteBrowserStorage();
  }

  /**
   * Flush all keys and values
   * @param {function} cb
   */
  self.flush = function(cb){
    log(false, '.flush() called');
    cache.db = {};
    cache.expirations = {};
    cache.refreshKeys = {};
    if(cb) cb();
    overwriteBrowserStorage();
  }

  /**
   * Enable browser storage if desired and available
   */
  function setupBrowserStorage(){
    if(browser || storageMock){
      if(storageMock){
        store = storageMock;
        storageKey = self.type + '-storage-mock';
      }
      else{
        var storageType = (config.storage === 'local' || config.storage === 'session') ? config.storage : null;
        store = (storageType && typeof Storage !== void(0)) ? window[storageType + 'Storage'] : false;
        storageKey = (storageType) ? self.type + '-' + storageType + '-storage' : null;
      }
      if(store){
        var db = store.getItem(storageKey);
        try {
          cache = JSON.parse(db) || cache;
        } catch (err) { /* Do nothing */ }
      }
      // If storageType is set but store is not, the desired storage mechanism was not available
      else if(storageType){
        log(true, 'Browser storage is not supported by this browser. Defaulting to an in-memory cache.');
      }
    }
  }

  /**
   * Overwrite namespaced browser storage with current cache
   */
  function overwriteBrowserStorage(){
    if((browser && store) || storageMock){
      var db = cache;
      try {
        db = JSON.stringify(db);
      } catch (err) { /* Do nothing */ }
      store.setItem(storageKey, db);
    }
  }

  /**
   * Throw a given error if error is true
   * @param {boolean} error
   * @param {string} message
   */
  function throwErrorIf(error, message){
    if(error) throw new Error(message);
  }

  /**
   * Delete a given key from cache.db and cache.expirations but not from cache.refreshKeys
   * @param {string} key
   */
  function expire(key){
    delete cache.db[key];
    delete cache.expirations[key];
    overwriteBrowserStorage();
  }

  /**
   * Initialize background refresh
   */
  function backgroundRefreshInit(){
    if(!backgroundRefreshEnabled){
      backgroundRefreshEnabled = true;
      if(self.backgroundRefreshIntervalCheck){
        if(self.backgroundRefreshInterval > self.backgroundRefreshMinTtl){
          throw new Error('BACKGROUND_REFRESH_INTERVAL_EXCEPTION: backgroundRefreshInterval cannot be greater than backgroundRefreshMinTtl.');
        }
      }
      setInterval(backgroundRefresh, self.backgroundRefreshInterval);
    }
  }

  /**
   * Handle the refresh callback from the consumer, save the data to redis.
   *
   * @param {string} key The key used to save.
   * @param {Object} data refresh keys data.
   * @param {Error|null} err consumer callback failure.
   * @param {*} response The consumer response.
   */
  function handleRefreshResponse (key, data, err, response) {
    if(!err) {
      this.set(key, response, (data.lifeSpan / 1000), data.refresh, function(){});
    }
  }

  /**
   * Refreshes all keys that were set with a refresh function
   */
  function backgroundRefresh() {
    var keys = Object.keys(cache.refreshKeys);
    keys.forEach(function(key) {
      var data = cache.refreshKeys[key];
      if(data.expiration - Date.now() < this.backgroundRefreshMinTtl){
        data.refresh(key, handleRefreshResponse.bind(this, key, data));
      }
    }, self);
  }

  /**
   * Error logging logic
   * @param {boolean} isError
   * @param {string} message
   * @param {object} data
   */
  function log(isError, message, data){
    if(self.verbose || isError){
      if(data) console.log(self.type + ': ' + message, data);
      else console.log(self.type + message);
    }
  }
}

module.exports = cacheModule;
<commit_msg>Replacing test key name.<commit_after>/**
 * cacheModule constructor
 * @param config: {
 *    type:                           {string  | 'cache-module'}
 *    verbose:                        {boolean | false},
 *    defaultExpiration:              {integer | 900},
 *    readOnly:                       {boolean | false},
 *    checkOnPreviousEmpty:           {boolean | true},
 *    backgroundRefreshIntervalCheck: {boolean | true},
 *    backgroundRefreshInterval:      {integer | 60000},
 *    backgroundRefreshMinTtl:        {integer | 70000},
 *    storage:                        {string  | null},
 *    storageMock:                    {object  | null}
 * }
 */
function cacheModule(config){
  var self = this;
  config = config || {};
  self.type = config.type || 'cache-module';
  self.verbose = config.verbose || false;
  self.defaultExpiration = config.defaultExpiration || 900;
  self.readOnly = config.readOnly || false;
  self.checkOnPreviousEmpty = (typeof config.checkOnPreviousEmpty === 'boolean') ? config.checkOnPreviousEmpty : true;
  self.backgroundRefreshIntervalCheck = (typeof config.backgroundRefreshIntervalCheck === 'boolean') ? config.backgroundRefreshIntervalCheck : true;
  self.backgroundRefreshInterval = config.backgroundRefreshInterval || 60000;
  self.backgroundRefreshMinTtl = config.backgroundRefreshMinTtl || 70000;
  var store = null;
  var storageMock = config.storageMock || false;
  var backgroundRefreshEnabled = false;
  var browser = (typeof window !== 'undefined');
  var cache = {
    db: {},
    expirations: {},
    refreshKeys: {}
  };
  var storageKey;

  setupBrowserStorage();
  log(false, 'Cache-module client created with the following defaults:', {type: self.type, defaultExpiration: self.defaultExpiration, verbose: self.verbose, readOnly: self.readOnly});

  /**
   * Get the value associated with a given key
   * @param {string} key
   * @param {function} cb
   */
  self.get = function(key, cb){
    throwErrorIf((arguments.length < 2), 'ARGUMENT_EXCEPTION: .get() requires 2 arguments.');
    log(false, 'get() called:', {key: key});
    try {
      var now = Date.now();
      var expiration = cache.expirations[key];
      if(expiration > now){
        cb(null, cache.db[key]);
      }
      else{
        expire(key);
        cb(null, null);
      }
    } catch (err) {
      cb({name: 'GetException', message: err}, null);
    }
  }

  /**
   * Get multiple values given multiple keys
   * @param {array} keys
   * @param {function} cb
   * @param {integer} index
   */
  self.mget = function(keys, cb, index){
    throwErrorIf((arguments.length < 2), 'ARGUMENT_EXCEPTION: .mget() requires 2 arguments.');
    log(false, '.mget() called:', {keys: keys});
    var values = {};
    for(var i = 0; i < keys.length; i++){
      var key = keys[i];
      self.get(key, function(err, response){
        if(response !== null){
          values[key] = response;
        }
      });
    }
    cb(null, values, index);
  }

  /**
   * Associate a key and value and optionally set an expiration
   * @param {string} key
   * @param {string | object} value
   * @param {integer} expiration
   * @param {function} refresh
   * @param {function} cb
   */
  self.set = function(){
    throwErrorIf((arguments.length < 2), 'ARGUMENT_EXCEPTION: .set() requires at least 2 arguments.');
    var key = arguments[0];
    var value = arguments[1];
    var expiration = arguments[2] || null;
    var refresh = (arguments.length == 5) ? arguments[3] : null;
    var cb = (arguments.length == 5) ? arguments[4] : arguments[3];
    log(false, '.set() called:', {key: key, value: value});
    if(!self.readOnly){
      try {
        expiration = (expiration) ? (expiration * 1000) : (self.defaultExpiration * 1000);
        var exp = expiration + Date.now();
        cache.expirations[key] = exp;
        cache.db[key] = value;
        if(cb) cb();
        if(refresh){
          cache.refreshKeys[key] = {expiration: exp, lifeSpan: expiration, refresh: refresh};
          backgroundRefreshInit();
        }
        overwriteBrowserStorage();
      } catch (err) {
        log(true, '.set() failed for cache of type ' + self.type, {name: 'CacheModuleSetException', message: err});
      }
    }
  }

  /**
   * Associate multiple keys with multiple values and optionally set expirations per function and/or key
   * @param {object} obj
   * @param {integer} expiration
   * @param {function} cb
   */
  self.mset = function(obj, expiration, cb){
    throwErrorIf((arguments.length < 1), 'ARGUMENT_EXCEPTION: .mset() requires at least 1 argument.');
    log(false, '.mset() called:', {data: obj});
    for(var key in obj){
      if(obj.hasOwnProperty(key)){
        var tempExpiration = expiration || self.defaultExpiration;
        var value = obj[key];
        if(typeof value === 'object' && value.cacheValue){
          tempExpiration = value.expiration || tempExpiration;
          value = value.cacheValue;
        }
        self.set(key, value, tempExpiration);
      }
    }
    if(cb) cb();
  }

  /**
   * Delete the provided keys and their associated values
   * @param {array} keys
   * @param {function} cb
   */
  self.del = function(keys, cb){
    throwErrorIf((arguments.length < 1), 'ARGUMENT_EXCEPTION: .del() requires at least 1 argument.');
    log(false, '.del() called:', {keys: keys});
    if(typeof keys === 'object'){
      for(var i = 0; i < keys.length; i++){
        var key = keys[i];
        delete cache.db[key];
        delete cache.expirations[key];
        delete cache.refreshKeys[key];
      }
      if(cb) cb(null, keys.length);
    }
    else{
      delete cache.db[keys];
      delete cache.expirations[keys];
      delete cache.refreshKeys[keys];
      if(cb) cb(null, 1);
    }
    overwriteBrowserStorage();
  }

  /**
   * Flush all keys and values
   * @param {function} cb
   */
  self.flush = function(cb){
    log(false, '.flush() called');
    cache.db = {};
    cache.expirations = {};
    cache.refreshKeys = {};
    if(cb) cb();
    overwriteBrowserStorage();
  }

  /**
   * Enable browser storage if desired and available
   */
  function setupBrowserStorage(){
    if(browser || storageMock){
      if(storageMock){
        store = storageMock;
        storageKey = 'cache-module-storage-mock';
      }
      else{
        var storageType = (config.storage === 'local' || config.storage === 'session') ? config.storage : null;
        store = (storageType && typeof Storage !== void(0)) ? window[storageType + 'Storage'] : false;
        storageKey = (storageType) ? self.type + '-' + storageType + '-storage' : null;
      }
      if(store){
        var db = store.getItem(storageKey);
        try {
          cache = JSON.parse(db) || cache;
        } catch (err) { /* Do nothing */ }
      }
      // If storageType is set but store is not, the desired storage mechanism was not available
      else if(storageType){
        log(true, 'Browser storage is not supported by this browser. Defaulting to an in-memory cache.');
      }
    }
  }

  /**
   * Overwrite namespaced browser storage with current cache
   */
  function overwriteBrowserStorage(){
    if((browser && store) || storageMock){
      var db = cache;
      try {
        db = JSON.stringify(db);
      } catch (err) { /* Do nothing */ }
      store.setItem(storageKey, db);
    }
  }

  /**
   * Throw a given error if error is true
   * @param {boolean} error
   * @param {string} message
   */
  function throwErrorIf(error, message){
    if(error) throw new Error(message);
  }

  /**
   * Delete a given key from cache.db and cache.expirations but not from cache.refreshKeys
   * @param {string} key
   */
  function expire(key){
    delete cache.db[key];
    delete cache.expirations[key];
    overwriteBrowserStorage();
  }

  /**
   * Initialize background refresh
   */
  function backgroundRefreshInit(){
    if(!backgroundRefreshEnabled){
      backgroundRefreshEnabled = true;
      if(self.backgroundRefreshIntervalCheck){
        if(self.backgroundRefreshInterval > self.backgroundRefreshMinTtl){
          throw new Error('BACKGROUND_REFRESH_INTERVAL_EXCEPTION: backgroundRefreshInterval cannot be greater than backgroundRefreshMinTtl.');
        }
      }
      setInterval(backgroundRefresh, self.backgroundRefreshInterval);
    }
  }

  /**
   * Handle the refresh callback from the consumer, save the data to redis.
   *
   * @param {string} key The key used to save.
   * @param {Object} data refresh keys data.
   * @param {Error|null} err consumer callback failure.
   * @param {*} response The consumer response.
   */
  function handleRefreshResponse (key, data, err, response) {
    if(!err) {
      this.set(key, response, (data.lifeSpan / 1000), data.refresh, function(){});
    }
  }

  /**
   * Refreshes all keys that were set with a refresh function
   */
  function backgroundRefresh() {
    var keys = Object.keys(cache.refreshKeys);
    keys.forEach(function(key) {
      var data = cache.refreshKeys[key];
      if(data.expiration - Date.now() < this.backgroundRefreshMinTtl){
        data.refresh(key, handleRefreshResponse.bind(this, key, data));
      }
    }, self);
  }

  /**
   * Error logging logic
   * @param {boolean} isError
   * @param {string} message
   * @param {object} data
   */
  function log(isError, message, data){
    if(self.verbose || isError){
      if(data) console.log(self.type + ': ' + message, data);
      else console.log(self.type + message);
    }
  }
}

module.exports = cacheModule;
<|endoftext|>


<commit_before>describe('geoSpecifierService', function() {
  'use strict';
  beforeEach(module('geoSpecifierService', function($provide) {
    $provide.constant('googleMapsAPIKey', 'a-test-key');
  }));

  describe('service', function() {
    var service, $window, $rootScope;
    beforeEach(inject(function(geoSpecifierService, _$window_, _$rootScope_) {
      service = geoSpecifierService;
      $window = _$window_;
      $rootScope = _$rootScope_;
      $window.document.body.appendChild = jasmine.createSpy('appendChild');
      $window.document.getElementById = jasmine.createSpy('getElementById');
      delete $window.google;
    }));

    function loadMockMapsAPI() {
      $window.google = {
        maps: {
          Map: jasmine.createSpy('maps.Map').and.returnValue({
            setCenter: jasmine.createSpy('maps.Map.setCenter'),
            fitBounds: jasmine.createSpy('maps.Map.fitBounds'),
          }),
          Marker: jasmine.createSpy('maps.Marker').and.returnValue({
            setPosition: jasmine.createSpy('maps.Marker.setPosition'),
          }),
          Geocoder: jasmine.createSpy('maps.Geocoder').and.returnValue({
            geocode: jasmine.createSpy('maps.Geocoder.geocode'),
          }),
          GeocoderStatus: {
            OK: 'OK',
            notOK: 'whatever',
          }
        },
      };
    }

    function callMapsCallback() {
      loadMockMapsAPI();
      var callback = /callback=([^&]*)/.exec($window.document.body.appendChild.calls.first().args[0].src)[1];
      $window[callback]();
    }

    describe('create', function() {
      describe('loading the Google Maps API', function() {
        var appendChild;
        beforeEach(function() {
          appendChild = $window.document.body.appendChild;
        });

        it('add the google maps API script to body', function() {
          service.create();
          expect(appendChild).toHaveBeenCalled();
          expect(appendChild.calls.first().args[0].src).toMatch('maps.googleapis.com/maps/api/js');
        });

        it('should include the configured key', function() {
          service.create('');
          expect(appendChild.calls.first().args[0].src).toMatch('key=a-test-key');
        });

        describe('with google maps API loaded by someone other than this service', function() {
          beforeEach(function() {
            loadMockMapsAPI();
          });

          it('should\'nt append another async loading script on create', function() {
            service.create();
            expect(appendChild).not.toHaveBeenCalled();
          });
        });

        describe('with API initialized', function() {
          beforeEach(function() {
            service.create('canvas-id');
            callMapsCallback();
          });

          it('shouldn\'t append another async loading script on create', function() {
            appendChild.calls.reset();
            service.create();
            expect(appendChild).not.toHaveBeenCalled();
          });

          it('should create the map on the specified canvas', function() {
            var mockElement = 'mockElement';
            $window.document.getElementById.and.returnValue(mockElement);

            $rootScope.$apply();

            expect($window.document.getElementById).toHaveBeenCalledWith('canvas-id');
            expect($window.google.maps.Map).toHaveBeenCalled();
            var args = $window.google.maps.Map.calls.first().args;
            expect(args[0]).toBe(mockElement);
            expect(args[1].zoom).toBeDefined();
          });
        });
      });

      describe('creating the Specifier, with maps initialized and map available', function() {
        var specifierPromise, position;
        beforeEach(function() {
          specifierPromise = service.create();
          callMapsCallback();
        });

        it('should resolve the promise with a specifier', function() {
          var specifier;
          specifierPromise.then(function(l) {
            specifier = l;
          });
          $rootScope.$apply();
          expect(specifier).toBeDefined();
        });

        describe('with the specifier resolved', function() {
          var specifier;
          beforeEach(function() {
            specifierPromise.then(function(l) {
              specifier = l;
            });
            $rootScope.$apply();
          });

          it('should not be able to provide a location yet', function() {
            expect(function() {
              specifier.getLocation();
            }).toThrow();
          });

          describe('setting the address', function() {
            var maps, map, marker, geocoder;
            beforeEach(function() {
              maps = $window.google.maps;
              map = maps.Map();
              maps.Map.calls.reset();
              geocoder = maps.Geocoder();
              maps.Geocoder.calls.reset();
              marker = maps.Marker();
              maps.Marker.calls.reset();
            });

            describe('for the first time', function() {
              it('should initialize the geocoder', function() {
                specifier.setAddress('whatever');
                expect(maps.Geocoder).toHaveBeenCalled();
              });

              it('should geocode the address', function() {
                specifier.setAddress('whatever');
                expect(geocoder.geocode).toHaveBeenCalled();
                var args = geocoder.geocode.calls.first().args;
                var geocoderParams = args[0];
                var callback = args[1];
                expect(geocoderParams.address).toEqual('whatever');
              });

              describe('with geocoding failing', function() {
                beforeEach(function() {
                  specifier.setAddress('whatever');
                  var callback = geocoder.geocode.calls.first().args[1];
                  callback({}, $window.google.maps.GeocoderStatus.notOK);
                });

                it('should not create the marker', function() {
                  $rootScope.$apply();
                  expect(maps.Marker).not.toHaveBeenCalled();
                });
              });

              describe('with geocoding succeeding', function() {
                beforeEach(function() {
                  specifier.setAddress('whatever');
                  var callback = geocoder.geocode.calls.first().args[1];
                  callback([{
                    geometry: {
                      location: 'the location',
                      viewport: 'the viewport',
                    },
                  }], $window.google.maps.GeocoderStatus.OK);
                });

                it('should create the marker', function() {
                  $rootScope.$apply();
                  expect(maps.Marker).toHaveBeenCalled();
                  var markerOptions = maps.Marker.calls.first().args[0];
                  expect(markerOptions.map).toEqual(map);
                  expect(markerOptions.position).toEqual('the location');
                });

                it('should set the map center location and viewport', function() {
                  $rootScope.$apply();
                  expect(map.setCenter).toHaveBeenCalledWith('the location');
                  expect(map.fitBounds).toHaveBeenCalledWith('the viewport');
                });
              });

              describe('with region included', function() {
                it('should geocode tha address including the region', function() {
                  specifier.setAddress('whatever', 'a-region');
                  expect(geocoder.geocode).toHaveBeenCalled();
                  var args = geocoder.geocode.calls.first().args;
                  var geocoderParams = args[0];
                  var callback = args[1];
                  expect(geocoderParams.address).toEqual('whatever');
                  expect(geocoderParams.region).toEqual('a-region');
                });
              });
            });

            describe('with the marker and geocoder already initialized', function() {
              beforeEach(function() {
                specifier.setAddress('whatever');
                var callback = geocoder.geocode.calls.first().args[1];
                callback([{
                  geometry: {
                    location: 'the location',
                    viewport: 'the viewport',
                  },
                }], $window.google.maps.GeocoderStatus.OK);
                $rootScope.$apply();
                maps.Geocoder.calls.reset();
                maps.Map.calls.reset();
                geocoder.geocode.calls.reset();
              });

              it('should NOT re-initialize the geocoder', function() {
                specifier.setAddress('whatever2');
                expect(maps.Geocoder).not.toHaveBeenCalled();
              });

              it('should geocode the address', function() {
                specifier.setAddress('whatever2');
                expect(geocoder.geocode).toHaveBeenCalled();
                var args = geocoder.geocode.calls.first().args;
                var geocoderParams = args[0];
                var callback = args[1];
                expect(geocoderParams.address).toEqual('whatever2');
              });

              describe('with geocoding failing', function() {
                beforeEach(function() {
                  specifier.setAddress('whatever');
                  var callback = geocoder.geocode.calls.first().args[1];
                  callback({}, $window.google.maps.GeocoderStatus.notOK);
                });

                it('should not set the marker position', function() {
                  $rootScope.$apply();
                  expect(marker.setPosition).not.toHaveBeenCalled();
                });
              });

              describe('with geocoding succeeding', function() {
                beforeEach(function() {
                  specifier.setAddress('whatever2');
                  var callback = geocoder.geocode.calls.first().args[1];
                  callback([{
                    geometry: {
                      location: 'the location2',
                      viewport: 'the viewport2',
                    },
                  }], $window.google.maps.GeocoderStatus.OK);
                });

                it('should set the marker position', function() {
                  $rootScope.$apply();
                  expect(marker.setPosition).toHaveBeenCalledWith('the location2');
                });

                it('should set the map center location and viewport', function() {
                  $rootScope.$apply();
                  expect(map.setCenter).toHaveBeenCalledWith('the location2');
                  expect(map.fitBounds).toHaveBeenCalledWith('the viewport2');
                });
              });

              describe('with region included', function() {
                it('should geocode tha address including the region', function() {
                  specifier.setAddress('whatever', 'a-region');
                  expect(geocoder.geocode).toHaveBeenCalled();
                  var args = geocoder.geocode.calls.first().args;
                  var geocoderParams = args[0];
                  var callback = args[1];
                  expect(geocoderParams.address).toEqual('whatever');
                  expect(geocoderParams.region).toEqual('a-region');
                });
              });
            });
          });
        });
      });
    });
  });
});
<commit_msg>add test for geoSpecifier#onResized<commit_after>describe('geoSpecifierService', function() {
  'use strict';
  beforeEach(module('geoSpecifierService', function($provide) {
    $provide.constant('googleMapsAPIKey', 'a-test-key');
  }));

  describe('service', function() {
    var service, $window, $rootScope;
    beforeEach(inject(function(geoSpecifierService, _$window_, _$rootScope_) {
      service = geoSpecifierService;
      $window = _$window_;
      $rootScope = _$rootScope_;
      $window.document.body.appendChild = jasmine.createSpy('appendChild');
      $window.document.getElementById = jasmine.createSpy('getElementById');
      delete $window.google;
    }));

    function loadMockMapsAPI() {
      $window.google = {
        maps: {
          Map: jasmine.createSpy('maps.Map').and.returnValue({
            setCenter: jasmine.createSpy('maps.Map.setCenter'),
            fitBounds: jasmine.createSpy('maps.Map.fitBounds'),
          }),
          Marker: jasmine.createSpy('maps.Marker').and.returnValue({
            setPosition: jasmine.createSpy('maps.Marker.setPosition'),
          }),
          Geocoder: jasmine.createSpy('maps.Geocoder').and.returnValue({
            geocode: jasmine.createSpy('maps.Geocoder.geocode'),
          }),
          GeocoderStatus: {
            OK: 'OK',
            notOK: 'whatever',
          }
        },
      };
    }

    function callMapsCallback() {
      loadMockMapsAPI();
      var callback = /callback=([^&]*)/.exec($window.document.body.appendChild.calls.first().args[0].src)[1];
      $window[callback]();
    }

    describe('create', function() {
      describe('loading the Google Maps API', function() {
        var appendChild;
        beforeEach(function() {
          appendChild = $window.document.body.appendChild;
        });

        it('add the google maps API script to body', function() {
          service.create();
          expect(appendChild).toHaveBeenCalled();
          expect(appendChild.calls.first().args[0].src).toMatch('maps.googleapis.com/maps/api/js');
        });

        it('should include the configured key', function() {
          service.create('');
          expect(appendChild.calls.first().args[0].src).toMatch('key=a-test-key');
        });

        describe('with google maps API loaded by someone other than this service', function() {
          beforeEach(function() {
            loadMockMapsAPI();
          });

          it('should\'nt append another async loading script on create', function() {
            service.create();
            expect(appendChild).not.toHaveBeenCalled();
          });
        });

        describe('with API initialized', function() {
          beforeEach(function() {
            service.create('canvas-id');
            callMapsCallback();
          });

          it('shouldn\'t append another async loading script on create', function() {
            appendChild.calls.reset();
            service.create();
            expect(appendChild).not.toHaveBeenCalled();
          });

          it('should create the map on the specified canvas', function() {
            var mockElement = 'mockElement';
            $window.document.getElementById.and.returnValue(mockElement);

            $rootScope.$apply();

            expect($window.document.getElementById).toHaveBeenCalledWith('canvas-id');
            expect($window.google.maps.Map).toHaveBeenCalled();
            var args = $window.google.maps.Map.calls.first().args;
            expect(args[0]).toBe(mockElement);
            expect(args[1].zoom).toBeDefined();
          });
        });
      });

      describe('creating the Specifier, with maps initialized and map available', function() {
        var specifierPromise, position;
        beforeEach(function() {
          specifierPromise = service.create();
          callMapsCallback();
        });

        it('should resolve the promise with a specifier', function() {
          var specifier;
          specifierPromise.then(function(l) {
            specifier = l;
          });
          $rootScope.$apply();
          expect(specifier).toBeDefined();
        });

        describe('with the specifier resolved', function() {
          var specifier;
          beforeEach(function() {
            specifierPromise.then(function(l) {
              specifier = l;
            });
            $rootScope.$apply();
          });

          it('should not be able to provide a location yet', function() {
            expect(function() {
              specifier.getLocation();
            }).toThrow();
          });

          describe('onResized', function() {
            var trigger;
            beforeEach(function() {
              trigger = jasmine.createSpy('trigger');
              $window.google.maps.event = {
                trigger: trigger,
              };
            });

            it('should trigger the resize event on the map', function() {
              specifier.onResized();
              expect(trigger).toHaveBeenCalledWith($window.google.maps.Map(), 'resize');
            });
          });

          describe('setting the address', function() {
            var maps, map, marker, geocoder;
            beforeEach(function() {
              maps = $window.google.maps;
              map = maps.Map();
              maps.Map.calls.reset();
              geocoder = maps.Geocoder();
              maps.Geocoder.calls.reset();
              marker = maps.Marker();
              maps.Marker.calls.reset();
            });

            describe('for the first time', function() {
              it('should initialize the geocoder', function() {
                specifier.setAddress('whatever');
                expect(maps.Geocoder).toHaveBeenCalled();
              });

              it('should geocode the address', function() {
                specifier.setAddress('whatever');
                expect(geocoder.geocode).toHaveBeenCalled();
                var args = geocoder.geocode.calls.first().args;
                var geocoderParams = args[0];
                var callback = args[1];
                expect(geocoderParams.address).toEqual('whatever');
              });

              describe('with geocoding failing', function() {
                beforeEach(function() {
                  specifier.setAddress('whatever');
                  var callback = geocoder.geocode.calls.first().args[1];
                  callback({}, $window.google.maps.GeocoderStatus.notOK);
                });

                it('should not create the marker', function() {
                  $rootScope.$apply();
                  expect(maps.Marker).not.toHaveBeenCalled();
                });
              });

              describe('with geocoding succeeding', function() {
                beforeEach(function() {
                  specifier.setAddress('whatever');
                  var callback = geocoder.geocode.calls.first().args[1];
                  callback([{
                    geometry: {
                      location: 'the location',
                      viewport: 'the viewport',
                    },
                  }], $window.google.maps.GeocoderStatus.OK);
                });

                it('should create the marker', function() {
                  $rootScope.$apply();
                  expect(maps.Marker).toHaveBeenCalled();
                  var markerOptions = maps.Marker.calls.first().args[0];
                  expect(markerOptions.map).toEqual(map);
                  expect(markerOptions.position).toEqual('the location');
                });

                it('should set the map center location and viewport', function() {
                  $rootScope.$apply();
                  expect(map.setCenter).toHaveBeenCalledWith('the location');
                  expect(map.fitBounds).toHaveBeenCalledWith('the viewport');
                });
              });

              describe('with region included', function() {
                it('should geocode tha address including the region', function() {
                  specifier.setAddress('whatever', 'a-region');
                  expect(geocoder.geocode).toHaveBeenCalled();
                  var args = geocoder.geocode.calls.first().args;
                  var geocoderParams = args[0];
                  var callback = args[1];
                  expect(geocoderParams.address).toEqual('whatever');
                  expect(geocoderParams.region).toEqual('a-region');
                });
              });
            });

            describe('with the marker and geocoder already initialized', function() {
              beforeEach(function() {
                specifier.setAddress('whatever');
                var callback = geocoder.geocode.calls.first().args[1];
                callback([{
                  geometry: {
                    location: 'the location',
                    viewport: 'the viewport',
                  },
                }], $window.google.maps.GeocoderStatus.OK);
                $rootScope.$apply();
                maps.Geocoder.calls.reset();
                maps.Map.calls.reset();
                geocoder.geocode.calls.reset();
              });

              it('should NOT re-initialize the geocoder', function() {
                specifier.setAddress('whatever2');
                expect(maps.Geocoder).not.toHaveBeenCalled();
              });

              it('should geocode the address', function() {
                specifier.setAddress('whatever2');
                expect(geocoder.geocode).toHaveBeenCalled();
                var args = geocoder.geocode.calls.first().args;
                var geocoderParams = args[0];
                var callback = args[1];
                expect(geocoderParams.address).toEqual('whatever2');
              });

              describe('with geocoding failing', function() {
                beforeEach(function() {
                  specifier.setAddress('whatever');
                  var callback = geocoder.geocode.calls.first().args[1];
                  callback({}, $window.google.maps.GeocoderStatus.notOK);
                });

                it('should not set the marker position', function() {
                  $rootScope.$apply();
                  expect(marker.setPosition).not.toHaveBeenCalled();
                });
              });

              describe('with geocoding succeeding', function() {
                beforeEach(function() {
                  specifier.setAddress('whatever2');
                  var callback = geocoder.geocode.calls.first().args[1];
                  callback([{
                    geometry: {
                      location: 'the location2',
                      viewport: 'the viewport2',
                    },
                  }], $window.google.maps.GeocoderStatus.OK);
                });

                it('should set the marker position', function() {
                  $rootScope.$apply();
                  expect(marker.setPosition).toHaveBeenCalledWith('the location2');
                });

                it('should set the map center location and viewport', function() {
                  $rootScope.$apply();
                  expect(map.setCenter).toHaveBeenCalledWith('the location2');
                  expect(map.fitBounds).toHaveBeenCalledWith('the viewport2');
                });
              });

              describe('with region included', function() {
                it('should geocode tha address including the region', function() {
                  specifier.setAddress('whatever', 'a-region');
                  expect(geocoder.geocode).toHaveBeenCalled();
                  var args = geocoder.geocode.calls.first().args;
                  var geocoderParams = args[0];
                  var callback = args[1];
                  expect(geocoderParams.address).toEqual('whatever');
                  expect(geocoderParams.region).toEqual('a-region');
                });
              });
            });
          });
        });
      });
    });
  });
});
<|endoftext|>


<commit_before>const expect = require('chai').expect,
      stats = require('../lib/parse/stats')

const tenNums = [5.232405283022672,
0.3472179570235312,
4.086789721623063,
6.360535882413387,
3.300832014065236,
7.874558444600552,
0.47707584453746676,
4.030399369075894,
2.0213337219320238,
0.5420976644381881
]

const epsilon = 0.00001

describe('statistics for 10 values', function(){
  it('simple stats', function(){
     const st = stats(tenNums)
     expect(st.mean).to.be.closeTo(3.42732, epsilon)
     expect(st.min).to.be.closeTo(0.3472179570235312, epsilon)
     expect(st.max).to.be.closeTo(7.874558444600552, epsilon)
     expect(st.stddev).to.be.closeTo(2.46874, epsilon)
     expect(st.conf).to.be.closeTo(2.22814*(2.46874/Math.sqrt(10)), epsilon)
     expect(st.samples).to.eql(10)
  });
})
<commit_msg>Test for a single stat.<commit_after>const expect = require('chai').expect,
      stats = require('../lib/parse/stats')

const tenNums = [5.232405283022672,
0.3472179570235312,
4.086789721623063,
6.360535882413387,
3.300832014065236,
7.874558444600552,
0.47707584453746676,
4.030399369075894,
2.0213337219320238,
0.5420976644381881
]

const epsilon = 0.00001

describe('statistics for 10 values', function(){
  it('simple stats', function(){
     const st = stats(tenNums)
     expect(st.mean).to.be.closeTo(3.42732, epsilon)
     expect(st.min).to.be.closeTo(0.3472179570235312, epsilon)
     expect(st.max).to.be.closeTo(7.874558444600552, epsilon)
     expect(st.stddev).to.be.closeTo(2.46874, epsilon)
     expect(st.conf).to.be.closeTo(2.22814*(2.46874/Math.sqrt(10)), epsilon)
     expect(st.samples).to.eql(10)
  });
})

describe('statistics for 1 value', function(){
  it('simple stats', function(){
     const st = stats([7])
     expect(st.mean).to.be.closeTo(7, epsilon)
     expect(st.min).to.be.closeTo(7, epsilon)
     expect(st.max).to.be.closeTo(7, epsilon)
     expect(st.stddev).to.be.closeTo(0, epsilon)
     expect(st.conf).to.be.closeTo(0, epsilon)
     expect(st.samples).to.eql(1)
  });
})
<|endoftext|>


<commit_before>/**
 * Created by User2 on 4/7/2015.
 */
var x = document.getElementById("map_holder");
var map;
var infowindow;
getLocation();
function getLocation() {
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(showPosition, showError);
    } else {
        x.innerHTML = "Geolocation is not supported by this browser.";
    }
}
function showPosition(position) {

    document.querySelector("#mapcanvas").style.height='300px';
    var latlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);

    var myOptions = {
        zoom: 15,
        center: latlng,
        mapTypeControl: false,
        navigationControlOptions: {style: google.maps.NavigationControlStyle.SMALL},
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
     map = new google.maps.Map(document.getElementById("mapcanvas"), myOptions);

    var infowindowmain = new google.maps.InfoWindow({
        map: map,
        position: latlng,
        content:"You are here! (at least within a "+position.coords.accuracy+" meter radius)"
    });

    var marker = new google.maps.Marker({
        position: latlng,
        map: map,
        title:"You are here! (at least within a "+position.coords.accuracy+" meter radius)"
    });

    var request = {
        location: latlng,
        radius: 500,
        types: ['food']
    };
    infowindow = new google.maps.InfoWindow();
    var service = new google.maps.places.PlacesService(map);
    service.nearbySearch(request, callback);
}

function callback(results, status) {
    if (status == google.maps.places.PlacesServiceStatus.OK) {
        for (var i = 0; i < results.length; i++) {
          createMarker(results[i]);
        }
    }
}

function createMarker(place) {

    var placeLoc = place.geometry.location;
    var marker = new google.maps.Marker({
        map: map,
        position: place.geometry.location
    });

    var requestdata = {
        placeId: place.place_id
    };
    var service = new google.maps.places.PlacesService(map);
    service.getDetails(requestdata, function(placedata, status) {
        if (status == google.maps.places.PlacesServiceStatus.OK) {
            var str="";

            var place_name = "
"+placedata.name+"
";
            var place_address = "
"+placedata.formatted_address+"
";
            var place_img_representation = "";
            var place_internationa_phonenuber =(typeof placedata.international_phone_number !=='undefined')?"
"+placedata.international_phone_number +"
":"";
            var place_rating =(typeof placedata.rating !=='undefined')? "
Place Rating:"+placedata.rating+"
":"";

            var place_website = "WEBSITE"
            var review_author="";
            var review_text="";
            var overall_rating="";
            var specific_rating = "";
            for (var key in placedata.reviews) {
                    if(typeof key.hasOwnProperty(key) !== 'undefined'){

                        if (key.hasOwnProperty(key)) {
                            var obj = placedata.reviews[key];
                            console.log(placedata.reviews[key]["author_name"]+" "+placedata.reviews[key]["author_url"]+" "+placedata.reviews[key]["rating"]+" "+placedata.reviews[key]["text"]+" "+placedata.reviews[key]["time"]);

                             review_author =  (typeof placedata.reviews[key]["author_name"] !=='undefined')? "  "+placedata.reviews[key]["author_name"]+"":"d";
                             review_text =  (typeof  placedata.reviews[key]["text"] !=='undefined')?"
"+placedata.reviews[key]["text"]+"
":"d";
                             overall_rating =(typeof placedata.reviews[key]["rating"] !=='undefined')?"Review Overall rating: "+ placedata.reviews[key]["rating"]+"
":"d";
                            for (var prop in obj) {
                                if (obj.hasOwnProperty(prop)) {
                                    if(prop === 'aspects'){

                                        for(var a in obj[prop]){
                                            if(obj[prop].hasOwnProperty(a)){
                                                specific_rating +=(typeof obj[prop][a]["type"] !=='undefined')? obj[prop][a]["type"] +": "+obj[prop][a]["rating"]+"
":"";
                                            }
                                        }
                                    }
                                }
                            }
                        }

                    }
            }

            var placereview="";
            if(review_author !==''){
                placereview= "
"+review_author+review_text+overall_rating+specific_rating+"
";
            }else{
                placereview='';
            }
            var str_container = "
   "+place_name+place_address+place_img_representation+place_internationa_phonenuber+place_rating+place_website +placereview+"
";
            var textnode= document.createTextNode(str_container);
            document.getElementById("placeres").insertAdjacentHTML('beforeend',str_container);

        }
    });

    google.maps.event.addListener(marker, 'click', function() {
        infowindow.setContent(place.name);
        infowindow.open(map, this);
    });

}

function showError(error) {
    switch (error.code) {
        case error.PERMISSION_DENIED:
            x.innerHTML = "User denied the request for Geolocation."
            break;
        case error.POSITION_UNAVAILABLE:
            x.innerHTML = "Location information is unavailable."
            break;
        case error.TIMEOUT:
            x.innerHTML = "The request to get user location timed out."
            break;
        case error.UNKNOWN_ERROR:
            x.innerHTML = "An unknown error occurred."
            break;
    }
}
<commit_msg>only display available data<commit_after>/**
 * Created by User2 on 4/7/2015.
 */
var x = document.getElementById("map_holder");
var map;
var infowindow;
getLocation();
function getLocation() {
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(showPosition, showError);
    } else {
        x.innerHTML = "Geolocation is not supported by this browser.";
    }
}
function showPosition(position) {

    document.querySelector("#mapcanvas").style.height='300px';
    var latlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);

    var myOptions = {
        zoom: 15,
        center: latlng,
        mapTypeControl: false,
        navigationControlOptions: {style: google.maps.NavigationControlStyle.SMALL},
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
     map = new google.maps.Map(document.getElementById("mapcanvas"), myOptions);

    var infowindowmain = new google.maps.InfoWindow({
        map: map,
        position: latlng,
        content:"You are here! (at least within a "+position.coords.accuracy+" meter radius)"
    });

    var marker = new google.maps.Marker({
        position: latlng,
        map: map,
        title:"You are here! (at least within a "+position.coords.accuracy+" meter radius)"
    });

    var request = {
        location: latlng,
        radius: 500,
        types: ['food']
    };
    infowindow = new google.maps.InfoWindow();
    var service = new google.maps.places.PlacesService(map);
    service.nearbySearch(request, callback);
}

function callback(results, status) {
    if (status == google.maps.places.PlacesServiceStatus.OK) {
        for (var i = 0; i < results.length; i++) {
          createMarker(results[i]);
        }
    }
}

function createMarker(place) {

    var placeLoc = place.geometry.location;

    var marker = new google.maps.Marker({
        map: map,
        position: place.geometry.location
    });

    var requestdata = {
        placeId: place.place_id
    };
    var service = new google.maps.places.PlacesService(map);
    service.getDetails(requestdata, function(placedata, status) {
       // alert( "\nstatus:"+ status+"\n error:"+google.maps.places.PlacesServiceStatus.ERROR+"\n INVALID_REQUEST: "+google.maps.places.PlacesServiceStatus.INVALID_REQUEST+"\n OK:"+google.maps.places.PlacesServiceStatus.OK+"\n OVER_QUERY_LIMIT:"+google.maps.places.PlacesServiceStatus.OVER_QUERY_LIMIT+"\n NOT_FOUND: "+google.maps.places.PlacesServiceStatus.NOT_FOUND+"\n REQUEST_DENIED :"+google.maps.places.PlacesServiceStatus.REQUEST_DENIED+" \n UNKNOWN_ERROR: "+google.maps.places.PlacesServiceStatus.UNKNOWN_ERROR+"\n ZERO_RESULTS: "+google.maps.places.PlacesServiceStatus.ZERO_RESULTS );

        if (status == google.maps.places.PlacesServiceStatus.OK)
        {

            var str="";

            var place_name = "
"+placedata.name+"
";
            var place_address = "
"+placedata.formatted_address+"
";
            var place_img_representation = "";
            var place_internationa_phonenuber =(typeof placedata.international_phone_number !=='undefined')?"
"+placedata.international_phone_number +"
":"";
            var place_rating =(typeof placedata.rating !=='undefined')? "
Place Rating:"+placedata.rating+"
":"";

            var place_website =(typeof placedata.website !== 'undefined')? "WEBSITE":"";
            var review_author="";
            var review_text="";
            var overall_rating="";
            var specific_rating = "";
            for (var key in placedata.reviews) {
                    if(typeof key.hasOwnProperty(key) !== 'undefined'){

                        if (key.hasOwnProperty(key)) {
                            var obj = placedata.reviews[key];
                            console.log(placedata.reviews[key]["author_name"]+" "+placedata.reviews[key]["author_url"]+" "+placedata.reviews[key]["rating"]+" "+placedata.reviews[key]["text"]+" "+placedata.reviews[key]["time"]);

                             review_author =  (typeof placedata.reviews[key]["author_name"] !=='undefined')? "  "+placedata.reviews[key]["author_name"]+"":"d";
                             review_text =  (typeof  placedata.reviews[key]["text"] !=='undefined')?"
"+placedata.reviews[key]["text"]+"
":"d";
                             overall_rating =(typeof placedata.reviews[key]["rating"] !=='undefined')?"Review Overall rating: "+ placedata.reviews[key]["rating"]+"
":"d";
                            for (var prop in obj) {
                                if (obj.hasOwnProperty(prop)) {
                                    if(prop === 'aspects'){

                                        for(var a in obj[prop]){
                                            if(obj[prop].hasOwnProperty(a)){
                                                specific_rating +=(typeof obj[prop][a]["type"] !=='undefined')? obj[prop][a]["type"] +": "+obj[prop][a]["rating"]+"
":"";
                                            }
                                        }
                                    }
                                }
                            }
                        }

                    }
            }

            var review_str="";
            if(review_author !=='' ) {
                review_str="
"+review_author+review_text+overall_rating+specific_rating+"
";
            }
            var str_container = "
   "+place_name+place_address+place_img_representation+place_internationa_phonenuber+place_rating+place_website +review_str+"
";
            var textnode= document.createTextNode(str_container);
            document.getElementById("placeres").insertAdjacentHTML('beforeend',str_container);

        }
    });

    google.maps.event.addListener(marker, 'click', function() {
        infowindow.setContent(place.name);
        infowindow.open(map, this);
    });

}

function showError(error) {
    switch (error.code) {
        case error.PERMISSION_DENIED:
            x.innerHTML = "User denied the request for Geolocation."
            break;
        case error.POSITION_UNAVAILABLE:
            x.innerHTML = "Location information is unavailable."
            break;
        case error.TIMEOUT:
            x.innerHTML = "The request to get user location timed out."
            break;
        case error.UNKNOWN_ERROR:
            x.innerHTML = "An unknown error occurred."
            break;
    }
}
<|endoftext|>