<commit_before>'use strict';

var inline = require('inline-source').sync;

var isArray = function (obj) {
    return ({}).toString.call(obj) === '[object Array]';
};
var getFileName = function (path) {
    var start = path.lastIndexOf('/');
    start = start > -1 ? start : path.lastIndexOf('\\');
    return path.substr(start + 1);
};

function InlineResourcePlugin(options) {
    this.options = options || {};
    this.options.list = this.options.list || [];
}

InlineResourcePlugin.prototype.doInline = function (options, compilation) {
    if (!isArray(options.list)) {
        options.list = [options.list];
    }
    options.list.forEach(function (path) {
        var html = inline(path, options),
            name = getFileName(path);
        compilation.assets[name] = {
            source: function () {
                return html;
            },
            size: function () {
                return html.length;
            }
        };
    });
};

InlineResourcePlugin.prototype.apply = function (compiler) {
    var doInline = this.doInline,
        options = this.options;
    compiler.plugin('emit', function (compilation, callback) {
        doInline(options, compilation);
        callback();
    });
};

module.exports = InlineResourcePlugin;<commit_msg>add some comment<commit_after>'use strict';

var inline = require('inline-source').sync;

var isArray = function (obj) {
    return ({}).toString.call(obj) === '[object Array]';
};
var getFileName = function (path) {
    var start = path.lastIndexOf('/');
    start = start > -1 ? start : path.lastIndexOf('\\');
    return path.substr(start + 1);
};

function InlineResourcePlugin(options) {
    this.options = options || {};
    this.options.list = this.options.list || [];
}

InlineResourcePlugin.prototype.doInline = function (options, compilation) {
    if (!isArray(options.list)) {
        options.list = [options.list];
    }
    options.list.forEach(function (path) {
        var html = inline(path, options),
            name = getFileName(path);

        //Insert this html file into the Webpack build as a new file asset
        //And webpack will help us generate the new file rather than use fs module to do this by us
        compilation.assets[name] = {
            source: function () {
                //This is the content of file
                return html;
            },
            size: function () {
                //This is the length or size of file
                return html.length;
            }
        };
    });
};

InlineResourcePlugin.prototype.apply = function (compiler) {
    var doInline = this.doInline,
        options = this.options;
    compiler.plugin('emit', function (compilation, callback) {
        doInline(options, compilation);
        callback();
    });
};

module.exports = InlineResourcePlugin;<|endoftext|>


<commit_before>module.exports = WebTorrent

var Buffer = require('safe-buffer').Buffer
var concat = require('simple-concat')
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 path = require('path')
var Peer = require('simple-peer')
var speedometer = require('speedometer')
var zeroFill = require('zero-fill')

var TCPPool = require('./lib/tcp-pool') // browser exclude
var Torrent = require('./lib/torrent')

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

/**
 * Version number in Azureus-style. Generated from major and minor semver version.
 * For example:
 *   '0.16.1' -> '0016'
 *   '1.2.5' -> '0102'
 */
var VERSION_STR = VERSION.match(/([0-9]+)/g).slice(0, 2).map(v => zeroFill(2, v)).join('')

/**
 * Version prefix string (used in peer ID). WebTorrent uses the Azureus-style
 * encoding: '-', two characters for client id ('WW'), four ascii digits for version
 * number, '-', followed by random numbers.
 * For example:
 *   '-WW0102-'...
 */
var VERSION_PREFIX = '-WW' + VERSION_STR + '-'

inherits(WebTorrent, EventEmitter)

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

  if (!opts) opts = {}

  if (typeof opts.peerId === 'string') {
    self.peerId = opts.peerId
  } else if (Buffer.isBuffer(opts.peerId)) {
    self.peerId = opts.peerId.toString('hex')
  } else {
    self.peerId = Buffer.from(VERSION_PREFIX + hat(48))
  }
  self.peerIdBuffer = Buffer.from(self.peerId, 'hex')

  if (typeof opts.nodeId === 'string') {
    self.nodeId = opts.nodeId
  } else if (Buffer.isBuffer(opts.nodeId)) {
    self.nodeId = opts.nodeId.toString('hex')
  } else {
    self.nodeId = hat(160)
  }
  self.nodeIdBuffer = Buffer.from(self.nodeId, 'hex')

  self.destroyed = false
  self.listening = false
  self.torrentPort = opts.torrentPort || 0
  self.dhtPort = opts.dhtPort || 0
  self.tracker = opts.tracker !== undefined ? opts.tracker : {}
  self.torrents = []
  self.maxConns = Number(opts.maxConns) || 55

  if (self.tracker) {
    if (typeof self.tracker !== 'object') self.tracker = {}
    if (opts.rtcConfig) {
      // TODO: remove in v1
      console.warn('WebTorrent: opts.rtcConfig is deprecated. Use opts.tracker.rtcConfig instead')
      self.tracker.rtcConfig = opts.rtcConfig
    }
    if (opts.wrtc) {
      // TODO: remove in v1
      console.warn('WebTorrent: opts.wrtc is deprecated. Use opts.tracker.wrtc instead')
      self.tracker.wrtc = opts.wrtc // to support `webtorrent-hybrid` package
    }
    if (global.WRTC && !self.tracker.wrtc) self.tracker.wrtc = global.WRTC
  }

  if (typeof TCPPool === 'function') {
    self._tcpPool = new TCPPool(self)
  } else {
    process.nextTick(function () {
      self._onListening()
    })
  }

  // stats
  self._downloadSpeed = speedometer()
  self._uploadSpeed = speedometer()

  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.once('error', function (err) {
      self._destroy(err)
    })

    self.dht.once('listening', function () {
      var address = self.dht.address()
      if (address) self.dhtPort = address.port
    })

    // Ignore warning when there are > 10 torrents in the client
    self.dht.setMaxListeners(0)

    self.dht.listen(self.dhtPort)
  } else {
    self.dht = false
  }

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

  if (typeof loadIPSet === 'function') {
    loadIPSet(opts.blocklist, {
      headers: {
        'user-agent': 'WebTorrent/' + VERSION + ' (https://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')
  }
}

WebTorrent.WEBRTC_SUPPORT = Peer.WEBRTC_SUPPORT

Object.defineProperty(WebTorrent.prototype, 'downloadSpeed', {
  get: function () { return this._downloadSpeed() }
})

Object.defineProperty(WebTorrent.prototype, 'uploadSpeed', {
  get: function () { return this._uploadSpeed() }
})

Object.defineProperty(WebTorrent.prototype, 'progress', {
  get: function () {
    var torrents = this.torrents.filter(function (torrent) {
      return torrent.progress !== 1
    })
    var downloaded = torrents.reduce(function (total, torrent) {
      return total + torrent.downloaded
    }, 0)
    var length = torrents.reduce(function (total, torrent) {
      return total + (torrent.length || 0)
    }, 0) || 1
    return downloaded / length
  }
})

Object.defineProperty(WebTorrent.prototype, 'ratio', {
  get: function () {
    var uploaded = this.torrents.reduce(function (total, torrent) {
      return total + torrent.uploaded
    }, 0)
    var received = this.torrents.reduce(function (total, torrent) {
      return total + torrent.received
    }, 0) || 1
    return uploaded / received
  }
})

/**
 * 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
  var i, torrent
  var len = self.torrents.length

  if (torrentId instanceof Torrent) {
    for (i = 0; i < len; i++) {
      torrent = self.torrents[i]
      if (torrent === torrentId) return torrent
    }
  } else {
    var parsed
    try { parsed = parseTorrent(torrentId) } catch (err) {}

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

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

// TODO: remove in v1
WebTorrent.prototype.download = function (torrentId, opts, ontorrent) {
  console.warn('WebTorrent: client.download() is deprecated. Use client.add() instead')
  return this.add(torrentId, opts, ontorrent)
}

/**
 * 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 = function (torrentId, opts, ontorrent) {
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')
  if (typeof opts === 'function') return self.add(torrentId, null, opts)

  debug('add')
  opts = opts ? extend(opts) : {}

  var torrent = new Torrent(torrentId, self, opts)
  self.torrents.push(torrent)

  torrent.once('_infoHash', onInfoHash)
  torrent.once('ready', onReady)
  torrent.once('close', onClose)

  function onInfoHash () {
    if (self.destroyed) return
    for (var i = 0, len = self.torrents.length; i < len; i++) {
      var t = self.torrents[i]
      if (t.infoHash === torrent.infoHash && t !== torrent) {
        torrent._destroy(new Error('Cannot add duplicate torrent ' + torrent.infoHash))
        return
      }
    }
  }

  function onReady () {
    if (self.destroyed) return
    if (typeof ontorrent === 'function') ontorrent(torrent)
    self.emit('torrent', torrent)
  }

  function onClose () {
    torrent.removeListener('_infoHash', onInfoHash)
    torrent.removeListener('ready', onReady)
    torrent.removeListener('close', onClose)
  }

  return torrent
}

/**
 * Start seeding a new file/folder.
 * @param  {string|File|FileList|Buffer|Array.<string|File|Buffer>} input
 * @param  {Object=} opts
 * @param  {function=} onseed called when torrent is seeding
 */
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')
  opts = opts ? extend(opts) : {}

  // When seeding from fs path, initialize store from that path to avoid a copy
  if (typeof input === 'string') opts.path = path.dirname(input)
  if (!opts.createdBy) opts.createdBy = 'WebTorrent/' + VERSION_STR
  if (!self.tracker) opts.announce = []

  var torrent = self.add(null, opts, onTorrent)
  var streams

  if (!Array.isArray(input)) input = [ input ]
  parallel(input.map(function (item) {
    return function (cb) {
      if (isReadable(item)) concat(item, cb)
      else cb(null, item)
    }
  }), function (err, input) {
    if (self.destroyed) return
    if (err) return torrent._destroy(err)

    createTorrent.parseInput(input, opts, function (err, files) {
      if (self.destroyed) return
      if (err) return torrent._destroy(err)

      streams = files.map(function (file) {
        return file.getStream
      })

      createTorrent(input, opts, function (err, torrentBuf) {
        if (self.destroyed) return
        if (err) return torrent._destroy(err)

        var existingTorrent = self.get(torrentBuf)
        if (existingTorrent) {
          torrent._destroy(new Error('Cannot add duplicate torrent ' + existingTorrent.infoHash))
        } else {
          torrent._onTorrentId(torrentBuf)
        }
      })
    })
  })

  function onTorrent (torrent) {
    var tasks = [
      function (cb) {
        torrent.load(streams, cb)
      }
    ]
    if (self.dht) {
      tasks.push(function (cb) {
        torrent.once('dhtAnnounce', cb)
      })
    }
    parallel(tasks, function (err) {
      if (self.destroyed) return
      if (err) return torrent._destroy(err)
      _onseed(torrent)
    })
  }

  function _onseed (torrent) {
    debug('on seed')
    if (typeof onseed === 'function') onseed(torrent)
    torrent.emit('seed')
    self.emit('seed', torrent)
  }

  return torrent
}

/**
 * Remove a torrent from the client.
 * @param  {string|Buffer|Torrent}   torrentId
 * @param  {function} cb
 */
WebTorrent.prototype.remove = function (torrentId, cb) {
  debug('remove')
  var torrent = this.get(torrentId)
  if (!torrent) throw new Error('No torrent with id ' + torrentId)
  this._remove(torrentId, cb)
}

WebTorrent.prototype._remove = function (torrentId, cb) {
  var torrent = this.get(torrentId)
  if (!torrent) return
  this.torrents.splice(this.torrents.indexOf(torrent), 1)
  torrent.destroy(cb)
}

WebTorrent.prototype.address = function () {
  if (!this.listening) return null
  return this._tcpPool
    ? this._tcpPool.server.address()
    : { address: '0.0.0.0', family: 'IPv4', port: 0 }
}

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

WebTorrent.prototype._destroy = function (err, cb) {
  var self = this
  debug('client destroy')
  self.destroyed = true

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

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

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

  parallel(tasks, cb)

  if (err) self.emit('error', err)

  self.torrents = []
  self._tcpPool = null
  self.dht = null
}

WebTorrent.prototype._onListening = function () {
  this.listening = true

  if (this._tcpPool) {
    // Sometimes server.address() returns `null` in Docker.
    // WebTorrent issue: https://github.com/feross/bittorrent-swarm/pull/18
    var address = this._tcpPool.server.address()
    if (address) this.torrentPort = address.port
  }

  this.emit('listening')
}

/**
 * Check if `obj` is a node Readable stream
 * @param  {*} obj
 * @return {boolean}
 */
function isReadable (obj) {
  return typeof obj === 'object' && obj != null && typeof obj.pipe === 'function'
}
<commit_msg>don't use arrow function<commit_after>module.exports = WebTorrent

var Buffer = require('safe-buffer').Buffer
var concat = require('simple-concat')
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 path = require('path')
var Peer = require('simple-peer')
var speedometer = require('speedometer')
var zeroFill = require('zero-fill')

var TCPPool = require('./lib/tcp-pool') // browser exclude
var Torrent = require('./lib/torrent')

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

/**
 * Version number in Azureus-style. Generated from major and minor semver version.
 * For example:
 *   '0.16.1' -> '0016'
 *   '1.2.5' -> '0102'
 */
var m = VERSION.match(/([0-9]+)/g).slice(0, 2)
var VERSION_STR = zeroFill(2, m[0]) + zeroFill(2, m[1])

/**
 * Version prefix string (used in peer ID). WebTorrent uses the Azureus-style
 * encoding: '-', two characters for client id ('WW'), four ascii digits for version
 * number, '-', followed by random numbers.
 * For example:
 *   '-WW0102-'...
 */
var VERSION_PREFIX = '-WW' + VERSION_STR + '-'

inherits(WebTorrent, EventEmitter)

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

  if (!opts) opts = {}

  if (typeof opts.peerId === 'string') {
    self.peerId = opts.peerId
  } else if (Buffer.isBuffer(opts.peerId)) {
    self.peerId = opts.peerId.toString('hex')
  } else {
    self.peerId = Buffer.from(VERSION_PREFIX + hat(48))
  }
  self.peerIdBuffer = Buffer.from(self.peerId, 'hex')

  if (typeof opts.nodeId === 'string') {
    self.nodeId = opts.nodeId
  } else if (Buffer.isBuffer(opts.nodeId)) {
    self.nodeId = opts.nodeId.toString('hex')
  } else {
    self.nodeId = hat(160)
  }
  self.nodeIdBuffer = Buffer.from(self.nodeId, 'hex')

  self.destroyed = false
  self.listening = false
  self.torrentPort = opts.torrentPort || 0
  self.dhtPort = opts.dhtPort || 0
  self.tracker = opts.tracker !== undefined ? opts.tracker : {}
  self.torrents = []
  self.maxConns = Number(opts.maxConns) || 55

  if (self.tracker) {
    if (typeof self.tracker !== 'object') self.tracker = {}
    if (opts.rtcConfig) {
      // TODO: remove in v1
      console.warn('WebTorrent: opts.rtcConfig is deprecated. Use opts.tracker.rtcConfig instead')
      self.tracker.rtcConfig = opts.rtcConfig
    }
    if (opts.wrtc) {
      // TODO: remove in v1
      console.warn('WebTorrent: opts.wrtc is deprecated. Use opts.tracker.wrtc instead')
      self.tracker.wrtc = opts.wrtc // to support `webtorrent-hybrid` package
    }
    if (global.WRTC && !self.tracker.wrtc) self.tracker.wrtc = global.WRTC
  }

  if (typeof TCPPool === 'function') {
    self._tcpPool = new TCPPool(self)
  } else {
    process.nextTick(function () {
      self._onListening()
    })
  }

  // stats
  self._downloadSpeed = speedometer()
  self._uploadSpeed = speedometer()

  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.once('error', function (err) {
      self._destroy(err)
    })

    self.dht.once('listening', function () {
      var address = self.dht.address()
      if (address) self.dhtPort = address.port
    })

    // Ignore warning when there are > 10 torrents in the client
    self.dht.setMaxListeners(0)

    self.dht.listen(self.dhtPort)
  } else {
    self.dht = false
  }

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

  if (typeof loadIPSet === 'function') {
    loadIPSet(opts.blocklist, {
      headers: {
        'user-agent': 'WebTorrent/' + VERSION + ' (https://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')
  }
}

WebTorrent.WEBRTC_SUPPORT = Peer.WEBRTC_SUPPORT

Object.defineProperty(WebTorrent.prototype, 'downloadSpeed', {
  get: function () { return this._downloadSpeed() }
})

Object.defineProperty(WebTorrent.prototype, 'uploadSpeed', {
  get: function () { return this._uploadSpeed() }
})

Object.defineProperty(WebTorrent.prototype, 'progress', {
  get: function () {
    var torrents = this.torrents.filter(function (torrent) {
      return torrent.progress !== 1
    })
    var downloaded = torrents.reduce(function (total, torrent) {
      return total + torrent.downloaded
    }, 0)
    var length = torrents.reduce(function (total, torrent) {
      return total + (torrent.length || 0)
    }, 0) || 1
    return downloaded / length
  }
})

Object.defineProperty(WebTorrent.prototype, 'ratio', {
  get: function () {
    var uploaded = this.torrents.reduce(function (total, torrent) {
      return total + torrent.uploaded
    }, 0)
    var received = this.torrents.reduce(function (total, torrent) {
      return total + torrent.received
    }, 0) || 1
    return uploaded / received
  }
})

/**
 * 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
  var i, torrent
  var len = self.torrents.length

  if (torrentId instanceof Torrent) {
    for (i = 0; i < len; i++) {
      torrent = self.torrents[i]
      if (torrent === torrentId) return torrent
    }
  } else {
    var parsed
    try { parsed = parseTorrent(torrentId) } catch (err) {}

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

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

// TODO: remove in v1
WebTorrent.prototype.download = function (torrentId, opts, ontorrent) {
  console.warn('WebTorrent: client.download() is deprecated. Use client.add() instead')
  return this.add(torrentId, opts, ontorrent)
}

/**
 * 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 = function (torrentId, opts, ontorrent) {
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')
  if (typeof opts === 'function') return self.add(torrentId, null, opts)

  debug('add')
  opts = opts ? extend(opts) : {}

  var torrent = new Torrent(torrentId, self, opts)
  self.torrents.push(torrent)

  torrent.once('_infoHash', onInfoHash)
  torrent.once('ready', onReady)
  torrent.once('close', onClose)

  function onInfoHash () {
    if (self.destroyed) return
    for (var i = 0, len = self.torrents.length; i < len; i++) {
      var t = self.torrents[i]
      if (t.infoHash === torrent.infoHash && t !== torrent) {
        torrent._destroy(new Error('Cannot add duplicate torrent ' + torrent.infoHash))
        return
      }
    }
  }

  function onReady () {
    if (self.destroyed) return
    if (typeof ontorrent === 'function') ontorrent(torrent)
    self.emit('torrent', torrent)
  }

  function onClose () {
    torrent.removeListener('_infoHash', onInfoHash)
    torrent.removeListener('ready', onReady)
    torrent.removeListener('close', onClose)
  }

  return torrent
}

/**
 * Start seeding a new file/folder.
 * @param  {string|File|FileList|Buffer|Array.<string|File|Buffer>} input
 * @param  {Object=} opts
 * @param  {function=} onseed called when torrent is seeding
 */
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')
  opts = opts ? extend(opts) : {}

  // When seeding from fs path, initialize store from that path to avoid a copy
  if (typeof input === 'string') opts.path = path.dirname(input)
  if (!opts.createdBy) opts.createdBy = 'WebTorrent/' + VERSION_STR
  if (!self.tracker) opts.announce = []

  var torrent = self.add(null, opts, onTorrent)
  var streams

  if (!Array.isArray(input)) input = [ input ]
  parallel(input.map(function (item) {
    return function (cb) {
      if (isReadable(item)) concat(item, cb)
      else cb(null, item)
    }
  }), function (err, input) {
    if (self.destroyed) return
    if (err) return torrent._destroy(err)

    createTorrent.parseInput(input, opts, function (err, files) {
      if (self.destroyed) return
      if (err) return torrent._destroy(err)

      streams = files.map(function (file) {
        return file.getStream
      })

      createTorrent(input, opts, function (err, torrentBuf) {
        if (self.destroyed) return
        if (err) return torrent._destroy(err)

        var existingTorrent = self.get(torrentBuf)
        if (existingTorrent) {
          torrent._destroy(new Error('Cannot add duplicate torrent ' + existingTorrent.infoHash))
        } else {
          torrent._onTorrentId(torrentBuf)
        }
      })
    })
  })

  function onTorrent (torrent) {
    var tasks = [
      function (cb) {
        torrent.load(streams, cb)
      }
    ]
    if (self.dht) {
      tasks.push(function (cb) {
        torrent.once('dhtAnnounce', cb)
      })
    }
    parallel(tasks, function (err) {
      if (self.destroyed) return
      if (err) return torrent._destroy(err)
      _onseed(torrent)
    })
  }

  function _onseed (torrent) {
    debug('on seed')
    if (typeof onseed === 'function') onseed(torrent)
    torrent.emit('seed')
    self.emit('seed', torrent)
  }

  return torrent
}

/**
 * Remove a torrent from the client.
 * @param  {string|Buffer|Torrent}   torrentId
 * @param  {function} cb
 */
WebTorrent.prototype.remove = function (torrentId, cb) {
  debug('remove')
  var torrent = this.get(torrentId)
  if (!torrent) throw new Error('No torrent with id ' + torrentId)
  this._remove(torrentId, cb)
}

WebTorrent.prototype._remove = function (torrentId, cb) {
  var torrent = this.get(torrentId)
  if (!torrent) return
  this.torrents.splice(this.torrents.indexOf(torrent), 1)
  torrent.destroy(cb)
}

WebTorrent.prototype.address = function () {
  if (!this.listening) return null
  return this._tcpPool
    ? this._tcpPool.server.address()
    : { address: '0.0.0.0', family: 'IPv4', port: 0 }
}

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

WebTorrent.prototype._destroy = function (err, cb) {
  var self = this
  debug('client destroy')
  self.destroyed = true

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

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

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

  parallel(tasks, cb)

  if (err) self.emit('error', err)

  self.torrents = []
  self._tcpPool = null
  self.dht = null
}

WebTorrent.prototype._onListening = function () {
  this.listening = true

  if (this._tcpPool) {
    // Sometimes server.address() returns `null` in Docker.
    // WebTorrent issue: https://github.com/feross/bittorrent-swarm/pull/18
    var address = this._tcpPool.server.address()
    if (address) this.torrentPort = address.port
  }

  this.emit('listening')
}

/**
 * Check if `obj` is a node Readable stream
 * @param  {*} obj
 * @return {boolean}
 */
function isReadable (obj) {
  return typeof obj === 'object' && obj != null && typeof obj.pipe === 'function'
}
<|endoftext|>


<commit_before>var express = require('express');
var app = express();
var BME280 = require('node-bme280');
var barometer = new BME280({address: 0x77});

barometer.begin(function(err) {
    if (err) {
        console.info('error initializing barometer', err);
        return;
    }
    console.info('barometer running');
});

app.get('/', function (req, res) {
	barometer.readPressureAndTemparature(function(err, pressure, temperature, humidity) {
		res.write("<meta http-equiv=\"refresh\" content=\"5\"><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">");
		res.write("Temperature: " + temperature.toFixed(2) + " ℃
");
		res.write("Pressure: " + (pressure / 100).toFixed(2) + " hPa
");
		res.write("Humidity: " + humidity.toFixed(2) + "%\n");
		res.end("");
	});
});

app.listen(3000, function () {
	console.log('Example app listening on port 3000!');
});
<commit_msg>add nedb<commit_after>var express = require('express');
var app = express();
var BME280 = require('node-bme280');
var barometer = new BME280({address: 0x77});

var Datastore = require('nedb')
  , db = new Datastore({ filename: 'data.txt', autoload: true });

barometer.begin(function(err) {
    if (err) {
        console.info('error initializing barometer', err);
        return;
    }
    console.info('barometer running');
});

app.get('/', function (req, res) {
	barometer.readPressureAndTemparature(function(err, pressure, temperature, humidity) {
		res.write("<meta http-equiv=\"refresh\" content=\"5\"><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">");
		res.write("Temperature: " + temperature.toFixed(2) + " ℃
");
		res.write("Pressure: " + (pressure / 100).toFixed(2) + " hPa
");
		res.write("Humidity: " + humidity.toFixed(2) + "%\n");
		res.end("");
	});
});

app.listen(3000, function () {
	console.log('Example app listening on port 3000!');
});
<|endoftext|>


<commit_before>var spawn       = require('child_process').spawn;
var gulp        = require('gulp');
var PluginError = require('gulp-util').PluginError;
var Promise     = require('pinkie-promise');

var isWorker = process.argv.indexOf('--ll-worker') > -1;
var disabled = process.argv.indexOf('--no-ll') > -1;

var args = process.argv.slice().filter(function (arg) {
    // NOTE: remove debugger breakpoints from worker args
    return arg.indexOf('--debug-brk') < 0;
});

var nodePath     = args.shift();
var tasks        = [];
var originalTask = gulp.task;

function getSpawnArgs (taskName) {
    var spawnArgs = args.filter(function (arg) {
        return tasks.indexOf(arg) < 0;
    });

    spawnArgs.push('ll:' + taskName);
    spawnArgs.push('--ll-worker');

    return spawnArgs;
}

function createLLTask (name) {
    return new Promise(function (resolve, reject) {
        var llTask = spawn(nodePath, getSpawnArgs(name), { stdio: 'inherit' });

        llTask.on('exit', function (code) {
            if (code === 0)
                resolve();
            else {
                reject(new PluginError('ll', {
                    message: 'Task ll:' + name + ' failed'
                }));
            }
        })
    });
}

gulp.task = function (name, deps, fn) {
    // NOTE: collect task names, so we
    // can filter them from worker args
    tasks.push(name);

    originalTask.call(gulp, name, deps, fn);
};

gulp.llTask = function (name, deps, fn) {
    if (!fn && typeof deps === 'function') {
        fn   = deps;
        deps = void 0;
    }

    if (disabled)
        gulp.task(name, deps, fn);

    else if (isWorker) {
        // NOTE: we should run without deps, since
        // they are already executed in the master
        gulp.task('ll:' + name, fn);
    }

    else {
        gulp.task(name, deps, function () {
            return createLLTask(name);
        });
    }
};<commit_msg>Debug only tasks, refactoring, new API<commit_after>var spawn       = require('child_process').spawn;
var gulp        = require('gulp');
var PluginError = require('gulp-util').PluginError;
var Promise     = require('pinkie-promise');

function getTaskListFromArgs (args) {
    if (Array.isArray(args[0]))
        return args[0];

    return Array.prototype.slice.call(args);
}

function GulpLL () {
    this.taskFn = gulp.task.bind(gulp);

    this.llTasks          = [];
    this.llTasksDebugOnly = [];
    this.allTasks         = [];

    this.isDebug  = typeof v8debug !== 'undefined' || process.argv.indexOf('--ll-debug') > -1;
    this.isWorker = process.argv.indexOf('--ll-worker') > -1;

    this.args = process.argv.slice(1).filter(function (arg) {
        // NOTE: remove debugger breakpoints from worker args
        return arg.indexOf('--debug-brk') < 0;
    });

    if (process.argv.indexOf('--no-ll') < 0)
        this._overrideGulpTaskFn()
}

GulpLL.prototype._getWorkerArgs = function (task) {
    var ll = this;

    var args = this.args.filter(function (arg) {
        return ll.allTasks.indexOf(arg) < 0;
    });

    args.push('worker:' + task);
    args.push('--ll-worker');

    if (this.isDebug)
        args.push('--ll-debug');

    return args;
};

GulpLL.prototype._createWorker = function (task) {
    var worker = spawn(process.execPath, this._getWorkerArgs(task), { stdio: 'inherit' });

    return new Promise(function (resolve, reject) {
        worker.on('exit', function (code) {
            if (code === 0)
                resolve();
            else {
                reject(new PluginError('ll', {
                    message: 'Task ll:' + task + ' failed'
                }));
            }
        })
    });
};

GulpLL.prototype._isLLTask = function (task) {
    return this.llTasks.indexOf(task) > -1 || (this.isDebug && this.llTasksDebugOnly.indexOf(task) > -1);
};

GulpLL.prototype._overrideGulpTaskFn = function () {
    var ll = this;

    gulp.task = function (name, deps, fn) {
        if (!fn && typeof deps === 'function') {
            fn   = deps;
            deps = void 0;
        }

        if (ll._isLLTask(name)) {
            if (ll.isWorker) {
                deps = void 0;
                name = 'worker:' + name;
            }
            else {
                fn = function () {
                    return ll._createWorker(name);
                };
            }
        }

        ll.allTasks.push(name);
        ll.taskFn(name, deps, fn);
    };
};

GulpLL.prototype.tasks = function () {
    this.llTasks = getTaskListFromArgs(arguments);
    return this;
};

GulpLL.prototype.onlyInDebug = function () {
    this.llTasksDebugOnly = getTaskListFromArgs(arguments);
    return this;
};

module.exports = new GulpLL();
<|endoftext|>


<commit_before>'use strict';

var express = require('express');
var favicon = require('serve-favicon');
var path = require('path');

module.exports = function(app, baseDir, config) {
    var env = app.get('env');
    var servedDirectory = 'app';

    if ('development' === env) {
        // Disable caching of scripts for easier testing
        app.use(function noCache(req, res, next) {
            if (req.url.indexOf('/js/') === 0) {
                res.header('Cache-Control', 'no-cache, no-store, must-revalidate');
                res.header('Pragma', 'no-cache');
                res.header('Expires', 0);
            }
            next();
        });

        app.use(config.baseUrl, express.static(path.join(__dirname, '.tmp')));
    }

    if ('production' === env) {
        // prerender.io
        app.use(require('prerender-node').set('prerenderToken', process.env.PRERENDER_TOKEN).set('protocol', 'https'));

        servedDirectory = 'dist';
        app.use(favicon(path.join(__dirname, 'dist', 'favicon.ico')));
    }

    app.use(config.baseUrl, express.static(path.join(__dirname, servedDirectory)));
    app.use(config.baseUrl + '/scripts/template.js', express.static(path.join(baseDir, config.scenarioTemplate)));
    app.use(config.baseUrl + '/scripts/constants.js', express.static(path.join(baseDir, config.constants)));

    app.get(config.baseUrl + '/scripts/server-config.js', function (req, res) {
        res.type('application/javascript').send('window.serverConfig = ' + JSON.stringify(config.serverConfig || {}) + ';');
    });

    app.route(config.baseUrl + '/*').get(function(req, res) {
        res.sendFile(path.join(__dirname, servedDirectory, 'index.html'));
    });
};
<commit_msg>Remove unmatchable path special headers<commit_after>'use strict';

var express = require('express');
var favicon = require('serve-favicon');
var path = require('path');

module.exports = function(app, baseDir, config) {
    var env = app.get('env');
    var servedDirectory = 'app';

    if ('development' === env) {
        app.use(config.baseUrl, express.static(path.join(__dirname, '.tmp')));
    }

    if ('production' === env) {
        // prerender.io
        app.use(require('prerender-node').set('prerenderToken', process.env.PRERENDER_TOKEN).set('protocol', 'https'));

        servedDirectory = 'dist';
        app.use(favicon(path.join(__dirname, 'dist', 'favicon.ico')));
    }

    app.use(config.baseUrl, express.static(path.join(__dirname, servedDirectory)));
    app.use(config.baseUrl + '/scripts/template.js', express.static(path.join(baseDir, config.scenarioTemplate)));
    app.use(config.baseUrl + '/scripts/constants.js', express.static(path.join(baseDir, config.constants)));

    app.get(config.baseUrl + '/scripts/server-config.js', function (req, res) {
        res.type('application/javascript').send('window.serverConfig = ' + JSON.stringify(config.serverConfig || {}) + ';');
    });

    app.route(config.baseUrl + '/*').get(function(req, res) {
        res.sendFile(path.join(__dirname, servedDirectory, 'index.html'));
    });
};
<|endoftext|>


<commit_before>var fs = require('fs');
module.exports = function (repl, file) {

  try {
    var stat = fs.statSync(file);
    repl.rli.history = fs.readFileSync(file, 'utf-8').split('\n').reverse();
    repl.rli.history.shift();
    repl.rli.historyIndex = -1; // will be incremented before pop
  } catch (e) {}

  var fd = fs.openSync(file, 'a'), reval = repl.eval;

  repl.rli.addListener('line', function(code) {
    if (code && code !== '.history') {
      fs.write(fd, code + '\n');
    } else {
      repl.rli.historyIndex++;
      repl.rli.history.pop();
    }
  });

  process.on('exit', function() {
    fs.closeSync(fd);
  });

  repl.commands['history'] = {
    help : 'Show the history',
    action : function() {
      var out = [];
      repl.rli.history.forEach(function(v, k) {
        out.push(v);
      });
      repl.outputStream.write(out.reverse().join('\n') + '\n');
      repl.displayPrompt();
    }
  };
};
<commit_msg>Prevent deprecation warning in node 7.4.0<commit_after>var fs = require('fs');
module.exports = function (repl, file) {

  try {
    var stat = fs.statSync(file);
    repl.rli.history = fs.readFileSync(file, 'utf-8').split('\n').reverse();
    repl.rli.history.shift();
    repl.rli.historyIndex = -1; // will be incremented before pop
  } catch (e) {}

  var fd = fs.openSync(file, 'a'), reval = repl.eval;
  var wstream = fs.createWriteStream(file, {
    fd: fd
  });
  wstream.on('error', function(err) {
    throw err;
  });

  repl.rli.addListener('line', function(code) {
    if (code && code !== '.history') {
      wstream.write(code + '\n');
    } else {
      repl.rli.historyIndex++;
      repl.rli.history.pop();
    }
  });

  process.on('exit', function() {
    fs.closeSync(fd);
  });

  repl.commands['history'] = {
    help : 'Show the history',
    action : function() {
      var out = [];
      repl.rli.history.forEach(function(v, k) {
        out.push(v);
      });
      repl.outputStream.write(out.reverse().join('\n') + '\n');
      repl.displayPrompt();
    }
  };
};
<|endoftext|>


<commit_before>'use strict';

const Funnel = require('broccoli-funnel');
const MergeTrees = require('broccoli-merge-trees');
const Path = require('path');

module.exports = {
  name: 'ember-mapbox-gl',

  treeForStyles(tree) {
    const mapboxGlTree = new Funnel(Path.dirname(require.resolve('mapbox-gl')), {
      files: [ 'mapbox-gl.css' ],
      destDir: 'app/styles'
    });

    if (tree) {
      return new MergeTrees([ tree, mapboxGlTree ]);
    }

    return mapboxGlTree;
  },

  treeForVendor(tree) {
    const mapboxGlTree = new Funnel(Path.dirname(require.resolve('mapbox-gl')), {
      files: [
        'mapbox-gl.js',
        'mapbox-gl.js.map',
        'mapbox-gl-dev.js'
      ],
      destDir: '/mapbox-gl'
    });

    if (tree) {
      return new MergeTrees([ tree, mapboxGlTree ]);
    }

    return mapboxGlTree;
  },

  included(app) {
    this._super.included.apply(this, arguments);

    const srcFile = process.env.EMBER_ENV === 'production'
      ? 'vendor/mapbox-gl/mapbox-gl.js'
      : 'vendor/mapbox-gl/mapbox-gl-dev.js';

    app.import(srcFile, {
      using: [
        { transformation: 'amd', as: 'mapbox-gl' }
      ]
    });

    app.import('app/styles/mapbox-gl.css');
  }
};
<commit_msg>fix source maps<commit_after>'use strict';

const Funnel = require('broccoli-funnel');
const MergeTrees = require('broccoli-merge-trees');
const Path = require('path');

module.exports = {
  name: 'ember-mapbox-gl',

  treeForStyles(tree) {
    const mapboxGlTree = new Funnel(Path.dirname(require.resolve('mapbox-gl')), {
      files: [ 'mapbox-gl.css' ],
      destDir: 'app/styles'
    });

    if (tree) {
      return new MergeTrees([ tree, mapboxGlTree ]);
    }

    return mapboxGlTree;
  },

  treeForVendor(tree) {
    const mapboxGlTree = new Funnel(Path.dirname(require.resolve('mapbox-gl')), {
      files: [
        this._srcFile()
      ],
      destDir: '/mapbox-gl'
    });

    if (tree) {
      return new MergeTrees([ tree, mapboxGlTree ]);
    }

    return mapboxGlTree;
  },

  treeForPublic(tree) {
    if (process.env.EMBER_ENV === 'production') {
      const mapboxGlTree = new Funnel(Path.dirname(require.resolve('mapbox-gl')), {
        files: [
          'mapbox-gl.js.map'
        ],
        destDir: '/assets'
      });

      if (tree) {
        return new MergeTrees([ tree, mapboxGlTree ]);
      }

      return mapboxGlTree;
    }

    return tree;
  },

  included(app) {
    this._super.included.apply(this, arguments);

    app.import('vendor/mapbox-gl/' + this._srcFile(), {
      using: [
        { transformation: 'amd', as: 'mapbox-gl' }
      ]
    });

    app.import('app/styles/mapbox-gl.css');
  },

  _srcFile() {
    return process.env.EMBER_ENV === 'production'
      ? 'mapbox-gl.js'
      : 'mapbox-gl-dev.js';
  }
};
<|endoftext|>


<commit_before>var _ = require('lodash');
var async = require('async-chainable');
var barcodeReader = require('quagga').default;
var events = require('events');
var fs = require('fs');
var temp = require('temp');
var pdfImage = require('pdf-image').PDFImage;
var scissors = require('scissors');
var util = require('util');

/**
 * PDFDicer functions that gives the all functionality.
 */
function PDFDicer() {
	var dicer = this;

	dicer._defaults = {
		reader: {
			numOfWorkers: 0, // Always 0 in Node backend
			locate: false, // We have to indicate the location of the barcode
			inputStream: {
				size: false, // Force full image resolution
			},
			decoder: {
				readers: ['code_128_reader'],
				multiple: false,
			},
		},
		temp: {
			prefix: 'pdfdicer-',
		},
		threads: {
			pages: 1,
			areas: 1,
		},
	};

	dicer.new = () => new PDFDicer();

	dicer.defaults = function(options) {
		_.merge(this._defaults, options);
		return this;
	};

	dicer.areas = function(areas) {
		this._defaults.areas = areas;
		return this;
	};

	/**
	 * Take an input file and split it into many PDF files based on configured options
	 * @emits stage Fired at each stage of the process. ENUM: 'init', 'readPDF', 'readPages', 'extracted', 'loadRange', 'splitPDFWithScissors'
	 * 							'splitPDFWithScissors' stage is thrown as many times as is needed.
	 * @emits tempDir The temp dir used for storage
	 * @emits pageConverted  Fired with each page and pageOffset extracted as they are extracted
	 * @emits pagesConverted Fired with an array of all extracted page collection
	 * @emits pageAnalyze Fired before each page gets analyzed with the page object and offset
	 * @emits pageAnalyzed Fired after each page has been analyzed with the page object and offset
	 * @emits pagesAnalyzed Fired after all pages have been analyzed with the page collection
	 * @emits rangeExtracted Fired after the splitted range has been calculated from the original input pdf
	 * @emits split Fired after each pdf has been separated in the range and it gives you the pdf resultant
	 * @emits splitted Fired after pdf have been separated in the range
	 * @param {string} input The input path to process (ends in .pdf usually)
	 * @param {Object} [options] Optional options which override the defaults
	 * @param {function} callback Callback function to fire on completion or error
	 * @return {PDFDicer} This chainable object
	 */
	dicer.split = function(input, options, callback) {
		// Argument mangling {{{
		if (_.isFunction(options)) { // input, options
			callback = options;
			options = {};
		} else if (!_.isFunction(callback)) {
			throw new Error('Callback required as the final parametert of pdfDicer.convert()');
		}
		// }}}

		var settings = _.defaults({}, options, this._defaults);

		async()
			// Sanity checks {{{
			.then(function(next) {
				dicer.emit('stage', 'init');
				if (!_.isString(input)) return next('Unknown input type');
				next();
			})
			// }}}
			// Check file exists {{{
			.then(function(next) {
				fs.access(input, err => next(err ? 'File not found' : null));
			})
			// }}}
			// Prepare (temp directory) {{{
			.then('tempDir', function(next) {
				temp.mkdir(settings.temp, next);
			})
			// }}}
			// Build the PDF processing instance {{{
			// More information about image processing on https://www.imagemagick.org/script/command-line-processing.php#option
			.then('pdf', function(next) {
				dicer.emit('tempDir', this.tempDir);
				next(null, new pdfImage(input, {
					outputDirectory: this.tempDir,
					convertOptions: {
						'-quality': 100,
						'-density': 150,
						'-background': 'white',
						'-alpha': 'remove'
					},
				}));
			})
			// }}}
			// Extract info about the input file {{{
			.then('info', function(next) {
				dicer.emit('stage', 'readPDF');
				this.pdf.getInfo()
					.catch(e => next(e))
					.then(data => next(null, data), next)
			})
			// }}}
			// Convert the file into PNG images {{{
			.set('pages', {})
			.then(function(next) {
				dicer.emit('stage', 'readPages');
				async()
					.set('pdf', this.pdf)
					.set('pages', this.pages)
					.forEach(0, parseInt(this.info.Pages) - 1, function(next, pageNumber) {
						this.pdf.convertPage(pageNumber)
							.then(path => {
								this.pages[pageNumber] = {
									path: path,
								},
								dicer.emit('pageConverted', this.pages[pageNumber], pageNumber);
								next();
							})
							.catch(next)
					})
					.end(next);
			})
			// }}}
			// Post conversion emitters {{{
			.then(function(next) {
				dicer.emit('pagesConverted', this.pages);
				dicer.emit('stage', 'extracted');
				next();
			})
			// }}}
			// Read each page and extract a barcode if there is one {{{
			.limit(settings.threads.pages)
			.forEach('pages', function(nextPage, page, pageOffset) {
				dicer.emit('pageAnalyze', page, pageOffset)
				page.barcode = false;
				async()
					.limit(settings.threads.areas)
					.forEach(settings.areas, function(nextArea, area) {
						barcodeReader.decodeSingle(_.merge({}, settings.reader, {
							src: page.path,
							inputStream: {
								area: area,
							},
						}), function(res) {
							if (!page.barcode && res && res.codeResult) page.barcode = res.codeResult.code;
							nextArea();
						});
					})
					.then(function(next) {
						dicer.emit('pageAnalyzed', page, pageOffset);
						next();
					})
					.end(nextPage);
			})
			// }}}
			// Post pages analyzed emitters {{{
			.then(function(next) {
				dicer.emit('pagesAnalyzed', this.pages);
				next();
			})
			// }}}
			// Build range to split the pdf {{{
			.set('range', new Object())
			.then(function(next) {
				dicer.emit('stage', 'loadRange');
				var memBarcodeID = '';
				var rangeCount = 1;
				var index = 0;

				try {
					for (var key in this.pages) {
						if (this.pages.hasOwnProperty(key)) {
							var page = this.pages[key];
							memBarcodeID = (page.barcode === false) ? memBarcodeID : page.barcode.split('-')[0];

							if (this.range[memBarcodeID] == null) {
								this.range[memBarcodeID] = new Object();
								this.range[memBarcodeID].barcode = new Object();
								try {
									this.range[memBarcodeID].barcode.id = page.barcode.substring(page.barcode.lastIndexOf("/") + 1, page.barcode.length);
								} catch (error) {
									// The barcode scanner is not able to find the barcode information.
									this.range[memBarcodeID].barcode.id = page.barcode;
								}
								this.range[memBarcodeID].barcode.start = page.barcode;
								this.range[memBarcodeID].pages = 1;
								this.range[memBarcodeID].from = index + 1;
							} else {
								this.range[memBarcodeID].pages++;
								if (page.barcode !== false)
									this.range[memBarcodeID].barcode.end = page.barcode;
							}
						}
						index++;
					}
				} catch (error) {
					return next(error);
				}

				next();
			})
			// }}}
			// Emits the data with the range to split the pdf {{{
			.then(function(next) {
				dicer.emit('rangeExtracted', this.range);
				next();
			})
			// }}}
			// Gives the resultant split pdfs {{{
			.forEach('range', function(nextRange, range, rangeIndex) {
				dicer.emit('stage', 'splitPDFWithScissors');
				var from = range.from;
				var to = range.from + range.pages - 1;

				dicer.emit('split', range, scissors(input).range(from, to).pdfStream());

				nextRange();
			})
			// }}}
			// Emits the end signal for the functionality {{{
			.then(function(next) {
				dicer.emit('splitted', true);
				next();
			})
			// }}}
			.end(callback);

		return this;
	};
};

util.inherits(PDFDicer, events.EventEmitter);

module.exports = PDFDicer;
<commit_msg>Added error log when it extracts the barcode information<commit_after>var _ = require('lodash');
var async = require('async-chainable');
var barcodeReader = require('quagga').default;
var events = require('events');
var fs = require('fs');
var temp = require('temp');
var pdfImage = require('pdf-image').PDFImage;
var scissors = require('scissors');
var util = require('util');

/**
 * PDFDicer functions that gives the all functionality.
 */
function PDFDicer() {
	var dicer = this;

	dicer._defaults = {
		reader: {
			numOfWorkers: 0, // Always 0 in Node backend
			locate: false, // We have to indicate the location of the barcode
			inputStream: {
				size: false, // Force full image resolution
			},
			decoder: {
				readers: ['code_128_reader'],
				multiple: false,
			},
		},
		temp: {
			prefix: 'pdfdicer-',
		},
		threads: {
			pages: 1,
			areas: 1,
		},
	};

	dicer.new = () => new PDFDicer();

	dicer.defaults = function(options) {
		_.merge(this._defaults, options);
		return this;
	};

	dicer.areas = function(areas) {
		this._defaults.areas = areas;
		return this;
	};

	/**
	 * Take an input file and split it into many PDF files based on configured options
	 * @emits stage Fired at each stage of the process. ENUM: 'init', 'readPDF', 'readPages', 'extracted', 'loadRange', 'splitPDFWithScissors'
	 * 							'splitPDFWithScissors' stage is thrown as many times as is needed.
	 * @emits tempDir The temp dir used for storage
	 * @emits pageConverted  Fired with each page and pageOffset extracted as they are extracted
	 * @emits pagesConverted Fired with an array of all extracted page collection
	 * @emits pageAnalyze Fired before each page gets analyzed with the page object and offset
	 * @emits pageAnalyzed Fired after each page has been analyzed with the page object and offset
	 * @emits pagesAnalyzed Fired after all pages have been analyzed with the page collection
	 * @emits rangeExtracted Fired after the splitted range has been calculated from the original input pdf
	 * @emits split Fired after each pdf has been separated in the range and it gives you the pdf resultant
	 * @emits splitted Fired after pdf have been separated in the range
	 * @param {string} input The input path to process (ends in .pdf usually)
	 * @param {Object} [options] Optional options which override the defaults
	 * @param {function} callback Callback function to fire on completion or error
	 * @return {PDFDicer} This chainable object
	 */
	dicer.split = function(input, options, callback) {
		// Argument mangling {{{
		if (_.isFunction(options)) { // input, options
			callback = options;
			options = {};
		} else if (!_.isFunction(callback)) {
			throw new Error('Callback required as the final parametert of pdfDicer.convert()');
		}
		// }}}

		var settings = _.defaults({}, options, this._defaults);

		async()
			// Sanity checks {{{
			.then(function(next) {
				dicer.emit('stage', 'init');
				if (!_.isString(input)) return next('Unknown input type');
				next();
			})
			// }}}
			// Check file exists {{{
			.then(function(next) {
				fs.access(input, err => next(err ? 'File not found' : null));
			})
			// }}}
			// Prepare (temp directory) {{{
			.then('tempDir', function(next) {
				temp.mkdir(settings.temp, next);
			})
			// }}}
			// Build the PDF processing instance {{{
			// More information about image processing on https://www.imagemagick.org/script/command-line-processing.php#option
			.then('pdf', function(next) {
				dicer.emit('tempDir', this.tempDir);
				next(null, new pdfImage(input, {
					outputDirectory: this.tempDir,
					convertOptions: {
						'-quality': 100,
						'-density': 150,
						'-background': 'white',
						'-alpha': 'remove'
					},
				}));
			})
			// }}}
			// Extract info about the input file {{{
			.then('info', function(next) {
				dicer.emit('stage', 'readPDF');
				this.pdf.getInfo()
					.catch(e => next(e))
					.then(data => next(null, data), next)
			})
			// }}}
			// Convert the file into PNG images {{{
			.set('pages', {})
			.then(function(next) {
				dicer.emit('stage', 'readPages');
				async()
					.set('pdf', this.pdf)
					.set('pages', this.pages)
					.forEach(0, parseInt(this.info.Pages) - 1, function(next, pageNumber) {
						this.pdf.convertPage(pageNumber)
							.then(path => {
								this.pages[pageNumber] = {
									path: path,
								},
								dicer.emit('pageConverted', this.pages[pageNumber], pageNumber);
								next();
							})
							.catch(next)
					})
					.end(next);
			})
			// }}}
			// Post conversion emitters {{{
			.then(function(next) {
				dicer.emit('pagesConverted', this.pages);
				dicer.emit('stage', 'extracted');
				next();
			})
			// }}}
			// Read each page and extract a barcode if there is one {{{
			.limit(settings.threads.pages)
			.forEach('pages', function(nextPage, page, pageOffset) {
				dicer.emit('pageAnalyze', page, pageOffset)
				page.barcode = false;
				async()
					.limit(settings.threads.areas)
					.forEach(settings.areas, function(nextArea, area) {
						barcodeReader.decodeSingle(_.merge({}, settings.reader, {
							src: page.path,
							inputStream: {
								area: area,
							},
						}), function(res) {
							if (!page.barcode && res && res.codeResult) page.barcode = res.codeResult.code;
							nextArea();
						});
					})
					.then(function(next) {
						dicer.emit('pageAnalyzed', page, pageOffset);
						next();
					})
					.end(nextPage);
			})
			// }}}
			// Post pages analyzed emitters {{{
			.then(function(next) {
				dicer.emit('pagesAnalyzed', this.pages);
				next();
			})
			// }}}
			// Build range to split the pdf {{{
			.set('range', new Object())
			.then(function(next) {
				dicer.emit('stage', 'loadRange');
				var memBarcodeID = '';
				var rangeCount = 1;
				var index = 0;

				try {
					for (var key in this.pages) {
						if (this.pages.hasOwnProperty(key)) {
							var page = this.pages[key];
							memBarcodeID = (page.barcode === false) ? memBarcodeID : page.barcode.split('-')[0];

							if (this.range[memBarcodeID] == null) {
								this.range[memBarcodeID] = new Object();
								this.range[memBarcodeID].barcode = new Object();
								try {
									this.range[memBarcodeID].barcode.id = page.barcode.substring(page.barcode.lastIndexOf("/") + 1, page.barcode.length);
								} catch (error) {
									// The barcode scanner is not able to find the barcode information.
									this.range[memBarcodeID].barcode.id = page.barcode;
								}
								this.range[memBarcodeID].barcode.start = page.barcode;
								this.range[memBarcodeID].pages = 1;
								this.range[memBarcodeID].from = index + 1;
							} else {
								this.range[memBarcodeID].pages++;
								if (page.barcode !== false)
									this.range[memBarcodeID].barcode.end = page.barcode;
							}
						}
						index++;
					}
				} catch (error) {
					console.error('[PDF-DICER] Error', error);
					return next(error);
				}

				next();
			})
			// }}}
			// Emits the data with the range to split the pdf {{{
			.then(function(next) {
				dicer.emit('rangeExtracted', this.range);
				next();
			})
			// }}}
			// Gives the resultant split pdfs {{{
			.forEach('range', function(nextRange, range, rangeIndex) {
				dicer.emit('stage', 'splitPDFWithScissors');
				var from = range.from;
				var to = range.from + range.pages - 1;

				dicer.emit('split', range, scissors(input).range(from, to).pdfStream());

				nextRange();
			})
			// }}}
			// Emits the end signal for the functionality {{{
			.then(function(next) {
				dicer.emit('splitted', true);
				next();
			})
			// }}}
			.end(callback);

		return this;
	};
};

util.inherits(PDFDicer, events.EventEmitter);

module.exports = PDFDicer;
<|endoftext|>


<commit_before>var request = require('request')

function FBMessenger (token, notificationType) {
  this.token = token
  this.notificationType = notificationType || 'REGULAR'
}

FBMessenger.prototype.sendTextMessage = function (id, text, notificationType, cb) {
  var messageData = {
    text: text
  }
  this.sendMessage(id, messageData, notificationType, cb)
}

FBMessenger.prototype.sendImageMessage = function (id, imageURL, notificationType, cb) {
  var messageData = {
    'attachment': {
      'type': 'image',
      'payload': {
        'url': imageURL
      }
    }
  }
  this.sendMessage(id, messageData, notificationType, cb)
}

FBMessenger.prototype.sendGenericMessage =
FBMessenger.prototype.sendHScrollMessage = function (id, elements, notificationType, cb) {
  var messageData = {
    'attachment': {
      'type': 'template',
      'payload': {
        'template_type': 'generic',
        'elements': elements
      }
    }
  }
  this.sendMessage(id, messageData, notificationType, cb)
}

FBMessenger.prototype.sendButtonMessage =
FBMessenger.prototype.sendButtonsMessage = function (id, text, buttons, notificationType, cb) {
  var messageData = {
    'attachment': {
      'type': 'template',
      'payload': {
        'template_type': 'button',
        'text': text,
        'buttons': buttons
      }
    }
  }
  this.sendMessage(id, messageData, notificationType, cb)
}

FBMessenger.prototype.sendReceiptMessage = function (id, payload, notificationType, cb) {
  payload.template_type = 'receipt'
  var messageData = {
    'attachment': {
      'type': 'template',
      'payload': payload
    }
  }
  this.sendMessage(id, messageData, notificationType, cb)
}

FBMessenger.prototype.sendMessage = function (id, data, notificationType, cb) {
  notificationType = notificationType || this.notificationType
  if (typeof notificationType === 'function') {
    cb = notificationType
    notificationType = this.notificationType
  }
  var req = {
    url: 'https://graph.facebook.com/v2.6/me/messages',
    qs: {access_token: this.token},
    method: 'POST',
    json: {
      recipient: {id: id},
      message: data,
      notification_type: notificationType
    }
  }
  request(req, function (err, res, body) {
    if (!cb) return
    if (err) return cb(err)
    if (body.error) return cb(body.error)
    cb(null, body)
  })
}

FBMessenger.prototype.getProfile = function (id, cb) {
  var req = {
    method: 'GET',
    uri: 'https://graph.facebook.com/v2.6/' + id,
    qs: {
      fields: 'first_name,last_name,profile_pic',
      access_token: this.token
    },
    json: true
  }
  request(req, function (err, res, body) {
    if (!cb) return
    if (err) return cb(err)
    if (body.error) return cb(body.error)
    cb(null, body)
  })
}

FBMessenger.prototype.setWelcomeMessage = function (pageId, message, cb) {
  if (typeof message === 'string') {
    message = {
      text: message
    }
  } else {
    message = {
      attachment: {
        type: 'template',
        payload: message
      }
    }
  }
  var req = {
    method: 'POST',
    uri: 'https://graph.facebook.com/v2.6/' + pageId + '/thread_settings',
    qs: {
      access_token: this.token
    },
    json: {
      setting_type: 'call_to_actions',
      thread_state: 'new_thread',
      call_to_actions:[
        {
          message: message
        }
      ]
    }
  }
  request(req, function (err, res, body) {
    if (!cb) return
    if (err) return cb(err)
    if (body.error) return cb(body.error)
    cb(null, body)
  })
}

module.exports = FBMessenger
<commit_msg>Get all fields<commit_after>var request = require('request')

function FBMessenger (token, notificationType) {
  this.token = token
  this.notificationType = notificationType || 'REGULAR'
}

FBMessenger.prototype.sendTextMessage = function (id, text, notificationType, cb) {
  var messageData = {
    text: text
  }
  this.sendMessage(id, messageData, notificationType, cb)
}

FBMessenger.prototype.sendImageMessage = function (id, imageURL, notificationType, cb) {
  var messageData = {
    'attachment': {
      'type': 'image',
      'payload': {
        'url': imageURL
      }
    }
  }
  this.sendMessage(id, messageData, notificationType, cb)
}

FBMessenger.prototype.sendGenericMessage =
FBMessenger.prototype.sendHScrollMessage = function (id, elements, notificationType, cb) {
  var messageData = {
    'attachment': {
      'type': 'template',
      'payload': {
        'template_type': 'generic',
        'elements': elements
      }
    }
  }
  this.sendMessage(id, messageData, notificationType, cb)
}

FBMessenger.prototype.sendButtonMessage =
FBMessenger.prototype.sendButtonsMessage = function (id, text, buttons, notificationType, cb) {
  var messageData = {
    'attachment': {
      'type': 'template',
      'payload': {
        'template_type': 'button',
        'text': text,
        'buttons': buttons
      }
    }
  }
  this.sendMessage(id, messageData, notificationType, cb)
}

FBMessenger.prototype.sendReceiptMessage = function (id, payload, notificationType, cb) {
  payload.template_type = 'receipt'
  var messageData = {
    'attachment': {
      'type': 'template',
      'payload': payload
    }
  }
  this.sendMessage(id, messageData, notificationType, cb)
}

FBMessenger.prototype.sendMessage = function (id, data, notificationType, cb) {
  notificationType = notificationType || this.notificationType
  if (typeof notificationType === 'function') {
    cb = notificationType
    notificationType = this.notificationType
  }
  var req = {
    url: 'https://graph.facebook.com/v2.6/me/messages',
    qs: {access_token: this.token},
    method: 'POST',
    json: {
      recipient: {id: id},
      message: data,
      notification_type: notificationType
    }
  }
  request(req, function (err, res, body) {
    if (!cb) return
    if (err) return cb(err)
    if (body.error) return cb(body.error)
    cb(null, body)
  })
}

FBMessenger.prototype.getProfile = function (id, cb) {
  var req = {
    method: 'GET',
    uri: 'https://graph.facebook.com/v2.6/' + id,
    qs: {
      fields: 'first_name,last_name,profile_pic,locale,timezone,gender',
      access_token: this.token
    },
    json: true
  }
  request(req, function (err, res, body) {
    if (!cb) return
    if (err) return cb(err)
    if (body.error) return cb(body.error)
    cb(null, body)
  })
}

FBMessenger.prototype.setWelcomeMessage = function (pageId, message, cb) {
  if (typeof message === 'string') {
    message = {
      text: message
    }
  } else {
    message = {
      attachment: {
        type: 'template',
        payload: message
      }
    }
  }
  var req = {
    method: 'POST',
    uri: 'https://graph.facebook.com/v2.6/' + pageId + '/thread_settings',
    qs: {
      access_token: this.token
    },
    json: {
      setting_type: 'call_to_actions',
      thread_state: 'new_thread',
      call_to_actions:[
        {
          message: message
        }
      ]
    }
  }
  request(req, function (err, res, body) {
    if (!cb) return
    if (err) return cb(err)
    if (body.error) return cb(body.error)
    cb(null, body)
  })
}

module.exports = FBMessenger
<|endoftext|>


<commit_before>'use strict'

// ===================================================================

var Bluebird = require('bluebird')

// ===================================================================

// Faster `Function.bind()`.
function bind (fn, ctx) {
  return function bindedFunction () {
    return fn.apply(ctx, arguments)
  }
}

function noop () {}

var toArray = Array.from || (function (slice) {
  return bind(slice.call, slice)
})(Array.prototype.slice)

// ===================================================================

function eventToPromise (emitter, event) {
  return new Bluebird(function (resolve, reject) {
    // Some emitter do not implement removeListener.
    var removeListener = emitter.removeListener ?
      bind(emitter.removeListener, emitter) :
      noop

    function eventListener () {
      removeListener(event, eventListener)
      removeListener('error', errorListener)

      if (arguments.length < 2) {
        resolve(arguments[0])
      } else {
        resolve(toArray(arguments))
      }
    }
    function errorListener (error) {
      removeListener(event, eventListener)
      removeListener('error', errorListener)

      reject(error)
    }

    emitter.on(event, eventListener)
    emitter.on('error', errorListener)
  }).bind(emitter)
}
exports = module.exports = eventToPromise
<commit_msg>Should support DOM events.<commit_after>'use strict'

// ===================================================================

var Bluebird = require('bluebird')

// ===================================================================

// Faster `Function.bind()`.
function bind (fn, ctx) {
  return function bindedFunction () {
    return fn.apply(ctx, arguments)
  }
}

function noop () {}

var toArray = Array.from || (function (slice) {
  return bind(slice.call, slice)
})(Array.prototype.slice)

// ===================================================================

function eventToPromise (emitter, event, opts) {
  return new Bluebird(function (resolve, reject) {
    var addListener =
      emitter.addEventListener ||
      emitter.addListener ||
      emitter.on
    if (!addListener) {
      throw new Error('cannot register an event listener')
    }
    addListener = bind(addListener, emitter)

    var removeListener =
      emitter.removeEventListener ||
      emitter.removeListener
    removeListener = removeListener ?
      bind(removeListener, emitter) :
      noop

    function eventListener () {
      removeListener(event, eventListener)
      removeListener('error', errorListener)

      if (arguments.length < 2) {
        resolve(arguments[0])
      } else {
        resolve(toArray(arguments))
      }
    }
    function errorListener (error) {
      removeListener(event, eventListener)
      removeListener('error', errorListener)

      reject(error)
    }

    emitter.on(event, eventListener)
    emitter.on('error', errorListener)
  }).bind(emitter)
}
exports = module.exports = eventToPromise
<|endoftext|>


<commit_before>var through = require('through');

/// start async stuff ////
// broken async.queue out at commit f01b3992fb
// from: https://github.com/caolan/async
var async = {};

function only_once(fn) {
    var called = false;
    return function() {
        if (called) throw new Error("Callback was already called.");
        called = true;
        fn.apply(this, arguments);
    };
}

async.setImmediate = setImmediate;

var _each = function (arr, iterator) {
    if (arr.forEach) {
        return arr.forEach(iterator);
    }
    for (var i = 0; i < arr.length; i += 1) {
        iterator(arr[i], i, arr);
    }
};

async.queue = function (worker, concurrency) {
    if (concurrency === undefined) {
        concurrency = 1;
    }
    function _insert(q, data, pos, callback) {
      if(data.constructor !== Array) {
          data = [data];
      }
      _each(data, function(task) {
          var item = {
              data: task,
              callback: typeof callback === 'function' ? callback : null
          };

          if (pos) {
            q.tasks.unshift(item);
          } else {
            q.tasks.push(item);
          }

          if (q.saturated && q.tasks.length === concurrency) {
              q.saturated();
          }
          async.setImmediate(q.process);
      });
    }

    var workers = 0;
    var q = {
        tasks: [],
        concurrency: concurrency,
        saturated: null,
        empty: null,
        drain: null,
        push: function (data, callback) {
          _insert(q, data, false, callback);
        },
        unshift: function (data, callback) {
          _insert(q, data, true, callback);
        },
        process: function () {
            if (workers < q.concurrency && q.tasks.length) {
                var task = q.tasks.shift();
                if (q.empty && q.tasks.length === 0) {
                    q.empty();
                }
                workers += 1;
                var next = function () {
                    workers -= 1;
                    if (task.callback) {
                        task.callback.apply(task, arguments);
                    }
                    if (q.drain && q.tasks.length + workers === 0) {
                        q.drain();
                    }
                    q.process();
                };
                var cb = only_once(next);
                worker(task.data, cb);
            }
        },
        length: function () {
            return q.tasks.length;
        },
        running: function () {
            return workers;
        }
    };
    return q;
};

/// end async stuff ////

//// meat ////

/**
 * stream wrapper of through using async.queue under the hood
 *
 * queueAsync(write_fn[[, end_fn], opts])
 */
var queueAsync = function (write_fn, end_fn, opts) {

    var self = this;

    this.opts = opts || {};

    // support queueAsync(write_fn, opts)
    if(typeof end_fn == 'object' && end_fn.constructor == Object) {
      opts = end_fn;
      end_fn = function() {this.emit('end');};
    }

    this.opts.concurrency = opts.concurrency || 1;

    this.opts.error_event = opts.error_event || 'failure';
    this.opts.stop_on_error = !!opts.stop_on_error;

    write_fn = write_fn || function(data, cb) {cb(null,data);};
    end_fn = end_fn || function() {this.emit('end');};

    // toggle to stop on error
    var error_raised = false;

    // set up queue and its hard worker
    var worker = function(_work, callback) {

      var _args, _f, _this;

      _f = _work.f;
      _this = _work._this;
      _args = _work._args || [];

      // wrap _write_callback to call async.queue callback
      var _w_cb = _work.write_cb;

      _args.push(_w_cb.bind({}, _work.write_cb_stream, callback));

      // _args.push(callback);
      if(error_raised !== true)
        return _f.apply(_this, _args);
    };

    var queue = async.queue(worker, this.opts.concurrency);

    // callback signature ref.
    //
    // callback(); - drop
    // callback(null, null); - emit null as data
    // callback(null, data); - emit data
    // callback(err, null); - emit err

    // _write_callback is called when write_fn wants to push err or data
    // reminder: cb is async.queue callback
    var _write_callback = function(stream, cb, err, data) {
        if(err) {
            return cb(err);
        }

        if(data !== void 0) {
            return stream.emit('data', data), cb();
        }
    };

    // _work_callback is called when
    var _work_callback = function(err) {
        if(err) {
            if(self.opts.stop_on_error && !error_raised) error_raised = true;

            return this.emit(self.opts.error_event, err);
        }
    };

    var _write = function(data) {

        var _work_callback_binded = _work_callback.bind(this);

        var work_package = {};
        work_package.f = write_fn;
        work_package._this = {};
        work_package._args = [data];

        work_package.write_cb = _write_callback;
        work_package.write_cb_stream = this;

        if(error_raised !== true)
          queue.push(work_package, _work_callback_binded);

    };

    var _empty = false;

    var stream = through(_write, function(){ _empty = true; });

    // Clean up
    queue.drain = function() {
        if(_empty) {
            end_fn.bind(stream)();
        }
    };

    return stream;
};

module.exports = queueAsync;
<commit_msg>fix breaking opts<commit_after>var through = require('through');

/// start async stuff ////
// broken async.queue out at commit f01b3992fb
// from: https://github.com/caolan/async
var async = {};

function only_once(fn) {
    var called = false;
    return function() {
        if (called) throw new Error("Callback was already called.");
        called = true;
        fn.apply(this, arguments);
    };
}

async.setImmediate = setImmediate;

var _each = function (arr, iterator) {
    if (arr.forEach) {
        return arr.forEach(iterator);
    }
    for (var i = 0; i < arr.length; i += 1) {
        iterator(arr[i], i, arr);
    }
};

async.queue = function (worker, concurrency) {
    if (concurrency === undefined) {
        concurrency = 1;
    }
    function _insert(q, data, pos, callback) {
      if(data.constructor !== Array) {
          data = [data];
      }
      _each(data, function(task) {
          var item = {
              data: task,
              callback: typeof callback === 'function' ? callback : null
          };

          if (pos) {
            q.tasks.unshift(item);
          } else {
            q.tasks.push(item);
          }

          if (q.saturated && q.tasks.length === concurrency) {
              q.saturated();
          }
          async.setImmediate(q.process);
      });
    }

    var workers = 0;
    var q = {
        tasks: [],
        concurrency: concurrency,
        saturated: null,
        empty: null,
        drain: null,
        push: function (data, callback) {
          _insert(q, data, false, callback);
        },
        unshift: function (data, callback) {
          _insert(q, data, true, callback);
        },
        process: function () {
            if (workers < q.concurrency && q.tasks.length) {
                var task = q.tasks.shift();
                if (q.empty && q.tasks.length === 0) {
                    q.empty();
                }
                workers += 1;
                var next = function () {
                    workers -= 1;
                    if (task.callback) {
                        task.callback.apply(task, arguments);
                    }
                    if (q.drain && q.tasks.length + workers === 0) {
                        q.drain();
                    }
                    q.process();
                };
                var cb = only_once(next);
                worker(task.data, cb);
            }
        },
        length: function () {
            return q.tasks.length;
        },
        running: function () {
            return workers;
        }
    };
    return q;
};

/// end async stuff ////

//// meat ////

/**
 * stream wrapper of through using async.queue under the hood
 *
 * queueAsync(write_fn[[, end_fn], opts])
 */
var queueAsync = function (write_fn, end_fn, opts) {

    var self = this;

    opts = opts || {};
    this.opts = {};

    // support queueAsync(write_fn, opts)
    if(typeof end_fn == 'object' && end_fn.constructor == Object) {
      opts = end_fn;
      end_fn = function() {this.emit('end');};
    }

    this.opts.concurrency = opts.concurrency || 1;

    this.opts.error_event = opts.error_event || 'failure';
    this.opts.stop_on_error = !!opts.stop_on_error;

    write_fn = write_fn || function(data, cb) {cb(null,data);};
    end_fn = end_fn || function() {this.emit('end');};

    // toggle to stop on error
    var error_raised = false;

    // set up queue and its hard worker
    var worker = function(_work, callback) {

      var _args, _f, _this;

      _f = _work.f;
      _this = _work._this;
      _args = _work._args || [];

      // wrap _write_callback to call async.queue callback
      var _w_cb = _work.write_cb;

      _args.push(_w_cb.bind({}, _work.write_cb_stream, callback));

      // _args.push(callback);
      if(error_raised !== true)
        return _f.apply(_this, _args);
    };

    var queue = async.queue(worker, this.opts.concurrency);

    // callback signature ref.
    //
    // callback(); - drop
    // callback(null, null); - emit null as data
    // callback(null, data); - emit data
    // callback(err, null); - emit err

    // _write_callback is called when write_fn wants to push err or data
    // reminder: cb is async.queue callback
    var _write_callback = function(stream, cb, err, data) {
        if(err) {
            return cb(err);
        }

        if(data !== void 0) {
            return stream.emit('data', data), cb();
        }
    };

    // _work_callback is called when
    var _work_callback = function(err) {
        if(err) {
            if(self.opts.stop_on_error && !error_raised) error_raised = true;

            return this.emit(self.opts.error_event, err);
        }
    };

    var _write = function(data) {

        var _work_callback_binded = _work_callback.bind(this);

        var work_package = {};
        work_package.f = write_fn;
        work_package._this = {};
        work_package._args = [data];

        work_package.write_cb = _write_callback;
        work_package.write_cb_stream = this;

        if(error_raised !== true)
          queue.push(work_package, _work_callback_binded);

    };

    var _empty = false;

    var stream = through(_write, function(){ _empty = true; });

    // Clean up
    queue.drain = function() {
        if(_empty) {
            end_fn.bind(stream)();
        }
    };

    return stream;
};

module.exports = queueAsync;
<|endoftext|>


<commit_before>//operations

//retain {op:'retain', n:5}
//pop {op:'pop'}
//push {op:'push', kind:[]} or {op:'push', kind:""}
//insert {op:'insert', values:[a b c]}
//insert {op:'insert', values:"text"}
//delete {op:'delete', values:[a b c]}
//delete {op:'delete', values:"text"}
// unpush, unpop

//with linearisation we also need UP and DOWN (with kind)

//with critical regions we also need START END

var doc     = "doc";
var p       = "p";
var bold    = "bold";

var RETAIN  = "retain";
var INSERT  = "insert";
var PUSH    = "push";
var POP     = "pop";
var DELETE  = "delete";
var UNPOP   = "unpop";
var UNPUSH  = "unpush";
var UP      = "up";
var DOWN    = "down";
var START   = "start";
var END     = "end";

var pop     = {op:POP};
var unpop   = {op:UNPOP};
var pushA   = {op:PUSH, kind:[]};
var pushS   = {op:PUSH, kind:""};
var unpushA = {op:UNPUSH, kind:[]};
var unpushS = {op:UNPUSH, kind:""};

//linearisation
var upA     = {op:UP, kind:[]};
var upS     = {op:UP, kind:""};
var down    = {op:DOWN};

//critical regions
var start   = {op:START};
var end     = {op:END};

//standard text/list operations
function r(n) {
  return {op:RETAIN, n:n};
}

function i(vs) {
  return {op:INSERT, values:vs, n:vs.length};
}

function d(vs) {
  return {op:DELETE, values:vs, n:vs.length};
}

var doca = [doc,[],[p,[],"Hello, World!"]];
var docb = [doc,[],[p,[],'Hello, ',[bold,[],'World!']]];

//IDEA: linearisation - might be easier to do things like compose and transform.
//we can serialise this into a simple form and generate it pretty easily.
var opa = [upA,r(2),upA,r(2),upS,r(7),i("Cruel "),r(6),down,down,down];
var opb = [upA,r(2),upA,r(2),upS,r(7),
  pop,pushA,i(["bold",[]]),pushS,r(6),down,pop,down,down];

//target = compose(opa,opbp) = compose(opb,opap)
var opab = [upA,r(2),upA,r(2),upS,r(7),
  pop,pushA,i(["bold",[]]),pushS,i("Cruel "),r(6),pop,down,down,down]

//target transformed
var opbp = [upA,r(2),upA,r(2),upS,r(7),
  pop,pushA,i(["bold",[]]),pushS,r(12),pop,down,down,down];
var opap = [upA,r(2),upA,r(2),upS,r(7),
  down,upA,r(2),upS,i("Cruel "),r(6),down,down,down,down];

//we might have the pops the wrong way up... (at the moment we don't check kind)

//RULES
// [upX,r(n),downX] ->[r(1)]
// [r(a),r(b)] -> [r(a+b)]
// [i(a),i(b)] -> [i(a+b)] //where + is concat if array.
// [start,end] -> []
// [end,start] -> []
// [r(0)]      -> []

function _push(ops, op) {
  //TODO (check rules first)
  return ops.push(op);
}

/*
NOTE:

Any list value you might be modifying non atomically should probably
be put in as pushA,popA except we need the system to know how to deal
with that kind of thing as we will want to insert large tables without
resorting to that kind of thing.

Although, we can simplify things by just throwing an error if we don't know
what to do. We should also not be transposing things if we are in a critical region.
perhaps we can have start critical and endcritical.

when we compose it is the unions of the critical regions.

*/

function invert(ops) {
  function invertOp(op) {
    switch (op.op) {
      case RETAIN: return op;
      case INSERT: return d(op.values);
      case DELETE: return i(op.values);
      case POP:    return unpop;
      case UNPOP:  return pop;
      case PUSH:
        return (typeof op.kind === 'string') ? unpushS : unpushA;
      case UNPUSH:
        return (typeof op.kind === 'string') ? pushS : pushA;
      default:
        return op;
    }
  }
  return ops.map(invertOp);
}

function makeAppend(result) {
  return function(op) {
    return _push(result, op);
  };
}

function _slice(op, s, e) {
  switch (op.op) {
    case INSERT: return i(op.values.slice(s,e));
    case DELETE: return d(op.values.slice(s,e));
    case RETAIN: return r((e === undefined ? op.n : e) - s);
    default:     return op;
  }
}

function makeTake(ops) {
  var i = 0; //current operation
  var offset = 0; //sub offset within operation

  function take(n,indivisableField) {
    if (i === ops.length)
      return (n === -1) ? null : r(n);

    var part;
    var c = ops[i];
    if (n === -1
      || c.n === undefined
      || c.n - offset <= n
      || c.op === indivisableField) {
      //return the remainder of the current operation
      part = _slice(c, offset)
      i++;
      offset=0;
      return part;
    } else {
      part = _slice(c, offset, offset + n);
      offset += n;
      return part;
    }
  }

  function peek() {
    return ops[i];
  }

  return {
    take: take,
    peek: peek,
  }
}

function compose(opA, opB) {
  var result = [];
  var critical = 0;
  var level_b = 0; //does not include push pop
  var level_a = 0; //includes push and pop

  var append = makeAppend(result);
  var _funs  = makeTake(opA);
  var take   = _funs.take;
  var peek   = _funs.peek;

  var chunk;
  var length = 0;

  for (var i = 0; i < opB.length; i++) {
    var op = opB[i];
    switch (op.op) {
      case PUSH:
      case POP:
      case UNPUSH:
      case UNPOP:
      case INSERT:
        append(op);
        break;
      case UP:
        level_b++;
        chunk = peek();
        if (chunk && (chunk.op === UP || chunk.op === PUSH)) {
          append(take(-1))
          level_a++;
        } else {
          append(op);
        }
        break;
      case DOWN:
        level_b--;
        chunk = peek();
        if (chunk && (chunk.op === DOWN || chunk.op === POP)) {
          append(take(-1))
          level_a--;
          break;
        } else {
          append(op);
          if (level_b === level_a)
            op = r(1); //fall through to retain !!!
          else
            break;
        }
      case RETAIN:
        length = op.n;
        while (length > 0) {
          chunk = take(length, DELETE);
          switch (chunk.op) {
            case INSERT:
            case RETAIN:
              append(chunk);
              length -= chunk.n;
              break;
            case DOWN:
            case POP:
              level_a--;
              if (level_a===level_b) length -= 1;
              append(chunk);
              break;
            case UP:
            case PUSH:
              level_b++;
              append(chunk);
              break;
            case START:
              if (critical === 0) append(chunk);
              critical++;
              break;
            case STOP:
              critical--;
              if (critical === 0) append(chunk);
              break;
            default:
              append(chunk);
              break;
          }
        }
        break;
      case DELETE:
        length = op.n;
        //TODO
        // peek loop at end to gobble all the pushes and pops.
        break;
      case START:
        if (critical === 0) append(op);
        critical++;
        break;
      case STOP:
        critical--;
        if (critical === 0) append(op);
        break;
    }
  };

  while ((chunk = take(-1)))
    append(chunk); //TODO: probably need special cases for each..

  return result;
}

function transform(opA, opB, side) {
  var left = side == 'left';
  var result = [];
  var critical = 0;

  var append = makeAppend(result);
  var _funs  = makeTake(opA);
  var take   = _funs.take;
  var peek   = _funs.peek;

  var chunk;

  for (var i = 0; i < opB.length; i++) {
    var op = opB[i];

    //TODO

  };

  while ((chunk = take(-1)))
    append(chunk);

  return result;
}

function apply(d,ops) {
  //stack and t(op) for traversing output documrny
  var stack = []
  var t = [];
  //stack and top(dp) for traversing input document
  var docStack = [];
  var dp = [d]; //wrap the doc so we can do [r(1)] and change nothing.
  var o = 0;

  function append(vals) {
    if (typeof t === 'string') {
      t += vals
    } else {
      t = t.concat(vals);
    }
  }

  for (var i = 0; i < ops.length; i++) {
    var op = ops[i];
    switch (op.op) {
      case INSERT:
        append(op.values);
        break;
      case RETAIN:
        append(dp.slice(o,o+op.n));
        o += op.n;
        break;
      case PUSH:
        stack.push(t);
        t = (typeof op.kind === 'string') ? "" : [];
        break;
      case POP:
        var temp = t;
        t = stack.pop();
        t.push(temp);
        break;
      case DELETE:
        o += op.n;
        break;
      case UNPUSH:
        //move deeper into the doc
        docStack.push({o:o, dp:dp});
        dp = dp[o];
        o = 0;
        break;
      case UNPOP:
        //move shallower in the doc
        var _d = docStack.pop();
        dp = _d.dp;
        o = _d.o + 1;
        break;
      case UP:
        //move deeper into the output
        stack.push(t);
        t = (typeof op.kind === 'string') ? "" : [];
        //move deeper into the doc
        docStack.push({o:o, dp:dp});
        dp = dp[o];
        o = 0;
        break;
      case DOWN:
        //move shallower in the doc
        var _d = docStack.pop();
        dp = _d.dp;
        o = _d.o + 1;
        //move shallower in the output
        var temp = t;
        t = stack.pop();
        t.push(temp);
        break;
      default:
        //do nothing for Start and End.
        break;
    }
  };

  return t[0];
}
<commit_msg>added failing compose test<commit_after>//operations

//retain {op:'retain', n:5}
//pop {op:'pop'}
//push {op:'push', kind:[]} or {op:'push', kind:""}
//insert {op:'insert', values:[a b c]}
//insert {op:'insert', values:"text"}
//delete {op:'delete', values:[a b c]}
//delete {op:'delete', values:"text"}
// unpush, unpop

//with linearisation we also need UP and DOWN (with kind)

//with critical regions we also need START END

var RETAIN  = "retain";
var INSERT  = "insert";
var PUSH    = "push";
var POP     = "pop";
var DELETE  = "delete";
var UNPOP   = "unpop";
var UNPUSH  = "unpush";
var UP      = "up";
var DOWN    = "down";
var START   = "start";
var END     = "end";

var pop     = {op:POP};
var unpop   = {op:UNPOP};
var pushA   = {op:PUSH, kind:[]};
var pushS   = {op:PUSH, kind:""};
var unpushA = {op:UNPUSH, kind:[]};
var unpushS = {op:UNPUSH, kind:""};

//linearisation
var upA     = {op:UP, kind:[]};
var upS     = {op:UP, kind:""};
var down    = {op:DOWN};

//critical regions
var start   = {op:START};
var end     = {op:END};

//standard text/list operations
function r(n) {
  return {op:RETAIN, n:n};
}

function i(vs) {
  return {op:INSERT, values:vs, n:vs.length};
}

function d(vs) {
  return {op:DELETE, values:vs, n:vs.length};
}

pop.toJSON = function() { return "pop"; };
unpop.toJSON = function() { return "unpop"; };
upA.toJSON = function() { return "upA"; };
upS.toJSON = function() { return "upS"; };
down.toJSON = function() { return "down"; };
pushA.toJSON = function() { return "pushA"; };
pushS.toJSON = function() { return "pushS"; };
unpushA.toJSON = function() { return "unpushA"; };
unpushS.toJSON = function() { return "unpushS"; };

//we might have the pops the wrong way up... (at the moment we don't check kind)

//RULES
// [upX,r(n),downX] ->[r(1)]
// [r(a),r(b)] -> [r(a+b)]
// [i(a),i(b)] -> [i(a+b)] //where + is concat if array.
// [start,end] -> []
// [end,start] -> []
// [r(0)]      -> []

function _push(ops, op) {
  //TODO (check rules first)
  return ops.push(op);
}

/*
NOTE:

Any list value you might be modifying non atomically should probably
be put in as pushA,popA except we need the system to know how to deal
with that kind of thing as we will want to insert large tables without
resorting to that kind of thing.

Although, we can simplify things by just throwing an error if we don't know
what to do. We should also not be transposing things if we are in a critical region.
perhaps we can have start critical and endcritical.

when we compose it is the unions of the critical regions.

*/

function invert(ops) {
  function invertOp(op) {
    switch (op.op) {
      case RETAIN: return op;
      case INSERT: return d(op.values);
      case DELETE: return i(op.values);
      case POP:    return unpop;
      case UNPOP:  return pop;
      case PUSH:
        return (typeof op.kind === 'string') ? unpushS : unpushA;
      case UNPUSH:
        return (typeof op.kind === 'string') ? pushS : pushA;
      default:
        return op;
    }
  }
  return ops.map(invertOp);
}

function makeAppend(result) {
  return function(op) {
    return _push(result, op);
  };
}

function _slice(op, s, e) {
  switch (op.op) {
    case INSERT: return i(op.values.slice(s,e));
    case DELETE: return d(op.values.slice(s,e));
    case RETAIN: return r((e === undefined ? op.n : e) - s);
    default:     return op;
  }
}

function makeTake(ops) {
  var i = 0; //current operation
  var offset = 0; //sub offset within operation

  function take(n,indivisableField) {
    if (i === ops.length)
      return (n === -1) ? null : r(n);

    var part;
    var c = ops[i];
    if (n === -1
      || c.n === undefined
      || c.n - offset <= n
      || c.op === indivisableField) {
      //return the remainder of the current operation
      part = _slice(c, offset)
      i++;
      offset=0;
      return part;
    } else {
      part = _slice(c, offset, offset + n);
      offset += n;
      return part;
    }
  }

  function peek() {
    return ops[i];
  }

  return {
    take: take,
    peek: peek,
  }
}

function compose(opA, opB) {
  var result = [];
  var critical = 0;
  var level_b = 0; //does not include push pop
  var level_a = 0; //includes push and pop

  var append = makeAppend(result);
  var _funs  = makeTake(opA);
  var take   = _funs.take;
  var peek   = _funs.peek;

  var chunk;
  var length = 0;

  for (var i = 0; i < opB.length; i++) {
    var op = opB[i];
    switch (op.op) {
      case PUSH:
      case POP:
      case UNPUSH:
      case UNPOP:
      case INSERT:
        append(op);
        break;
      case UP:
        level_b++;
        chunk = peek();
        if (chunk && (chunk.op === UP || chunk.op === PUSH)) {
          append(take(-1))
          level_a++;
        } else {
          append(op);
        }
        break;
      case DOWN:
        level_b--;
        chunk = peek();
        if (chunk && (chunk.op === DOWN || chunk.op === POP)) {
          append(take(-1))
          level_a--;
          break;
        } else {
          append(op);
          if (level_b === level_a)
            op = r(1); //fall through to retain !!!
          else
            break;
        }
      case RETAIN:
        length = op.n;
        while (length > 0) {
          chunk = take(length, DELETE);
          switch (chunk.op) {
            case INSERT:
            case RETAIN:
              append(chunk);
              length -= chunk.n;
              break;
            case DOWN:
            case POP:
              level_a--;
              if (level_a===level_b) length -= 1;
              append(chunk);
              break;
            case UP:
            case PUSH:
              level_b++;
              append(chunk);
              break;
            case START:
              if (critical === 0) append(chunk);
              critical++;
              break;
            case STOP:
              critical--;
              if (critical === 0) append(chunk);
              break;
            default:
              append(chunk);
              break;
          }
        }
        break;
      case DELETE:
        length = op.n;
        //TODO
        // peek loop at end to gobble all the pushes and pops.
        break;
      case START:
        if (critical === 0) append(op);
        critical++;
        break;
      case STOP:
        critical--;
        if (critical === 0) append(op);
        break;
    }
  };

  while ((chunk = take(-1)))
    append(chunk); //TODO: probably need special cases for each..

  return result;
}

function transform(opA, opB, side) {
  var left = side == 'left';
  var result = [];
  var critical = 0;

  var append = makeAppend(result);
  var _funs  = makeTake(opA);
  var take   = _funs.take;
  var peek   = _funs.peek;

  var chunk;

  for (var i = 0; i < opB.length; i++) {
    var op = opB[i];

    //TODO

  };

  while ((chunk = take(-1)))
    append(chunk);

  return result;
}

function apply(d,ops) {
  //stack and t(op) for traversing output documrny
  var stack = []
  var t = [];
  //stack and top(dp) for traversing input document
  var docStack = [];
  var dp = [d]; //wrap the doc so we can do [r(1)] and change nothing.
  var o = 0;

  function append(vals) {
    if (typeof t === 'string') {
      t += vals
    } else {
      t = t.concat(vals);
    }
  }

  for (var i = 0; i < ops.length; i++) {
    var op = ops[i];
    switch (op.op) {
      case INSERT:
        append(op.values);
        break;
      case RETAIN:
        append(dp.slice(o,o+op.n));
        o += op.n;
        break;
      case PUSH:
        stack.push(t);
        t = (typeof op.kind === 'string') ? "" : [];
        break;
      case POP:
        var temp = t;
        t = stack.pop();
        t.push(temp);
        break;
      case DELETE:
        o += op.n;
        break;
      case UNPUSH:
        //move deeper into the doc
        docStack.push({o:o, dp:dp});
        dp = dp[o];
        o = 0;
        break;
      case UNPOP:
        //move shallower in the doc
        var _d = docStack.pop();
        dp = _d.dp;
        o = _d.o + 1;
        break;
      case UP:
        //move deeper into the output
        stack.push(t);
        t = (typeof op.kind === 'string') ? "" : [];
        //move deeper into the doc
        docStack.push({o:o, dp:dp});
        dp = dp[o];
        o = 0;
        break;
      case DOWN:
        //move shallower in the doc
        var _d = docStack.pop();
        dp = _d.dp;
        o = _d.o + 1;
        //move shallower in the output
        var temp = t;
        t = stack.pop();
        t.push(temp);
        break;
      default:
        //do nothing for Start and End.
        break;
    }
  };

  return t[0];
}

module.exports = {
  name: 'sexpr',
  uri: 'https://github.com/mavxg/ot-sexpr',
  apply: apply,
  transform: transform,
  compose: compose,
  invert: invert,
  optypes: {
    retain  : r,
    insert  : i,
    "delete": d,
    pop     : pop,
    unpop   : unpop,
    pushA   : pushA,
    pushS   : pushS,
    unpushA : unpushA,
    unpushS : unpushS,
    upA     : upA,
    upS     : upS,
    down    : down,
    start   : start,
    end     : end,
  },
};<|endoftext|>