<commit_before>// defaults, types, and shorthands.

var path = require('path')
var url = require('url')
var Stream = require('stream').Stream
var semver = require('semver')
var stableFamily = semver.parse(process.version)
var nopt = require('nopt')
var os = require('os')
var osenv = require('osenv')
var umask = require('../utils/umask')
var hasUnicode = require('has-unicode')

var log
try {
  log = require('npmlog')
} catch (er) {
  var util = require('util')
  log = { warn: function (m) {
    console.warn(m + ' ' + util.format.apply(util, [].slice.call(arguments, 1)))
  } }
}

exports.Umask = Umask
function Umask () {}
function validateUmask (data, k, val) {
  return umask.validate(data, k, val)
}

function validateSemver (data, k, val) {
  if (!semver.valid(val)) return false
  data[k] = semver.valid(val)
}

function validateStream (data, k, val) {
  if (!(val instanceof Stream)) return false
  data[k] = val
}

nopt.typeDefs.semver = { type: semver, validate: validateSemver }
nopt.typeDefs.Stream = { type: Stream, validate: validateStream }
nopt.typeDefs.Umask = { type: Umask, validate: validateUmask }

nopt.invalidHandler = function (k, val, type) {
  log.warn('invalid config', k + '=' + JSON.stringify(val))

  if (Array.isArray(type)) {
    if (type.indexOf(url) !== -1) type = url
    else if (type.indexOf(path) !== -1) type = path
  }

  switch (type) {
    case Umask:
      log.warn('invalid config', 'Must be umask, octal number in range 0000..0777')
      break
    case url:
      log.warn('invalid config', "Must be a full url with 'http://'")
      break
    case path:
      log.warn('invalid config', 'Must be a valid filesystem path')
      break
    case Number:
      log.warn('invalid config', 'Must be a numeric value')
      break
    case Stream:
      log.warn('invalid config', 'Must be an instance of the Stream class')
      break
  }
}

if (!stableFamily || (+stableFamily.minor % 2)) stableFamily = null
else stableFamily = stableFamily.major + '.' + stableFamily.minor

var defaults

var temp = osenv.tmpdir()
var home = osenv.home()

var uidOrPid = process.getuid ? process.getuid() : process.pid

if (home) process.env.HOME = home
else home = path.resolve(temp, 'npm-' + uidOrPid)

var cacheExtra = process.platform === 'win32' ? 'npm-cache' : '.npm'
var cacheRoot = process.platform === 'win32' && process.env.APPDATA || home
var cache = path.resolve(cacheRoot, cacheExtra)

var globalPrefix
Object.defineProperty(exports, 'defaults', {get: function () {
  if (defaults) return defaults

  if (process.env.PREFIX) {
    globalPrefix = process.env.PREFIX
  } else if (process.platform === 'win32') {
    // c:\node\node.exe --> prefix=c:\node\
    globalPrefix = path.dirname(process.execPath)
  } else {
    // /usr/local/bin/node --> prefix=/usr/local
    globalPrefix = path.dirname(path.dirname(process.execPath))

    // destdir only is respected on Unix
    if (process.env.DESTDIR) {
      globalPrefix = path.join(process.env.DESTDIR, globalPrefix)
    }
  }

  defaults = {
    access: null,
    'always-auth': false,

    'bin-links': true,
    browser: null,

    ca: null,
    cafile: null,

    cache: cache,

    'cache-lock-stale': 60000,
    'cache-lock-retries': 10,
    'cache-lock-wait': 10000,

    'cache-max': Infinity,
    'cache-min': 10,

    cert: null,

    color: true,
    depth: Infinity,
    description: true,
    dev: false,
    'dry-run': false,
    editor: osenv.editor(),
    'engine-strict': false,
    force: false,

    'fetch-retries': 2,
    'fetch-retry-factor': 10,
    'fetch-retry-mintimeout': 10000,
    'fetch-retry-maxtimeout': 60000,

    git: 'git',
    'git-tag-version': true,

    global: false,
    globalconfig: path.resolve(globalPrefix, 'etc', 'npmrc'),
    group: process.platform === 'win32' ? 0
            : process.env.SUDO_GID || (process.getgid && process.getgid()),
    heading: 'npm',
    'if-present': false,
    'ignore-scripts': false,
    'init-module': path.resolve(home, '.npm-init.js'),
    'init-author-name': '',
    'init-author-email': '',
    'init-author-url': '',
    'init-version': '1.0.0',
    'init-license': 'ISC',
    json: false,
    key: null,
    link: false,
    'local-address': undefined,
    loglevel: 'warn',
    logstream: process.stderr,
    long: false,
    message: '%s',
    'node-version': process.version,
    npat: false,
    'onload-script': false,
    only: null,
    optional: true,
    parseable: false,
    prefix: globalPrefix,
    production: process.env.NODE_ENV === 'production',
    'progress': process.env.TRAVIS !== 'true' && process.env.CI !== 'true',
    'proprietary-attribs': true,
    proxy: null,
    'https-proxy': null,
    'user-agent': 'npm/{npm-version} ' +
                    'node/{node-version} ' +
                    '{platform} ' +
                    '{arch}',
    'rebuild-bundle': true,
    registry: 'https://registry.npmjs.org/',
    rollback: true,
    save: false,
    'save-bundle': false,
    'save-dev': false,
    'save-exact': false,
    'save-optional': false,
    'save-prefix': '^',
    scope: '',
    searchopts: '',
    searchexclude: null,
    searchsort: 'name',
    shell: osenv.shell(),
    shrinkwrap: true,
    'sign-git-tag': false,
    'strict-ssl': true,
    tag: 'latest',
    'tag-version-prefix': 'v',
    tmp: temp,
    unicode: hasUnicode(),
    'unsafe-perm': process.platform === 'win32' ||
                     process.platform === 'cygwin' ||
                     !(process.getuid && process.setuid &&
                       process.getgid && process.setgid) ||
                     process.getuid() !== 0,
    usage: false,
    user: process.platform === 'win32' ? 0 : 'nobody',
    userconfig: path.resolve(home, '.npmrc'),
    umask: process.umask ? process.umask() : umask.fromString('022'),
    version: false,
    versions: false,
    viewer: process.platform === 'win32' ? 'browser' : 'man',

    _exit: true
  }

  return defaults
}})

exports.types = {
  access: [null, 'restricted', 'public'],
  'always-auth': Boolean,
  'bin-links': Boolean,
  browser: [null, String],
  ca: [null, String, Array],
  cafile: path,
  cache: path,
  'cache-lock-stale': Number,
  'cache-lock-retries': Number,
  'cache-lock-wait': Number,
  'cache-max': Number,
  'cache-min': Number,
  cert: [null, String],
  color: ['always', Boolean],
  depth: Number,
  description: Boolean,
  dev: Boolean,
  'dry-run': Boolean,
  editor: String,
  'engine-strict': Boolean,
  force: Boolean,
  'fetch-retries': Number,
  'fetch-retry-factor': Number,
  'fetch-retry-mintimeout': Number,
  'fetch-retry-maxtimeout': Number,
  git: String,
  'git-tag-version': Boolean,
  global: Boolean,
  globalconfig: path,
  group: [Number, String],
  'https-proxy': [null, url],
  'user-agent': String,
  'heading': String,
  'if-present': Boolean,
  'ignore-scripts': Boolean,
  'init-module': path,
  'init-author-name': String,
  'init-author-email': String,
  'init-author-url': ['', url],
  'init-license': String,
  'init-version': semver,
  json: Boolean,
  key: [null, String],
  link: Boolean,
  // local-address must be listed as an IP for a local network interface
  // must be IPv4 due to node bug
  'local-address': getLocalAddresses(),
  loglevel: ['silent', 'error', 'warn', 'http', 'info', 'verbose', 'silly'],
  logstream: Stream,
  long: Boolean,
  message: String,
  'node-version': [null, semver],
  npat: Boolean,
  'onload-script': [null, String],
  only: [null, 'dev', 'development', 'prod', 'production'],
  optional: Boolean,
  parseable: Boolean,
  prefix: path,
  production: Boolean,
  progress: Boolean,
  'proprietary-attribs': Boolean,
  proxy: [null, false, url], // allow proxy to be disabled explicitly
  'rebuild-bundle': Boolean,
  registry: [null, url],
  rollback: Boolean,
  save: Boolean,
  'save-bundle': Boolean,
  'save-dev': Boolean,
  'save-exact': Boolean,
  'save-optional': Boolean,
  'save-prefix': String,
  scope: String,
  searchopts: String,
  searchexclude: [null, String],
  searchsort: [
    'name', '-name',
    'description', '-description',
    'author', '-author',
    'date', '-date',
    'keywords', '-keywords'
  ],
  shell: String,
  shrinkwrap: Boolean,
  'sign-git-tag': Boolean,
  'strict-ssl': Boolean,
  tag: String,
  tmp: path,
  unicode: Boolean,
  'unsafe-perm': Boolean,
  usage: Boolean,
  user: [Number, String],
  userconfig: path,
  umask: Umask,
  version: Boolean,
  'tag-version-prefix': String,
  versions: Boolean,
  viewer: String,
  _exit: Boolean
}

function getLocalAddresses () {
  var interfaces
  // #8094: some environments require elevated permissions to enumerate
  // interfaces, and synchronously throw EPERM when run without
  // elevated privileges
  try {
    interfaces = os.networkInterfaces()
  } catch (e) {
    interfaces = {}
  }

  return Object.keys(interfaces).map(function (nic) {
    return interfaces[nic].filter(function (addr) {
      return addr.family === 'IPv4'
    })
    .map(function (addr) {
      return addr.address
    })
  }).reduce(function (curr, next) {
    return curr.concat(next)
  }, []).concat(undefined)
}

exports.shorthands = {
  s: ['--loglevel', 'silent'],
  d: ['--loglevel', 'info'],
  dd: ['--loglevel', 'verbose'],
  ddd: ['--loglevel', 'silly'],
  noreg: ['--no-registry'],
  N: ['--no-registry'],
  reg: ['--registry'],
  'no-reg': ['--no-registry'],
  silent: ['--loglevel', 'silent'],
  verbose: ['--loglevel', 'verbose'],
  quiet: ['--loglevel', 'warn'],
  q: ['--loglevel', 'warn'],
  h: ['--usage'],
  H: ['--usage'],
  '?': ['--usage'],
  help: ['--usage'],
  v: ['--version'],
  f: ['--force'],
  gangster: ['--force'],
  gangsta: ['--force'],
  desc: ['--description'],
  'no-desc': ['--no-description'],
  'local': ['--no-global'],
  l: ['--long'],
  m: ['--message'],
  p: ['--parseable'],
  porcelain: ['--parseable'],
  g: ['--global'],
  S: ['--save'],
  D: ['--save-dev'],
  E: ['--save-exact'],
  O: ['--save-optional'],
  y: ['--yes'],
  n: ['--no-yes'],
  B: ['--save-bundle'],
  C: ['--prefix']
}
<commit_msg>config: Add default for new --also option<commit_after>// defaults, types, and shorthands.

var path = require('path')
var url = require('url')
var Stream = require('stream').Stream
var semver = require('semver')
var stableFamily = semver.parse(process.version)
var nopt = require('nopt')
var os = require('os')
var osenv = require('osenv')
var umask = require('../utils/umask')
var hasUnicode = require('has-unicode')

var log
try {
  log = require('npmlog')
} catch (er) {
  var util = require('util')
  log = { warn: function (m) {
    console.warn(m + ' ' + util.format.apply(util, [].slice.call(arguments, 1)))
  } }
}

exports.Umask = Umask
function Umask () {}
function validateUmask (data, k, val) {
  return umask.validate(data, k, val)
}

function validateSemver (data, k, val) {
  if (!semver.valid(val)) return false
  data[k] = semver.valid(val)
}

function validateStream (data, k, val) {
  if (!(val instanceof Stream)) return false
  data[k] = val
}

nopt.typeDefs.semver = { type: semver, validate: validateSemver }
nopt.typeDefs.Stream = { type: Stream, validate: validateStream }
nopt.typeDefs.Umask = { type: Umask, validate: validateUmask }

nopt.invalidHandler = function (k, val, type) {
  log.warn('invalid config', k + '=' + JSON.stringify(val))

  if (Array.isArray(type)) {
    if (type.indexOf(url) !== -1) type = url
    else if (type.indexOf(path) !== -1) type = path
  }

  switch (type) {
    case Umask:
      log.warn('invalid config', 'Must be umask, octal number in range 0000..0777')
      break
    case url:
      log.warn('invalid config', "Must be a full url with 'http://'")
      break
    case path:
      log.warn('invalid config', 'Must be a valid filesystem path')
      break
    case Number:
      log.warn('invalid config', 'Must be a numeric value')
      break
    case Stream:
      log.warn('invalid config', 'Must be an instance of the Stream class')
      break
  }
}

if (!stableFamily || (+stableFamily.minor % 2)) stableFamily = null
else stableFamily = stableFamily.major + '.' + stableFamily.minor

var defaults

var temp = osenv.tmpdir()
var home = osenv.home()

var uidOrPid = process.getuid ? process.getuid() : process.pid

if (home) process.env.HOME = home
else home = path.resolve(temp, 'npm-' + uidOrPid)

var cacheExtra = process.platform === 'win32' ? 'npm-cache' : '.npm'
var cacheRoot = process.platform === 'win32' && process.env.APPDATA || home
var cache = path.resolve(cacheRoot, cacheExtra)

var globalPrefix
Object.defineProperty(exports, 'defaults', {get: function () {
  if (defaults) return defaults

  if (process.env.PREFIX) {
    globalPrefix = process.env.PREFIX
  } else if (process.platform === 'win32') {
    // c:\node\node.exe --> prefix=c:\node\
    globalPrefix = path.dirname(process.execPath)
  } else {
    // /usr/local/bin/node --> prefix=/usr/local
    globalPrefix = path.dirname(path.dirname(process.execPath))

    // destdir only is respected on Unix
    if (process.env.DESTDIR) {
      globalPrefix = path.join(process.env.DESTDIR, globalPrefix)
    }
  }

  defaults = {
    access: null,
    'always-auth': false,
    also: null,

    'bin-links': true,
    browser: null,

    ca: null,
    cafile: null,

    cache: cache,

    'cache-lock-stale': 60000,
    'cache-lock-retries': 10,
    'cache-lock-wait': 10000,

    'cache-max': Infinity,
    'cache-min': 10,

    cert: null,

    color: true,
    depth: Infinity,
    description: true,
    dev: false,
    'dry-run': false,
    editor: osenv.editor(),
    'engine-strict': false,
    force: false,

    'fetch-retries': 2,
    'fetch-retry-factor': 10,
    'fetch-retry-mintimeout': 10000,
    'fetch-retry-maxtimeout': 60000,

    git: 'git',
    'git-tag-version': true,

    global: false,
    globalconfig: path.resolve(globalPrefix, 'etc', 'npmrc'),
    group: process.platform === 'win32' ? 0
            : process.env.SUDO_GID || (process.getgid && process.getgid()),
    heading: 'npm',
    'if-present': false,
    'ignore-scripts': false,
    'init-module': path.resolve(home, '.npm-init.js'),
    'init-author-name': '',
    'init-author-email': '',
    'init-author-url': '',
    'init-version': '1.0.0',
    'init-license': 'ISC',
    json: false,
    key: null,
    link: false,
    'local-address': undefined,
    loglevel: 'warn',
    logstream: process.stderr,
    long: false,
    message: '%s',
    'node-version': process.version,
    npat: false,
    'onload-script': false,
    only: null,
    optional: true,
    parseable: false,
    prefix: globalPrefix,
    production: process.env.NODE_ENV === 'production',
    'progress': process.env.TRAVIS !== 'true' && process.env.CI !== 'true',
    'proprietary-attribs': true,
    proxy: null,
    'https-proxy': null,
    'user-agent': 'npm/{npm-version} ' +
                    'node/{node-version} ' +
                    '{platform} ' +
                    '{arch}',
    'rebuild-bundle': true,
    registry: 'https://registry.npmjs.org/',
    rollback: true,
    save: false,
    'save-bundle': false,
    'save-dev': false,
    'save-exact': false,
    'save-optional': false,
    'save-prefix': '^',
    scope: '',
    searchopts: '',
    searchexclude: null,
    searchsort: 'name',
    shell: osenv.shell(),
    shrinkwrap: true,
    'sign-git-tag': false,
    'strict-ssl': true,
    tag: 'latest',
    'tag-version-prefix': 'v',
    tmp: temp,
    unicode: hasUnicode(),
    'unsafe-perm': process.platform === 'win32' ||
                     process.platform === 'cygwin' ||
                     !(process.getuid && process.setuid &&
                       process.getgid && process.setgid) ||
                     process.getuid() !== 0,
    usage: false,
    user: process.platform === 'win32' ? 0 : 'nobody',
    userconfig: path.resolve(home, '.npmrc'),
    umask: process.umask ? process.umask() : umask.fromString('022'),
    version: false,
    versions: false,
    viewer: process.platform === 'win32' ? 'browser' : 'man',

    _exit: true
  }

  return defaults
}})

exports.types = {
  access: [null, 'restricted', 'public'],
  'always-auth': Boolean,
  also: [null, 'dev', 'development'],
  'bin-links': Boolean,
  browser: [null, String],
  ca: [null, String, Array],
  cafile: path,
  cache: path,
  'cache-lock-stale': Number,
  'cache-lock-retries': Number,
  'cache-lock-wait': Number,
  'cache-max': Number,
  'cache-min': Number,
  cert: [null, String],
  color: ['always', Boolean],
  depth: Number,
  description: Boolean,
  dev: Boolean,
  'dry-run': Boolean,
  editor: String,
  'engine-strict': Boolean,
  force: Boolean,
  'fetch-retries': Number,
  'fetch-retry-factor': Number,
  'fetch-retry-mintimeout': Number,
  'fetch-retry-maxtimeout': Number,
  git: String,
  'git-tag-version': Boolean,
  global: Boolean,
  globalconfig: path,
  group: [Number, String],
  'https-proxy': [null, url],
  'user-agent': String,
  'heading': String,
  'if-present': Boolean,
  'ignore-scripts': Boolean,
  'init-module': path,
  'init-author-name': String,
  'init-author-email': String,
  'init-author-url': ['', url],
  'init-license': String,
  'init-version': semver,
  json: Boolean,
  key: [null, String],
  link: Boolean,
  // local-address must be listed as an IP for a local network interface
  // must be IPv4 due to node bug
  'local-address': getLocalAddresses(),
  loglevel: ['silent', 'error', 'warn', 'http', 'info', 'verbose', 'silly'],
  logstream: Stream,
  long: Boolean,
  message: String,
  'node-version': [null, semver],
  npat: Boolean,
  'onload-script': [null, String],
  only: [null, 'dev', 'development', 'prod', 'production'],
  optional: Boolean,
  parseable: Boolean,
  prefix: path,
  production: Boolean,
  progress: Boolean,
  'proprietary-attribs': Boolean,
  proxy: [null, false, url], // allow proxy to be disabled explicitly
  'rebuild-bundle': Boolean,
  registry: [null, url],
  rollback: Boolean,
  save: Boolean,
  'save-bundle': Boolean,
  'save-dev': Boolean,
  'save-exact': Boolean,
  'save-optional': Boolean,
  'save-prefix': String,
  scope: String,
  searchopts: String,
  searchexclude: [null, String],
  searchsort: [
    'name', '-name',
    'description', '-description',
    'author', '-author',
    'date', '-date',
    'keywords', '-keywords'
  ],
  shell: String,
  shrinkwrap: Boolean,
  'sign-git-tag': Boolean,
  'strict-ssl': Boolean,
  tag: String,
  tmp: path,
  unicode: Boolean,
  'unsafe-perm': Boolean,
  usage: Boolean,
  user: [Number, String],
  userconfig: path,
  umask: Umask,
  version: Boolean,
  'tag-version-prefix': String,
  versions: Boolean,
  viewer: String,
  _exit: Boolean
}

function getLocalAddresses () {
  var interfaces
  // #8094: some environments require elevated permissions to enumerate
  // interfaces, and synchronously throw EPERM when run without
  // elevated privileges
  try {
    interfaces = os.networkInterfaces()
  } catch (e) {
    interfaces = {}
  }

  return Object.keys(interfaces).map(function (nic) {
    return interfaces[nic].filter(function (addr) {
      return addr.family === 'IPv4'
    })
    .map(function (addr) {
      return addr.address
    })
  }).reduce(function (curr, next) {
    return curr.concat(next)
  }, []).concat(undefined)
}

exports.shorthands = {
  s: ['--loglevel', 'silent'],
  d: ['--loglevel', 'info'],
  dd: ['--loglevel', 'verbose'],
  ddd: ['--loglevel', 'silly'],
  noreg: ['--no-registry'],
  N: ['--no-registry'],
  reg: ['--registry'],
  'no-reg': ['--no-registry'],
  silent: ['--loglevel', 'silent'],
  verbose: ['--loglevel', 'verbose'],
  quiet: ['--loglevel', 'warn'],
  q: ['--loglevel', 'warn'],
  h: ['--usage'],
  H: ['--usage'],
  '?': ['--usage'],
  help: ['--usage'],
  v: ['--version'],
  f: ['--force'],
  gangster: ['--force'],
  gangsta: ['--force'],
  desc: ['--description'],
  'no-desc': ['--no-description'],
  'local': ['--no-global'],
  l: ['--long'],
  m: ['--message'],
  p: ['--parseable'],
  porcelain: ['--parseable'],
  g: ['--global'],
  S: ['--save'],
  D: ['--save-dev'],
  E: ['--save-exact'],
  O: ['--save-optional'],
  y: ['--yes'],
  n: ['--no-yes'],
  B: ['--save-bundle'],
  C: ['--prefix']
}
<|endoftext|>


<commit_before>var _ = require('underscore');
var Cassandra = require('cassandra-client');
var EventEmitter = require('events').EventEmitter;
var util = require('util');

var BucketedConnectionPool = module.exports = function (options) {
  this._options = options;

  this.waiting = [];
  this.serverPools = [];
  this.nextServer = 0;

  this.createPools();

  EventEmitter.call(this);
};
util.inherits(BucketedConnectionPool, EventEmitter);

BucketedConnectionPool.prototype.createPools = function () {
  var self = this;

  // Construct a set of connection pools one per host in options.hosts
  var hosts = self._options.hosts;
  for (var i = 0; i < hosts.length; i++) {
    if (!hosts[i]) continue;

    var parts = hosts[i].split(':');
    if (parts.length > 2) {
      throw new Error('Malformed host entry "' + hostSpec + '"');
    }

    var pool = new ConnectionPool(_.extend({}, self._options, {
      host: parts[0],
      port: (isNaN(parts[1])) ? 9160 : parts[1],
    }));

    bubbleEvent(self, pool, 'log');

    // When any pool has a connection become available, send it to a waiter.
    pool.on('available', function (avail) {
      if (!self.waiting.length) return;
      var waiter = self.waiting.shift();
      var connection = avail.acquire();
      return waiter(null, connection);
    });

    self.serverPools.push(pool);
  }
};

BucketedConnectionPool.prototype.acquire = function (callback) {
  var startServer = this.nextServer;
  var healthyCount = 0;
  var nextPool, connection;

  do {
    nextPool = this.serverPools[this.nextServer];

    this.nextServer += 1;
    if (this.nextServer == this.serverPools.length) this.nextServer = 0;

    healthyCount += nextPool.isHealthy() ? 1 : 0;

    connection = nextPool.acquire();
  } while (!connection && startServer != this.nextServer);

  if (!healthyCount) {
    return callback(new Error('All connections are unhealthy.'));
  }

  if (connection) {
    return callback(null, connection);
  }
  // There are no available connections. Add this acquisition to a waiter queue.
  // Will be given a connection when one is returned to the pool.
  else {
    return this.waiting.push(callback);
  }
};

BucketedConnectionPool.prototype.release = function (connection) {
  return connection._connectionpool.release(connection);
};

BucketedConnectionPool.prototype.execute = function (query, args, callback) {
  var self = this;
  self.acquire(function (err, connection) {
    if (err) return callback(err);

    try {
      connection.execute(query, args, function (err, results) {
        if (err && (!err.hasOwnProperty('name') || !(err.name in appExceptions))) {
          connection._connectionpool.unhealthyAt = new Date().getTime();
        }

        self.release(connection);

        return callback(err, results);
      });
    }
    catch (err) {
      connection._connectionpool.unhealthyAt = new Date().getTime();
      self.release(connection);
      return callback(err);
    }
  })
};

var ConnectionPool = function (options) {
  this._options = _.extend({
    staleThreshold: 1000,
    maxPoolSize: 1,
  }, options);

  this.unhealthyAt = 0; // timestamp of the most recent server failure
  this.connections = [];
  this.available = [];

  EventEmitter.call(this);
};
util.inherits(ConnectionPool, EventEmitter);

ConnectionPool.prototype.isHealthy = function() {
  // The connection is be considered healthy again after the staleThreshold.
  if (new Date().getTime() - this.unhealthyAt > this._options.staleThreshold) {
    this.unhealthyAt = 0;
  }
  return this.unhealthyAt === 0;
};

ConnectionPool.prototype.hasAvailable = function () {
  return this.available.length;
};

/**
 * Acquire a connection from the connection pool. The connection is returned
 * to the callback when a connection is ready. If no connections are available,
 * the callback is placed on a queue and is sent a connection when the next
 * connection is released.
 *
 * @param callback
 * @returns
 */
ConnectionPool.prototype.acquire = function () {
  var self = this;

  // Don't hand out connections during closing or unhealthy times.
  if (!this.isHealthy() || this.closing) {
    return null;
  }

  // There's already a connection free, acquire it and return it.
  if (this.available.length) {
    return this.available.pop();
  }

  // Create a new connection if there are less than maxPoolSize already.
  // Don't return it. It will be available after the connection is established.
  // Just use this acquire as a signal that the pool should be grown.
  if (this.connections.length < this._options.maxPoolSize) {
    console.log(this.connections.length, this._options.maxPoolSize)
    self.connections.push(this.create());
  }

  return null;
};

ConnectionPool.prototype.create = function () {
  var self = this;

  var connection = new Cassandra.Connection(this._options);
  connection._connectionpool = this;

  bubbleEvent(self, connection, 'log');

  // Catch errors, close the client and remove it from the pool.
  connection.on('error', function (err) {
    self.emit('log', 'error', 'Cassandra error', err);
    self.remove(connection);
  });

  connection.connect(function (err) {
    if (err) self.emit('log', 'error', 'Error connecting to Cassandra', err);

    connection._connected = !err;
    self.unhealthyAt = err ? new Date().getTime() : 0;

    if (err) {
      self.remove(connection);
    }
    else {
      self.release(connection);
    }
  });

  return connection;
};

ConnectionPool.prototype.remove = function (connection) {
  var self = this;

  // Remove all references to the connection
  self.connections.splice(self.connections.indexOf(connection), 1);
  self.available.splice(self.available.indexOf(connection), 1);

  connection._connected = false;
  connection.close(function () {
    self.emit('remove', connection);
  });
};

/**
 * Releases a connection to be used again by another query. Must be called to
 * avoid starving other consumers.
 *
 * @param connection
 * @returns
 */
ConnectionPool.prototype.release = function (connection) {
  var self = this;

  // Don't accept connections to other servers.
  if (connection._connectionpool !== self) return;

  // Don't return bad connections to the free list.
  if (!connection._connected) return self.remove(connection);

  // Drain while the pool is closing
  if (self.closing) return self.remove(connection);

  // Don't allow double free
  if (self.available.indexOf(connection) >= 0) return;

  // Released connections go back on the free list.
  // Add it back and announce on nextTick to avoid letting another thread
  // run. This prevents this function from having unpredictable runtime from
  // the releasing thread's point of view.
  // Waiting to add it back to the availbable list prevents someone from
  // stealing the connection from waiters already in the queue.
  process.nextTick(function () {
    self.available.push(connection);
    self.emit('available', self);
  });
};

ConnectionPool.prototype.close = function (callback) {
  var self = this;

  // When all of the connections have been removed, call the callback.
  var remove = function () {
    if (!self.connections.length) {
      self.removeListener('remove', remove);
      self.closing = false;
      return callback();
    }
  };
  self.on('remove', remove);

  // Start by closing all the free connections.
  self.closing = true;
  self.available.slice().forEach(function (connection) {
    self.remove(connection);
  });
};

var bubbleEvent = function (parent, child, name) {
  child.on(name, function () {
    var args = [name].concat(Array.prototype.slice.call(arguments));
    parent.emit.apply(parent, args);
  });
};

var appExceptions = [
  'InvalidRequestException',
  'TimedOutException',
  'UnavailableException',
  'SchemaDisagreementException'
];
<commit_msg>Fix unhealthy setting in connection pool<commit_after>var _ = require('underscore');
var Cassandra = require('cassandra-client');
var EventEmitter = require('events').EventEmitter;
var util = require('util');

var BucketedConnectionPool = module.exports = function (options) {
  this._options = options;

  this.waiting = [];
  this.serverPools = [];
  this.nextServer = 0;

  this.createPools();

  EventEmitter.call(this);
};
util.inherits(BucketedConnectionPool, EventEmitter);

BucketedConnectionPool.prototype.appExceptions = [
  'InvalidRequestException',
  'TimedOutException',
  'UnavailableException',
  'SchemaDisagreementException'
];

BucketedConnectionPool.prototype.createPools = function () {
  var self = this;

  // Construct a set of connection pools one per host in options.hosts
  var hosts = self._options.hosts;
  for (var i = 0; i < hosts.length; i++) {
    if (!hosts[i]) continue;

    var parts = hosts[i].split(':');
    if (parts.length > 2) {
      throw new Error('Malformed host entry "' + hostSpec + '"');
    }

    var pool = new ConnectionPool(_.extend({}, self._options, {
      host: parts[0],
      port: (isNaN(parts[1])) ? 9160 : parts[1],
    }));

    bubbleEvent(self, pool, 'log');

    // When any pool has a connection become available, send it to a waiter.
    pool.on('available', function (avail) {
      if (!self.waiting.length) return;
      var waiter = self.waiting.shift();
      var connection = avail.acquire();
      return waiter(null, connection);
    });

    self.serverPools.push(pool);
  }
};

BucketedConnectionPool.prototype.acquire = function (callback) {
  var startServer = this.nextServer;
  var healthyCount = 0;
  var nextPool, connection;

  do {
    nextPool = this.serverPools[this.nextServer];

    this.nextServer += 1;
    if (this.nextServer == this.serverPools.length) this.nextServer = 0;

    healthyCount += nextPool.isHealthy() ? 1 : 0;

    connection = nextPool.acquire();
  } while (!connection && startServer != this.nextServer);

  if (!healthyCount) {
    return callback(new Error('All connections are unhealthy.'));
  }

  if (connection) {
    return callback(null, connection);
  }
  // There are no available connections. Add this acquisition to a waiter queue.
  // Will be given a connection when one is returned to the pool.
  else {
    return this.waiting.push(callback);
  }
};

BucketedConnectionPool.prototype.release = function (connection) {
  return connection._connectionpool.release(connection);
};

BucketedConnectionPool.prototype.execute = function (query, args, callback) {
  var self = this;
  self.acquire(function (err, connection) {
    if (err) return callback(err);

    try {
      connection.execute(query, args, function (err, results) {

        if (err && (!err.hasOwnProperty('name') || self.appExceptions.indexOf(err.name) === -1)) {
          connection._connectionpool.unhealthyAt = new Date().getTime();
          self.emit('log', 'error', 'Set connection to unhealthy after execute', err);
        }

        self.release(connection);

        return callback(err, results);
      });
    }
    catch (err) {
      connection._connectionpool.unhealthyAt = new Date().getTime();
      self.emit('log', 'error', 'Set connection to unhealthy on exception', err);
      self.release(connection);
      return callback(err);
    }
  })
};

var ConnectionPool = function (options) {
  this._options = _.extend({
    staleThreshold: 1000,
    maxPoolSize: 1,
  }, options);

  this.unhealthyAt = 0; // timestamp of the most recent server failure
  this.connections = [];
  this.available = [];

  EventEmitter.call(this);
};
util.inherits(ConnectionPool, EventEmitter);

ConnectionPool.prototype.isHealthy = function() {
  // The connection is be considered healthy again after the staleThreshold.
  if (new Date().getTime() - this.unhealthyAt > this._options.staleThreshold) {
    this.unhealthyAt = 0;
  }
  return this.unhealthyAt === 0;
};

ConnectionPool.prototype.hasAvailable = function () {
  return this.available.length;
};

/**
 * Acquire a connection from the connection pool. The connection is returned
 * to the callback when a connection is ready. If no connections are available,
 * the callback is placed on a queue and is sent a connection when the next
 * connection is released.
 *
 * @param callback
 * @returns
 */
ConnectionPool.prototype.acquire = function () {
  var self = this;

  // Don't hand out connections during closing or unhealthy times.
  if (!this.isHealthy() || this.closing) {
    return null;
  }

  // There's already a connection free, acquire it and return it.
  if (this.available.length) {
    return this.available.pop();
  }

  // Create a new connection if there are less than maxPoolSize already.
  // Don't return it. It will be available after the connection is established.
  // Just use this acquire as a signal that the pool should be grown.
  if (this.connections.length < this._options.maxPoolSize) {
    self.connections.push(this.create());
  }

  return null;
};

ConnectionPool.prototype.create = function () {
  var self = this;

  var connection = new Cassandra.Connection(this._options);
  connection._connectionpool = this;

  bubbleEvent(self, connection, 'log');

  // Catch errors, close the client and remove it from the pool.
  connection.on('error', function (err) {
    self.emit('log', 'error', 'Cassandra error', err);
    self.remove(connection);
  });

  connection.connect(function (err) {
    connection._connected = !err;
    self.unhealthyAt = err ? new Date().getTime() : 0;

    if (err) {
      self.emit('log', 'error', 'Error connecting to Cassandra', err);
      self.emit('log', 'error', 'Set connection to unhealthy on connect', err);
      self.remove(connection);
    }
    else {
      self.release(connection);
    }
  });

  return connection;
};

ConnectionPool.prototype.remove = function (connection) {
  var self = this;

  // Remove all references to the connection
  self.connections.splice(self.connections.indexOf(connection), 1);
  self.available.splice(self.available.indexOf(connection), 1);

  connection._connected = false;
  connection.close(function () {
    self.emit('remove', connection);
  });
};

/**
 * Releases a connection to be used again by another query. Must be called to
 * avoid starving other consumers.
 *
 * @param connection
 * @returns
 */
ConnectionPool.prototype.release = function (connection) {
  var self = this;

  // Don't accept connections to other servers.
  if (connection._connectionpool !== self) return;

  // Don't return bad connections to the free list.
  if (!connection._connected) return self.remove(connection);

  // Drain while the pool is closing
  if (self.closing) return self.remove(connection);

  // Don't allow double free
  if (self.available.indexOf(connection) >= 0) return;

  // Released connections go back on the free list.
  // Add it back and announce on nextTick to avoid letting another thread
  // run. This prevents this function from having unpredictable runtime from
  // the releasing thread's point of view.
  // Waiting to add it back to the availbable list prevents someone from
  // stealing the connection from waiters already in the queue.
  process.nextTick(function () {
    self.available.push(connection);
    self.emit('available', self);
  });
};

ConnectionPool.prototype.close = function (callback) {
  var self = this;

  // When all of the connections have been removed, call the callback.
  var remove = function () {
    if (!self.connections.length) {
      self.removeListener('remove', remove);
      self.closing = false;
      return callback();
    }
  };
  self.on('remove', remove);

  // Start by closing all the free connections.
  self.closing = true;
  self.available.slice().forEach(function (connection) {
    self.remove(connection);
  });
};

var bubbleEvent = function (parent, child, name) {
  child.on(name, function () {
    var args = [name].concat(Array.prototype.slice.call(arguments));
    parent.emit.apply(parent, args);
  });
};
<|endoftext|>


<commit_before>import {RECOMMENDED_R5_VERSION} from 'lib/modules/r5-version/constants'

export const ENV = process.env.NODE_ENV
export const MB_TOKEN = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN
export const isServer = typeof window === 'undefined'

/**
 * Logo
 */
export const LOGO_URL = '/img/conveyal-128x128.png'
export const CB_HEX = '#2389c9'
export const CB_DARK = '#185c86'
export const CB_RGB = {r: 35, g: 137, b: 201}

/**
 * API URLs
 */
export const API_URL = `${process.env.NEXT_PUBLIC_API_URL}/api`

export const API = {
  Analysis: `${API_URL}/analysis`,
  Bundle: `${API_URL}/bundle`,
  GraphQL: `${API_URL}/graphql`,
  Jobs: `${API_URL}/jobs`,
  Modification: `${API_URL}/modification`,
  Opportunities: `${API_URL}/opportunities`,
  Project: `${API_URL}/project`,
  Region: `${API_URL}/region`,
  Regional: `${API_URL}/regional`,
  Resources: `${API_URL}/resources`,
  Timetables: `${API_URL}/timetables`,
  Workers: `${API_URL}/workers`
}
export const ANALYSIS_URL = `${API_URL}/analysis`

/**
 * Routes
 */
export const RouteTo = {
  changelog: '/changelog',
  regions: '/',
  regionCreate: '/regions/create',
  regionSettings: '/regions/[regionId]/edit',
  project: '/regions/[regionId]/projects/[projectId]',
  projects: '/regions/[regionId]',
  projectCreate: '/regions/[regionId]/create-project',
  projectSettings: '/regions/[regionId]/projects/[projectId]/edit',
  projectSelect: '/regions/[regionId]/projects',
  report: '/report',
  bundles: '/regions/[regionId]/bundles',
  bundleCreate: '/regions/[regionId]/bundles/create',
  bundleEdit: '/regions/[regionId]/bundles/[bundleId]',
  opportunities: '/regions/[regionId]/opportunities',
  opportunitiesUpload: '/regions/[regionId]/opportunities/upload',
  importShapefile: '/regions/[regionId]/projects/[projectId]/import-shapefile',
  modificationEdit:
    '/regions/[regionId]/projects/[projectId]/modifications/[modificationId]',
  modificationImport:
    '/regions/[regionId]/projects/[projectId]/import-modifications',
  modifications: '/regions/[regionId]/projects/[projectId]/modifications',
  analysis: '/regions/[regionId]/analysis',
  regionalAnalyses: '/regions/[regionId]/regional',
  regionalAnalysis: '/regions/[regionId]/regional/[analysisId]',
  login: '/api/login',
  logout: '/api/logout',
  authCallback: '/api/callback',
  resources: '/regions/[regionId]/resources',
  resourceUpload: '/regions/[regionId]/resources/upload',
  resourceEdit: '/regions/[regionId]/resources/[resourceId]'
}

/**
 * Local Storage Keys
 */
export const LS_MOM = 'modifications-on-map'

/**
 * Map
 */
export const PROJECTION_ZOOM_LEVEL = 9

/**
 * Line Editing and display
 */
export const NEW_LINE_WEIGHT = 3
export const HIGHLIGHTED_LINE_WEIGHT = NEW_LINE_WEIGHT * 2
export const NEW_STOP_RADIUS = 2.5

/**
 * Modification types
 */
export const ADD_STREETS = 'add-streets'
export const ADD_TRIP_PATTERN = 'add-trip-pattern'
export const ADJUST_DWELL_TIME = 'adjust-dwell-time'
export const ADJUST_SPEED = 'adjust-speed'
export const CONVERT_TO_FREQUENCY = 'convert-to-frequency'
export const CUSTOM_MODIFICATION = 'custom'
export const MODIFY_STREETS = 'modify-streets'
export const REMOVE_STOPS = 'remove-stops'
export const REMOVE_TRIPS = 'remove-trips'
export const REROUTE = 'reroute'
export const MODIFICATION_TYPES = [
  ADD_STREETS,
  ADD_TRIP_PATTERN,
  ADJUST_DWELL_TIME,
  ADJUST_SPEED,
  CONVERT_TO_FREQUENCY,
  MODIFY_STREETS,
  REMOVE_STOPS,
  REMOVE_TRIPS,
  REROUTE,
  CUSTOM_MODIFICATION
]

/**
 * Transit Modes
 */
export const BUS = 'BUS'
export const RAIL = 'RAIL'
export const TRAM = 'TRAM'
export const SUBWAY = 'SUBWAY'
export const FERRY = 'FERRY'
export const CABLE_CAR = 'CABLE_CAR'
export const GONDOLA = 'GONDOLA'
export const FUNICULAR = 'FUNICULAR'

// The mode index corresponds to the GTFS route_type integer
export const ALL_TRANSIT_MODES = [
  TRAM,
  SUBWAY,
  RAIL,
  BUS,
  FERRY,
  CABLE_CAR,
  GONDOLA,
  FUNICULAR
]

/**
 * Street modes
 */
export const BICYCLE = 'BICYCLE'
export const CAR = 'CAR'
export const WALK = 'WALK'
export const ALL_STREET_MODES = [BICYCLE, CAR, WALK]

/**
 * Fetch types
 */
export const FETCH_TRAVEL_TIME_SURFACE = 'FETCH_TRAVEL_TIME_SURFACE'

/**
 * Default values
 */
export const DEFAULT_ADD_STOPS_DWELL = 0
export const DEFAULT_ADJUST_DWELL_TIME_VALUE = 30
export const DEFAULT_ADJUST_SPEED_SCALE = 1
export const DEFAULT_STOP_SPACING_METERS = 400
export const MINIMUM_SNAP_STOP_ZOOM_LEVEL = 12
export const MINIMUM_STOP_SPACING = 50

/**
 * Map states
 */
export const MAP_STATE_TRANSIT_EDITOR = 'map state transit editor'
export const MAP_STATE_HIGHLIGHT_SEGMENT = 'map state highlight segment'
export const MAP_STATE_HIGHLIGHT_STOP = 'map state highlight stop'
export const MAP_STATE_HOP_SELECTION = 'map state hop selection'
export const MAP_STATE_STOP_SELECTION = 'map state stop selection'
export const MAP_STATE_SINGLE_STOP_SELECTION = 'map state single stop selection'
export const MAP_STATE_SELECT_FROM_STOP = 'map state select from stop'
export const MAP_STATE_SELECT_TO_STOP = 'map state select to stop'

/**
 * Update delay so that we can batch updates to the server with debounce
 */
export const UPDATE_DELAY_MS = 1000

/**
 * For autosaving modifications.
 */
export const AUTOSAVE_EVERY_MS = 10 * 1000

/**
 * Percentiles of travel time to request from the backend. This is for
 * TRAVEL_TIME_SURFACE requests.
 */
export const TRAVEL_TIME_PERCENTILES = [5, 25, 50, 75, 95]

/**
 * Default Profile Request object
 */
export const PROFILE_REQUEST_DEFAULTS = {
  accessModes: 'WALK',
  bikeSpeed: 4.166666666666667,
  date:
    process.env.NODE_ENV === 'test'
      ? '2020-06-10'
      : new Date().toISOString().split('T')[0],
  directModes: 'WALK',
  egressModes: 'WALK',
  fromTime: 25200,
  maxBikeTime: 20,
  maxRides: 4,
  maxTripDurationMinutes: 60,
  maxWalkTime: 20,
  monteCarloDraws: 200,
  percentiles: TRAVEL_TIME_PERCENTILES,
  toTime: 32400,
  transitModes: 'BUS,TRAM,RAIL,SUBWAY,FERRY,CABLE_CAR,GONDOLA,FUNICULAR',
  travelTimePercentile: 50,
  walkSpeed: 1.3888888888888888,
  workerVersion: RECOMMENDED_R5_VERSION,
  variantIndex: -1
}

export const UNDEFINED_PROJECT_NAME = 'Not set'
<commit_msg>Remove maxTripDurationMinutes and travelTimePercentile from defaults<commit_after>import {RECOMMENDED_R5_VERSION} from 'lib/modules/r5-version/constants'

export const ENV = process.env.NODE_ENV
export const MB_TOKEN = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN
export const isServer = typeof window === 'undefined'

/**
 * Logo
 */
export const LOGO_URL = '/img/conveyal-128x128.png'
export const CB_HEX = '#2389c9'
export const CB_DARK = '#185c86'
export const CB_RGB = {r: 35, g: 137, b: 201}

/**
 * API URLs
 */
export const API_URL = `${process.env.NEXT_PUBLIC_API_URL}/api`

export const API = {
  Analysis: `${API_URL}/analysis`,
  Bundle: `${API_URL}/bundle`,
  GraphQL: `${API_URL}/graphql`,
  Jobs: `${API_URL}/jobs`,
  Modification: `${API_URL}/modification`,
  Opportunities: `${API_URL}/opportunities`,
  Project: `${API_URL}/project`,
  Region: `${API_URL}/region`,
  Regional: `${API_URL}/regional`,
  Resources: `${API_URL}/resources`,
  Timetables: `${API_URL}/timetables`,
  Workers: `${API_URL}/workers`
}
export const ANALYSIS_URL = `${API_URL}/analysis`

/**
 * Routes
 */
export const RouteTo = {
  changelog: '/changelog',
  regions: '/',
  regionCreate: '/regions/create',
  regionSettings: '/regions/[regionId]/edit',
  project: '/regions/[regionId]/projects/[projectId]',
  projects: '/regions/[regionId]',
  projectCreate: '/regions/[regionId]/create-project',
  projectSettings: '/regions/[regionId]/projects/[projectId]/edit',
  projectSelect: '/regions/[regionId]/projects',
  report: '/report',
  bundles: '/regions/[regionId]/bundles',
  bundleCreate: '/regions/[regionId]/bundles/create',
  bundleEdit: '/regions/[regionId]/bundles/[bundleId]',
  opportunities: '/regions/[regionId]/opportunities',
  opportunitiesUpload: '/regions/[regionId]/opportunities/upload',
  importShapefile: '/regions/[regionId]/projects/[projectId]/import-shapefile',
  modificationEdit:
    '/regions/[regionId]/projects/[projectId]/modifications/[modificationId]',
  modificationImport:
    '/regions/[regionId]/projects/[projectId]/import-modifications',
  modifications: '/regions/[regionId]/projects/[projectId]/modifications',
  analysis: '/regions/[regionId]/analysis',
  regionalAnalyses: '/regions/[regionId]/regional',
  regionalAnalysis: '/regions/[regionId]/regional/[analysisId]',
  login: '/api/login',
  logout: '/api/logout',
  authCallback: '/api/callback',
  resources: '/regions/[regionId]/resources',
  resourceUpload: '/regions/[regionId]/resources/upload',
  resourceEdit: '/regions/[regionId]/resources/[resourceId]'
}

/**
 * Local Storage Keys
 */
export const LS_MOM = 'modifications-on-map'

/**
 * Map
 */
export const PROJECTION_ZOOM_LEVEL = 9

/**
 * Line Editing and display
 */
export const NEW_LINE_WEIGHT = 3
export const HIGHLIGHTED_LINE_WEIGHT = NEW_LINE_WEIGHT * 2
export const NEW_STOP_RADIUS = 2.5

/**
 * Modification types
 */
export const ADD_STREETS = 'add-streets'
export const ADD_TRIP_PATTERN = 'add-trip-pattern'
export const ADJUST_DWELL_TIME = 'adjust-dwell-time'
export const ADJUST_SPEED = 'adjust-speed'
export const CONVERT_TO_FREQUENCY = 'convert-to-frequency'
export const CUSTOM_MODIFICATION = 'custom'
export const MODIFY_STREETS = 'modify-streets'
export const REMOVE_STOPS = 'remove-stops'
export const REMOVE_TRIPS = 'remove-trips'
export const REROUTE = 'reroute'
export const MODIFICATION_TYPES = [
  ADD_STREETS,
  ADD_TRIP_PATTERN,
  ADJUST_DWELL_TIME,
  ADJUST_SPEED,
  CONVERT_TO_FREQUENCY,
  MODIFY_STREETS,
  REMOVE_STOPS,
  REMOVE_TRIPS,
  REROUTE,
  CUSTOM_MODIFICATION
]

/**
 * Transit Modes
 */
export const BUS = 'BUS'
export const RAIL = 'RAIL'
export const TRAM = 'TRAM'
export const SUBWAY = 'SUBWAY'
export const FERRY = 'FERRY'
export const CABLE_CAR = 'CABLE_CAR'
export const GONDOLA = 'GONDOLA'
export const FUNICULAR = 'FUNICULAR'

// The mode index corresponds to the GTFS route_type integer
export const ALL_TRANSIT_MODES = [
  TRAM,
  SUBWAY,
  RAIL,
  BUS,
  FERRY,
  CABLE_CAR,
  GONDOLA,
  FUNICULAR
]

/**
 * Street modes
 */
export const BICYCLE = 'BICYCLE'
export const CAR = 'CAR'
export const WALK = 'WALK'
export const ALL_STREET_MODES = [BICYCLE, CAR, WALK]

/**
 * Fetch types
 */
export const FETCH_TRAVEL_TIME_SURFACE = 'FETCH_TRAVEL_TIME_SURFACE'

/**
 * Default values
 */
export const DEFAULT_ADD_STOPS_DWELL = 0
export const DEFAULT_ADJUST_DWELL_TIME_VALUE = 30
export const DEFAULT_ADJUST_SPEED_SCALE = 1
export const DEFAULT_STOP_SPACING_METERS = 400
export const MINIMUM_SNAP_STOP_ZOOM_LEVEL = 12
export const MINIMUM_STOP_SPACING = 50

/**
 * Map states
 */
export const MAP_STATE_TRANSIT_EDITOR = 'map state transit editor'
export const MAP_STATE_HIGHLIGHT_SEGMENT = 'map state highlight segment'
export const MAP_STATE_HIGHLIGHT_STOP = 'map state highlight stop'
export const MAP_STATE_HOP_SELECTION = 'map state hop selection'
export const MAP_STATE_STOP_SELECTION = 'map state stop selection'
export const MAP_STATE_SINGLE_STOP_SELECTION = 'map state single stop selection'
export const MAP_STATE_SELECT_FROM_STOP = 'map state select from stop'
export const MAP_STATE_SELECT_TO_STOP = 'map state select to stop'

/**
 * Update delay so that we can batch updates to the server with debounce
 */
export const UPDATE_DELAY_MS = 1000

/**
 * For autosaving modifications.
 */
export const AUTOSAVE_EVERY_MS = 10 * 1000

/**
 * Percentiles of travel time to request from the backend. This is for
 * TRAVEL_TIME_SURFACE requests.
 */
export const TRAVEL_TIME_PERCENTILES = [5, 25, 50, 75, 95]

/**
 * Default Profile Request object
 */
export const PROFILE_REQUEST_DEFAULTS = {
  accessModes: 'WALK',
  bikeSpeed: 4.166666666666667,
  date:
    process.env.NODE_ENV === 'test'
      ? '2020-06-10'
      : new Date().toISOString().split('T')[0],
  directModes: 'WALK',
  egressModes: 'WALK',
  fromTime: 25200,
  maxBikeTime: 20,
  maxRides: 4,
  maxWalkTime: 20,
  monteCarloDraws: 200,
  percentiles: TRAVEL_TIME_PERCENTILES,
  toTime: 32400,
  transitModes: 'BUS,TRAM,RAIL,SUBWAY,FERRY,CABLE_CAR,GONDOLA,FUNICULAR',
  walkSpeed: 1.3888888888888888,
  workerVersion: RECOMMENDED_R5_VERSION,
  variantIndex: -1
}

export const UNDEFINED_PROJECT_NAME = 'Not set'
<|endoftext|>


<commit_before>const CollectingWalker = require('./import/collecting-walker');
const ScriptCollector = require('./import/script-collector');
const ConfigurationCollector = require('./import/configuration-collector');
const LibraryImporter = require('./import/lib-import/library-importer');
const Providers = require('./providers/providers');
const fs = require('fs');
const path = require('path');

/**
  * Static utilities. Uses an internal Builder to
  * construct the appropriate context.
  *
  */
const ContextBuilder = (function(module, undefined) {
  'use strict';

  let explicitRoot, explicitLib, instance;

  /**
   * Internal Builder class, allows us to defer the decision
   * around which providers and seed to use.
   */
  class Builder {

    constructor() {
      this.resourceName = undefined;
      this.additionalContext = {};
      this.providersImpl = undefined;
      this.seedValue = undefined;
    }

    forNamedResource(resourceName) {
      this.resourceName = resourceName;
      return this;
    }

    addContext(additionalContext) {
      Object.assign(this.additionalContext, additionalContext);
      return this;
    }

    withProvidersImpl(providersImpl) {
      this.providersImpl = providersImpl;
      return this;
    }

    withLocalStore(store) {
      this.seedValue = store;
      return this;
    }

    withRestConfig(restConfig) {
      this.seedValue = restConfig;
      return this;
    }

    build() {
      const normalizedName = normalize(this.resourceName);
      const {
        scripts,
        configurations
      } = read(this.resourceName);
      const imports = getLibImports(scripts);
      const code = scripts
        .concat(imports)
        .join('\n');
      const sandbox = Object.assign({}, {
        Providers: this.providersImpl.seed(this.seedValue, configurations)
      }, this.additionalContext || {});
      return {
        code: code,
        sandbox: sandbox
      };
    }
  }

  function compose(root) {
    if (!fs.lstatSync(root).isDirectory) {
      root = path.dirname(root);
    }
    explicitRoot = root;
    return instance;
  }

  function overrideLib(newLib) {
    explicitLib = newLib;
    return instance;
  }

  function getRoot() {
    return explicitRoot || module.parent.filename;
  }

  function build(name, additionalContext) {
    const builder = new Builder()
      .forNamedResource(name)
      .addContext(additionalContext);
    return {
      forRest: (restConfig) => {
        return builder
          .withProvidersImpl(Providers.rest)
          .withRestConfig(restConfig)
          .build();
      },
      forLocal: (store) => {
        return builder
          .withProvidersImpl(Providers.local)
          .withLocalStore(store)
          .build();
      }
    };
  }

  function getLibImports(scripts) {
    new LibraryImporter(libNormalize).getImportedScripts();
  }

  function normalize(name) {
    return path.resolve(getRoot(), name);
  }

  function libNormalize(name) {
    if (explicitLib) {
      let lib = path.resolve(getRoot(), explicitLib, name);
      return lib;
    }
    return resolveStandardLib(name);
  }

  function resolveStandardLib(name) {
    let libRoot = path.join(getRoot(), '..', 'lib');
    return path.resolve(libRoot, name);
  }

  function read(name) {
    if (fs.lstatSync(name).isDirectory()) {
      return readModule(name);
    }
    return readFile(name);
  }

  function readModule(moduleName) {
    return new CollectingWalker()
      .withDefaultOptions()
      .withCollectors(new ScriptCollector(), new ConfigurationCollector())
      .walk(moduleName);
  }

  function readFile(filename, errHandler) {
    try {
      return {
        'scripts': [fs.readFileSync(filename, 'utf-8')]
      };
    } catch (err) {
      if (errHandler) {
        errHandler(err);
      } else {
        throw err;
      }
    }
  }

  instance = {
    compose: compose,
    overrideLib: overrideLib,
    build: build
  };

  return instance;
}());

module.exports = ContextBuilder;
<commit_msg>bug fix: forgot param<commit_after>const CollectingWalker = require('./import/collecting-walker');
const ScriptCollector = require('./import/script-collector');
const ConfigurationCollector = require('./import/configuration-collector');
const LibraryImporter = require('./import/lib-import/library-importer');
const Providers = require('./providers/providers');
const fs = require('fs');
const path = require('path');

/**
  * Static utilities. Uses an internal Builder to
  * construct the appropriate context.
  *
  */
const ContextBuilder = (function(module, undefined) {
  'use strict';

  let explicitRoot, explicitLib, instance;

  /**
   * Internal Builder class, allows us to defer the decision
   * around which providers and seed to use.
   */
  class Builder {

    constructor() {
      this.resourceName = undefined;
      this.additionalContext = {};
      this.providersImpl = undefined;
      this.seedValue = undefined;
    }

    forNamedResource(resourceName) {
      this.resourceName = resourceName;
      return this;
    }

    addContext(additionalContext) {
      Object.assign(this.additionalContext, additionalContext);
      return this;
    }

    withProvidersImpl(providersImpl) {
      this.providersImpl = providersImpl;
      return this;
    }

    withLocalStore(store) {
      this.seedValue = store;
      return this;
    }

    withRestConfig(restConfig) {
      this.seedValue = restConfig;
      return this;
    }

    build() {
      const normalizedName = normalize(this.resourceName);
      const {
        scripts,
        configurations
      } = read(this.resourceName);
      const imports = getLibImports(scripts);
      const code = scripts
        .concat(imports)
        .join('\n');
      const sandbox = Object.assign({}, {
        Providers: this.providersImpl.seed(this.seedValue, configurations)
      }, this.additionalContext || {});
      return {
        code: code,
        sandbox: sandbox
      };
    }
  }

  function compose(root) {
    if (!fs.lstatSync(root).isDirectory) {
      root = path.dirname(root);
    }
    explicitRoot = root;
    return instance;
  }

  function overrideLib(newLib) {
    explicitLib = newLib;
    return instance;
  }

  function getRoot() {
    return explicitRoot || module.parent.filename;
  }

  function build(name, additionalContext) {
    const builder = new Builder()
      .forNamedResource(name)
      .addContext(additionalContext);
    return {
      forRest: (restConfig) => {
        return builder
          .withProvidersImpl(Providers.rest)
          .withRestConfig(restConfig)
          .build();
      },
      forLocal: (store) => {
        return builder
          .withProvidersImpl(Providers.local)
          .withLocalStore(store)
          .build();
      }
    };
  }

  function getLibImports(scripts) {
    new LibraryImporter(libNormalize).getImportedScripts(scripts);
  }

  function normalize(name) {
    return path.resolve(getRoot(), name);
  }

  function libNormalize(name) {
    if (explicitLib) {
      let lib = path.resolve(getRoot(), explicitLib, name);
      return lib;
    }
    return resolveStandardLib(name);
  }

  function resolveStandardLib(name) {
    let libRoot = path.join(getRoot(), '..', 'lib');
    return path.resolve(libRoot, name);
  }

  function read(name) {
    if (fs.lstatSync(name).isDirectory()) {
      return readModule(name);
    }
    return readFile(name);
  }

  function readModule(moduleName) {
    return new CollectingWalker()
      .withDefaultOptions()
      .withCollectors(new ScriptCollector(), new ConfigurationCollector())
      .walk(moduleName);
  }

  function readFile(filename, errHandler) {
    try {
      return {
        'scripts': [fs.readFileSync(filename, 'utf-8')]
      };
    } catch (err) {
      if (errHandler) {
        errHandler(err);
      } else {
        throw err;
      }
    }
  }

  instance = {
    compose: compose,
    overrideLib: overrideLib,
    build: build
  };

  return instance;
}());

module.exports = ContextBuilder;
<|endoftext|>


<commit_before>import React, {
  Component,
  PropTypes,
} from 'react';

import {
  NativeModules,
  Platform,
  PixelRatio,
  processColor,
  requireNativeComponent,
  Text,
} from 'react-native';

const NativeIconAPI = NativeModules && (NativeModules.RNVectorIconsManager || NativeModules.RNVectorIconsModule);

const DEFAULT_ICON_SIZE = 12;
const DEFAULT_ICON_COLOR = 'black';

import createIconButtonComponent from './icon-button';
import createTabBarItemIOSComponent from './tab-bar-item-ios';
import createToolbarAndroidComponent from './toolbar-android';

export default function createIconSet(glyphMap, fontFamily, fontFile) {
  let fontReference = fontFamily;
  // Android doesn't care about actual fontFamily name, it will only look in fonts folder.
  if (Platform.OS === 'android' && fontFile) {
    fontReference = fontFile.replace(/\.(otf|ttf)$/, '');
  }

  const IconNamePropType = PropTypes.oneOf(Object.keys(glyphMap));

  class Icon extends Component {
    static propTypes = {
      name: IconNamePropType.isRequired,
      size: PropTypes.number,
      color: PropTypes.string,
    };

    static defaultProps = {
      size: DEFAULT_ICON_SIZE,
      allowFontScaling: false,
    };

    setNativeProps(nativeProps) {
      if (this._root) {
        this._root.setNativeProps(nativeProps);
      }
    }

    render() {
      const { name, size, color, style, ...props } = this.props;

      let glyph = glyphMap[name] || '?';
      if (typeof glyph === 'number') {
        glyph = String.fromCharCode(glyph);
      }

      const styleDefaults:Object = {
        fontSize: size,
        fontWeight: 'normal',
        fontStyle: 'normal',
        color,
      };

      props.style = [styleDefaults, style];
      props.ref = (component) => {
        this._root = component;
      };

      styleDefaults.fontFamily = fontReference;

      return (<Text {...props}>{glyph}{this.props.children}</Text>);
    }
  }

  let imageSourceCache = {};

  function getImageSource(name, size = DEFAULT_ICON_SIZE : number, color = DEFAULT_ICON_COLOR) : Promise {
    if (!NativeIconAPI) {
      if (Platform.OS === 'android') {
        throw new Error('RNVectorIconsModule not available, did you properly integrate the module?');
      }
      throw new Error('RNVectorIconsManager not available, did you add the library to your project and link with libRNVectorIcons.a?');
    }

    let glyph = glyphMap[name] || '?';
    if (typeof glyph === 'number') {
      glyph = String.fromCharCode(glyph);
    }

    const proessedColor = processColor(color);
    const cacheKey = glyph + ':' + size + ':' + proessedColor;
    const scale = PixelRatio.get();

    return new Promise((resolve, reject) => {
      const cached = imageSourceCache[cacheKey];
      if (typeof cached !== 'undefined') {
        if (!cached || cached instanceof Error ) { reject(cached); }
        resolve({ uri: cached, scale });
      } else {
        NativeIconAPI.getImageForFont(fontReference, glyph, size, proessedColor, (err, image) => {
          const error = (typeof err === 'string' ? new Error(err) : err);
          imageSourceCache[cacheKey] = image || error || false;
          if (!error && image) {
            resolve({ uri: image, scale });
          } else {
            reject(error);
          }
        });
      }
    });
  }

  Icon.Button = createIconButtonComponent(Icon);
  Icon.TabBarItem = Icon.TabBarItemIOS = createTabBarItemIOSComponent(IconNamePropType, getImageSource);
  Icon.ToolbarAndroid = createToolbarAndroidComponent(IconNamePropType, getImageSource);
  Icon.getImageSource = getImageSource;

  return Icon;
}
<commit_msg>Removes lingering flow type definitions.<commit_after>import React, {
  Component,
  PropTypes,
} from 'react';

import {
  NativeModules,
  Platform,
  PixelRatio,
  processColor,
  requireNativeComponent,
  Text,
} from 'react-native';

const NativeIconAPI = NativeModules && (NativeModules.RNVectorIconsManager || NativeModules.RNVectorIconsModule);

const DEFAULT_ICON_SIZE = 12;
const DEFAULT_ICON_COLOR = 'black';

import createIconButtonComponent from './icon-button';
import createTabBarItemIOSComponent from './tab-bar-item-ios';
import createToolbarAndroidComponent from './toolbar-android';

export default function createIconSet(glyphMap, fontFamily, fontFile) {
  let fontReference = fontFamily;
  // Android doesn't care about actual fontFamily name, it will only look in fonts folder.
  if (Platform.OS === 'android' && fontFile) {
    fontReference = fontFile.replace(/\.(otf|ttf)$/, '');
  }

  const IconNamePropType = PropTypes.oneOf(Object.keys(glyphMap));

  class Icon extends Component {
    static propTypes = {
      name: IconNamePropType.isRequired,
      size: PropTypes.number,
      color: PropTypes.string,
    };

    static defaultProps = {
      size: DEFAULT_ICON_SIZE,
      allowFontScaling: false,
    };

    setNativeProps(nativeProps) {
      if (this._root) {
        this._root.setNativeProps(nativeProps);
      }
    }

    render() {
      const { name, size, color, style, ...props } = this.props;

      let glyph = glyphMap[name] || '?';
      if (typeof glyph === 'number') {
        glyph = String.fromCharCode(glyph);
      }

      const styleDefaults = {
        fontSize: size,
        fontWeight: 'normal',
        fontStyle: 'normal',
        color,
      };

      props.style = [styleDefaults, style];
      props.ref = (component) => {
        this._root = component;
      };

      styleDefaults.fontFamily = fontReference;

      return (<Text {...props}>{glyph}{this.props.children}</Text>);
    }
  }

  let imageSourceCache = {};

  function getImageSource(name, size = DEFAULT_ICON_SIZE, color = DEFAULT_ICON_COLOR) {
    if (!NativeIconAPI) {
      if (Platform.OS === 'android') {
        throw new Error('RNVectorIconsModule not available, did you properly integrate the module?');
      }
      throw new Error('RNVectorIconsManager not available, did you add the library to your project and link with libRNVectorIcons.a?');
    }

    let glyph = glyphMap[name] || '?';
    if (typeof glyph === 'number') {
      glyph = String.fromCharCode(glyph);
    }

    const proessedColor = processColor(color);
    const cacheKey = glyph + ':' + size + ':' + proessedColor;
    const scale = PixelRatio.get();

    return new Promise((resolve, reject) => {
      const cached = imageSourceCache[cacheKey];
      if (typeof cached !== 'undefined') {
        if (!cached || cached instanceof Error ) { reject(cached); }
        resolve({ uri: cached, scale });
      } else {
        NativeIconAPI.getImageForFont(fontReference, glyph, size, proessedColor, (err, image) => {
          const error = (typeof err === 'string' ? new Error(err) : err);
          imageSourceCache[cacheKey] = image || error || false;
          if (!error && image) {
            resolve({ uri: image, scale });
          } else {
            reject(error);
          }
        });
      }
    });
  }

  Icon.Button = createIconButtonComponent(Icon);
  Icon.TabBarItem = Icon.TabBarItemIOS = createTabBarItemIOSComponent(IconNamePropType, getImageSource);
  Icon.ToolbarAndroid = createToolbarAndroidComponent(IconNamePropType, getImageSource);
  Icon.getImageSource = getImageSource;

  return Icon;
}
<|endoftext|>


<commit_before>/**
 * @module denali
 * @submodule data
 */

/**
 * Serializers allow you to customize what data is returned the response, and
 * apply simple transformations to it. They allow you to decouple what data is
 * sent, with how that data is structured / rendered.
 *
 * @class Serializer
 * @static
 */

export default class Serializer {

  constructor() {
    throw new Error('Serializers are static classes - you should not instantiate them.');
  }

  /**
   * Take the supplied payload of record(s) or error(s) and the supplied options
   * and return a rendered a JSON response object.
   *
   * @method serialize
   * @param payload {Object[]|Error[]|Object|Error}
   * @param options {Object}
   * @return {Object} the JSON response object
   */
  static serialize() {
    throw new Error('You must implement the render method!');
  }

  /**
   * Take a serialized JSON document (i.e. an incoming request body), and
   * perform any normalization required.
   *
   * The return value of this method is entirely up to the specific serializer,
   * i.e. some may return the payload unchanged, others may tweak the structure,
   * or some could even return actual ORM model instances.
   *
   * This method is optional - the default implementation returns the payload
   * unchanged.
   *
   * @method parse
   * @param payload {Object}
   * @param options {Object}
   * @return {Object}
   */
  static parse(payload) {
    return payload;
  }

  /**
   * The list of attribute names that should be serialized. Attributes not
   * included in this list will be omitted from the final rendered payload.
   *
   * @property attributes;
   * @type {String[]}
   */
  static attributes = [];

  /**
   * An object with configuration on how to serialize relationships.
   * Relationships that have no configuration present are omitted from the final
   * rendered payload.
   *
   * Out of the box, two options are supported:
   *
   * **strategy**
   *
   * It has one of four possible values:
   *
   *   * `records`: embed all related records (1-n relationships)
   *   * `record`: embed the related record (1-1 relationships)
   *   * `ids`: include only the ids of related records (1-n relationships)
   *   * `id`: include only the id of the related record (1-1 relationships)
   *
   * What the embedded records or ids look like is up to each serializer to
   * determine.
   *
   * **type**
   *
   * The model type of the related records.
   *
   * Specific serializers may also accept additional options in the relationship
   * configuration to customize their response format.
   *
   * @property relationships
   * @type {Object}
   */
  static relationships = {};

}

<commit_msg>Fix serializer `serialize` error message<commit_after>/**
 * @module denali
 * @submodule data
 */

/**
 * Serializers allow you to customize what data is returned the response, and
 * apply simple transformations to it. They allow you to decouple what data is
 * sent, with how that data is structured / rendered.
 *
 * @class Serializer
 * @static
 */

export default class Serializer {

  constructor() {
    throw new Error('Serializers are static classes - you should not instantiate them.');
  }

  /**
   * Take the supplied payload of record(s) or error(s) and the supplied options
   * and return a rendered a JSON response object.
   *
   * @method serialize
   * @param payload {Object[]|Error[]|Object|Error}
   * @param options {Object}
   * @return {Object} the JSON response object
   */
  static serialize() {
    throw new Error('You must implement the `serialize` method!');
  }

  /**
   * Take a serialized JSON document (i.e. an incoming request body), and
   * perform any normalization required.
   *
   * The return value of this method is entirely up to the specific serializer,
   * i.e. some may return the payload unchanged, others may tweak the structure,
   * or some could even return actual ORM model instances.
   *
   * This method is optional - the default implementation returns the payload
   * unchanged.
   *
   * @method parse
   * @param payload {Object}
   * @param options {Object}
   * @return {Object}
   */
  static parse(payload) {
    return payload;
  }

  /**
   * The list of attribute names that should be serialized. Attributes not
   * included in this list will be omitted from the final rendered payload.
   *
   * @property attributes;
   * @type {String[]}
   */
  static attributes = [];

  /**
   * An object with configuration on how to serialize relationships.
   * Relationships that have no configuration present are omitted from the final
   * rendered payload.
   *
   * Out of the box, two options are supported:
   *
   * **strategy**
   *
   * It has one of four possible values:
   *
   *   * `records`: embed all related records (1-n relationships)
   *   * `record`: embed the related record (1-1 relationships)
   *   * `ids`: include only the ids of related records (1-n relationships)
   *   * `id`: include only the id of the related record (1-1 relationships)
   *
   * What the embedded records or ids look like is up to each serializer to
   * determine.
   *
   * **type**
   *
   * The model type of the related records.
   *
   * Specific serializers may also accept additional options in the relationship
   * configuration to customize their response format.
   *
   * @property relationships
   * @type {Object}
   */
  static relationships = {};

}

<|endoftext|>


<commit_before>/**
 *  @fileoverview Database table utilities
 *
 *  @author  Scott Nath
 *  @author  Sam Richard
 *
 */
'use strict';

/*
  Create a table in the database

  ## TODO
  * if table exists, confirm schema & database align

  @param {function} knex - knex Configuration information
  @param {string} name - name of table to create
  @param {object} schema -

  @returns {string} activity-completed message
 */
const createTable = (knex, schema) => {
  return knex.schema.hasTable(schema.name).then(exists => {
    if (exists) {
      return `table ${schema.name} EXISTS`; // TODO: tables exists, we need to check the schema and MAYBE update the table
    }

    // Create tables

    return knex.schema.createTable(schema.name, table => {
      schema.fields.map(field => {
        // If field is a timestamp, default to current datetime
        if (field.type === 'timestamp') {
          table[field.type](field.name).defaultTo(knex.fn.now());
        }
        else {
          table[field.type](field.name);
        }

        // If the field is an index, declare it
        if (field.index) {
          table.index(field.name);
        }
      });

      return `table ${schema.name} created`;
    }).catch(e => {
      console.log(e);

      // Testing race condition sadness
      return `table ${schema.name} EXISTS`; // TODO: Figure out how we can fail the exists condition but still fail because the table already exists
    });
  });
};

module.exports = {
  create: createTable,
};
<commit_msg>:crystal_ball: Only eat error if it's a unique<commit_after>/**
 *  @fileoverview Database table utilities
 *
 *  @author  Scott Nath
 *  @author  Sam Richard
 *
 */
'use strict';

/*
  Create a table in the database

  ## TODO
  * if table exists, confirm schema & database align

  @param {function} knex - knex Configuration information
  @param {string} name - name of table to create
  @param {object} schema -

  @returns {string} activity-completed message
 */
const createTable = (knex, schema) => {
  return knex.schema.hasTable(schema.name).then(exists => {
    if (exists) {
      return `table ${schema.name} EXISTS`; // TODO: tables exists, we need to check the schema and MAYBE update the table
    }

    // Create tables

    return knex.schema.createTable(schema.name, table => {
      schema.fields.map(field => {
        // If field is a timestamp, default to current datetime
        if (field.type === 'timestamp') {
          table[field.type](field.name).defaultTo(knex.fn.now());
        }
        else {
          table[field.type](field.name);
        }

        // If the field is an index, declare it
        if (field.index) {
          table.index(field.name);
        }
      });

      return `table ${schema.name} created`;
    }).catch(e => {
      // Testing race condition sadness
      if (e.routine === '_bt_check_unique') {
        return `table ${schema.name} EXISTS`; // TODO: Figure out how we can fail the exists condition but still fail because the table already exists
      }

      throw new Error (e);
    });
  });
};

module.exports = {
  create: createTable,
};
<|endoftext|>


<commit_before>
(function () {
    'use strict';

    /* ngInject */
    function Geocoder ($http, $q, MapControl, Config) {

        // Private variables
        var searchUrl = 'http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/find';
        var suggestUrl = 'http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/suggest';
        var boundingBox = [
            Config.bounds.southWest.lng,
            Config.bounds.southWest.lat,
            Config.bounds.northEast.lng,
            Config.bounds.northEast.lat
        ].join(',');
        var maxResults = 10;
        var searchOutFields = 'StAddr,City,Postal';
        var searchCategories = [
            'Address',
            'Postal',
            'POI'
        ].join(',');

        // Public Interface
        // TODO: Expose the bbox, outFields and categories vars as config options
        var module = {
            /**
             * Perform a geocoder search using some text
             * @param {string} The text string to search
             * @return {array} An array of features, where each feature has an attributes dict and
             *                 and a geometry dict
             */
            search: search,

            /**
             * Perform a geocoder suggest, this is faster than search and should be used e.g. to
             *  to fill an autocomplete search box
             *
             * @param {string} The text string to search
             * @return {array} An array of matching strings that relate to the searched text
             */
            suggest: suggest
        };

        return module;

        // Hoisted function definitions

        function search(text, magicKey) {
            var dfd = $q.defer();
            $http.get(searchUrl, {
                params: {
                    'text': text,
                    'category': searchCategories,
                    'outFields': searchOutFields,
                    'maxLocations': maxResults,
                    'magicKey': magicKey || null,
                    'f': 'pjson'
                }
            }).success(function (data) {
                var response = searchToList(data);
                if (response.length) {
                    // check if location is within graph bounds
                    MapControl.getGraphBounds().then(function(geojson) {
                        var geom = response[0].geometry;
                        // must use turf's geojson-y objects for turf.inside
                        var point = turf.point([geom.x, geom.y]);
                        var polygon = turf.polygon(geojson.coordinates);
                        if (turf.inside(point, polygon)) {
                            dfd.resolve(response);
                        } else {
                            dfd.reject('Address is outside the routing bounds. Please choose a different address.');
                        }
                    });
                } else {
                    dfd.reject('Unable to find the selected address. Please try a different one.');
                }
            }).error(function (error) {
                dfd.reject('Error attempting to geocode address.');
                console.error('Geocoder.search(): ', error);
            });
            return dfd.promise;
        }

        // Helper function transforms response to array of features, where each feature
        //  has an attributes and geometry dict
        function searchToList(response) {
            return _.pluck(response.locations, 'feature');
        }

        function suggest(text) {
            var dfd = $q.defer();
            $http.get(suggestUrl, {
                params: {
                    category: searchCategories,
                    f: 'pjson',
                    searchExtent: boundingBox,
                    text: text
                }
            }).success(function (data) {
                dfd.resolve(suggestToList(data));
            }).error(function (data) {
                dfd.resolve([]);
                console.error('Geocoder.suggest(): ', data);
            });

            return dfd.promise;
        }

        // Helper function transforms response to array of suggested string locations
        function suggestToList(response) {
            return _.map(response.suggestions, function (suggestion) {
                return _.pick(suggestion, ['text', 'magicKey']);
            });
        }
    }

    angular.module('nih.geocoder')
    .factory('Geocoder', Geocoder);

})();
<commit_msg>Return geocode response if bounds not found<commit_after>
(function () {
    'use strict';

    /* ngInject */
    function Geocoder ($http, $q, MapControl, Config) {

        // Private variables
        var searchUrl = 'http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/find';
        var suggestUrl = 'http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/suggest';
        var boundingBox = [
            Config.bounds.southWest.lng,
            Config.bounds.southWest.lat,
            Config.bounds.northEast.lng,
            Config.bounds.northEast.lat
        ].join(',');
        var maxResults = 10;
        var searchOutFields = 'StAddr,City,Postal';
        var searchCategories = [
            'Address',
            'Postal',
            'POI'
        ].join(',');

        // Public Interface
        // TODO: Expose the bbox, outFields and categories vars as config options
        var module = {
            /**
             * Perform a geocoder search using some text
             * @param {string} The text string to search
             * @return {array} An array of features, where each feature has an attributes dict and
             *                 and a geometry dict
             */
            search: search,

            /**
             * Perform a geocoder suggest, this is faster than search and should be used e.g. to
             *  to fill an autocomplete search box
             *
             * @param {string} The text string to search
             * @return {array} An array of matching strings that relate to the searched text
             */
            suggest: suggest
        };

        return module;

        // Hoisted function definitions

        function search(text, magicKey) {
            var dfd = $q.defer();
            $http.get(searchUrl, {
                params: {
                    'text': text,
                    'category': searchCategories,
                    'outFields': searchOutFields,
                    'maxLocations': maxResults,
                    'magicKey': magicKey || null,
                    'f': 'pjson'
                }
            }).success(function (data) {
                var response = searchToList(data);
                if (response.length) {
                    // check if location is within graph bounds
                    MapControl.getGraphBounds().then(function(geojson) {
                        var geom = response[0].geometry;
                        // must use turf's geojson-y objects for turf.inside
                        var point = turf.point([geom.x, geom.y]);
                        var polygon = turf.polygon(geojson.coordinates);
                        if (turf.inside(point, polygon)) {
                            dfd.resolve(response);
                        } else {
                            dfd.reject('Address is outside the routing bounds. Please choose a different address.');
                        }
                    }, function(error) {
                        // could not get graph bounds
                        console.error('Could not get graph bounds from OTP');
                        console.error(error);
                        // go ahead and return response
                        dfd.resolve(response);
                    });
                } else {
                    dfd.reject('Unable to find the selected address. Please try a different one.');
                }
            }).error(function (error) {
                dfd.reject('Error attempting to geocode address.');
                console.error('Geocoder.search(): ', error);
            });
            return dfd.promise;
        }

        // Helper function transforms response to array of features, where each feature
        //  has an attributes and geometry dict
        function searchToList(response) {
            return _.pluck(response.locations, 'feature');
        }

        function suggest(text) {
            var dfd = $q.defer();
            $http.get(suggestUrl, {
                params: {
                    category: searchCategories,
                    f: 'pjson',
                    searchExtent: boundingBox,
                    text: text
                }
            }).success(function (data) {
                dfd.resolve(suggestToList(data));
            }).error(function (data) {
                dfd.resolve([]);
                console.error('Geocoder.suggest(): ', data);
            });

            return dfd.promise;
        }

        // Helper function transforms response to array of suggested string locations
        function suggestToList(response) {
            return _.map(response.suggestions, function (suggestion) {
                return _.pick(suggestion, ['text', 'magicKey']);
            });
        }
    }

    angular.module('nih.geocoder')
    .factory('Geocoder', Geocoder);

})();
<|endoftext|>