<commit_before>"use strict";
const
	logger = require("bunyan"),
	restify = require("restify"),
	errorHandler = require('express-error-handler')
	;

class RestifyServerManager {

	constructor(config){
		this.logger, this.server;
		this.versioningMiddleware = require("restify-versioning-middleware");
		this.config = mergeConfig(config);
		this.apiMonitorer = require('./libs/apiMonitorer')

		if(this.config.server && this.config.server.protocol === 'HTTPS'){
			if(this.config.server.tsl){
				this.config.server.tsl = {
					key: this.config.server.tsl.key || "certs/server-key.pem",
					cert: this.config.server.tsl.cert || "certs/server-cert.pem"
				}
			}else{
				this.config.server.tsl = {
					key: "certs/server-key.pem",
					cert: "certs/server-cert.pem"
				}
			}
		}

		if(this.config.logging.enabled){

			this.logger = logger.createLogger({
				name: this.config.name + "LOG",
				streams: [{
				   type: 'rotating-file',
				   src: true,
				   path: process.cwd() + this.config.logging.path +'/'+  this.config.name +'_'+ "LOG" +'.log',
				   period: this.config.logging.rotation_period,   // daily rotation
				   count: this.config.logging.back_copies_count  // back copies
				}],
				level: this.config.logging.level
			  })

			this.config.log = this.logger;
        	this.config.log.info('Server started with log level %s', config.logging.level);
		}
	}

	createServer(){
	let itself = this;
      if (this.config.server.protocol === "HTTPS" || this.config.server.protocol === "HTTP") {
        this.server = restify.createServer(this.config);
        } else {
            throw new TypeError("Missing protocol or invalid value: " + this.config.server.protocol);
          }

		// Error handlers
		let handleError = errorHandler({
			server: this.server,
			framework: 'restify'
		})

		// Since restify error handlers take error last, and process 'uncaughtError' sends error first, you'll need another one for process exceptions.
		// Don't pass the framework: 'restify' setting this time:
		let handleProcessError = errorHandler({	server: this.server	})

		// This is called when an error is accidentally thrown. Under the hood, it uses Node's domain module for error handling, which limits the
		// scope of the domain to the request / response cycle. Restify hooks up the domain for you, so you don't have to worry about binding request and response to the domain.
		this.server.on('uncaughtException', handleError);

		// We should still listen for process errors and shut down if we catch them. This handler will try to let server connections drain, first,
		// and invoke your custom handlers if you have any defined.
		process.on('uncaughtException', handleProcessError);

		// PRE handlers
		this.server.pre(restify.pre.sanitizePath());
		this.server.pre(this.versioningMiddleware().unless({path: this.config.exceptedRoutes}));

		// Set up adhoc pre handlers
		if(this.config.preHandlers.length > 0){
			this.config.preHandlers.map(
				item => itself.server.pre(item)
			)
		}

		// Uses for all routes
		this.server.use(restify.acceptParser(this.server.acceptable ));
		this.server.use(restify.queryParser());
		this.server.use(restify.bodyParser());
		this.server.use(restify.gzipResponse());
		this.server.use(restify.fullResponse());
		this.server.use(restify.requestLogger({}));
		this.server.use(noCacheMiddleware);

		// Set up adhoc uses
		if(this.config.uses.length > 0){
			this.config.uses.map(
				item => itself.server.use(item)
			)
		}

		//Set up heartbeat route
		if(this.config.heartbeat){
			this.server.get({ path: '/'}, function(req, res, next) {
				return next(res.json({name: itself.config.name , version: itself.config.version , date: new Date() }));
			});
		}

		//Set up routes
		if(this.config.routes.length > 0){
			this.config.routes.map(
				item => require(process.cwd() + item)(itself.server, restify, itself.logger, itself.config)
			)
		}

		// Set up after hooks
		if(this.config.monitorer.enabled) this.server.on('after', this.apiMonitorer(this.config));

		// Set up adhoc after hooks
		if(this.config.afterHooks.length > 0){
			this.config.afterHooks.map(
				item => itself.server.on('after', item(itself.config))
			)
		}

		// Set up Audit logger
		if(this.config.logging.audit){
			this.server.on('after', restify.auditLogger({
			   log: logger.createLogger({
				 name: itself.config.name + 'AUDIT',
				 streams: [{
					type: 'rotating-file',
					path: process.cwd() + itself.config.logging.path +'/'+ itself.config.name +'_'+ 'AUDIT' +'.log',
					period: itself.config.logging.rotation_period,   // daily rotation
					count: itself.config.logging.back_copies_count  // back copies
				}]
			   })
			  }));
		}

		return this.server;
	}

	startServer(){
	 let itself = this;
	 return this.server.listen(this.config.server.port, function () {
       console.log(itself.config.server.protocol + " Server listening on port: " + itself.config.server.port);
     });
	}

	stopServer(){
	 return this.server.close();
	}
}

module.exports = {
 RestifyServerManager: RestifyServerManager,
 errorHandler: errorHandler
}

/**
 * @private
 * @memberOf ServerSetup
 * Applies API global headers to avoid caching
 * @returns {Void}
 */
var noCacheMiddleware = function noCacheMiddleware(req, res, next) {
	res.header("Cache-Control", "no-cache, no-store, must-revalidate");
	res.header("Pragma", "no-cache");
	res.header("Expires", -1);
	res.header("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
	res.header("X-Content-Type-Options", "no sniff");
	next();
}

var mergeConfig = function (config) {
	 let paramConf = config || {};
     let _conf =  {
		    name: paramConf.name || 'Default',
		    version: paramConf.version || '1.0.0',
			preHandlers: paramConf.preHandlers || [],
			uses: paramConf.uses || [],
			monitorer: {
				enabled: (paramConf.monitorer && paramConf.monitorer.enabled)?paramConf.monitorer.enabled:false,
				basepath: (paramConf.monitorer && paramConf.monitorer.basepath)?paramConf.monitorer.basepath:'http://127.0.0.1:8080',
				urlpath: (paramConf.monitorer && paramConf.monitorer.urlpath)?paramConf.monitorer.urlpath:'/api/v1/log',
			},
			formatters: paramConf.formatters || {},
			afterHooks: paramConf.afterHooks || [],
			heartbeat: paramConf.heartbeat || true,
			exceptedRoutes: paramConf.exceptedRoutes || [],
			routes: paramConf.routes || [],
			logging: {
				enabled: (paramConf.logging && paramConf.logging.enabled)?paramConf.logging.enabled:false,
				level: (paramConf.logging && paramConf.logging.level)?paramConf.logging.level:"info",
				audit: (paramConf.logging && paramConf.logging.audit)?paramConf.logging.audit:false,
				path: (paramConf.logging && paramConf.logging.path)?paramConf.logging.path:"logs",
				rotation_period: (paramConf.logging && paramConf.logging.rotation_period)?paramConf.logging.rotation_period:"15m",
				back_copies_count: (paramConf.logging && paramConf.logging.back_copies_count)?paramConf.logging.back_copies_count:2
			},
			server:
			{
				port: (paramConf.server && paramConf.server.port)?paramConf.server.port:8480,
				protocol: (paramConf.server && paramConf.server.protocol)?paramConf.server.protocol:'HTTP',
				url: (paramConf.server && paramConf.server.url)?paramConf.server.url:"http://0.0.0.1:8480"
			}
  	    };
	return _conf;
}

<commit_msg>MethodNotAllowed, VersionNotAllowed, UnsupportedMediaType and NotFound custom error handling due to restify behaviour<commit_after>"use strict";
const
	logger = require("bunyan"),
	restify = require("restify"),
	errorHandler = require('express-error-handler')
	;

class RestifyServerManager {

	constructor(config){
		this.logger, this.server;
		this.versioningMiddleware = require("restify-versioning-middleware");
		this.config = mergeConfig(config);
		this.apiMonitorer = require('./libs/apiMonitorer')

		if(this.config.server && this.config.server.protocol === 'HTTPS'){
			if(this.config.server.tsl){
				this.config.server.tsl = {
					key: this.config.server.tsl.key || "certs/server-key.pem",
					cert: this.config.server.tsl.cert || "certs/server-cert.pem"
				}
			}else{
				this.config.server.tsl = {
					key: "certs/server-key.pem",
					cert: "certs/server-cert.pem"
				}
			}
		}

		if(this.config.logging.enabled){

			this.logger = logger.createLogger({
				name: this.config.name + "LOG",
				streams: [{
				   type: 'rotating-file',
				   src: true,
				   path: process.cwd() + this.config.logging.path +'/'+  this.config.name +'_'+ "LOG" +'.log',
				   period: this.config.logging.rotation_period,   // daily rotation
				   count: this.config.logging.back_copies_count  // back copies
				}],
				level: this.config.logging.level
			  })

			this.config.log = this.logger;
        	this.config.log.info('Server started with log level %s', config.logging.level);
		}
	}

	createServer(){
	let itself = this;
      if (this.config.server.protocol === "HTTPS" || this.config.server.protocol === "HTTP") {
        this.server = restify.createServer(this.config);
        } else {
            throw new TypeError("Missing protocol or invalid value: " + this.config.server.protocol);
          }

		// Error handlers
		let handleError = errorHandler({
			server: this.server,
			framework: 'restify'
		})

		// Since restify error handlers take error last, and process 'uncaughtError' sends error first, you'll need another one for process exceptions.
		// Don't pass the framework: 'restify' setting this time:
		let handleProcessError = errorHandler({	server: this.server	})

		// This is called when an error is accidentally thrown. Under the hood, it uses Node's domain module for error handling, which limits the
		// scope of the domain to the request / response cycle. Restify hooks up the domain for you, so you don't have to worry about binding request and response to the domain.
		this.server.on('uncaughtException', handleError);

		// This is required as MethodNotAllowed, VersionNotAllowed, UnsupportedMediaType and NotFound sends the response before any error handling can occur
        // so it was stopping the process as no headers can be set when response is already sent
        this.server.on('MethodNotAllowed', (request, response, cb) => response.send(cb));
        this.server.on('NotFound', (request, response, cb) => response.send(cb));
        this.server.on('VersionNotAllowed', (request, response, cb) => response.send(cb));
        this.server.on('UnsupportedMediaType', (request, response, cb) => response.send(cb));

		// We should still listen for process errors and shut down if we catch them. This handler will try to let server connections drain, first,
		// and invoke your custom handlers if you have any defined.
		process.on('uncaughtException', handleProcessError);

		// PRE handlers
		this.server.pre(restify.pre.sanitizePath());
		this.server.pre(this.versioningMiddleware().unless({path: this.config.exceptedRoutes}));

		// Set up adhoc pre handlers
		if(this.config.preHandlers.length > 0){
			this.config.preHandlers.map(
				item => itself.server.pre(item)
			)
		}

		// Uses for all routes
		this.server.use(restify.acceptParser(this.server.acceptable ));
		this.server.use(restify.queryParser());
		this.server.use(restify.bodyParser());
		this.server.use(restify.gzipResponse());
		this.server.use(restify.fullResponse());
		this.server.use(restify.requestLogger({}));
		this.server.use(noCacheMiddleware);

		// Set up adhoc uses
		if(this.config.uses.length > 0){
			this.config.uses.map(
				item => itself.server.use(item)
			)
		}

		//Set up heartbeat route
		if(this.config.heartbeat){
			this.server.get({ path: '/'}, function(req, res, next) {
				return next(res.json({name: itself.config.name , version: itself.config.version , date: new Date() }));
			});
		}

		//Set up routes
		if(this.config.routes.length > 0){
			this.config.routes.map(
				item => require(process.cwd() + item)(itself.server, restify, itself.logger, itself.config)
			)
		}

		// Set up after hooks
		if(this.config.monitorer.enabled) this.server.on('after', this.apiMonitorer(this.config));

		// Set up adhoc after hooks
		if(this.config.afterHooks.length > 0){
			this.config.afterHooks.map(
				item => itself.server.on('after', item(itself.config))
			)
		}

		// Set up Audit logger
		if(this.config.logging.audit){
			this.server.on('after', restify.auditLogger({
			   log: logger.createLogger({
				 name: itself.config.name + 'AUDIT',
				 streams: [{
					type: 'rotating-file',
					path: process.cwd() + itself.config.logging.path +'/'+ itself.config.name +'_'+ 'AUDIT' +'.log',
					period: itself.config.logging.rotation_period,   // daily rotation
					count: itself.config.logging.back_copies_count  // back copies
				}]
			   })
			  }));
		}

		return this.server;
	}

	startServer(){
	 let itself = this;
	 return this.server.listen(this.config.server.port, function () {
       console.log(itself.config.server.protocol + " Server listening on port: " + itself.config.server.port);
     });
	}

	stopServer(){
	 return this.server.close();
	}
}

module.exports = {
 RestifyServerManager: RestifyServerManager,
 errorHandler: errorHandler
}

/**
 * @private
 * @memberOf ServerSetup
 * Applies API global headers to avoid caching
 * @returns {Void}
 */
var noCacheMiddleware = function noCacheMiddleware(req, res, next) {
	res.header("Cache-Control", "no-cache, no-store, must-revalidate");
	res.header("Pragma", "no-cache");
	res.header("Expires", -1);
	res.header("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
	res.header("X-Content-Type-Options", "no sniff");
	next();
}

var mergeConfig = function (config) {
	 let paramConf = config || {};
     let _conf =  {
		    name: paramConf.name || 'Default',
		    version: paramConf.version || '1.0.0',
			preHandlers: paramConf.preHandlers || [],
			uses: paramConf.uses || [],
			monitorer: {
				enabled: (paramConf.monitorer && paramConf.monitorer.enabled)?paramConf.monitorer.enabled:false,
				basepath: (paramConf.monitorer && paramConf.monitorer.basepath)?paramConf.monitorer.basepath:'http://127.0.0.1:8080',
				urlpath: (paramConf.monitorer && paramConf.monitorer.urlpath)?paramConf.monitorer.urlpath:'/api/v1/log',
			},
			formatters: paramConf.formatters || {},
			afterHooks: paramConf.afterHooks || [],
			heartbeat: paramConf.heartbeat || true,
			exceptedRoutes: paramConf.exceptedRoutes || [],
			routes: paramConf.routes || [],
			logging: {
				enabled: (paramConf.logging && paramConf.logging.enabled)?paramConf.logging.enabled:false,
				level: (paramConf.logging && paramConf.logging.level)?paramConf.logging.level:"info",
				audit: (paramConf.logging && paramConf.logging.audit)?paramConf.logging.audit:false,
				path: (paramConf.logging && paramConf.logging.path)?paramConf.logging.path:"logs",
				rotation_period: (paramConf.logging && paramConf.logging.rotation_period)?paramConf.logging.rotation_period:"15m",
				back_copies_count: (paramConf.logging && paramConf.logging.back_copies_count)?paramConf.logging.back_copies_count:2
			},
			server:
			{
				port: (paramConf.server && paramConf.server.port)?paramConf.server.port:8480,
				protocol: (paramConf.server && paramConf.server.protocol)?paramConf.server.protocol:'HTTP',
				url: (paramConf.server && paramConf.server.url)?paramConf.server.url:"http://0.0.0.1:8480"
			}
  	    };
	return _conf;
}

<|endoftext|>


<commit_before>module.exports = WebTorrent

var search = require('kat-search.ph')
var createTorrent = require('create-torrent')
var debug = require('debug')('webtorrent')
var DHT = require('bittorrent-dht/client') // browser exclude
var EventEmitter = require('events').EventEmitter
var extend = require('xtend')
var hat = require('hat')
var inherits = require('inherits')
var loadIPSet = require('load-ip-set') // browser exclude
var parallel = require('run-parallel')
var parseTorrent = require('parse-torrent')
var speedometer = require('speedometer')
var zeroFill = require('zero-fill')
var path = require('path')

var Torrent = require('./lib/torrent')

inherits(WebTorrent, EventEmitter)

var VERSION = require('./package.json').version

/**
 * BitTorrent client version string (used in peer ID).
 * Generated from package.json major and minor version. For example:
 *   '0.16.1' -> '0016'
 *   '1.2.5' -> '0102'
 */
var VERSION_STR = VERSION.match(/([0-9]+)/g).slice(0, 2).map(zeroFill(2)).join('')

/**
 * WebTorrent Client
 * @param {Object} opts
 */
function WebTorrent (opts) {
  var self = this
  if (!(self instanceof WebTorrent)) return new WebTorrent(opts)
  if (!opts) opts = {}
  EventEmitter.call(self)
  if (!debug.enabled) self.setMaxListeners(0)

  self.destroyed = false
  self.torrentPort = opts.torrentPort || 0
  self.tracker = opts.tracker !== undefined ? opts.tracker : true

  self._rtcConfig = opts.rtcConfig
  self._wrtc = opts.wrtc || global.WRTC // to support `webtorrent-hybrid` package

  self.torrents = []

  self.downloadSpeed = speedometer()
  self.uploadSpeed = speedometer()

  self.peerId = opts.peerId === undefined
    ? new Buffer('-WW' + VERSION_STR + '-' + hat(48), 'utf8')
    : typeof opts.peerId === 'string'
      ? new Buffer(opts.peerId, 'hex')
      : opts.peerId
  self.peerIdHex = self.peerId.toString('hex')

  self.nodeId = opts.nodeId === undefined
    ? new Buffer(hat(160), 'hex')
    : typeof opts.nodeId === 'string'
      ? new Buffer(opts.nodeId, 'hex')
      : opts.nodeId
  self.nodeIdHex = self.nodeId.toString('hex')

  if (opts.dht !== false && typeof DHT === 'function' /* browser exclude */) {
    // use a single DHT instance for all torrents, so the routing table can be reused
    self.dht = new DHT(extend({ nodeId: self.nodeId }, opts.dht))
    self.dht.listen(opts.dhtPort)
  }

  debug('new webtorrent (peerId %s, nodeId %s)', self.peerIdHex, self.nodeIdHex)

  if (typeof loadIPSet === 'function') {
    loadIPSet(opts.blocklist, {
      headers: { 'user-agent': 'WebTorrent/' + VERSION + ' (http://webtorrent.io)' }
    }, function (err, ipSet) {
      if (err) return self.error('Failed to load blocklist: ' + err.message)
      self.blocked = ipSet
      ready()
    })
  } else process.nextTick(ready)

  function ready () {
    if (self.destroyed) return
    self.ready = true
    self.emit('ready')
  }
}

/**
 * Seed ratio for all torrents in the client.
 * @type {number}
 */
Object.defineProperty(WebTorrent.prototype, 'ratio', {
  get: function () {
    var self = this
    var uploaded = self.torrents.reduce(function (total, torrent) {
      return total + torrent.uploaded
    }, 0)
    var downloaded = self.torrents.reduce(function (total, torrent) {
      return total + torrent.downloaded
    }, 0) || 1
    return uploaded / downloaded
  }
})

WebTorrent.prototype.getBySearch = function (query) {
  var self = this
  if(!query) return

  search(query).then(function(search_results) {
    search_results = search_results.slice(0, 9).filter(function(r){ if(r.torrent || r.magnet) return })
    return self.get(search_results[0].magnet)
  }
}

/**
 * Returns the torrent with the given `torrentId`. Convenience method. Easier than
 * searching through the `client.torrents` array. Returns `null` if no matching torrent
 * found.
 *
 * @param  {string|Buffer|Object|Torrent} torrentId
 * @return {Torrent|null}
 */
WebTorrent.prototype.get = function (torrentId) {
  var self = this
  if (torrentId instanceof Torrent) return torrentId

  var parsed
  try { parsed = parseTorrent(torrentId) } catch (err) {}

  if (!parsed) return null
  if (!parsed.infoHash) throw new Error('Invalid torrent identifier')

  for (var i = 0, len = self.torrents.length; i < len; i++) {
    var torrent = self.torrents[i]
    if (torrent.infoHash === parsed.infoHash) return torrent
  }
  return null
}

/**
 * Start downloading a new torrent. Aliased as `client.download`.
 * @param {string|Buffer|Object} torrentId
 * @param {Object} opts torrent-specific options
 * @param {function=} ontorrent called when the torrent is ready (has metadata)
 */
WebTorrent.prototype.add =
WebTorrent.prototype.download = function (torrentId, opts, ontorrent) {
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')
  if (typeof opts === 'function') return self.add(torrentId, opts, null)
  debug('add')
  if (!opts) opts = {}
  else opts = extend({}, opts)

  opts.client = self

  var torrent = self.get(torrentId)

  function _ontorrent () {
    debug('on torrent %s', torrent.infoHash)
    if (typeof ontorrent === 'function') ontorrent(torrent)
  }

  if (torrent) {
    if (torrent.ready) process.nextTick(_ontorrent)
    else torrent.on('ready', _ontorrent)
  } else {
    torrent = new Torrent(torrentId, opts)
    self.torrents.push(torrent)

    torrent.on('error', function (err) {
      self.emit('error', err, torrent)
      self.remove(torrent)
    })

    torrent.on('listening', function (port) {
      self.emit('listening', port, torrent)
    })

    torrent.on('paused', function (port) {
      self.emit('paused', port, torrent)
    })

    torrent.on('resume', function (port) {
      self.emit('resume', torrent)
    })

    torrent.on('infoHash', function () {
      self.emit('infoHash', torrent)
    })

    torrent.on('ready', function () {
      _ontorrent()
      self.emit('torrent', torrent)
    })
  }

  return torrent
}

WebTorrent.prototype.pause = function(currentTorrent){
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')

  if (currentTorrent === null) throw new Error('torrent does not exist')

  currentTorrent.pause();
}

WebTorrent.prototype.resume = function(currentTorrent){
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')

  if (currentTorrent === null) throw new Error('torrent does not exist')

  currentTorrent.resume();
}

/**
 * Start seeding a new file/folder.
 * @param  {string|File|FileList|Buffer|Array.<string|File|Buffer>} input
 * @param  {Object=} opts
 * @param  {function=} onseed
 */
WebTorrent.prototype.seed = function (input, opts, onseed) {
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')
  if (typeof opts === 'function') return self.seed(input, null, opts)
  debug('seed')
  if (!opts) opts = {}
  else opts = extend({}, opts)

  // When seeding from filesystem, initialize store from that path (avoids a copy)
  if (typeof input === 'string') opts.path = path.dirname(input)
  if (!opts.createdBy) opts.createdBy = 'WebTorrent/' + VERSION

  var streams
  var torrent = self.add(undefined, opts, function (torrent) {
    var tasks = [
      function (cb) {
        torrent.load(streams, cb)
      }
    ]
    if (self.dht) {
      tasks.push(function (cb) {
        torrent.on('dhtAnnounce', cb)
      })
    }
    parallel(tasks, function (err) {
      if (err) return self.emit('error', err)
      _onseed()
      self.emit('seed', torrent)
    })
  })

  createTorrent.parseInput(input, opts, function (err, files) {
    if (err) return self.emit('error', err)
    streams = files.map(function (file) { return file.getStream })

    createTorrent(input, opts, function (err, torrentBuf) {
      if (err) return self.emit('error', err)
      if (self.destroyed) return

      var existingTorrent = self.get(torrentBuf)
      if (existingTorrent) {
        torrent.destroy()
        _onseed()
        return
      } else {
        torrent._onTorrentId(torrentBuf)
      }
    })
  })

  function _onseed () {
    debug('on seed')
    if (typeof onseed === 'function') onseed(torrent)
  }

  return torrent
}

/**
 * Remove a torrent from the client.
 * @param  {string|Buffer|Torrent}   torrentId
 * @param  {function} cb
 */
WebTorrent.prototype.remove = function (torrentId, cb) {
  var self = this
  debug('remove')

  var torrent = self.get(torrentId)
  if (!torrent) throw new Error('No torrent with id ' + torrentId)

  self.torrents.splice(self.torrents.indexOf(torrent), 1)
  torrent.destroy(cb)
}

WebTorrent.prototype.address = function () {
  var self = this
  return { address: '0.0.0.0', family: 'IPv4', port: self.torrentPort }
}

/**
 * Destroy the client, including all torrents and connections to peers.
 * @param  {function} cb
 */
WebTorrent.prototype.destroy = function (cb) {
  var self = this
  if (self.destroyed) throw new Error('client already destroyed')
  self.destroyed = true
  debug('destroy')

  var tasks = self.torrents.map(function (torrent) {
    return function (cb) { self.remove(torrent, cb) }
  })

  if (self.dht) tasks.push(function (cb) { self.dht.destroy(cb) })

  parallel(tasks, cb)
}
<commit_msg>added documentation for getBySearch<commit_after>module.exports = WebTorrent

var search = require('kat-search.ph')
var createTorrent = require('create-torrent')
var debug = require('debug')('webtorrent')
var DHT = require('bittorrent-dht/client') // browser exclude
var EventEmitter = require('events').EventEmitter
var extend = require('xtend')
var hat = require('hat')
var inherits = require('inherits')
var loadIPSet = require('load-ip-set') // browser exclude
var parallel = require('run-parallel')
var parseTorrent = require('parse-torrent')
var speedometer = require('speedometer')
var zeroFill = require('zero-fill')
var path = require('path')

var Torrent = require('./lib/torrent')

inherits(WebTorrent, EventEmitter)

var VERSION = require('./package.json').version

/**
 * BitTorrent client version string (used in peer ID).
 * Generated from package.json major and minor version. For example:
 *   '0.16.1' -> '0016'
 *   '1.2.5' -> '0102'
 */
var VERSION_STR = VERSION.match(/([0-9]+)/g).slice(0, 2).map(zeroFill(2)).join('')

/**
 * WebTorrent Client
 * @param {Object} opts
 */
function WebTorrent (opts) {
  var self = this
  if (!(self instanceof WebTorrent)) return new WebTorrent(opts)
  if (!opts) opts = {}
  EventEmitter.call(self)
  if (!debug.enabled) self.setMaxListeners(0)

  self.destroyed = false
  self.torrentPort = opts.torrentPort || 0
  self.tracker = opts.tracker !== undefined ? opts.tracker : true

  self._rtcConfig = opts.rtcConfig
  self._wrtc = opts.wrtc || global.WRTC // to support `webtorrent-hybrid` package

  self.torrents = []

  self.downloadSpeed = speedometer()
  self.uploadSpeed = speedometer()

  self.peerId = opts.peerId === undefined
    ? new Buffer('-WW' + VERSION_STR + '-' + hat(48), 'utf8')
    : typeof opts.peerId === 'string'
      ? new Buffer(opts.peerId, 'hex')
      : opts.peerId
  self.peerIdHex = self.peerId.toString('hex')

  self.nodeId = opts.nodeId === undefined
    ? new Buffer(hat(160), 'hex')
    : typeof opts.nodeId === 'string'
      ? new Buffer(opts.nodeId, 'hex')
      : opts.nodeId
  self.nodeIdHex = self.nodeId.toString('hex')

  if (opts.dht !== false && typeof DHT === 'function' /* browser exclude */) {
    // use a single DHT instance for all torrents, so the routing table can be reused
    self.dht = new DHT(extend({ nodeId: self.nodeId }, opts.dht))
    self.dht.listen(opts.dhtPort)
  }

  debug('new webtorrent (peerId %s, nodeId %s)', self.peerIdHex, self.nodeIdHex)

  if (typeof loadIPSet === 'function') {
    loadIPSet(opts.blocklist, {
      headers: { 'user-agent': 'WebTorrent/' + VERSION + ' (http://webtorrent.io)' }
    }, function (err, ipSet) {
      if (err) return self.error('Failed to load blocklist: ' + err.message)
      self.blocked = ipSet
      ready()
    })
  } else process.nextTick(ready)

  function ready () {
    if (self.destroyed) return
    self.ready = true
    self.emit('ready')
  }
}

/**
 * Seed ratio for all torrents in the client.
 * @type {number}
 */
Object.defineProperty(WebTorrent.prototype, 'ratio', {
  get: function () {
    var self = this
    var uploaded = self.torrents.reduce(function (total, torrent) {
      return total + torrent.uploaded
    }, 0)
    var downloaded = self.torrents.reduce(function (total, torrent) {
      return total + torrent.downloaded
    }, 0) || 1
    return uploaded / downloaded
  }
})

/**
 * Searchs by query and downloads the first torrent that most closely matches with `query`.
 *
 * @param  {string} query
 * @return {Torrent|null}
 */
WebTorrent.prototype.getBySearch = function (query) {
  var self = this
  if(!query) return null

  search(query).then(function(search_results) {
    search_results = search_results.slice(0, 9).filter(function(r){ if(r.torrent || r.magnet) return })
    return self.get(search_results[0].magnet)
  }
}

/**
 * Returns the torrent with the given `torrentId`. Convenience method. Easier than
 * searching through the `client.torrents` array. Returns `null` if no matching torrent
 * found.
 *
 * @param  {string|Buffer|Object|Torrent} torrentId
 * @return {Torrent|null}
 */
WebTorrent.prototype.get = function (torrentId) {
  var self = this
  if (torrentId instanceof Torrent) return torrentId

  var parsed
  try { parsed = parseTorrent(torrentId) } catch (err) {}

  if (!parsed) return null
  if (!parsed.infoHash) throw new Error('Invalid torrent identifier')

  for (var i = 0, len = self.torrents.length; i < len; i++) {
    var torrent = self.torrents[i]
    if (torrent.infoHash === parsed.infoHash) return torrent
  }
  return null
}

/**
 * Start downloading a new torrent. Aliased as `client.download`.
 * @param {string|Buffer|Object} torrentId
 * @param {Object} opts torrent-specific options
 * @param {function=} ontorrent called when the torrent is ready (has metadata)
 */
WebTorrent.prototype.add =
WebTorrent.prototype.download = function (torrentId, opts, ontorrent) {
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')
  if (typeof opts === 'function') return self.add(torrentId, opts, null)
  debug('add')
  if (!opts) opts = {}
  else opts = extend({}, opts)

  opts.client = self

  var torrent = self.get(torrentId)

  function _ontorrent () {
    debug('on torrent %s', torrent.infoHash)
    if (typeof ontorrent === 'function') ontorrent(torrent)
  }

  if (torrent) {
    if (torrent.ready) process.nextTick(_ontorrent)
    else torrent.on('ready', _ontorrent)
  } else {
    torrent = new Torrent(torrentId, opts)
    self.torrents.push(torrent)

    torrent.on('error', function (err) {
      self.emit('error', err, torrent)
      self.remove(torrent)
    })

    torrent.on('listening', function (port) {
      self.emit('listening', port, torrent)
    })

    torrent.on('paused', function (port) {
      self.emit('paused', port, torrent)
    })

    torrent.on('resume', function (port) {
      self.emit('resume', torrent)
    })

    torrent.on('infoHash', function () {
      self.emit('infoHash', torrent)
    })

    torrent.on('ready', function () {
      _ontorrent()
      self.emit('torrent', torrent)
    })
  }

  return torrent
}

WebTorrent.prototype.pause = function(currentTorrent){
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')

  if (currentTorrent === null) throw new Error('torrent does not exist')

  currentTorrent.pause();
}

WebTorrent.prototype.resume = function(currentTorrent){
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')

  if (currentTorrent === null) throw new Error('torrent does not exist')

  currentTorrent.resume();
}

/**
 * Start seeding a new file/folder.
 * @param  {string|File|FileList|Buffer|Array.<string|File|Buffer>} input
 * @param  {Object=} opts
 * @param  {function=} onseed
 */
WebTorrent.prototype.seed = function (input, opts, onseed) {
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')
  if (typeof opts === 'function') return self.seed(input, null, opts)
  debug('seed')
  if (!opts) opts = {}
  else opts = extend({}, opts)

  // When seeding from filesystem, initialize store from that path (avoids a copy)
  if (typeof input === 'string') opts.path = path.dirname(input)
  if (!opts.createdBy) opts.createdBy = 'WebTorrent/' + VERSION

  var streams
  var torrent = self.add(undefined, opts, function (torrent) {
    var tasks = [
      function (cb) {
        torrent.load(streams, cb)
      }
    ]
    if (self.dht) {
      tasks.push(function (cb) {
        torrent.on('dhtAnnounce', cb)
      })
    }
    parallel(tasks, function (err) {
      if (err) return self.emit('error', err)
      _onseed()
      self.emit('seed', torrent)
    })
  })

  createTorrent.parseInput(input, opts, function (err, files) {
    if (err) return self.emit('error', err)
    streams = files.map(function (file) { return file.getStream })

    createTorrent(input, opts, function (err, torrentBuf) {
      if (err) return self.emit('error', err)
      if (self.destroyed) return

      var existingTorrent = self.get(torrentBuf)
      if (existingTorrent) {
        torrent.destroy()
        _onseed()
        return
      } else {
        torrent._onTorrentId(torrentBuf)
      }
    })
  })

  function _onseed () {
    debug('on seed')
    if (typeof onseed === 'function') onseed(torrent)
  }

  return torrent
}

/**
 * Remove a torrent from the client.
 * @param  {string|Buffer|Torrent}   torrentId
 * @param  {function} cb
 */
WebTorrent.prototype.remove = function (torrentId, cb) {
  var self = this
  debug('remove')

  var torrent = self.get(torrentId)
  if (!torrent) throw new Error('No torrent with id ' + torrentId)

  self.torrents.splice(self.torrents.indexOf(torrent), 1)
  torrent.destroy(cb)
}

WebTorrent.prototype.address = function () {
  var self = this
  return { address: '0.0.0.0', family: 'IPv4', port: self.torrentPort }
}

/**
 * Destroy the client, including all torrents and connections to peers.
 * @param  {function} cb
 */
WebTorrent.prototype.destroy = function (cb) {
  var self = this
  if (self.destroyed) throw new Error('client already destroyed')
  self.destroyed = true
  debug('destroy')

  var tasks = self.torrents.map(function (torrent) {
    return function (cb) { self.remove(torrent, cb) }
  })

  if (self.dht) tasks.push(function (cb) { self.dht.destroy(cb) })

  parallel(tasks, cb)
}
<|endoftext|>


<commit_before>'use strict';

const exec = require('child_process').exec;
const pify = require('pify');

const f = function (cb) {
	exec('ps -e | grep -E \'^.* xfce4-session$\'', (error, stdout) => {
		if (error) {
			cb(null, false);
		}
		if ((stdout).length > 0) {
			cb(null, true);
		}
		cb(null, false);
	});
};

module.exports = pify(f);
<commit_msg>refactor: use destructuring when possible<commit_after>'use strict';

const {exec} = require('child_process');
const pify = require('pify');

const f = function (cb) {
	exec('ps -e | grep -E \'^.* xfce4-session$\'', (error, stdout) => {
		if (error) {
			cb(null, false);
		}
		if ((stdout).length > 0) {
			cb(null, true);
		}
		cb(null, false);
	});
};

module.exports = pify(f);
<|endoftext|>


<commit_before>'use strict';
const got = require('got');
const cheerio = require('cheerio');
const uncss = require('uncss');
const pify = require('pify');

const uncssP = pify(uncss);
const fixtureURL = 'https://github.com/sindresorhus/generate-github-markdown-css/blob/master/fixture.md';

const getCSS = () => got('https://github.com').then(response => {
	const ret = [];
	const $ = cheerio.load(response.body);

	$('link[href$=".css"]').each((i, el) => {
		ret.push(el.attribs.href);
	});

	if (ret.length === 0) {
		throw new Error('Could not find GitHub stylesheets');
	}

	return ret;
});

const getRenderedFixture = () => got(fixtureURL).then(response => {
	const $ = cheerio.load(response.body);
	return $('.markdown-body').parent().html();
});

const cleanupCss = str => {
	const css = require('css');

	const style = css.parse(str);
	const mdBodyProps = [];

	style.stylesheet.rules = style.stylesheet.rules.filter(el => {
		if (el.type === 'keyframes' || el.type === 'comment' || el.type === 'font-face') {
			return false;
		}

		if (el.type === 'rule') {
			if (/::-webkit-validation|[:\-]placeholder$|^\.integrations-slide-content|^\.prose-diff|@font-face|^button::|^article$|^.plan-choice|^.plan-price|^.plan-notice|\.site-search|^::-webkit-file-upload-button$|^input::-webkit-outer-spin-button$/.test(el.selectors[0])) {
				return false;
			}

			// work around GitHub Markdown API inconsistency #10
			if (el.selectors[0] === '.task-list-item-checkbox') {
				el.selectors[0] = '.task-list-item input';
			}

			// remove `body` from `body, input {}`
			if (el.selectors[0] === 'body' && el.selectors[1] === 'input') {
				el.selectors.shift();
			}

			// rename the .octoicons font
			if (el.selectors[0] === '.octicon') {
				el.declarations = el.declarations.map(el => {
					if (el.property === 'font') {
						el.value += '-link';
					}

					return el;
				});
			}

			if (el.selectors.length === 1 && /^(?:html|body)$/.test(el.selectors[0])) {
				// remove everything from body/html other than these
				el.declarations = el.declarations.filter(x => /^(?:line-height|color)$|text-size-adjust$/.test(x.property));
			}

			el.selectors = el.selectors.map(selector => {
				if (/^(?:body|html)$/.test(selector)) {
					selector = '.markdown-body';
				}

				if (!/\.markdown-body/.test(selector)) {
					selector = `.markdown-body ${selector}`;
				}

				return selector;
			});

			// collect `.markdown-body` rules
			if (el.selectors.length === 1 && el.selectors[0] === '.markdown-body') {
				[].push.apply(mdBodyProps, el.declarations);
				return false;
			}
		}

		return el.declarations && el.declarations.length !== 0;
	});

	// merge `.markdown-body` rules
	style.stylesheet.rules.unshift({
		type: 'rule',
		selectors: ['.markdown-body'],
		declarations: mdBodyProps
	});

	return `@font-face {\n  font-family: octicons-link;\n  src: url(data:font/woff;charset=utf-8;base64,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) format(\'woff\');\n}\n\n${css.stringify(style)}`;
};

module.exports = () =>
	Promise.all([
		getRenderedFixture(),
		getCSS()
	])
	.then(x => uncssP(x[0], {stylesheets: x[1], ignore: [/^\.pl/]}))
	.then(cleanupCss);
<commit_msg>Exclude some more irrelevant CSS<commit_after>'use strict';
const got = require('got');
const cheerio = require('cheerio');
const uncss = require('uncss');
const pify = require('pify');

const uncssP = pify(uncss);
const fixtureURL = 'https://github.com/sindresorhus/generate-github-markdown-css/blob/master/fixture.md';

const getCSS = () => got('https://github.com').then(response => {
	const ret = [];
	const $ = cheerio.load(response.body);

	$('link[href$=".css"]').each((i, el) => {
		ret.push(el.attribs.href);
	});

	if (ret.length === 0) {
		throw new Error('Could not find GitHub stylesheets');
	}

	return ret;
});

const getRenderedFixture = () => got(fixtureURL).then(response => {
	const $ = cheerio.load(response.body);
	return $('.markdown-body').parent().html();
});

const cleanupCss = str => {
	const css = require('css');

	const style = css.parse(str);
	const mdBodyProps = [];

	style.stylesheet.rules = style.stylesheet.rules.filter(el => {
		if (el.type === 'keyframes' || el.type === 'comment' || el.type === 'font-face') {
			return false;
		}

		if (el.type === 'rule') {
			if (/::-webkit-validation|[:-]placeholder$|^\.integrations-slide-content|^\.prose-diff|@font-face|^button::|^article$|^.plan-|^.plans-|\.site-search|^::-webkit-file-upload-button$|^input::-webkit-outer-spin-button$/.test(el.selectors[0])) {
				return false;
			}

			// work around GitHub Markdown API inconsistency #10
			if (el.selectors[0] === '.task-list-item-checkbox') {
				el.selectors[0] = '.task-list-item input';
			}

			// remove `body` from `body, input {}`
			if (el.selectors[0] === 'body' && el.selectors[1] === 'input') {
				el.selectors.shift();
			}

			// rename the .octoicons font
			if (el.selectors[0] === '.octicon') {
				el.declarations = el.declarations.map(el => {
					if (el.property === 'font') {
						el.value += '-link';
					}

					return el;
				});
			}

			if (el.selectors.length === 1 && /^(?:html|body)$/.test(el.selectors[0])) {
				// remove everything from body/html other than these
				el.declarations = el.declarations.filter(x => /^(?:line-height|color)$|text-size-adjust$/.test(x.property));
			}

			el.selectors = el.selectors.map(selector => {
				if (/^(?:body|html)$/.test(selector)) {
					selector = '.markdown-body';
				}

				if (!/\.markdown-body/.test(selector)) {
					selector = `.markdown-body ${selector}`;
				}

				return selector;
			});

			// collect `.markdown-body` rules
			if (el.selectors.length === 1 && el.selectors[0] === '.markdown-body') {
				[].push.apply(mdBodyProps, el.declarations);
				return false;
			}
		}

		return el.declarations && el.declarations.length !== 0;
	});

	// merge `.markdown-body` rules
	style.stylesheet.rules.unshift({
		type: 'rule',
		selectors: ['.markdown-body'],
		declarations: mdBodyProps
	});

	return `@font-face {\n  font-family: octicons-link;\n  src: url(data:font/woff;charset=utf-8;base64,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) format(\'woff\');\n}\n\n${css.stringify(style)}`;
};

module.exports = () =>
	Promise.all([
		getRenderedFixture(),
		getCSS()
	])
	.then(x => uncssP(x[0], {stylesheets: x[1], ignore: [/^\.pl/]}))
	.then(cleanupCss);
<|endoftext|>


<commit_before>"use strict";

/**
 * lei-proto
 *
 * @author Zongmin Lei <leizongmin@gmail.com>
 */

const DATA_TYPES = ["int", "uint", "float", "double", "string", "buffer"];

function NotSupportDataTypeError(parameter, type) {
  const err = new Error("not support type `" + type + "`");
  err.code = "NOT_SUPPORT_TYPE";
  err.parameter = parameter;
  err.type = type;
  return err;
}

// eslint-disable-next-line
function InvalidDataTypeError(parameter, type) {
  const err = new Error("`" + parameter + "` is not a " + type);
  err.code = "INVALID_TYPE";
  err.parameter = parameter;
  err.type = type;
  return err;
}

function InvalidDataSizeError(parameter, size) {
  const err = new Error("invalid size for `" + parameter + "`");
  err.code = "INVALID_DATA_SIZE";
  err.parameter = parameter;
  err.size = size;
  return err;
}

function InvalidParameterNameFormatError(name) {
  const err = new Error("invalid parameter format `" + name + "`");
  err.code = "INVALID_PARAMETER_FORMAT";
  err.name = name;
  return err;
}

function InvalidProtocolInfoError(msg) {
  const err = new Error("invalid protocol info: " + msg);
  err.code = "INVALID_PROTOCOL_INFO";
  err.info = msg;
  return err;
}

function generateJsCodes(
  encodeSource,
  encodeExSource,
  encodeStrictSource,
  encodeExStrictSource,
  decodeSource,
  decodeStrictSource,
  offset,
) {
  return `(function () {
    var proto = {
      /** 编码器 */
      encode: ${encodeSource},
      /** 编码器，参数为一个对象 */
      encodeEx: ${encodeExSource},
      /** 严格模式的编码器 */
      encodeStrict: ${encodeStrictSource},
      /** 严格模式的编码器，参数为一个对象 */
      encodeExStrict: ${encodeExStrictSource},
      /** 解码器 */
      decode: ${decodeSource},
      /** 严格模式的解码器 */
      decodeStrict: ${decodeStrictSource},
      /** 数据包长度，如果最后一项是不定长的，则总长度为size+最后一项的长度 */
      size: offset,
    };
    return proto;
  })()`;
}

function parseProto(list, options) {
  options = options || {};

  const encodeArgs = [];
  const encodeCheck = [];
  const encodeBody = [];
  const decodeBody = [];
  let offset = 0;

  list.forEach(function(item, i) {
    if (!Array.isArray(item)) throw new InvalidProtocolInfoError("item must be an array");
    if (item.length < 2) throw new InvalidProtocolInfoError("missing `name` and `type`");

    const name = String(item[0]);
    const type = String(item[1]).toLowerCase();
    let size = Number(item[2]);
    let bytes = String(item[3]).toUpperCase();

    if (!(size > 0)) size = 0;
    if (bytes !== "LE") bytes = "BE";
    if (type === "float") size = 4;
    if (type === "double") size = 8;

    if (!/^[a-zA-Z_]/.test(name)) throw new InvalidParameterNameFormatError(name);
    if (encodeArgs.indexOf(name) !== -1) throw new InvalidParameterNameFormatError(name);

    if (DATA_TYPES.indexOf(type) === -1) throw new NotSupportDataTypeError(name, type);

    if (type === "string" || type === "buffer") {
      if (size < 1 && i < list.length - 1) {
        throw new InvalidDataSizeError(name, size);
      }
    } else if (size < 1) {
      throw new InvalidDataSizeError(name, size);
    }

    encodeArgs.push(name);
    switch (type) {
      case "int":
        encodeCheck.push(
          `if (typeof a !== "number" || isNaN(${name})) throw new InvalidDataTypeError("${name}", "${type}");`,
        );
        if (size === 1) {
          encodeBody.push(`$buf.writeUInt8(${name}, ${offset});`);
          decodeBody.push(`${name}: $buf.readUInt8(${offset})`);
        } else if (size === 2) {
          encodeBody.push(`$buf.writeInt16${bytes}(${name}, ${offset});`);
          decodeBody.push(`${name}: $buf.readInt16${bytes}(${offset})`);
        } else if (size === 4) {
          encodeBody.push(`$buf.writeInt32${bytes}(${name}, ${offset});`);
          decodeBody.push(`${name}: $buf.readInt32${bytes}(${offset})`);
        } else {
          encodeBody.push(`$buf.writeInt${bytes}(${name}, ${offset}, ${size});`);
          decodeBody.push(`${name}: $buf.readInt${bytes}(${offset}, ${size})`);
        }
        break;
      case "uint":
        encodeCheck.push(
          `if (typeof a !== "number" || isNaN(${name})) throw new InvalidDataTypeError("${name}", "${type}");`,
        );
        if (size === 1) {
          encodeBody.push(`$buf.writeUInt8(${name}, ${offset});`);
          decodeBody.push(`${name}: $buf.readUInt8(${offset})`);
        } else if (size === 2) {
          encodeBody.push(`$buf.writeUInt16${bytes}(${name}, ${offset});`);
          decodeBody.push(`${name}: $buf.readUInt16${bytes}(${offset})`);
        } else if (size === 4) {
          encodeBody.push(`$buf.writeUInt32${bytes}(${name}, ${offset});`);
          decodeBody.push(`${name}: $buf.readUInt32${bytes}(${offset})`);
        } else {
          encodeBody.push(`$buf.writeUInt${bytes}(${name}, ${offset}, ${size});`);
          decodeBody.push(`${name}: $buf.readUInt${bytes}(${offset}, ${size})`);
        }
        break;
      case "float":
        encodeCheck.push(
          `if (typeof a !== "number" || isNaN(${name})) throw new InvalidDataTypeError("${name}", "${type}");`,
        );
        encodeBody.push(`$buf.writeFloat${bytes}(${name}, ${offset});`);
        decodeBody.push(`${name}: $buf.readFloat${bytes}(${offset})`);
        break;
      case "double":
        encodeCheck.push(
          `if (typeof a !== "number" || isNaN(${name})) throw new InvalidDataTypeError("${name}", "${type}");`,
        );
        encodeBody.push(`$buf.writeDouble${bytes}(${name}, ${offset});`);
        decodeBody.push(`${name}: $buf.readDouble${bytes}(${offset})`);
        break;
      case "string":
        encodeCheck.push(`if (typeof ${name} !== "string") throw new InvalidDataTypeError("${name}", "${type}");`);
        if (size > 0) {
          encodeBody.push(`Buffer.from(${name}).copy($buf, ${offset}, 0, ${size})`);
          decodeBody.push(`${name}: $buf.slice(${offset}, ${offset + size}).toString()`);
        } else {
          encodeBody.push(`Buffer.from(${name}).copy($buf, ${offset}, 0)`);
          decodeBody.push(`${name}: $buf.slice(${offset}).toString()`);
        }
        break;
      case "buffer":
        encodeCheck.push(`if (!Buffer.isBuffer(${name})) throw new InvalidDataTypeError("${name}", "${type}");`);
        if (size > 0) {
          encodeBody.push(`${name}.copy($buf, ${offset}, 0, ${size})`);
          decodeBody.push(`${name}: $buf.slice(${offset}, ${offset + size})`);
        } else {
          encodeBody.push(`${name}.copy($buf, ${offset}, 0)`);
          decodeBody.push(`${name}: $buf.slice(${offset})`);
        }
        break;
      default:
        throw new NotSupportDataTypeError(name, type);
    }

    offset += size;
  });

  const lastItemType = String(list[list.length - 1][1]).toLowerCase();
  let lastItemSize = Number(list[list.length - 1][2]);
  if (lastItemType === "float") lastItemSize = 4;
  if (lastItemType === "double") lastItemSize = 8;
  const lastItemName = list[list.length - 1][0];
  const isDynamicSize = !(lastItemSize > 0);
  const argNames = encodeArgs.join(", ");
  const argNames2 = encodeArgs
    .map(function(n) {
      return "data." + n;
    })
    .join(", ");
  const encodeSource =
    "(function (" +
    argNames +
    ") {\n" +
    (isDynamicSize ? `${lastItemName} = Buffer.from(${lastItemName});` : "") +
    "var $buf = Buffer.allocUnsafe(" +
    (lastItemSize > 0 ? offset : offset + " + " + lastItemName + ".length") +
    ")\n" +
    encodeBody.join("\n") +
    "\n" +
    "return $buf;\n" +
    "})";
  const encodeStrictSource =
    "(function (" +
    argNames +
    ") {\n" +
    encodeCheck.join("\n") +
    "\n" +
    (isDynamicSize ? `${lastItemName} = Buffer.from(${lastItemName});` : "") +
    "var $buf = Buffer.allocUnsafe(" +
    (lastItemSize > 0 ? offset : offset + " + " + lastItemName + ".length") +
    ")\n" +
    encodeBody.join("\n") +
    "\n" +
    "return $buf;\n" +
    "})";
  const encodeExSource = "(function (data) {\n" + "return proto.encode(" + argNames2 + ");\n" + "})";
  const encodeExStrictSource = "(function (data) {\n" + "return proto.encodeStrict(" + argNames2 + ");\n" + "})";
  const decodeSource = "(function ($buf) {\n" + "return {\n" + decodeBody.join(",\n") + "\n" + "};\n" + "})";
  const decodeStrictSource =
    "(function ($buf) {\n" +
    "if ($buf.length < " +
    offset +
    ") throw new IncorrectBufferSizeError(" +
    offset +
    ", $buf.length);\n" +
    "return {\n" +
    decodeBody.join(",\n") +
    "\n" +
    "};\n" +
    "})";

  const js = generateJsCodes(
    encodeSource,
    encodeExSource,
    encodeStrictSource,
    encodeExStrictSource,
    decodeSource,
    decodeStrictSource,
    offset,
  );
  if (options.sourceCode) {
    return js;
  }
  return eval(js);
}

module.exports = parseProto;
<commit_msg>refactor: 通过反引号简化代码<commit_after>"use strict";

/**
 * lei-proto
 *
 * @author Zongmin Lei <leizongmin@gmail.com>
 */

const DATA_TYPES = ["int", "uint", "float", "double", "string", "buffer"];

function NotSupportDataTypeError(parameter, type) {
  const err = new Error("not support type `" + type + "`");
  err.code = "NOT_SUPPORT_TYPE";
  err.parameter = parameter;
  err.type = type;
  return err;
}

// eslint-disable-next-line
function InvalidDataTypeError(parameter, type) {
  const err = new Error("`" + parameter + "` is not a " + type);
  err.code = "INVALID_TYPE";
  err.parameter = parameter;
  err.type = type;
  return err;
}

function InvalidDataSizeError(parameter, size) {
  const err = new Error("invalid size for `" + parameter + "`");
  err.code = "INVALID_DATA_SIZE";
  err.parameter = parameter;
  err.size = size;
  return err;
}

function InvalidParameterNameFormatError(name) {
  const err = new Error("invalid parameter format `" + name + "`");
  err.code = "INVALID_PARAMETER_FORMAT";
  err.name = name;
  return err;
}

function InvalidProtocolInfoError(msg) {
  const err = new Error("invalid protocol info: " + msg);
  err.code = "INVALID_PROTOCOL_INFO";
  err.info = msg;
  return err;
}

function generateJsCodes(
  encodeSource,
  encodeExSource,
  encodeStrictSource,
  encodeExStrictSource,
  decodeSource,
  decodeStrictSource,
  offset,
) {
  return `(function () {
    var proto = {
      /** 编码器 */
      encode: ${encodeSource},
      /** 编码器，参数为一个对象 */
      encodeEx: ${encodeExSource},
      /** 严格模式的编码器 */
      encodeStrict: ${encodeStrictSource},
      /** 严格模式的编码器，参数为一个对象 */
      encodeExStrict: ${encodeExStrictSource},
      /** 解码器 */
      decode: ${decodeSource},
      /** 严格模式的解码器 */
      decodeStrict: ${decodeStrictSource},
      /** 数据包长度，如果最后一项是不定长的，则总长度为size+最后一项的长度 */
      size: offset,
    };
    return proto;
  })()`;
}

function parseProto(list, options) {
  options = options || {};

  const encodeArgs = [];
  const encodeCheck = [];
  const encodeBody = [];
  const decodeBody = [];
  let offset = 0;

  list.forEach(function(item, i) {
    if (!Array.isArray(item)) throw new InvalidProtocolInfoError("item must be an array");
    if (item.length < 2) throw new InvalidProtocolInfoError("missing `name` and `type`");

    const name = String(item[0]);
    const type = String(item[1]).toLowerCase();
    let size = Number(item[2]);
    let bytes = String(item[3]).toUpperCase();

    if (!(size > 0)) size = 0;
    if (bytes !== "LE") bytes = "BE";
    if (type === "float") size = 4;
    if (type === "double") size = 8;

    if (!/^[a-zA-Z_]/.test(name)) throw new InvalidParameterNameFormatError(name);
    if (encodeArgs.indexOf(name) !== -1) throw new InvalidParameterNameFormatError(name);

    if (DATA_TYPES.indexOf(type) === -1) throw new NotSupportDataTypeError(name, type);

    if (type === "string" || type === "buffer") {
      if (size < 1 && i < list.length - 1) {
        throw new InvalidDataSizeError(name, size);
      }
    } else if (size < 1) {
      throw new InvalidDataSizeError(name, size);
    }

    encodeArgs.push(name);
    switch (type) {
      case "int":
        encodeCheck.push(
          `if (typeof a !== "number" || isNaN(${name})) throw new InvalidDataTypeError("${name}", "${type}");`,
        );
        if (size === 1) {
          encodeBody.push(`$buf.writeUInt8(${name}, ${offset});`);
          decodeBody.push(`${name}: $buf.readUInt8(${offset})`);
        } else if (size === 2) {
          encodeBody.push(`$buf.writeInt16${bytes}(${name}, ${offset});`);
          decodeBody.push(`${name}: $buf.readInt16${bytes}(${offset})`);
        } else if (size === 4) {
          encodeBody.push(`$buf.writeInt32${bytes}(${name}, ${offset});`);
          decodeBody.push(`${name}: $buf.readInt32${bytes}(${offset})`);
        } else {
          encodeBody.push(`$buf.writeInt${bytes}(${name}, ${offset}, ${size});`);
          decodeBody.push(`${name}: $buf.readInt${bytes}(${offset}, ${size})`);
        }
        break;
      case "uint":
        encodeCheck.push(
          `if (typeof a !== "number" || isNaN(${name})) throw new InvalidDataTypeError("${name}", "${type}");`,
        );
        if (size === 1) {
          encodeBody.push(`$buf.writeUInt8(${name}, ${offset});`);
          decodeBody.push(`${name}: $buf.readUInt8(${offset})`);
        } else if (size === 2) {
          encodeBody.push(`$buf.writeUInt16${bytes}(${name}, ${offset});`);
          decodeBody.push(`${name}: $buf.readUInt16${bytes}(${offset})`);
        } else if (size === 4) {
          encodeBody.push(`$buf.writeUInt32${bytes}(${name}, ${offset});`);
          decodeBody.push(`${name}: $buf.readUInt32${bytes}(${offset})`);
        } else {
          encodeBody.push(`$buf.writeUInt${bytes}(${name}, ${offset}, ${size});`);
          decodeBody.push(`${name}: $buf.readUInt${bytes}(${offset}, ${size})`);
        }
        break;
      case "float":
        encodeCheck.push(
          `if (typeof a !== "number" || isNaN(${name})) throw new InvalidDataTypeError("${name}", "${type}");`,
        );
        encodeBody.push(`$buf.writeFloat${bytes}(${name}, ${offset});`);
        decodeBody.push(`${name}: $buf.readFloat${bytes}(${offset})`);
        break;
      case "double":
        encodeCheck.push(
          `if (typeof a !== "number" || isNaN(${name})) throw new InvalidDataTypeError("${name}", "${type}");`,
        );
        encodeBody.push(`$buf.writeDouble${bytes}(${name}, ${offset});`);
        decodeBody.push(`${name}: $buf.readDouble${bytes}(${offset})`);
        break;
      case "string":
        encodeCheck.push(`if (typeof ${name} !== "string") throw new InvalidDataTypeError("${name}", "${type}");`);
        if (size > 0) {
          encodeBody.push(`Buffer.from(${name}).copy($buf, ${offset}, 0, ${size})`);
          decodeBody.push(`${name}: $buf.slice(${offset}, ${offset + size}).toString()`);
        } else {
          encodeBody.push(`Buffer.from(${name}).copy($buf, ${offset}, 0)`);
          decodeBody.push(`${name}: $buf.slice(${offset}).toString()`);
        }
        break;
      case "buffer":
        encodeCheck.push(`if (!Buffer.isBuffer(${name})) throw new InvalidDataTypeError("${name}", "${type}");`);
        if (size > 0) {
          encodeBody.push(`${name}.copy($buf, ${offset}, 0, ${size})`);
          decodeBody.push(`${name}: $buf.slice(${offset}, ${offset + size})`);
        } else {
          encodeBody.push(`${name}.copy($buf, ${offset}, 0)`);
          decodeBody.push(`${name}: $buf.slice(${offset})`);
        }
        break;
      default:
        throw new NotSupportDataTypeError(name, type);
    }

    offset += size;
  });

  const lastItemType = String(list[list.length - 1][1]).toLowerCase();
  let lastItemSize = Number(list[list.length - 1][2]);
  if (lastItemType === "float") lastItemSize = 4;
  if (lastItemType === "double") lastItemSize = 8;
  const lastItemName = list[list.length - 1][0];
  const isDynamicSize = !(lastItemSize > 0);
  const argNames = encodeArgs.join(", ");
  const argNames2 = encodeArgs.map(n => `data.${n}`).join(", ");
  const encodeBodyLines = encodeBody.join("\n");
  const decodeBodyLines = decodeBody.join(",\n");
  const encodeCheckLines = encodeCheck.join("\n");
  const encodeSource = `(function (${argNames}) {
    ${isDynamicSize ? `${lastItemName} = Buffer.from(${lastItemName});` : ""}
    var $buf = Buffer.allocUnsafe(${lastItemSize > 0 ? offset : `${offset}+${lastItemName}.length`});
    ${encodeBodyLines}
    return $buf;
  })`;
  const encodeStrictSource = `(function (${argNames}) {
    ${encodeCheckLines}
    ${isDynamicSize ? `${lastItemName} = Buffer.from(${lastItemName});` : ""}
    var $buf = Buffer.allocUnsafe(${lastItemSize > 0 ? offset : `${offset}+${lastItemName}.length`});
    ${encodeBodyLines}
    return $buf;
    })`;
  const encodeExSource = `(function (data) { return proto.encode(${argNames2}); })`;
  const encodeExStrictSource = `(function (data) { return proto.encodeStrict(${argNames2}); })`;
  const decodeSource = `(function ($buf) { return { ${decodeBodyLines} }; })`;
  const decodeStrictSource = `(function ($buf) {
    if ($buf.length < ${offset}) throw new IncorrectBufferSizeError(${offset}, $buf.length);
    return { ${decodeBodyLines} };
    })`;

  const js = generateJsCodes(
    encodeSource,
    encodeExSource,
    encodeStrictSource,
    encodeExStrictSource,
    decodeSource,
    decodeStrictSource,
    offset,
  );
  if (options.sourceCode) {
    return js;
  }
  return eval(js);
}

module.exports = parseProto;
<|endoftext|>


<commit_before>"use strict";

var fs = require("fs");

class SpaceManager {
	constructor(reservationSpaces, walkInSpaces) {
		this.currentTime = new Date();
		this.reservationSpaces = reservationSpaces;
    this.walkInSpaces = walkInSpaces;
		this.timeSlots = 365*24;
		this.reservedTimes = new Array(this.timeSlots);
    //Initialize the number of reservations for each time slot to zero
		for(var i = 0; i < this.timeSlots; i++) {
			this.reservedTimes[i] = 0;
		}
	}

  getReservedTimes() {
    return this.reservedTimes;
  }

  isfull() {
    for(var i = 1; i < this.timeSlots; i++) {
      if(this.reservedTimes[i] != 200) {
        return false;
      }
    }
    return true;
  }

  checkReservation(startDateObj, endDateObj) {
    if (startDateObj === undefined &&
        endDateObj === undefined) return false;
		var startSection = this.convertToSection(startDateObj);
		var endSection = this.convertToSection(endDateObj);
    //Check that the date reservation range is valid
		if(startSection < 0 || endSection >= this.timeSlots) {
			console.log("One or more of the given dates is outside of range.");
			return false;
		}
    //Check each timeslot to make sure there is enough space
    for(var i = startSection; i < endSection; i++) {
      if(this.reservedTimes[i] >= this.reservationSpaces) {
        console.log("Not enough space.");
        return false;
      }
    }
    return true;
  }

  //Checks for any descrepencies, then 1up's the reservation time slots
	setReservation(startDateObj, endDateObj) {
    if (startDateObj === undefined &&
        endDateObj === undefined) return false;
    console.log(startDateObj);
    console.log(endDateObj);
		var startSection = this.convertToSection(startDateObj);
		var endSection = this.convertToSection(endDateObj);
    if(!this.checkReservation(startDateObj, endDateObj)) {
      return false;
    }
    console.log(startSection);
    console.log(endSection);
    //Increment reservation number for each time slot
    for(var i = startSection; i < endSection; i++) {
        this.reservedTimes[i] += 1;
        console.log(i);
    }
    this.writeToFile();
    return true;
	}

	getReservedSpaces(dateObj) {
		var section = this.convertToSection(dateObj);
    //Check that date given falls within the time slots
		if(section < 0 || section >= this.timeslots) {
			console.log("Date is outside of range.");
			return undefined;
		}
		return this.reservedTimes[section];
	}

	getUnreservedSpaces(dateObj) {
		return this.reservationSpaces - this.getReservedSpaces(dateObj);
	}

	//Will replace this with a cron job or something later down the line
	incrementHour() {
    //Shift the array down one, then push a new slot to the end
		this.reservedTimes.shift();
		this.reservedTimes.push(0);
    //Increment the current time by an hour
    //3.6E6 is miliseconds in hour
		this.currentTime = new Date(this.currentTime.getTime() + 3.6E6);
	}

  //Converts the given date to the corresponding timeslot array index
	convertToSection(dateObj) {
    //3.6E6 is miliseconds in hour
		return Math.floor((dateObj - this.currentTime) / 3.6E6);
	}

  writeToFile() {
    fs.writeFile("./oldReservedTimes.js", JSON.stringify(this.reservedTimes), function(err) {
      if(err) {
            console.log(err);
      } else {
        console.log("Output saved to oldReservedTimes.js.");
      }
    });
    fs.writeFile("./oldTime.js", JSON.stringify(this.currentTime), function(err) {
      if(err) {
            console.log(err);
      } else {
        console.log("Output saved to oldTime.js.");
      }
    });
  }

  arrayFileThingy() {
    try {
      this.reservedTimes = JSON.parse("./oldReservedTimes.js");
      var oldTime = JSON.parse("./oldTime.js");
    } catch(err) {
      console.log(err);
    }
    var sectionsToShift = Math.abs(this.convertToSection(oldTime));
    for(var i = 0; i < sectionsToShift; i++) {
      this.incrementHour();
    }
  }
};

module.exports = SpaceManager;
<commit_msg>even more debugging<commit_after>"use strict";

var fs = require("fs");

class SpaceManager {
	constructor(reservationSpaces, walkInSpaces) {
		this.currentTime = new Date();
		this.reservationSpaces = reservationSpaces;
    this.walkInSpaces = walkInSpaces;
		this.timeSlots = 365*24;
		this.reservedTimes = new Array(this.timeSlots);
    //Initialize the number of reservations for each time slot to zero
		for(var i = 0; i < this.timeSlots; i++) {
			this.reservedTimes[i] = 0;
		}
	}

  getReservedTimes() {
    return this.reservedTimes;
  }

  isfull() {
    for(var i = 1; i < this.timeSlots; i++) {
      if(this.reservedTimes[i] != 200) {
        return false;
      }
    }
    return true;
  }

  checkReservation(startDateObj, endDateObj) {
    if (startDateObj === undefined &&
        endDateObj === undefined) return false;
		var startSection = this.convertToSection(startDateObj);
		var endSection = this.convertToSection(endDateObj);
    //Check that the date reservation range is valid
		if(startSection < 0 || endSection >= this.timeSlots) {
			console.log("One or more of the given dates is outside of range.");
			return false;
		}
    //Check each timeslot to make sure there is enough space
    for(var i = startSection; i < endSection; i++) {
      if(this.reservedTimes[i] >= this.reservationSpaces) {
        console.log("Not enough space.");
        return false;
      }
    }
    return true;
  }

  //Checks for any descrepencies, then 1up's the reservation time slots
	setReservation(startDateObj, endDateObj) {
    if (startDateObj === undefined &&
        endDateObj === undefined) return false;
    console.log(startDateObj);
    console.log(endDateObj);
		var startSection = this.convertToSection(startDateObj);
		var endSection = this.convertToSection(endDateObj);
    if(!this.checkReservation(startDateObj, endDateObj)) {
      return false;
    }
    console.log(startSection);
    console.log(endSection);
    //Increment reservation number for each time slot
    for(var i = startSection; i < endSection; i++) {
        this.reservedTimes[i] += 1;
        console.log(i);
    }
    this.writeToFile();
    return true;
	}

	getReservedSpaces(dateObj) {
		var section = this.convertToSection(dateObj);
    console.log(section);
    //Check that date given falls within the time slots
		if(section < 0 || section >= this.timeslots) {
			console.log("Date is outside of range.");
			return undefined;
		}
    console.log(this.reservedTimes[section]);
		return this.reservedTimes[section];
	}

	getUnreservedSpaces(dateObj) {
		return this.reservationSpaces - this.getReservedSpaces(dateObj);
	}

	//Will replace this with a cron job or something later down the line
	incrementHour() {
    //Shift the array down one, then push a new slot to the end
		this.reservedTimes.shift();
		this.reservedTimes.push(0);
    //Increment the current time by an hour
    //3.6E6 is miliseconds in hour
		this.currentTime = new Date(this.currentTime.getTime() + 3.6E6);
	}

  //Converts the given date to the corresponding timeslot array index
	convertToSection(dateObj) {
    //3.6E6 is miliseconds in hour
		return Math.floor((dateObj - this.currentTime) / 3.6E6);
	}

  writeToFile() {
    fs.writeFile("./oldReservedTimes.js", JSON.stringify(this.reservedTimes), function(err) {
      if(err) {
            console.log(err);
      } else {
        console.log("Output saved to oldReservedTimes.js.");
      }
    });
    fs.writeFile("./oldTime.js", JSON.stringify(this.currentTime), function(err) {
      if(err) {
            console.log(err);
      } else {
        console.log("Output saved to oldTime.js.");
      }
    });
  }

  arrayFileThingy() {
    try {
      this.reservedTimes = JSON.parse("./oldReservedTimes.js");
      var oldTime = JSON.parse("./oldTime.js");
    } catch(err) {
      console.log(err);
    }
    var sectionsToShift = Math.abs(this.convertToSection(oldTime));
    for(var i = 0; i < sectionsToShift; i++) {
      this.incrementHour();
    }
  }
};

module.exports = SpaceManager;
<|endoftext|>


<commit_before>var getSetRelevance = require('./pure/setrelevance'),
    coalesceZooms = require('carmen-cache').Cache.coalesceZooms,
    sm = new(require('sphericalmercator'))(),
    ops = require('./util/ops'),
    mp2_14 = Math.pow(2, 14),
    mp2_28 = Math.pow(2, 28),
    queue = require('queue-async'),
    termops = require('./util/termops'),
    feature = require('./util/feature');

var xd = Math.pow(2, 39),
    yd = Math.pow(2, 25),
    mp2_14 = Math.pow(2, 14),
    mp2_28 = Math.pow(2, 28);

module.exports = spatialmatch;

// Given that we've geocoded potential results in multiple sources, given
// arrays of `feats` and `grids` of the same length, combine matches that
// are over the same point, factoring in the zoom levels on which they
// occur.
// Calls `callback` with `(err, contexts, relevd)` in which
//
// @param `contexts` is an array of bboxes which are assigned scores
// @param `relevd` which is an object mapping place ids to places
// @param {Object} geocoder the geocoder instance
// @param {Array} feats an array of feature objects
// @param {Array} grids an array of grid objects
// @param {Array} zooms an array of zoom numbers
// @param {Function} callback
function spatialmatch(query, stats, geocoder, feats, grids, zooms, options, callback) {
    var types = Object.keys(geocoder.indexes);

    // Combine the scores for each match across multiple grids and zoom levels,
    // producing a mapping from `zxy` to matches
    coalesceZooms(grids, zooms, function(err, coalesced) {
    if (err) return callback(err);

    // Generate a light modifier to apply to results from indexes without
    // address handling if an address is present in the query.
    var addrmod = {};
    var addridx = {};
    var address = termops.address(query);

    for (var id in geocoder.indexes) {
        addrmod[id] = address && geocoder.indexes[id]._geocoder.geocoder_address ? 0.01 : 0;
        addridx[geocoder.order[id]] = address && geocoder.indexes[id]._geocoder.geocoder_address;
    }

    var sets = {},
        rowMemo = {},
        done = {},
        i, c, l,
        feat,
        rows,
        relev,
        pushed,
        prev;

    for (var c in coalesced) {
        var key = coalesced[c].key;
        pushed = false;

        // Check cache for whether this set's combination of features
        // has already been scored.
        if (done[key]) {
            continue;
        } else {
            done[key] = true;
        }

        rows = new Array(coalesced[c].length);
        for (var i = 0; i < coalesced[c].length; i++) {
            rows[i] = feats[coalesced[c][i]];
        }

        // Sort by db, relev such that total relev can be
        // calculated without results for the same db being summed.
        rows.sort(sortRelevReason);
        relev = getSetRelevance(query.length, rows);

        for (i = 0, l = rows.length; i < l; i++) {
            if (!rows[i]) continue;

            sets[rows[i].tmpid] = sets[rows[i].tmpid] || rows[i];

            // Don't use results after the topmost index in the stack.
            if (pushed && pushed !== rows[i].dbid) continue;
            pushed = rows[i].dbid;
            prev = rowMemo[rows[i].tmpid];

            // Don't overwrite an existing result entry for the same
            // feature that scores higher.
            if (prev && prev.relev > (relev + addrmod[rows[i].dbid])) continue;

            rowMemo[rows[i].tmpid] = {
                dbid: rows[i].dbid,
                id: rows[i].id,
                tmpid: rows[i].tmpid,
                relev: relev + addrmod[rows[i].dbid],
                idx: rows[i].idx
            };
        }
    }

    var results = [],
        contexts = [],
        formatted = [],
        memo = null;

    for (var j in rowMemo) {
        results.push(rowMemo[j]);
    }

    results.sort(sortByRelev);

    for (var k = 0; k < results.length; k++) {
        feat = results[k];
        if (memo === null || memo - feat.relev < 0.1) {
            memo = memo || feat.relev;
            formatted.push(feat);
        }
    }

    results = formatted;

    stats.relevTime = +new Date() - stats.relevTime;
    stats.relevCount = results.length;

   //Stores the proximity latlng as zxy at all source zoom levels
    var zxyProximity = {};

    //If latlng is given, favour results closest locale
    if (options.proximity) {
        for (var i = 0; i < zooms.length; i++) {
            zxyProximity[zooms[i]] = sm.px(options.proximity, zooms[i]);
        }
        var zxyResult = [];
        for (var i = 0; i < results.length; i++) {
            results[i].z = geocoder.indexes[results[i].dbid]._geocoder.zoom;
            results[i].x = Math.floor(results[i].tmpid / xd);
            results[i].y = Math.floor(results[i].tmpid % xd / yd);
            results[i].distance = Math.pow(Math.pow(zxyProximity[results[i].z][0] - results[i].x, 2) + Math.pow(zxyProximity[results[i].z][1] - results[i].y, 2), 0.5);
        }
        results.sort(sortByProximity);
    }

    callback(null, {
        sets: sets,
        results: results,
        coalesced: coalesced
    });
    });
}

function sortByProximity(a, b) {
    if (a.idx > b.idx) return -1;
    else if (a.idx < b.idx) return 1;
    else if (a.relev > b.relev) return -1;
    else if (a.relev < b.relev) return 1;
    else if (a.distance < b.distance) return -1;
    else if (a.distance > b.distance) return 1;
    else if (a.id < b.id) return -1;
    else if (a.id > b.id) return 1;
    return 0;
}

function sortRelevReason(a, b) {
    if (a.idx > b.idx) return -1;
    else if (a.idx < b.idx) return 1;
    else if (a.relev > b.relev) return -1;
    else if (a.relev < b.relev) return 1;
    else if (a.reason > b.reason) return -1;
    else if (a.reason < b.reason) return 1;
    else if (a.id < b.id) return -1;
    else if (a.id > b.id) return 1;
    return 0;
}

function sortByRelev(a, b) {
    return b.relev - a.relev;
}

<commit_msg>Use async spatialmatch (Broken).<commit_after>var getSetRelevance = require('./pure/setrelevance'),
    coalesceZooms = require('carmen-cache').Cache.coalesceZooms,
    spatialMatch = require('carmen-cache').Cache.spatialMatch,
    sm = new(require('sphericalmercator'))(),
    ops = require('./util/ops'),
    mp2_14 = Math.pow(2, 14),
    mp2_28 = Math.pow(2, 28),
    queue = require('queue-async'),
    termops = require('./util/termops'),
    feature = require('./util/feature');

var xd = Math.pow(2, 39),
    yd = Math.pow(2, 25),
    mp2_14 = Math.pow(2, 14),
    mp2_28 = Math.pow(2, 28);

module.exports = spatialmatch;

// Given that we've geocoded potential results in multiple sources, given
// arrays of `feats` and `grids` of the same length, combine matches that
// are over the same point, factoring in the zoom levels on which they
// occur.
// Calls `callback` with `(err, contexts, relevd)` in which
//
// @param `contexts` is an array of bboxes which are assigned scores
// @param `relevd` which is an object mapping place ids to places
// @param {Object} geocoder the geocoder instance
// @param {Array} feats an array of feature objects
// @param {Array} grids an array of grid objects
// @param {Array} zooms an array of zoom numbers
// @param {Function} callback
function spatialmatch(query, stats, geocoder, feats, grids, zooms, options, callback) {
    var types = Object.keys(geocoder.indexes);

    // Generate a light modifier to apply to results from indexes without
    // address handling if an address is present in the query.
    var addrmod = {};
    var addridx = {};
    var address = termops.address(query);

    for (var id in geocoder.indexes) {
        addrmod[id] = address && geocoder.indexes[id]._geocoder.geocoder_address ? 0.01 : 0;
        addridx[geocoder.order[id]] = address && geocoder.indexes[id]._geocoder.geocoder_address;
    }

    spatialMatch(query.length, feats, grids, zooms, function(err, res) {
        if (err) return callback(err);
        return callback(null, res);
        console.log('HERE', err, res);
    });

//    // Combine the scores for each match across multiple grids and zoom levels,
//    // producing a mapping from `zxy` to matches
//    coalesceZooms(grids, zooms, function(err, coalesced) {
//    if (err) return callback(err);

//    var sets = {},
//        rowMemo = {},
//        done = {},
//        i, c, l,
//        feat,
//        rows,
//        relev,
//        pushed,
//        prev;

//    for (var c in coalesced) {
//        var key = coalesced[c].key;
//        pushed = false;

//        // Check cache for whether this set's combination of features
//        // has already been scored.
//        if (done[key]) {
//            continue;
//        } else {
//            done[key] = true;
//        }

//        rows = new Array(coalesced[c].length);
//        for (var i = 0; i < coalesced[c].length; i++) {
//            rows[i] = feats[coalesced[c][i]];
//        }

//        // Sort by db, relev such that total relev can be
//        // calculated without results for the same db being summed.
//        rows.sort(sortRelevReason);
//        relev = getSetRelevance(query.length, rows);

//        for (i = 0, l = rows.length; i < l; i++) {
//            if (!rows[i]) continue;

//            sets[rows[i].tmpid] = sets[rows[i].tmpid] || rows[i];

//            // Don't use results after the topmost index in the stack.
//            if (pushed && pushed !== rows[i].dbid) continue;
//            pushed = rows[i].dbid;
//            prev = rowMemo[rows[i].tmpid];

//            // Don't overwrite an existing result entry for the same
//            // feature that scores higher.
//            if (prev && prev.relev > (relev + addrmod[rows[i].dbid])) continue;

//            rowMemo[rows[i].tmpid] = {
//                dbid: rows[i].dbid,
//                id: rows[i].id,
//                tmpid: rows[i].tmpid,
//                relev: relev + addrmod[rows[i].dbid],
//                idx: rows[i].idx
//            };
//        }
//    }

//    var results = [],
//        contexts = [],
//        formatted = [],
//        memo = null;

//    for (var j in rowMemo) {
//        results.push(rowMemo[j]);
//    }

//    results.sort(sortByRelev);

//    for (var k = 0; k < results.length; k++) {
//        feat = results[k];
//        if (memo === null || memo - feat.relev < 0.1) {
//            memo = memo || feat.relev;
//            formatted.push(feat);
//        }
//    }

//    results = formatted;

//    stats.relevTime = +new Date() - stats.relevTime;
//    stats.relevCount = results.length;

//   //Stores the proximity latlng as zxy at all source zoom levels
//    var zxyProximity = {};

//    //If latlng is given, favour results closest locale
//    if (options.proximity) {
//        for (var i = 0; i < zooms.length; i++) {
//            zxyProximity[zooms[i]] = sm.px(options.proximity, zooms[i]);
//        }
//        var zxyResult = [];
//        for (var i = 0; i < results.length; i++) {
//            results[i].z = geocoder.indexes[results[i].dbid]._geocoder.zoom;
//            results[i].x = Math.floor(results[i].tmpid / xd);
//            results[i].y = Math.floor(results[i].tmpid % xd / yd);
//            results[i].distance = Math.pow(Math.pow(zxyProximity[results[i].z][0] - results[i].x, 2) + Math.pow(zxyProximity[results[i].z][1] - results[i].y, 2), 0.5);
//        }
//        results.sort(sortByProximity);
//    }

//    callback(null, {
//        sets: sets,
//        results: results,
//        coalesced: coalesced
//    });
//    });
}

function sortByProximity(a, b) {
    if (a.idx > b.idx) return -1;
    else if (a.idx < b.idx) return 1;
    else if (a.relev > b.relev) return -1;
    else if (a.relev < b.relev) return 1;
    else if (a.distance < b.distance) return -1;
    else if (a.distance > b.distance) return 1;
    else if (a.id < b.id) return -1;
    else if (a.id > b.id) return 1;
    return 0;
}

function sortRelevReason(a, b) {
    if (a.idx > b.idx) return -1;
    else if (a.idx < b.idx) return 1;
    else if (a.relev > b.relev) return -1;
    else if (a.relev < b.relev) return 1;
    else if (a.reason > b.reason) return -1;
    else if (a.reason < b.reason) return 1;
    else if (a.id < b.id) return -1;
    else if (a.id > b.id) return 1;
    return 0;
}

function sortByRelev(a, b) {
    return b.relev - a.relev;
}

<|endoftext|>