<commit_before>module.exports = Swarm

var debug = require('debug')('bittorrent-tracker:swarm')
var LRU = require('lru')
var randomIterate = require('random-iterate')

// Regard this as the default implementation of an interface that you
// need to support when overriding Server.createSwarm() and Server.getSwarm()
function Swarm (infoHash, server) {
  this.peers = new LRU({
    max: server.peersCacheLength || 1000,
    maxAge: server.peersCacheTtl || 20 * 60 * 1000 // 20 minutes
  })
  this.complete = 0
  this.incomplete = 0
}

Swarm.prototype.announce = function (params, cb) {
  var self = this
  var id = params.type === 'ws' ? params.peer_id : params.addr
  // Mark the source peer as recently used in cache
  var peer = self.peers.get(id)

  if (params.event === 'started') {
    self._onAnnounceStarted(params, peer, id)
  } else if (params.event === 'stopped') {
    self._onAnnounceStopped(params, peer, id)
  } else if (params.event === 'completed') {
    self._onAnnounceCompleted(params, peer, id)
  } else if (params.event === 'update') {
    self._onAnnounceUpdate(params, peer, id)
  } else {
    cb(new Error('invalid event'))
    return
  }
  cb(null, {
    complete: self.complete,
    incomplete: self.incomplete,
    peers: self._getPeers(params.numwant, params.peer_id, !!params.socket)
  })
}

Swarm.prototype.scrape = function (params, cb) {
  cb(null, {
    complete: this.complete,
    incomplete: this.incomplete
  })
}

Swarm.prototype._onAnnounceStarted = function (params, peer, id) {
  if (peer) {
    debug('unexpected `started` event from peer that is already in swarm')
    return this._onAnnounceUpdate(params, peer, id) // treat as an update
  }

  if (params.left === 0) this.complete += 1
  else this.incomplete += 1
  peer = this.peers.set(id, {
    type: params.type,
    complete: params.left === 0,
    peerId: params.peer_id, // as hex
    ip: params.ip,
    port: params.port,
    socket: params.socket // only websocket
  })
}

Swarm.prototype._onAnnounceStopped = function (params, peer, id) {
  if (!peer) {
    debug('unexpected `stopped` event from peer that is not in swarm')
    return // do nothing
  }

  if (peer.complete) this.complete -= 1
  else this.incomplete -= 1
  this.peers.remove(id)
}

Swarm.prototype._onAnnounceCompleted = function (params, peer, id) {
  if (!peer) {
    debug('unexpected `completed` event from peer that is not in swarm')
    return this._onAnnounceStarted(params, peer, id) // treat as a start
  }
  if (peer.complete) {
    debug('unexpected `completed` event from peer that is already completed')
    return this._onAnnounceUpdate(params, peer, id) // treat as an update
  }

  this.complete += 1
  this.incomplete -= 1
  peer.complete = true
  this.peers.set(id, peer)
}

Swarm.prototype._onAnnounceUpdate = function (params, peer, id) {
  if (!peer) {
    debug('unexpected `update` event from peer that is not in swarm')
    return this._onAnnounceStarted(params, peer, id) // treat as a start
  }

  if (!peer.complete && params.left === 0) {
    this.complete += 1
    this.incomplete -= 1
    peer.complete = true
  }
  this.peers.set(id, peer)
}

Swarm.prototype._getPeers = function (numwant, ownPeerId, isWebRTC) {
  var peers = []
  var ite = randomIterate(this.peers.keys)
  var peerId
  while ((peerId = ite()) && peers.length < numwant) {
    // Don't mark the peer as most recently used on announce
    var peer = this.peers.peek(peerId)
    if (!peer) continue
    if (isWebRTC && peer.peerId === ownPeerId) continue // don't send peer to itself
    if ((isWebRTC && peer.type !== 'ws') || (!isWebRTC && peer.type === 'ws')) continue // send proper peer type
    peers.push(peer)
  }
  return peers
}
<commit_msg>Close websockets when peers are evicted from LRU cache<commit_after>module.exports = Swarm

var debug = require('debug')('bittorrent-tracker:swarm')
var LRU = require('lru')
var randomIterate = require('random-iterate')

// Regard this as the default implementation of an interface that you
// need to support when overriding Server.createSwarm() and Server.getSwarm()
function Swarm (infoHash, server) {
  this.complete = 0
  this.incomplete = 0
  this.peers = new LRU({
    max: server.peersCacheLength || 1000,
    maxAge: server.peersCacheTtl || 20 * 60 * 1000 // 20 minutes
  })

  // When a peer is evicted from the LRU cache, if it's a websocket peer,
  // close the websocket.
  this.peers.on('evict', function (data) {
    var peer = data.value
    if (peer.socket) {
      try {
        peer.socket.close()
        peer.socket = null
      } catch (err) {}
    }
  })
}

Swarm.prototype.announce = function (params, cb) {
  var self = this
  var id = params.type === 'ws' ? params.peer_id : params.addr
  // Mark the source peer as recently used in cache
  var peer = self.peers.get(id)

  if (params.event === 'started') {
    self._onAnnounceStarted(params, peer, id)
  } else if (params.event === 'stopped') {
    self._onAnnounceStopped(params, peer, id)
  } else if (params.event === 'completed') {
    self._onAnnounceCompleted(params, peer, id)
  } else if (params.event === 'update') {
    self._onAnnounceUpdate(params, peer, id)
  } else {
    cb(new Error('invalid event'))
    return
  }
  cb(null, {
    complete: self.complete,
    incomplete: self.incomplete,
    peers: self._getPeers(params.numwant, params.peer_id, !!params.socket)
  })
}

Swarm.prototype.scrape = function (params, cb) {
  cb(null, {
    complete: this.complete,
    incomplete: this.incomplete
  })
}

Swarm.prototype._onAnnounceStarted = function (params, peer, id) {
  if (peer) {
    debug('unexpected `started` event from peer that is already in swarm')
    return this._onAnnounceUpdate(params, peer, id) // treat as an update
  }

  if (params.left === 0) this.complete += 1
  else this.incomplete += 1
  peer = this.peers.set(id, {
    type: params.type,
    complete: params.left === 0,
    peerId: params.peer_id, // as hex
    ip: params.ip,
    port: params.port,
    socket: params.socket // only websocket
  })
}

Swarm.prototype._onAnnounceStopped = function (params, peer, id) {
  if (!peer) {
    debug('unexpected `stopped` event from peer that is not in swarm')
    return // do nothing
  }

  if (peer.complete) this.complete -= 1
  else this.incomplete -= 1
  this.peers.remove(id)
}

Swarm.prototype._onAnnounceCompleted = function (params, peer, id) {
  if (!peer) {
    debug('unexpected `completed` event from peer that is not in swarm')
    return this._onAnnounceStarted(params, peer, id) // treat as a start
  }
  if (peer.complete) {
    debug('unexpected `completed` event from peer that is already completed')
    return this._onAnnounceUpdate(params, peer, id) // treat as an update
  }

  this.complete += 1
  this.incomplete -= 1
  peer.complete = true
  this.peers.set(id, peer)
}

Swarm.prototype._onAnnounceUpdate = function (params, peer, id) {
  if (!peer) {
    debug('unexpected `update` event from peer that is not in swarm')
    return this._onAnnounceStarted(params, peer, id) // treat as a start
  }

  if (!peer.complete && params.left === 0) {
    this.complete += 1
    this.incomplete -= 1
    peer.complete = true
  }
  this.peers.set(id, peer)
}

Swarm.prototype._getPeers = function (numwant, ownPeerId, isWebRTC) {
  var peers = []
  var ite = randomIterate(this.peers.keys)
  var peerId
  while ((peerId = ite()) && peers.length < numwant) {
    // Don't mark the peer as most recently used on announce
    var peer = this.peers.peek(peerId)
    if (!peer) continue
    if (isWebRTC && peer.peerId === ownPeerId) continue // don't send peer to itself
    if ((isWebRTC && peer.type !== 'ws') || (!isWebRTC && peer.type === 'ws')) continue // send proper peer type
    peers.push(peer)
  }
  return peers
}
<|endoftext|>


<commit_before>var STATUS_CODES = require('http').STATUS_CODES || {};
var TYPES = ['whistle', 'Fiddler'];

function getMethod(method) {
  if (typeof method !== 'string') {
    return 'GET';
  }
  return method.toUpperCase() || 'GET';
}

function getHeadersRaw(headers) {
  if (!headers) {
    return [];
  }
  delete headers.Host;
  return Object.keys(headers).map(function(name) {
    return name + ': ' + headers[name];
  });
}

function getReqRaw(req) {
  var headers = getHeadersRaw(req.headers);
  var url = String(req.url || '').replace(/^ws/, 'http');
  headers.unshift([getMethod(req.method), url, 'HTTP/1.1'].join(' '));
  return headers.join('\r\n') + '\r\n\r\n' + (req.body || '');
}

exports.getReqRaw = getReqRaw;

function getResRaw(res) {
  var headers = getHeadersRaw(res.headers);
  headers.unshift(['HTTP/1.1', res.statusCode, res.statusMessage || STATUS_CODES[res.statusCode] || 'unknown'].join(' '));
  return headers.join('\r\n') + '\r\n\r\n' + (res.body || '');
}

exports.getResRaw = getResRaw;

var BODY_SEP = ['\r\n\r\n', '\r\n\r', '\r\r\n', '\r\n\n', '\r\r', '\r\n', '\n\n', '\n\r'];

function getBodyOffset(raw) {
  for (var i = 0; i < BODY_SEP.length; i++) {
    var sep = BODY_SEP[i];
    var index = raw.indexOf(sep);
    if (index !== -1) {
      return [index, index + sep.length];
    }
  }
}

function foratRawData(raw) {
  if (typeof raw !== 'string' || !(raw = raw.trim())) {
    return;
  }
  var offset = getBodyOffset(raw);
  var body = '';
  if (offset) {
    body = raw.substring(offset[1]).replace(BODY_SEP, '');
    raw = raw.substring(0, offset[0]);
  }
  raw = raw.trim().split(/\r\n?|\n/g);
  var firstLine = raw.shift().split(/\s+/g);
  var headers = {};
  raw.forEach(function(line) {
    var index = line.indexOf(':');
    if (index === -1) {
      return;
    }
    var name = line.substring(0, index).trim();
    if (!name) {
      return;
    }
    var value = headers[name];
    var val = line.substring(index + 1).trim();
    if (value != null) {
      if (Array.isArray(value)) {
        value.push(val);
      } else {
        value = [value, val];
      }
    } else {
      value = val;
    }
    headers[name.toLowerCase()] = value;
  });

  return {
    firstLine: firstLine,
    headers: headers,
    body: body
  };
}

function getReq(raw) {
  raw = foratRawData(raw);
  return raw ? {
    method: raw.firstLine[0],
    httpVersion: '1.1',
    url: raw.firstLine[1],
    headers: raw.headers,
    body: raw.body
  } : null;
}

exports.getReq = getReq;

function getRes(raw) {
  raw = foratRawData(raw);
  return raw ? {
    statusCode: raw.firstLine[1],
    httpVersion: '1.1',
    statusMessage: raw.firstLine[2],
    headers: raw.headers,
    body: raw.body
  } : {};
}

exports.getRes = getRes;

function parseJSON(str) {
  try {
    return JSON.parse(str);
  } catch(e) {}
}

exports.parseJSON = parseJSON;

function padding(num) {
  return num < 10 ? '0' + num : num;
}

function getFilename(type) {
  if (!~TYPES.indexOf(type)) {
    type = 'whistle';
  }
  var date = new Date();
  var filename = [date.getFullYear(), padding(date.getMonth() + 1), padding(date.getDate())].join('-')
    + '_' + [padding(date.getHours()), padding(date.getMinutes()), padding(date.getSeconds())].join('-');
  return filename + (type === 'whistle' ? '.txt' : '.saz');
}

exports.getFilename = getFilename;

var ONE_MINUTE = 60 * 1000;
function toISOString(time) {
  var date = new Date();
  var offet = -date.getTimezoneOffset();
  time += offet * ONE_MINUTE;
  offet /= 60;
  time = time >= 0 ? new Date(time) : new Date();
  return time.toISOString().slice(0, -1) + '0000'
    + (offet >= 0 ? '+' : '-') + padding(Math.abs(offet)) + ':00';
}

exports.toISOString = toISOString;
<commit_msg>feat: Import saz file<commit_after>var zlib = require('zlib');

var STATUS_CODES = require('http').STATUS_CODES || {};
var TYPES = ['whistle', 'Fiddler'];

function getMethod(method) {
  if (typeof method !== 'string') {
    return 'GET';
  }
  return method.toUpperCase() || 'GET';
}

function getHeadersRaw(headers) {
  if (!headers) {
    return [];
  }
  delete headers.Host;
  return Object.keys(headers).map(function(name) {
    return name + ': ' + headers[name];
  });
}

function getReqRaw(req) {
  var headers = getHeadersRaw(req.headers);
  var url = String(req.url || '').replace(/^ws/, 'http');
  headers.unshift([getMethod(req.method), url, 'HTTP/1.1'].join(' '));
  return headers.join('\r\n') + '\r\n\r\n' + (req.body || '');
}

exports.getReqRaw = getReqRaw;

function getResRaw(res) {
  var headers = getHeadersRaw(res.headers);
  headers.unshift(['HTTP/1.1', res.statusCode, res.statusMessage || STATUS_CODES[res.statusCode] || 'unknown'].join(' '));
  return headers.join('\r\n') + '\r\n\r\n' + (res.body || '');
}

exports.getResRaw = getResRaw;

var BODY_SEP = ['\r\n\r\n', '\r\n\r', '\r\r\n', '\r\n\n', '\r\r', '\r\n', '\n\n', '\n\r'];

function getBodyOffset(raw) {
  for (var i = 0; i < BODY_SEP.length; i++) {
    var sep = BODY_SEP[i];
    var index = raw.indexOf(sep);
    if (index !== -1) {
      return [index, index + sep.length];
    }
  }
}

function foratRawData(raw) {
  if (typeof raw !== 'string' || !(raw = raw.trim())) {
    return;
  }
  var offset = getBodyOffset(raw);
  var body = '';
  if (offset) {
    body = raw.substring(offset[1]);
    raw = raw.substring(0, offset[0]);
  }
  raw = raw.trim().split(/\r\n?|\n/g);
  var firstLine = raw.shift().split(/\s+/g);
  var headers = {};
  raw.forEach(function(line) {
    var index = line.indexOf(':');
    if (index === -1) {
      return;
    }
    var name = line.substring(0, index).trim();
    if (!name) {
      return;
    }
    var value = headers[name];
    var val = line.substring(index + 1).trim();
    if (value != null) {
      if (Array.isArray(value)) {
        value.push(val);
      } else {
        value = [value, val];
      }
    } else {
      value = val;
    }
    headers[name.toLowerCase()] = value;
  });

  //TODO: gzip, chunked
//  var contentEncoding = headers && headers['content-encoding'];
//  if (body && typeof contentEncoding === 'string') {
//    switch (contentEncoding.toLowerCase()) {
//    case 'gzip':
//      try {
//        body = zlib.gunzipSync(new Buffer(body));
//      } catch(e) {
//        body = e.stack;
//      }
//      break;
//    case 'deflate':
//      try {
//        body = zlib.deflateSync(new Buffer(body));
//      } catch(e) {}
//      break;
//    }
//  }
//
  return {
    firstLine: firstLine,
    headers: headers,
    body: body
  };
}

function getReq(raw) {
  raw = foratRawData(raw);
  return raw ? {
    method: raw.firstLine[0],
    httpVersion: '1.1',
    url: raw.firstLine[1],
    headers: raw.headers,
    body: raw.body
  } : null;
}

exports.getReq = getReq;

function getRes(raw) {
  raw = foratRawData(raw);
  return raw ? {
    statusCode: raw.firstLine[1],
    httpVersion: '1.1',
    statusMessage: raw.firstLine[2],
    headers: raw.headers,
    body: raw.body
  } : {};
}

exports.getRes = getRes;

function parseJSON(str) {
  try {
    return JSON.parse(str);
  } catch(e) {}
}

exports.parseJSON = parseJSON;

function padding(num) {
  return num < 10 ? '0' + num : num;
}

function getFilename(type) {
  if (!~TYPES.indexOf(type)) {
    type = 'whistle';
  }
  var date = new Date();
  var filename = [date.getFullYear(), padding(date.getMonth() + 1), padding(date.getDate())].join('-')
    + '_' + [padding(date.getHours()), padding(date.getMinutes()), padding(date.getSeconds())].join('-');
  return filename + (type === 'whistle' ? '.txt' : '.saz');
}

exports.getFilename = getFilename;

var ONE_MINUTE = 60 * 1000;
function toISOString(time) {
  var date = new Date();
  var offet = -date.getTimezoneOffset();
  time += offet * ONE_MINUTE;
  offet /= 60;
  time = time >= 0 ? new Date(time) : new Date();
  return time.toISOString().slice(0, -1) + '0000'
    + (offet >= 0 ? '+' : '-') + padding(Math.abs(offet)) + ':00';
}

exports.toISOString = toISOString;
<|endoftext|>


<commit_before>var url = require('url');
var http = require('http');
var sys = require('sys');

exports.fetchJSON = function(remoteURL, callback) {
  remoteURL = url.parse(remoteURL);

  var pagegen = http.createClient(remoteURL.port, remoteURL.hostname);
  // TODO: remoteURL.search
  var request = pagegen.request('GET', remoteURL.pathname, {"host": remoteURL.hostname});
  request.addListener('response', function(response) {

    response.client.responseBodyParts = '';

    response.addListener('data', function(chunk) {
      response.client.responseBodyParts += chunk;
    });

    response.addListener('end', function() {
      callback(response.client.responseBodyParts);
    });
  });
  request.close();
};<commit_msg>Default proxy to port 80<commit_after>var url = require('url');
var http = require('http');
var sys = require('sys');

exports.fetchJSON = function(remoteURL, callback) {
  remoteURL = url.parse(remoteURL);

  var pagegen = http.createClient(remoteURL.port || 80, remoteURL.hostname);
  // TODO: remoteURL.search
  var request = pagegen.request('GET', remoteURL.pathname, {"host": remoteURL.hostname});
  request.addListener('response', function(response) {

    response.client.responseBodyParts = '';

    response.addListener('data', function(chunk) {
      response.client.responseBodyParts += chunk;
    });

    response.addListener('end', function() {
      callback(response.client.responseBodyParts);
    });
  });
  request.close();
};
<|endoftext|>


<commit_before>'use strict'

const miss = require('mississippi')
const config = require('../config')

module.exports = miss.through(function (chunck, encoding, callback) {
  var item = JSON.parse(chunck)

  if (item.dsfError) {
    return callback(null, JSON.stringify(item))
  }

  var contact = item.dsfContact
  var svarUt = {}
  var options = {
    config: {
      url: 'https://' + config.SVARUT_USERNAME + ':' + config.SVARUT_PASSWORD + '@' + config.SVARUT_URL,
      action: 'http://www.ks.no/svarut/services/ForsendelsesServiceV4/sendForsendelse'
    },
    tittel: item.svarUtTitle,
    dokumenter: [],
    forsendelse: {
      avgivendeSystem: config.SVARUT_AVGIVENDE_SYSTEM,
      konteringskode: config.SVARUT_KONTERINGSKODE,
      krevNiva4Innlogging: false,
      kryptert: false,
      kunDigitalLevering: false
    },
    mottaker: [
      {
        type: 'privatPerson',
        navn: item.recipientName,
        adresse1: contact.ADR,
        adresse2: '',
        adresse3: '',
        postnr: contact.POSTN,
        poststed: contact.POSTS,
        fodselsnr: item.recipientPersonalIdNumber
      }
    ],
    printkonfigurasjon: {
      brevtype: 'BPOST',
      fargePrint: true,
      tosidig: true
    }
  }

  item.documents.forEach(function (document) {
    options.dokumenter.push({
      filsti: document.document,
      mimetype: 'application/pdf'
    })
  })

  if (item.sendCopyToGuardian && item.dsfGuardian) {
    console.log(item._id + ': setup-svarut. Adds guardian')
    var guardian = item.dsfGuardian
    var guardianAddress = {
      type: 'privatPerson',
      navn: guardian.NAVN,
      adresse1: guardian.ADR,
      adresse2: '',
      adresse3: '',
      postnr: guardian.POSTN,
      poststed: guardian.POSTS,
      fodselsnr: guardian.FODT.toString() + guardian.PERS.toString()
    }
    options.mottaker.push(guardianAddress)
  }

  svarUt.options = options

  item.svarUt = svarUt

  console.log(item._id + ': setup-svarut')

  return callback(null, JSON.stringify(item))
})
<commit_msg>Changes BPOST to APOST<commit_after>'use strict'

const miss = require('mississippi')
const config = require('../config')

module.exports = miss.through(function (chunck, encoding, callback) {
  var item = JSON.parse(chunck)

  if (item.dsfError) {
    return callback(null, JSON.stringify(item))
  }

  var contact = item.dsfContact
  var svarUt = {}
  var options = {
    config: {
      url: 'https://' + config.SVARUT_USERNAME + ':' + config.SVARUT_PASSWORD + '@' + config.SVARUT_URL,
      action: 'http://www.ks.no/svarut/services/ForsendelsesServiceV4/sendForsendelse'
    },
    tittel: item.svarUtTitle,
    dokumenter: [],
    forsendelse: {
      avgivendeSystem: config.SVARUT_AVGIVENDE_SYSTEM,
      konteringskode: config.SVARUT_KONTERINGSKODE,
      krevNiva4Innlogging: false,
      kryptert: false,
      kunDigitalLevering: false
    },
    mottaker: [
      {
        type: 'privatPerson',
        navn: item.recipientName,
        adresse1: contact.ADR,
        adresse2: '',
        adresse3: '',
        postnr: contact.POSTN,
        poststed: contact.POSTS,
        fodselsnr: item.recipientPersonalIdNumber
      }
    ],
    printkonfigurasjon: {
      brevtype: 'APOST',
      fargePrint: true,
      tosidig: true
    }
  }

  item.documents.forEach(function (document) {
    options.dokumenter.push({
      filsti: document.document,
      mimetype: 'application/pdf'
    })
  })

  if (item.sendCopyToGuardian && item.dsfGuardian) {
    console.log(item._id + ': setup-svarut. Adds guardian')
    var guardian = item.dsfGuardian
    var guardianAddress = {
      type: 'privatPerson',
      navn: guardian.NAVN,
      adresse1: guardian.ADR,
      adresse2: '',
      adresse3: '',
      postnr: guardian.POSTN,
      poststed: guardian.POSTS,
      fodselsnr: guardian.FODT.toString() + guardian.PERS.toString()
    }
    options.mottaker.push(guardianAddress)
  }

  svarUt.options = options

  item.svarUt = svarUt

  console.log(item._id + ': setup-svarut')

  return callback(null, JSON.stringify(item))
})
<|endoftext|>


<commit_before>window.AdminController = (() => {
  return {
    init: () => {

      function updateDeleteUsersList() {
        $('#deleteUsersInput').empty();
        $.get('http://localhost:3000/users', function(data) {
          var option = '<option selected hidden value=\'default\'>Select User...</option>';
          for (user in data) {
            option += '<option value="' + data[user]['_id'] + '">' + data[user]['name']['first'] + ' ' + data[user]['name']['last'] + '</option>';
          }
          $('#deleteUsersInput').append(option);
        });
      }
      updateDeleteUsersList();

      $('#button-delete-user').on('click', function() {
        attemptDeleteUser();
      });

      function attemptDeleteUser() {
        $('#deleteUsersInput').removeClass('is-invalid');
        $('#deleteUsersSmall').removeClass('text-danger');
        $('#deleteUsersSmall').removeClass('text-success');
        $('#deleteUsersSmall').text('');
        var val = $('#deleteUsersInput').val();
        if (val == 'default') {
          $('#deleteUsersInput').addClass('is-invalid');
          $('#deleteUsersSmall').addClass('text-danger');
          $('#deleteUsersSmall').text('Must select a user to delete.');
        } else {
          deleteUser();
        }
      }

      function deleteUser() {
        var val = $('#deleteUsersInput').val()
        var name = $('#deleteUsersInput option:selected').text();
        if (val != 'default') {
          $.ajax({
            url: 'http://localhost:3000/user/' + val,
            type: 'DELETE',
            success: function(result) {
              $('#deleteUsersSmall').text('Deleted user ' + name + '.');
              $('#deleteUsersSmall').addClass('text-success');
              updateDeleteUsersList();
            }
          });
        }
      }

    }

  };

})();
<commit_msg>Final commit before master push<commit_after>window.AdminController = (() => {
  return {
    init: () => {

      function updateDeleteUsersList() {
        $('#deleteUsersInput').empty();
        $.get('http://localhost:3000/users', function(data) {
          var option = '<option selected hidden value=\'default\'>Select User...</option>';
          for (user in data) {
            option += '<option value="' + data[user]['_id'] + '">' + data[user]['name']['first'] + ' ' + data[user]['name']['last'] + '</option>';
          }
          $('#deleteUsersInput').append(option);
        });
      }
      updateDeleteUsersList();

      $('#button-delete-user').on('click', function() {
        attemptDeleteUser();
      });

      function attemptDeleteUser() {
        $('#deleteUsersInput').removeClass('is-invalid');
        $('#deleteUsersSmall').removeClass('text-danger');
        $('#deleteUsersSmall').removeClass('text-success');
        $('#deleteUsersSmall').text('');
        var val = $('#deleteUsersInput').val();
        if (val == 'default') {
          $('#deleteUsersInput').addClass('is-invalid');
          $('#deleteUsersSmall').addClass('text-danger');
          $('#deleteUsersSmall').text('Must select a user to delete.');
        } else {
          deleteUser();
        }
      }

      function deleteUser() {
        var val = $('#deleteUsersInput').val()
        var name = $('#deleteUsersInput option:selected').text();
        if (val != 'default') {
          $.ajax({
            url: 'http://localhost:3000/user/' + val,
            type: 'DELETE',
            success: function(result) {
              $('#deleteUsersSmall').text('Successfully deleted user ' + name + '.');
              $('#deleteUsersSmall').addClass('text-success');
              updateDeleteUsersList();
            }
          });
        }
      }

    }

  };

})();
<|endoftext|>


<commit_before>window.InputController = (() => {
  return {
    init: () => {
      const token = window.sessionStorage.getItem('userInfo-token');
      $.getScript('js/caseFieldFunctionality.js', () => {
        function submitVictimForm(existingVictimInput) {
          if (!existingVictimInput) {
            return $.ajax({
              url: 'http://localhost:3000/victims',
              type: 'POST',
              headers: {
                'x-access-token': token
              },
              data: {
                victName: {
                  first: caseUI.fields['victFirstName']['input'].val(),
                  middle: caseUI.fields['victMiddleName']['input'].val(),
                  last: caseUI.fields['victLastName']['input'].val()
                },
                victSex: caseUI.fields['victSex']['input'].val(),
                victDesc: caseUI.fields['victDesc']['input'].val(),
                victAge: caseUI.fields['victAge']['input'].val()
              },
              statusCode: {
                400: (err) => {
                  didAPICallFail = true;
                  $('#victimFormSmall').text(err['responseJSON']['errors']['message']);
                  $('#victimFormLabel').addClass('text-danger');
                  console.log('Victim submission failed:');
                  console.log(err);
                },
                201: (victim) => {
                  victimJSON = victim;
                  $('#victimFormSmall').text('');
                  $('#victimFormLabel').removeClass('text-danger');
                }
              }
            });
          }
          return existingVictimInput; // TODO: change?
        }

        function submitSuspectForm(existingSuspectInput) {
          if (!existingSuspectInput) {
            return $.ajax({
              url: 'http://localhost:3000/suspects',
              type: 'POST',
              headers: {
                'x-access-token': token
              },
              data: {
                suspName: {
                  first: caseUI.fields['suspFirstName']['input'].val(),
                  middle: caseUI.fields['suspMiddleName']['input'].val(),
                  last: caseUI.fields['suspLastName']['input'].val()
                },
                suspSex: caseUI.fields['suspSex']['input'].val(),
                supervisedReleaseStatus: caseUI.fields['suspSupervisedReleaseStatus']['input'].val(),
                suspDesc: caseUI.fields['suspDesc']['input'].val(),
                suspAge: caseUI.fields['suspAge']['input'].val(),
                juvenileTriedAsAdult: caseUI.fields['juvenileTriedAsAdult']['input'].val()
              },
              statusCode: {
                400: (err) => {
                  // TODO: commented out?
                  // didAPICallFail = true;
                  $('#suspectFormSmall').text(err['responseJSON']['errors']['message']);
                  $('#suspectFormLabel').addClass('text-danger');
                  console.log('Suspect submission failed:');
                  console.log(err);
                },
                201: (suspect) => {
                  // TODO: commented out?
                  // suspectJSON = suspect;
                  $('#suspectFormSmall').text('');
                  $('#suspectFormLabel').removeClass('text-danger');
                }
              }
            });
          }
          return existingSuspectInput; // TODO: change?
        }

        function submitCaseForm(victimId, suspectIds) {
          return $.ajax({
            url: 'http://localhost:3000/cases',
            type: 'POST',
            headers: {
              'x-access-token': token
            },
            data: {
              drNumber: caseUI.fields['drNum']['input'].val(),
              masterDrNumber: caseUI.fields['masterDrNum']['input'].val(),
              division: caseUI.fields['division']['input'].val(),
              bureau: caseUI.fields['bureau']['input'].val(),
              notes: caseUI.fields['notes']['input'].val(),
              dateOccured: (new Date(caseUI.fields['dateOccured']['input'].val())).toISOString(),
              dateReported: (new Date(caseUI.fields['dateReported']['input'].val())).toISOString(),
              reportingDistrict: caseUI.fields['reportingDistrict']['input'].val(),
              caseStatus: caseUI.fields['caseStatus']['input'].val(),
              caseStatusDate: (new Date(caseUI.fields['caseStatusDate']['input'].val())).toISOString(),
              solvabilityFactor: caseUI.fields['solvabilityFactor']['input'].val(),
              weaponUsed: (function getWeaponsUsed() {
                const weaponsList = [];
                const weaponInputs = Object.keys(caseUI.fields['weapon']['inputs']);
                weaponInputs.forEach((weaponCheckbox) => {
                  if (caseUI.fields['weapon']['inputs'][weaponCheckbox].prop('checked')) {
                    weaponsList.push(caseUI.fields['weapon']['inputs'][weaponCheckbox].attr('name'));
                  }
                });
                return weaponsList;
              }()),
              motive: (function getMotives() {
                const motivesList = [];
                const motiveInputs = Object.keys(caseUI.fields['motive']['inputs']);
                motiveInputs.forEach((motiveCheckbox) => {
                  if (caseUI.fields['motive']['inputs'][motiveCheckbox].prop('checked')) {
                    motivesList.push(caseUI.fields['motive']['inputs'][motiveCheckbox].attr('name'));
                  }
                });
                return motivesList;
              }()),
              lastModifiedDate: (new Date()).toISOString(),
              lastModifiedBy: '5a07dcad41156921c81b70e4', // TODO: Get userId of user logged in
              victim: victimId,
              address: {
                streetNumber: caseUI.fields['streetNumber']['input'].val(),
                streetName: caseUI.fields['streetName']['input'].val(),
                city: caseUI.fields['city']['input'].val(),
                zipCode: caseUI.fields['zipCode']['input'].val()
              },
              suspects: [suspectIds]
            },
            statusCode: {
              400: (err) => {
                console.log('Case submission failed:');
                console.log(caseJSON);
                didAPICallFail = true;
                $('#submitFormSmall').removeClass('text-success');
                $('#submitFormSmall').addClass('text-danger');
                $('#submitFormSmall').text('Oops! Could not submit form due to the following database errors. ' + err['responseJSON']['errors']['message']);
              },
              404: (err) => {
                console.log('Case submission failed:');
                console.log(err);
                didAPICallFail = true;
                $('#submitFormSmall').removeClass('text-success');
                $('#submitFormSmall').addClass('text-danger');
                $('#submitFormSmall').text('Oops! Could not submit form due to the following database error. ' + err['responseJSON']['text'] + ': ' + err['responseJSON']['value'] + '.');
              },
              201: (caseJSON) => {
                console.log('Case submission results:');
                console.log(caseJSON);
                $('#submitFormSmall').removeClass('text-danger');
                $('#submitFormSmall').addClass('text-success');
                $('#submitFormSmall').text('Case submission succeeded with DR# ' + caseUI.fields['drNum']['input'].val() + '.');
                clearAllInputs();
              }
            }
          });
        }

        function attemptMasterFormSubmission() {
          if (caseUI.checkFormValidityAndAnnotate()) {
            const existingVictimID = caseUI.fields['newOrExistingVictim']['input'].val() === 'old' ?
              caseUI.fields['victId']['input'].val() : null; // pass in an existing ID or null.
            const existingSuspectID = caseUI.fields['newOrExistingSuspect']['input'].val() === 'old' ?
              caseUI.fields['suspId']['input'].val() : null; // pass in an existing ID or null.

            Promise.all([submitVictimForm(existingVictimID), submitSuspectForm(existingSuspectID)])
              .then((values) => {
                const victim = values[0]['_id'] || values[0];
                const suspect = values[1]['_id'] || values[1];
                console.log('victim: ', victim);
                submitCaseForm(victim, suspect);
              }).catch((err) => {
                console.log(err);
              });
          }
        }

        function clearAllInputs() {
          for (field in caseUI.fields) {
            if (caseUI.fields[field]['input'] === undefined) {
              for (input in caseUI.fields[field]['inputs']) {
                caseUI.fields[field]['inputs'][input].prop('checked', false);
              }
            } else {
              caseUI.fields[field]['input'].val('');
            }
          }
          caseUI.newOrExistingSuspectInput.val('default');
          updateSuspectInputsVisibility();
          caseUI.newOrExistingVictimInput.val('default');
          updateVictimInputsVisibility();
        }

        function updateVictimInputsVisibility() {
          const val = caseUI.newOrExistingVictimInput.val();
          if (val === 'default') {
            caseUI.newVictimForm.hide();
            caseUI.existingVictimForm.hide();
          } else if (val === 'new') {
            caseUI.newVictimForm.show();
            caseUI.existingVictimForm.hide();
          } else if (val === 'old') {
            caseUI.newVictimForm.hide();
            caseUI.existingVictimForm.show();
          }
        }
        updateVictimInputsVisibility();

        function updateSuspectInputsVisibility() {
          const val = caseUI.newOrExistingSuspectInput.val();
          if (val === 'default') {
            caseUI.newSuspectForm.hide();
            caseUI.existingSuspectForm.hide();
          } else if (val === 'new') {
            caseUI.newSuspectForm.show();
            caseUI.existingSuspectForm.hide();
          } else if (val === 'old') { // g?
            caseUI.newSuspectForm.hide();
            caseUI.existingSuspectForm.show();
          }
        }
        updateSuspectInputsVisibility();

        caseUI.newOrExistingVictimInput.change(() => {
          updateVictimInputsVisibility();
        });

        caseUI.newOrExistingSuspectInput.change(() => {
          updateSuspectInputsVisibility();
        });
      });
    }
  };
})();
<commit_msg>Added submit functionality back<commit_after>window.InputController = (() => {
  return {
    init: () => {
      const token = window.sessionStorage.getItem('userInfo-token');
      $.getScript('js/caseFieldFunctionality.js', () => {
        function submitVictimForm(existingVictimInput) {
          if (!existingVictimInput) {
            return $.ajax({
              url: 'http://localhost:3000/victims',
              type: 'POST',
              headers: {
                'x-access-token': token
              },
              data: {
                victName: {
                  first: caseUI.fields['victFirstName']['input'].val(),
                  middle: caseUI.fields['victMiddleName']['input'].val(),
                  last: caseUI.fields['victLastName']['input'].val()
                },
                victSex: caseUI.fields['victSex']['input'].val(),
                victDesc: caseUI.fields['victDesc']['input'].val(),
                victAge: caseUI.fields['victAge']['input'].val()
              },
              statusCode: {
                400: (err) => {
                  didAPICallFail = true;
                  $('#victimFormSmall').text(err['responseJSON']['errors']['message']);
                  $('#victimFormLabel').addClass('text-danger');
                  console.log('Victim submission failed:');
                  console.log(err);
                },
                201: (victim) => {
                  victimJSON = victim;
                  $('#victimFormSmall').text('');
                  $('#victimFormLabel').removeClass('text-danger');
                }
              }
            });
          }
          return existingVictimInput; // TODO: change?
        }

        function submitSuspectForm(existingSuspectInput) {
          if (!existingSuspectInput) {
            return $.ajax({
              url: 'http://localhost:3000/suspects',
              type: 'POST',
              headers: {
                'x-access-token': token
              },
              data: {
                suspName: {
                  first: caseUI.fields['suspFirstName']['input'].val(),
                  middle: caseUI.fields['suspMiddleName']['input'].val(),
                  last: caseUI.fields['suspLastName']['input'].val()
                },
                suspSex: caseUI.fields['suspSex']['input'].val(),
                supervisedReleaseStatus: caseUI.fields['suspSupervisedReleaseStatus']['input'].val(),
                suspDesc: caseUI.fields['suspDesc']['input'].val(),
                suspAge: caseUI.fields['suspAge']['input'].val(),
                juvenileTriedAsAdult: caseUI.fields['juvenileTriedAsAdult']['input'].val()
              },
              statusCode: {
                400: (err) => {
                  // TODO: commented out?
                  // didAPICallFail = true;
                  $('#suspectFormSmall').text(err['responseJSON']['errors']['message']);
                  $('#suspectFormLabel').addClass('text-danger');
                  console.log('Suspect submission failed:');
                  console.log(err);
                },
                201: (suspect) => {
                  // TODO: commented out?
                  // suspectJSON = suspect;
                  $('#suspectFormSmall').text('');
                  $('#suspectFormLabel').removeClass('text-danger');
                }
              }
            });
          }
          return existingSuspectInput; // TODO: change?
        }

        function submitCaseForm(victimId, suspectIds) {
          return $.ajax({
            url: 'http://localhost:3000/cases',
            type: 'POST',
            headers: {
              'x-access-token': token
            },
            data: {
              drNumber: caseUI.fields['drNum']['input'].val(),
              masterDrNumber: caseUI.fields['masterDrNum']['input'].val(),
              division: caseUI.fields['division']['input'].val(),
              bureau: caseUI.fields['bureau']['input'].val(),
              notes: caseUI.fields['notes']['input'].val(),
              dateOccured: (new Date(caseUI.fields['dateOccured']['input'].val())).toISOString(),
              dateReported: (new Date(caseUI.fields['dateReported']['input'].val())).toISOString(),
              reportingDistrict: caseUI.fields['reportingDistrict']['input'].val(),
              caseStatus: caseUI.fields['caseStatus']['input'].val(),
              caseStatusDate: (new Date(caseUI.fields['caseStatusDate']['input'].val())).toISOString(),
              solvabilityFactor: caseUI.fields['solvabilityFactor']['input'].val(),
              weaponUsed: (function getWeaponsUsed() {
                const weaponsList = [];
                const weaponInputs = Object.keys(caseUI.fields['weapon']['inputs']);
                weaponInputs.forEach((weaponCheckbox) => {
                  if (caseUI.fields['weapon']['inputs'][weaponCheckbox].prop('checked')) {
                    weaponsList.push(caseUI.fields['weapon']['inputs'][weaponCheckbox].attr('name'));
                  }
                });
                return weaponsList;
              }()),
              motive: (function getMotives() {
                const motivesList = [];
                const motiveInputs = Object.keys(caseUI.fields['motive']['inputs']);
                motiveInputs.forEach((motiveCheckbox) => {
                  if (caseUI.fields['motive']['inputs'][motiveCheckbox].prop('checked')) {
                    motivesList.push(caseUI.fields['motive']['inputs'][motiveCheckbox].attr('name'));
                  }
                });
                return motivesList;
              }()),
              lastModifiedDate: (new Date()).toISOString(),
              lastModifiedBy: '5a07dcad41156921c81b70e4', // TODO: Get userId of user logged in
              victim: victimId,
              address: {
                streetNumber: caseUI.fields['streetNumber']['input'].val(),
                streetName: caseUI.fields['streetName']['input'].val(),
                city: caseUI.fields['city']['input'].val(),
                zipCode: caseUI.fields['zipCode']['input'].val()
              },
              suspects: [suspectIds]
            },
            statusCode: {
              400: (err) => {
                console.log('Case submission failed:');
                console.log(caseJSON);
                didAPICallFail = true;
                $('#submitFormSmall').removeClass('text-success');
                $('#submitFormSmall').addClass('text-danger');
                $('#submitFormSmall').text('Oops! Could not submit form due to the following database errors. ' + err['responseJSON']['errors']['message']);
              },
              404: (err) => {
                console.log('Case submission failed:');
                console.log(err);
                didAPICallFail = true;
                $('#submitFormSmall').removeClass('text-success');
                $('#submitFormSmall').addClass('text-danger');
                $('#submitFormSmall').text('Oops! Could not submit form due to the following database error. ' + err['responseJSON']['text'] + ': ' + err['responseJSON']['value'] + '.');
              },
              201: (caseJSON) => {
                console.log('Case submission results:');
                console.log(caseJSON);
                $('#submitFormSmall').removeClass('text-danger');
                $('#submitFormSmall').addClass('text-success');
                $('#submitFormSmall').text('Case submission succeeded with DR# ' + caseUI.fields['drNum']['input'].val() + '.');
                clearAllInputs();
              }
            }
          });
        }

        function attemptMasterFormSubmission() {
          if (caseUI.checkFormValidityAndAnnotate()) {
            const existingVictimID = caseUI.fields['newOrExistingVictim']['input'].val() === 'old' ?
              caseUI.fields['victId']['input'].val() : null; // pass in an existing ID or null.
            const existingSuspectID = caseUI.fields['newOrExistingSuspect']['input'].val() === 'old' ?
              caseUI.fields['suspId']['input'].val() : null; // pass in an existing ID or null.

            Promise.all([submitVictimForm(existingVictimID), submitSuspectForm(existingSuspectID)])
              .then((values) => {
                const victim = values[0]['_id'] || values[0];
                const suspect = values[1]['_id'] || values[1];
                console.log('victim: ', victim);
                submitCaseForm(victim, suspect);
              }).catch((err) => {
                console.log(err);
              });
          }
        }

        $('#button-submit-forms').on('click', () => {
          attemptMasterFormSubmission();
        });

        function clearAllInputs() {
          for (field in caseUI.fields) {
            if (caseUI.fields[field]['input'] === undefined) {
              for (input in caseUI.fields[field]['inputs']) {
                caseUI.fields[field]['inputs'][input].prop('checked', false);
              }
            } else {
              caseUI.fields[field]['input'].val('');
            }
          }
          caseUI.newOrExistingSuspectInput.val('default');
          updateSuspectInputsVisibility();
          caseUI.newOrExistingVictimInput.val('default');
          updateVictimInputsVisibility();
        }

        function updateVictimInputsVisibility() {
          const val = caseUI.newOrExistingVictimInput.val();
          if (val === 'default') {
            caseUI.newVictimForm.hide();
            caseUI.existingVictimForm.hide();
          } else if (val === 'new') {
            caseUI.newVictimForm.show();
            caseUI.existingVictimForm.hide();
          } else if (val === 'old') {
            caseUI.newVictimForm.hide();
            caseUI.existingVictimForm.show();
          }
        }
        updateVictimInputsVisibility();

        function updateSuspectInputsVisibility() {
          const val = caseUI.newOrExistingSuspectInput.val();
          if (val === 'default') {
            caseUI.newSuspectForm.hide();
            caseUI.existingSuspectForm.hide();
          } else if (val === 'new') {
            caseUI.newSuspectForm.show();
            caseUI.existingSuspectForm.hide();
          } else if (val === 'old') { // g?
            caseUI.newSuspectForm.hide();
            caseUI.existingSuspectForm.show();
          }
        }
        updateSuspectInputsVisibility();

        caseUI.newOrExistingVictimInput.change(() => {
          updateVictimInputsVisibility();
        });

        caseUI.newOrExistingSuspectInput.change(() => {
          updateSuspectInputsVisibility();
        });
      });
    }
  };
})();
<|endoftext|>


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

var EventEmitter = process.EventEmitter;

var items = new EventEmitter();

items.post = function(item)
{
    items.emit('item', item);
}

items.on('item', function(item)
{
    if (typeof item == 'function')
        item();
});

function ActorWrapper(obj)
{
    var agent = this;

    for (n in obj)
    {
        if (typeof obj[n] != 'function')
            continue;

        this[n] = makePost(obj, obj[n]);
    }

    this._isActor = true;
}

function makeFunction(target, fn, args, cb)
{
    return function() {
        try {
            var result = fn.apply(target, args);
        }
        catch (err)
        {
            if (cb && cb.length == 2)
                cb(err);
            return;
        }

        if (cb)
            if (cb.length == 2)
                cb(null, result);
            else
                cb(result);
        else
            return result;
    }
}

function makePost(target, fn)
{
    return function() {
        var args = asArray(arguments);
        var cb;

        // last argument is a callback
        if (args && args.length > 0 && typeof args[args.length-1] == 'function')
        {
            cb = args[args.length-1];
            args = args.slice(0, args.length-1);
        }
        // pre-last argument is a callback to be passed to final method invocation
        else if (args && args.length > 1 && typeof args[args.length-2] == 'function' && args[args.length-1] === true)
            args = args.slice(0, args.length-1);

        items.post(makeFunction(target, fn, args, cb));
    }
}

// Hack: function receives an array-like object but apply expects an array

function asArray(obj)
{
    if (Array.isArray(obj))
        return obj;

    var result = [];

    for (var n in obj)
        result[parseInt(n)] = obj[n];

    return result;
}

exports.post = function(item)
{
    items.post(item);
}

exports.asActor = function(obj)
{
    return new ActorWrapper(obj);
}

exports.buildActor = function(obj, mthname)
{
    if (!mthname)
        mthname = 'process';

    obj._isBuildActor = true;
    obj.post = makePost(obj, obj[mthname]);
}

exports.isActor = function(actor)
{
	if (actor == null)
		return false;

    return (actor._isActor || actor._isBuildActor) ? true : false;
}

function ActorSystem(name, node) {
    var counter = 0;
    this.name = name;
    this.path = "sactors://" + name;
    this.children = { };
    this.node = node;

    this.nextNumber = function () {
        counter++;
        return counter;
    }
}

ActorSystem.prototype.actorOf = function(clss, name) {
    var actor = newActor(clss, this, null, name);
    this.children[name] = actor;
    return actor;
}

ActorSystem.prototype.actorFor = function (name) {
    if (name.indexOf(':') > 0) {
        var path = parsePath(name);

        if (path.server) {
            var servername = path.server + ':' + path.port;
            if (servername !== this.node.name)
                return this.node.getNode(servername).getSystem(path.system).actorFor(path.path);
        }

        name = path.path;
    }

    if (name && name[0] === '/')
        name = name.substring(1);

    return this.children[name];
}

function RemoteActorSystem(name, node) {
    this.name = name;
    this.path = "sactors://" + name;
    this.node = node;
}

RemoteActorSystem.prototype.actorFor = function (name) {
    var path = this.path;

    if (name[0] === '/')
        path += name;
    else
        path += '/' + name;

    return new RemoteActorRef(this.node, path);
}

function newActor(clss, system, parent, name) {
    var actor;

    if (typeof clss === 'function')
        actor = new clss();
    else
        actor = clss;

    if (!name)
        name = system.nextNumber();

    var ref = new ActorRef(actor, parent ? parent.path : system.path, name);
    var context = new ActorContext(actor, ref, system, parent);

    return ref;
}

function ActorContext(actor, reference, system, parent) {
    actor.context = this;
    actor.self = reference;
    this.system = system;
    this.self = reference;
    this.parent = parent;
    this.children = { };
}

ActorContext.prototype.actorOf = function(clss, name) {
    var child = newActor(clss, this.system, this.self, name);
    this.children[name] = child;
    return child;
}

ActorContext.prototype.actorFor = function(name) {
    return this.children[name];
}

function ActorRef(actor, parentpath, name) {
    this.actor = actor;

    this.path = parentpath + "/" + name;
}

ActorRef.prototype.tell = function (msg) {
    var self = this;
    process.nextTick(function () { self.actor.receive(msg); });
}

function RemoteActorRef(node, path, remotenodename) {
    this.path = path;
    this.node = node;
    this.remote = remotenodename;
}

RemoteActorRef.prototype.tell = function (msg) {
    if (this.remote)
        this.node.tellNode(this.remote, this.path, msg);
    else
        this.node.process({ path: this.path, message: msg });
}

exports.create = function (name) {
    return new ActorSystem(name);
}

function parsePath(path) {
    var result = { };
    var position = path.indexOf(':');

    result.protocol = path.substring(0, position);

    var rest = path.substring(position + 3);

    var positionat = rest.indexOf('@');
    position = rest.indexOf('/');

    if (positionat >= 0 && positionat < position) {
        result.system = rest.substring(0, positionat);
        result.server = rest.substring(positionat + 1, position);

        var poscolon = result.server.indexOf(':');

        if (poscolon > 0) {
            result.port = parseInt(result.server.substring(poscolon + 1));
            result.server = result.server.substring(0, poscolon);
        }
    }
    else
        result.system = rest.substring(0, position);

    result.path = rest.substring(position);

    return result;
}

function makeName(port, host) {
    if (!port)
        return host;

    if (!host)
        host = 'localhost';

    return host + ':' + port;
}

function parseName(name) {
    var position = name.indexOf(':');
    var host;
    var port;

    if (position > 0) {
        host = name.substring(0, position);
        port = parseInt(name.substring(position+1));
    }
    else
        host = name;

    return { host: host, port: port };
}

exports.parsePath = parsePath;

function Node(port, host) {
    if (typeof port === 'string') {
        this.name = port;
        var porthost = parseName(port);
        port = porthost.port;
        host = porthost.host;
    }
    else
        this.name = makeName(port, host);

    var server;
    var systems = { };
    var nodes = [];
    var clients = [];

    this.create = function (name) {
        var fullname = name + '@' + this.name;
        var system = new ActorSystem(fullname, this);
        systems[name] = system;
        return system;
    }

    this.getSystem = function (name) {
        return systems[name];
    }

    this.start = function () {
        var self = this;
        server = simplemessages.createServer(function (stream) {
            stream.on('data', function (msg) {
                self.process(msg, stream);
            });
        });

        server.listen(port, host);
    }

    this.createRemoteNode = function (port, host) {
        return new RemoteNode(port, host, this);
    }

    this.process = function (msg, sender) {
        // Tell message to actor
        if (msg.path) {
            var path = msg.path;
            var message = msg.message;
            var ph = parsePath(path);
            systems[ph.system].actorFor(ph.path).tell(message);
            return;
        }
        // New node
        if (msg.node) {
            var position = msg.node.indexOf(':');
            var host = msg.node.substring(0, position);
            var port = parseInt(msg.node.substring(position+1));
            new RemoteNode(port, host, this, sender);
            return;
        }
    }

    this.registerNode = function (name, node) {
        nodes[name] = node;
    }

    this.getNode = function (name) {
        return nodes[name];
    }

    this.unregisterNode = function (name) {
        delete nodes[name];
    }

    this.tellNode = function (name, path, message) {
        nodes[name].process({ path: path, message: message });
    }

    this.stop = function () {
        if (server) {
            server.close();
            server = null;
        }

        nodes.forEach(function (node) { node.stop(); });
    }
}

function RemoteNode(port, host, local, client) {
    var systems = { };

    this.name = makeName(port, host);

    if (local)
        local.registerNode(this.name, this);

    this.create = function (name) {
        var fullname = name + '@' + this.name;
        var system = new RemoteActorSystem(fullname, this);
        systems[name] = system;
        return system;
    }

    this.getSystem = function (name) {
        if (systems[name])
            return systems[name];

        return this.create(name);
    }

    this.start = function (fn) {
        var self = this;
        client = simplemessages.createClient(port, host, function() {
            if (local) {
                client.on('end', function () { local.unregisterNode(this.name); });
                client.on('error', function () { local.unregisterNode(this.name); });
                client.on('close', function () { local.unregisterNode(this.name); });
                client.on('data', function (msg) { local.process(msg); });
                client.write({ node: local.name });
            }

            if (fn)
                fn(client);
        });
    }

    this.process = function (msg) {
        client.write(msg);
    }

    this.stop = function () {
        if (client) {
            client.end();
            client = null;
        }
    }
}

exports.createNode = function (port,host) {
    return new Node(port, host);
}

<commit_msg>Refactor new RemoteActor to support name as host:name<commit_after>
var simplemessages = require('simplemessages');

var EventEmitter = process.EventEmitter;

var items = new EventEmitter();

items.post = function(item)
{
    items.emit('item', item);
}

items.on('item', function(item)
{
    if (typeof item == 'function')
        item();
});

function ActorWrapper(obj)
{
    var agent = this;

    for (n in obj)
    {
        if (typeof obj[n] != 'function')
            continue;

        this[n] = makePost(obj, obj[n]);
    }

    this._isActor = true;
}

function makeFunction(target, fn, args, cb)
{
    return function() {
        try {
            var result = fn.apply(target, args);
        }
        catch (err)
        {
            if (cb && cb.length == 2)
                cb(err);
            return;
        }

        if (cb)
            if (cb.length == 2)
                cb(null, result);
            else
                cb(result);
        else
            return result;
    }
}

function makePost(target, fn)
{
    return function() {
        var args = asArray(arguments);
        var cb;

        // last argument is a callback
        if (args && args.length > 0 && typeof args[args.length-1] == 'function')
        {
            cb = args[args.length-1];
            args = args.slice(0, args.length-1);
        }
        // pre-last argument is a callback to be passed to final method invocation
        else if (args && args.length > 1 && typeof args[args.length-2] == 'function' && args[args.length-1] === true)
            args = args.slice(0, args.length-1);

        items.post(makeFunction(target, fn, args, cb));
    }
}

// Hack: function receives an array-like object but apply expects an array

function asArray(obj)
{
    if (Array.isArray(obj))
        return obj;

    var result = [];

    for (var n in obj)
        result[parseInt(n)] = obj[n];

    return result;
}

exports.post = function(item)
{
    items.post(item);
}

exports.asActor = function(obj)
{
    return new ActorWrapper(obj);
}

exports.buildActor = function(obj, mthname)
{
    if (!mthname)
        mthname = 'process';

    obj._isBuildActor = true;
    obj.post = makePost(obj, obj[mthname]);
}

exports.isActor = function(actor)
{
	if (actor == null)
		return false;

    return (actor._isActor || actor._isBuildActor) ? true : false;
}

function ActorSystem(name, node) {
    var counter = 0;
    this.name = name;
    this.path = "sactors://" + name;
    this.children = { };
    this.node = node;

    this.nextNumber = function () {
        counter++;
        return counter;
    }
}

ActorSystem.prototype.actorOf = function(clss, name) {
    var actor = newActor(clss, this, null, name);
    this.children[name] = actor;
    return actor;
}

ActorSystem.prototype.actorFor = function (name) {
    if (name.indexOf(':') > 0) {
        var path = parsePath(name);

        if (path.server) {
            var servername = path.server + ':' + path.port;
            if (servername !== this.node.name)
                return this.node.getNode(servername).getSystem(path.system).actorFor(path.path);
        }

        name = path.path;
    }

    if (name && name[0] === '/')
        name = name.substring(1);

    return this.children[name];
}

function RemoteActorSystem(name, node) {
    this.name = name;
    this.path = "sactors://" + name;
    this.node = node;
}

RemoteActorSystem.prototype.actorFor = function (name) {
    var path = this.path;

    if (name[0] === '/')
        path += name;
    else
        path += '/' + name;

    return new RemoteActorRef(this.node, path);
}

function newActor(clss, system, parent, name) {
    var actor;

    if (typeof clss === 'function')
        actor = new clss();
    else
        actor = clss;

    if (!name)
        name = system.nextNumber();

    var ref = new ActorRef(actor, parent ? parent.path : system.path, name);
    var context = new ActorContext(actor, ref, system, parent);

    return ref;
}

function ActorContext(actor, reference, system, parent) {
    actor.context = this;
    actor.self = reference;
    this.system = system;
    this.self = reference;
    this.parent = parent;
    this.children = { };
}

ActorContext.prototype.actorOf = function(clss, name) {
    var child = newActor(clss, this.system, this.self, name);
    this.children[name] = child;
    return child;
}

ActorContext.prototype.actorFor = function(name) {
    return this.children[name];
}

function ActorRef(actor, parentpath, name) {
    this.actor = actor;

    this.path = parentpath + "/" + name;
}

ActorRef.prototype.tell = function (msg) {
    var self = this;
    process.nextTick(function () { self.actor.receive(msg); });
}

function RemoteActorRef(node, path, remotenodename) {
    this.path = path;
    this.node = node;
    this.remote = remotenodename;
}

RemoteActorRef.prototype.tell = function (msg) {
    if (this.remote)
        this.node.tellNode(this.remote, this.path, msg);
    else
        this.node.process({ path: this.path, message: msg });
}

exports.create = function (name) {
    return new ActorSystem(name);
}

function parsePath(path) {
    var result = { };
    var position = path.indexOf(':');

    result.protocol = path.substring(0, position);

    var rest = path.substring(position + 3);

    var positionat = rest.indexOf('@');
    position = rest.indexOf('/');

    if (positionat >= 0 && positionat < position) {
        result.system = rest.substring(0, positionat);
        result.server = rest.substring(positionat + 1, position);

        var poscolon = result.server.indexOf(':');

        if (poscolon > 0) {
            result.port = parseInt(result.server.substring(poscolon + 1));
            result.server = result.server.substring(0, poscolon);
        }
    }
    else
        result.system = rest.substring(0, position);

    result.path = rest.substring(position);

    return result;
}

function makeName(port, host) {
    if (!port)
        return host;

    if (!host)
        host = 'localhost';

    return host + ':' + port;
}

function parseName(name) {
    var position = name.indexOf(':');
    var host;
    var port;

    if (position > 0) {
        host = name.substring(0, position);
        port = parseInt(name.substring(position+1));
    }
    else
        host = name;

    return { host: host, port: port };
}

exports.parsePath = parsePath;

function Node(port, host) {
    if (typeof port === 'string') {
        this.name = port;
        var porthost = parseName(port);
        port = porthost.port;
        host = porthost.host;
    }
    else
        this.name = makeName(port, host);

    var server;
    var systems = { };
    var nodes = [];
    var clients = [];

    this.create = function (name) {
        var fullname = name + '@' + this.name;
        var system = new ActorSystem(fullname, this);
        systems[name] = system;
        return system;
    }

    this.getSystem = function (name) {
        return systems[name];
    }

    this.start = function () {
        var self = this;
        server = simplemessages.createServer(function (stream) {
            stream.on('data', function (msg) {
                self.process(msg, stream);
            });
        });

        server.listen(port, host);
    }

    this.createRemoteNode = function (port, host) {
        if (typeof port === 'string')
            return new RemoteNode(port, this);
        else
            return new RemoteNode(port, host, this);
    }

    this.process = function (msg, sender) {
        // Tell message to actor
        if (msg.path) {
            var path = msg.path;
            var message = msg.message;
            var ph = parsePath(path);
            systems[ph.system].actorFor(ph.path).tell(message);
            return;
        }
        // New node
        if (msg.node) {
            new RemoteNode(msg.node, this, sender);
            return;
        }
    }

    this.registerNode = function (name, node) {
        nodes[name] = node;
    }

    this.getNode = function (name) {
        return nodes[name];
    }

    this.unregisterNode = function (name) {
        delete nodes[name];
    }

    this.tellNode = function (name, path, message) {
        nodes[name].process({ path: path, message: message });
    }

    this.stop = function () {
        if (server) {
            server.close();
            server = null;
        }

        nodes.forEach(function (node) { node.stop(); });
    }
}

function RemoteNode(port, host, local, client) {
    if (typeof port === 'string') {
        client = local;
        local = host;
        this.name = port;
        var porthost = parseName(port);
        port = porthost.port;
        host = porthost.host;
    }
    else
        this.name = makeName(port, host);

    var systems = { };

    if (local)
        local.registerNode(this.name, this);

    this.create = function (name) {
        var fullname = name + '@' + this.name;
        var system = new RemoteActorSystem(fullname, this);
        systems[name] = system;
        return system;
    }

    this.getSystem = function (name) {
        if (systems[name])
            return systems[name];

        return this.create(name);
    }

    this.start = function (fn) {
        var self = this;
        client = simplemessages.createClient(port, host, function() {
            if (local) {
                client.on('end', function () { local.unregisterNode(this.name); });
                client.on('error', function () { local.unregisterNode(this.name); });
                client.on('close', function () { local.unregisterNode(this.name); });
                client.on('data', function (msg) { local.process(msg); });
                client.write({ node: local.name });
            }

            if (fn)
                fn(client);
        });
    }

    this.process = function (msg) {
        client.write(msg);
    }

    this.stop = function () {
        if (client) {
            client.end();
            client = null;
        }
    }
}

exports.createNode = function (port,host) {
    return new Node(port, host);
}

<|endoftext|>


<commit_before>setupDesktopMenu()
setupMobileMenu()

function setupDesktopMenu () {
  var menu = $('.menu__nav')

  menu.find('a').click(function(event) {
    var menuItem = $(this)

    // When click on current section.
    // Scroll to top.
    if (menuItem.hasClass('item-selected')) {
      if (menuItem.attr('id') !== 'blog') {
        event.preventDefault()

        // Add effect on current button
        menuItem
          .addClass('animated rubberBand', function () {
            $(this).removeClass('animated rubberBand')
          })

        // Scroll page to top
        scrollTop({
          position: 0,
          speed: 300
        })
      }
    }
  })
}

function setupMobileMenu () {
  var body = $('body')
  var jfmenu = $('.jfmenu')
  var jfmenuContent = $('.jfmenu_content')
  var jfmenuOverlay = $('.jfmenu_overlay')
  var menuButton = $('.jfmenu__mobileButton')
  var header = $('#menu')

  $(window).resize(function(elem) {
    const deskstopScreen = 1100
    if ($(window).width() > deskstopScreen) {
      hideMenu()
    }
  })

  jfmenuOverlay.click(function() {
    if ($('.jfmenu').hasClass('jfmenu_displayed')) {
      hideMenu()
    }
  })

  // Actions: if click on a menu item which is the current section
  // hide the menu. If click on a item with a submenu, then open it.
  jfmenu.find('li').click(function(event) {
    var e = $(this)
    var submenu_list = e.find('ul')

    var hasClickedOnCurrentPage = e.hasClass('current_section')
    if (hasClickedOnCurrentPage) {
      event.preventDefault() // Don't open url again.
      hideMenu()
    }

    if (submenu_list.length > 0) {
      submenu_list.each(function() {
        var menu = $(this)
        menu.toggleClass('jfmenu_displayed') // Display the submenu
      })
    }
  })

  menuButton.click(function() {
    var e = $(this)

    if (jfmenu.hasClass('jfmenu_displayed')) {
      hideMenu()
    }
    else {
      displayMenu()
    }
  })

  function hideMenu () {
    const headerTopMargin = hasMenuAboveItem() ? getHeaderAboveItemHeigth() : 0
    if (jfmenu.hasClass('jfmenu_displayed')) {
      header.css({ top: headerTopMargin })
      body.removeClass('body_overflow')
      jfmenu.removeClass('jfmenu_displayed')
      jfmenuOverlay.removeClass('fadeIn')
      jfmenuContent.removeClass('fadeInDownBig')
      menuButton.removeClass('jfmenu__mobileButton__active')
    }
  }

  function displayMenu () {
    if (!jfmenu.hasClass('jfmenu_displayed')) {
      header.css({ top: 0 })
      body.addClass('body_overflow')
      jfmenu.addClass('jfmenu_displayed')
      jfmenuOverlay.addClass('animated fadeIn')
      jfmenuContent.addClass('animated fadeInDownBig')
      menuButton.addClass('jfmenu__mobileButton__active')
    }
  }

  function hasMenuAboveItem () {
    const aboveItem = $('.mostRecentPostAd')
    return aboveItem.length > 0
  }

  function getHeaderAboveItemHeigth () {
    const aboveItem = $('.mostRecentPostAd')
    return hasMenuAboveItem() ? aboveItem.outerHeight() : 0
  }
}

function scrollTop (opts) {
  const position = opts && opts.position ? opts.position : 0
  const speed = opts && opts.speed ? opts.speed : 300

  $('html, body').animate({
    scrollTop: opts && opts.position
  }, speed)
}<commit_msg>Fixed bug on jfmenu when above item is not visible<commit_after>setupDesktopMenu()
setupMobileMenu()

function setupDesktopMenu () {
  var menu = $('.menu__nav')

  menu.find('a').click(function(event) {
    var menuItem = $(this)

    // When click on current section.
    // Scroll to top.
    if (menuItem.hasClass('item-selected')) {
      if (menuItem.attr('id') !== 'blog') {
        event.preventDefault()

        // Add effect on current button
        menuItem
          .addClass('animated rubberBand', function () {
            $(this).removeClass('animated rubberBand')
          })

        // Scroll page to top
        scrollTop({
          position: 0,
          speed: 300
        })
      }
    }
  })
}

function setupMobileMenu () {
  var body = $('body')
  var jfmenu = $('.jfmenu')
  var jfmenuContent = $('.jfmenu_content')
  var jfmenuOverlay = $('.jfmenu_overlay')
  var menuButton = $('.jfmenu__mobileButton')
  var header = $('#menu')

  $(window).resize(function(elem) {
    const deskstopScreen = 1100
    if ($(window).width() > deskstopScreen) {
      hideMenu()
    }
  })

  jfmenuOverlay.click(function() {
    if ($('.jfmenu').hasClass('jfmenu_displayed')) {
      hideMenu()
    }
  })

  // Actions: if click on a menu item which is the current section
  // hide the menu. If click on a item with a submenu, then open it.
  jfmenu.find('li').click(function(event) {
    var e = $(this)
    var submenu_list = e.find('ul')

    var hasClickedOnCurrentPage = e.hasClass('current_section')
    if (hasClickedOnCurrentPage) {
      event.preventDefault() // Don't open url again.
      hideMenu()
    }

    if (submenu_list.length > 0) {
      submenu_list.each(function() {
        var menu = $(this)
        menu.toggleClass('jfmenu_displayed') // Display the submenu
      })
    }
  })

  menuButton.click(function() {
    var e = $(this)

    if (jfmenu.hasClass('jfmenu_displayed')) {
      hideMenu()
    }
    else {
      displayMenu()
    }
  })

  function hideMenu () {
    const headerTopMargin = hasMenuAboveItem() ? getHeaderAboveItemHeigth() : 0
    if (jfmenu.hasClass('jfmenu_displayed')) {
      header.css({ top: headerTopMargin })
      body.removeClass('body_overflow')
      jfmenu.removeClass('jfmenu_displayed')
      jfmenuOverlay.removeClass('fadeIn')
      jfmenuContent.removeClass('fadeInDownBig')
      menuButton.removeClass('jfmenu__mobileButton__active')
    }
  }

  function displayMenu () {
    if (!jfmenu.hasClass('jfmenu_displayed')) {
      header.css({ top: 0 })
      body.addClass('body_overflow')
      jfmenu.addClass('jfmenu_displayed')
      jfmenuOverlay.addClass('animated fadeIn')
      jfmenuContent.addClass('animated fadeInDownBig')
      menuButton.addClass('jfmenu__mobileButton__active')
    }
  }

  function hasMenuAboveItem () {
    // Check if above item exists and is displayed.
    const aboveItem = $('.mostRecentPostAd')
    return aboveItem.length > 0 && aboveItem.is(":visible")
  }

  function getHeaderAboveItemHeigth () {
    const aboveItem = $('.mostRecentPostAd')
    return hasMenuAboveItem() ? aboveItem.outerHeight() : 0
  }
}

function scrollTop (opts) {
  const position = opts && opts.position ? opts.position : 0
  const speed = opts && opts.speed ? opts.speed : 300

  $('html, body').animate({
    scrollTop: opts && opts.position
  }, speed)
}<|endoftext|>


<commit_before>'use strict';

function StoryCtrl(session, lib, $scope, $timeout) {

	$scope.isAndroid = function() {
        // return /Android/i.test(navigator.userAgent);
        return false;
    }(); // closure

	var isStory = function (story) {
		if (!story || story.isDeadline || story.isNextMeeting) {
			return false;
		}

		return true;
	};

	var isStoryStatus = function (story, status) {
		if (!isStory(story)) {
			return false;
		}

		if (story.status === status) {
			return true;
		}

		return false;
	}

	$scope.isStoryNew = function (story) {
		if (!isStory(story)) {
			return false;
		}

		if (!story.status || story.status === "") {
			return true;
		}

		return false;
	};

	$scope.isStorySad = function (story) {
		return isStoryStatus(story, "sad");
	};

	$scope.isStoryAssigned = function (story) {
		return isStoryStatus(story, "assigned");
	};

	$scope.isStoryActive = function (story) {
		return isStoryStatus(story, "active");
	};

	$scope.isStoryDone = function (story) {
		return isStoryStatus(story, "done");
	};

	$scope.isStoryMine = function (story) {
		if (story.owner && $scope.accountName) {
			var owner = story.owner.toLowerCase();
			var member = $scope.accountName;
			if (member) {
				member = member.toLowerCase();
				if (owner === member) {
					return true;
				}
			}
		}
		return false;
	};

	$scope.setStoryStatus = function (story, status) {
		if (story) {
			story.status = status;
			$scope.$emit('storyChanged', story);
		}
	};

	var statusOrder = ['sad','assigned','active','done'];
	$scope.bumpStatus = function (story) {
		if (story) {
			var index = statusOrder.indexOf(story.status);
			if (index > -1) {
				index++;
				if (index < statusOrder.length) {
					// TODO: This is defined in HomeCtrl
					$scope.setStoryStatus(story, statusOrder[index]);
				}
			}
			else {
				// Do this here so we can move from sad to
				// assigned in one go
				if ($scope.isStoryNew(story)) {
					// TODO: This is defined in HomeCtrl
					$scope.setStoryStatus(story, 'assigned');
				};
			}
		}
	};

	$scope.isMindset = function (m) {
		if ($scope.mindset) {
			return $scope.mindset === m;
		}
		return lib.mindset.is(m);
	};

	$scope.moveToTop = function (e, story) {
		$scope.$emit('storyMovedToTop', story);
		e.stopPropagation();
		e.preventDefault();
	};

	var isDragging = false;
	$scope.$on('spIsDragging', function (e, val) {
		isDragging = val;
	});

	$scope.select = function (story) {
		if (isDragging || story.isBeingDragged) {
			// Do nothing. We're dragging. See the note
			// in 'drag:end' as to why.
			return;
		}

		// Do not refocus stuff if we're already on this story.
		if (!story.isSelected) {
			$scope.$emit('beforeStorySelected');
			story.isSelected = true;
			// TODO: Should this 'isHighlighted' change be here?
			// This might be better suited for a list operation?
			// Or is this even what we want to happen?
			// story.isHighlighted = false;
			$scope.$emit('storySelected', story);
		}
	};

	$scope.highlight = function (story) {
		// FYI: Stories will be highlighted when they are
		// selected (opened). This is the desired behavior for now.
		if (!story.isSelected) {
			$scope.$emit('storyHighlight', story, 'single');

			story.highlightedFrom = 'first';
			$scope.mouse.isHighlighting = true;
			$scope.mouse.lastMouseDownStory = story;
		}
	};

	$scope.rememberHighlight = function (story) {
		if ($scope.mouse.lastMouseUpStory &&
			$scope.mouse.lastMouseUpStory.id === story.id &&
			$scope.mouse.lastMouseUpStory.id === $scope.mouse.lastMouseDownStory.id) {
			$scope.select(story);
		}
		$scope.mouse.lastMouseUpStory = story;
	};

	$scope.$on('mouseUp', function () {
		$scope.mouse.isHighlighting = false;
	});

	$scope.handleSingleClicks = function (story) {
		if ($scope.keyboard.isShiftDown) {
			$scope.select(story);
			return;
		}
	};

	$scope.mouseEnter = function (story) {
		if ($scope.mouse.isHighlighting) {
			if (!story.isHighlighted) {
				$scope.$emit('storyHighlight', story);
			}
		}
	};

	$scope.mouseLeave = function (story) {
		if ($scope.mouse.isHighlighting) {
			if (story.isHighlighted && story.highlightedFrom !== 'first') {
				$scope.$emit('storyUnhighlight', story, $scope.mouse.direction);
			}
		}
	};

	$scope.deselect = function (story, event) {
		if (story && story.isSelected) {
			story.isSelected = false;

			$scope.$emit('storyDeselected', story, event);

			if (event) {
				event.stopPropagation();
			}
		}
	};

	$scope.archive = function (story) {
		$scope.$emit('storyArchived', story);
	};

	$scope.remove = function (story) {
		$scope.$emit('storyRemoved', story);
	};

	$scope.notify = function (story, event) {
		$scope.$emit('storyNotify', story, event);
	};

	$scope.save = function (story, event) {
		$timeout(function () {
			$scope.deselect(story, event);
		});
		$scope.$emit('storySaved', story);
	};

	$scope.saveComment = function (story, event) {
		$scope.$emit('storyCommentSaved', story);
	};

	$scope.getTimestampFilter = function (comment) {
		var date = new Date(comment.timestamp);
		var now = new Date();
		if (now.getDate() === date.getDate()
		&& now.getMonth() === date.getMonth()
		&& now.getFullYear() === date.getFullYear()) {
			// Today
			return "'at' h:mm a";
		}
		else if (now.getFullYear() === date.getFullYear()) {
			// This year
			return "'on' MMM d";
		}
		else {
			return "'on' MMM d, y";
		}
	};

	$scope.isOwnerInCircle = function (owner) {
		if (!owner) {
			return false;
		}

		owner = owner.trim();
		// TODO: If this becomes a performance issue,
		// which could be possible with many stories
		// and many owners, maybe make a table of owners.
		var owners = $scope.owners || [];
		for (var index in owners) {
			if (owners[index] === owner) {
				return true;
			}
		}

		return false;
	};

	$scope.isNotificationEnabled = function () {
		if (session.settings && session.settings['smtp-enabled'].value) {
			return true;
		}
		return false;
	};
}
StoryCtrl.$inject = ['session', 'lib', '$scope', '$timeout'];<commit_msg>List: Make group drag work with highlights<commit_after>'use strict';

function StoryCtrl(session, lib, $scope, $timeout) {

	$scope.isAndroid = function() {
        // return /Android/i.test(navigator.userAgent);
        return false;
    }(); // closure

	var isStory = function (story) {
		if (!story || story.isDeadline || story.isNextMeeting) {
			return false;
		}

		return true;
	};

	var isStoryStatus = function (story, status) {
		if (!isStory(story)) {
			return false;
		}

		if (story.status === status) {
			return true;
		}

		return false;
	}

	$scope.isStoryNew = function (story) {
		if (!isStory(story)) {
			return false;
		}

		if (!story.status || story.status === "") {
			return true;
		}

		return false;
	};

	$scope.isStorySad = function (story) {
		return isStoryStatus(story, "sad");
	};

	$scope.isStoryAssigned = function (story) {
		return isStoryStatus(story, "assigned");
	};

	$scope.isStoryActive = function (story) {
		return isStoryStatus(story, "active");
	};

	$scope.isStoryDone = function (story) {
		return isStoryStatus(story, "done");
	};

	$scope.isStoryMine = function (story) {
		if (story.owner && $scope.accountName) {
			var owner = story.owner.toLowerCase();
			var member = $scope.accountName;
			if (member) {
				member = member.toLowerCase();
				if (owner === member) {
					return true;
				}
			}
		}
		return false;
	};

	$scope.setStoryStatus = function (story, status) {
		if (story) {
			story.status = status;
			$scope.$emit('storyChanged', story);
		}
	};

	var statusOrder = ['sad','assigned','active','done'];
	$scope.bumpStatus = function (story) {
		if (story) {
			var index = statusOrder.indexOf(story.status);
			if (index > -1) {
				index++;
				if (index < statusOrder.length) {
					// TODO: This is defined in HomeCtrl
					$scope.setStoryStatus(story, statusOrder[index]);
				}
			}
			else {
				// Do this here so we can move from sad to
				// assigned in one go
				if ($scope.isStoryNew(story)) {
					// TODO: This is defined in HomeCtrl
					$scope.setStoryStatus(story, 'assigned');
				};
			}
		}
	};

	$scope.isMindset = function (m) {
		if ($scope.mindset) {
			return $scope.mindset === m;
		}
		return lib.mindset.is(m);
	};

	$scope.moveToTop = function (e, story) {
		$scope.$emit('storyMovedToTop', story);
		e.stopPropagation();
		e.preventDefault();
	};

	var isDragging = false;
	$scope.$on('spIsDragging', function (e, val) {
		isDragging = val;
	});

	$scope.select = function (story) {
		if (isDragging || story.isBeingDragged) {
			// Do nothing. We're dragging. See the note
			// in 'drag:end' as to why.
			return;
		}

		// Do not refocus stuff if we're already on this story.
		if (!story.isSelected) {
			$scope.$emit('beforeStorySelected');
			story.isSelected = true;
			// TODO: Should this 'isHighlighted' change be here?
			// This might be better suited for a list operation?
			// Or is this even what we want to happen?
			// story.isHighlighted = false;
			$scope.$emit('storySelected', story);
		}
	};

	var highlight = function (story) {
		// FYI: Stories will be highlighted when they are
		// selected (opened). This is the desired behavior for now.
		if (!story.isSelected) {
			$scope.$emit('storyHighlight', story, 'single');

			story.highlightedFrom = 'first';
			$scope.mouse.isHighlighting = true;
			$scope.mouse.lastMouseDownStory = story;
		}
	}

	$scope.highlight = function (story) {
		// Prevent drag operations from resetting our
		// selection
		if (story.isHighlighted) {
			$scope.mouse.isHighlighting = true;
			return;
		}
		highlight(story);
	};

	$scope.rememberHighlight = function (story) {
		if ($scope.mouse.lastMouseUpStory &&
			$scope.mouse.lastMouseUpStory.id === story.id &&
			$scope.mouse.lastMouseUpStory.id === $scope.mouse.lastMouseDownStory.id) {
			$scope.select(story);
		}
		$scope.mouse.lastMouseUpStory = story;
	};

	$scope.$on('mouseUp', function () {
		$scope.mouse.isHighlighting = false;
	});

	$scope.handleSingleClicks = function (story) {
		if ($scope.keyboard.isShiftDown) {
			$scope.select(story);
			return;
		}

		highlight(story);
		$scope.mouse.isHighlighting = false;
	};

	$scope.mouseEnter = function (story) {
		if ($scope.mouse.isHighlighting) {
			if (!story.isHighlighted) {
				$scope.$emit('storyHighlight', story);
			}
		}
	};

	$scope.mouseLeave = function (story) {
		if ($scope.mouse.isHighlighting) {
			if (story.isHighlighted && story.highlightedFrom !== 'first') {
				$scope.$emit('storyUnhighlight', story, $scope.mouse.direction);
			}
		}
	};

	$scope.deselect = function (story, event) {
		if (story && story.isSelected) {
			story.isSelected = false;

			$scope.$emit('storyDeselected', story, event);

			if (event) {
				event.stopPropagation();
			}
		}
	};

	$scope.archive = function (story) {
		$scope.$emit('storyArchived', story);
	};

	$scope.remove = function (story) {
		$scope.$emit('storyRemoved', story);
	};

	$scope.notify = function (story, event) {
		$scope.$emit('storyNotify', story, event);
	};

	$scope.save = function (story, event) {
		$timeout(function () {
			$scope.deselect(story, event);
		});
		$scope.$emit('storySaved', story);
	};

	$scope.saveComment = function (story, event) {
		$scope.$emit('storyCommentSaved', story);
	};

	$scope.getTimestampFilter = function (comment) {
		var date = new Date(comment.timestamp);
		var now = new Date();
		if (now.getDate() === date.getDate()
		&& now.getMonth() === date.getMonth()
		&& now.getFullYear() === date.getFullYear()) {
			// Today
			return "'at' h:mm a";
		}
		else if (now.getFullYear() === date.getFullYear()) {
			// This year
			return "'on' MMM d";
		}
		else {
			return "'on' MMM d, y";
		}
	};

	$scope.isOwnerInCircle = function (owner) {
		if (!owner) {
			return false;
		}

		owner = owner.trim();
		// TODO: If this becomes a performance issue,
		// which could be possible with many stories
		// and many owners, maybe make a table of owners.
		var owners = $scope.owners || [];
		for (var index in owners) {
			if (owners[index] === owner) {
				return true;
			}
		}

		return false;
	};

	$scope.isNotificationEnabled = function () {
		if (session.settings && session.settings['smtp-enabled'].value) {
			return true;
		}
		return false;
	};
}
StoryCtrl.$inject = ['session', 'lib', '$scope', '$timeout'];<|endoftext|>


<commit_before>import React from "react"

import CoderEstimateHidden    from "./coder_estimate_hidden"
import CoderEstimateCompleted from "./coder_estimate_completed"
import CoderEstimatePending   from "./coder_estimate_pending"

let Ticket = React.createClass({
  propTypes: {
    channel:   React.PropTypes.object,
    ticketId:  React.PropTypes.string
  },

  getInitialState() {
    return {
      pointOptions:  [],
      estimates:     {}
    }
  },

  componentDidMount() {
    this.props.channel.on("update", (payload) => {
      this.setState({
        estimates:     payload.estimates,
        pointOptions:  payload.point_options
      })
    })

    this.props.channel.push('get_ticket', { ticket_id: this.props.ticketId })
  },

  render() {
    let estimateComplete = this.isEstimateComplete(this.state.estimates)

    let estimateRows = Object.keys(this.state.estimates).sort().map((key) => {
      return this.estimateRow(estimateComplete, key, this.state.estimates[key])
    })

    let heading = (estimateComplete)
                  ?

Estimation complete

                  :

Estimation pending

                        Estimates are hidden until all team members have estimated

    return(

        {heading}

          Click the cards to choose/change your estimate

          {estimateRows}

    )
  },

  estimatesRemaining(estimates) {
    let values = Object.keys(estimates).map((key) => { return estimates[key] })
    return values.filter((i) => { return i === 0 }).length
  },

  isEstimateComplete(estimates) {
    return this.estimatesRemaining(estimates) === 0
  },

  estimateRow(estimateComplete, coder, points) {
    var rtn

    if (points === 0) {
      rtn = <CoderEstimatePending key={coder}
                                  coder={coder}
                                  pointOptions={this.state.pointOptions}
                                  onEstimate={this.updateEstimate} />
    } else {
      rtn = (estimateComplete)
            ? <CoderEstimateCompleted key={coder}
                                      coder={coder}
                                      points={points}
                                      onClick={this.updateEstimate} />
            : <CoderEstimateHidden key={coder}
                                   coder={coder}
                                   onClick={this.updateEstimate} />
    }

    return rtn
  },

  updateEstimate(coder, points) {
    this.props.channel.push('update', {
      ticket_id:  this.props.ticketId,
      coder:      coder,
      points:     points
    })
  },

  updateStateFromServerData(payload) {
    this.setState({
      estimates:     payload.estimates,
      pointOptions:  payload.point_options
    })
  }

})

module.exports = Ticket
<commit_msg>show estimates remaining in the browser tab<commit_after>import React from "react"

import CoderEstimateHidden    from "./coder_estimate_hidden"
import CoderEstimateCompleted from "./coder_estimate_completed"
import CoderEstimatePending   from "./coder_estimate_pending"

let Ticket = React.createClass({
  propTypes: {
    channel:   React.PropTypes.object,
    ticketId:  React.PropTypes.string
  },

  getInitialState() {
    return {
      pointOptions:  [],
      estimates:     {}
    }
  },

  componentDidMount() {
    this.props.channel.on("update", (payload) => {
      this.setState({
        estimates:     payload.estimates,
        pointOptions:  payload.point_options
      })
    })

    this.props.channel.push('get_ticket', { ticket_id: this.props.ticketId })
  },

  render() {
    let estimateComplete = this.isEstimateComplete(this.state.estimates)

    this.setBrowserTitle(this.estimatesRemaining(this.state.estimates))

    let estimateRows = Object.keys(this.state.estimates).sort().map((key) => {
      return this.estimateRow(estimateComplete, key, this.state.estimates[key])
    })

    let heading = (estimateComplete)
                  ?

Estimation complete

                  :

Estimation pending

                        Estimates are hidden until all team members have estimated

    return(

        {heading}

          Click the cards to choose/change your estimate

          {estimateRows}

    )
  },

  estimatesRemaining(estimates) {
    let values = Object.keys(estimates).map((key) => { return estimates[key] })
    return values.filter((i) => { return i === 0 }).length
  },

  isEstimateComplete(estimates) {
    return this.estimatesRemaining(estimates) === 0
  },

  estimateRow(estimateComplete, coder, points) {
    var rtn

    if (points === 0) {
      rtn = <CoderEstimatePending key={coder}
                                  coder={coder}
                                  pointOptions={this.state.pointOptions}
                                  onEstimate={this.updateEstimate} />
    } else {
      rtn = (estimateComplete)
            ? <CoderEstimateCompleted key={coder}
                                      coder={coder}
                                      points={points}
                                      onClick={this.updateEstimate} />
            : <CoderEstimateHidden key={coder}
                                   coder={coder}
                                   onClick={this.updateEstimate} />
    }

    return rtn
  },

  updateEstimate(coder, points) {
    this.props.channel.push('update', {
      ticket_id:  this.props.ticketId,
      coder:      coder,
      points:     points
    })
  },

  updateStateFromServerData(payload) {
    this.setState({
      estimates:     payload.estimates,
      pointOptions:  payload.point_options
    })
  },

  setBrowserTitle(count) {
    let prefix = (count === 0) ? '[done]' : `(${count})`
    document.title = `${prefix} Ticket`
  }

})

module.exports = Ticket
<|endoftext|>