<commit_before>var Vinyl = require('vinyl')
  , through2 = require('through2')
  , BlobStore = require('content-addressable-blob-store')
  , eos = require('end-of-stream')
  , xtend = require('xtend')
  , path = require('path')
  , index = require('level-map-index')
  , micromatch = require('micromatch')
  , isGlob = require('is-glob')
  , unixify = require('unixify')
  , Minimatch = require("minimatch").Minimatch
  , ordered = require('ordered-read-streams')
  , unique = require('unique-stream')
  , glob2base = require('glob2base')
  , Stats = require('fs').Stats
  , constants = require('constants')
  , EventEmitter = require('events').EventEmitter

module.exports = levelVinyl

function levelVinyl(db, opts) {
  opts = typeof opts == 'string' ? { path: opts } : opts || {}
  if (!opts.path) throw new Error('Missing or empty `opts.path`')
  opts.path = path.resolve(process.cwd(), opts.path)

  var put = db.put, get = db.get, batch = db.batch
    , blobsPath = opts.path, blobs = BlobStore(opts)

  // don't coerce to vinyl while indexing
  index(db, {get: { valueEncoding: 'json' }});

  // TODO: auto-remove old indices (but when?)
  // TODO: what if it's an absolute path?
  db.src = function(globs, opts) {
    if (typeof globs == 'string') globs = [globs]
    else if (!Array.isArray(globs)) throw new Error('Invalid glob')

    globs = globs.map(function(glob){
      if (!glob || typeof glob != 'string') throw new Error('Invalid glob')
      return unixify(glob, true)
    })

    opts = xtend({ read: true }, opts || {})

    if (!globs.length) {
      // Like vinyl-fs, return a dead stream
      var stream = through2.obj()
      process.nextTick(stream.end.bind(stream))
      return stream
    }

    var negatives = [], positives = []

    // separate globs
    globs.forEach(function(glob, i){
      var a = glob[0] === '!' ? negatives : positives
      a.push({index: i, glob: glob})
    })

    var numPositive = positives.length

    if (!numPositive || (numPositive==1 && positives[0]=='**')) {
      return db.createValueStream()
        .pipe(filterNegatives(negatives.map(toGlob)))
        .pipe(toVinyl(opts))
    }

    // create stream for each positive glob, so indices get reused
    // the logic for this (and some code) is copied from `glob-stream`
    var streams = positives.map(function(positive){
      var glob = positive.glob, i = positive.index

      if (isGlob(glob)) {
        db.index(glob, indexGlob);

        var stream = db.streamBy(glob)

        // set file.base to glob base
        if (!opts.base) stream = stream.pipe(setBase(glob))
      } else {
        // get by path
        if (glob[glob.length-1]!=='/') {
          // assume a single file is wanted
          var range = { gte: glob, limit: 1, lt: glob+'*'}
        } else {
          // effectively a directory glob
          range = { gt: glob, lt: glob+'\xff' }
        }

        stream = db.createValueStream(range)
      }

      // only filter if negative glob came after this positive glob
      var negativeGlobs = negatives.filter(indexGreaterThan(i)).map(toGlob)

      if (negativeGlobs.length)
        stream = stream.pipe(filterNegatives(negativeGlobs))

      return stream
    })

    // aggregate into stream of unique items
    return ordered(streams).pipe(unique('relative')).pipe(toVinyl(opts))
  }

  db.vinylBlobs = function() { return blobs }

  db.get = function(path, opts, cb) {
    if (typeof opts == 'function') cb = opts, opts = {}
    opts = xtend({read: true}, opts || {})

    if (opts.valueEncoding || opts.encoding)
      return get.call(db, path, opts, cb) //as-is

    get.call(db, unixify(path, true), function(err, file){
      if (err) return cb(err)
      cb(null, decode(file, opts.read))
    })
  }

  db.subvinyl = function(prefix, opts) {
    if (typeof prefix != 'string') throw new Error('Not supported')
    var sub = db.sublevel(prefix, opts)
    return levelVinyl(sub, path.join(blobsPath, '_'+prefix))
  }

  // key, value, [opts], cb
  // vinyl, [opts], cb
  db.put = function(key, vinyl, opts, cb) {
    if (typeof key != 'string') cb = opts, opts = vinyl, vinyl = key, key = vinyl.relative
    if (typeof opts == 'function') cb = opts, opts = {}
    if (!opts) opts = {}

    if (!isVinyl(vinyl) || opts.valueEncoding || opts.encoding)
      return put.call(db, key, vinyl, opts, cb) // as-is

    // dont modify incoming files?
    // vinyl = vinyl.clone()

    save(vinyl, opts, function(err, value, key){
      if (err || value==null) return cb(err, vinyl)
      put.call(db, key, value, opts, function(err){
        cb(err, vinyl)
      })
    })
  }

  // note that blobs are auto-deleted in a post hook (see below)
  var del = db.del; db.del = function(key, cb) {
    key = unixify(key.relative || key, true)
    del.call(db, key, cb)
  }

  db.dest = function(prefix, opts) {
    opts || (opts = {})

    if (typeof prefix === 'function') {
      prefix = function(fn, vinyl) {
        return normalizePrefix(fn(vinyl))
      }.bind(null, prefix)
    } else {
      prefix = normalizePrefix(prefix)
    }

    var cwd = opts.cwd || process.cwd()

    // "The write path is calculated by appending the file relative path
    // to the given destination directory."

    var stream = through2.obj(function(vinyl, _, next){
      var relative = vinyl.relative
      var vprefix = typeof prefix === 'function' ? prefix(vinyl) : prefix

      vinyl.base = vinyl.cwd = cwd
      vinyl.path = path.resolve(cwd, vprefix, relative)

      db.put(vinyl, opts, next)
    })

    stream.resume()
    return stream
  }

  // [pattern(s)][, opts][, cb])
  db.watch = function(patterns, opts, cb) {
    if (typeof patterns == 'function') cb = patterns, opts = {}, patterns = null
    else if (typeof opts == 'function') cb = opts, opts = {}

    opts = xtend({ debounceDelay: 500 }, opts || {})

    var changes = {}

    function debounce(path, change) {
      var has = changes[path]
      changes[path] = change
      if (!has) setTimeout(function(){
        var change = changes[path]
        delete changes[path]
        emitter.emit('change', {type: change, path: path})
      }, opts.debounceDelay)
    }

    var removeHook = db.post(function(op){
      var path = op.key
        , patterns = emitter.patterns

      if (!patterns.length || !micromatch(path, patterns).length) {
        return emitter.emit('nomatch', path)
      }

      debounce(path, op.type=='del' ? 'deleted' : 'changed')
    })

    var emitter = new EventEmitter()

    if (opts.maxListeners)
      emitter.setMaxListeners(opts.maxListeners)

    emitter.patterns = []

    emitter.add = function(patterns, cb) {
      this.patterns = this.patterns.concat(patterns)
    }

    emitter.remove = function(patterns) {
      if (typeof patterns == 'string') var negative = '!' + patterns
      else negative = patterns.map(function(p){ return '!'+p })
      this.patterns = this.patterns.concat(negative)
    }

    emitter.end = function() {
      removeHook()
      emitter.emit('end')
    }

    if (patterns) emitter.add(patterns)
    if (cb) emitter.on('change', cb)

    return emitter
  }

  // extra opts: `mode`
  db.batch = function(ops, opts, cb) {
    if (typeof opts == 'function') cb = opts, opts = {}

    var len = ops.length

    ;(function next(i) {
      // skip dels and non-vinyl
      while(i<len && (ops[i].type!='put' || !isVinyl(ops[i].value))) i++
      if (i==len) return batch.call(db, ops, cb)

      var op = ops[i], vinyl = op.value

      op.mode = op.mode || opts.mode

      save(vinyl, op, function(err, value, key){
        if (err) return cb(err)

        if (value==null) {
          ops = ops.splice(i, 1); --len
          return setImmediate(next.bind(null, i))
        }

        op.value = value; op.key = key
        setImmediate(next.bind(null, i+1))
      })
    })(0)
  }

  // remove blob on file removal
  db.index('digest').post(function(op){
    if (op.type=='del') {
      // TODO: index ranges without item key, so the extra
      // lookup becomes unneccesary?
      var digest = op.key[0]
      db.getBy('digest', digest, function(err, item){
        if (!item) blobs.remove(digest)
      })
    }
  })

  return db

  function isVinyl(vinyl) {
    return typeof vinyl == 'object' && '_contents' in vinyl
  }

  function toVinyl(opts) {
    var since = opts.since ? +opts.since : 0

    return through2.obj(function(file, _, next){
      if (since && since >= file.stat.mtime)
        return next()

      if (opts.base) file.base = opts.base
      next(null, decode(file, opts.read))
    })
  }

  // save contents in blob store and update stat
  function save(vinyl, opts, cb) {
    if (vinyl.isNull()) return cb()

    var ws = blobs.createWriteStream()

    eos(vinyl.pipe(ws), function(err) {
      if (err) return cb(err)

      var stat = vinyl.stat = vinyl.stat || new Stats
        , oldDigest = vinyl.digest
        , now = new Date

      vinyl.digest = ws.key

      stat.size = ws.size
      stat.ctime = now

      if (!stat.mtime || (oldDigest && vinyl.digest!==oldDigest))
        stat.mtime = now

      // default perm is 777, always set file flag
      stat.mode = (opts.mode || 0777) | constants.S_IFREG

      var value = encode(vinyl)
      var key = value.relative

      cb(null, value, key)
    })
  }

  function decode(file, read) {
    var cwd = file.cwd = process.cwd()

    file.base = path.resolve(cwd, file.base || '.')
    file.path = path.join(cwd, path.normalize(file.relative))
    file.stat = decodeStat(file.stat)

    var vinyl = new Vinyl(file)

    customProperties(file).forEach(function(prop){
      vinyl[prop] = file[prop]
    })

    // TODO: vinyl.key instead of digest (so other stores can be used)
    if (read && vinyl.digest)
      vinyl.contents = blobs.createReadStream(vinyl.digest)

    return vinyl
  }
}

function customProperties(file) {
  var stdProps =
    [ 'history', 'contents', 'cwd'
    , 'base', 'stat', 'path', 'relative' ]

  return Object.keys(file).filter(function(prop){
    return prop[0]!=='_' && stdProps.indexOf(prop)<0
  })
}

function encode(vinyl) {
  var plain = {
    relative: unixify(vinyl.relative, true),
    stat: encodeStat(vinyl.stat)
  }

  customProperties(vinyl).forEach(function(prop){
    var val = vinyl[prop]
    if (val!=null) this[prop] = val
  }, plain)

  return plain
}

function encodeStat(stat) {
  return {
    ctime: +stat.ctime,
    mtime: +stat.mtime,
    mode : 0777 & stat.mode, // only save permissions
    size : stat.size
  }
}

function decodeStat(plain) {
  var stat = new Stats()

  stat.mtime = new Date(plain.mtime)
  stat.ctime = new Date(plain.ctime)
  stat.mode  = constants.S_IFREG | plain.mode // set regular file flag
  stat.size  = plain.size

  return stat
}

function setBase(glob) {
  var mm = new Minimatch(glob)
  var base = glob2base({minimatch: mm})

  return through2.obj(function(file, _, next){
    file.base = base
    next(null, file)
  })
}

function filterNegatives(globs) {
  if (!globs.length) return through2.obj()

  globs.unshift('**') // or micromatch won't match
  return through2.obj(function(file, _, next){
    if (micromatch(file.relative, globs).length) this.push(file)
    next()
  })
}

function indexGlob(path, file, globs){
  return micromatch(path, globs).length ? [] : null
}

// taken from `glob-stream`
function toGlob(item) {
  return item.glob
}

// taken from `glob-stream`
function indexGreaterThan(index) {
  return function(obj) {
    return obj.index > index;
  };
}

function normalizePrefix(prefix) {
  if (!prefix) return '.'

  prefix = unixify(prefix)
  if (prefix[0]==='/') prefix = prefix.slice(1)

  return prefix
}
<commit_msg>stream range by glob base, then filter<commit_after>var Vinyl = require('vinyl')
  , through2 = require('through2')
  , BlobStore = require('content-addressable-blob-store')
  , eos = require('end-of-stream')
  , xtend = require('xtend')
  , path = require('path')
  , index = require('level-map-index')
  , micromatch = require('micromatch')
  , isGlob = require('is-glob')
  , unixify = require('unixify')
  , Minimatch = require("minimatch").Minimatch
  , ordered = require('ordered-read-streams')
  , unique = require('unique-stream')
  , glob2base = require('glob2base')
  , Stats = require('fs').Stats
  , constants = require('constants')
  , EventEmitter = require('events').EventEmitter

module.exports = levelVinyl

function levelVinyl(db, opts) {
  opts = typeof opts == 'string' ? { path: opts } : opts || {}
  if (!opts.path) throw new Error('Missing or empty `opts.path`')
  opts.path = path.resolve(process.cwd(), opts.path)

  var put = db.put, get = db.get, batch = db.batch
    , blobsPath = opts.path, blobs = BlobStore(opts)

  // don't coerce to vinyl while indexing
  index(db, {get: { valueEncoding: 'json' }});

  // TODO: auto-remove old indices (but when?)
  // TODO: what if it's an absolute path?
  db.src = function(globs, opts) {
    if (typeof globs == 'string') globs = [globs]
    else if (!Array.isArray(globs)) throw new Error('Invalid glob')

    globs = globs.map(function(glob){
      if (!glob || typeof glob != 'string') throw new Error('Invalid glob')
      return unixify(glob, true)
    })

    opts = xtend({ read: true }, opts || {})

    if (!globs.length) {
      // Like vinyl-fs, return a dead stream
      var stream = through2.obj()
      process.nextTick(stream.end.bind(stream))
      return stream
    }

    var negatives = [], positives = []

    // separate globs
    globs.forEach(function(glob, i){
      var a = glob[0] === '!' ? negatives : positives
      a.push({index: i, glob: glob})
    })

    var numPositive = positives.length

    if (!numPositive || (numPositive==1 && positives[0]=='**')) {
      return db.createValueStream()
        .pipe(filterNegatives(negatives.map(toGlob)))
        .pipe(toVinyl(opts))
    }

    // create stream for each positive glob, so indices get reused
    // the logic for this (and some code) is copied from `glob-stream`
    var streams = positives.map(function(positive){
      var glob = positive.glob, i = positive.index

      if (isGlob(glob)) {
        var base = getBase(glob)

        // stream range starting with glob base
        var stream = db.createValueStream({ gt: base, lt: base+'\xff' })
            .pipe(filterGlobs([glob]))

        // set file.base to glob base
        if (!opts.base && base) stream = stream.pipe(setBase(base))
      } else {
        // get by path
        if (glob[glob.length-1]!=='/') {
          // assume a single file is wanted
          var range = { gte: glob, lte: glob+'*'}
        } else {
          // effectively a directory glob
          range = { gt: glob, lt: glob+'\xff' }
        }

        stream = db.createValueStream(range)
      }

      // only filter if negative glob came after this positive glob
      var negativeGlobs = negatives.filter(indexGreaterThan(i)).map(toGlob)

      if (negativeGlobs.length)
        stream = stream.pipe(filterNegatives(negativeGlobs))

      return stream
    })

    // aggregate into stream of unique items
    return ordered(streams).pipe(unique('relative')).pipe(toVinyl(opts))
  }

  db.vinylBlobs = function() { return blobs }

  db.get = function(path, opts, cb) {
    if (typeof opts == 'function') cb = opts, opts = {}
    opts = xtend({read: true}, opts || {})

    if (opts.valueEncoding || opts.encoding)
      return get.call(db, path, opts, cb) //as-is

    get.call(db, unixify(path, true), function(err, file){
      if (err) return cb(err)
      cb(null, decode(file, opts.read))
    })
  }

  db.subvinyl = function(prefix, opts) {
    if (typeof prefix != 'string') throw new Error('Not supported')
    var sub = db.sublevel(prefix, opts)
    return levelVinyl(sub, path.join(blobsPath, '_'+prefix))
  }

  // key, value, [opts], cb
  // vinyl, [opts], cb
  db.put = function(key, vinyl, opts, cb) {
    if (typeof key != 'string') cb = opts, opts = vinyl, vinyl = key, key = vinyl.relative
    if (typeof opts == 'function') cb = opts, opts = {}
    if (!opts) opts = {}

    if (!isVinyl(vinyl) || opts.valueEncoding || opts.encoding)
      return put.call(db, key, vinyl, opts, cb) // as-is

    // dont modify incoming files?
    // vinyl = vinyl.clone()

    save(vinyl, opts, function(err, value, key){
      if (err || value==null) return cb(err, vinyl)
      put.call(db, key, value, opts, function(err){
        cb(err, vinyl)
      })
    })
  }

  // note that blobs are auto-deleted in a post hook (see below)
  var del = db.del; db.del = function(key, cb) {
    key = unixify(key.relative || key, true)
    del.call(db, key, cb)
  }

  db.dest = function(prefix, opts) {
    opts || (opts = {})

    if (typeof prefix === 'function') {
      prefix = function(fn, vinyl) {
        return normalizePrefix(fn(vinyl))
      }.bind(null, prefix)
    } else {
      prefix = normalizePrefix(prefix)
    }

    var cwd = opts.cwd || process.cwd()

    // "The write path is calculated by appending the file relative path
    // to the given destination directory."

    var stream = through2.obj(function(vinyl, _, next){
      var relative = vinyl.relative
      var vprefix = typeof prefix === 'function' ? prefix(vinyl) : prefix

      vinyl.base = vinyl.cwd = cwd
      vinyl.path = path.resolve(cwd, vprefix, relative)

      db.put(vinyl, opts, next)
    })

    stream.resume()
    return stream
  }

  // [pattern(s)][, opts][, cb])
  db.watch = function(patterns, opts, cb) {
    if (typeof patterns == 'function') cb = patterns, opts = {}, patterns = null
    else if (typeof opts == 'function') cb = opts, opts = {}

    opts = xtend({ debounceDelay: 500 }, opts || {})

    var changes = {}

    function debounce(path, change) {
      var has = changes[path]
      changes[path] = change
      if (!has) setTimeout(function(){
        var change = changes[path]
        delete changes[path]
        emitter.emit('change', {type: change, path: path})
      }, opts.debounceDelay)
    }

    var removeHook = db.post(function(op){
      var path = op.key
        , patterns = emitter.patterns

      if (!patterns.length || !micromatch(path, patterns).length) {
        return emitter.emit('nomatch', path)
      }

      debounce(path, op.type=='del' ? 'deleted' : 'changed')
    })

    var emitter = new EventEmitter()

    if (opts.maxListeners)
      emitter.setMaxListeners(opts.maxListeners)

    emitter.patterns = []

    emitter.add = function(patterns, cb) {
      this.patterns = this.patterns.concat(patterns)
    }

    emitter.remove = function(patterns) {
      if (typeof patterns == 'string') var negative = '!' + patterns
      else negative = patterns.map(function(p){ return '!'+p })
      this.patterns = this.patterns.concat(negative)
    }

    emitter.end = function() {
      removeHook()
      emitter.emit('end')
    }

    if (patterns) emitter.add(patterns)
    if (cb) emitter.on('change', cb)

    return emitter
  }

  // extra opts: `mode`
  db.batch = function(ops, opts, cb) {
    if (typeof opts == 'function') cb = opts, opts = {}

    var len = ops.length

    ;(function next(i) {
      // skip dels and non-vinyl
      while(i<len && (ops[i].type!='put' || !isVinyl(ops[i].value))) i++
      if (i==len) return batch.call(db, ops, cb)

      var op = ops[i], vinyl = op.value

      op.mode = op.mode || opts.mode

      save(vinyl, op, function(err, value, key){
        if (err) return cb(err)

        if (value==null) {
          ops = ops.splice(i, 1); --len
          return setImmediate(next.bind(null, i))
        }

        op.value = value; op.key = key
        setImmediate(next.bind(null, i+1))
      })
    })(0)
  }

  // remove blob on file removal
  db.index('digest').post(function(op){
    if (op.type=='del') {
      // TODO: index ranges without item key, so the extra
      // lookup becomes unneccesary?
      var digest = op.key[0]
      db.getBy('digest', digest, function(err, item){
        if (!item) blobs.remove(digest)
      })
    }
  })

  return db

  function isVinyl(vinyl) {
    return typeof vinyl == 'object' && '_contents' in vinyl
  }

  function toVinyl(opts) {
    var since = opts.since ? +opts.since : 0

    return through2.obj(function(file, _, next){
      if (since && since >= file.stat.mtime)
        return next()

      if (opts.base) file.base = opts.base
      next(null, decode(file, opts.read))
    })
  }

  // save contents in blob store and update stat
  function save(vinyl, opts, cb) {
    if (vinyl.isNull()) return cb()

    var ws = blobs.createWriteStream()

    eos(vinyl.pipe(ws), function(err) {
      if (err) return cb(err)

      var stat = vinyl.stat = vinyl.stat || new Stats
        , oldDigest = vinyl.digest
        , now = new Date

      vinyl.digest = ws.key

      stat.size = ws.size
      stat.ctime = now

      if (!stat.mtime || (oldDigest && vinyl.digest!==oldDigest))
        stat.mtime = now

      // default perm is 777, always set file flag
      stat.mode = (opts.mode || 0777) | constants.S_IFREG

      var value = encode(vinyl)
      var key = value.relative

      cb(null, value, key)
    })
  }

  function decode(file, read) {
    var cwd = file.cwd = process.cwd()

    file.base = path.resolve(cwd, file.base || '.')
    file.path = path.join(cwd, path.normalize(file.relative))
    file.stat = decodeStat(file.stat)

    var vinyl = new Vinyl(file)

    customProperties(file).forEach(function(prop){
      vinyl[prop] = file[prop]
    })

    // TODO: vinyl.key instead of digest (so other stores can be used)
    if (read && vinyl.digest)
      vinyl.contents = blobs.createReadStream(vinyl.digest)

    return vinyl
  }
}

function customProperties(file) {
  var stdProps =
    [ 'history', 'contents', 'cwd'
    , 'base', 'stat', 'path', 'relative' ]

  return Object.keys(file).filter(function(prop){
    return prop[0]!=='_' && stdProps.indexOf(prop)<0
  })
}

function encode(vinyl) {
  var plain = {
    relative: unixify(vinyl.relative, true),
    stat: encodeStat(vinyl.stat)
  }

  customProperties(vinyl).forEach(function(prop){
    var val = vinyl[prop]
    if (val!=null) this[prop] = val
  }, plain)

  return plain
}

function encodeStat(stat) {
  return {
    ctime: +stat.ctime,
    mtime: +stat.mtime,
    mode : 0777 & stat.mode, // only save permissions
    size : stat.size
  }
}

function decodeStat(plain) {
  var stat = new Stats()

  stat.mtime = new Date(plain.mtime)
  stat.ctime = new Date(plain.ctime)
  stat.mode  = constants.S_IFREG | plain.mode // set regular file flag
  stat.size  = plain.size

  return stat
}

function setBase(base) {
  return through2.obj(function(file, _, next){
    file.base = base
    next(null, file)
  })
}

function getBase(glob) {
  var mm = new Minimatch(glob)
  var base = unixify(glob2base({minimatch: mm}))
  return base==='./' ? '' : base
}

function filterGlobs(globs) {
  if (!globs.length) return through2.obj()

  return through2.obj(function(file, _, next){
    if (micromatch(file.relative, globs).length) this.push(file)
    next()
  })
}

function filterNegatives(globs) {
  if (!globs.length) return through2.obj()
  globs.unshift('**') // or micromatch won't match
  return filterGlobs(globs)
}

// taken from `glob-stream`
function toGlob(item) {
  return item.glob
}

// taken from `glob-stream`
function indexGreaterThan(index) {
  return function(obj) {
    return obj.index > index;
  };
}

function normalizePrefix(prefix) {
  if (!prefix) return '.'

  prefix = unixify(prefix)
  if (prefix[0]==='/') prefix = prefix.slice(1)

  return prefix
}
<|endoftext|>


<commit_before>var fs = require('fs'),
  https = require('https'),
  concat = require('concat-stream');

var geoclient = (function() {
  var TYPE = {
    ADDRESS: '1B',
    BBL: 'BL',
    BIN: 'BN',
    BLOCKFACE: 3,
    INTERSECTION: 2,
    PLACE: '1BP'
  };

  var RESPONSE_TYPE = {
    JSON: 'json',
    XML: 'xml'
  };

  var BOROUGH = {
    MANHATTAN: 'manhattan',
    BRONX: 'bronx',
    BROOKLYN: 'brooklyn',
    QUEENS: 'queens',
    STATEN_ISLAND: 'staten island'
  };

  var DIRECTION = {
    N: 'N',
    S: 'S',
    E: 'E',
    W: 'W',
  };

  var app_id, app_key;

  function MissingValueException(message) {
    this.message = message;
    this.name = "MissingValueException";
  }

  // /v1/address.json?houseNumber=314&street=west 100 st&borough=manhattan&app_id=abc123&app_key=def456
  // /v1/address.xml?houseNumber=109-20&street=71st rd&borough=queens&app_id=abc123&app_key=def456
  var getAddress = function(values, responseType, callBack) {
    if(!values.houseNumber) {
      callBack(new MissingValueException('House Number is Required'));
      return false;
    }

    if(!values.street) {
      callBack(new MissingValueException('Street (Name or 7-Digit Code) is Required'));
      return false;
    }

    if(!values.borough && !values.zip) {
      callBack(new MissingValueException('Either Borough or Zip Code is Required.'));
      return false;
    }

    var apiUrl = buildApiUrl(TYPE.ADDRESS, values, responseType);
    callApi(apiUrl, callBack);
  };

  // /v1/bbl.json?borough=manhattan&block=1889&lot=1&app_id=abc123&app_key=def456
  // /v1/bbl.xml?borough=manhattan&block=67&lot=1&app_id=abc123&app_key=def456
  var getBbl = function(values, responseType, callBack) {
    if(!values.borough) {
      callBack(new MissingValueException('Borough is Required'));
      return false;
    }

    if(!values.block) {
      callBack(new MissingValueException('Block is Required'));
      return false;
    }

    if(!values.lot) {
      callBack(new MissingValueException('Lot is Required'));
      return false;
    }

    var apiUrl = buildApiUrl(TYPE.BBL, values, responseType);
    callApi(apiUrl, callBack);
  };

  // /v1/bin.json?bin=1079043&app_id=abc123&app_key=def456
  // /v1/bin.xml?bin=1057127&app_id=abc123&app_key=def456
  var getBin = function(values, responseType, callBack) {
    if(!values.bin) {
      callBack(new MissingValueException('Building Identification Number is Required'));
      return false;
    }

    var apiUrl = buildApiUrl(TYPE.BIN, values, responseType);
    callApi(apiUrl, callBack);
  };

  // /v1/blockface.json?onStreet=amsterdam ave&crossStreetOne=w 110 st&crossStreetTwo=w 111 st&borough=manhattan&app_id=abc123&app_key=def456
  // /v1/blockface.xml?onStreet=amsterdam ave&crossStreetOne=w 110 st&crossStreetTwo=w 111 st&borough=manhattan&compassDirection=e&app_id=abc123&app_key=def456
  // /v1/blockface.xml?onStreet=eldert ln&crossStreetOne=etna street&crossStreetTwo=ridgewood ave&borough=queens&boroughCrossStreetOne=brooklyn&boughCrossStreetTwo=brooklyn&compassDirection=e&app_id=abc123&app_key=def456
  var getBlockface = function(values, responseType, callBack) {
    if(!values.onStreet) {
      callBack(new MissingValueException('"On street" (Street name of target blockface) is Required'));
      return false;
    }

    if(!values.crossStreetOne) {
      callBack(new MissingValueException('1st Cross Street of Blockface is Required'));
      return false;
    }

    if(!values.crossStreetTwo) {
      callBack(new MissingValueException('2nd Cross Street of Blockface is Required'));
      return false;
    }

    if(!values.borough) {
      callBack(new MissingValueException('Borough is Required'));
      return false;
    }

    var apiUrl = buildApiUrl(TYPE.BLOCKFACE, values, responseType);
    callApi(apiUrl, callBack);
  };

  // /v1/intersection.json?crossStreetOne=broadway&crossStreetTwo=w 99 st&borough=manhattan&app_id=abc123&app_key=def456
  // /v1/intersection.xml?crossStreetOne=rsd&crossStreetTwo=w 97 st&borough=manhattan&compassDirection=e&app_id=abc123&app_key=def456
  // /v1/intersection.json?crossStreetOne=jamaica ave&crossStreetTwo=eldert ln&borough=brooklyn&boroughCrossStreetTwo=queens&app_id=abc123&app_key=def456
  var getIntersection = function(values, responseType, callBack) {
    if(!values.crossStreetOne) {
      callBack(new MissingValueException('1st Cross Street is Required'));
      return false;
    }

    if(!values.crossStreetTwo) {
      callBack(new MissingValueException('2nd Cross Street is Required'));
      return false;
    }

    if(!values.borough) {
      callBack(new MissingValueException('Borough is Required'));
      return false;
    }

    var apiUrl = buildApiUrl(TYPE.INTERSECTION, values, responseType);
    callApi(apiUrl, callBack);
  };

  // /v1/place.json?name=empire state building&borough=manhattan&app_id=abc123&app_key=def456
  // /v1/place.xml?name=rfk bridge&borough=queens&app_id=abc123&app_key=def456
  var getPlace = function(values, responseType, callBack) {
    if(!values.name) {
      callBack(new MissingValueException('Place Name is Required'));
      return false;
    }

    if(!values.borough && !values.zip) {
      callBack(new MissingValueException('Either Borough or Zip Code is Required.'));
      return false;
    }

    var apiUrl = buildApiUrl(TYPE.PLACE, values, responseType);
    callApi(apiUrl, callBack);
  };

  var buildAddressUrl = function(values, responseType) {
    var url = [];
    url.push('address.', responseType,
      '?houseNumber=', values.houseNumber,
      '&street=', values.street);

    if(values.borough) {
      url.push('&borough=', values.borough);
    } else {
      url.push('&zip=', values.zip);
    }

    return url;
  };

  var buildBblUrl = function(values, responseType) {
    var url = [];
    url.push('bbl.', responseType,
      '?borough=', values.borough,
      '&block=', values.block,
      '&lot=', values.lot);
    return url;
  };

  var buildBinUrl = function(values, responseType) {
    var url = [];
    url.push('bin.', responseType, '?bin=', values.bin);
    return url;
  };

  var buildBlockfaceUrl = function(values, responseType) {
    var url = [];
    url.push('blockface.', responseType,
      '?onStreet=', values.onStreet,
      '&crossStreetOne=', values.crossStreetOne,
      '&crossStreetTwo=', values.crossStreetTwo,
      '&borough=', values.borough);

    if(values.boroughCrossStreetOne) {
      url.push('&boroughCrossStreetOne=', values.boroughCrossStreetOne);
    }

    if(values.boroughCrossStreetTwo) {
      url.push('&boroughCrossStreetTwo=', values.boroughCrossStreetTwo);
    }

    if(values.compassDirection) {
      url.push('&compassDirection=', values.compassDirection);
    }

    return url;
  };

  var buildIntersectionUrl = function(values, responseType) {
    var url = [];
    url.push('intersection.', responseType,
      '?crossStreetOne=', values.crossStreetOne,
      '&crossStreetTwo=', values.crossStreetTwo,
      '&borough=', values.borough);

    if(values.boroughCrossStreetTwo) {
      url.push('&boroughCrossStreetTwo=', values.boroughCrossStreetTwo);
    }

    if(values.compassDirection) {
      url.push('&compassDirection=', values.compassDirection);
    }

    return url;
  };

  var buildPlaceUrl = function(values, responseType) {
    var url = [];
    url.push('place.', responseType,
      '?name=', values.name);

    if(values.borough) {
      url.push('&borough=', values.borough);
    } else {
      url.push('&zip=', values.zip);
    }

    return url;
  };

  var buildApiUrl = function(type, values, responseType) {
    responseType = responseType || RESPONSE_TYPE.JSON;
    var url = ['https://api.cityofnewyork.us/geoclient/', 'v1/'];

    switch (type) {
      case TYPE.ADDRESS:
        url = url.concat(buildAddressUrl(values, responseType));
        break;
      case TYPE.BBL:
        url = url.concat(buildBblUrl(values, responseType));
        break;
      case TYPE.BIN:
        url = url.concat(buildBinUrl(values, responseType));
        break;
      case TYPE.BLOCKFACE:
        url = url.concat(buildBlockfaceUrl(values, responseType));
        break;
      case TYPE.INTERSECTION:
        url = url.concat(buildIntersectionUrl(values, responseType));
        break;
      case TYPE.PLACE:
        url = url.concat(buildPlaceUrl(values, responseType));
        break;
      default:
        return '';
    }

    url.push('&app_id=', app_id, '&app_key=', app_key);
    var re = /%%/g;
    return url.join('%%').replace(re,'');
  };

  var callApi = function(url, callBack) {
    console.log('requesting', url);
    https.get(url, function(res) {
      res.pipe(concat(function (data) { callBack(null, data.toString()); }));
    }).on('error', function(err) {
      callBack(err);
    });
  };

  return {
    setApi: function(appKey, appId) {
      /*
        appKey: Required
        appId: Required
      */
      app_id = appId;
      app_key = appKey;
    },
    address: function(houseNumber, street, borough, zip, responseType, callBack) {
      /*
        houseNumber: Required
        street: Required
        borough: BOROUGH Must supply either borough or zip.
        zip: Must supply either borough or zip.
        responseType: RESPONSE_TYPE Optional
      */
      return getAddress({ houseNumber: houseNumber,
        street: street,
        borough: borough,
        zip: zip }, responseType, callBack)
    },
    bbl: function(borough, block, lot, responseType, callBack) {
      /*
        borough: BOROUGH Required
        block: Required
        lot: Required
        responseType: RESPONSE_TYPE Optional
      */
      return getBbl({ borough : borough,
        block: block,
        lot: lot }, responseType, callBack);
    },
    bin: function(bin, responseType, callBack) {
      /*
        bin: Required
        responseType: RESPONSE_TYPE Optional
      */
      return getBin({ bin: bin }, responseType, callBack);
    },
    blockface: function(onStreet, crossStreetOne, crossStreetTwo, borough, boroughCrossStreetOne, boroughCrossStreetTwo, compassDirection, responseType, callBack) {
      /*
        onStreet: Required
        crossStreetOne: Required
        crossStreetTwo: Required
        borough: BOROUGH Required
        boroughCrossStreetOne: BOROUGH Optional
        boroughCrossStreetTwo: BOROUGH Optional
        compassDirection: DIRECTION Optional
        responseType: RESPONSE_TYPE Optional
      */
      return getBlockface({ onStreet: onStreet,
        crossStreetOne: crossStreetOne,
        crossStreetTwo: crossStreetTwo,
        borough: borough,
        boroughCrossStreetOne: boroughCrossStreetOne,
        boroughCrossStreetTwo: boroughCrossStreetTwo,
        compassDirection: compassDirection }, responseType, callBack);
    },
    intersection: function(crossStreetOne, crossStreetTwo, borough, boroughCrossStreetTwo, compassDirection, responseType, callBack) {
      /*
        crossStreetOne: Required
        crossStreetTwo: Required
        borough: BOROUGH Required
        boroughCrossStreetTwo:BOROUGH Optional
        compassDirection: DIRECTION Optional
        responseType: RESPONSE_TYPE Optional
      */
      return getIntersection({ crossStreetOne: crossStreetOne,
        crossStreetTwo: crossStreetTwo,
        borough: borough,
        boughCrossStreetTwo: boroughCrossStreetTwo,
        compassDirection: compassDirection }, responseType, callBack);
    },
    place: function(name, borough, zip, responseType, callBack) {
      /*
        name: Required
        borough: BOROUGH Must supply either borough or zip.
        zip: Must supply either borough or zip.
        responseType: RESPONSE_TYPE
      */
      return getPlace({ name: name,
        borough: borough,
        zip: zip }, responseType, callBack);
    },
    BOROUGH: BOROUGH,
    DIRECTION: DIRECTION,
    RESPONSE_TYPE: RESPONSE_TYPE,
    TYPE: TYPE
  };
})();

module.exports = geoclient;
<commit_msg>small tweaks<commit_after>var fs = require('fs'),
  https = require('https'),
  concat = require('concat-stream');

var geoclient = (function() {
  var TYPE = {
    ADDRESS: '1B',
    BBL: 'BL',
    BIN: 'BN',
    BLOCKFACE: 3,
    INTERSECTION: 2,
    PLACE: '1BP'
  };

  var RESPONSE_TYPE = {
    JSON: 'json',
    XML: 'xml'
  };

  var BOROUGH = {
    MANHATTAN: 'manhattan',
    BRONX: 'bronx',
    BROOKLYN: 'brooklyn',
    QUEENS: 'queens',
    STATEN_ISLAND: 'staten island'
  };

  var DIRECTION = {
    N: 'N',
    S: 'S',
    E: 'E',
    W: 'W',
  };

  var API_URL = 'https://api.cityofnewyork.us/geoclient/v1/';

  var app_id, app_key;

  function MissingValueException(message) {
    this.message = message;
    this.name = "MissingValueException";
  }

  // /v1/address.json?houseNumber=314&street=west 100 st&borough=manhattan&app_id=abc123&app_key=def456
  // /v1/address.xml?houseNumber=109-20&street=71st rd&borough=queens&app_id=abc123&app_key=def456
  var getAddress = function(values, responseType, callBack) {
    if(!values.houseNumber) {
      callBack(new MissingValueException('House Number is Required'));
      return false;
    }

    if(!values.street) {
      callBack(new MissingValueException('Street (Name or 7-Digit Code) is Required'));
      return false;
    }

    if(!values.borough && !values.zip) {
      callBack(new MissingValueException('Either Borough or Zip Code is Required.'));
      return false;
    }

    var apiUrl = buildApiUrl(TYPE.ADDRESS, values, responseType);
    callApi(apiUrl, callBack);
  };

  // /v1/bbl.json?borough=manhattan&block=1889&lot=1&app_id=abc123&app_key=def456
  // /v1/bbl.xml?borough=manhattan&block=67&lot=1&app_id=abc123&app_key=def456
  var getBbl = function(values, responseType, callBack) {
    if(!values.borough) {
      callBack(new MissingValueException('Borough is Required'));
      return false;
    }

    if(!values.block) {
      callBack(new MissingValueException('Block is Required'));
      return false;
    }

    if(!values.lot) {
      callBack(new MissingValueException('Lot is Required'));
      return false;
    }

    var apiUrl = buildApiUrl(TYPE.BBL, values, responseType);
    callApi(apiUrl, callBack);
  };

  // /v1/bin.json?bin=1079043&app_id=abc123&app_key=def456
  // /v1/bin.xml?bin=1057127&app_id=abc123&app_key=def456
  var getBin = function(values, responseType, callBack) {
    if(!values.bin) {
      callBack(new MissingValueException('Building Identification Number is Required'));
      return false;
    }

    var apiUrl = buildApiUrl(TYPE.BIN, values, responseType);
    callApi(apiUrl, callBack);
  };

  // /v1/blockface.json?onStreet=amsterdam ave&crossStreetOne=w 110 st&crossStreetTwo=w 111 st&borough=manhattan&app_id=abc123&app_key=def456
  // /v1/blockface.xml?onStreet=amsterdam ave&crossStreetOne=w 110 st&crossStreetTwo=w 111 st&borough=manhattan&compassDirection=e&app_id=abc123&app_key=def456
  // /v1/blockface.xml?onStreet=eldert ln&crossStreetOne=etna street&crossStreetTwo=ridgewood ave&borough=queens&boroughCrossStreetOne=brooklyn&boughCrossStreetTwo=brooklyn&compassDirection=e&app_id=abc123&app_key=def456
  var getBlockface = function(values, responseType, callBack) {
    if(!values.onStreet) {
      callBack(new MissingValueException('"On street" (Street name of target blockface) is Required'));
      return false;
    }

    if(!values.crossStreetOne) {
      callBack(new MissingValueException('1st Cross Street of Blockface is Required'));
      return false;
    }

    if(!values.crossStreetTwo) {
      callBack(new MissingValueException('2nd Cross Street of Blockface is Required'));
      return false;
    }

    if(!values.borough) {
      callBack(new MissingValueException('Borough is Required'));
      return false;
    }

    var apiUrl = buildApiUrl(TYPE.BLOCKFACE, values, responseType);
    callApi(apiUrl, callBack);
  };

  // /v1/intersection.json?crossStreetOne=broadway&crossStreetTwo=w 99 st&borough=manhattan&app_id=abc123&app_key=def456
  // /v1/intersection.xml?crossStreetOne=rsd&crossStreetTwo=w 97 st&borough=manhattan&compassDirection=e&app_id=abc123&app_key=def456
  // /v1/intersection.json?crossStreetOne=jamaica ave&crossStreetTwo=eldert ln&borough=brooklyn&boroughCrossStreetTwo=queens&app_id=abc123&app_key=def456
  var getIntersection = function(values, responseType, callBack) {
    if(!values.crossStreetOne) {
      callBack(new MissingValueException('1st Cross Street is Required'));
      return false;
    }

    if(!values.crossStreetTwo) {
      callBack(new MissingValueException('2nd Cross Street is Required'));
      return false;
    }

    if(!values.borough) {
      callBack(new MissingValueException('Borough is Required'));
      return false;
    }

    var apiUrl = buildApiUrl(TYPE.INTERSECTION, values, responseType);
    callApi(apiUrl, callBack);
  };

  // /v1/place.json?name=empire state building&borough=manhattan&app_id=abc123&app_key=def456
  // /v1/place.xml?name=rfk bridge&borough=queens&app_id=abc123&app_key=def456
  var getPlace = function(values, responseType, callBack) {
    if(!values.name) {
      callBack(new MissingValueException('Place Name is Required'));
      return false;
    }

    if(!values.borough && !values.zip) {
      callBack(new MissingValueException('Either Borough or Zip Code is Required.'));
      return false;
    }

    var apiUrl = buildApiUrl(TYPE.PLACE, values, responseType);
    callApi(apiUrl, callBack);
  };

  var buildAddressUrl = function(values, responseType) {
    var url = [];
    url.push('address.', responseType,
      '?houseNumber=', values.houseNumber,
      '&street=', values.street);

    if(values.borough) {
      url.push('&borough=', values.borough);
    } else {
      url.push('&zip=', values.zip);
    }

    return url;
  };

  var buildBblUrl = function(values, responseType) {
    var url = [];
    url.push('bbl.', responseType,
      '?borough=', values.borough,
      '&block=', values.block,
      '&lot=', values.lot);
    return url;
  };

  var buildBinUrl = function(values, responseType) {
    var url = [];
    url.push('bin.', responseType, '?bin=', values.bin);
    return url;
  };

  var buildBlockfaceUrl = function(values, responseType) {
    var url = [];
    url.push('blockface.', responseType,
      '?onStreet=', values.onStreet,
      '&crossStreetOne=', values.crossStreetOne,
      '&crossStreetTwo=', values.crossStreetTwo,
      '&borough=', values.borough);

    if(values.boroughCrossStreetOne) {
      url.push('&boroughCrossStreetOne=', values.boroughCrossStreetOne);
    }

    if(values.boroughCrossStreetTwo) {
      url.push('&boroughCrossStreetTwo=', values.boroughCrossStreetTwo);
    }

    if(values.compassDirection) {
      url.push('&compassDirection=', values.compassDirection);
    }

    return url;
  };

  var buildIntersectionUrl = function(values, responseType) {
    var url = [];
    url.push('intersection.', responseType,
      '?crossStreetOne=', values.crossStreetOne,
      '&crossStreetTwo=', values.crossStreetTwo,
      '&borough=', values.borough);

    if(values.boroughCrossStreetTwo) {
      url.push('&boroughCrossStreetTwo=', values.boroughCrossStreetTwo);
    }

    if(values.compassDirection) {
      url.push('&compassDirection=', values.compassDirection);
    }

    return url;
  };

  var buildPlaceUrl = function(values, responseType) {
    var url = [];
    url.push('place.', responseType,
      '?name=', values.name);

    if(values.borough) {
      url.push('&borough=', values.borough);
    } else {
      url.push('&zip=', values.zip);
    }

    return url;
  };

  var buildApiUrl = function(type, values, responseType) {
    responseType = responseType || RESPONSE_TYPE.JSON;
    var url = [];
    url.push(API_URL);

    switch (type) {
      case TYPE.ADDRESS:
        url = url.concat(buildAddressUrl(values, responseType));
        break;
      case TYPE.BBL:
        url = url.concat(buildBblUrl(values, responseType));
        break;
      case TYPE.BIN:
        url = url.concat(buildBinUrl(values, responseType));
        break;
      case TYPE.BLOCKFACE:
        url = url.concat(buildBlockfaceUrl(values, responseType));
        break;
      case TYPE.INTERSECTION:
        url = url.concat(buildIntersectionUrl(values, responseType));
        break;
      case TYPE.PLACE:
        url = url.concat(buildPlaceUrl(values, responseType));
        break;
      default:
        return '';
    }

    url.push('&app_id=', app_id, '&app_key=', app_key);
    var re = /%%/g;
    return url.join('%%').replace(re,'');
  };

  var callApi = function(url, callBack) {
    // console.log('requesting', url);
    https.get(url, function(res) {
      res.pipe(concat(function (data) { callBack(null, data.toString()); }));
    }).on('error', function(err) {
      // console.log('ERROR', err);
      callBack(err);
    });
  };

  return {
    setApi: function(appKey, appId) {
      /*
        appKey: Required
        appId: Required
      */
      app_id = appId;
      app_key = appKey;
    },
    address: function(houseNumber, street, borough, zip, responseType, callBack) {
      /*
        houseNumber: Required
        street: Required
        borough: BOROUGH Must supply either borough or zip.
        zip: Must supply either borough or zip.
        responseType: RESPONSE_TYPE Optional
      */
      return getAddress({ houseNumber: houseNumber,
        street: street,
        borough: borough,
        zip: zip }, responseType, callBack);
    },
    bbl: function(borough, block, lot, responseType, callBack) {
      /*
        borough: BOROUGH Required
        block: Required
        lot: Required
        responseType: RESPONSE_TYPE Optional
      */
      return getBbl({ borough : borough,
        block: block,
        lot: lot }, responseType, callBack);
    },
    bin: function(bin, responseType, callBack) {
      /*
        bin: Required
        responseType: RESPONSE_TYPE Optional
      */
      return getBin({ bin: bin }, responseType, callBack);
    },
    blockface: function(onStreet, crossStreetOne, crossStreetTwo, borough, boroughCrossStreetOne, boroughCrossStreetTwo, compassDirection, responseType, callBack) {
      /*
        onStreet: Required
        crossStreetOne: Required
        crossStreetTwo: Required
        borough: BOROUGH Required
        boroughCrossStreetOne: BOROUGH Optional
        boroughCrossStreetTwo: BOROUGH Optional
        compassDirection: DIRECTION Optional
        responseType: RESPONSE_TYPE Optional
      */
      return getBlockface({ onStreet: onStreet,
        crossStreetOne: crossStreetOne,
        crossStreetTwo: crossStreetTwo,
        borough: borough,
        boroughCrossStreetOne: boroughCrossStreetOne,
        boroughCrossStreetTwo: boroughCrossStreetTwo,
        compassDirection: compassDirection }, responseType, callBack);
    },
    intersection: function(crossStreetOne, crossStreetTwo, borough, boroughCrossStreetTwo, compassDirection, responseType, callBack) {
      /*
        crossStreetOne: Required
        crossStreetTwo: Required
        borough: BOROUGH Required
        boroughCrossStreetTwo:BOROUGH Optional
        compassDirection: DIRECTION Optional
        responseType: RESPONSE_TYPE Optional
      */
      return getIntersection({ crossStreetOne: crossStreetOne,
        crossStreetTwo: crossStreetTwo,
        borough: borough,
        boughCrossStreetTwo: boroughCrossStreetTwo,
        compassDirection: compassDirection }, responseType, callBack);
    },
    place: function(name, borough, zip, responseType, callBack) {
      /*
        name: Required
        borough: BOROUGH Must supply either borough or zip.
        zip: Must supply either borough or zip.
        responseType: RESPONSE_TYPE
      */
      return getPlace({ name: name,
        borough: borough,
        zip: zip }, responseType, callBack);
    },
    BOROUGH: BOROUGH,
    DIRECTION: DIRECTION,
    RESPONSE_TYPE: RESPONSE_TYPE,
    TYPE: TYPE
  };
})();

module.exports = geoclient;
<|endoftext|>


<commit_before>var jaws = require('jaws');
var fc = require('./FileCache');
var util = require('util');
var tileCache = require('./TileCache');
var app = jaws();
var port = process.env.PORT || 5000;
var logger = require('bunyan');
var log = logger.createLogger({
	name:'NodeTileCache',
	streams: [
		{
			type: 'rotating-file',
			level: 'info',
			period: '1d',
			path: 'logs/info.log'
		},
		{
			type: 'rotating-file',
			level: 'error',
			period: '1d',
			path: 'logs/error.log'
		}
	]
});
log.on('error', function (err, stream) {
    console.log(err);
    console.log(stream);
});

var timer;

var tiles = {
	"capabaseargenmap": {},
	"osm": {
		source: [
			'http://a.tile.openstreetmaps.org',
			'http://b.tile.openstreetmaps.org',
			'http://c.tile.openstreetmaps.org'
		],
		timeout: 10000,
		getTileUrl: function() {
			var r = {};
			r.capa = this.capa;
			r.z = this.z;
			r.x = this.x;
			r.y = this.y;
			r.format = this.format;

			// Invert tile y origin from top to bottom of map
			var ymax = 1 << r.z;
			r.y = ymax - r.y - 1;
			r.capa = '';
			return util.format("/%s/%s/%s.%s", r.z, r.x, r.y, r.format);
		}
	}
};

var grandTileCache = new tileCache({tileTypes:tiles,defaultTile:'256.png'});

// Event Handlers
grandTileCache.on('cache_hit',function(data){
	// log.info(data.tile,'CACHE HIT');
	console.log('CACHE HIT: ' + data.tile.url);
	var lap = process.hrtime(timer);
	console.log('IN: ' + process.hrtime(timer)[0] + 1 / 1000000000 * lap[1]);
});
grandTileCache.on('cache_miss',function(data){
	// log.info(data.tile,'CACHE MISS');
	console.log('CACHE MISS: ');
	console.log(data.tile.url);
	var lap = process.hrtime(timer);
	console.log('IN: ' + process.hrtime(timer)[0] + 1 / 1000000000 * lap[1]);
});

grandTileCache.on('error',function(err){
	// log.error(err);
	console.log('ERROR: ');
	console.log(err);
	var lap = process.hrtime(timer);
	console.log(process.hrtime(timer)[0] + 1 / 1000000000 * lap[1]);
});

// Request Handlers
getTile = function(req,res) {
	console.log('REQUEST: ' + req.url);
	timer = process.hrtime();
	var tile = grandTileCache.getTile(req.route.params,req.route.params.capa);

	tile.on('error',function(err){
		//aca tendria que ir un switch para disintos errores
		// deberia responder una imagen vacia o algo que indique el error
		// res.error(err,408);
		// res.end('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAMAAAAoyzS7AAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAA1JREFUeNoBAgD9/wAAAAIAAVMrnDAAAAAASUVORK5CYII=');
		// res.end('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAMAAAAoyzS7AAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAA1JREFUeNoBAgD9/wAAAAIAAVMrnDAAAAAASUVORK5CYII=');
		res.end();
	});

	var head = {
		'Content-Type': 'image/png'
	}
	//la primera vez que devuelva un tile no puedo sacar el Etag
	//ya que no tengo el file y por ende el mtime
	if(tile.stats.cached) {
		head.Etag = tile.stats.Etag;
	}
	res.writeHead(200,head);
	res.on('finish',function(){
		console.log('Done:');
		var lap = process.hrtime(timer);
		console.log(process.hrtime(timer)[0] + 1 / 1000000000 * lap[1]);
	});
	tile.pipe(res);
}

queryTile = function(req, res) {
	switch(req.route.params.capa) {
		case 'osm':
			res.json(osmTileCache.queryTile(req.route.params));
		break;
		default:
			res.json(argenmapTileCache.queryTile(req.route.params));
		break;
	}
}
cacheStats = function(req, res) {
	var cache = new fc();
	console.log(JSON.stringify(cache.getStats()));
	res.json(cache.getStats());
}

app.route('/tms/:capa/:z/:x/:y.:format', getTile).nocache();
// app.route('/tms/osm/:z/:y/:x.:format', getTileDeOsm).nocache();
app.route('/tms/:capa/:z/:x/:y.:format/status.json', queryTile).nocache();
app.route('/cache/status.json', cacheStats).nocache();

app.httpServer.listen(port, function () {
	console.log('NodeTileCache started at port '+port);
	setInterval(function () {
	  log.info('Flushing data...');
	  app.flush();
	},1000 * 60 * 60);
});

<commit_msg>with providers<commit_after>var jaws = require('jaws');
var fc = require('./FileCache');
var util = require('util');
var tileCache = require('./TileCache');
var app = jaws();
var port = process.env.PORT || 5000;
var logger = require('bunyan');
var log = logger.createLogger({
	name:'NodeTileCache',
	streams: [
		{
			type: 'rotating-file',
			level: 'info',
			period: '1d',
			path: 'logs/info.log'
		},
		{
			type: 'rotating-file',
			level: 'error',
			period: '1d',
			path: 'logs/error.log'
		}
	]
});
log.on('error', function (err, stream) {
    console.log(err);
    console.log(stream);
});

var timer;

var tiles = {
	"capabaseargenmap": {
		provider: {
			domain: 'ign.gob.ar/geoserver/gwc/service/tms/1.0.0',
			urlTemplate: 'http://{s}.ign.gob.ar/geoserver/gwc/service/tms/1.0.0',
			subdomains: ['mapa']
		}
	},
	"osm": {
		provider: {
			domain: 'tile.openstreetmaps.org',
			urlTemplate: 'http://{s}.tile.openstreetmaps.org',
			subdomains: 'a,b,c'.split(',')
		},
		source: [
			'http://a.tile.openstreetmaps.org',
			'http://b.tile.openstreetmaps.org',
			'http://c.tile.openstreetmaps.org'
		],
		timeout: 10000,
		getTileUrl: function() {
			var r = {};
			r.capa = this.capa;
			r.z = this.z;
			r.x = this.x;
			r.y = this.y;
			r.format = this.format;

			// Invert tile y origin from top to bottom of map
			var ymax = 1 << r.z;
			r.y = ymax - r.y - 1;
			r.capa = '';
			return util.format("/%s/%s/%s.%s", r.z, r.x, r.y, r.format);
		}
	}
};

var grandTileCache = new tileCache({tileTypes:tiles,defaultTile:'256.png'});

// Event Handlers
grandTileCache.on('cache_hit',function(data){
	// log.info(data.tile,'CACHE HIT');
	console.log('CACHE HIT: ' + data.tile.url);
	var lap = process.hrtime(timer);
	console.log('IN: ' + process.hrtime(timer)[0] + 1 / 1000000000 * lap[1]);
});
grandTileCache.on('cache_miss',function(data){
	// log.info(data.tile,'CACHE MISS');
	console.log('CACHE MISS: ');
	console.log(data.tile.url);
	var lap = process.hrtime(timer);
	console.log('IN: ' + process.hrtime(timer)[0] + 1 / 1000000000 * lap[1]);
});

grandTileCache.on('error',function(err){
	// log.error(err);
	console.log('ERROR: ');
	console.log(err);
	var lap = process.hrtime(timer);
	console.log(process.hrtime(timer)[0] + 1 / 1000000000 * lap[1]);
});

// Request Handlers
getTile = function(req,res) {
	console.log('REQUEST: ' + req.url);
	timer = process.hrtime();
	var tile = grandTileCache.getTile(req.route.params,req.route.params.capa);

	tile.on('error',function(err){
		//aca tendria que ir un switch para disintos errores
		// deberia responder una imagen vacia o algo que indique el error
		// res.error(err,408);
		// res.end('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAMAAAAoyzS7AAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAA1JREFUeNoBAgD9/wAAAAIAAVMrnDAAAAAASUVORK5CYII=');
		// res.end('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAMAAAAoyzS7AAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAA1JREFUeNoBAgD9/wAAAAIAAVMrnDAAAAAASUVORK5CYII=');
		res.end();
	});

	var head = {
		'Content-Type': 'image/png'
	}
	//la primera vez que devuelva un tile no puedo sacar el Etag
	//ya que no tengo el file y por ende el mtime
	if(tile.stats.cached) {
		head.Etag = tile.stats.Etag;
	}
	res.writeHead(200,head);
	res.on('finish',function(){
		console.log('Done:');
		var lap = process.hrtime(timer);
		console.log(process.hrtime(timer)[0] + 1 / 1000000000 * lap[1]);
	});
	tile.pipe(res);
}

queryTile = function(req, res) {
	switch(req.route.params.capa) {
		case 'osm':
			res.json(osmTileCache.queryTile(req.route.params));
		break;
		default:
			res.json(argenmapTileCache.queryTile(req.route.params));
		break;
	}
}
cacheStats = function(req, res) {
	var cache = new fc();
	console.log(JSON.stringify(cache.getStats()));
	res.json(cache.getStats());
}

app.route('/tms/:capa/:z/:x/:y.:format', getTile).nocache();
// app.route('/tms/osm/:z/:y/:x.:format', getTileDeOsm).nocache();
app.route('/tms/:capa/:z/:x/:y.:format/status.json', queryTile).nocache();
app.route('/cache/status.json', cacheStats).nocache();

app.httpServer.listen(port, function () {
	console.log('NodeTileCache started at port '+port);
	setInterval(function () {
	  log.info('Flushing data...');
	  app.flush();
	},1000 * 60 * 60);
});

<|endoftext|>


<commit_before>#!/usr/bin/env node

var argv = require('minimist')(process.argv.slice(2))

function version () {
  return require('./package.json').version
}

function help () {
  return [
    'Usage:',
    '  matrix-bundler [entry] [output] [opts]',
    '',
    'Options:',
    '  --help, -h           show help message',
    '  --version, -v        show version information',
    '  --entry, -e          the entry point for bundler, default "."',
    '  --output, -o         the exported file bundle, default "./bundle.tgz"',
    '  --parent, -p         an asset id to import the files under, default "1"',
    '  --link, -l           the link type all assets will be imported as, default "TYPE_1"',
    '  --unrestricted, -u   whether unrestricted access is allowed, default "false"',
    '',
    'Examples:',
    '  matrix-bundler ./files ./files.tgz',
    '  matrix-bundler --entry ./files --link 2 --unrestricted --output ./files.tgz'
  ].join('\n')
}

function cli (opts) {
  var readdirp = require('readdirp')
  var Bundler = require('node-matrix-bundler')
  var writer = require('node-matrix-importer')()
  var path = require('path')
  var fs = require('fs')

  if (!opts.rootNode) {
    opts.rootNode = writer.createAsset({
      type: 'folder'
    }).id

    writer.setAttribute({
      assetId: opts.rootNode,
      attribute: 'name',
      value: path.basename(path.resolve(__dirname, opts.entry))
    })

    writer.setAttribute({
      assetId: opts.rootNode,
      attribute: 'short_name',
      value: path.basename(path.resolve(__dirname, opts.entry))
    })

    writer.addPath({
      assetId: opts.rootNode,
      path: path.basename(path.resolve(__dirname, opts.entry))
    })
  }

  var bundle = Bundler({
    writer: writer,
    globalLinkType: opts.linkType,
    globalRootNode: opts.rootNode,
    globalUnrestricted: opts.unrestricted
  })
  var files = readdirp({
    root: opts.entry,
    fileFilter: ['!.*'] // ignore hidden files
  })

  files
    .on('data', function processFile (file) {
      bundle.add(file.fullPath)
    })
    .on('end', function createBundle () {
      bundle.createBundle()
        .pipe(fs.createWriteStream(opts.output))
    })
}

if (argv.version || argv.v) {
  console.log(version())
} else if (!argv._.length || argv.h || argv.help) {
  console.log(help())
} else {
  cli({
    entry: argv.entry || argv.e || argv._[0] || './',
    output: argv.output || argv.o || argv._[1] || './bundle.tgz',
    rootNode: argv.parent || argv.p,
    linkType: argv.link || argv.l,
    unrestricted: argv.unrestricted || argv.u
  })
}
<commit_msg>Add support for recursively adding files and folders to a bundle<commit_after>#!/usr/bin/env node

var argv = require('minimist')(process.argv.slice(2))

function version () {
  return require('./package.json').version
}

function help () {
  return [
    'Usage:',
    '  matrix-bundler [entry] [output] [opts]',
    '',
    'Options:',
    '  --help, -h           show help message',
    '  --version, -v        show version information',
    '  --entry, -e          the entry point for bundler, default "."',
    '  --output, -o         the exported file bundle, default "./bundle.tgz"',
    '  --recursive, -r      recursively include subdirectories, default "false"',
    '  --parent, -p         an asset id to import the files under, default "1"',
    '  --link, -l           the link type all assets will be imported as, default "TYPE_1"',
    '  --unrestricted, -u   whether unrestricted access is allowed, default "false"',
    '',
    'Examples:',
    '  matrix-bundler ./files ./files.tgz',
    '  matrix-bundler --entry ./files --link 2 --unrestricted --output ./files.tgz'
  ].join('\n')
}

function cli (opts) {
  var readdirp = require('readdirp')
  var Bundler = require('node-matrix-bundler')
  var writer = require('node-matrix-importer')({ sorted: true })
  var path = require('path')
  var fs = require('fs')

  var bundle = Bundler({
    writer: writer,
    globalLinkType: opts.linkType,
    globalRootNode: opts.rootNode,
    globalUnrestricted: opts.unrestricted
  })
  var entries = readdirp({
    root: path.resolve(__dirname, opts.entry),
    fileFilter: ['!.*'], // ignore hidden files
    directoryFilter: opts.recursive ? ['!.*'] : ['!.*', '!*'], // ignore hidden folders
    entryType: 'both'
  })
  var folders = {}

  entries
    .on('data', function processFile (entry) {
      var fullPath = entry.fullPath
      var fullParentDir = entry.fullParentDir
      var opts = {}

      if (!~Object.keys(folders).indexOf(fullParentDir)) {
        folders[fullParentDir] = createFolder(
          path.basename(fullParentDir),
          folders[path.resolve(fullParentDir, '../')] || bundle.globalRootNode
        ).id
      }

      opts['parentId'] = folders[fullParentDir]

      if (entry.stat.isFile()) {
        bundle.add(fullPath, opts)
      }
    })
    .on('end', function createBundle () {
      bundle.createBundle()
        .pipe(fs.createWriteStream(opts.output))
    })

  function createFolder (name, parentId) {
    var folder = writer.createAsset({
      parentId: parentId,
      type: 'folder'
    })
    var folderId = folder.id

    writer.setAttribute({
      assetId: folderId,
      attribute: 'name',
      value: name
    })

    writer.addPath({
      assetId: folderId,
      path: name
    })

    return folder
  }
}

if (argv.version || argv.v) {
  console.log(version())
} else if (!argv._.length || argv.h || argv.help) {
  console.log(help())
} else {
  cli({
    entry: argv.entry || argv.e || argv._[0] || './',
    output: argv.output || argv.o || argv._[1] || './bundle.tgz',
    recursive: argv.recursive || argv.r,
    rootNode: argv.parent || argv.p,
    linkType: argv.link || argv.l,
    unrestricted: argv.unrestricted || argv.u
  })
}
<|endoftext|>


<commit_before>'use strict';

const _    = require('lodash');
const Hoek = require('hoek');
const Qs   = require('qs');
const Boom = require('boom');

const internals = {
    defaults: {
        query: {
            page: {
                name: 'page',
                default: 1
            },
            limit: {
                name: 'limit',
                default: 25
            },
            pagination: {
                name: 'pagination',
                default: true
            },
            invalid: 'defaults'
        },
        meta: {
            name: 'meta',
            count: {
                active: true,
                name: 'count'
            },
            totalCount: {
                active: true,
                name: 'totalCount'
            },
            pageCount: {
                active: true,
                name: 'pageCount'
            },
            self: {
                active: true,
                name: 'self'
            },
            previous: {
                active: true,
                name: 'previous'
            },
            next: {
                active: true,
                name: 'next'
            },
            first: {
                active: true,
                name: 'first'
            },
            last: {
                active: true,
                name: 'last'
            },

            // name == default.query.page.name
            page: {
                active: false,
            },
            // name == default.query.limit.name
            limit: {
                active: false
            }

        },
        results: {
            name: 'results'
        },
        reply: {
            paginate: 'paginate'
        },
        routes: {
            include: ['*'],
            exclude: [],
            override: [{
                routes: [],
                limit: 25,
                page: 1
            }]
        }
    }
};

exports.register = function (server, options, next) {

    Hoek.assert(server.connections.length === 1,
                'You cannot register this plugin for two connections at once. ' +
                'Register it for each connection on your server.');

    internals.uri = server.info.uri

    const config = Hoek.applyToDefaults(internals.defaults, options);

    Hoek.assert(config.query.invalid === 'defaults' || config.query.invalid === 'badRequest',
                'options.query.invalid can only be: \'defaults\' or \'badRequest\' ');

    server.decorate('reply', config.reply.paginate, function(results, totalCount) {
        Hoek.assert(Array.isArray(results), '#reply.' + config.reply.paginate + ' results must be an array.');

        return this.response({ results: results, totalCount: totalCount });
    });

    server.ext('onPreHandler', function (request, reply) {

        const include = config.routes.include;
        const exclude = config.routes.exclude;
        const path = request.route.path;

        let pagination = request.query[config.query.pagination.name];

        if (typeof pagination === 'undefined') {
            pagination = config.query.pagination.default;
        }

        request.query[config.query.pagination.name] = pagination;

        if (pagination === 'false') {
            return reply.continue();
        }

        // If the route does not match, just skip this part
        if (request.route.method === 'get' && (include[0] === '*' || _.includes(include, path)) &&
            !_.includes(exclude, path)) {

            let page = config.query.page.default;
            let limit = config.query.limit.default;

            const checkRoute = v => {
                let match = _.includes(v.routes, request.route.path);
                if (match) {
                    page = v.page;
                    limit = v.limit;
                }

                return !match;
            };

            _.every(config.routes.override, checkRoute);

            if (_.has(request.query, config.query.page.name)) {
                const temp = _.parseInt(request.query[config.query.page.name]);
                if (_.isNaN(temp)) {
                    if (config.query.invalid === 'defaults') {
                        page = config.query.page.default;
                    } else {
                        return reply(Boom.badRequest('Invalid page'));
                    }

                } else {
                    page = temp;
                }
            }

            if (_.has(request.query, config.query.limit.name)) {
                const temp = _.parseInt(request.query[config.query.limit.name]);
                if (_.isNaN(temp)) {
                    if (config.query.invalid === 'defaults') {
                        limit = config.query.limit.default;
                    } else {
                        return reply(Boom.badRequest('Invalid limit'));
                    }
                } else {
                    limit = temp;
                }
            }

            request.query[config.query.page.name] = page;
            request.query[config.query.limit.name] = limit;
        }

        return reply.continue();
    });

    /**
     * totalCount must be exposed to the request object if you wants
     * the links to works properly.
     * request.totalCount = totalCount;
     * where totalCount == config.meta.totalCount.name
     * If totalCount is not exposed through the request object, the following
     * metadata will be null :
     * - last link
     * - next link
     * - totalCount
     * - pageCount
     */
    server.ext('onPreResponse', function (request, reply) {

        if (request.response.isBoom || request.route.method !== 'get') {
            return reply.continue();
        }

        if (request.query[config.query.pagination.name] === 'false') {
            return reply.continue();
        }

        const include = config.routes.include;
        const exclude = config.routes.exclude;
        const path = request.route.path;

        if ((include[0] === '*' || _.includes(include, path)) &&
            !_.includes(exclude, path)) {

            const temp = request.response.source;

            const results = Array.isArray(temp) ? request.response.source : request.response.source.results;
            Hoek.assert(Array.isArray(results), 'The results must be an array');
            const totalCount = request.response.source.totalCount || request[config.meta.totalCount.name];

            const baseUrl = internals.uri + request.url.pathname + '?';
            const qs = request.query;
            const qsPage = qs[config.query.page.name];

            const meta = {};

            const getPageCount = function() {
                return Math.trunc(totalCount / qs[config.query.limit.name]) +
                     ((totalCount % qs[config.query.limit.name] === 0) ? 0 : 1);
            };

            if (config.meta.page.active) {
                meta[config.query.page.name] = qs[config.query.page.name];
            }

            if (config.meta.limit.active) {
                meta[config.query.limit.name] = qs[config.query.limit.name];
            }

            if (config.meta.count.active) {
                meta[config.meta.count.name] = results.length;
            }

            if (config.meta.totalCount.active) {
                meta[config.meta.totalCount.name] = totalCount || null;
            }

            if (config.meta.pageCount.active) {
                let pageCount = null;

                if (totalCount) {
                    pageCount = getPageCount();
                }

                meta[config.meta.pageCount.name] = pageCount;
            }

            if (config.meta.self.active) {
                meta[config.meta.self.name] = baseUrl + Qs.stringify(qs);
            }

            const getUrl = function(page) {
                const override = {};
                override[config.query.page.name] = page;

                return baseUrl + Qs.stringify(Hoek.applyToDefaults(qs, override));
            };

            if (config.meta.previous.active) {
                let url = null;
                if (qsPage !== 1) {
                    url = getUrl(qsPage - 1);
                }

                meta[config.meta.previous.name] = url;
            }

            if (config.meta.next.active) {
                let url = null;

                if (totalCount) {
                    const pageCount = getPageCount();

                    if (qsPage < pageCount) {
                        url = getUrl(qsPage + 1);
                    }
                }

                meta[config.meta.next.name] = url;
            }

            if (config.meta.first.active) {
                meta[config.meta.first.name] = getUrl(1);
            }

            if (config.meta.last.active) {
                let url = null;

                if (totalCount) {
                    url = getUrl(getPageCount());
                }

                meta[config.meta.last.name] = url;
            }

            const response = {};
            response[config.meta.name] = meta;
            response[config.results.name] = results;
            request.response.source = response;

        }

        return reply.continue();

    });

    next();
};

exports.register.attributes = {
    name: require('./package.json').name,
    version: require('./package.json').version
};
<commit_msg>request.query.pagination is now a boolean<commit_after>'use strict';

const _    = require('lodash');
const Hoek = require('hoek');
const Qs   = require('qs');
const Boom = require('boom');

const internals = {
    defaults: {
        query: {
            page: {
                name: 'page',
                default: 1
            },
            limit: {
                name: 'limit',
                default: 25
            },
            pagination: {
                name: 'pagination',
                default: true
            },
            invalid: 'defaults'
        },
        meta: {
            name: 'meta',
            count: {
                active: true,
                name: 'count'
            },
            totalCount: {
                active: true,
                name: 'totalCount'
            },
            pageCount: {
                active: true,
                name: 'pageCount'
            },
            self: {
                active: true,
                name: 'self'
            },
            previous: {
                active: true,
                name: 'previous'
            },
            next: {
                active: true,
                name: 'next'
            },
            first: {
                active: true,
                name: 'first'
            },
            last: {
                active: true,
                name: 'last'
            },

            // name == default.query.page.name
            page: {
                active: false,
            },
            // name == default.query.limit.name
            limit: {
                active: false
            }

        },
        results: {
            name: 'results'
        },
        reply: {
            paginate: 'paginate'
        },
        routes: {
            include: ['*'],
            exclude: [],
            override: [{
                routes: [],
                limit: 25,
                page: 1
            }]
        }
    }
};

exports.register = function (server, options, next) {

    Hoek.assert(server.connections.length === 1,
                'You cannot register this plugin for two connections at once. ' +
                'Register it for each connection on your server.');

    internals.uri = server.info.uri

    const config = Hoek.applyToDefaults(internals.defaults, options);

    Hoek.assert(config.query.invalid === 'defaults' || config.query.invalid === 'badRequest',
                'options.query.invalid can only be: \'defaults\' or \'badRequest\' ');

    server.decorate('reply', config.reply.paginate, function(results, totalCount) {
        Hoek.assert(Array.isArray(results), '#reply.' + config.reply.paginate + ' results must be an array.');

        return this.response({ results: results, totalCount: totalCount });
    });

    server.ext('onPreHandler', function (request, reply) {

        const include = config.routes.include;
        const exclude = config.routes.exclude;
        const path = request.route.path;

        let pagination = request.query[config.query.pagination.name];

        if (typeof pagination === 'undefined') {
            pagination = config.query.pagination.default;
        } else if (pagination === 'false') {
            pagination = false;
        } else if (pagination === 'true') {
            pagination = true;
        }

        request.query[config.query.pagination.name] = pagination;

        if (pagination === false) {
            return reply.continue();
        }

        // If the route does not match, just skip this part
        if (request.route.method === 'get' && (include[0] === '*' || _.includes(include, path)) &&
            !_.includes(exclude, path)) {

            let page = config.query.page.default;
            let limit = config.query.limit.default;

            const checkRoute = v => {
                let match = _.includes(v.routes, request.route.path);
                if (match) {
                    page = v.page;
                    limit = v.limit;
                }

                return !match;
            };

            _.every(config.routes.override, checkRoute);

            if (_.has(request.query, config.query.page.name)) {
                const temp = _.parseInt(request.query[config.query.page.name]);
                if (_.isNaN(temp)) {
                    if (config.query.invalid === 'defaults') {
                        page = config.query.page.default;
                    } else {
                        return reply(Boom.badRequest('Invalid page'));
                    }

                } else {
                    page = temp;
                }
            }

            if (_.has(request.query, config.query.limit.name)) {
                const temp = _.parseInt(request.query[config.query.limit.name]);
                if (_.isNaN(temp)) {
                    if (config.query.invalid === 'defaults') {
                        limit = config.query.limit.default;
                    } else {
                        return reply(Boom.badRequest('Invalid limit'));
                    }
                } else {
                    limit = temp;
                }
            }

            request.query[config.query.page.name] = page;
            request.query[config.query.limit.name] = limit;
        }

        return reply.continue();
    });

    /**
     * totalCount must be exposed to the request object if you wants
     * the links to works properly.
     * request.totalCount = totalCount;
     * where totalCount == config.meta.totalCount.name
     * If totalCount is not exposed through the request object, the following
     * metadata will be null :
     * - last link
     * - next link
     * - totalCount
     * - pageCount
     */
    server.ext('onPreResponse', function (request, reply) {

        if (request.response.isBoom || request.route.method !== 'get') {
            return reply.continue();
        }

        if (request.query[config.query.pagination.name] === false) {
            return reply.continue();
        } else {
            delete request.query[config.query.pagination.name];
        }

        const include = config.routes.include;
        const exclude = config.routes.exclude;
        const path = request.route.path;

        if ((include[0] === '*' || _.includes(include, path)) &&
            !_.includes(exclude, path)) {

            const temp = request.response.source;

            const results = Array.isArray(temp) ? request.response.source : request.response.source.results;
            Hoek.assert(Array.isArray(results), 'The results must be an array');
            const totalCount = request.response.source.totalCount || request[config.meta.totalCount.name];

            const baseUrl = internals.uri + request.url.pathname + '?';
            const qs = request.query;
            const qsPage = qs[config.query.page.name];

            const meta = {};

            const getPageCount = function() {
                return Math.trunc(totalCount / qs[config.query.limit.name]) +
                     ((totalCount % qs[config.query.limit.name] === 0) ? 0 : 1);
            };

            if (config.meta.page.active) {
                meta[config.query.page.name] = qs[config.query.page.name];
            }

            if (config.meta.limit.active) {
                meta[config.query.limit.name] = qs[config.query.limit.name];
            }

            if (config.meta.count.active) {
                meta[config.meta.count.name] = results.length;
            }

            if (config.meta.totalCount.active) {
                meta[config.meta.totalCount.name] = totalCount || null;
            }

            if (config.meta.pageCount.active) {
                let pageCount = null;

                if (totalCount) {
                    pageCount = getPageCount();
                }

                meta[config.meta.pageCount.name] = pageCount;
            }

            if (config.meta.self.active) {
                meta[config.meta.self.name] = baseUrl + Qs.stringify(qs);
            }

            const getUrl = function(page) {
                const override = {};
                override[config.query.page.name] = page;

                return baseUrl + Qs.stringify(Hoek.applyToDefaults(qs, override));
            };

            if (config.meta.previous.active) {
                let url = null;
                if (qsPage !== 1) {
                    url = getUrl(qsPage - 1);
                }

                meta[config.meta.previous.name] = url;
            }

            if (config.meta.next.active) {
                let url = null;

                if (totalCount) {
                    const pageCount = getPageCount();

                    if (qsPage < pageCount) {
                        url = getUrl(qsPage + 1);
                    }
                }

                meta[config.meta.next.name] = url;
            }

            if (config.meta.first.active) {
                meta[config.meta.first.name] = getUrl(1);
            }

            if (config.meta.last.active) {
                let url = null;

                if (totalCount) {
                    url = getUrl(getPageCount());
                }

                meta[config.meta.last.name] = url;
            }

            const response = {};
            response[config.meta.name] = meta;
            response[config.results.name] = results;
            request.response.source = response;

        }

        return reply.continue();

    });

    next();
};

exports.register.attributes = {
    name: require('./package.json').name,
    version: require('./package.json').version
};
<|endoftext|>


<commit_before>var through = require('through2'),
  gutil = require('gulp-util'),
  coveralls = require('coveralls');

module.exports = function (gulpOptions) {
  gulpOptions = gulpOptions || { filepath : '.' };

  return through.obj(function(file, enc, callback) {
    var stream = this;

    function handleError(done, err) {
      if (err){
        stream.emit('error', new gutil.PluginError('gulp-coveralls', err));
        done();
      }
    }

    function sendToCoverallsCallback(done, err, response, body) {
      handleError(done, err);
      if (response.statusCode >= 400){
        handleError(done, 'Bad response:' + response.statusCode + ' ' + body);
      } else {
        done();
      }
    }

    function sendToCoveralls(input, done) {
      coveralls.getBaseOptions(function(err, options){
        options.filepath = gulpOptions.filepath;
        coveralls.convertLcovToCoveralls(input, options, function(err, postData){
          handleError(done, err);
          coveralls.sendToCoveralls(postData, function(err, response, body){
            sendToCoverallsCallback(done, err, response, body);
          });
        });
      });
    }

    if (file.isNull()) {
      this.push(file);
      return callback();
    }

    if (file.isStream()) {
      this.emit('error', new gutil.PluginError('gulp-coveralls', 'Stream content is not supported'));
      return callback();
    }

    if (file.isBuffer()) {
      sendToCoveralls(file.contents.toString(), callback);
      this.push(file);
    }
  });
};
<commit_msg>Delete index.js<commit_after><|endoftext|>


<commit_before>import {HTML5Video, Log, Events} from 'clappr'
import shaka from 'shaka-player'

const SEND_STATS_AT = 30 * 1000

export default class DashShakaPlayback extends HTML5Video {
  get name() {return 'dash_shaka_playback'}

  get shakaVersion() {return shaka.player.Player.version}

  constructor(options) {
    super(options)
    this._readyToPlay = false

    var checkIfIsReady = (fn) => {
      return (arg) => {
        if (this._readyToPlay) return fn(arg)
      }
    }

    this.enableTextTrack = checkIfIsReady((enable) => this._player.enableTextTrack(enable))
    this.textTracks = checkIfIsReady(() => this._player.getTextTracks())
    this.selectTextTrack = checkIfIsReady((id) => this._player.selectTextTrack(id))
    this.audioTracks = checkIfIsReady(() => this._player.getAudioTracks())
    this.selectAudioTrack = checkIfIsReady((id) => this._player.selectAudioTrack(id))
    this.videoTracks = checkIfIsReady(() => this._player.getVideoTracks())
    this.selectVideoTrack = checkIfIsReady((id) => this._player.selectVideoTrack(id))

    this.getPlaybackType = checkIfIsReady(() => (this._player.isLive()?'live':'vod'))
  }

  render() {
    super.render()
    this._setup()
    return this
  }

  play() {
    if (!this._readyToPlay) {
      this.once(Events.PLAYBACK_READY, this.play)
      return
    }
    super.play()
  }

  // skipping ready event on video tag in favor of ready on shaka
  ready() {}

  // skipping error handling on video tag in favor of error on shaka
  error(event) { Log.error('an error was raised by the video tag', event, this.el.error)}

  isHighDefinitionInUse() { return !!this.highDefinition }

  stop() {
    clearInterval(this.sendStatsId)
    super.stop()
    this._sendStats()
  }

  destroy() {
    clearInterval(this.sendStatsId)
    this._player.destroy().
      then(() => this._destroy()).
      catch(() => {
        this._destroy()
        Log.error('shaka could not be destroyed')
      })
  }

  _setup() {
    this._player = this._createPlayer()

    var playerLoaded = this._player.load(new shaka.player.DashVideoSource(this.options.src))
    playerLoaded.then(() => this._loaded())
      .catch((e) => this._setupError(e))
  }

  _createPlayer() {
    var player = new shaka.player.Player(this.el)
    player.addEventListener('bufferingStart', () => this._bufferingHandler())
    player.addEventListener('bufferingEnd', () => this._bufferingFullHandler())
    player.addEventListener('error', (e) => this._error(e))
    player.addEventListener('adaptation', () => this._onAdaptation())
    return player
  }

  _loaded() {
    this._ready()
    this._startToSendStats()
  }

  _startToSendStats() {
    this.sendStatsId = setInterval(() => this._sendStats(), SEND_STATS_AT)
  }

  _sendStats() {this.trigger(Events.PLAYBACK_STATS_ADD, this._player.getStats())}

  _setupError(e) { this._error({detail: 'shaka could not be setup: ' + e}) }

  _bufferingHandler() { this.trigger(Events.PLAYBACK_BUFFERING, this.name) }

  _bufferingFullHandler() { this.trigger(Events.PLAYBACK_BUFFERFULL, this.name) }

  _error(error) {
    Log.error('an error was raised by shaka player', error.detail)
    this.trigger(Events.PLAYBACK_ERROR, error.detail, this.name)
  }

  _onAdaptation(event) {
    if (!!event.size) return

    Log.debug('an adaptation has happened:', event)
    this.highDefinition = (event.size.height >= 720)
    this.trigger(Events.PLAYBACK_HIGHDEFINITIONUPDATE, this.highDefinition)
    this.trigger(Events.PLAYBACK_BITRATE, {bitrate: event.size.height})
  }

  _destroy() {
    super.destroy()
    this._readyToPlay = false
    Log.debug('shaka was destroyed')
  }

  _ready() {
    this._readyToPlay = true
    super.ready()
  }
}

DashShakaPlayback.canPlay = function(resource, mimeType) {
  shaka.polyfill.installAll()

  if (!shaka.player.Player.isBrowserSupported()) {
    Log.debug('This browser does not support this video')
    return false
  }

  var resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || []
  return "mpd" === resourceParts[1]
}

<commit_msg>handle stop video<commit_after>import {HTML5Video, Log, Events} from 'clappr'
import shaka from 'shaka-player'

const SEND_STATS_AT = 30 * 1000

export default class DashShakaPlayback extends HTML5Video {
  get name() {return 'dash_shaka_playback'}

  get shakaVersion() {return shaka.player.Player.version}

  constructor(options) {
    super(options)
    this._readyToPlay = false

    var checkIfIsReady = (fn) => {
      return (arg) => {
        if (this._readyToPlay) return fn(arg)
      }
    }

    this.enableTextTrack = checkIfIsReady((enable) => this._player.enableTextTrack(enable))
    this.textTracks = checkIfIsReady(() => this._player.getTextTracks())
    this.selectTextTrack = checkIfIsReady((id) => this._player.selectTextTrack(id))
    this.audioTracks = checkIfIsReady(() => this._player.getAudioTracks())
    this.selectAudioTrack = checkIfIsReady((id) => this._player.selectAudioTrack(id))
    this.videoTracks = checkIfIsReady(() => this._player.getVideoTracks())
    this.selectVideoTrack = checkIfIsReady((id) => this._player.selectVideoTrack(id))

    this.getPlaybackType = checkIfIsReady(() => (this._player.isLive()?'live':'vod'))
  }

  play() {
    !this._player && this._setup()

    if (!this._readyToPlay) {
      this.once(Events.PLAYBACK_READY, this.play)
      return
    }
    super.play()
  }

  // skipping ready event on video tag in favor of ready on shaka
  ready() {}

  // skipping error handling on video tag in favor of error on shaka
  error(event) { Log.error('an error was raised by the video tag', event, this.el.error)}

  isHighDefinitionInUse() { return !!this.highDefinition }

  stop() {
    clearInterval(this.sendStatsId)
    this.trigger(Events.CONTAINER_STOP, this.name)
    this._sendStats()
    this._player.unload()
    this._player = null
    this._readyToPlay = false
  }

  destroy() {
    clearInterval(this.sendStatsId)
    this._player.destroy().
      then(() => this._destroy()).
      catch(() => {
        this._destroy()
        Log.error('shaka could not be destroyed')
      })
  }

  _setup() {
    this._player = this._createPlayer()

    var playerLoaded = this._player.load(new shaka.player.DashVideoSource(this.options.src))
    playerLoaded.then(() => this._loaded())
      .catch((e) => this._setupError(e))
  }

  _createPlayer() {
    var player = new shaka.player.Player(this.el)
    player.addEventListener('bufferingStart', () => this._bufferingHandler())
    player.addEventListener('bufferingEnd', () => this._bufferingFullHandler())
    player.addEventListener('error', (e) => this._error(e))
    player.addEventListener('adaptation', () => this._onAdaptation())
    return player
  }

  _loaded() {
    this._ready()
    this._startToSendStats()
  }

  _startToSendStats() {
    this.sendStatsId = setInterval(() => this._sendStats(), SEND_STATS_AT)
  }

  _sendStats() {this.trigger(Events.PLAYBACK_STATS_ADD, this._player.getStats())}

  _setupError(e) { this._error({detail: 'shaka could not be setup: ' + e}) }

  _bufferingHandler() { this.trigger(Events.PLAYBACK_BUFFERING, this.name) }

  _bufferingFullHandler() { this.trigger(Events.PLAYBACK_BUFFERFULL, this.name) }

  _error(error) {
    Log.error('an error was raised by shaka player', error.detail)
    this.trigger(Events.PLAYBACK_ERROR, error.detail, this.name)
  }

  _onAdaptation(event) {
    if (!!event.size) return

    Log.debug('an adaptation has happened:', event)
    this.highDefinition = (event.size.height >= 720)
    this.trigger(Events.PLAYBACK_HIGHDEFINITIONUPDATE, this.highDefinition)
    this.trigger(Events.PLAYBACK_BITRATE, {bitrate: event.size.height})
  }

  _destroy() {
    super.destroy()
    this._readyToPlay = false
    Log.debug('shaka was destroyed')
  }

  _ready() {
    this._readyToPlay = true
    super.ready()
  }
}

DashShakaPlayback.canPlay = function(resource, mimeType) {
  shaka.polyfill.installAll()

  if (!shaka.player.Player.isBrowserSupported()) {
    Log.debug('This browser does not support this video')
    return false
  }

  var resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || []
  return "mpd" === resourceParts[1]
}

<|endoftext|>