<commit_before>var sys = require('sys');
var net = require('net');
var logger = require('./logger');
var Peer = require('./peer').Peer;
var Connection = require('./connection').Connection;

var PeerManager = exports.PeerManager = function (node) {
  events.EventEmitter.call(this);

  this.node = node;
  this.enabled = false;
  this.timer = null;

  this.peers = [];
  this.forcePeers = [];
  this.connections = [];
  this.isConnected = false;
  this.connectAutoConn = null;
  this.peerDiscovery = true;

  // Move these to the Node's settings object
  this.interval = 5000;
  this.minConnections = 8;
  this.minKnownPeers = 10;
};

sys.inherits(PeerManager, events.EventEmitter);

PeerManager.prototype.enable = function ()
{
  this.enabled = true;

  var initialPeers = this.node.cfg.network.initialPeers;
  var forcePeers = this.node.cfg.network.initialPeers;
  var bootstrap = this.node.cfg.network.bootstrap;

  if (this.node.cfg.network.connect === "auto") {
    // Connect auto mode, try to connect to localhost:8333 and if that doesn't
    // work fall back to bootstrapping.

    logger.info('Trying to detect local Bitcoin node (connect="auto")');

    var localPeer = new Peer('localhost', this.node.cfg.network.port);
    this.addPeer(localPeer);
    var localConn = this.connectTo(localPeer);
    if (localConn) {
      localConn.once('verack', function verackHandler() {
        // Once we have made a successful connection once, we will enter
        // full-on proxy node mode and connect only to the local peer.
        this.connectAutoConn = false;
        this.bootstrap = [];
        this.peers = [localPeer];
        this.peerDiscovery = false;
      }.bind(this));

      this.connectAutoConn = localConn;
    } else {
      // Could not create local connection, nothing to do, we'll fallback to
      // the standard peer-to-peer mode with bootstrapping.
    }

  } else if (this.node.cfg.network.connect === "p2p") {
    // P2P is the default, do nothing
  } else if (this.node.cfg.network.connect === "none") {
    initialPeers = [];
    bootstrap = [];
    this.peerDiscovery = false;
  } else if ("string" === typeof this.node.cfg.network.connect) {
    initialPeers = [this.node.cfg.network.connect];
    bootstrap = [];
    this.peerDiscovery = false;
  } else if (Array.isArray(this.node.cfg.network.connect)) {
    initialPeers = this.node.cfg.network.connect;
    bootstrap = [];
    this.peerDiscovery = false;
  }

  initialPeers.forEach(function (peer) {
    if ("string" !== typeof peer) {
      throw new Error("PeerManager.enable(): Invalid configuration for initial"
                      + "peers.");
    }
    this.addPeer(peer);
  }.bind(this));
  this.bootstrap = bootstrap;

  this.forcePeers = forcePeers.map(function (peer) {
    if ("string" !== typeof peer) {
      throw new Error("PeerManager.enable(): Invalid configuration for initial"
                      + "peers.");
    }
    return new Peer(peer);
  });

  // Listen to incoming connections
  if (!this.node.cfg.network.noListen) {
    logger.info('Listening for Bitcoin connections on port '+
                this.node.cfg.network.port);
    try {
      this.server = net.createServer(function (socketConn) {
        this.addConnection(socketConn, new Peer(socketConn.remoteAddress));
      }.bind(this));
      this.server.listen(this.node.cfg.network.port);
    } catch (e) {
      logger.warn("Could not start Bitcoin server");
      logger.warn("Reason: "+e.message);
    }
  }

  if (!this.timer) {
    this.pingStatus();
  }
};

PeerManager.prototype.disable = function ()
{
  this.enabled = false;
};

PeerManager.prototype.addPeer = function (peer, port) {
  if (peer instanceof Peer) {
    this.peers.push(peer);
  } else if ("string" == typeof peer) {
    this.addPeer(new Peer(peer, port));
  } else {
    logger.log('error', 'Node.addPeer(): Invalid value provided for peer',
               {val: peer});
    throw 'Node.addPeer(): Invalid value provided for peer.';
  }
};

PeerManager.prototype.pingStatus = function pingStatus()
{
  if (!this.enabled) {
    return;
  }

  this.checkStatus();

  this.timer = setTimeout(this.pingStatus.bind(this), this.interval);
};

PeerManager.prototype.checkStatus = function checkStatus()
{
  // Make sure we are connected to all forcePeers
  if (this.forcePeers.length) {
    var forcePeerIndex = {};
    this.forcePeers.forEach(function (peer) {
      forcePeerIndex[peer.toString()] = peer;
    });

    // Ignore the ones we're already connected to
    this.connections.forEach(function (conn) {
      var peerName = conn.peer.toString();
      if ("undefined" !== forcePeerIndex[peerName]) {
        delete forcePeerIndex[peerName];
      }
    });

    Object.keys(forcePeerIndex).forEach(function (i) {
      this.connectTo(forcePeerIndex[i]);
    }.bind(this));
  }

  // If this flag is set it means we're still trying the local peer.
  if (this.connectAutoConn) {
    if (this.connections.indexOf(this.connectAutoConn) != -1) {
      // Connection hasn't failed or closed, we'll keep going
      return;
    } else {
      this.connectAutoConn = null;
    }
  }

  var i;
  if (this.peers.length < this.minKnownPeers) {
    var bootstrap = this.bootstrap;
    for (i = 0; i < bootstrap.length; i++) {
      if ("function" == typeof bootstrap[i].bootstrap) {
        bootstrap[i].bootstrap(this.node, this);
      }
    }
  }

  // Find peers that we think are valid, but aren't connected to
  var connectablePeers = [];
  outerloop:
  for (i = 0; i < this.peers.length; i++) {
    for (var j = 0; j < this.connections.length; j++) {
      if (this.connections[j].peer == this.peers[i]) {
        continue outerloop;
      }
    }
    connectablePeers.push(this.peers[i]);
  }

  while (this.connections.length < this.minConnections &&
         connectablePeers.length) {
    var peer = connectablePeers.splice(Math.random()*connectablePeers.length, 1);

    this.connectTo(peer[0]);
  }
};

PeerManager.prototype.connectTo = function (peer)
{
  logger.info('Connecting to peer '+peer);

  try {
    this.addConnection(peer.createConnection(), peer);
  } catch (e) {
    logger.error('Error creating connection',e);
    return null;
  }
};

PeerManager.prototype.addConnection = function (socketConn, peer) {
  var conn = new Connection(this.node, socketConn, peer);
  this.connections.push(conn);
  this.node.addConnection(conn);

  conn.addListener('version', this.handleVersion.bind(this));
  conn.addListener('verack', this.handleReady.bind(this));
  conn.addListener('addr', this.handleAddr.bind(this));
  conn.addListener('getaddr', this.handleGetAddr.bind(this));
  conn.addListener('error', this.handleError.bind(this));
  conn.addListener('disconnect', this.handleDisconnect.bind(this));

  return conn;
};

PeerManager.prototype.handleVersion = function (e) {
  if (!e.conn.inbound) {
    // TODO: Advertise our address (if listening)
  }
  // Get recent addresses
  if (this.peerDiscovery &&
      (e.message.version >= 31402 || this.peers.length < 1000)) {
    e.conn.sendGetAddr();
    e.conn.getaddr = true;
  }
};

PeerManager.prototype.handleReady = function (e) {
  this.emit('connect', {
    pm: this,
    conn: e.conn,
    socket: e.socket,
    peer: e.peer
  });

  if (this.isConnected == false) {
    this.emit('netConnected');

    this.isConnected = true;
  }
};

PeerManager.prototype.handleAddr = function (e) {
  if (!this.peerDiscovery) {
    return;
  }

  var now = this.node.getAdjustedTime();
  e.message.addrs.forEach(function (addr) {
    try {
      // In case of an invalid time, assume "5 days ago"
      if (addr.time <= 100000000 || addr.time > (now + 10 * 60)) {
        addr.time = now - 5 * 24 * 60 * 60;
      }
      var peer = new Peer(addr.ip, addr.port, addr.services);
      peer.lastSeen = addr.time;

      // TODO: Handle duplicate peers
      this.peers.push(peer);

      // TODO: Handle addr relay
    } catch(e) {
      logger.warn("Invalid addr received: "+e.message);
    }
  }.bind(this));
  if (e.message.addrs.length < 1000 ) {
    e.conn.getaddr = false;
  }
};

PeerManager.prototype.handleGetAddr = function (e) {
  // TODO: Reply with addr message.
};

PeerManager.prototype.handleError = function (e) {
  this.handleDisconnect.apply(this, [].slice.call(arguments));
};

PeerManager.prototype.handleDisconnect = function (e) {
  logger.info('Disconnected from peer '+e.peer);
  var i = this.connections.indexOf(e.conn);
  if (i != -1) {
    this.connections.splice(i, 1);
  }

  if (this.connectAutoConn) {
    logger.info('No local proxy, connecting to network instead (connect="auto")');
    this.connectAutoConn = null;
    this.peers = [];
    this.pingStatus();
  }

  if (!this.connections.length) {
    this.emit('netDisconnected');

    this.isConnected = false;
  }
};

PeerManager.prototype.getActiveConnection = function () {
  var activeConnections = this.connections.filter(function (conn) {
    return conn.active;
  });

  if (activeConnections.length) {
    var randomIndex = Math.floor(Math.random()*activeConnections.length);
    var candidate = activeConnections[randomIndex];
    if (candidate.socket.writable) {
      return candidate;
    } else {
      // Socket is not writable, remove it from active connections
      activeConnections.splice(randomIndex, 1);

      // Then try again
      // TODO: This causes an infinite recursion when all connections are dead,
      //       although it shouldn't.
      return this.getActiveConnection();
    }
  } else {
    return null;
  }
};

PeerManager.prototype.getActiveConnections = function () {
  return this.connections.slice(0);
};
<commit_msg>Fixed regression where --connect would not behave correctly.<commit_after>var sys = require('sys');
var net = require('net');
var logger = require('./logger');
var Peer = require('./peer').Peer;
var Connection = require('./connection').Connection;

var PeerManager = exports.PeerManager = function (node) {
  events.EventEmitter.call(this);

  this.node = node;
  this.enabled = false;
  this.timer = null;

  this.peers = [];
  this.forcePeers = [];
  this.connections = [];
  this.isConnected = false;
  this.connectAutoConn = null;
  this.peerDiscovery = true;

  // Move these to the Node's settings object
  this.interval = 5000;
  this.minConnections = 8;
  this.minKnownPeers = 10;
};

sys.inherits(PeerManager, events.EventEmitter);

PeerManager.prototype.enable = function ()
{
  this.enabled = true;

  var initialPeers = this.node.cfg.network.initialPeers;
  var forcePeers = this.node.cfg.network.initialPeers;
  var bootstrap = this.node.cfg.network.bootstrap;

  if (this.node.cfg.network.connect === "auto") {
    // Connect auto mode, try to connect to localhost:8333 and if that doesn't
    // work fall back to bootstrapping.

    logger.info('Trying to detect local Bitcoin node (connect="auto")');

    var localPeer = new Peer('localhost', this.node.cfg.network.port);
    this.addPeer(localPeer);
    var localConn = this.connectTo(localPeer);
    if (localConn) {
      localConn.once('verack', function verackHandler() {
        // Once we have made a successful connection once, we will enter
        // full-on proxy node mode and connect only to the local peer.
        this.connectAutoConn = false;
        this.bootstrap = [];
        this.peers = [localPeer];
        this.peerDiscovery = false;
      }.bind(this));

      this.connectAutoConn = localConn;
    } else {
      // Could not create local connection, nothing to do, we'll fallback to
      // the standard peer-to-peer mode with bootstrapping.
    }

  } else if (this.node.cfg.network.connect === "p2p") {
    // P2P is the default, do nothing
  } else if (this.node.cfg.network.connect === "none") {
    initialPeers = [];
    bootstrap = [];
    this.peerDiscovery = false;
  } else if ("string" === typeof this.node.cfg.network.connect) {
    initialPeers = [this.node.cfg.network.connect];
    bootstrap = [];
    this.peerDiscovery = false;
  } else if (Array.isArray(this.node.cfg.network.connect)) {
    initialPeers = this.node.cfg.network.connect;
    bootstrap = [];
    this.peerDiscovery = false;
  }

  initialPeers.forEach(function (peer) {
    if ("string" !== typeof peer) {
      throw new Error("PeerManager.enable(): Invalid configuration for initial"
                      + "peers.");
    }
    this.addPeer(peer);
  }.bind(this));
  this.bootstrap = bootstrap;

  this.forcePeers = forcePeers.map(function (peer) {
    if ("string" !== typeof peer) {
      throw new Error("PeerManager.enable(): Invalid configuration for initial"
                      + "peers.");
    }
    return new Peer(peer);
  });

  // Listen to incoming connections
  if (!this.node.cfg.network.noListen) {
    logger.info('Listening for Bitcoin connections on port '+
                this.node.cfg.network.port);
    try {
      this.server = net.createServer(function (socketConn) {
        this.addConnection(socketConn, new Peer(socketConn.remoteAddress));
      }.bind(this));
      this.server.listen(this.node.cfg.network.port);
    } catch (e) {
      logger.warn("Could not start Bitcoin server");
      logger.warn("Reason: "+e.message);
    }
  }

  if (!this.timer) {
    this.pingStatus();
  }
};

PeerManager.prototype.disable = function ()
{
  this.enabled = false;
};

PeerManager.prototype.addPeer = function (peer, port) {
  if (peer instanceof Peer) {
    this.peers.push(peer);
  } else if ("string" == typeof peer) {
    this.addPeer(new Peer(peer, port));
  } else {
    logger.log('error', 'Node.addPeer(): Invalid value provided for peer',
               {val: peer});
    throw 'Node.addPeer(): Invalid value provided for peer.';
  }
};

PeerManager.prototype.pingStatus = function pingStatus()
{
  if (!this.enabled) {
    return;
  }

  this.checkStatus();

  this.timer = setTimeout(this.pingStatus.bind(this), this.interval);
};

PeerManager.prototype.checkStatus = function checkStatus()
{
  // Make sure we are connected to all forcePeers
  if (this.forcePeers.length) {
    var forcePeerIndex = {};
    this.forcePeers.forEach(function (peer) {
      forcePeerIndex[peer.toString()] = peer;
    });

    // Ignore the ones we're already connected to
    this.connections.forEach(function (conn) {
      var peerName = conn.peer.toString();
      if ("undefined" !== forcePeerIndex[peerName]) {
        delete forcePeerIndex[peerName];
      }
    });

    Object.keys(forcePeerIndex).forEach(function (i) {
      this.connectTo(forcePeerIndex[i]);
    }.bind(this));
  }

  // If this flag is set it means we're still trying the local peer.
  if (this.connectAutoConn) {
    if (this.connections.indexOf(this.connectAutoConn) != -1) {
      // Connection hasn't failed or closed, we'll keep going
      return;
    } else {
      this.connectAutoConn = null;
    }
  }

  var i;
  if (this.peers.length < this.minKnownPeers) {
    var bootstrap = this.bootstrap;
    for (i = 0; i < bootstrap.length; i++) {
      if ("function" == typeof bootstrap[i].bootstrap) {
        bootstrap[i].bootstrap(this.node, this);
      }
    }
  }

  // Find peers that we think are valid, but aren't connected to
  var connectablePeers = [];
  outerloop:
  for (i = 0; i < this.peers.length; i++) {
    for (var j = 0; j < this.connections.length; j++) {
      if (this.connections[j].peer == this.peers[i]) {
        continue outerloop;
      }
    }
    connectablePeers.push(this.peers[i]);
  }

  while (this.connections.length < this.minConnections &&
         connectablePeers.length) {
    var peer = connectablePeers.splice(Math.random()*connectablePeers.length, 1);

    this.connectTo(peer[0]);
  }
};

PeerManager.prototype.connectTo = function (peer)
{
  logger.info('Connecting to peer '+peer);

  try {
    return this.addConnection(peer.createConnection(), peer);
  } catch (e) {
    logger.error('Error creating connection',e);
    return null;
  }
};

PeerManager.prototype.addConnection = function (socketConn, peer) {
  var conn = new Connection(this.node, socketConn, peer);
  this.connections.push(conn);
  this.node.addConnection(conn);

  conn.addListener('version', this.handleVersion.bind(this));
  conn.addListener('verack', this.handleReady.bind(this));
  conn.addListener('addr', this.handleAddr.bind(this));
  conn.addListener('getaddr', this.handleGetAddr.bind(this));
  conn.addListener('error', this.handleError.bind(this));
  conn.addListener('disconnect', this.handleDisconnect.bind(this));

  return conn;
};

PeerManager.prototype.handleVersion = function (e) {
  if (!e.conn.inbound) {
    // TODO: Advertise our address (if listening)
  }
  // Get recent addresses
  if (this.peerDiscovery &&
      (e.message.version >= 31402 || this.peers.length < 1000)) {
    e.conn.sendGetAddr();
    e.conn.getaddr = true;
  }
};

PeerManager.prototype.handleReady = function (e) {
  this.emit('connect', {
    pm: this,
    conn: e.conn,
    socket: e.socket,
    peer: e.peer
  });

  if (this.isConnected == false) {
    this.emit('netConnected');

    this.isConnected = true;
  }
};

PeerManager.prototype.handleAddr = function (e) {
  if (!this.peerDiscovery) {
    return;
  }

  var now = this.node.getAdjustedTime();
  e.message.addrs.forEach(function (addr) {
    try {
      // In case of an invalid time, assume "5 days ago"
      if (addr.time <= 100000000 || addr.time > (now + 10 * 60)) {
        addr.time = now - 5 * 24 * 60 * 60;
      }
      var peer = new Peer(addr.ip, addr.port, addr.services);
      peer.lastSeen = addr.time;

      // TODO: Handle duplicate peers
      this.peers.push(peer);

      // TODO: Handle addr relay
    } catch(e) {
      logger.warn("Invalid addr received: "+e.message);
    }
  }.bind(this));
  if (e.message.addrs.length < 1000 ) {
    e.conn.getaddr = false;
  }
};

PeerManager.prototype.handleGetAddr = function (e) {
  // TODO: Reply with addr message.
};

PeerManager.prototype.handleError = function (e) {
  this.handleDisconnect.apply(this, [].slice.call(arguments));
};

PeerManager.prototype.handleDisconnect = function (e) {
  logger.info('Disconnected from peer '+e.peer);
  var i = this.connections.indexOf(e.conn);
  if (i != -1) {
    this.connections.splice(i, 1);
  }

  if (this.connectAutoConn) {
    logger.info('No local proxy, connecting to network instead (connect="auto")');
    this.connectAutoConn = null;
    this.peers = [];
    this.pingStatus();
  }

  if (!this.connections.length) {
    this.emit('netDisconnected');

    this.isConnected = false;
  }
};

PeerManager.prototype.getActiveConnection = function () {
  var activeConnections = this.connections.filter(function (conn) {
    return conn.active;
  });

  if (activeConnections.length) {
    var randomIndex = Math.floor(Math.random()*activeConnections.length);
    var candidate = activeConnections[randomIndex];
    if (candidate.socket.writable) {
      return candidate;
    } else {
      // Socket is not writable, remove it from active connections
      activeConnections.splice(randomIndex, 1);

      // Then try again
      // TODO: This causes an infinite recursion when all connections are dead,
      //       although it shouldn't.
      return this.getActiveConnection();
    }
  } else {
    return null;
  }
};

PeerManager.prototype.getActiveConnections = function () {
  return this.connections.slice(0);
};
<|endoftext|>


<commit_before>/**
* Copyright 2012 Microsoft Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

var should = require('should');

var cli = require('../cli');
var capture = require('../util').capture;

var fs = require('fs');
var pathUtil = require('path');
if (!fs.existsSync) {
    fs.existsSync = pathUtil.existsSync;
};

var baseTestTempDir = "__temp";
var testDirBase = "test";
var testDirIndex = 0;
var testDir = "";

suite('cli', function () {
    suite('site deploymentscript', function () {
        setup(function () {
            // Reset site deploymentscript command
            for (var i in cli.categories.site.commands) {
                var command = cli.categories.site.commands[i];
                if (command.name == 'deploymentscript') {
                    delete command.php;
                    delete command.basic;
                    delete command.aspWAP;
                    delete command.aspWebSite;
                    delete command.python;
                    delete command.node;
                    delete command.repositoryRoot;
                    delete command.suppressPrompt;
                    delete command.scriptType;
                    delete command.solutionFile;
                }
            }

            testDirIndex++;
            testDir = pathUtil.join(baseTestTempDir, testDirBase + testDirIndex);
            ensurePathExists(testDir);
        });

        teardown(function () {
            removePath(testDir);
        });

        test('generate basic deployment script (--basic -y -r)', function (done) {
            var cmd = ('node cli.js site deploymentscript --basic -y -r ' + testDir).split(' ');

            runBasicSiteDeploymentScriptScenario(cmd, done);
        });

        test('generate php deployment script (--php -y -r)', function (done) {
            var cmd = ('node cli.js site deploymentscript --php -y -r ' + testDir).split(' ');

            runBasicSiteDeploymentScriptScenario(cmd, done);
        });

        test('generate python deployment script (--python -y -r)', function (done) {
            var cmd = ('node cli.js site deploymentscript --python -y -r ' + testDir).split(' ');

            runBasicSiteDeploymentScriptScenario(cmd, done);
        });

        test('generate basic aspWebSite deployment script (--aspWebSite -y -r)', function (done) {
            var cmd = ('node cli.js site deploymentscript --aspWebSite -y -r ' + testDir).split(' ');

            runBasicSiteDeploymentScriptScenario(cmd, done);
        });

        test('using exclusion flags together should fail (--aspWebSite --python ...)', function (done) {
            var cmd = ('node cli.js site deploymentscript --aspWebSite --python -r ' + testDir).split(' ');

            runErrorScenario(cmd, 'specify only one of these flags', done);
        });

        test('using exclusion flags together should fail (--node --php ...)', function (done) {
            var cmd = ('node cli.js site deploymentscript --node --php -r ' + testDir).split(' ');

            runErrorScenario(cmd, 'specify only one of these flags', done);
        });

        test('using exclusion flags together should fail (--aspWAP . --basic ...)', function (done) {
            var cmd = ('node cli.js site deploymentscript --aspWAP . --basic -r ' + testDir).split(' ');

            runErrorScenario(cmd, 'specify only one of these flags', done);
        });

        test('using exclusion flags together should fail (--basic --php ...)', function (done) {
            var cmd = ('node cli.js site deploymentscript --basic --php -r ' + testDir).split(' ');

            runErrorScenario(cmd, 'specify only one of these flags', done);
        });

        test('--aspWAP requires project file path argument', function (done) {
            var cmd = ('node cli.js site deploymentscript -r ' + testDir + ' --aspWAP').split(' ');
            i = 0;
            runErrorScenario(cmd, 'argument missing', done);
        });

        test('--node requires directory to contain server.js file (--node)', function (done) {
            var cmd = ('node cli.js site deploymentscript --node -r ' + testDir).split(' ');

            runErrorScenario(cmd, 'Missing server.js/app.js file', done);
        });
    });
});

function runBasicSiteDeploymentScriptScenario(cmd, callback) {
    runSiteDeploymentScriptScenario(
        cmd,
        ['Generating deployment script for Web Site', 'Generated deployment script (deploy.cmd and .deployment)'],
        ['echo Handling Basic Web Site deployment.'],
        'deploy.cmd',
        callback);
}
var i = 0;
function runErrorScenario(cmd, errorText, callback) {
    runCommand(cmd, function (result, e) {
        if (e) {
            callback(e);
            return;
        }

        result.exitStatus.should.equal(1, 'Received success status exit code');
        result.errorText.should.include(errorText);
        callback();
    });
}

function runSiteDeploymentScriptScenario(cmd, outputContains, scriptContains, scriptFileName, callback) {
    runCommand(cmd, function (result, e) {
        if (e) {
            callback(e);
            return;
        }

        result.exitStatus.should.equal(0, 'Received an error status exit code');

        for (var i; i < outputContains.length; i++) {
            result.text.should.include(outputContains[i]);
        }

        var deployCmdContent = getFileContent(scriptFileName);
        for (var i; i < scriptContains.length; i++) {
            deployCmdContent.should.include(scriptContains[i]);
        }

        var deploymentFileContent = getFileContent('.deployment');
        deploymentFileContent.should.include(scriptFileName);

        callback();
    });
}

function runCommand(cmd, callback) {
    capture(function () {
        cli.parse(cmd);
    }, function (result) {
        if (result.error) {
            // To avoid calling done twice
            return;
        }

        try {
            console.log('\n' + result.text);
            console.log(result.errorText);
            callback(result);
        }
        catch (e) {
            callback(result, e);
        }
    });
}

function getFileContent(path) {
    path = pathUtil.join(testDir, path);
    fs.existsSync(path).should.equal(true, "File doesn't exist: " + path);
    return fs.readFileSync(path, 'utf8');
}

function removePath(path) {
    var stat = tryGetFileStat(path);
    if (stat) {
        if (!stat.isDirectory()) {
            tryRemoveFile(path);
        }
        else {
            var files = fs.readdirSync(path);
            for (var index in files) {
                var file = files[index];
                var filePath = pathUtil.join(path, file);
                removePath(filePath);
            }

            tryRemoveDir(path);
        }
    }
}

function tryGetFileStat(path) {
    try {
        return fs.statSync(path);
    }
    catch (e) {
        if (e.errno == 34) {
            // Return null if path doesn't exist
            return null;
        }

        throw e;
    }
}

function tryRemoveFile(path) {
    try {
        fs.unlinkSync(path);
    }
    catch (e) {
        console.log(e);
    }
}

function tryRemoveDir(path) {
    try {
        fs.rmdirSync(path);
    }
    catch (e) {
    }
}

function ensurePathExists(path) {
    if (!fs.existsSync(path)) {
        ensurePathExists(pathUtil.dirname(path));
        fs.mkdirSync(path);
    }
}
<commit_msg>Added node.js web site type test.<commit_after>/**
* Copyright 2012 Microsoft Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

var should = require('should');

var cli = require('../cli');
var capture = require('../util').capture;

var fs = require('fs');
var pathUtil = require('path');
if (!fs.existsSync) {
    fs.existsSync = pathUtil.existsSync;
};

var baseTestTempDir = "__temp";
var testDirBase = "test";
var testDirIndex = 0;
var testDir = "";

suite('cli', function () {
    suite('site deploymentscript', function () {
        setup(function () {
            // Reset site deploymentscript command
            for (var i in cli.categories.site.commands) {
                var command = cli.categories.site.commands[i];
                if (command.name == 'deploymentscript') {
                    delete command.php;
                    delete command.basic;
                    delete command.aspWAP;
                    delete command.aspWebSite;
                    delete command.python;
                    delete command.node;
                    delete command.repositoryRoot;
                    delete command.suppressPrompt;
                    delete command.scriptType;
                    delete command.solutionFile;
                }
            }

            testDirIndex++;
            testDir = pathUtil.join(baseTestTempDir, testDirBase + testDirIndex);
            ensurePathExists(testDir);
        });

        teardown(function () {
            removePath(testDir);
        });

        test('generate basic deployment script (--basic -r)', function (done) {
            var cmd = ('node cli.js site deploymentscript --basic -r ' + testDir).split(' ');

            runBasicSiteDeploymentScriptScenario(cmd, done);
        });

        test('generate php deployment script (--php -r)', function (done) {
            var cmd = ('node cli.js site deploymentscript --php -r ' + testDir).split(' ');

            runBasicSiteDeploymentScriptScenario(cmd, done);
        });

        test('generate python deployment script (--python -r)', function (done) {
            var cmd = ('node cli.js site deploymentscript --python -r ' + testDir).split(' ');

            runBasicSiteDeploymentScriptScenario(cmd, done);
        });

        test('generate python deployment script twice with -y (--python -y -r)', function (done) {
            var cmd = ('node cli.js site deploymentscript --python -y -r ' + testDir).split(' ');

            runBasicSiteDeploymentScriptScenario(cmd, function () {
                runBasicSiteDeploymentScriptScenario(cmd, done);
            });
        });

        test('generate basic aspWebSite deployment script (--aspWebSite -r)', function (done) {
            var cmd = ('node cli.js site deploymentscript --aspWebSite -r ' + testDir).split(' ');

            runBasicSiteDeploymentScriptScenario(cmd, done);
        });

        test('using exclusion flags together should fail (--aspWebSite --python ...)', function (done) {
            var cmd = ('node cli.js site deploymentscript --aspWebSite --python -r ' + testDir).split(' ');

            runErrorScenario(cmd, 'specify only one of these flags', done);
        });

        test('using exclusion flags together should fail (--node --php ...)', function (done) {
            var cmd = ('node cli.js site deploymentscript --node --php -r ' + testDir).split(' ');

            runErrorScenario(cmd, 'specify only one of these flags', done);
        });

        test('using exclusion flags together should fail (--aspWAP . --basic ...)', function (done) {
            var cmd = ('node cli.js site deploymentscript --aspWAP . --basic -r ' + testDir).split(' ');

            runErrorScenario(cmd, 'specify only one of these flags', done);
        });

        test('using exclusion flags together should fail (--basic --php ...)', function (done) {
            var cmd = ('node cli.js site deploymentscript --basic --php -r ' + testDir).split(' ');

            runErrorScenario(cmd, 'specify only one of these flags', done);
        });

        test('--aspWAP requires project file path argument', function (done) {
            var cmd = ('node cli.js site deploymentscript -r ' + testDir + ' --aspWAP').split(' ');

            runErrorScenario(cmd, 'argument missing', done);
        });

        test('--node requires directory to contain server.js file (--node)', function (done) {
            var cmd = ('node cli.js site deploymentscript --node -r ' + testDir).split(' ');

            runErrorScenario(cmd, 'Missing server.js/app.js file', done);
        });

        test('generate node deployment script (--node -r)', function (done) {
            var cmd = ('node cli.js site deploymentscript --node -r ' + testDir).split(' ');

            runNodeSiteDeploymentScriptScenario(cmd, done);
        });
    });
});

function runBasicSiteDeploymentScriptScenario(cmd, callback) {
    runSiteDeploymentScriptScenario(
        cmd,
        ['Generating deployment script for Web Site', 'Generated deployment script (deploy.cmd and .deployment)'],
        ['echo Handling Basic Web Site deployment.'],
        'deploy.cmd',
        callback);
}

function runNodeSiteDeploymentScriptScenario(cmd, callback) {
    generateServerJsFile();

    runSiteDeploymentScriptScenario(
        cmd,
        ['Generating deployment script for node', 'Generated deployment script (deploy.cmd and .deployment)'],
        ['echo Handling node.js deployment.'],
        'deploy.cmd',
        callback);
}

function runSiteDeploymentScriptScenario(cmd, outputContains, scriptContains, scriptFileName, callback) {
    runCommand(cmd, function (result, e) {
        if (e) {
            callback(e);
            return;
        }

        result.exitStatus.should.equal(0, 'Received an error status exit code');

        for (var i = 0; i < outputContains.length; i++) {
            result.text.should.include(outputContains[i]);
        }

        var deployCmdContent = getFileContent(scriptFileName);
        for (var i = 0; i < scriptContains.length; i++) {
            deployCmdContent.should.include(scriptContains[i]);
        }

        var deploymentFileContent = getFileContent('.deployment');
        deploymentFileContent.should.include(scriptFileName);

        callback();
    });
}

function runErrorScenario(cmd, errorText, callback) {
    runCommand(cmd, function (result, e) {
        if (e) {
            callback(e);
            return;
        }

        result.exitStatus.should.equal(1, 'Received success status exit code');
        result.errorText.should.include(errorText);
        callback();
    });
}

function runCommand(cmd, callback) {
    capture(function () {
        cli.parse(cmd);
    }, function (result) {
        if (result.error) {
            // To avoid calling done twice
            return;
        }

        try {
            console.log('\n' + result.text);
            console.log(result.errorText);
            callback(result);
        }
        catch (e) {
            callback(result, e);
        }
    });
}

function generateServerJsFile() {
    fs.writeFileSync(pathUtil.join(testDir, 'server.js'), '//do nothing', 'utf8');
}

function getFileContent(path) {
    path = pathUtil.join(testDir, path);
    fs.existsSync(path).should.equal(true, "File doesn't exist: " + path);
    return fs.readFileSync(path, 'utf8');
}

function removePath(path) {
    var stat = tryGetFileStat(path);
    if (stat) {
        if (!stat.isDirectory()) {
            tryRemoveFile(path);
        }
        else {
            var files = fs.readdirSync(path);
            for (var index in files) {
                var file = files[index];
                var filePath = pathUtil.join(path, file);
                removePath(filePath);
            }

            tryRemoveDir(path);
        }
    }
}

function tryGetFileStat(path) {
    try {
        return fs.statSync(path);
    }
    catch (e) {
        if (e.errno == 34) {
            // Return null if path doesn't exist
            return null;
        }

        throw e;
    }
}

function tryRemoveFile(path) {
    try {
        fs.unlinkSync(path);
    }
    catch (e) {
        console.log(e);
    }
}

function tryRemoveDir(path) {
    try {
        fs.rmdirSync(path);
    }
    catch (e) {
    }
}

function ensurePathExists(path) {
    if (!fs.existsSync(path)) {
        ensurePathExists(pathUtil.dirname(path));
        fs.mkdirSync(path);
    }
}
<|endoftext|>


<commit_before>/**
*  Service that provides functionalities to edit nodes in a topology.
*/
define(function (require) {
  'use strict';

  var angular = require('angular');
  var modules = require('modules');

  modules.get('a4c-topology-editor').factory('topoEditSubstitution', [ 'topologyServices', 'suggestionServices', '$state',
    function(topologyServices, suggestionServices, $state) {
      var TopologyEditorMixin = function(scope) {
        this.scope = scope;
      };

      TopologyEditorMixin.prototype = {
        constructor: TopologyEditorMixin,
        /** Init method is called when the controller is ready. */
        init: function() {},

        refresh: function() {
          // about substitution
          if (this.scope.topology.topology.substitutionMapping && this.scope.topology.topology.substitutionMapping.substitutionType) {
            this.scope.substitutionType = this.scope.topology.topology.substitutionMapping.substitutionType.elementId;
            this.scope.substitutionVersion = this.scope.topology.topology.substitutionMapping.substitutionType.archiveVersion;
          } else {
            this.scope.substitutionType = undefined;
            this.scope.substitutionVersion = undefined;
          }
        },

        selectType: function(substitutionType) {
          var self = this;
          if (!this.scope.topology.topology.substitutionMapping || this.scope.topology.topology.substitutionMapping.substitutionType !== substitutionType) {
            this.scope.execute({
              type: 'org.alien4cloud.tosca.editor.operations.substitution.AddSubstitutionTypeOperation',
              topologyId: self.scope.topology.topology.id,
              elementId: substitutionType
            }, function(result) {
              if (!result.error) {
                self.scope.refreshTopology(result.data);
              }
            });
          }
        },

        remove: function() {
          var self = this;
          this.scope.execute({
            type: 'org.alien4cloud.tosca.editor.operations.substitution.RemoveSubstitutionTypeOperation',
            topologyId: self.scope.topology.topology.id
          }, function(result) {
            if (!result.error) {
              self.scope.refreshTopology(result.data);
            }
          });
        },

        isTypeInDependencies: function(nodeType) {
          for (var i=0; i< this.scope.topology.topology.dependencies.length; i++) {
            if (this.scope.topology.topology.dependencies[i].name === nodeType.archiveName) {
              return true;
            }
          }
          return false;
        },

        exposeCapability: function(capabilityId) {
          var self = this;
          if (this.isCapabilityExposed(capabilityId)) {
            return;
          }
          this.scope.execute({
            type: 'org.alien4cloud.tosca.editor.operations.substitution.AddCapabilitySubstitutionTypeOperation',
            topologyId: self.scope.topology.topology.id,
            nodeTemplateName: self.scope.selectedNodeTemplate.name,
            substitutionCapabilityId: capabilityId,
            capabilityId: capabilityId
          }, function(result) {
            if (!result.error) {
              self.scope.refreshTopology(result.data);
            }
          });
        },

        isCapabilityExposed: function(capabilityId) {
          var instance = this;
          var result = false;
          angular.forEach(instance.scope.topology.topology.substitutionMapping.capabilities, function(value) {
            if (value.nodeTemplateName === instance.scope.selectedNodeTemplate.name && value.targetId === capabilityId) {
              result = true;
            }
          });
          return result;
        },

        updateCababilityKey: function(oldKey, newKey) {
          var self = this;
          this.scope.execute({
            type: 'org.alien4cloud.tosca.editor.operations.substitution.UpdateCapabilitySubstitutionTypeOperation',
            topologyId: self.scope.topology.topology.id,
            substitutionCapabilityId: oldKey,
            newCapabilityId: newKey
          }, function(result) {
            if (!result.error) {
              self.scope.refreshTopology(result.data);
            }
          });
        },

        removeCabability: function(key) {
          var self = this;
          this.scope.execute({
            type: 'org.alien4cloud.tosca.editor.operations.substitution.RemoveCapabilitySubstitutionTypeOperation',
            topologyId: self.scope.topology.topology.id,
            substitutionCapabilityId: key
          }, function(result) {
            if (!result.error) {
              self.scope.refreshTopology(result.data);
            }
          });
        },

        exposeRequirement: function(requirementId) {
          var self = this;
          if (this.isRequirementExposed(requirementId)) {
            return;
          }
          this.scope.execute({
            type: 'org.alien4cloud.tosca.editor.operations.substitution.AddRequirementSubstitutionTypeOperation',
            topologyId: self.scope.topology.topology.id,
            nodeTemplateName: self.scope.selectedNodeTemplate.name,
            substitutionRequirementId: requirementId,
            requirementId: requirementId
          }, function(result) {
            if (!result.error) {
              self.scope.refreshTopology(result.data);
            }
          });
        },

        isRequirementExposed: function(requirementId) {
          var instance = this;
          var result = false;
          angular.forEach(instance.scope.topology.topology.substitutionMapping.requirements, function(value) {
            if (value.nodeTemplateName === instance.scope.selectedNodeTemplate.name && value.targetId === requirementId) {
              result = true;
            }
          });
          return result;
        },

        updateRequirementKey: function(oldKey, newKey) {
          var self = this;
          this.scope.execute({
            type: 'org.alien4cloud.tosca.editor.operations.substitution.UpdateRequirementSubstitutionTypeOperation',
            topologyId: self.scope.topology.topology.id,
            substitutionRequirementId: oldKey,
            newRequirementId: newKey
          }, function(result) {
            if (!result.error) {
              self.scope.refreshTopology(result.data);
            }
          });
        },

        removeRequirement: function(key) {
          var self = this;
          this.scope.execute({
            type: 'org.alien4cloud.tosca.editor.operations.substitution.RemoveRequirementSubstitutionTypeOperation',
            topologyId: self.scope.topology.topology.id,
            substitutionRequirementId: key
          }, function(result) {
            if (!result.error) {
              self.scope.refreshTopology(result.data);
            }
          });
        },

        displayEmbededTopology: function(topologyId) {
          topologyServices.getTopologyVersion({
            topologyId: topologyId
          }, {}, function(result) {
            if (!result.error) {
              $state.go('topologytemplates.detail.topology.editor', {
                id: result.data.topologyTemplateId,
                version: result.data.version
              });
            }
          });
        }
      };

      return function(scope) {
        var instance = new TopologyEditorMixin(scope);
        scope.substitution = instance;
        scope.substitution.getTypeSuggestions = {
          get: suggestionServices.nodetypeSuggestions,
          waitBeforeRequest: 0, // TODO this seems unused...
          minLength: 2
        };
      };
    }
  ]); // modules
}); // define
<commit_msg>And fix the ui for substitution to remove useless topology id.<commit_after>/**
*  Service that provides functionalities to edit nodes in a topology.
*/
define(function (require) {
  'use strict';

  var angular = require('angular');
  var modules = require('modules');

  modules.get('a4c-topology-editor').factory('topoEditSubstitution', [ 'topologyServices', 'suggestionServices', '$state',
    function(topologyServices, suggestionServices, $state) {
      var TopologyEditorMixin = function(scope) {
        this.scope = scope;
      };

      TopologyEditorMixin.prototype = {
        constructor: TopologyEditorMixin,
        /** Init method is called when the controller is ready. */
        init: function() {},

        refresh: function() {
          // about substitution
          if (this.scope.topology.topology.substitutionMapping && this.scope.topology.topology.substitutionMapping.substitutionType) {
            this.scope.substitutionType = this.scope.topology.topology.substitutionMapping.substitutionType.elementId;
            this.scope.substitutionVersion = this.scope.topology.topology.substitutionMapping.substitutionType.archiveVersion;
          } else {
            this.scope.substitutionType = undefined;
            this.scope.substitutionVersion = undefined;
          }
        },

        selectType: function(substitutionType) {
          var self = this;
          if (!this.scope.topology.topology.substitutionMapping || this.scope.topology.topology.substitutionMapping.substitutionType !== substitutionType) {
            this.scope.execute({
              type: 'org.alien4cloud.tosca.editor.operations.substitution.AddSubstitutionTypeOperation',
              elementId: substitutionType
            }, function(result) {
              if (!result.error) {
                self.scope.refreshTopology(result.data);
              }
            });
          }
        },

        remove: function() {
          var self = this;
          this.scope.execute({
            type: 'org.alien4cloud.tosca.editor.operations.substitution.RemoveSubstitutionTypeOperation'
          }, function(result) {
            if (!result.error) {
              self.scope.refreshTopology(result.data);
            }
          });
        },

        isTypeInDependencies: function(nodeType) {
          for (var i=0; i< this.scope.topology.topology.dependencies.length; i++) {
            if (this.scope.topology.topology.dependencies[i].name === nodeType.archiveName) {
              return true;
            }
          }
          return false;
        },

        exposeCapability: function(capabilityId) {
          var self = this;
          if (this.isCapabilityExposed(capabilityId)) {
            return;
          }
          this.scope.execute({
            type: 'org.alien4cloud.tosca.editor.operations.substitution.AddCapabilitySubstitutionTypeOperation',
            nodeTemplateName: self.scope.selectedNodeTemplate.name,
            substitutionCapabilityId: capabilityId,
            capabilityId: capabilityId
          }, function(result) {
            if (!result.error) {
              self.scope.refreshTopology(result.data);
            }
          });
        },

        isCapabilityExposed: function(capabilityId) {
          var instance = this;
          var result = false;
          angular.forEach(instance.scope.topology.topology.substitutionMapping.capabilities, function(value) {
            if (value.nodeTemplateName === instance.scope.selectedNodeTemplate.name && value.targetId === capabilityId) {
              result = true;
            }
          });
          return result;
        },

        updateCababilityKey: function(oldKey, newKey) {
          var self = this;
          this.scope.execute({
            type: 'org.alien4cloud.tosca.editor.operations.substitution.UpdateCapabilitySubstitutionTypeOperation',
            substitutionCapabilityId: oldKey,
            newCapabilityId: newKey
          }, function(result) {
            if (!result.error) {
              self.scope.refreshTopology(result.data);
            }
          });
        },

        removeCabability: function(key) {
          var self = this;
          this.scope.execute({
            type: 'org.alien4cloud.tosca.editor.operations.substitution.RemoveCapabilitySubstitutionTypeOperation',
            substitutionCapabilityId: key
          }, function(result) {
            if (!result.error) {
              self.scope.refreshTopology(result.data);
            }
          });
        },

        exposeRequirement: function(requirementId) {
          var self = this;
          if (this.isRequirementExposed(requirementId)) {
            return;
          }
          this.scope.execute({
            type: 'org.alien4cloud.tosca.editor.operations.substitution.AddRequirementSubstitutionTypeOperation',
            nodeTemplateName: self.scope.selectedNodeTemplate.name,
            substitutionRequirementId: requirementId,
            requirementId: requirementId
          }, function(result) {
            if (!result.error) {
              self.scope.refreshTopology(result.data);
            }
          });
        },

        isRequirementExposed: function(requirementId) {
          var instance = this;
          var result = false;
          angular.forEach(instance.scope.topology.topology.substitutionMapping.requirements, function(value) {
            if (value.nodeTemplateName === instance.scope.selectedNodeTemplate.name && value.targetId === requirementId) {
              result = true;
            }
          });
          return result;
        },

        updateRequirementKey: function(oldKey, newKey) {
          var self = this;
          this.scope.execute({
            type: 'org.alien4cloud.tosca.editor.operations.substitution.UpdateRequirementSubstitutionTypeOperation',
            substitutionRequirementId: oldKey,
            newRequirementId: newKey
          }, function(result) {
            if (!result.error) {
              self.scope.refreshTopology(result.data);
            }
          });
        },

        removeRequirement: function(key) {
          var self = this;
          this.scope.execute({
            type: 'org.alien4cloud.tosca.editor.operations.substitution.RemoveRequirementSubstitutionTypeOperation',
            substitutionRequirementId: key
          }, function(result) {
            if (!result.error) {
              self.scope.refreshTopology(result.data);
            }
          });
        },

        displayEmbededTopology: function(topologyId) {
          topologyServices.getTopologyVersion({
            topologyId: topologyId
          }, {}, function(result) {
            if (!result.error) {
              $state.go('topologytemplates.detail.topology.editor', {
                id: result.data.topologyTemplateId,
                version: result.data.version
              });
            }
          });
        }
      };

      return function(scope) {
        var instance = new TopologyEditorMixin(scope);
        scope.substitution = instance;
        scope.substitution.getTypeSuggestions = {
          get: suggestionServices.nodetypeSuggestions,
          waitBeforeRequest: 0, // TODO this seems unused...
          minLength: 2
        };
      };
    }
  ]); // modules
}); // define
<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require_tree .
//= reviews
//= restaurants
//= active_admin
<commit_msg>Added jquery.formatDateTime plugin<commit_after>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require_tree .
//= reviews
//= restaurants
//= active_admin
//= jquery.formatDateTime
<|endoftext|>


<commit_before>var termops = require('./util/termops');
var token = require('./util/token');

// # phrasematch
//
// @param {Object} source a Geocoder datasource
// @param {Array} query a list of terms composing the query to Carmen
// @param {Function} callback called with `(err, features, result, stats)`
module.exports = function phrasematch(source, query, callback) {
    var tokenized = termops.tokenize(token.replaceToken(source._geocoder.token_replacer, query));
    var getter = source.getGeocoderData.bind(source);

    // Get all subquery permutations from the query
    var subqueries = termops.permutations(tokenized);

    // Include housenum tokenized permutations if source has addresses
    if (source._geocoder.geocoder_address) {
        var numTokenized = termops.numTokenize(tokenized);
        for (var i = 0; i < numTokenized.length; i++) {
            subqueries = subqueries.concat(termops.permutations(numTokenized[i]));
        }
    }

    subqueries = termops.uniqPermutations(subqueries);

    var loadStats = [];
    for (var l = 0; l < subqueries.length; l++) {
        var phrase = termops.encodePhrase(subqueries[l], subqueries[l].ender);
        subqueries[l].phrase = phrase;
        loadStats.push(phrase);
    }

    source._geocoder.loadall(getter, 'stat', loadStats, function(err) {
        if (err) return callback(err);
        var toLoad = [];
        for (var l = 0; l < loadStats.length; l++) {
            if (source._geocoder.exists('stat', loadStats[l])) {
                toLoad.push(loadStats[l]);
            }
        }
        source._geocoder.loadall(getter, 'grid', loadStats, loaded);
    });

    function loaded(err) {
        if (err) return callback(err);

        var results = [];

        var l = subqueries.length;
        while (l--) {
            if (!source._geocoder.exists('grid', subqueries[l].phrase)) continue;
            // Augment permutations with matched grids,
            // index position and weight relative to input query.
            subqueries[l].cache = source._geocoder;
            subqueries[l].idx = source._geocoder.idx;
            subqueries[l].zoom = source._geocoder.zoom;
            subqueries[l].nmask = 1 << source._geocoder.ndx;
            subqueries[l].weight = subqueries[l].length / tokenized.length;
            results.push(subqueries[l]);
        }

        return callback(null, results);
    }
};

<commit_msg>Actually use all that optimization...<commit_after>var termops = require('./util/termops');
var token = require('./util/token');

// # phrasematch
//
// @param {Object} source a Geocoder datasource
// @param {Array} query a list of terms composing the query to Carmen
// @param {Function} callback called with `(err, features, result, stats)`
module.exports = function phrasematch(source, query, callback) {
    var tokenized = termops.tokenize(token.replaceToken(source._geocoder.token_replacer, query));
    var getter = source.getGeocoderData.bind(source);

    // Get all subquery permutations from the query
    var subqueries = termops.permutations(tokenized);

    // Include housenum tokenized permutations if source has addresses
    if (source._geocoder.geocoder_address) {
        var numTokenized = termops.numTokenize(tokenized);
        for (var i = 0; i < numTokenized.length; i++) {
            subqueries = subqueries.concat(termops.permutations(numTokenized[i]));
        }
    }

    subqueries = termops.uniqPermutations(subqueries);

    var loadStats = [];
    for (var l = 0; l < subqueries.length; l++) {
        var phrase = termops.encodePhrase(subqueries[l], subqueries[l].ender);
        subqueries[l].phrase = phrase;
        loadStats.push(phrase);
    }

    source._geocoder.loadall(getter, 'stat', loadStats, function(err) {
        if (err) return callback(err);
        var toLoad = [];
        for (var l = 0; l < loadStats.length; l++) {
            if (source._geocoder.exists('stat', loadStats[l])) {
                toLoad.push(loadStats[l]);
            }
        }
        source._geocoder.loadall(getter, 'grid', toLoad, loaded);
    });

    function loaded(err) {
        if (err) return callback(err);

        var results = [];

        var l = subqueries.length;
        while (l--) {
            if (!source._geocoder.exists('grid', subqueries[l].phrase)) continue;
            // Augment permutations with matched grids,
            // index position and weight relative to input query.
            subqueries[l].cache = source._geocoder;
            subqueries[l].idx = source._geocoder.idx;
            subqueries[l].zoom = source._geocoder.zoom;
            subqueries[l].nmask = 1 << source._geocoder.ndx;
            subqueries[l].weight = subqueries[l].length / tokenized.length;
            results.push(subqueries[l]);
        }

        return callback(null, results);
    }
};

<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
$(document).ready(function(){
  $('.nav-list').hide();

  $('.menu-toggle').mouseover(function(event) {
    event.preventDefault();
    $('.nav-list').show();
  });

  $('.menu-toggle').mouseout(function(event){
    $('.nav-list').hide();
  })

  $('.vote').on('click', function(){
    event.preventDefault();
    var $target = $(event.target)
    console.log("You've made it this far kid");

    $.ajax({
      url: $target.attr("action"),
      type: "POST"
    })
    .done(function(){
      console.log("You've made it this far kid");
    })

  })
})
<commit_msg>adding ajax for voting<commit_after>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
$(document).ready(function(){
  $('.nav-list').hide();

  $('.menu-toggle').mouseover(function(event) {
    event.preventDefault();
    $('.nav-list').show();
  });

  $('.menu-toggle').mouseout(function(event){
    $('.nav-list').hide();
  })

  $('.vote').on('click', function(){
    event.preventDefault();
    var $target = $(event.target)
    console.log("You've made it this far kid");

    $.ajax({
      url: $target.attr("method"),
      type: "POST"
    })
    .done(function(){
      console.log("You've made it this far kid");
    })

  })
})
<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
<commit_msg>Removed require turbolinks<commit_after>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require_tree .
<|endoftext|>


<commit_before>Rye.implement(function(exports){

    var util = Rye.require('util')
      , _slice  = Array.prototype.slice
      , _filter = Array.prototype.filter

    var selectorRE = /^([.#]?)([\w\-]+)$/
      , selectorType = {
            '.': 'getElementsByClassName'
          , '#': 'getElementById'
          , '' : 'getElementsByTagName'
          , '_': 'querySelectorAll'
        }
      , dummyDiv = document.createElement('div')

    // Creates a new Rye instance applying a filter if necessary
    function create(elements, selector) {
        return selector === undefined ? new Rye(elements) : new Rye(elements).filter(selector)
    }

    function matches(element, selector) {
        var matchesSelector, match
        if (!element || element.nodeType !== 1) {
            return false
        }
        matchesSelector = util.prefix('matchesSelector', dummyDiv)
        if (matchesSelector) {
            return matchesSelector.call(element, selector)
        }
        // fall back to performing a selector:
        if (!element.parentNode) {
            dummyDiv.appendChild(element)
        }
        match = ~qsa(selector, element.parentNode).indexOf(element)
        if (element.parentNode === dummyDiv) {
            dummyDiv.removeChild(element)
        }
        return match
    }

    function qsa (selector, context) {
        var method, result

        context || (context = document)
        // http://jsperf.com/getelementbyid-vs-queryselector/11
        if (!selector.match(selectorRE) || (RegExp.$1 === '#' && context !== document)) {
            method = selectorType._
        } else {
            method = selectorType[RegExp.$1]
            selector = RegExp.$2
        }

        result = context[method](selector)

        if (result instanceof NodeList){
            return _slice.call(result)
        }

        if (util.isElement(result)){
            return [result]
        }

        return []
    }

    exports.qsa = qsa

    exports.find = function find (selector){
        var elements
        if (this.length === 1) {
            elements = qsa(selector, this.elements[0])
        } else {
            elements = this.elements.map(function(){
                return qsa(selector, this)
            })
        }
        return create(elements)
    }

    exports.filter = function(selector){
        return create(_filter.call(this.elements, function(element){
            return matches(element, selector)
        }))
    }

    exports.is = function(selector){
        return this.length > 0 && this.filter(selector).length > 0
    }

    exports.add = function(selector, context){
        var elements = selector
        if (typeof selector === 'string') {
            elements = new Rye(selector, context).elements
        }
        return this.concat(elements)
    }

    // Walks the DOM tree using `method`, returns
    // when an element node is found
    function getClosestNode(element, method){
        do {
            element = element[method]
        } while (element && !util.isElement(element))
        return element
    }

    // Extract a list with the provided property for each value.
    // This works like underscore's pluck, with the added
    // getClosestNode() method to avoid picking up non-html nodes.
    exports.pluckNode = function(property){
        return this.map(function(element){
            return getClosestNode(element, property)
        })
    }

    exports.pluck = function(property){
        return util.pluck(this.elements, property)
    }

    exports.next = function(){
        return create(this.pluckNode('nextSibling'))
    }

    exports.prev = function(){
        return create(this.pluckNode('previousSibling'))
    }

    exports.parent = function(selector){
        return create(util.unique(this.pluckNode('parentNode')), selector)
    }

    exports.first = function(){
        return create(this.get(0))
    }

    exports.last = function(){
        return create(this.get(-1))
    }

    exports.siblings = function(selector){
        var siblings = []
        this.forEach(function(element){
            _slice.call(element.parentNode.childNodes).forEach(function(child){
                if (util.isElement(child) && child !== element){
                    siblings.push(child)
                }
            })
        })
        return create(util.unique(siblings), selector)
    }

    exports.parent = function(selector){
        return create(util.unique(this.pluck('parentNode')), selector)
    }

    exports.children = function(selector){
        return create(this.map(function(){
            return _slice.call(this.children)
        }), selector)
    }

})
<commit_msg>closest<commit_after>Rye.implement(function(exports){

    var util = Rye.require('util')
      , _slice  = Array.prototype.slice
      , _filter = Array.prototype.filter

    var selectorRE = /^([.#]?)([\w\-]+)$/
      , selectorType = {
            '.': 'getElementsByClassName'
          , '#': 'getElementById'
          , '' : 'getElementsByTagName'
          , '_': 'querySelectorAll'
        }
      , dummyDiv = document.createElement('div')

    // Creates a new Rye instance applying a filter if necessary
    function create(elements, selector) {
        return selector === undefined ? new Rye(elements) : new Rye(elements).filter(selector)
    }

    function matches(element, selector) {
        var matchesSelector, match
        if (!element || element.nodeType !== 1) {
            return false
        }
        matchesSelector = util.prefix('matchesSelector', dummyDiv)
        if (matchesSelector) {
            return matchesSelector.call(element, selector)
        }
        // fall back to performing a selector:
        if (!element.parentNode) {
            dummyDiv.appendChild(element)
        }
        match = ~qsa(selector, element.parentNode).indexOf(element)
        if (element.parentNode === dummyDiv) {
            dummyDiv.removeChild(element)
        }
        return match
    }

    function qsa (selector, context) {
        var method, result

        context || (context = document)
        // http://jsperf.com/getelementbyid-vs-queryselector/11
        if (!selector.match(selectorRE) || (RegExp.$1 === '#' && context !== document)) {
            method = selectorType._
        } else {
            method = selectorType[RegExp.$1]
            selector = RegExp.$2
        }

        result = context[method](selector)

        if (result instanceof NodeList){
            return _slice.call(result)
        }

        if (util.isElement(result)){
            return [result]
        }

        return []
    }

    exports.qsa = qsa

    exports.find = function find (selector){
        var elements
        if (this.length === 1) {
            elements = qsa(selector, this.elements[0])
        } else {
            elements = this.elements.map(function(){
                return qsa(selector, this)
            })
        }
        return create(elements)
    }

    exports.filter = function(selector){
        return create(_filter.call(this.elements, function(element){
            return matches(element, selector)
        }))
    }

    exports.is = function(selector){
        return this.length > 0 && this.filter(selector).length > 0
    }

    exports.add = function(selector, context){
        var elements = selector
        if (typeof selector === 'string') {
            elements = new Rye(selector, context).elements
        }
        return this.concat(elements)
    }

    // Walks the DOM tree using `method`, returns
    // when an element node is found
    function getClosestNode(element, method){
        do {
            element = element[method]
        } while (element && !util.isElement(element))
        return element
    }

    // Extract a list with the provided property for each value.
    // This works like underscore's pluck, with the added
    // getClosestNode() method to avoid picking up non-html nodes.
    exports.pluckNode = function(property){
        return this.map(function(element){
            return getClosestNode(element, property)
        })
    }

    exports.pluck = function(property){
        return util.pluck(this.elements, property)
    }

    exports.next = function(){
        return create(this.pluckNode('nextSibling'))
    }

    exports.prev = function(){
        return create(this.pluckNode('previousSibling'))
    }

    exports.parent = function(selector){
        return create(util.unique(this.pluckNode('parentNode')), selector)
    }

    exports.first = function(){
        return create(this.get(0))
    }

    exports.last = function(){
        return create(this.get(-1))
    }

    exports.siblings = function(selector){
        var siblings = []
        this.forEach(function(element){
            _slice.call(element.parentNode.childNodes).forEach(function(child){
                if (util.isElement(child) && child !== element){
                    siblings.push(child)
                }
            })
        })
        return create(util.unique(siblings), selector)
    }

    exports.parent = function(selector){
        return create(util.unique(this.pluck('parentNode')), selector)
    }

    exports.closest = function(selector){
        var elements = []
        this.forEach(function(element){
            do {
                if (matches(element, selector)) {
                    elements.push(element)
                    break
                }
                element = element.parentNode
            } while (element)
        })

        if (elements.length > 1) {
            elements = util.unique(elements)
        }
        return create(elements)
    }

    exports.children = function(selector){
        return create(this.map(function(){
            return _slice.call(this.children)
        }), selector)
    }

})
<|endoftext|>


<commit_before>// Load in dependencies
var exec = require('child_process').exec;
var path = require('path');
var expect = require('chai').expect;
var sinon = require('sinon');
var shell = require('shelljs');
var wrench = require('wrench');
var gitRelease = require('../');
var fixtureUtils = require('./utils/fixtures');

describe('Committing', function () {
  describe('in a git folder', function () {
    fixtureUtils.bundle.mkdir('commit_test');
    fixtureUtils.bundle.exec('git init');
    fixtureUtils.bundle.exec('touch a');
    fixtureUtils.bundle.exec('git add -A');
    fixtureUtils.bundle.exec('git commit -m "Initial commit =D"');

    before(function commit (done) {
      this.inBundle(function () {
        gitRelease.commit({
          version: '0.1.0',
          message: 'Release 0.1.0',
          description: null
        }, done);
      });
    });

    it('adds a git tag', function (done) {
      this.inBundle(function () {
        exec('git tag', function (err, stdout, stderr) {
          if (err) {
            return done(err);
          }
          expect(stdout).to.equal('0.1.0\n');
          done();
        });
      });
    });

    it('adds a git commit', function (done) {
      this.inBundle(function () {
        exec('git log --format=oneline -n 1', function (err, stdout, stderr) {
          if (err) {
            return done(err);
          }
          expect(stdout).to.match(/\w{32} Release 0.1.0\n/);
          done();
        });
      });
    });
  });
});

describe('Publishing', function () {
  describe('in a git folder', function () {
    fixtureUtils.bundle.mkdir('publish_test');
    fixtureUtils.bundle.exec('git init');
    fixtureUtils.bundle.exec('touch a');
    fixtureUtils.bundle.exec('git add -A');
    fixtureUtils.bundle.exec('git commit -m "Initial commit =D"');

    // DEV: Ideally we would override `child_process.exec` but it causes complications with `shelljs`
    before(function stubExec () {
      this.execStub = sinon.stub(shell, 'exec');
    });
    after(function restoreExec () {
      this.execStub.restore();
    });

    before(function publish (done) {
      this.inBundle(function () {
        gitRelease.publish({
          version: '0.1.0',
          message: 'Release 0.1.0',
          description: null
        }, done);
      });
    });

    it('publishes the commit', function () {
      expect(this.execStub.args[0]).to.deep.equal(['git push']);
    });
    it('publishes the tags', function () {
      expect(this.execStub.args[1]).to.deep.equal(['git push --tags']);
    });
  });
});
<commit_msg>Updated tests to match<commit_after>// Load in dependencies
var exec = require('child_process').exec;
var path = require('path');
var expect = require('chai').expect;
var sinon = require('sinon');
var shell = require('shelljs');
var wrench = require('wrench');
var gitRelease = require('../');
var fixtureUtils = require('./utils/fixtures');

describe('Publishing', function () {
  describe('in a git folder', function () {
    fixtureUtils.bundle.mkdir('publish_test');
    fixtureUtils.bundle.exec('git init');
    fixtureUtils.bundle.exec('touch a');
    fixtureUtils.bundle.exec('git add -A');
    fixtureUtils.bundle.exec('git commit -m "Initial commit =D"');

    // DEV: Ideally we would override `child_process.exec` but it causes complications with `shelljs`
    before(function stubExec () {
      this.execStub = sinon.stub(shell, 'exec');
    });
    after(function restoreExec () {
      this.execStub.restore();
    });

    before(function publish (done) {
      this.inBundle(function () {
        gitRelease.publish({
          version: '1.0.0',
          message: 'Release 1.0.0',
          description: null
        }, done);
      });
    });

    it('updates and publishes the minor variable branch', function () {
      expect(this.execStub.args[0]).to.deep.equal(['git checkout -B 1.x.x']);
      expect(this.execStub.args[1]).to.deep.equal(['git push']);
      expect(this.execStub.args[2]).to.deep.equal(['git checkout -']);
    });

    it('publishes the tags', function () {
      expect(this.execStub.args[3]).to.deep.equal(['git checkout -B 1.0.x']);
      expect(this.execStub.args[4]).to.deep.equal(['git push']);
      expect(this.execStub.args[5]).to.deep.equal(['git checkout -']);
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash')
  , moment = require('moment')
  , times = require('../times');

function init(ctx) {
  var translate = ctx.language.translate;
  var iob = require('./iob')(ctx);

  var cob = {
    name: 'cob'
    , label: 'Carbs-on-Board'
    , pluginType: 'pill-minor'
  };

  cob.RECENCY_THRESHOLD = times.mins(30).msecs;

  cob.setProperties = function setProperties(sbx) {
    sbx.offerProperty('cob', function setCOB ( ) {
      return cob.cobTotal(sbx.data.treatments, sbx.data.devicestatus, sbx.data.profile, sbx.time);
    });
  };

  cob.cobTotal = function cobTotal(treatments, devicestatus, profile, time, spec_profile) {

    if (!profile || !profile.hasData()) {
      console.warn('For the COB plugin to function you need a treatment profile');
      return {};
    }

    if (!profile.getSensitivity(time, spec_profile) || !profile.getCarbRatio(time, spec_profile)) {
      console.warn('For the COB plugin to function your treatment profile must have both sens and carbratio fields');
      return {};
    }

    if (typeof time === 'undefined') {
      time = Date.now();
    } else if (time && time.getTime) {
      time = time.getTime();
    }

    var devicestatusCOB = cob.lastCOBDeviceStatus(devicestatus, time);

    var treatmentCOB = (treatments !== undefined && treatments.length) ? cob.fromTreatments(treatments, devicestatus, profile, time, spec_profile) : {};

    var result = devicestatusCOB;
    if (_.isEmpty(result)) {
      result = treatmentCOB;
      result.source = 'Care Portal';
    } else if (treatmentCOB.cob) {
      result.treatmentCOB = treatmentCOB;
    }

    return addDisplay(result);
  };

  function addDisplay(cob) {
    if (_.isEmpty(cob) || cob.cob === undefined) {
      return {};
    }

    var display = Math.round(cob.cob * 10) / 10;
    return _.merge(cob, {
      display: display
      , displayLine: 'COB: ' + display + 'g'
    });
  }

  cob.lastCOBDeviceStatus = function lastCOBDeviceStatus (devicestatus, time) {

    var futureMills = time + times.mins(5).msecs; //allow for clocks to be a little off
    var recentMills = time - cob.RECENCY_THRESHOLD;

    return _.chain(devicestatus)
      .map(cob.fromDeviceStatus)
      .reject(_.isEmpty)
      .filter(function (cobStatus) {
        return cobStatus.mills <= futureMills && cobStatus.mills >= recentMills;
      })
      .sortBy('mills')
      .last()
      .value();
  };

  cob.fromDeviceStatus = function fromDeviceStatus(devicestatusEntry) {

    var cobObj;
    if (_.get(devicestatusEntry, 'openaps') !== undefined) {
      var suggested = devicestatusEntry.openaps.suggested;
      var enacted = devicestatusEntry.openaps.enacted;

      var lastCOB = 0;
      var lastMoment = null;

      if (suggested && enacted) {
        var suggestedMoment = moment(suggested.timestamp);
        var enactedMoment = moment(enacted.timestamp);
        if (enactedMoment.isAfter(suggestedMoment)) {
          lastCOB = enacted.COB;
          lastMoment = enactedMoment;
        } else {
          lastCOB = suggested.COB;
          lastMoment = suggestedMoment;
        }
      } else if (enacted) {
        lastCOB = enacted.COB;
        lastMoment = moment(enacted.timestamp);
      } else if (suggested) {
        lastCOB = suggested.COB;
        lastMoment = moment(suggested.timestamp);
      }

      if (lastCOB === 0 || !lastMoment) {
        return {};
      }

      return {
        cob: lastCOB
        , source: 'OpenAPS'
        , device: devicestatusEntry.device
        , mills: lastMoment.valueOf( )
      };
    } else if (_.get(devicestatusEntry, 'loop.cob') !== undefined) {
      cobObj = devicestatusEntry.loop.cob;
      return {
        cob: cobObj.cob
        , source: 'Loop'
        , device: devicestatusEntry.device
        , mills: moment(cobObj.timestamp).valueOf( )
      };
    } else {
      return {};
    }
  };

  cob.fromTreatments = function fromTreatments (treatments, devicestatus, profile, time, spec_profile) {
    // TODO: figure out the liverSensRatio that gives the most accurate purple line predictions
    var liverSensRatio = 8;
    var totalCOB = 0;
    var lastCarbs = null;

    var isDecaying = 0;
    var lastDecayedBy = 0;

    _.each(treatments, function eachTreatment(treatment) {
      if (treatment.carbs && treatment.mills < time) {
        lastCarbs = treatment;
        var cCalc = cob.cobCalc(treatment, profile, lastDecayedBy, time, spec_profile);
        var decaysin_hr = (cCalc.decayedBy - time) / 1000 / 60 / 60;
        if (decaysin_hr > -10) {
          // units: BG
          var actStart = iob.calcTotal(treatments, devicestatus, profile, lastDecayedBy, spec_profile).activity;
          var actEnd = iob.calcTotal(treatments, devicestatus, profile, cCalc.decayedBy, spec_profile).activity;
          var avgActivity = (actStart + actEnd) / 2;
          // units:  g     =       BG      *      scalar     /          BG / U                           *     g / U
          var delayedCarbs = ( avgActivity *  liverSensRatio / profile.getSensitivity(treatment.mills, spec_profile) ) * profile.getCarbRatio(treatment.mills, spec_profile);
          var delayMinutes = Math.round(delayedCarbs / profile.getCarbAbsorptionRate(treatment.mills, spec_profile) * 60);
          if (delayMinutes > 0) {
            cCalc.decayedBy.setMinutes(cCalc.decayedBy.getMinutes() + delayMinutes);
            decaysin_hr = (cCalc.decayedBy - time) / 1000 / 60 / 60;
          }
        }

        if (cCalc) {
          lastDecayedBy = cCalc.decayedBy;
        }

        if (decaysin_hr > 0) {
          //console.info('Adding ' + delayMinutes + ' minutes to decay of ' + treatment.carbs + 'g bolus at ' + treatment.mills);
          totalCOB += Math.min(Number(treatment.carbs), decaysin_hr * profile.getCarbAbsorptionRate(treatment.mills, spec_profile));
          //console.log('cob:', Math.min(cCalc.initialCarbs, decaysin_hr * profile.getCarbAbsorptionRate(treatment.mills)),cCalc.initialCarbs,decaysin_hr,profile.getCarbAbsorptionRate(treatment.mills));
          isDecaying = cCalc.isDecaying;
        } else {
          totalCOB = 0;
        }

      }
    });

    var rawCarbImpact = isDecaying * profile.getSensitivity(time, spec_profile) / profile.getCarbRatio(time, spec_profile) * profile.getCarbAbsorptionRate(time, spec_profile) / 60;

    return {
      decayedBy: lastDecayedBy
      , isDecaying: isDecaying
      , carbs_hr: profile.getCarbAbsorptionRate(time, spec_profile)
      , rawCarbImpact: rawCarbImpact
      , cob: totalCOB
      , lastCarbs: lastCarbs
    };
  };

  cob.carbImpact = function carbImpact(rawCarbImpact, insulinImpact) {
    var liverSensRatio = 1.0;
    var liverCarbImpactMax = 0.7;
    var liverCarbImpact = Math.min(liverCarbImpactMax, liverSensRatio * insulinImpact);
    //var liverCarbImpact = liverSensRatio*insulinImpact;
    var netCarbImpact = Math.max(0, rawCarbImpact - liverCarbImpact);
    var totalImpact = netCarbImpact - insulinImpact;
    return {
      netCarbImpact: netCarbImpact,
      totalImpact: totalImpact
    };
  };

  cob.cobCalc = function cobCalc(treatment, profile, lastDecayedBy, time, spec_profile) {

    var delay = 20;
    var isDecaying = 0;
    var initialCarbs;

    if (treatment.carbs) {
      var carbTime = new Date(treatment.mills);

      var carbs_hr = profile.getCarbAbsorptionRate(treatment.mills, spec_profile);
      var carbs_min = carbs_hr / 60;

      var decayedBy = new Date(carbTime);
      var minutesleft = (lastDecayedBy - carbTime) / 1000 / 60;
      decayedBy.setMinutes(decayedBy.getMinutes() + Math.max(delay, minutesleft) + treatment.carbs / carbs_min);
      if (delay > minutesleft) {
        initialCarbs = parseInt(treatment.carbs);
      }
      else {
        initialCarbs = parseInt(treatment.carbs) + minutesleft * carbs_min;
      }
      var startDecay = new Date(carbTime);
      startDecay.setMinutes(carbTime.getMinutes() + delay);
      if (time < lastDecayedBy || time > startDecay) {
        isDecaying = 1;
      }
      else {
        isDecaying = 0;
      }
      return {
        initialCarbs: initialCarbs,
        decayedBy: decayedBy,
        isDecaying: isDecaying,
        carbTime: carbTime
      };
    }
    else {
      return '';
    }
  };

  cob.updateVisualisation = function updateVisualisation(sbx) {

    var prop = sbx.properties.cob;

    if (prop === undefined || prop.cob === undefined) { return; }

    var displayCob = Math.round(prop.cob * 10) / 10;

    var info = null;
    if (prop.treatmentCOB !== undefined) {
      info = [ ];
      info.push({label: translate('Careportal COB'), value: Math.round(prop.treatmentCOB.cob * 10) / 10});

      if (prop.treatmentCOB.lastCarbs) {
        var when = new Date(prop.treatmentCOB.lastCarbs.mills).toLocaleString();
        var amount = prop.treatmentCOB.lastCarbs.carbs + 'g';
        info.push({label: translate('Last Carbs'), value: amount + ' @ ' + when});
      }
    }

    sbx.pluginBase.updatePillText(sbx, {
      value: displayCob + 'g'
      , label: translate('COB')
      , info: info
    });
  };

  function alexaCOBHandler (next, slots, sbx) {
    var preamble = (slots && slots.pwd && slots.pwd.value) ? slots.pwd.value.replace('\'s', '') + ' has' : 'You have';
    var value = 'no';
    if (sbx.properties.cob && sbx.properties.cob.cob !== 0) {
      value = sbx.properties.cob.cob;
    }
    var response = preamble + ' ' + value + ' carbohydrates on board';
    next('Current COB', response);
  }

  cob.alexa = {
    intentHandlers: [{
      intent: 'MetricNow'
      , routableSlot:'metric'
      , slots:['cob', 'carbs on board', 'carbohydrates on board']
      , intentHandler: alexaCOBHandler
    }]
  };

  return cob;

}

module.exports = init;
<commit_msg>show last carbs even if careportal cob is 0<commit_after>'use strict';

var _ = require('lodash')
  , moment = require('moment')
  , times = require('../times');

function init(ctx) {
  var translate = ctx.language.translate;
  var iob = require('./iob')(ctx);

  var cob = {
    name: 'cob'
    , label: 'Carbs-on-Board'
    , pluginType: 'pill-minor'
  };

  cob.RECENCY_THRESHOLD = times.mins(30).msecs;

  cob.setProperties = function setProperties(sbx) {
    sbx.offerProperty('cob', function setCOB ( ) {
      return cob.cobTotal(sbx.data.treatments, sbx.data.devicestatus, sbx.data.profile, sbx.time);
    });
  };

  cob.cobTotal = function cobTotal(treatments, devicestatus, profile, time, spec_profile) {

    if (!profile || !profile.hasData()) {
      console.warn('For the COB plugin to function you need a treatment profile');
      return {};
    }

    if (!profile.getSensitivity(time, spec_profile) || !profile.getCarbRatio(time, spec_profile)) {
      console.warn('For the COB plugin to function your treatment profile must have both sens and carbratio fields');
      return {};
    }

    if (typeof time === 'undefined') {
      time = Date.now();
    } else if (time && time.getTime) {
      time = time.getTime();
    }

    var devicestatusCOB = cob.lastCOBDeviceStatus(devicestatus, time);

    var treatmentCOB = (treatments !== undefined && treatments.length) ? cob.fromTreatments(treatments, devicestatus, profile, time, spec_profile) : {};

    var result = devicestatusCOB;
    if (_.isEmpty(result)) {
      result = treatmentCOB;
      result.source = 'Care Portal';
    } else if (treatmentCOB) {
      result.treatmentCOB = treatmentCOB;
    }

    return addDisplay(result);
  };

  function addDisplay(cob) {
    if (_.isEmpty(cob) || cob.cob === undefined) {
      return {};
    }

    var display = Math.round(cob.cob * 10) / 10;
    return _.merge(cob, {
      display: display
      , displayLine: 'COB: ' + display + 'g'
    });
  }

  cob.lastCOBDeviceStatus = function lastCOBDeviceStatus (devicestatus, time) {

    var futureMills = time + times.mins(5).msecs; //allow for clocks to be a little off
    var recentMills = time - cob.RECENCY_THRESHOLD;

    return _.chain(devicestatus)
      .map(cob.fromDeviceStatus)
      .reject(_.isEmpty)
      .filter(function (cobStatus) {
        return cobStatus.mills <= futureMills && cobStatus.mills >= recentMills;
      })
      .sortBy('mills')
      .last()
      .value();
  };

  cob.fromDeviceStatus = function fromDeviceStatus(devicestatusEntry) {

    var cobObj;
    if (_.get(devicestatusEntry, 'openaps') !== undefined) {
      var suggested = devicestatusEntry.openaps.suggested;
      var enacted = devicestatusEntry.openaps.enacted;

      var lastCOB = 0;
      var lastMoment = null;

      if (suggested && enacted) {
        var suggestedMoment = moment(suggested.timestamp);
        var enactedMoment = moment(enacted.timestamp);
        if (enactedMoment.isAfter(suggestedMoment)) {
          lastCOB = enacted.COB;
          lastMoment = enactedMoment;
        } else {
          lastCOB = suggested.COB;
          lastMoment = suggestedMoment;
        }
      } else if (enacted) {
        lastCOB = enacted.COB;
        lastMoment = moment(enacted.timestamp);
      } else if (suggested) {
        lastCOB = suggested.COB;
        lastMoment = moment(suggested.timestamp);
      }

      if (lastCOB === 0 || !lastMoment) {
        return {};
      }

      return {
        cob: lastCOB
        , source: 'OpenAPS'
        , device: devicestatusEntry.device
        , mills: lastMoment.valueOf( )
      };
    } else if (_.get(devicestatusEntry, 'loop.cob') !== undefined) {
      cobObj = devicestatusEntry.loop.cob;
      return {
        cob: cobObj.cob
        , source: 'Loop'
        , device: devicestatusEntry.device
        , mills: moment(cobObj.timestamp).valueOf( )
      };
    } else {
      return {};
    }
  };

  cob.fromTreatments = function fromTreatments (treatments, devicestatus, profile, time, spec_profile) {
    // TODO: figure out the liverSensRatio that gives the most accurate purple line predictions
    var liverSensRatio = 8;
    var totalCOB = 0;
    var lastCarbs = null;

    var isDecaying = 0;
    var lastDecayedBy = 0;

    _.each(treatments, function eachTreatment(treatment) {
      if (treatment.carbs && treatment.mills < time) {
        lastCarbs = treatment;
        var cCalc = cob.cobCalc(treatment, profile, lastDecayedBy, time, spec_profile);
        var decaysin_hr = (cCalc.decayedBy - time) / 1000 / 60 / 60;
        if (decaysin_hr > -10) {
          // units: BG
          var actStart = iob.calcTotal(treatments, devicestatus, profile, lastDecayedBy, spec_profile).activity;
          var actEnd = iob.calcTotal(treatments, devicestatus, profile, cCalc.decayedBy, spec_profile).activity;
          var avgActivity = (actStart + actEnd) / 2;
          // units:  g     =       BG      *      scalar     /          BG / U                           *     g / U
          var delayedCarbs = ( avgActivity *  liverSensRatio / profile.getSensitivity(treatment.mills, spec_profile) ) * profile.getCarbRatio(treatment.mills, spec_profile);
          var delayMinutes = Math.round(delayedCarbs / profile.getCarbAbsorptionRate(treatment.mills, spec_profile) * 60);
          if (delayMinutes > 0) {
            cCalc.decayedBy.setMinutes(cCalc.decayedBy.getMinutes() + delayMinutes);
            decaysin_hr = (cCalc.decayedBy - time) / 1000 / 60 / 60;
          }
        }

        if (cCalc) {
          lastDecayedBy = cCalc.decayedBy;
        }

        if (decaysin_hr > 0) {
          //console.info('Adding ' + delayMinutes + ' minutes to decay of ' + treatment.carbs + 'g bolus at ' + treatment.mills);
          totalCOB += Math.min(Number(treatment.carbs), decaysin_hr * profile.getCarbAbsorptionRate(treatment.mills, spec_profile));
          //console.log('cob:', Math.min(cCalc.initialCarbs, decaysin_hr * profile.getCarbAbsorptionRate(treatment.mills)),cCalc.initialCarbs,decaysin_hr,profile.getCarbAbsorptionRate(treatment.mills));
          isDecaying = cCalc.isDecaying;
        } else {
          totalCOB = 0;
        }

      }
    });

    var rawCarbImpact = isDecaying * profile.getSensitivity(time, spec_profile) / profile.getCarbRatio(time, spec_profile) * profile.getCarbAbsorptionRate(time, spec_profile) / 60;

    return {
      decayedBy: lastDecayedBy
      , isDecaying: isDecaying
      , carbs_hr: profile.getCarbAbsorptionRate(time, spec_profile)
      , rawCarbImpact: rawCarbImpact
      , cob: totalCOB
      , lastCarbs: lastCarbs
    };
  };

  cob.carbImpact = function carbImpact(rawCarbImpact, insulinImpact) {
    var liverSensRatio = 1.0;
    var liverCarbImpactMax = 0.7;
    var liverCarbImpact = Math.min(liverCarbImpactMax, liverSensRatio * insulinImpact);
    //var liverCarbImpact = liverSensRatio*insulinImpact;
    var netCarbImpact = Math.max(0, rawCarbImpact - liverCarbImpact);
    var totalImpact = netCarbImpact - insulinImpact;
    return {
      netCarbImpact: netCarbImpact,
      totalImpact: totalImpact
    };
  };

  cob.cobCalc = function cobCalc(treatment, profile, lastDecayedBy, time, spec_profile) {

    var delay = 20;
    var isDecaying = 0;
    var initialCarbs;

    if (treatment.carbs) {
      var carbTime = new Date(treatment.mills);

      var carbs_hr = profile.getCarbAbsorptionRate(treatment.mills, spec_profile);
      var carbs_min = carbs_hr / 60;

      var decayedBy = new Date(carbTime);
      var minutesleft = (lastDecayedBy - carbTime) / 1000 / 60;
      decayedBy.setMinutes(decayedBy.getMinutes() + Math.max(delay, minutesleft) + treatment.carbs / carbs_min);
      if (delay > minutesleft) {
        initialCarbs = parseInt(treatment.carbs);
      }
      else {
        initialCarbs = parseInt(treatment.carbs) + minutesleft * carbs_min;
      }
      var startDecay = new Date(carbTime);
      startDecay.setMinutes(carbTime.getMinutes() + delay);
      if (time < lastDecayedBy || time > startDecay) {
        isDecaying = 1;
      }
      else {
        isDecaying = 0;
      }
      return {
        initialCarbs: initialCarbs,
        decayedBy: decayedBy,
        isDecaying: isDecaying,
        carbTime: carbTime
      };
    }
    else {
      return '';
    }
  };

  cob.updateVisualisation = function updateVisualisation(sbx) {

    var prop = sbx.properties.cob;

    if (prop === undefined || prop.cob === undefined) { return; }

    var displayCob = Math.round(prop.cob * 10) / 10;

    var info = [ ];
    if (prop.treatmentCOB !== undefined && prop.treatmentCOB.cob) {
      info.push({label: translate('Careportal COB'), value: Math.round(prop.treatmentCOB.cob * 10) / 10});
    }

    var lastCarbs = prop.lastCarbs || (prop.treatmentCOB && prop.treatmentCOB.lastCarbs);
    if (lastCarbs) {
      var when = new Date(lastCarbs.mills).toLocaleString();
      var amount = lastCarbs.carbs + 'g';
      info.push({label: translate('Last Carbs'), value: amount + ' @ ' + when});
    }

    sbx.pluginBase.updatePillText(sbx, {
      value: displayCob + 'g'
      , label: translate('COB')
      , info: info
    });
  };

  function alexaCOBHandler (next, slots, sbx) {
    var preamble = (slots && slots.pwd && slots.pwd.value) ? slots.pwd.value.replace('\'s', '') + ' has' : 'You have';
    var value = 'no';
    if (sbx.properties.cob && sbx.properties.cob.cob !== 0) {
      value = sbx.properties.cob.cob;
    }
    var response = preamble + ' ' + value + ' carbohydrates on board';
    next('Current COB', response);
  }

  cob.alexa = {
    intentHandlers: [{
      intent: 'MetricNow'
      , routableSlot:'metric'
      , slots:['cob', 'carbs on board', 'carbohydrates on board']
      , intentHandler: alexaCOBHandler
    }]
  };

  return cob;

}

module.exports = init;
<|endoftext|>