<commit_before>// To keep the UI snappy, we run WebTorrent in its own hidden window, a separate
// process from the main window.
console.time('init')

var WebTorrent = require('webtorrent')
var defaultAnnounceList = require('create-torrent').announceList
var deepEqual = require('deep-equal')
var electron = require('electron')
var fs = require('fs-extra')
var musicmetadata = require('musicmetadata')
var networkAddress = require('network-address')
var path = require('path')

var crashReporter = require('../crash-reporter')
var config = require('../config')
var torrentPoster = require('./lib/torrent-poster')

// Report when the process crashes
crashReporter.init()

// Send & receive messages from the main window
var ipc = electron.ipcRenderer

// Force use of webtorrent trackers on all torrents
global.WEBTORRENT_ANNOUNCE = defaultAnnounceList
  .map((arr) => arr[0])
  .filter((url) => url.indexOf('wss://') === 0 || url.indexOf('ws://') === 0)

// Connect to the WebTorrent and BitTorrent networks. WebTorrent Desktop is a hybrid
// client, as explained here: https://webtorrent.io/faq
var client = new WebTorrent()

// WebTorrent-to-HTTP streaming sever
var server = window.server = null

// Used for diffing, so we only send progress updates when necessary
var prevProgress = window.prevProgress = null

init()

function init () {
  client.on('warning', (err) => ipc.send('wt-warning', null, err.message))
  client.on('error', (err) => ipc.send('wt-error', null, err.message))

  ipc.on('wt-start-torrenting', (e, torrentKey, torrentID, path, fileModtimes, selections) =>
    startTorrenting(torrentKey, torrentID, path, fileModtimes, selections))
  ipc.on('wt-stop-torrenting', (e, infoHash) =>
    stopTorrenting(infoHash))
  ipc.on('wt-create-torrent', (e, torrentKey, options) =>
    createTorrent(torrentKey, options))
  ipc.on('wt-save-torrent-file', (e, torrentKey) =>
    saveTorrentFile(torrentKey))
  ipc.on('wt-generate-torrent-poster', (e, torrentKey) =>
    generateTorrentPoster(torrentKey))
  ipc.on('wt-get-audio-metadata', (e, infoHash, index) =>
    getAudioMetadata(infoHash, index))
  ipc.on('wt-start-server', (e, infoHash, index) =>
    startServer(infoHash, index))
  ipc.on('wt-stop-server', (e) =>
    stopServer())
  ipc.on('wt-select-files', (e, infoHash, selections) =>
    selectFiles(infoHash, selections))

  ipc.send('ipcReadyWebTorrent')

  setInterval(updateTorrentProgress, 1000)
}

// Starts a given TorrentID, which can be an infohash, magnet URI, etc. Returns WebTorrent object
// See https://github.com/feross/webtorrent/blob/master/docs/api.md#clientaddtorrentid-opts-function-ontorrent-torrent-
function startTorrenting (torrentKey, torrentID, path, fileModtimes, selections) {
  console.log('starting torrent %s: %s', torrentKey, torrentID)

  var torrent = client.add(torrentID, {
    path: path,
    fileModtimes: fileModtimes
  })
  torrent.key = torrentKey

  // Listen for ready event, progress notifications, etc
  addTorrentEvents(torrent)

  // Only download the files the user wants, not necessarily all files
  torrent.once('ready', () => selectFiles(torrent, selections))

  return torrent
}

function stopTorrenting (infoHash) {
  var torrent = client.get(infoHash)
  if (torrent) torrent.destroy()
}

// Create a new torrent, start seeding
function createTorrent (torrentKey, options) {
  console.log('creating torrent', torrentKey, options)
  var paths = options.files.map((f) => f.path)
  var torrent = client.seed(paths, options)
  torrent.key = torrentKey
  addTorrentEvents(torrent)
  ipc.send('wt-new-torrent')
}

function addTorrentEvents (torrent) {
  torrent.on('warning', (err) =>
    ipc.send('wt-warning', torrent.key, err.message))
  torrent.on('error', (err) =>
    ipc.send('wt-error', torrent.key, err.message))
  torrent.on('infoHash', () =>
    ipc.send('wt-infohash', torrent.key, torrent.infoHash))
  torrent.on('metadata', torrentMetadata)
  torrent.on('ready', torrentReady)
  torrent.on('done', torrentDone)

  function torrentMetadata () {
    var info = getTorrentInfo(torrent)
    ipc.send('wt-metadata', torrent.key, info)

    updateTorrentProgress()
  }

  function torrentReady () {
    var info = getTorrentInfo(torrent)
    ipc.send('wt-ready', torrent.key, info)
    ipc.send('wt-ready-' + torrent.infoHash, torrent.key, info) // TODO: hack

    updateTorrentProgress()
  }

  function torrentDone () {
    var info = getTorrentInfo(torrent)
    ipc.send('wt-done', torrent.key, info)

    updateTorrentProgress()

    torrent.getFileModtimes(function (err, fileModtimes) {
      if (err) return onError(err)
      ipc.send('wt-file-modtimes', torrent.key, fileModtimes)
    })
  }
}

// Produces a JSON saveable summary of a torrent
function getTorrentInfo (torrent) {
  return {
    infoHash: torrent.infoHash,
    magnetURI: torrent.magnetURI,
    name: torrent.name,
    path: torrent.path,
    files: torrent.files.map(getTorrentFileInfo),
    bytesReceived: torrent.received
  }
}

// Produces a JSON saveable summary of a file in a torrent
function getTorrentFileInfo (file) {
  return {
    name: file.name,
    length: file.length,
    path: file.path
  }
}

// Every time we resolve a magnet URI, save the torrent file so that we never
// have to download it again. Never ask the DHT the same question twice.
function saveTorrentFile (torrentKey) {
  var torrent = getTorrent(torrentKey)
  checkIfTorrentFileExists(torrent.infoHash, function (torrentPath, exists) {
    var fileName = torrent.infoHash + '.torrent'
    if (exists) {
      // We've already saved the file
      return ipc.send('wt-file-saved', torrentKey, fileName)
    }

    // Otherwise, save the .torrent file, under the app config folder
    fs.mkdir(config.CONFIG_TORRENT_PATH, function (_) {
      fs.writeFile(torrentPath, torrent.torrentFile, function (err) {
        if (err) return console.log('error saving torrent file %s: %o', torrentPath, err)
        console.log('saved torrent file %s', torrentPath)
        return ipc.send('wt-file-saved', torrentKey, fileName)
      })
    })
  })
}

// Checks whether we've already resolved a given infohash to a torrent file
// Calls back with (torrentPath, exists). Logs, does not call back on error
function checkIfTorrentFileExists (infoHash, cb) {
  var torrentPath = path.join(config.CONFIG_TORRENT_PATH, infoHash + '.torrent')
  fs.exists(torrentPath, function (exists) {
    cb(torrentPath, exists)
  })
}

// Save a JPG that represents a torrent.
// Auto chooses either a frame from a video file, an image, etc
function generateTorrentPoster (torrentKey) {
  var torrent = getTorrent(torrentKey)
  torrentPoster(torrent, function (err, buf, extension) {
    if (err) return console.log('error generating poster: %o', err)
    // save it for next time
    fs.mkdirp(config.CONFIG_POSTER_PATH, function (err) {
      if (err) return console.log('error creating poster dir: %o', err)
      var posterFileName = torrent.infoHash + extension
      var posterFilePath = path.join(config.CONFIG_POSTER_PATH, posterFileName)
      fs.writeFile(posterFilePath, buf, function (err) {
        if (err) return console.log('error saving poster: %o', err)
        // show the poster
        ipc.send('wt-poster', torrentKey, posterFileName)
      })
    })
  })
}

function updateTorrentProgress () {
  var progress = getTorrentProgress()
  // TODO: diff torrent-by-torrent, not once for the whole update
  if (prevProgress && deepEqual(progress, prevProgress, {strict: true})) {
    return /* don't send heavy object if it hasn't changed */
  }
  ipc.send('wt-progress', progress)
  prevProgress = progress
}

function getTorrentProgress () {
  // First, track overall progress
  var progress = client.progress
  var hasActiveTorrents = client.torrents.some(function (torrent) {
    return torrent.progress !== 1
  })

  // Track progress for every file in each torrent
  // TODO: ideally this would be tracked by WebTorrent, which could do it
  // more efficiently than looping over torrent.bitfield
  var torrentProg = client.torrents.map(function (torrent) {
    var fileProg = torrent.files && torrent.files.map(function (file, index) {
      var numPieces = file._endPiece - file._startPiece + 1
      var numPiecesPresent = 0
      for (var piece = file._startPiece; piece <= file._endPiece; piece++) {
        if (torrent.bitfield.get(piece)) numPiecesPresent++
      }
      return {
        startPiece: file._startPiece,
        endPiece: file._endPiece,
        numPieces,
        numPiecesPresent
      }
    })
    return {
      torrentKey: torrent.key,
      ready: torrent.ready,
      progress: torrent.progress,
      downloaded: torrent.downloaded,
      downloadSpeed: torrent.downloadSpeed,
      uploadSpeed: torrent.uploadSpeed,
      numPeers: torrent.numPeers,
      length: torrent.length,
      bitfield: torrent.bitfield,
      files: fileProg
    }
  })

  return {
    torrents: torrentProg,
    progress,
    hasActiveTorrents
  }
}

function startServer (infoHash, index) {
  var torrent = client.get(infoHash)
  if (torrent.ready) startServerFromReadyTorrent(torrent, index)
  else torrent.on('ready', () => startServerFromReadyTorrent(torrent, index))
}

function startServerFromReadyTorrent (torrent, index, cb) {
  if (server) return

  // start the streaming torrent-to-http server
  server = torrent.createServer()
  server.listen(0, function () {
    var port = server.address().port
    var urlSuffix = ':' + port + '/' + index
    var info = {
      torrentKey: torrent.key,
      localURL: 'http://localhost' + urlSuffix,
      networkURL: 'http://' + networkAddress() + urlSuffix
    }

    ipc.send('wt-server-running', info)
    ipc.send('wt-server-' + torrent.infoHash, info) // TODO: hack
  })
}

function stopServer () {
  if (!server) return
  server.destroy()
  server = null
}

function getAudioMetadata (infoHash, index) {
  var torrent = client.get(infoHash)
  var file = torrent.files[index]
  musicmetadata(file.createReadStream(), function (err, info) {
    if (err) return
    console.log('got audio metadata for %s: %o', file.name, info)
    ipc.send('wt-audio-metadata', infoHash, index, info)
  })
}

function selectFiles (torrentOrInfoHash, selections) {
  // Get the torrent object
  var torrent
  if (typeof torrentOrInfoHash === 'string') {
    torrent = client.get(torrentOrInfoHash)
  } else {
    torrent = torrentOrInfoHash
  }

  // Selections not specified?
  // Load all files. We still need to replace the default whole-torrent
  // selection with individual selections for each file, so we can
  // select/deselect files later on
  if (!selections) {
    selections = torrent.files.map((x) => true)
  }

  // Selections specified incorrectly?
  if (selections.length !== torrent.files.length) {
    throw new Error('got ' + selections.length + ' file selections, ' +
      'but the torrent contains ' + torrent.files.length + ' files')
  }

  // Remove default selection (whole torrent)
  torrent.deselect(0, torrent.pieces.length - 1, false)

  // Add selections (individual files)
  for (var i = 0; i < selections.length; i++) {
    var file = torrent.files[i]
    if (selections[i]) {
      file.select()
    } else {
      console.log('deselecting file ' + i + ' of torrent ' + torrent.name)
      file.deselect()
    }
  }
}

// Gets a WebTorrent handle by torrentKey
// Throws an Error if we're not currently torrenting anything w/ that key
function getTorrent (torrentKey) {
  var ret = client.torrents.find((x) => x.key === torrentKey)
  if (!ret) throw new Error('missing torrent key ' + torrentKey)
  return ret
}

function onError (err) {
  console.log(err)
}
<commit_msg>style<commit_after>// To keep the UI snappy, we run WebTorrent in its own hidden window, a separate
// process from the main window.
console.time('init')

var WebTorrent = require('webtorrent')
var defaultAnnounceList = require('create-torrent').announceList
var deepEqual = require('deep-equal')
var electron = require('electron')
var fs = require('fs-extra')
var musicmetadata = require('musicmetadata')
var networkAddress = require('network-address')
var path = require('path')

var crashReporter = require('../crash-reporter')
var config = require('../config')
var torrentPoster = require('./lib/torrent-poster')

// Report when the process crashes
crashReporter.init()

// Send & receive messages from the main window
var ipc = electron.ipcRenderer

// Force use of webtorrent trackers on all torrents
global.WEBTORRENT_ANNOUNCE = defaultAnnounceList
  .map((arr) => arr[0])
  .filter((url) => url.indexOf('wss://') === 0 || url.indexOf('ws://') === 0)

// Connect to the WebTorrent and BitTorrent networks. WebTorrent Desktop is a hybrid
// client, as explained here: https://webtorrent.io/faq
var client = new WebTorrent()

// WebTorrent-to-HTTP streaming sever
var server = null

// Used for diffing, so we only send progress updates when necessary
var prevProgress = null

init()

function init () {
  client.on('warning', (err) => ipc.send('wt-warning', null, err.message))
  client.on('error', (err) => ipc.send('wt-error', null, err.message))

  ipc.on('wt-start-torrenting', (e, torrentKey, torrentID, path, fileModtimes, selections) =>
    startTorrenting(torrentKey, torrentID, path, fileModtimes, selections))
  ipc.on('wt-stop-torrenting', (e, infoHash) =>
    stopTorrenting(infoHash))
  ipc.on('wt-create-torrent', (e, torrentKey, options) =>
    createTorrent(torrentKey, options))
  ipc.on('wt-save-torrent-file', (e, torrentKey) =>
    saveTorrentFile(torrentKey))
  ipc.on('wt-generate-torrent-poster', (e, torrentKey) =>
    generateTorrentPoster(torrentKey))
  ipc.on('wt-get-audio-metadata', (e, infoHash, index) =>
    getAudioMetadata(infoHash, index))
  ipc.on('wt-start-server', (e, infoHash, index) =>
    startServer(infoHash, index))
  ipc.on('wt-stop-server', (e) =>
    stopServer())
  ipc.on('wt-select-files', (e, infoHash, selections) =>
    selectFiles(infoHash, selections))

  ipc.send('ipcReadyWebTorrent')

  setInterval(updateTorrentProgress, 1000)
}

// Starts a given TorrentID, which can be an infohash, magnet URI, etc. Returns WebTorrent object
// See https://github.com/feross/webtorrent/blob/master/docs/api.md#clientaddtorrentid-opts-function-ontorrent-torrent-
function startTorrenting (torrentKey, torrentID, path, fileModtimes, selections) {
  console.log('starting torrent %s: %s', torrentKey, torrentID)

  var torrent = client.add(torrentID, {
    path: path,
    fileModtimes: fileModtimes
  })
  torrent.key = torrentKey

  // Listen for ready event, progress notifications, etc
  addTorrentEvents(torrent)

  // Only download the files the user wants, not necessarily all files
  torrent.once('ready', () => selectFiles(torrent, selections))

  return torrent
}

function stopTorrenting (infoHash) {
  var torrent = client.get(infoHash)
  if (torrent) torrent.destroy()
}

// Create a new torrent, start seeding
function createTorrent (torrentKey, options) {
  console.log('creating torrent', torrentKey, options)
  var paths = options.files.map((f) => f.path)
  var torrent = client.seed(paths, options)
  torrent.key = torrentKey
  addTorrentEvents(torrent)
  ipc.send('wt-new-torrent')
}

function addTorrentEvents (torrent) {
  torrent.on('warning', (err) =>
    ipc.send('wt-warning', torrent.key, err.message))
  torrent.on('error', (err) =>
    ipc.send('wt-error', torrent.key, err.message))
  torrent.on('infoHash', () =>
    ipc.send('wt-infohash', torrent.key, torrent.infoHash))
  torrent.on('metadata', torrentMetadata)
  torrent.on('ready', torrentReady)
  torrent.on('done', torrentDone)

  function torrentMetadata () {
    var info = getTorrentInfo(torrent)
    ipc.send('wt-metadata', torrent.key, info)

    updateTorrentProgress()
  }

  function torrentReady () {
    var info = getTorrentInfo(torrent)
    ipc.send('wt-ready', torrent.key, info)
    ipc.send('wt-ready-' + torrent.infoHash, torrent.key, info) // TODO: hack

    updateTorrentProgress()
  }

  function torrentDone () {
    var info = getTorrentInfo(torrent)
    ipc.send('wt-done', torrent.key, info)

    updateTorrentProgress()

    torrent.getFileModtimes(function (err, fileModtimes) {
      if (err) return onError(err)
      ipc.send('wt-file-modtimes', torrent.key, fileModtimes)
    })
  }
}

// Produces a JSON saveable summary of a torrent
function getTorrentInfo (torrent) {
  return {
    infoHash: torrent.infoHash,
    magnetURI: torrent.magnetURI,
    name: torrent.name,
    path: torrent.path,
    files: torrent.files.map(getTorrentFileInfo),
    bytesReceived: torrent.received
  }
}

// Produces a JSON saveable summary of a file in a torrent
function getTorrentFileInfo (file) {
  return {
    name: file.name,
    length: file.length,
    path: file.path
  }
}

// Every time we resolve a magnet URI, save the torrent file so that we never
// have to download it again. Never ask the DHT the same question twice.
function saveTorrentFile (torrentKey) {
  var torrent = getTorrent(torrentKey)
  checkIfTorrentFileExists(torrent.infoHash, function (torrentPath, exists) {
    var fileName = torrent.infoHash + '.torrent'
    if (exists) {
      // We've already saved the file
      return ipc.send('wt-file-saved', torrentKey, fileName)
    }

    // Otherwise, save the .torrent file, under the app config folder
    fs.mkdir(config.CONFIG_TORRENT_PATH, function (_) {
      fs.writeFile(torrentPath, torrent.torrentFile, function (err) {
        if (err) return console.log('error saving torrent file %s: %o', torrentPath, err)
        console.log('saved torrent file %s', torrentPath)
        return ipc.send('wt-file-saved', torrentKey, fileName)
      })
    })
  })
}

// Checks whether we've already resolved a given infohash to a torrent file
// Calls back with (torrentPath, exists). Logs, does not call back on error
function checkIfTorrentFileExists (infoHash, cb) {
  var torrentPath = path.join(config.CONFIG_TORRENT_PATH, infoHash + '.torrent')
  fs.exists(torrentPath, function (exists) {
    cb(torrentPath, exists)
  })
}

// Save a JPG that represents a torrent.
// Auto chooses either a frame from a video file, an image, etc
function generateTorrentPoster (torrentKey) {
  var torrent = getTorrent(torrentKey)
  torrentPoster(torrent, function (err, buf, extension) {
    if (err) return console.log('error generating poster: %o', err)
    // save it for next time
    fs.mkdirp(config.CONFIG_POSTER_PATH, function (err) {
      if (err) return console.log('error creating poster dir: %o', err)
      var posterFileName = torrent.infoHash + extension
      var posterFilePath = path.join(config.CONFIG_POSTER_PATH, posterFileName)
      fs.writeFile(posterFilePath, buf, function (err) {
        if (err) return console.log('error saving poster: %o', err)
        // show the poster
        ipc.send('wt-poster', torrentKey, posterFileName)
      })
    })
  })
}

function updateTorrentProgress () {
  var progress = getTorrentProgress()
  // TODO: diff torrent-by-torrent, not once for the whole update
  if (prevProgress && deepEqual(progress, prevProgress, {strict: true})) {
    return /* don't send heavy object if it hasn't changed */
  }
  ipc.send('wt-progress', progress)
  prevProgress = progress
}

function getTorrentProgress () {
  // First, track overall progress
  var progress = client.progress
  var hasActiveTorrents = client.torrents.some(function (torrent) {
    return torrent.progress !== 1
  })

  // Track progress for every file in each torrent
  // TODO: ideally this would be tracked by WebTorrent, which could do it
  // more efficiently than looping over torrent.bitfield
  var torrentProg = client.torrents.map(function (torrent) {
    var fileProg = torrent.files && torrent.files.map(function (file, index) {
      var numPieces = file._endPiece - file._startPiece + 1
      var numPiecesPresent = 0
      for (var piece = file._startPiece; piece <= file._endPiece; piece++) {
        if (torrent.bitfield.get(piece)) numPiecesPresent++
      }
      return {
        startPiece: file._startPiece,
        endPiece: file._endPiece,
        numPieces,
        numPiecesPresent
      }
    })
    return {
      torrentKey: torrent.key,
      ready: torrent.ready,
      progress: torrent.progress,
      downloaded: torrent.downloaded,
      downloadSpeed: torrent.downloadSpeed,
      uploadSpeed: torrent.uploadSpeed,
      numPeers: torrent.numPeers,
      length: torrent.length,
      bitfield: torrent.bitfield,
      files: fileProg
    }
  })

  return {
    torrents: torrentProg,
    progress,
    hasActiveTorrents
  }
}

function startServer (infoHash, index) {
  var torrent = client.get(infoHash)
  if (torrent.ready) startServerFromReadyTorrent(torrent, index)
  else torrent.on('ready', () => startServerFromReadyTorrent(torrent, index))
}

function startServerFromReadyTorrent (torrent, index, cb) {
  if (server) return

  // start the streaming torrent-to-http server
  server = torrent.createServer()
  server.listen(0, function () {
    var port = server.address().port
    var urlSuffix = ':' + port + '/' + index
    var info = {
      torrentKey: torrent.key,
      localURL: 'http://localhost' + urlSuffix,
      networkURL: 'http://' + networkAddress() + urlSuffix
    }

    ipc.send('wt-server-running', info)
    ipc.send('wt-server-' + torrent.infoHash, info) // TODO: hack
  })
}

function stopServer () {
  if (!server) return
  server.destroy()
  server = null
}

function getAudioMetadata (infoHash, index) {
  var torrent = client.get(infoHash)
  var file = torrent.files[index]
  musicmetadata(file.createReadStream(), function (err, info) {
    if (err) return
    console.log('got audio metadata for %s: %o', file.name, info)
    ipc.send('wt-audio-metadata', infoHash, index, info)
  })
}

function selectFiles (torrentOrInfoHash, selections) {
  // Get the torrent object
  var torrent
  if (typeof torrentOrInfoHash === 'string') {
    torrent = client.get(torrentOrInfoHash)
  } else {
    torrent = torrentOrInfoHash
  }

  // Selections not specified?
  // Load all files. We still need to replace the default whole-torrent
  // selection with individual selections for each file, so we can
  // select/deselect files later on
  if (!selections) {
    selections = torrent.files.map((x) => true)
  }

  // Selections specified incorrectly?
  if (selections.length !== torrent.files.length) {
    throw new Error('got ' + selections.length + ' file selections, ' +
      'but the torrent contains ' + torrent.files.length + ' files')
  }

  // Remove default selection (whole torrent)
  torrent.deselect(0, torrent.pieces.length - 1, false)

  // Add selections (individual files)
  for (var i = 0; i < selections.length; i++) {
    var file = torrent.files[i]
    if (selections[i]) {
      file.select()
    } else {
      console.log('deselecting file ' + i + ' of torrent ' + torrent.name)
      file.deselect()
    }
  }
}

// Gets a WebTorrent handle by torrentKey
// Throws an Error if we're not currently torrenting anything w/ that key
function getTorrent (torrentKey) {
  var ret = client.torrents.find((x) => x.key === torrentKey)
  if (!ret) throw new Error('missing torrent key ' + torrentKey)
  return ret
}

function onError (err) {
  console.log(err)
}
<|endoftext|>


<commit_before>const { EventEmitter } = require('events');
const LinkedList = require('./LinkedList');

/**Razorframe constructor
 *
 * MSG parameters
 * @param {string} MSG.contents => the message value
 * @param {string} MSG.eventOut => the outbound event name
 * @param {string} MSG.channel => the channel name
 */
class Razorframe {
  constructor() {
    this.storage = new LinkedList();
    this.notification = new EventEmitter();
  }

  enqueue(MSG) {
    console.log('enqueue has run');

    if (!MSG) console.error('Error: must pass in valid msg object!');
    else if (!MSG.contents) console.error('Error: contents should not be null');
    else if (!MSG.eventOut) console.error("Error: MSG object must contain valid outbound event name");

    else {
      this.storage.push(MSG);
      this.notification.emit('enq', this.storage.length);
    }
  }

  dequeue() {
    console.log('dequeue has run.');

    if (this.storage.length === 0) console.error('Error: message queue is currently empty!');

    else {
      let MSG = this.storage.pop();
      this.notification.emit('deq', MSG);
    }
  }
}

let io;

const rz = {
  razorframe: new Razorframe(),

  init(http, config) {
    io = require('socket.io')(http);
    handleSockets(config);
  },

  // If errors occur during database writes, the message will be sent to onError function. If it is the first time the message has caused an error, it will be reentered into the queue for a second attempt at a database write, but it will have an error flag. If the same message comes back, it will emit an error message to the client.
  onError(MSG, count = 2) {
    if (MSG.error === count) {
      console.log('inside error function with msg:', MSG.contents);
      io.to(MSG.id).emit('error', `There was an error writing "${MSG.contents}" to the database.`);
    } else {
      if (!MSG.error) MSG.error = 1;
      else MSG.error++;
      this.razorframe.enqueue(MSG);
    }
  }
}

const handleSockets = (config) => {
  const { write, show } = config;

  io.on('connection', (socket) => {
    console.log('a user connected! 💃');
    show(socket);

    socket.on('disconnect', () => console.log('user disconnected!'));

    // Client listeners
    socket.on('msgSent', (MSG, id) => {
      MSG.id = socket.id;
      rz.razorframe.enqueue(MSG);
    });

  });

  // Server-side listeners
  rz.razorframe.notification.on('enq', (data) => {
    rz.razorframe.dequeue();
  });

  rz.razorframe.notification.on('deq', (MSG) => {
    write(MSG);
    if (!MSG.error) io.emit(MSG.eventOut, MSG.contents);

    // Potential way to broadcast error message to client who is source of error
    // io.to(MSG.id).emit(MSG.eventOut, MSG.contents);
  });

};

module.exports = { Razorframe, rz };

/**
 * Messaging queue module for razorframe.
 * @author - Travis Huff (huff.travis@gmail.com)
 * @author - Eddie Park (ed.sh.park@gmail.com)
 * @author - Michael Sotkin (michael.sotkin@gmail.com)
 */
<commit_msg>Refactor<commit_after>const { EventEmitter } = require('events');
const LinkedList = require('./LinkedList');

/**Razorframe constructor
 *
 * MSG parameters
 * @param {string} MSG.contents => the message value
 * @param {string} MSG.eventOut => the outbound event name
 * @param {string} MSG.channel => the channel name
 */
class Razorframe {
  constructor() {
    this.storage = new LinkedList();
    this.notification = new EventEmitter();
  }

  enqueue(MSG) {
    console.log('enqueue has run');

    if (!MSG) console.error('Error: must pass in valid msg object!');
    else if (!MSG.contents) console.error('Error: contents should not be null');
    else if (!MSG.eventOut) console.error("Error: MSG object must contain valid outbound event name");

    else {
      this.storage.push(MSG);
      this.notification.emit('enq', this.storage.length);
    }
  }

  dequeue() {
    console.log('dequeue has run.');

    if (this.storage.length === 0) console.error('Error: message queue is currently empty!');

    else {
      let MSG = this.storage.pop();
      this.notification.emit('deq', MSG);
    }
  }
}

let io;

const rz = {
  razorframe: new Razorframe(),

  init(http, config) {
    io = require('socket.io')(http);
    handleSockets(config);
  },

  // If errors occur during database writes, the message will be sent to onError function. If it is the first time the message has caused an error, it will be reentered into the queue for a second attempt at a database write, but it will have an error flag. If the same message comes back, it will emit an error message to the client.
  onError(MSG, count = 2) {
    if (MSG.error === count) {
      console.log('inside error function with msg:', MSG.contents);
      io.to(MSG.id).emit('error', `There was an error writing "${MSG.contents}" to the database.`);
    } else {
      if (!MSG.error) MSG.error = 1;
      else MSG.error++;
      this.razorframe.enqueue(MSG);
    }
  }
}

const handleSockets = (config) => {
  const { write, show } = config;

  io.on('connection', (socket) => {
    console.log('a user connected! 💃');
    show(socket);

    socket.on('disconnect', () => console.log('user disconnected!'));

    // Client listeners
    socket.on('msgSent', (MSG, id) => {
      MSG.id = socket.id;
      rz.razorframe.enqueue(MSG);
    });

  });

  // Server-side listeners
  rz.razorframe.notification.on('enq', (data) => {
    rz.razorframe.dequeue();
  });

  rz.razorframe.notification.on('deq', (MSG) => {
    write(MSG);
    if (!MSG.error) io.emit(MSG.eventOut, MSG.contents);
  });

};

module.exports = { Razorframe, rz };

/**
 * Messaging queue module for razorframe.
 * @author - Travis Huff (huff.travis@gmail.com)
 * @author - Eddie Park (ed.sh.park@gmail.com)
 * @author - Michael Sotkin (michael.sotkin@gmail.com)
 */
<|endoftext|>


<commit_before>(function ($) {
  var state = {
    instances: [],
    active: null
  }

  //TODO join this function with $.fn.googleMap
  $.fn.googleMapInstances = function(options) {
    var module = {
      map: {
        add: function(element, settings) {
          //Create new map instance
          var map = $(element).googleMap(settings)

          state.instances.push(map)
          if(state.active === null) { //This is the first map, set it as active
            state.active = state.instances.length -1
          }

          module.map.setup()
        },
        setup: function() {
          var activeMap = state.active

          //Create map controls
          var controlContainer = $('<div>', {
            class: 'toolbar'
          })

          if (navigator.geolocation) {
            var controlUserLocation = $('<div>', {
              class: 'btn showMyLocation'
            })
            .append(
              $('<span>', {
                class: 'fa fa-location-arrow'
              }),
              $('<span>')
              .text('Min position')
            )
            .on('click', function () {
              module.map.set(activeMap)
              $.fn.vegosvar.map().updateUserLocation()
            })

            $(controlContainer).append(controlUserLocation)
          }

          if (fullscreenSupported()) {
            var controlFullscreen = $('<div>', {
              class: 'btn showFullscreen'
            })
            .append(
              $('<span>', {
                class: 'glyphicon glyphicon-fullscreen'
              })
            )
            .on('click', function () {
              module.map.set(activeMap)
              $.fn.vegosvar.map().toggleFullscreen()
            })

            $(controlContainer).append(controlFullscreen)
          }

          //Add controls to map
          module.map.current().addControl(controlContainer[0])
        },
        current: function() {
          //Return the current instance
          if(state.active !== null) {
            if(state.instances[state.active] !== 'undefined') {
              return state.instances[state.active]
            }
          }

          return false
        },
        get: function(instance) {
          //Get instance if one is provided, else return all instances
          if(typeof(instance) === 'undefined') {
            return state.instances
          }

          if(state.instances[instance] !== 'undefined') {
            return state.instances[instance]
          } else {
            return false
          }
        },
        set: function(position) {
          //Set the current
          if(state.instances[position] !== 'undefined') {
            state.active = position
          } else {
            return false
          }
        }
      },
      marker: {
        add: function(element) {

        },
        get: function(element) {

        },
        remove: function(element) {

        }
      }
    }

    return module
  }

  $.fn.vegosvar.map = function() {
    var module = {
      getInfowindowContent: function(content) {
        var content = (content.length > 90) ? content.substr(0, 90) + '...' : content
        var result = $('<div>', {
          class: 'infowindow-content'
        })
        .html(content)

        return result
      },
      entryContent: function(entry) {
        return $('<div>')
          .append(
            $('<div>', {
              id: 'infowindow-' + entry._id,
              class: 'infowindow-container'
            })
            .append(
              $('<div>', {
                class: 'infowindow-header'
              })
              .append(
                $('<div>', {
                  class: 'infowindow-image'
                })
                .append(
                  $('<img>', {
                    src: '/uploads/' + entry.post.cover.filename + '.jpg'
                  })
               ),
                $('<div>', {
                  class: 'infowindow-title'
                })
                .append(
                  $('<a>', {
                    href: entry.url
                  })
                  .html(entry.title)
               ),
                $('<div>', {
                  class: 'infowindow-stars-container'
                })
                .append(
                  $('<div>', {
                    class: 'stars'
                  })
                  .append(
                    $.map(new Array(5), function (value, i) {
                      return $('<div>', {
                        class: ('rating' in entry && 'votes_sum' in entry.rating && (i + 1) <= entry.rating.votes_sum) ? 'star-static active' : 'star-static'
                      }).append(
                        $('<span>', {
                          class: 'glyphicon glyphicon-star'
                        })
                     )
                    })
                 )
               )
             ),
             module.getInfowindowContent(entry.post.content)
           )
         )
      },
      applyMarkerData: function(data, options) {
        if (options.hasOwnProperty('data')) {
          if (options.data.filterName === 'single') {
            if (data.length > 0 && data[0].post.hasOwnProperty('coordinates')) {
              var mapInstance = $.fn.googleMapInstances().map.current()

              mapInstance.setCenter({
                lat: parseFloat(data[0].post.coordinates.latitude),
                lng: parseFloat(data[0].post.coordinates.longitude)
              })

              mapInstance.setZoom(15)

              module.setMarkers(data, { infoWindowOpen: true })

              var filter = {
                data: {
                  filter: {
                    ignore: data[0]._id
                  }
                }
              }

              module.getMarkerData(filter, function (data) {
                module.applyMarkerData(data, {})
              })
            } else {
              //console.log('Page is missing map coordinates')
              return false
            }
          }
        }

        module.setMarkers(data, options)
      },
      setMarkers: function(data, settings) {
        var mapInstance = $.fn.googleMapInstances().map.current()

        for (var i = 0; i < data.length; i++) {
          entry = data[i]
          if (typeof(entry.post.coordinates) !== 'undefined') {
            var iconUrl = '/assets/images/'

            switch (entry.type) {
              case '3':
                iconUrl += 'pin-restaurant.png'
                break
              case '5':
                iconUrl += 'pin-store.png'
                break
              case '5':
                iconUrl += 'pin-cafe.png'
                break
              default:
                return false
            }

            var content = module.entryContent(entry)

            var marker = mapInstance.setMarker({
              position: {
                lat: parseFloat(entry.post.coordinates.latitude),
                lng: parseFloat(entry.post.coordinates.longitude)
              },
              title: entry.title,
              content: content.html(),
              icon: {
                url: iconUrl,
                size: new google.maps.Size(32, 32)
              }
            })

            if(settings) {
              if ('infoWindowOpen' in settings && settings.infoWindowOpen === true) {
                //Open the newly added infowindow
                marker.infowindow.open(mapInstance.getMap(), marker)

                //Pan to fit the new infowindow
                google.maps.event.addListenerOnce(marker.infowindow, 'domready', function () {
                  $.fn.vegosvar.map().panToFit()
                })
              }
            }
          }
        }

        if (typeof(settings) !== 'undefined' && settings.hasOwnProperty('cluster')) {
          if (settings.cluster === true) {
            mapInstance.clusterMarkers()
          }
        }
      },
      panToFit: function() {
        var mapInstance = $.fn.googleMapInstances().map.current()

        var projection = mapInstance.getMap().getProjection()

        var bounds = $.fn.googleMap().getBounds()
        var center = bounds.getCenter()

        var latLng = projection.fromLatLngToPoint(center)
        var scale = 1 << mapInstance.getZoom()

        var position = null
        var offset = null

        if ($(window).height() > 640) {
          offset = ((($(window).height() * 256)) / scale)
        } else {
          offset = ((($(window).height() * 256) * 2.5) / scale)
        }

        if(offset) {
          position = $.fn.googleMap().getPoint(
            ((latLng.x * scale)) / scale,
            ((latLng.y * scale) - offset - 100) / scale
          )

          if (position) {
            var newCenter = projection.fromPointToLatLng(position)
            mapInstance.setCenter(newCenter)
            return
          }
        }
      },
      getMapOptions: function(elementId) {
        var options = {}
        var mapData = $(elementId).data()

        //Check if map is configured to auto initialize
        if (mapData.hasOwnProperty('mapInit')) {
          options.init = mapData.mapInit
        }

        if (mapData.hasOwnProperty('mapCluster')) {
          options.cluster = mapData.mapCluster
        }

        if (mapData.hasOwnProperty('mapFilter')) {
          var filters = {
            single: function () {
              return {
                id: $('.content')[0].id //Include this in the map data attributes
              }
            },
            restaurant: function () {
              return {
                type: '3'
              }
            },
            butik: function () {
              return {
                type: '5'
              }
            },
            cafe: function () {
              return {
                type: '6'
              }
            }
          }

          if (mapData.mapFilter in filters) {
            options.data = {
              filter: filters[mapData.mapFilter](),
              filterName: mapData.mapFilter
            }
          }
        }

        if (mapData.hasOwnProperty('markerLatitude')) {
          options.markerLatitude = mapData.markerLatitude
        }

        if (mapData.hasOwnProperty('markerLongitude')) {
          options.markerLongitude = mapData.markerLongitude
        }

        if (mapData.hasOwnProperty('markerTitle')) {
          options.markerTitle = mapData.markerTitle
        }

        return options
      },
      setSingleOpenMarker: function(obj) {
        var mapInstance = $.fn.googleMapInstances().map.current()
        var marker = mapInstance.setMarker({
          position: {
            lat: parseFloat(obj.coordinates.latitude),
            lng: parseFloat(obj.coordinates.longitude)
          },
          content: obj.content
        })

        mapInstance.setZoom(11)

        mapInstance.setCenter({
          lat: parseFloat(obj.coordinates.latitude),
          lng: parseFloat(obj.coordinates.longitude)
        })

        marker.infowindow.open(mapInstance.getMap(), marker)
        google.maps.event.addListenerOnce(marker.infowindow, 'domready', function () {
          $.fn.vegosvar.map().panToFit()
        })
      },
      getMarkerData: function(options, callback) {
        var settings = $.extend({
          url: '/ajax/map'
        }, options)

        $.ajax(settings)
        .done(function (data) {
          callback(data)
        })
      },
      toggleFullscreen: function () {
        var mapInstance = $.fn.googleMapInstances().map.current()
        var mapElement = mapInstance.getElement()
        var element = $(mapElement).parent()

        if (isFullscreen()) {
          exitFullscreen()
          $(element).css({
            margin: '',
            width: '',
            height: '',
            position: '',
            top: ''
          })
          return
        }

        var center = mapInstance.getCenter()
        enterFullscreen(element[0])

        $(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange', function (e) {
          if (isFullscreen()) {
            $(element).css({
              margin: '0',
              width: '100%',
              height: '100%',
              position: 'absolute',
              top: '0'
            })
          } else {
            $(element).css({
              margin: '',
              width: '',
              height: '',
              position: '',
              top: ''
            })
          }

          mapInstance.triggerResize()
          mapInstance.setCenter(center)
        })
      },
      updateUserLocation: function () {
        var userMarker = {
          title: 'Din plats',
          content: 'Du är här!',
          icon: '/assets/images/pin-my-position.png',
        }

        $.fn.geoLocation(function (result) {
          if (result.success === true) {
            userMarker.position = {
              lat: parseFloat(result.position.latitude),
              lng: parseFloat(result.position.longitude)
            }

            var mapInstance = $.fn.googleMapInstances().map.current()
            var markers = mapInstance.getMarkers()

            var setNew = true

            //Loop over all existing markers, if user location marker is found, update it
            $.each(markers, function (i, marker) {
              if ('title' in marker) {
                if (marker.title === userMarker.title) { //This is bad and I know it's bad, deal with it
                  setNew = false

                  marker.setPosition(userMarker.position)
                  marker.infowindow.open(mapInstance.getMap(), marker)
                }
              }
            })

            if (setNew) {
              var marker = mapInstance.setMarker(userMarker)
              marker.infowindow.open(mapInstance.getMap(), marker)
            }

            mapInstance.setCenter(userMarker.position)
            mapInstance.setZoom(11)
          }
        })
      }
    }

    return module
  }
}(jQuery))

function zoomToUserLocation () {
  $.fn.geoLocation(function (result) {
    if (result.success === true) {
      var position = {
        lat: parseFloat(result.position.latitude),
        lng: parseFloat(result.position.longitude)
      }

      var locationObj = {
        position: position,
        title: 'Din plats',
        content: 'Du är här!',
        icon: '/assets/images/pin-my-position.png',
      }

      var mapInstance = $.fn.googleMapInstances().map.current()
      var markers = mapInstance.getMarkers()
      var setNew = true

      //Loop over all existing markers, if user location marker is found, update it
      $.each(markers, function(i, marker) {
        if('title' in marker) {
          if(marker.title === locationObj.title) {
            setNew = false
            marker.setPosition(position)
            marker.infowindow.open(mapInstance.getMap(), marker)
          }
        }
      })

      if(setNew) {
        var marker = mapInstance.setMarker(locationObj)
        marker.infowindow.open(mapInstance.getMap(), marker)
      }

      mapInstance.setCenter(position)
      mapInstance.setZoom(11)
    } else {
      //TODO display error
    }
  })
}

$(document).bind('mapready', function (e) {
  //Initialize map(s)
  $('.googleMap').each(function(i, map) {
    $.fn.googleMapInstances().map.add(map)
  })

  $('#show-map').on('click', function (e) {
    e.preventDefault()

    $('.filter').addClass('hidden') //Remove overlay filter

    //Get map options
    var options = $.fn.vegosvar.map().getMapOptions('#map')

    //Get marker data
    $.fn.vegosvar.map().getMarkerData(options, function (data) {
      $.fn.vegosvar.map().applyMarkerData(data, options)
    })

    $.fn.vegosvar.map().updateUserLocation()
  })

  if ($('#map').length > 0) {
    var options = $.fn.vegosvar.map().getMapOptions('#map')

    if ('init' in options && options.init === true) {
      if ('markerLatitude' in options && 'markerLongitude' in options) {
        //Map provides its own coordinates
        $.fn.vegosvar.map().setSingleOpenMarker({
          coordinates: {
            latitude: options.markerLatitude,
            longitude: options.markerLongitude
          },
          content: options.markerTitle
        })
      } else {
        $.fn.vegosvar.map().getMarkerData(options, function (data) {
          $.fn.vegosvar.map().applyMarkerData(data, options)
        })
      }
    }
  }
})<commit_msg>Only zoom to user position if button has relevant class<commit_after>(function ($) {
  var state = {
    instances: [],
    active: null
  }

  //TODO join this function with $.fn.googleMap
  $.fn.googleMapInstances = function(options) {
    var module = {
      map: {
        add: function(element, settings) {
          //Create new map instance
          var map = $(element).googleMap(settings)

          state.instances.push(map)
          if(state.active === null) { //This is the first map, set it as active
            state.active = state.instances.length -1
          }

          module.map.setup()
        },
        setup: function() {
          var activeMap = state.active

          //Create map controls
          var controlContainer = $('<div>', {
            class: 'toolbar'
          })

          if (navigator.geolocation) {
            var controlUserLocation = $('<div>', {
              class: 'btn showMyLocation'
            })
            .append(
              $('<span>', {
                class: 'fa fa-location-arrow'
              }),
              $('<span>')
              .text('Min position')
            )
            .on('click', function () {
              module.map.set(activeMap)
              $.fn.vegosvar.map().updateUserLocation()
            })

            $(controlContainer).append(controlUserLocation)
          }

          if (fullscreenSupported()) {
            var controlFullscreen = $('<div>', {
              class: 'btn showFullscreen'
            })
            .append(
              $('<span>', {
                class: 'glyphicon glyphicon-fullscreen'
              })
            )
            .on('click', function () {
              module.map.set(activeMap)
              $.fn.vegosvar.map().toggleFullscreen()
            })

            $(controlContainer).append(controlFullscreen)
          }

          //Add controls to map
          module.map.current().addControl(controlContainer[0])
        },
        current: function() {
          //Return the current instance
          if(state.active !== null) {
            if(state.instances[state.active] !== 'undefined') {
              return state.instances[state.active]
            }
          }

          return false
        },
        get: function(instance) {
          //Get instance if one is provided, else return all instances
          if(typeof(instance) === 'undefined') {
            return state.instances
          }

          if(state.instances[instance] !== 'undefined') {
            return state.instances[instance]
          } else {
            return false
          }
        },
        set: function(position) {
          //Set the current
          if(state.instances[position] !== 'undefined') {
            state.active = position
          } else {
            return false
          }
        }
      },
      marker: {
        add: function(element) {

        },
        get: function(element) {

        },
        remove: function(element) {

        }
      }
    }

    return module
  }

  $.fn.vegosvar.map = function() {
    var module = {
      getInfowindowContent: function(content) {
        var content = (content.length > 90) ? content.substr(0, 90) + '...' : content
        var result = $('<div>', {
          class: 'infowindow-content'
        })
        .html(content)

        return result
      },
      entryContent: function(entry) {
        return $('<div>')
          .append(
            $('<div>', {
              id: 'infowindow-' + entry._id,
              class: 'infowindow-container'
            })
            .append(
              $('<div>', {
                class: 'infowindow-header'
              })
              .append(
                $('<div>', {
                  class: 'infowindow-image'
                })
                .append(
                  $('<img>', {
                    src: '/uploads/' + entry.post.cover.filename + '.jpg'
                  })
               ),
                $('<div>', {
                  class: 'infowindow-title'
                })
                .append(
                  $('<a>', {
                    href: entry.url
                  })
                  .html(entry.title)
               ),
                $('<div>', {
                  class: 'infowindow-stars-container'
                })
                .append(
                  $('<div>', {
                    class: 'stars'
                  })
                  .append(
                    $.map(new Array(5), function (value, i) {
                      return $('<div>', {
                        class: ('rating' in entry && 'votes_sum' in entry.rating && (i + 1) <= entry.rating.votes_sum) ? 'star-static active' : 'star-static'
                      }).append(
                        $('<span>', {
                          class: 'glyphicon glyphicon-star'
                        })
                     )
                    })
                 )
               )
             ),
             module.getInfowindowContent(entry.post.content)
           )
         )
      },
      applyMarkerData: function(data, options) {
        if (options.hasOwnProperty('data')) {
          if (options.data.filterName === 'single') {
            if (data.length > 0 && data[0].post.hasOwnProperty('coordinates')) {
              var mapInstance = $.fn.googleMapInstances().map.current()

              mapInstance.setCenter({
                lat: parseFloat(data[0].post.coordinates.latitude),
                lng: parseFloat(data[0].post.coordinates.longitude)
              })

              mapInstance.setZoom(15)

              module.setMarkers(data, { infoWindowOpen: true })

              var filter = {
                data: {
                  filter: {
                    ignore: data[0]._id
                  }
                }
              }

              module.getMarkerData(filter, function (data) {
                module.applyMarkerData(data, {})
              })
            } else {
              //console.log('Page is missing map coordinates')
              return false
            }
          }
        }

        module.setMarkers(data, options)
      },
      setMarkers: function(data, settings) {
        var mapInstance = $.fn.googleMapInstances().map.current()

        for (var i = 0; i < data.length; i++) {
          entry = data[i]
          if (typeof(entry.post.coordinates) !== 'undefined') {
            var iconUrl = '/assets/images/'

            switch (entry.type) {
              case '3':
                iconUrl += 'pin-restaurant.png'
                break
              case '5':
                iconUrl += 'pin-store.png'
                break
              case '5':
                iconUrl += 'pin-cafe.png'
                break
              default:
                return false
            }

            var content = module.entryContent(entry)

            var marker = mapInstance.setMarker({
              position: {
                lat: parseFloat(entry.post.coordinates.latitude),
                lng: parseFloat(entry.post.coordinates.longitude)
              },
              title: entry.title,
              content: content.html(),
              icon: {
                url: iconUrl,
                size: new google.maps.Size(32, 32)
              }
            })

            if(settings) {
              if ('infoWindowOpen' in settings && settings.infoWindowOpen === true) {
                //Open the newly added infowindow
                marker.infowindow.open(mapInstance.getMap(), marker)

                //Pan to fit the new infowindow
                google.maps.event.addListenerOnce(marker.infowindow, 'domready', function () {
                  $.fn.vegosvar.map().panToFit()
                })
              }
            }
          }
        }

        if (typeof(settings) !== 'undefined' && settings.hasOwnProperty('cluster')) {
          if (settings.cluster === true) {
            mapInstance.clusterMarkers()
          }
        }
      },
      panToFit: function() {
        var mapInstance = $.fn.googleMapInstances().map.current()

        var projection = mapInstance.getMap().getProjection()

        var bounds = $.fn.googleMap().getBounds()
        var center = bounds.getCenter()

        var latLng = projection.fromLatLngToPoint(center)
        var scale = 1 << mapInstance.getZoom()

        var position = null
        var offset = null

        if ($(window).height() > 640) {
          offset = ((($(window).height() * 256)) / scale)
        } else {
          offset = ((($(window).height() * 256) * 2.5) / scale)
        }

        if(offset) {
          position = $.fn.googleMap().getPoint(
            ((latLng.x * scale)) / scale,
            ((latLng.y * scale) - offset - 100) / scale
          )

          if (position) {
            var newCenter = projection.fromPointToLatLng(position)
            mapInstance.setCenter(newCenter)
            return
          }
        }
      },
      getMapOptions: function(elementId) {
        var options = {}
        var mapData = $(elementId).data()

        //Check if map is configured to auto initialize
        if (mapData.hasOwnProperty('mapInit')) {
          options.init = mapData.mapInit
        }

        if (mapData.hasOwnProperty('mapCluster')) {
          options.cluster = mapData.mapCluster
        }

        if (mapData.hasOwnProperty('mapFilter')) {
          var filters = {
            single: function () {
              return {
                id: $('.content')[0].id //Include this in the map data attributes
              }
            },
            restaurant: function () {
              return {
                type: '3'
              }
            },
            butik: function () {
              return {
                type: '5'
              }
            },
            cafe: function () {
              return {
                type: '6'
              }
            }
          }

          if (mapData.mapFilter in filters) {
            options.data = {
              filter: filters[mapData.mapFilter](),
              filterName: mapData.mapFilter
            }
          }
        }

        if (mapData.hasOwnProperty('markerLatitude')) {
          options.markerLatitude = mapData.markerLatitude
        }

        if (mapData.hasOwnProperty('markerLongitude')) {
          options.markerLongitude = mapData.markerLongitude
        }

        if (mapData.hasOwnProperty('markerTitle')) {
          options.markerTitle = mapData.markerTitle
        }

        return options
      },
      setSingleOpenMarker: function(obj) {
        var mapInstance = $.fn.googleMapInstances().map.current()
        var marker = mapInstance.setMarker({
          position: {
            lat: parseFloat(obj.coordinates.latitude),
            lng: parseFloat(obj.coordinates.longitude)
          },
          content: obj.content
        })

        mapInstance.setZoom(11)

        mapInstance.setCenter({
          lat: parseFloat(obj.coordinates.latitude),
          lng: parseFloat(obj.coordinates.longitude)
        })

        marker.infowindow.open(mapInstance.getMap(), marker)
        google.maps.event.addListenerOnce(marker.infowindow, 'domready', function () {
          $.fn.vegosvar.map().panToFit()
        })
      },
      getMarkerData: function(options, callback) {
        var settings = $.extend({
          url: '/ajax/map'
        }, options)

        $.ajax(settings)
        .done(function (data) {
          callback(data)
        })
      },
      toggleFullscreen: function () {
        var mapInstance = $.fn.googleMapInstances().map.current()
        var mapElement = mapInstance.getElement()
        var element = $(mapElement).parent()

        if (isFullscreen()) {
          exitFullscreen()
          $(element).css({
            margin: '',
            width: '',
            height: '',
            position: '',
            top: ''
          })
          return
        }

        var center = mapInstance.getCenter()
        enterFullscreen(element[0])

        $(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange', function (e) {
          if (isFullscreen()) {
            $(element).css({
              margin: '0',
              width: '100%',
              height: '100%',
              position: 'absolute',
              top: '0'
            })
          } else {
            $(element).css({
              margin: '',
              width: '',
              height: '',
              position: '',
              top: ''
            })
          }

          mapInstance.triggerResize()
          mapInstance.setCenter(center)
        })
      },
      updateUserLocation: function () {
        var userMarker = {
          title: 'Din plats',
          content: 'Du är här!',
          icon: '/assets/images/pin-my-position.png',
        }

        $.fn.geoLocation(function (result) {
          if (result.success === true) {
            userMarker.position = {
              lat: parseFloat(result.position.latitude),
              lng: parseFloat(result.position.longitude)
            }

            var mapInstance = $.fn.googleMapInstances().map.current()
            var markers = mapInstance.getMarkers()

            var setNew = true

            //Loop over all existing markers, if user location marker is found, update it
            $.each(markers, function (i, marker) {
              if ('title' in marker) {
                if (marker.title === userMarker.title) { //This is bad and I know it's bad, deal with it
                  setNew = false

                  marker.setPosition(userMarker.position)
                  marker.infowindow.open(mapInstance.getMap(), marker)
                }
              }
            })

            if (setNew) {
              var marker = mapInstance.setMarker(userMarker)
              marker.infowindow.open(mapInstance.getMap(), marker)
            }

            mapInstance.setCenter(userMarker.position)
            mapInstance.setZoom(11)
          }
        })
      }
    }

    return module
  }
}(jQuery))

function zoomToUserLocation () {
  $.fn.geoLocation(function (result) {
    if (result.success === true) {
      var position = {
        lat: parseFloat(result.position.latitude),
        lng: parseFloat(result.position.longitude)
      }

      var locationObj = {
        position: position,
        title: 'Din plats',
        content: 'Du är här!',
        icon: '/assets/images/pin-my-position.png',
      }

      var mapInstance = $.fn.googleMapInstances().map.current()
      var markers = mapInstance.getMarkers()
      var setNew = true

      //Loop over all existing markers, if user location marker is found, update it
      $.each(markers, function(i, marker) {
        if('title' in marker) {
          if(marker.title === locationObj.title) {
            setNew = false
            marker.setPosition(position)
            marker.infowindow.open(mapInstance.getMap(), marker)
          }
        }
      })

      if(setNew) {
        var marker = mapInstance.setMarker(locationObj)
        marker.infowindow.open(mapInstance.getMap(), marker)
      }

      mapInstance.setCenter(position)
      mapInstance.setZoom(11)
    } else {
      //TODO display error
    }
  })
}

$(document).bind('mapready', function (e) {
  //Initialize map(s)
  $('.googleMap').each(function(i, map) {
    $.fn.googleMapInstances().map.add(map)
  })

  $('#show-map').on('click', function (e) {
    e.preventDefault()

    $('.filter').addClass('hidden') //Remove overlay filter

    //Get map options
    var options = $.fn.vegosvar.map().getMapOptions('#map')

    //Get marker data
    $.fn.vegosvar.map().getMarkerData(options, function (data) {
      $.fn.vegosvar.map().applyMarkerData(data, options)
    })

    if($(this).hasClass('showFullscreen')) {
      $.fn.vegosvar.map().updateUserLocation()
    }
  })

  if ($('#map').length > 0) {
    var options = $.fn.vegosvar.map().getMapOptions('#map')

    if ('init' in options && options.init === true) {
      if ('markerLatitude' in options && 'markerLongitude' in options) {
        //Map provides its own coordinates
        $.fn.vegosvar.map().setSingleOpenMarker({
          coordinates: {
            latitude: options.markerLatitude,
            longitude: options.markerLongitude
          },
          content: options.markerTitle
        })
      } else {
        $.fn.vegosvar.map().getMarkerData(options, function (data) {
          $.fn.vegosvar.map().applyMarkerData(data, options)
        })
      }
    }
  }
})<|endoftext|>


<commit_before>const settings = require("./nightwatch.json");

const { TRAVIS_JOB_NUMBER, TRAVIS_BRANCH } = process.env;
if(TRAVIS_JOB_NUMBER) {
    const desiredCapabilities = settings.test_settings.default.desiredCapabilities;
    if(TRAVIS_BRANCH === "master") {
        desiredCapabilities.build = `build-${ TRAVIS_JOB_NUMBER }`;
    }
    desiredCapabilities["tunnel-identifier"] = TRAVIS_JOB_NUMBER;
}

module.exports = settings;<commit_msg>support for pull request in TravisCI<commit_after>const settings = require("./nightwatch.json");

const { TRAVIS_JOB_NUMBER, TRAVIS_BRANCH, TRAVIS_PULL_REQUEST } = process.env;
if(TRAVIS_JOB_NUMBER) {
    const desiredCapabilities = settings.test_settings.default.desiredCapabilities;
    if(TRAVIS_BRANCH === "master" && TRAVIS_PULL_REQUEST === "false") {
        desiredCapabilities.build = `build-${ TRAVIS_JOB_NUMBER }`;
    }
    desiredCapabilities["tunnel-identifier"] = TRAVIS_JOB_NUMBER;
}

module.exports = settings;<|endoftext|>


<commit_before>
var util = require('util')
var Readable = require('stream').Readable
var co = require('co')

var ReadStream = function (r, conf, meta) {
  Readable.call(this)
  this.r = r
  this.conf = conf
  this.meta = meta
  this.num = 0
  // this.currentChunk = new Buffer(0)
}

util.inherits(ReadStream, Readable)

ReadStream.prototype._read = function () {
  co(function *() {
    if (!this.file) {
      this.file = yield this.r.table(this.conf.fileTable).filter({filename: this.meta.filename}).nth(0).default(null)
    }

    if (!this.file) {
      throw new Error('File not found')
    }

    var chunk = yield this.r.table(this.conf.chunkTable).getAll([this.file.id, this.num], {index: 'chunk_lookup'}).nth(0).default(null)

    if (chunk) {
      this.push(chunk.data)
      this.num += 1
    } else {
      this.push(null)
    }
  }.bind(this)).catch(function (err) {
    console.log(err.stack)
  })
}

module.exports = ReadStream
<commit_msg>use the config<commit_after>
var util = require('util')
var Readable = require('stream').Readable
var co = require('co')

var ReadStream = function (r, conf, meta) {
  Readable.call(this)
  this.r = r
  this.conf = conf
  this.meta = meta
  this.num = 0
  // this.currentChunk = new Buffer(0)
}

util.inherits(ReadStream, Readable)

ReadStream.prototype._read = function () {
  co(function *() {
    if (!this.file) {
      this.file = yield this.r.table(this.conf.fileTable).filter({filename: this.meta.filename}).nth(0).default(null)
    }

    if (!this.file) {
      throw new Error('File not found')
    }

    var chunk = yield this.r.table(this.conf.chunkTable).getAll([this.file.id, this.num], {index: this.conf.chunkIndex}).nth(0).default(null)

    if (chunk) {
      this.push(chunk.data)
      this.num += 1
    } else {
      this.push(null)
    }
  }.bind(this)).catch(function (err) {
    console.log(err.stack)
  })
}

module.exports = ReadStream
<|endoftext|>


<commit_before>var cost = function(body){
	var mycost = 0;
	for(var bodypart of body){
		mycost += BODYPART_COST[bodypart]
	}
	return mycost
}

var bodies = {
	miner : function(maxEnergy){
		var body=[WORK];
		var minEnergy = cost([WORK,MOVE])
		if(maxEnergy > minEnergy){
			var n = Math.min(Math.floor((maxEnergy - minEnergy)/BODYPART_COST[WORK]),4); //a maximum of 5 WORK parts
			for(i=0; i<n; i++){
				body.push(WORK);
			}
		}
		body.push(MOVE);
		return body
	},
	civilian : function(maxEnergy){
		var template = [WORK,CARRY,MOVE];
		var intervalEnergy = cost(template);
		var n = Math.min(Math.floor(maxEnergy/intervalEnergy),10); //hardcapped at 10
		var body = [];
		for(i=0;i<n;i++){
			body.push(WORK,CARRY,MOVE);
		}
		return body
	},
	upgrader : function(maxEnergy){
		return this.civilian(maxEnergy);
	},
	repairer : function(maxEnergy){
		return this.civilian(maxEnergy);
	},
	harvester : function(maxEnergy){
		var template = [WORK,CARRY,MOVE];
		var intervalEnergy = cost(template);
		var n = Math.min(Math.floor(maxEnergy/intervalEnergy),10); //hardcapped at 10
		var body = [];
		for(i=0;i<n;i++){
			body.push(WORK,CARRY,MOVE);
		}
		return body
	},
	runner : function(maxEnergy){
		var template = [CARRY,MOVE];
		var intervalEnergy=cost(template);
		var n = Math.min(Math.floor(maxEnergy/intervalEnergy),10); //currently hardcapped at 10
		var body = [];
		for(i=0;i<n;i++){
			body.push(CARRY,MOVE);
		}
		return body
	}
}

module.exports = {
    run : function(myrooms) {
    	// room based spawning
		for(var room of myrooms){
			var spawn = room.find(FIND_STRUCTURES,{filter : (s) => s.structureType == STRUCTURE_SPAWN && s.spawning == null})[0];
			if(spawn==undefined){
				continue
			}
			var maxEnergy = room.energyCapacityAvailable;

			var upgrader_target = 1; //guarantees one upgrader
			// number of civilians: at least 1, maximally 3, else enough to upgrade ca. 500 per 50 ticks, +1 for every 200k in storage
			let storage = room.find(FIND_STRUCTURES,{filter: (s) => s.structureType == STRUCTURE_STORAGE})[0];
			var civilian_target = Math.max(Math.min(Math.ceil(20/(bodies.civilian(maxEnergy).length/3))-1,3),1) + ( storage != undefined ? Math.floor(storage.store.energy/200000) : 0 );
			var builder_target = 0;
			var repairer_target = 1; //repairer is a builder that prioritises repairing non-wall structures
			var miner_target = 2;
			var runner_target = 2;
			var thief_target = 0;
			var hunter_target = 0;

			var hostiles = room.find(FIND_HOSTILE_CREEPS);
			if(hostiles.length){
				hunter_target=Math.ceil(hostiles.length/2)
			}

			var creepsByRole = _.groupBy(_.filter(Game.creeps,(c) => c.pos.roomName == room.name),'memory.role'); //this also counts spawning creeps
			var upgraders = creepsByRole.upgrader != undefined ? creepsByRole.upgrader : [];
			var civilians = creepsByRole.civilian != undefined ? creepsByRole.civilian : [];
			var repairers = creepsByRole.repairer != undefined ? creepsByRole.repairer : [];
			var miners = creepsByRole.miner != undefined ? creepsByRole.miner : [];
			var runners = creepsByRole.runner != undefined ? creepsByRole.runner : [];
			var hunters = creepsByRole.hunter != undefined ? creepsByRole.hunter : [];
			if(room.memory.requestList === undefined){ //checking this every tick is a waste
				room.memory.requestList = JSON.stringify([]);
			}
			var requestList = JSON.parse(room.memory.requestList);
			// var upgraders = room.find(FIND_MY_CREEPS,{filter: (creep) => creep.memory.role == 'upgrader'});
			// var builders = room.find(FIND_MY_CREEPS,{filter: (creep) => creep.memory.role == 'builder'});
			// var repairers = room.find(FIND_MY_CREEPS,{filter: (creep) => creep.memory.role == 'repairer'});
			// var miners = room.find(FIND_MY_CREEPS,{filter: (creep) => creep.memory.role == 'miner'});
			// var runners = room.find(FIND_MY_CREEPS,{filter: (creep) => creep.memory.role == 'runner'});
			// var hunters = room.find(FIND_MY_CREEPS,{filter: (creep) => creep.memory.role == 'hunter'});
			// var thiefs = room.find(FIND_MY_CREEPS,{filter: (creep) => creep.memory.role == 'thief'});
	       // console.log(miners.length + ' ' + runners.length + ' ' + upgraders.length + ' ' + repairers.length)

			//first ensure 1 miner, 1 runner, 1 upgrader are always available
			if(miners.length < 1){
				spawn.createCreep(bodies.miner(room.energyAvailable),undefined,{role:'miner'});
			}
			else if(runners.length < 1){
				spawn.createCreep(bodies.runner(room.energyAvailable),undefined,{role:'runner'});
			}
			else if(upgraders.length < 1 && room.controller.ticksToDowngrade < 500){
				spawn.createCreep(bodies.upgrader(room.energyAvailable),undefined,{role:'upgrader'});
			}
			// //prioritize first repairer so we have something to build early on
			// else if(repairers.length < 1){
				// spawn.createCreep(bodies.repairer(room.energyAvailable),undefined,{role:'repairer'});
			// }
			//after essentials are ensured, work on the requestList
			else if(requestList.length > 0){
				if(spawn.canCreateCreep(...requestList[0])){
					spawn.createCreep(...requestList.shift()) //spawns the first creep in the list and deletes it from the list
				}
			}
			//now proceed with the rest in priority order
			else if(miners.length < miner_target){
				spawn.createCreep(bodies.miner(maxEnergy),undefined,{role:'miner'});
			}
			else if(runners.length < runner_target){
				spawn.createCreep(bodies.runner(maxEnergy/2),undefined,{role:'runner'});
			}
			else if(upgraders.length < upgrader_target) {
				spawn.createCreep(bodies.upgrader(maxEnergy),undefined,{role:'upgrader'});
			}
			else if(civilians.length < civilian_target) {
				spawn.createCreep(bodies.civilian(maxEnergy),undefined,{role:'civilian'});
			}
			else if(repairers.length < repairer_target) {
				spawn.createCreep(bodies.repairer(maxEnergy),undefined,{role: 'repairer'});
			}
			else if(hunters.length < hunter_target) {
				if (spawn.room.energyAvailable > 600){
					spawn.createCreep([TOUGH,TOUGH,TOUGH,MOVE,MOVE,MOVE,MOVE,MOVE,ATTACK,ATTACK,ATTACK,MOVE],undefined,{role: 'hunter'});
				}
			}
			// if(thiefs.length < thief_target) {
				// if (spawn.room.energyAvailable > 600){
					// spawn.createCreep([CARRY,CARRY,CARRY,CARRY,MOVE,MOVE,MOVE,MOVE],undefined,{role: 'thief'});
				// }
			// }

			//flag based spawning with homeRoom
			var harvester_target = 2; //harvesters per remote site
			for (var flag of _.filter(Game.flags, (f)=>f.memory.homeRoom == room.name)){
				if(/reserve/.test(flag.name)){
					//defend remote room
					if(flag.pos.roomName in Game.rooms){//check to prevent breaking from no vision
						if(flag.room.find(FIND_HOSTILE_CREEPS).length){
							flag.memory.underAttack = true;
						}
						else{
							flag.memory.underAttack = false;
						}
					}
					if(flag.memory.underAttack){
						harvester_target = 0;
						var remoteHunters = _.filter(Game.creeps, (c) => c.memory.role == 'remoteHunter' && c.memory.myflag == flag.name);
						if(remoteHunters.length < 2){
							spawn.createCreep([TOUGH,MOVE,TOUGH,MOVE,ATTACK,MOVE,ATTACK,MOVE,ATTACK,MOVE,ATTACK,MOVE,ATTACK,MOVE],undefined,{
							role : 'remoteHunter', myflag : flag.name, homeRoom : room.name
							});
						}
					}
					//logic to spawn reservers
					var reservers = _.filter(Game.creeps, (creep) =>
						creep.memory.role == 'reserver' && creep.memory.myflag == flag.name
					);
					if (reservers.length < 1){
						var tospawn = false
						if (flag.memory.reserved && flag.pos.roomName in Game.rooms){ //second check is to prevent breaking from no vision
							var con = flag.pos.lookFor(LOOK_STRUCTURES,{filter: (s) => s.structureType == STRUCTURE_CONTROLLER})[0];
							if(con.reservation == undefined){ //yet more failsafes
								flag.memory.reserved = false;
							}
							else if(con.reservation.ticksToEnd < 500){
								tospawn = true;
							}
						}
						else{
							if(!flag.memory.reserved && spawn.canCreateCreep([MOVE,CLAIM,CLAIM,MOVE],undefined) == OK){
								tospawn = true;
								flag.memory.reserved = true; //should have some kind of check to prevent breaking
							}
						}
						if(tospawn){
							spawn.createCreep([MOVE,CLAIM,CLAIM,MOVE],undefined,{
								role: 'reserver', myflag: flag.name, homeRoom : room.name
							});
						}
					}
				}
				if(/harvest/.test(flag.name)){ //see that every remote site has enough harvesters
					var harvesters = _.filter(Game.creeps, (creep) =>
						creep.memory.role == 'harvester' && creep.memory.myflag == flag.name
					);
					if (harvesters.length < harvester_target){
						spawn.createCreep(bodies.harvester(maxEnergy),undefined,{
							role: 'harvester', myflag: flag.name, homeRoom: room.name
						});
					}
				}
			}
		}
		//flag based spawning
		for (var flag in Game.flags){
			if(/claim/.test(flag)){ //continously respawn remoteUpgraders to help establish the new room until flag is removed
				var remoteUpgraders = _.filter(Game.creeps,(creep) =>
					creep.memory.role == 'remoteUpgrader' && creep.memory.myflag == flag
				);
				if (remoteUpgraders.length < 1){
					Game.spawns['Spawn1'].createCreep([WORK,WORK,WORK,WORK,CARRY,CARRY,CARRY,CARRY,MOVE,MOVE,MOVE,MOVE],undefined,{
						role : 'remoteUpgrader' , myflag : flag
					});
				}
			}
		}

    }
};

module.exports.bodies = bodies;
<commit_msg>fix to requestList spawning<commit_after>var cost = function(body){
	var mycost = 0;
	for(var bodypart of body){
		mycost += BODYPART_COST[bodypart]
	}
	return mycost
}

var bodies = {
	miner : function(maxEnergy){
		var body=[WORK];
		var minEnergy = cost([WORK,MOVE])
		if(maxEnergy > minEnergy){
			var n = Math.min(Math.floor((maxEnergy - minEnergy)/BODYPART_COST[WORK]),4); //a maximum of 5 WORK parts
			for(i=0; i<n; i++){
				body.push(WORK);
			}
		}
		body.push(MOVE);
		return body
	},
	civilian : function(maxEnergy){
		var template = [WORK,CARRY,MOVE];
		var intervalEnergy = cost(template);
		var n = Math.min(Math.floor(maxEnergy/intervalEnergy),10); //hardcapped at 10
		var body = [];
		for(i=0;i<n;i++){
			body.push(WORK,CARRY,MOVE);
		}
		return body
	},
	upgrader : function(maxEnergy){
		return this.civilian(maxEnergy);
	},
	repairer : function(maxEnergy){
		return this.civilian(maxEnergy);
	},
	harvester : function(maxEnergy){
		var template = [WORK,CARRY,MOVE];
		var intervalEnergy = cost(template);
		var n = Math.min(Math.floor(maxEnergy/intervalEnergy),10); //hardcapped at 10
		var body = [];
		for(i=0;i<n;i++){
			body.push(WORK,CARRY,MOVE);
		}
		return body
	},
	runner : function(maxEnergy){
		var template = [CARRY,MOVE];
		var intervalEnergy=cost(template);
		var n = Math.min(Math.floor(maxEnergy/intervalEnergy),10); //currently hardcapped at 10
		var body = [];
		for(i=0;i<n;i++){
			body.push(CARRY,MOVE);
		}
		return body
	}
}

module.exports = {
    run : function(myrooms) {
    	// room based spawning
		for(var room of myrooms){
			var spawn = room.find(FIND_STRUCTURES,{filter : (s) => s.structureType == STRUCTURE_SPAWN && s.spawning == null})[0];
			if(spawn==undefined){
				continue
			}
			var maxEnergy = room.energyCapacityAvailable;

			var upgrader_target = 1; //guarantees one upgrader
			// number of civilians: at least 1, maximally 3, else enough to upgrade ca. 500 per 50 ticks, +1 for every 200k in storage
			let storage = room.find(FIND_STRUCTURES,{filter: (s) => s.structureType == STRUCTURE_STORAGE})[0];
			var civilian_target = Math.max(Math.min(Math.ceil(20/(bodies.civilian(maxEnergy).length/3))-1,3),1) + ( storage != undefined ? Math.floor(storage.store.energy/200000) : 0 );
			var builder_target = 0;
			var repairer_target = 1; //repairer is a builder that prioritises repairing non-wall structures
			var miner_target = 2;
			var runner_target = 2;
			var thief_target = 0;
			var hunter_target = 0;

			var hostiles = room.find(FIND_HOSTILE_CREEPS);
			if(hostiles.length){
				hunter_target=Math.ceil(hostiles.length/2)
			}

			var creepsByRole = _.groupBy(_.filter(Game.creeps,(c) => c.pos.roomName == room.name),'memory.role'); //this also counts spawning creeps
			var upgraders = creepsByRole.upgrader != undefined ? creepsByRole.upgrader : [];
			var civilians = creepsByRole.civilian != undefined ? creepsByRole.civilian : [];
			var repairers = creepsByRole.repairer != undefined ? creepsByRole.repairer : [];
			var miners = creepsByRole.miner != undefined ? creepsByRole.miner : [];
			var runners = creepsByRole.runner != undefined ? creepsByRole.runner : [];
			var hunters = creepsByRole.hunter != undefined ? creepsByRole.hunter : [];
			if(room.memory.requestList === undefined){ //checking this every tick is a waste
				room.memory.requestList = JSON.stringify([]);
			}
			var requestList = JSON.parse(room.memory.requestList);
			// var upgraders = room.find(FIND_MY_CREEPS,{filter: (creep) => creep.memory.role == 'upgrader'});
			// var builders = room.find(FIND_MY_CREEPS,{filter: (creep) => creep.memory.role == 'builder'});
			// var repairers = room.find(FIND_MY_CREEPS,{filter: (creep) => creep.memory.role == 'repairer'});
			// var miners = room.find(FIND_MY_CREEPS,{filter: (creep) => creep.memory.role == 'miner'});
			// var runners = room.find(FIND_MY_CREEPS,{filter: (creep) => creep.memory.role == 'runner'});
			// var hunters = room.find(FIND_MY_CREEPS,{filter: (creep) => creep.memory.role == 'hunter'});
			// var thiefs = room.find(FIND_MY_CREEPS,{filter: (creep) => creep.memory.role == 'thief'});
	       // console.log(miners.length + ' ' + runners.length + ' ' + upgraders.length + ' ' + repairers.length)

			//first ensure 1 miner, 1 runner, 1 upgrader are always available
			if(miners.length < 1){
				spawn.createCreep(bodies.miner(room.energyAvailable),undefined,{role:'miner'});
			}
			else if(runners.length < 1){
				spawn.createCreep(bodies.runner(room.energyAvailable),undefined,{role:'runner'});
			}
			else if(upgraders.length < 1 && room.controller.ticksToDowngrade < 500){
				spawn.createCreep(bodies.upgrader(room.energyAvailable),undefined,{role:'upgrader'});
			}
			// //prioritize first repairer so we have something to build early on
			// else if(repairers.length < 1){
				// spawn.createCreep(bodies.repairer(room.energyAvailable),undefined,{role:'repairer'});
			// }
			//after essentials are ensured, work on the requestList
			else if(requestList.length > 0){
				if(spawn.canCreateCreep(...requestList[0]) == OK){
					spawn.createCreep(...requestList.shift()) //spawns the first creep in the list and deletes it from the list
				}
			}
			//now proceed with the rest in priority order
			else if(miners.length < miner_target){
				spawn.createCreep(bodies.miner(maxEnergy),undefined,{role:'miner'});
			}
			else if(runners.length < runner_target){
				spawn.createCreep(bodies.runner(maxEnergy/2),undefined,{role:'runner'});
			}
			else if(upgraders.length < upgrader_target) {
				spawn.createCreep(bodies.upgrader(maxEnergy),undefined,{role:'upgrader'});
			}
			else if(civilians.length < civilian_target) {
				spawn.createCreep(bodies.civilian(maxEnergy),undefined,{role:'civilian'});
			}
			else if(repairers.length < repairer_target) {
				spawn.createCreep(bodies.repairer(maxEnergy),undefined,{role: 'repairer'});
			}
			else if(hunters.length < hunter_target) {
				if (spawn.room.energyAvailable > 600){
					spawn.createCreep([TOUGH,TOUGH,TOUGH,MOVE,MOVE,MOVE,MOVE,MOVE,ATTACK,ATTACK,ATTACK,MOVE],undefined,{role: 'hunter'});
				}
			}
			// if(thiefs.length < thief_target) {
				// if (spawn.room.energyAvailable > 600){
					// spawn.createCreep([CARRY,CARRY,CARRY,CARRY,MOVE,MOVE,MOVE,MOVE],undefined,{role: 'thief'});
				// }
			// }

			//flag based spawning with homeRoom
			var harvester_target = 2; //harvesters per remote site
			for (var flag of _.filter(Game.flags, (f)=>f.memory.homeRoom == room.name)){
				if(/reserve/.test(flag.name)){
					//defend remote room
					if(flag.pos.roomName in Game.rooms){//check to prevent breaking from no vision
						if(flag.room.find(FIND_HOSTILE_CREEPS).length){
							flag.memory.underAttack = true;
						}
						else{
							flag.memory.underAttack = false;
						}
					}
					if(flag.memory.underAttack){
						harvester_target = 0;
						var remoteHunters = _.filter(Game.creeps, (c) => c.memory.role == 'remoteHunter' && c.memory.myflag == flag.name);
						if(remoteHunters.length < 2){
							spawn.createCreep([TOUGH,MOVE,TOUGH,MOVE,ATTACK,MOVE,ATTACK,MOVE,ATTACK,MOVE,ATTACK,MOVE,ATTACK,MOVE],undefined,{
							role : 'remoteHunter', myflag : flag.name, homeRoom : room.name
							});
						}
					}
					//logic to spawn reservers
					var reservers = _.filter(Game.creeps, (creep) =>
						creep.memory.role == 'reserver' && creep.memory.myflag == flag.name
					);
					if (reservers.length < 1){
						var tospawn = false
						if (flag.memory.reserved && flag.pos.roomName in Game.rooms){ //second check is to prevent breaking from no vision
							var con = flag.pos.lookFor(LOOK_STRUCTURES,{filter: (s) => s.structureType == STRUCTURE_CONTROLLER})[0];
							if(con.reservation == undefined){ //yet more failsafes
								flag.memory.reserved = false;
							}
							else if(con.reservation.ticksToEnd < 500){
								tospawn = true;
							}
						}
						else{
							if(!flag.memory.reserved && spawn.canCreateCreep([MOVE,CLAIM,CLAIM,MOVE],undefined) == OK){
								tospawn = true;
								flag.memory.reserved = true; //should have some kind of check to prevent breaking
							}
						}
						if(tospawn){
							spawn.createCreep([MOVE,CLAIM,CLAIM,MOVE],undefined,{
								role: 'reserver', myflag: flag.name, homeRoom : room.name
							});
						}
					}
				}
				if(/harvest/.test(flag.name)){ //see that every remote site has enough harvesters
					var harvesters = _.filter(Game.creeps, (creep) =>
						creep.memory.role == 'harvester' && creep.memory.myflag == flag.name
					);
					if (harvesters.length < harvester_target){
						spawn.createCreep(bodies.harvester(maxEnergy),undefined,{
							role: 'harvester', myflag: flag.name, homeRoom: room.name
						});
					}
				}
			}
		}
		//flag based spawning
		for (var flag in Game.flags){
			if(/claim/.test(flag)){ //continously respawn remoteUpgraders to help establish the new room until flag is removed
				var remoteUpgraders = _.filter(Game.creeps,(creep) =>
					creep.memory.role == 'remoteUpgrader' && creep.memory.myflag == flag
				);
				if (remoteUpgraders.length < 1){
					Game.spawns['Spawn1'].createCreep([WORK,WORK,WORK,WORK,CARRY,CARRY,CARRY,CARRY,MOVE,MOVE,MOVE,MOVE],undefined,{
						role : 'remoteUpgrader' , myflag : flag
					});
				}
			}
		}

    }
};

module.exports.bodies = bodies;
<|endoftext|>


<commit_before>
/*!
 * socket.io-node
 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Test dependencies.
 */

var io = require('socket.io')
  , parser = io.parser
  , http = require('http')
  , https = require('https');

/**
 * Exports.
 */

var should = module.exports = require('should');

should.HTTPClient = HTTPClient;

/**
 * Client utility.
 *
 * @api publiC
 */

function HTTPClient (port) {
  this.port = port;
  this.agent = new http.Agent({
      host: 'localhost'
    , port: port
  });
};

/**
 * Issue a request
 *
 * @api private
 */

HTTPClient.prototype.request = function (path, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = opts || {};
  opts.agent = this.agent;
  opts.host = 'localhost';
  opts.port = this.port;
  opts.path = path.replace(/{protocol}/g, io.protocol);
  opts.headers = {
      'Host': 'localhost'
    , 'Connection': 'keep-alive'
  };

  var req = http.request(opts, function (res) {
    if (false === opts.buffer)
      return fn && fn(res);

    var buf = '';

    res.on('data', function (chunk) {
      buf += chunk;
    });

    res.on('end', function () {
      fn && fn(res, opts.parse ? opts.parse(buf) : buf);
    });
  });

  req.on('error', function (err) { });

  if (undefined !== opts.data)
    req.write(opts.data);

  req.end();

  return req;
};

/**
 * Terminates the client and associated connections.
 *
 * @api public
 */

HTTPClient.prototype.end = function () {
  this.agent.sockets.forEach(function (socket) {
    socket.end();
  });
};

/**
 * Issue a GET request
 *
 * @api public
 */

HTTPClient.prototype.get = function (path, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = opts || {};
  opts.method = 'GET';

  // override the parser for transport requests
  if (/\/(xhr-polling|htmlfile|jsonp-polling)\//.test(path)) {
    // parser that might be necessary for transport-specific framing
    var transportParse = opts.parse;
    opts.parse = function (data) {
      if (data === '') return data;

      data = transportParse ? transportParse(data) : data;
      return parser.decodePayload(data);
    };
  } else {
    opts.parse = undefined;
  }

  return this.request(path, opts, fn);
};

/**
 * Issue a POST request
 *
 * @api private
 */

HTTPClient.prototype.post = function (path, data, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = opts || {};
  opts.method = 'POST';
  opts.data = data;

  return this.request(path, opts, fn);
};

/**
 * Performs a handshake (GET) request
 *
 * @api private
 */

HTTPClient.prototype.handshake = function (opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  return this.get('/socket.io/{protocol}', opts, function (res, data) {
    fn && fn.apply(null, data.split(':'));
  });
};

/**
 * Generates a new client for the given port.
 *
 * @api private
 */

client = function (port) {
  return new HTTPClient(port);
};

/**
 * Create a socket.io server.
 */

create = function (cl) {
  console.error('');
  return io.listen(cl.port);
};
<commit_msg>Added header override support in test client.<commit_after>
/*!
 * socket.io-node
 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Test dependencies.
 */

var io = require('socket.io')
  , parser = io.parser
  , http = require('http')
  , https = require('https');

/**
 * Exports.
 */

var should = module.exports = require('should');

should.HTTPClient = HTTPClient;

/**
 * Client utility.
 *
 * @api publiC
 */

function HTTPClient (port) {
  this.port = port;
  this.agent = new http.Agent({
      host: 'localhost'
    , port: port
  });
};

/**
 * Issue a request
 *
 * @api private
 */

HTTPClient.prototype.request = function (path, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = opts || {};
  opts.agent = this.agent;
  opts.host = 'localhost';
  opts.port = this.port;
  opts.path = path.replace(/{protocol}/g, io.protocol);

  opts.headers = opts.headers || {};
  opts.headers.Host = 'localhost';
  opts.headers.Connection = 'keep-alive';

  var req = http.request(opts, function (res) {
    if (false === opts.buffer)
      return fn && fn(res);

    var buf = '';

    res.on('data', function (chunk) {
      buf += chunk;
    });

    res.on('end', function () {
      fn && fn(res, opts.parse ? opts.parse(buf) : buf);
    });
  });

  req.on('error', function (err) { });

  if (undefined !== opts.data)
    req.write(opts.data);

  req.end();

  return req;
};

/**
 * Terminates the client and associated connections.
 *
 * @api public
 */

HTTPClient.prototype.end = function () {
  this.agent.sockets.forEach(function (socket) {
    socket.end();
  });
};

/**
 * Issue a GET request
 *
 * @api public
 */

HTTPClient.prototype.get = function (path, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = opts || {};
  opts.method = 'GET';

  // override the parser for transport requests
  if (/\/(xhr-polling|htmlfile|jsonp-polling)\//.test(path)) {
    // parser that might be necessary for transport-specific framing
    var transportParse = opts.parse;
    opts.parse = function (data) {
      if (data === '') return data;

      data = transportParse ? transportParse(data) : data;
      return parser.decodePayload(data);
    };
  } else {
    opts.parse = undefined;
  }

  return this.request(path, opts, fn);
};

/**
 * Issue a POST request
 *
 * @api private
 */

HTTPClient.prototype.post = function (path, data, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = opts || {};
  opts.method = 'POST';
  opts.data = data;

  return this.request(path, opts, fn);
};

/**
 * Performs a handshake (GET) request
 *
 * @api private
 */

HTTPClient.prototype.handshake = function (opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  return this.get('/socket.io/{protocol}', opts, function (res, data) {
    fn && fn.apply(null, data.split(':'));
  });
};

/**
 * Generates a new client for the given port.
 *
 * @api private
 */

client = function (port) {
  return new HTTPClient(port);
};

/**
 * Create a socket.io server.
 */

create = function (cl) {
  console.error('');
  return io.listen(cl.port);
};
<|endoftext|>