<commit_before>// Copyright 2010 Matthew Wood
//
// Licensed under the Apache License, Version 2.0

var http = require('http');
var sys = require('sys');
var fs = require('fs');
var dmap = require('./dmap');
var url = require('url');
var Buffer = require('buffer').Buffer;
var Router = require('./router').Router;
var mdns;
try {
  mdns = require('mdns');
} catch (e) {
  sys.log('[startup] mdns not loaded: ' + e);
}
var Song = require('./song').Song;

var updates = 0;

function DaapServer(options) {
  options = options || {};

  var name = options.name || 'daap.js';
  this.state = {
    sessions: 1,
    revision: 2, // itunes doesn't show files if this is 1
    name: name,
    databases: [
      {
        name: name,
        songs: [],
        playlists: [{
          name: name,
          songs: []
        }],
      }
    ]
  };

  var target = this;
  if (options.songs) {
    var i = 0;
    options.songs.forEach(function(song) {
      target.state.databases[0].songs.push(song);
      target.state.databases[0].playlists[0].songs.push(i++);
    });
  }

  this.advertise = (options.advertise == true);

  this._http = http.createServer(function (req, res) {
    DaapServer.router().exec(req, res, target);
  });
}

exports.createServer = function(options) {
  return new DaapServer(options);
};

DaapServer.prototype.listen = function(port) {
  // iTunes 12 won't consider us without a txtRecord
  var txt = {
    "txtvers": 1,
    "iTSh Version": 196619,
    "Media Kinds Shared": 1,
    "OSsi": "0x1F5",
    "dmv": 131082,
    "Version": 196620
    //"Machine ID": 1,
    //"Database ID": 1
  };

  port = port || 3689;

  this._http.listen(port);

  if (mdns && this.advertise) {
    sys.log('[mdns] advertising _daap._tcp on port ' + port);
    var mdnsAdvert = mdns.createAdvertisement(mdns.tcp('daap'), port, {txtRecord: txt});
    mdnsAdvert.start();
  }
}

DaapServer.router = function() {
  return DaapServer.prototype._router;
};

DaapServer.prototype._router = new Router();
DaapServer.prototype._router.add_route('/server-info', function(context) {
  reply(context.response,
    ["msrv",[
      ["mstt", 200],
      ["apro", {"major":3,"minor":0}],
      ["msix", 1], // indexing?
      ["msex", 1],
      ["msup", 1], // support update?
      ["msal", 0], // autologout
      ["mstm", 1800], // timeout
      ["mslr", 1], // login required?
      ["msqy", 1], // supports queries?
      ["minm", this.state.name], // server name
      ["msrs", 0], // supports resolve
      ["msbr", 1], // supports browsing?
      ["mspi", 1], // persistent ids?

      ["mpro", {"major":2,"minor":0}],
      ["msau", 0]
      //["msdc", this.state.databases.length]
    ]]
  ,true);
});

DaapServer.prototype._router.add_route("/content-codes", function(context) {
  var mccr_contents = dmap.contentCodes.getAllTags().map(function(tag) {
        var defn = dmap.contentCodes.definition(tag);

        return ['mdcl', [
          ['mcna', defn.name],
          ['mcnm', tag],
          ['mcty', defn.type]
        ]];
      });

  mccr_contents.unshift(['mstt', 200]);
  reply(context.response, ['mccr', mccr_contents], true);
});

DaapServer.prototype._router.add_route("/login", function(context) {
  reply(context.response,
    ["mlog",[
      ["mstt", 200],
      // perhaps we should hardcode the session id while debugging ...
      // itunes won't continue if it gets a 0 sessionid, so do pre-increment
      ["mlid", ++this.state.sessions]
    ]]
  ,true);
});

DaapServer.prototype._router.add_route("/update", function(context) {
  updates++;
  // iTunes sends delta, Banshee doesn't send delta,
  // so let's pause on the 2nd update request we get for a given session id
  if(context.params['delta'] > 0 || updates > 1) {
    /*
    reply(context.response,
      ["mupd",[
        ["mstt", 200],
        ["musr", this.state.revision]
      ]]
    ,true);
    */
    // iTunes expects library updates to be streamed in response.
    // If this connection is closed, playback seems to stop
    context.response.setTimeout(0);
  } else {
    reply(context.response,
      ["mupd",[
        ["mstt", 200],
        ["musr", this.state.revision]
      ]]
    ,true);
  }
});

DaapServer.prototype._router.add_route('/databases', function(context) {
  reply(context.response,
    ["avdb", [
      ["mstt", 200],
      ["muty", 0],
      ["mtco", this.state.databases.length],
      ["mrco", this.state.databases.length],
      ["mlcl", this.state.databases.map(function(db, i) {
        return ["mlit", [
                 ["miid", indexToUrlkey(i)],
                 ["mper", indexToUrlkey(i)],
                 ["minm", db.name],
                 ["mimc", db.songs.length],
                 ["mctc", db.playlists.length]
        ]];
      })]
    ]]
   ,true);
});

DaapServer.prototype._router.add_route('/databases/:dbid/items', function(context) {
  var meta = context.params['meta'].split(',');

  var songs = this.state.databases[urlkeyToIndex(context.params['dbid'])].songs;

  reply(context.response,
    ["adbs",[
      ["mstt", 200],
      ["muty", 0],
      ["mtco", songs.length],
      ["mrco", songs.length],
      ["mlcl", songs.map(function(song, i) {
        var urlkey = indexToUrlkey(i);
        return ["mlit", [
          ['miid', urlkey], // daap.itemid
          ['minm', song.name], // daap.itemname
          //['mikd', 2], // daap.itemkind
          ['mper', urlkey], // daap.persistenid
          ['asal', song.album], // daap.songalbum
          ['asar', song.artist], // daap.songartist
          // daap.songalbumartist
          // daap.songbitrate
          //['ascm', ''], // daap.songcomment
          // daap.songcomposer
          //['asda', song.dateAdded], // daap.songdateadded
          //['asdm', song.dateModified], // daap.songdatemodified
          ['asdc', song.disccount], // songdisccount
          ['asdn', song.discnumber], // daap.songdiscnumber
          //['asdb', 0], // daap.songdisabled
          ['asfm', song.format], // daap.songformat
          ['asgn', song.genre], // daap.songgenre
          //['asdt', 'song description'], // daap.songdescription
          // daap.songrelativevolume
          // songsamplerate
          ['assz', song.size], // daap.songsize
          /*
          ['asst', 0], // daap.songstarttime
          ['assp', 0], // daap.songstoptime
          */
          // something's wrong with this
          ['astm', song.time], // daap.songtime
          ['astc', song.trackcount], // daap.songtrackcount
          ['astn', song.tracknumber], // daap.songtracknumber
          // songuserrating
          ['asyr', song.year] // daap.songyear
          //['asdk', 0], // daap.songdatakind
          //['asul', ''] // daap.songdataurl
        ].filter(function(item) {
          if (meta == 'all') {
            return true;
          }
          return (meta.indexOf(dmap.contentCodes.definition(dmap.tag(item)).name) > -1);
        })];
      })]
    ]]
  ,true);
},{
  pattern:{
    dbid: '(\\d+)'
  },
  type:{
    dbid: Number
  }
});

DaapServer.prototype._router.add_route('/databases/:dbid/containers', function(context) {
  var playlists = this.state.databases[urlkeyToIndex(context.params['dbid'])].playlists;

  reply(context.response,
    ["aply", [
      ["mstt",200],
      ["muty",0],
      ["mtco", playlists.length],
      ["mrco", playlists.length],
      ["mlcl", playlists.map(function(playlist, i) {
          return ['mlit', [
            ["miid", indexToUrlkey(i)],
            ["minm", "Sample Playlist"],
            ["mper", indexToUrlkey(i)],
            //["mimc", playlist.songs.length]
            ["mpco", 0],
        ]];
      })]
    ]]
  ,true);
}, {
  pattern: {
    dbid: '(\\d+)'
  },
  type: {
    dbid: Number
  }
});

DaapServer.prototype._router.add_route('/databases/:dbid/containers/:playlistid/items', function(context) {
  var songs = this.state.databases[urlkeyToIndex(context.params['dbid'])].playlists[urlkeyToIndex(context.params['playlistid'])].songs;

  reply(context.response,
    ["apso",[
      ["mstt",200],
      ["muty",0],
      ["mtco",songs.length],
      ["mrco",songs.length],
      ["mlcl", songs.map(function(song, i) {
        return ["mlit", [
          //["mikd", 2],
          ["miid", indexToUrlkey(i)],
          ["mcti", indexToUrlkey(i)]
        ]];
      })]
    ]]
  ,true);
}, {
  pattern: {
    dbid: '(\\d+)',
    playlistid: '(\\d+)'
  },
  type: {
    dbid: Number,
    playlistid: Number
  }
});

DaapServer.prototype._router.add_route('/databases/:dbid/items/:songid', function(context) {
  var song = this.state.databases[urlkeyToIndex(context.params['dbid'])].songs[urlkeyToIndex(context.params['songid'])];

  context.response.connection.setTimeout(0);

  var streamHeaders = {
    'Content-Type': 'audio/' + song.format,
    'Accept-Range': 'bytes',
    'Connection': 'Close'
  };

  var status, label, offset;
  var end  = song.size - 1;
  if (context.request.headers.range) {
    status = 206;
    label = 'Partial Content';

    var range = context.request.headers.range.split('=');
    offset = Number(range[1].split('-')[0]);

    streamHeaders['Content-Range'] = 'bytes ' + offset + '-' + end + '/' + song.size;
  } else {
    status = 200;
    label = 'OK';

    offset = 0;
  }

  streamHeaders['Content-Length'] = song.size - offset;
  context.response.writeHead(status, label, headers(streamHeaders));

  sys.log('[streaming] ' + song.file + ' from byte ' + offset);
  var reader = fs.createReadStream(song.file, {
    start: offset,
    end: end,
    bufferSize: 512}
  );

  reader.pipe(context.response);

}, {pattern: {
  dbid: '(\\d+)',
  songid: '(\\d+).mp3'
}, type: {
  dbid: Number,
  songid: Number
}});

function reply(response, item, debug) {
  var label;
  var buffer;

  if (item.constructor == Buffer) {
    label = '<binary>';
    buffer = item;
  } else {
    label = dmap.tag(item);
    buffer = new Buffer(dmap.encodedLength(item));
    dmap.encode(item, buffer);
  }

  response.writeHead(200, headers({
    'Content-Length': buffer.length
  }));

  response.end(buffer);

  sys.log('[reply] ' + label + ' ' + buffer.length + ' bytes');
  if(debug){
    var reader = require('./reader.js');
    console.log(JSON.stringify(reader.read(buffer)[1],null,1));
  }
}

function headers(extra) {
  var headers = {
    'Date': new Date().toString(),
    'Content-Type': 'application/x-dmap-tagged',
    'DAAP-Server': 'daap.js/0.0'
  };

  for(var key in extra) {
    headers[key] = extra[key];
  }

  return headers;
}

function indexToUrlkey(i) {
  return i + 1;
}

function urlkeyToIndex(k) {
  return k - 1;
}
<commit_msg>fix session support<commit_after>// Copyright 2010 Matthew Wood
//
// Licensed under the Apache License, Version 2.0

var http = require('http');
var sys = require('sys');
var fs = require('fs');
var dmap = require('./dmap');
var url = require('url');
var Buffer = require('buffer').Buffer;
var Router = require('./router').Router;
var mdns;
try {
  mdns = require('mdns');
} catch (e) {
  sys.log('[startup] mdns not loaded: ' + e);
}
var Song = require('./song').Song;

var updates = 0;

function DaapServer(options) {
  options = options || {};

  var name = options.name || 'daap.js';
  this.sessions = {}
  this.state = {
    revision: 2, // itunes doesn't show files if this is 1
    name: name,
    databases: [
      {
        name: name,
        songs: [],
        playlists: [{
          name: name,
          songs: []
        }],
      }
    ]
  };

  var target = this;
  if (options.songs) {
    var i = 0;
    options.songs.forEach(function(song) {
      target.state.databases[0].songs.push(song);
      target.state.databases[0].playlists[0].songs.push(i++);
    });
  }

  this.advertise = (options.advertise == true);

  this._http = http.createServer(function (req, res) {
    DaapServer.router().exec(req, res, target);
  });
}

exports.createServer = function(options) {
  return new DaapServer(options);
};

DaapServer.prototype.listen = function(port) {
  // iTunes 12 won't consider us without a txtRecord
  var txt = {
    "txtvers": 1,
    "iTSh Version": 196619,
    "Media Kinds Shared": 1,
    "OSsi": "0x1F5",
    "dmv": 131082,
    "Version": 196620
    //"Machine ID": 1,
    //"Database ID": 1
  };

  port = port || 3689;

  this._http.listen(port);

  if (mdns && this.advertise) {
    sys.log('[mdns] advertising _daap._tcp on port ' + port);
    var mdnsAdvert = mdns.createAdvertisement(mdns.tcp('daap'), port, {txtRecord: txt});
    mdnsAdvert.start();
  }
}

DaapServer.router = function() {
  return DaapServer.prototype._router;
};

DaapServer.prototype._router = new Router();
DaapServer.prototype._router.add_route('/server-info', function(context) {
  reply(context.response,
    ["msrv",[
      ["mstt", 200],
      ["apro", {"major":3,"minor":0}],
      ["msix", 1], // indexing?
      ["msex", 1],
      ["msup", 1], // support update?
      ["msal", 0], // autologout
      ["mstm", 1800], // timeout
      ["mslr", 1], // login required?
      ["msqy", 1], // supports queries?
      ["minm", this.state.name], // server name
      ["msrs", 0], // supports resolve
      ["msbr", 1], // supports browsing?
      ["mspi", 1], // persistent ids?

      ["mpro", {"major":2,"minor":0}],
      ["msau", 0]
      //["msdc", this.state.databases.length]
    ]]
  ,true);
});

DaapServer.prototype._router.add_route("/content-codes", function(context) {
  var mccr_contents = dmap.contentCodes.getAllTags().map(function(tag) {
        var defn = dmap.contentCodes.definition(tag);

        return ['mdcl', [
          ['mcna', defn.name],
          ['mcnm', tag],
          ['mcty', defn.type]
        ]];
      });

  mccr_contents.unshift(['mstt', 200]);
  reply(context.response, ['mccr', mccr_contents], true);
});

DaapServer.prototype._router.add_route("/login", function(context) {
  var getRandomInt = function(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
  }
  var sessionId = getRandomInt(10000, 99999);
  this.sessions[ sessionId ] = {
    since: Date.now(),
    revision: this.state.revision,
    updateRequests: 0
  };
  reply(context.response,
    ["mlog",[
      ["mstt", 200],
      // perhaps we should hardcode the session id while debugging ...
      // itunes won't continue if it gets a 0 sessionid, so do pre-increment
      ["mlid", sessionId]
    ]]
  ,true);
});

DaapServer.prototype._router.add_route("/update", function(context) {
  console.log('session: ' + context.params['session-id']);
  var sessionId = context.params['session-id']
  var session = {
    updateRequests: 0,
    revision: this.state.revision
  }
  if (sessionId in this.sessions) { // should be
    session = this.sessions[sessionId]
  }

  // iTunes sends delta, Banshee doesn't send delta,
  // so let's pause on the 2nd update request we get for a given session id
  if(context.params['delta'] > 0 || session.updateRequests > 1) {
    /*
    reply(context.response,
      ["mupd",[
        ["mstt", 200],
        ["musr", this.state.revision]
      ]]
    ,true);
    */
    // iTunes expects library updates to be streamed in response.
    // If this connection is closed, playback seems to stop
    context.response.setTimeout(0);
  } else {
    console.log('Update revision number: ' + session.revision);
    reply(context.response,
      ["mupd",[
        ["mstt", 200],
        ["musr", session.revision]
      ]]
    ,true);
  }
});

DaapServer.prototype._router.add_route('/databases', function(context) {
  reply(context.response,
    ["avdb", [
      ["mstt", 200],
      ["muty", 0],
      ["mtco", this.state.databases.length],
      ["mrco", this.state.databases.length],
      ["mlcl", this.state.databases.map(function(db, i) {
        return ["mlit", [
                 ["miid", indexToUrlkey(i)],
                 ["mper", indexToUrlkey(i)],
                 ["minm", db.name],
                 ["mimc", db.songs.length],
                 ["mctc", db.playlists.length]
        ]];
      })]
    ]]
   ,true);
});

DaapServer.prototype._router.add_route('/databases/:dbid/items', function(context) {
  var meta = context.params['meta'].split(',');

  var songs = this.state.databases[urlkeyToIndex(context.params['dbid'])].songs;

  reply(context.response,
    ["adbs",[
      ["mstt", 200],
      ["muty", 0],
      ["mtco", songs.length],
      ["mrco", songs.length],
      ["mlcl", songs.map(function(song, i) {
        var urlkey = indexToUrlkey(i);
        return ["mlit", [
          ['miid', urlkey], // daap.itemid
          ['minm', song.name], // daap.itemname
          //['mikd', 2], // daap.itemkind
          ['mper', urlkey], // daap.persistenid
          ['asal', song.album], // daap.songalbum
          ['asar', song.artist], // daap.songartist
          // daap.songalbumartist
          // daap.songbitrate
          //['ascm', ''], // daap.songcomment
          // daap.songcomposer
          //['asda', song.dateAdded], // daap.songdateadded
          //['asdm', song.dateModified], // daap.songdatemodified
          ['asdc', song.disccount], // songdisccount
          ['asdn', song.discnumber], // daap.songdiscnumber
          //['asdb', 0], // daap.songdisabled
          ['asfm', song.format], // daap.songformat
          ['asgn', song.genre], // daap.songgenre
          //['asdt', 'song description'], // daap.songdescription
          // daap.songrelativevolume
          // songsamplerate
          ['assz', song.size], // daap.songsize
          /*
          ['asst', 0], // daap.songstarttime
          ['assp', 0], // daap.songstoptime
          */
          // something's wrong with this
          ['astm', song.time], // daap.songtime
          ['astc', song.trackcount], // daap.songtrackcount
          ['astn', song.tracknumber], // daap.songtracknumber
          // songuserrating
          ['asyr', song.year] // daap.songyear
          //['asdk', 0], // daap.songdatakind
          //['asul', ''] // daap.songdataurl
        ].filter(function(item) {
          if (meta == 'all') {
            return true;
          }
          return (meta.indexOf(dmap.contentCodes.definition(dmap.tag(item)).name) > -1);
        })];
      })]
    ]]
  ,true);
},{
  pattern:{
    dbid: '(\\d+)'
  },
  type:{
    dbid: Number
  }
});

DaapServer.prototype._router.add_route('/databases/:dbid/containers', function(context) {
  var playlists = this.state.databases[urlkeyToIndex(context.params['dbid'])].playlists;

  reply(context.response,
    ["aply", [
      ["mstt",200],
      ["muty",0],
      ["mtco", playlists.length],
      ["mrco", playlists.length],
      ["mlcl", playlists.map(function(playlist, i) {
          return ['mlit', [
            ["miid", indexToUrlkey(i)],
            ["minm", "Sample Playlist"],
            ["mper", indexToUrlkey(i)],
            //["mimc", playlist.songs.length]
            ["mpco", 0],
        ]];
      })]
    ]]
  ,true);
}, {
  pattern: {
    dbid: '(\\d+)'
  },
  type: {
    dbid: Number
  }
});

DaapServer.prototype._router.add_route('/databases/:dbid/containers/:playlistid/items', function(context) {
  var songs = this.state.databases[urlkeyToIndex(context.params['dbid'])].playlists[urlkeyToIndex(context.params['playlistid'])].songs;

  reply(context.response,
    ["apso",[
      ["mstt",200],
      ["muty",0],
      ["mtco",songs.length],
      ["mrco",songs.length],
      ["mlcl", songs.map(function(song, i) {
        return ["mlit", [
          //["mikd", 2],
          ["miid", indexToUrlkey(i)],
          ["mcti", indexToUrlkey(i)]
        ]];
      })]
    ]]
  ,true);
}, {
  pattern: {
    dbid: '(\\d+)',
    playlistid: '(\\d+)'
  },
  type: {
    dbid: Number,
    playlistid: Number
  }
});

DaapServer.prototype._router.add_route('/databases/:dbid/items/:songid', function(context) {
  var song = this.state.databases[urlkeyToIndex(context.params['dbid'])].songs[urlkeyToIndex(context.params['songid'])];

  context.response.connection.setTimeout(0);

  var streamHeaders = {
    'Content-Type': 'audio/' + song.format,
    'Accept-Range': 'bytes',
    'Connection': 'Close'
  };

  var status, label, offset;
  var end  = song.size - 1;
  if (context.request.headers.range) {
    status = 206;
    label = 'Partial Content';

    var range = context.request.headers.range.split('=');
    offset = Number(range[1].split('-')[0]);

    streamHeaders['Content-Range'] = 'bytes ' + offset + '-' + end + '/' + song.size;
  } else {
    status = 200;
    label = 'OK';

    offset = 0;
  }

  streamHeaders['Content-Length'] = song.size - offset;
  context.response.writeHead(status, label, headers(streamHeaders));

  sys.log('[streaming] ' + song.file + ' from byte ' + offset);
  var reader = fs.createReadStream(song.file, {
    start: offset,
    end: end,
    bufferSize: 512}
  );

  reader.pipe(context.response);

}, {pattern: {
  dbid: '(\\d+)',
  songid: '(\\d+).mp3'
}, type: {
  dbid: Number,
  songid: Number
}});

function reply(response, item, debug) {
  var label;
  var buffer;

  if (item.constructor == Buffer) {
    label = '<binary>';
    buffer = item;
  } else {
    label = dmap.tag(item);
    buffer = new Buffer(dmap.encodedLength(item));
    dmap.encode(item, buffer);
  }

  response.writeHead(200, headers({
    'Content-Length': buffer.length
  }));

  response.end(buffer);

  sys.log('[reply] ' + label + ' ' + buffer.length + ' bytes');
  if(debug){
    var reader = require('./reader.js');
    //console.log(JSON.stringify(reader.read(buffer)[1],null,1));
  }
}

function headers(extra) {
  var headers = {
    'Date': new Date().toString(),
    'Content-Type': 'application/x-dmap-tagged',
    'DAAP-Server': 'daap.js/0.0'
  };

  for(var key in extra) {
    headers[key] = extra[key];
  }

  return headers;
}

function indexToUrlkey(i) {
  return i + 1;
}

function urlkeyToIndex(k) {
  return k - 1;
}
<|endoftext|>


<commit_before>var color = require('../config/color');
var fs = require('fs');
var moment = require('moment');

var BR = '
';
var SPACE = ' ';
var profileColor = '#cc301f';
var trainersprites = [1, 2, 101, 102, 169, 170, 265, 266, 168];

/**
 * Profile constructor.
 *
 * @param {Boolean} isOnline
 * @param {Object|String} user - if isOnline then Object else String
 * @param {String} image
 */
function Profile(isOnline, user, image) {
	this.isOnline = isOnline || false;
	this.user = user || null;
	this.image = image;

	this.username = Tools.escapeHTML(this.isOnline ? this.user.name : this.user);
	this.url = Config.avatarurl || '';
}

/**
 * Create an bold html tag element.
 *
 * Example:
 * createFont('Hello World!');
 * => 'Hello World!'
 *
 * @param {String} color
 * @param {String} text
 * @return {String}
 */
function bold(text) {
	return '' + text + '';
}

/**
 * Create an font html tag element.
 *
 * Example:
 * createFont('Hello World!', 'blue');
 * => 'Hello World!'
 *
 * @param {String} color
 * @param {String} text
 * @return {String}
 */
function font(color, text) {
	return '' + text + '';
}

/**
 * Create an img tag element.
 *
 * Example:
 * createImg('phil.png');
 * => ''
 *
 * @param {String} link
 * @return {String}
 */
function img(link) {
	return '

';
}

/**
 * Create a font html element wrap around by a bold html element.
 * Uses to `profileColor` as a color.
 * Adds a colon at the end of the text and a SPACE at the end of the element.
 *
 * Example:
 * label('Name');
 * => 'Name: '
 *
 * @param {String} text
 * @return {String}
 */
function label(text) {
	return bold(font(profileColor, text + ':')) + SPACE;
}

function titleCSS(text) {
	return '' + text + '';
}

Profile.prototype.avatar = function () {
	if (this.isOnline) {
		if (typeof this.image === 'string') return img(this.url + '/avatars/' + this.image);
		return img('http://play.pokemonshowdown.com/sprites/trainers/' + this.image + '.png');
	}
	for (var name in Config.customAvatars) {
		if (this.username === name) {
			return img(this.url + '/avatars/' + Config.customAvatars[name]);
		}
	}
	var selectedSprite = trainersprites[Math.floor(Math.random() * trainersprites.length)];
	return img('http://play.pokemonshowdown.com/sprites/trainers/' + selectedSprite + '.png');
};

Profile.prototype.group = function () {
	if (this.isOnline && this.user.group === ' ') return label('Group') + 'Regular User';
	if (this.isOnline) return label('Group') + Config.groups[this.user.group].name;
	for (var name in Users.usergroups) {
		if (toId(this.username) === name) {
			return label('Group') + Config.groups[Users.usergroups[name].charAt(0)].name;
		}
	}
	return label('Group') + 'Regular User';
};

Profile.prototype.bp = function (amount) {
	return label('Battle Points') + amount;
};

Profile.prototype.name = function () {
	return label('Name') + bold(font(color(toId(this.username)), this.username));
};

Profile.prototype.seen = function (timeAgo) {
	if (this.isOnline) return label('Last Seen') + font('#2ECC40', 'Currently Online');
	if (!timeAgo) return label('Last Seen') + 'Offline';
	return label('Last Seen') + moment(timeAgo).fromNow();
};

Profile.prototype.title = function (amount) {
	return titleCSS(amount);
};

Profile.prototype.show = function (callback) {
	var userid = toId(this.username);

	Database.read('bp', userid, function (err, bp) {
		if (err) throw err;
		if (!bp) bp = 0;
		Database.read('title', userid, function (err, title) {
			if (err) throw err;
			if (title) {
				return callback(this.avatar() +
												SPACE + this.title(title) + BR +
												SPACE + this.name() + BR +
												SPACE + this.group() + BR +
												SPACE + this.bp(bp) + BR +
												SPACE + this.seen(Seen[userid]) +
												'
');
			} else {
				return callback(this.avatar() +
												SPACE + this.name() + BR +
												SPACE + this.group() + BR +
												SPACE + this.bp(bp) + BR +
												SPACE + this.seen(Seen[userid]) +
												'
');
			}
		}.bind(this));
	}.bind(this));
};

exports.commands = {
	profile: function (target, room, user) {
		if (!this.canBroadcast()) return;
		if (target.length >= 19) return this.sendReply("Usernames are required to be less than 19 characters long.");
		var targetUser = this.targetUserOrSelf(target);
		var profile;
		if (!targetUser) {
			profile = new Profile(false, target);
		} else {
			profile = new Profile(true, targetUser, targetUser.avatar);
		}
		profile.show(function (display) {
			this.sendReplyBox(display);
			room.update();
		}.bind(this));
	},
	profilehelp: ["/profile -	Shows information regarding user's name, group, Battle Points, and when they were last seen."]
};
<commit_msg>Update profile.js<commit_after>var color = require('../config/color');
var fs = require('fs');
var moment = require('moment');

var BR = '
';
var SPACE = ' ';
var profileColor = '#cc301f';
var trainersprites = [1, 2, 101, 102, 169, 170, 265, 266, 168];

/**
 * Profile constructor.
 *
 * @param {Boolean} isOnline
 * @param {Object|String} user - if isOnline then Object else String
 * @param {String} image
 */
function Profile(isOnline, user, image) {
	this.isOnline = isOnline || false;
	this.user = user || null;
	this.image = image;

	this.username = Tools.escapeHTML(this.isOnline ? this.user.name : this.user);
	this.url = Config.avatarurl || '';
}

/**
 * Create an bold html tag element.
 *
 * Example:
 * createFont('Hello World!');
 * => 'Hello World!'
 *
 * @param {String} color
 * @param {String} text
 * @return {String}
 */
function bold(text) {
	return '' + text + '';
}

/**
 * Create an font html tag element.
 *
 * Example:
 * createFont('Hello World!', 'blue');
 * => 'Hello World!'
 *
 * @param {String} color
 * @param {String} text
 * @return {String}
 */
function font(color, text) {
	return '' + text + '';
}

/**
 * Create an img tag element.
 *
 * Example:
 * createImg('phil.png');
 * => ''
 *
 * @param {String} link
 * @return {String}
 */
function img(link) {
	return '

';
}

/**
 * Create a font html element wrap around by a bold html element.
 * Uses to `profileColor` as a color.
 * Adds a colon at the end of the text and a SPACE at the end of the element.
 *
 * Example:
 * label('Name');
 * => 'Name: '
 *
 * @param {String} text
 * @return {String}
 */
function label(text) {
	return bold(font(profileColor, text + ':')) + SPACE;
}

function titleCSS(text) {
	return '' + text + '';
}

Profile.prototype.avatar = function () {
	if (this.isOnline) {
		if (typeof this.image === 'string') return img(this.url + '/avatars/' + this.image);
		return img('http://play.pokemonshowdown.com/sprites/trainers/' + this.image + '.png');
	}
	for (var name in Config.customAvatars) {
		if (this.username === name) {
			return img(this.url + '/avatars/' + Config.customAvatars[name]);
		}
	}
	var selectedSprite = trainersprites[Math.floor(Math.random() * trainersprites.length)];
	return img('http://play.pokemonshowdown.com/sprites/trainers/' + selectedSprite + '.png');
};

Profile.prototype.group = function () {
	if (this.isOnline && this.user.group === ' ') return label('Group') + 'Regular User';
	if (this.isOnline) return label('Group') + Config.groups[this.user.group].name;
	for (var name in Users.usergroups) {
		if (toId(this.username) === name) {
			return label('Group') + Config.groups[Users.usergroups[name].charAt(0)].name;
		}
	}
	return label('Group') + 'Regular User';
};

Profile.prototype.bp = function (amount) {
	return label('Battle Points') + amount;
};

Profile.prototype.name = function () {
	return label('Name') + bold(font(color(toId(this.username)), this.username));
};

Profile.prototype.seen = function (timeAgo) {
	if (this.isOnline) return label('Last Seen') + font('#1fcc33', 'Online');
	if (!timeAgo) return label('Last Seen') + 'Offline';
	return label('Last Seen') + moment(timeAgo).fromNow();
};

Profile.prototype.title = function (amount) {
	return titleCSS(amount);
};

Profile.prototype.show = function (callback) {
	var userid = toId(this.username);

	Database.read('bp', userid, function (err, bp) {
		if (err) throw err;
		if (!bp) bp = 0;
		Database.read('title', userid, function (err, title) {
			if (err) throw err;
			if (title) {
				return callback(this.avatar() +
												SPACE + this.title(title) + BR +
												SPACE + this.name() + BR +
												SPACE + this.group() + BR +
												SPACE + this.bp(bp) + BR +
												SPACE + this.seen(Seen[userid]) +
												'
');
			} else {
				return callback(this.avatar() +
												SPACE + this.name() + BR +
												SPACE + this.group() + BR +
												SPACE + this.bp(bp) + BR +
												SPACE + this.seen(Seen[userid]) +
												'
');
			}
		}.bind(this));
	}.bind(this));
};

exports.commands = {
	profile: function (target, room, user) {
		if (!this.canBroadcast()) return;
		if (target.length >= 19) return this.sendReply("Usernames are required to be less than 19 characters long.");
		var targetUser = this.targetUserOrSelf(target);
		var profile;
		if (!targetUser) {
			profile = new Profile(false, target);
		} else {
			profile = new Profile(true, targetUser, targetUser.avatar);
		}
		profile.show(function (display) {
			this.sendReplyBox(display);
			room.update();
		}.bind(this));
	},
	profilehelp: ["/profile -	Shows information regarding user's name, group, Battle Points, and when they were last seen."]
};
<|endoftext|>


<commit_before>function startTest() {
  mediaFile = document.querySelector('input').value;
  keySystem = keySystemList.value;
  mediaType = mediaTypeList.value;

  loadEncryptedMediaFromURL(video, useSRC.checked);
  video.play();
}

function loadEncryptedMediaFromURL(video, useSRC) {
  return loadEncryptedMedia(video, mediaFile, keySystem, mediaType, useSRC);
}

function loadEncryptedMedia(video, mediaFiles, keySystem, mediaTypes, useSRC) {
  console.log('Trying to run:');
  console.log('mediaFiles: ' + mediaFiles);
  console.log('mediaTypes: ' + mediaTypes);
  console.log('keySystem: ' + keySystem);
  mediaSource = null;
  video = video;

  // Add a property to video to check key was added.
  video.hasKeyAdded = false;
  mediaFiles = ConvertToArray(mediaFiles);
  mediaTypes = ConvertToArray(mediaTypes);

  if (!(video && mediaFiles && keySystem))
    failTest('Missing parameters in loadEncryptedMedia().');

  var totalAppended = 0;
  function onSourceOpen(e) {
    console.log('onSourceOpen', e);
    // We can load multiple media files using the same media type. However, if
    // more than one media type is used, we expect to have a media type entry
    // for each corresponding media file.
    var srcBuffer = null;
    for (var i = 0; i < mediaFiles.length; i++) {
      if (i == 0 || mediaFiles.length == mediaTypes.length) {
        console.log('Creating a source buffer for type ' + mediaTypes[i]);
        srcBuffer = mediaSource.addSourceBuffer(mediaTypes[i]);
      }
      doAppend(mediaFiles[i], srcBuffer);
    }
  }
  function doAppend(mediaFile, srcBuffer) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', mediaFile);
    xhr.responseType = 'arraybuffer';
    xhr.addEventListener('load', function(e) {
      console.log('Appending to buffer ' + mediaFile);
      srcBuffer.append(new Uint8Array(e.target.response));
      totalAppended++;
      if (totalAppended == mediaFiles.length)
        mediaSource.endOfStream();
    });
    xhr.send();
  }
  //removeEventListeners(video, mediaSource);

  video.addEventListener('webkitneedkey', onNeedKey);
  video.addEventListener('webkitkeymessage', onKeyMessage);
  video.addEventListener('webkitkeyerror', failTest);
  video.addEventListener('webkitkeyadded', onKeyAdded);

  if (useSRC)
    video.src = mediaFile;
  else {
    mediaSource = new WebKitMediaSource();
    mediaSource.addEventListener('webkitsourceopen', onSourceOpen);
    video.src = window.URL.createObjectURL(mediaSource);
  }
}

function onNeedKey(e) {
  console.log('onNeedKey', e);
  console.log('Generate key request with initData: ' + getHexString(e.initData));
  e.target.webkitGenerateKeyRequest(keySystem, e.initData);
}

function onKeyAdded(e) {
  e.target.hasKeyAdded = true;
}

function onKeyMessage(e) {
  console.log('onKeyMessage', e);
  if (isHeartBeatMessage(e.message)) {
    console.log('onKeyMessage - heart beat', e);
    return;
  }
  if (keySystem == 'com.widevine.alpha' || USE_WV_SERVER) {
    console.log('Requesting key from widevine server.');
    requestKey(e);
  }
  else {
    var initData = e.message;
    if (keySystem.indexOf('clearkey') != -1 && mediaType.indexOf('mp4') != -1)
      initData = initDataFromKeyId(); // Temporary hack for Clear Key in v0.1.
    e.target.webkitAddKey(keySystem, KEY, initData);
  }
}

var KEY = new Uint8Array([0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
                          0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c]);
// Key ID used for init data.
var keyID = "0123456789012345";
// Stores a failure message that is read by the browser test when it fails.
var failMessage = '';

function failTest(msg) {
  if (msg instanceof Event)
    failMessage = msg.target + '.' + msg.type;
  else
    failMessage = msg;
  console.log(failMessage);
}

<commit_msg>Delete requiredJavaScript.js<commit_after><|endoftext|>


<commit_before>/**
 * Created by tim on 2015-10-03.
 */
var geigerManager;
var geigerGameModel;
var geigerLabView;
var gameCaseID = 0;
var gameNumber = 0;
var max_dose = 20000;

/**
 * called from the html when user presses the new game button
 */
function newGame() {
    if (gameCaseID > 0) {
        codapHelper.closeCase(
            'games',
            [this.gameNumber, geigerGameModel.dose, geigerGameModel.sourceX, geigerGameModel.sourceY],
            gameCaseID
        );
    }
    gameNumber += 1;
    /**
     * start a new game case
     */
    codapHelper.openCase(
        'games',
        [this.gameNumber, 0, null, null],
        setUpNewGameData
    );

    geigerManager.newGame();
    console.log("New game. Source at (" + (geigerGameModel.sourceX) + ", " + (geigerGameModel.sourceY) + ")");
}

/**
 * Used to set the game's caseID. Callback from codapHelper.openCase().
 * @param iResult
 */
function    setUpNewGameData(iResult) {
    gameCaseID = iResult.caseID;
    console.log("got case ID " + (gameCaseID));
}

/**
 * Singleton view of the "Lab" -- the region in which the speck might hide
 *
 * @type {{canvas: null, ctx: null, pixelsPerUnit: null, unitsAcross: number, setup: Function, update: Function, drawDetector: Function}}
 */
geigerLabView = {
    /**
     * The HTML <canvas>
     *     @property
     */
    canvas: null,
    /**
     * The drawing context for this.canvas
     */
    ctx: null,
    /**
     * The scale of the coordinate system in the view; determined in this.setup()
     */
    pixelsPerUnit: null,
    /**
     * How many units across is this canvas?
     */
    unitsAcross: 100.0,

    /**
     * Sets up the properties
     * Also adds event listeners
     */
    setup: function() {
        this.canvas = document.getElementById("lab");
        this.canvas.addEventListener("mouseup",clickInLab,false);
        this.ctx = this.canvas.getContext('2d');
        this.pixelsPerUnit = [
            this.canvas.width / this.unitsAcross,
            this.canvas.height / this.unitsAcross
        ];
    },

    /**
     * Update this view.
     */
    update: function() {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);

        this.ctx.beginPath();
        this.drawDetector();
        this.ctx.closePath();

        // UI text changes

        var geigerCoordinates = "(" + geigerGameModel.detectorX + ", "
            + geigerGameModel.detectorY + ")";
        document.getElementById("takeMeasurement").innerHTML = "measure at " + geigerCoordinates;

        // window.requestAnimationFrame(geigerLabView.update);
    },

    /**
     * Draws the detector at the current location. Called by this.update().
     */
    drawDetector: function( ) {
        var tContext = this.ctx;

        tContext.save();
        tContext.strokeStyle = "#DDEEFF";
        tContext.translate(
            geigerGameModel.detectorX * this.pixelsPerUnit[0],
            this.canvas.height - geigerGameModel.detectorY * this.pixelsPerUnit[1]
        );

        tContext.beginPath();
        tContext.moveTo(0,6);
        tContext.lineTo(-6,0);
        tContext.lineTo(0, -6);
        tContext.lineTo(6,0);
        tContext.closePath();
        tContext.stroke();

        tContext.restore();
    }

};

/**
 * Model for the game.
 * @type {{sourceX: number, sourceY: number, sourceStrength: number, detectorX: number, detectorY: number, dose: number, latestCount: number, setup: Function, signalStrength: Function, doMeasurement: Function}}
 */
geigerGameModel = {
    /**
     * (secret) position of the source of radiation
     */
    sourceX: 0,
    sourceY: 0,
    /**
     * Strength of the radiation
     */
    sourceStrength: 0,
    /**
     * Current position of the detector
     */
    detectorX: 0,
    detectorY: 0,
    /**
     * Total dose so far
     */
    dose: 0,
    /**
     * The radiation count for the last time the detector was used
     */
    latestCount: 0,

    /**
     * Initialize model varaibles
     */
    setup: function () {
        this.sourceX = (geigerLabView.unitsAcross * (0.25 + 0.50 * Math.random())).toFixed(2);
        this.sourceY = (geigerLabView.unitsAcross * (0.25 + 0.50 * Math.random())).toFixed(2); // TODO: fix vertical coordinate of source
        this.sourceStrength = 1000000;
        this.dose = 0;
    },

    /**
     * Compute the strength of the radiation at the detector. Use inverse square.
     * @returns {number}
     */
    signalStrength: function () {
        var tDsquared = (this.detectorX - this.sourceX ) * (this.detectorX - this.sourceX )
            + (this.detectorY - this.sourceY ) * (this.detectorY - this.sourceY );
        var tCount = (geigerGameModel.sourceStrength / tDsquared);
        tCount = Math.round(tCount);
        return tCount;
    },

    /**
     * Perform a measurement. Updates internal positions. Updates this.latestcount.
     * TODO: figure out if we need to update the positions here. If not, we can even lose the parameters.
     * @param x     X-position of detector.
     * @param y
     */
    doMeasurement: function( x, y ) {
        this.detectorX = x;
        this.detectorY = y;
        var tSignal = this.signalStrength();
        this.latestCount = document.forms.geigerForm.useRandom.checked ? randomPoisson(tSignal) : tSignal;

        this.dose += this.latestCount;   // TODO: Update game case with current dose.

    }

};

/**
 * Overall controller for the game.
 *
 * @type {{initializeComponent: Function, newGame: Function, updateScreen: Function, moveDetectorTo: Function, moveDetectorByTyping: Function, doMeasurement: Function}}
 */
geigerManager = {

    /**
     * Initializes various items.
     */
    initializeComponent: function() {
        geigerLabView.setup();
        gauge.setup('doseGauge','Dose',0,max_dose);
        newGame();
    },

    /**
     * Resets Geiger for a new game.
     */
    newGame: function() {
        displayInfo("New game. Find the source!");
        geigerGameModel.setup();
        this.latestCount = 0;
        this.updateScreen();
    },

    /**
     * Calls what's necessary to refresh the screen.
     * This includes the Lab, the gauge, and any other stuff such as text.
     */
    updateScreen: function() {
        geigerLabView.update();
        gauge.update( geigerGameModel.dose);
    },

    /**
     * In charge of moving the detector.
     * Responds to clicks OR to edits in the coordinates from text boxes.
     * @param x
     * @param y
     */
    moveDetectorTo: function (x, y) {
        geigerGameModel.detectorX = x;
        geigerGameModel.detectorY = y;

        // Make sure the coordinate boxes read the right values.
        // Redundant if user has changed position by typing.
        document.getElementById('detectorX').value = x.toString();
        document.getElementById('detectorY').value = y.toString();

        displayInfo("Detector moved to (" + x + ", " + y + ")");
        this.updateScreen();
    },

    /**
     * Called when a coordinate text box loses focus (onblur).
     * Updates coordinates of the detector.
     */
    moveDetectorByTyping: function() {
        var x = document.forms.geigerForm.detectorX.value.trim();
        var y = document.forms.geigerForm.detectorY.value.trim();
        if (x < 0) x = 0;
        if (x > geigerLabView.unitsAcross) x = geigerLabView.unitsAcross;
        if (y < 0) y = 0;
        if (y > geigerLabView.unitsAcross) y = geigerLabView.unitsAcross;   // TODO: calculate y distance correctly

        this.moveDetectorTo(x, y);
    },

    /**
     * User has called for a measurement.
     * Creates a "measurement" case in CODAP.
     * //TODO: determine if we really need to get the coordinates from the text fields
     */
    doMeasurement: function() {
        var tX = document.forms.geigerForm.detectorX.value.trim();
        var tY = document.forms.geigerForm.detectorY.value.trim();

        geigerGameModel.doMeasurement( tX, tY );

        codapHelper.createCase(
            'measurements',
            [geigerGameModel.detectorX, geigerGameModel.detectorY, geigerGameModel.latestCount],
            gameCaseID
        ); // no callback?

        displayGeigerCount( geigerGameModel.latestCount ); // note: only on doMeasurement!

        this.updateScreen();
    }
};

/**
 * Required call to initialize the sim, connect it to CODAP.
 */
codapHelper.initSim({
    name: 'Geiger',
    dimensions: {width: 444, height: 600},
    collections: [  // There are two collections: a parent and a child
        {
            name: 'games',
            // The parent collection spec:
            attrs: [
                {name: "gameNumber", type: 'categorical'},
                {name: "dose", type: 'numeric', precision: 0},
                {name: "sourceX", type: 'numeric', precision: 2},
                {name: "sourceY", type: 'numeric', precision: 2}
            ],
            childAttrName: "measurement"
        },
        {
            name: 'measurements',
            labels: {
                pluralCase: "measurements",
                setOfCasesWithArticle: "a game"
            },
            // The child collection specification:
            attrs: [
                {name: "x", type: 'numeric', precision: 1},
                {name: "y", type: 'numeric', precision: 1},
                {name: "count", type: 'numeric', precision: 0}
            ]
        }
    ]
});

/**
 * Called from HTML whn user clicks in the "Lab"
 * @param e
 */
function clickInLab( e ) {
//    Note: this routine gives page coordinates. We want the coordinates in the canvas.
    if (!e) e = window.event;
    var tX = e.layerX / geigerLabView.pixelsPerUnit[0];     //  convert to units
    var tY = (geigerLabView.canvas.height - e.layerY) / geigerLabView.pixelsPerUnit[1];

    geigerManager.moveDetectorTo(tX, tY);
}

/**
 * Display function: basic info about the sim.
 * @param message {string} The string message.
 */
function    displayInfo( message ) {
    var geigerInfoText = document.getElementById('geigerInfo');
    geigerInfoText.innerHTML = message;
}

/**
 * Display function: the latest count from the geiger counter
 * @param count The number to display
 */
function    displayGeigerCount(count) {
    var geigerCountText = document.getElementById('geigerCount');
    geigerCountText.innerHTML = "count: " + (count) + " at (" +
        (geigerGameModel.detectorX) + ", " + (geigerGameModel.detectorY) + ")" +
        " Dose: " + geigerGameModel.dose + ".";

}

/**
 * A funky random Poisson function.
 * Use Knuth algorithm up to n = 100; normal approximation beyond that.
 * @param mean
 * @returns {number}
 */
function    randomPoisson( mean ) {

    if (mean > 100) {
        var sd = Math.sqrt(mean);
        var tCount = Math.round(randomNormal(mean,sd));
        return tCount;
    }
    var L = Math.exp(-mean);
    var p = 1.0;
    var k = 0;
    do {
        k++;
        p *= Math.random();
    } while (p > L);
    return (k - 1);
}

/**
 * Random normal, Box-Muller transform. Use only one value.
 * @param mean
 * @param sd
 * @returns {*}
 */
function    randomNormal(mean,sd) {
    var t1 = Math.random();
    var t2 = Math.random();

    var tZ = Math.sqrt(-2 * Math.log(t1)) * Math.cos(2 * Math.PI*t2);

    return mean + sd * tZ;
}<commit_msg>Streamlining some calls, better separation between model and controller.<commit_after>/**
 * Created by tim on 2015-10-03.
 */
var geigerManager;
var geigerGameModel;
var geigerLabView;
var gameCaseID = 0;
var gameNumber = 0;
var max_dose = 20000;

/**
 * called from the html when user presses the new game button
 */
function newGame() {
    if (gameCaseID > 0) {
        codapHelper.closeCase(
            'games',
            [this.gameNumber, geigerGameModel.dose, geigerGameModel.sourceX, geigerGameModel.sourceY],
            gameCaseID
        );
    }
    gameNumber += 1;
    /**
     * start a new game case
     */
    codapHelper.openCase(
        'games',
        [this.gameNumber, 0, null, null],
        setUpNewGameData
    );

    geigerManager.newGame();
    console.log("New game. Source at (" + (geigerGameModel.sourceX) + ", " + (geigerGameModel.sourceY) + ")");
}

/**
 * Used to set the game's caseID. Callback from codapHelper.openCase().
 * @param iResult
 */
function    setUpNewGameData(iResult) {
    gameCaseID = iResult.caseID;
    console.log("got case ID " + (gameCaseID));
}

/**
 * Singleton view of the "Lab" -- the region in which the speck might hide
 *
 * @type {{canvas: null, ctx: null, pixelsPerUnit: null, unitsAcross: number, setup: Function, update: Function, drawDetector: Function}}
 */
geigerLabView = {
    /**
     * The HTML <canvas>
     *     @property
     */
    canvas: null,
    /**
     * The drawing context for this.canvas
     */
    ctx: null,
    /**
     * The scale of the coordinate system in the view; determined in this.setup()
     */
    pixelsPerUnit: null,
    /**
     * How many units across is this canvas?
     */
    unitsAcross: 100.0,

    /**
     * Sets up the properties
     * Also adds event listeners
     */
    setup: function() {
        this.canvas = document.getElementById("lab");
        this.canvas.addEventListener("mouseup",clickInLab,false);
        this.ctx = this.canvas.getContext('2d');
        this.pixelsPerUnit = [
            this.canvas.width / this.unitsAcross,
            this.canvas.height / this.unitsAcross
        ];
    },

    /**
     * Update this view.
     */
    update: function() {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);

        this.ctx.beginPath();
        this.drawDetector();
        this.ctx.closePath();

        // UI text changes

        var geigerCoordinates = "(" + geigerGameModel.detectorX + ", "
            + geigerGameModel.detectorY + ")";
        document.getElementById("takeMeasurement").innerHTML = "measure at " + geigerCoordinates;

        // window.requestAnimationFrame(geigerLabView.update);
    },

    /**
     * Draws the detector at the current location. Called by this.update().
     */
    drawDetector: function( ) {
        var tContext = this.ctx;

        tContext.save();
        tContext.strokeStyle = "#DDEEFF";
        tContext.translate(
            geigerGameModel.detectorX * this.pixelsPerUnit[0],
            this.canvas.height - geigerGameModel.detectorY * this.pixelsPerUnit[1]
        );

        tContext.beginPath();
        tContext.moveTo(0,6);
        tContext.lineTo(-6,0);
        tContext.lineTo(0, -6);
        tContext.lineTo(6,0);
        tContext.closePath();
        tContext.stroke();

        tContext.restore();
    }

};

/**
 * Model for the game.
 * @type {{sourceX: number, sourceY: number, sourceStrength: number, detectorX: number, detectorY: number, dose: number, latestCount: number, setup: Function, signalStrength: Function, doMeasurement: Function}}
 */
geigerGameModel = {
    /**
     * (secret) position of the source of radiation
     */
    sourceX: 0,
    sourceY: 0,
    /**
     * Strength of the radiation
     */
    sourceStrength: 0,
    /**
     * Current position of the detector
     */
    detectorX: 10, // Todo: HTML also sets initial values of the boxes to 10; should only be in one place.
    detectorY: 10,
    /**
     * Total dose so far
     */
    dose: 0,
    /**
     * The radiation count for the last time the detector was used
     */
    latestCount: 0,

    /**
     * Initialize model properties for a new game
     */
    newGame: function () {
        this.sourceX = (geigerLabView.unitsAcross * (0.25 + 0.50 * Math.random())).toFixed(2);
        this.sourceY = (geigerLabView.unitsAcross * (0.25 + 0.50 * Math.random())).toFixed(2); // TODO: fix vertical coordinate of source
        this.sourceStrength = 1000000;
        this.latestCount = 0;
        this.dose = 0;
    },

    /**
     * Compute the strength of the radiation at the detector. Use inverse square.
     * @returns {number}
     */
    signalStrength: function () {
        var tDsquared = (this.detectorX - this.sourceX ) * (this.detectorX - this.sourceX )
            + (this.detectorY - this.sourceY ) * (this.detectorY - this.sourceY );
        var tCount = (geigerGameModel.sourceStrength / tDsquared);
        tCount = Math.round(tCount);
        return tCount;
    },

    /**
     * Perform a measurement. Updates internal positions. Updates this.latestcount.
     * @param x     X-position of detector.
     * @param y
     */
    doMeasurement: function(  ) {
        //  this.detectorX = x;
        //  this.detectorY = y;
        var tSignal = this.signalStrength();
        this.latestCount = document.forms.geigerForm.useRandom.checked ? randomPoisson(tSignal) : tSignal;

        this.dose += this.latestCount;   // TODO: Update game case with current dose.

    }

};

/**
 * Overall controller for the game.
 *
 * @type {{initializeComponent: Function, newGame: Function, updateScreen: Function, moveDetectorTo: Function, moveDetectorByTyping: Function, doMeasurement: Function}}
 */
geigerManager = {

    /**
     * initial coordinates of the detector
     */
    initialX: 10,
    initialY: 10,
    /**
     * Initializes various items.
     */
    initializeComponent: function() {
        geigerLabView.setup();
        gauge.setup('doseGauge','Dose',0,max_dose);
        newGame();
    },

    /**
     * Resets Geiger for a new game.
     */
    newGame: function() {
        geigerGameModel.newGame();

        displayInfo("New game. Find the source!");

        this.moveDetectorTo(this.initialX, this.initialY);
        this.updateScreen();
    },

    /**
     * Calls what's necessary to refresh the screen.
     * This includes the Lab, the gauge, and any other stuff such as text.
     */
    updateScreen: function() {
        geigerLabView.update();
        gauge.update( geigerGameModel.dose);
        displayGeigerCount( geigerGameModel.latestCount);
    },

    /**
     * In charge of moving the detector.
     * Responds to clicks OR to edits in the coordinates from text boxes.
     * @param x
     * @param y
     */
    moveDetectorTo: function (x, y) {
        geigerGameModel.detectorX = x;
        geigerGameModel.detectorY = y;

        // Make sure the coordinate boxes read the right values.
        // Redundant if user has changed position by typing.
        document.getElementById('detectorX').value = x.toString();
        document.getElementById('detectorY').value = y.toString();

        displayInfo("Detector moved to (" + x + ", " + y + ")");
        this.updateScreen();
    },

    /**
     * Called when a coordinate text box loses focus (onblur).
     * Updates coordinates of the detector.
     */
    moveDetectorByTyping: function() {
        var x = document.forms.geigerForm.detectorX.value.trim();
        var y = document.forms.geigerForm.detectorY.value.trim();
        if (x < 0) x = 0;
        if (x > geigerLabView.unitsAcross) x = geigerLabView.unitsAcross;
        if (y < 0) y = 0;
        if (y > geigerLabView.unitsAcross) y = geigerLabView.unitsAcross;   // TODO: calculate y distance correctly

        this.moveDetectorTo(x, y);
    },

    /**
     * User has called for a measurement.
     * Creates a "measurement" case in CODAP.
     */
    doMeasurement: function() {
        //var tX = document.forms.geigerForm.detectorX.value.trim();
        //var tY = document.forms.geigerForm.detectorY.value.trim();

        geigerGameModel.doMeasurement(  );

        codapHelper.createCase(
            'measurements',
            [geigerGameModel.detectorX, geigerGameModel.detectorY, geigerGameModel.latestCount],
            gameCaseID
        ); // no callback?

        displayGeigerCount( geigerGameModel.latestCount ); // note: only on doMeasurement!

        this.updateScreen();
    }
};

/**
 * Required call to initialize the sim, connect it to CODAP.
 */
codapHelper.initSim({
    name: 'Geiger',
    dimensions: {width: 444, height: 600},
    collections: [  // There are two collections: a parent and a child
        {
            name: 'games',
            // The parent collection spec:
            attrs: [
                {name: "gameNumber", type: 'categorical'},
                {name: "dose", type: 'numeric', precision: 0},
                {name: "sourceX", type: 'numeric', precision: 2},
                {name: "sourceY", type: 'numeric', precision: 2}
            ],
            childAttrName: "measurement"
        },
        {
            name: 'measurements',
            labels: {
                pluralCase: "measurements",
                setOfCasesWithArticle: "a game"
            },
            // The child collection specification:
            attrs: [
                {name: "x", type: 'numeric', precision: 1},
                {name: "y", type: 'numeric', precision: 1},
                {name: "count", type: 'numeric', precision: 0}
            ]
        }
    ]
});

/**
 * Called from HTML whn user clicks in the "Lab"
 * @param e
 */
function clickInLab( e ) {
//    Note: this routine gives page coordinates. We want the coordinates in the canvas.
    if (!e) e = window.event;
    var tX = e.layerX / geigerLabView.pixelsPerUnit[0];     //  convert to units
    var tY = (geigerLabView.canvas.height - e.layerY) / geigerLabView.pixelsPerUnit[1];

    geigerManager.moveDetectorTo(tX, tY);
}

/**
 * Display function: basic info about the sim.
 * @param message {string} The string message.
 */
function    displayInfo( message ) {
    var geigerInfoText = document.getElementById('geigerInfo');
    geigerInfoText.innerHTML = message;
}

/**
 * Display function: the latest count from the geiger counter
 * @param count The number to display
 */
function    displayGeigerCount(count) {
    var geigerCountText = document.getElementById('geigerCount');
    geigerCountText.innerHTML = "count: " + (count) + " at (" +
        (geigerGameModel.detectorX) + ", " + (geigerGameModel.detectorY) + ")" +
        " Dose: " + geigerGameModel.dose + ".";

}

/**
 * A funky random Poisson function.
 * Use Knuth algorithm up to n = 100; normal approximation beyond that.
 * @param mean
 * @returns {number}
 */
function    randomPoisson( mean ) {

    if (mean > 100) {
        var sd = Math.sqrt(mean);
        var tCount = Math.round(randomNormal(mean,sd));
        return tCount;
    }
    var L = Math.exp(-mean);
    var p = 1.0;
    var k = 0;
    do {
        k++;
        p *= Math.random();
    } while (p > L);
    return (k - 1);
}

/**
 * Random normal, Box-Muller transform. Use only one value.
 * @param mean
 * @param sd
 * @returns {*}
 */
function    randomNormal(mean,sd) {
    var t1 = Math.random();
    var t2 = Math.random();

    var tZ = Math.sqrt(-2 * Math.log(t1)) * Math.cos(2 * Math.PI*t2);

    return mean + sd * tZ;
}<|endoftext|>


<commit_before>/**
 * deal.js
 *
 * Copyright (C) 2013 by Florian Holzapfel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
**/
var utils = require('./utils');

var deal = function(client, xml) {
	this.client = client;
	if(xml) {
		utils.parse_xml(this, xml.deal || xml);
	}
};
deal.prototype.delete = function(callback) {
	var url = '/deals/' + this.id + '.xml';

	this.client.send('DELETE', url, function(err, res) {
		callback(err);
	});
};
deal.prototype.get = function(id, callback) {
	this.client.get_item('/deals/' + id + '.xml', callback, deal);
};

module.exports = deal;
<commit_msg>ADD: deals<commit_after>/**
 * deal.js
 *
 * Copyright (C) 2013 by Florian Holzapfel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
**/
var utils = require('./utils');
var util = require('util');

var deal = function(client, xml) {
	this.client = client;
	if(xml) {
		utils.parse_xml(this, xml.deal || xml);
	}
};
deal.prototype.create = function(item, callback) {
	this.client.create_item('/deals.xml', utils.to_xml(item, 'deal'), callback, deal);
};
deal.prototype.delete = function(callback) {
	var url = '/deals/' + this.id + '.xml';

	this.client.send('DELETE', url, function(err, res) {
		callback(err);
	});
};
deal.prototype.get = function(id, callback) {
	this.client.get_item('/deals/' + id + '.xml', callback, deal);
};
deal.prototype.new = function(id, callback) {
	this.client.get_item('/deals/new.xml', callback, deal);
};
deal.prototype.to_xml = function(excluded_keys) {
	return utils.to_xml(this, 'deal', excluded_keys);
};
deal.prototype.update = function(callback) {
	var path = '/deals/' + this.id + '.xml';
	this.client.send('PUT', path, this.to_xml(), callback);
};
deal.prototype.update_status = function(status, callback) {
	var path = '/deals/' + this.id + '/status.xml';
	var xml = util.format('<status><name>%s</name></status>', status)
	this.client.send('PUT', path, xml, callback);
};

module.exports = deal;
<|endoftext|>


<commit_before>var Q     = require('q'),
    utils = require('./utils');

module.exports.create = function (id, client) {
  var instance = {},
      logger   = utils.logger(__filename),
      pendingMessages = {};

  /*

  */
  instance.id = 'radio.' + id;

  instance.volume = function (params) {
    return sendCommandForAction('player.volume', params);
  };

  instance.add = function (params) {
    return sendCommandForAction('playlist.add', params);
  };

  instance.play = function (params) {
    return sendCommandForAction('player.play', params);
  };

  instance.updateDatabase = function () {
    return sendCommandForAction('database.update');
  };

  client.on('message', handleClientMessage);

  function handleClientMessage(data) {
    var deferred,
        content = data.content;

    logger.debug('Incoming message', data.content);

    if (content && content.correlationId) {
      deferred = pendingMessages[content.correlationId];

      if (content.error) {
        deferred.reject(content.error);
      } else {
        deferred.resolve(content);
      }

      delete pendingMessages[content.correlationId];
    }
  }

  function sendCommandForAction(action, params) {
    params = params || {};

    var deferred = Q.defer(),
        sendPromise;

    params.action = action;
    sendPromise = client.createExchange({ exchangeName: 'radiodan' })
                        .then(function () {
                           return sendCommandToExchange(params);
                          })
                        .then(null, utils.failedPromiseHandler());

    sendPromise.then(function (command) {
      pendingMessages[command.correlationId] = deferred;
    });

    return deferred.promise;
  }

  function sendCommandToExchange(params) {
    return client.sendCommand({
      exchangeName : 'radiodan',
      topicKey     : instance.id + '.command',
      command      : params
    });
  }

  return instance;
};
<commit_msg>Expose sendCommandForAction to clients<commit_after>var Q     = require('q'),
    utils = require('./utils');

module.exports.create = function (id, client) {
  var instance = {},
      logger   = utils.logger(__filename),
      pendingMessages = {};

  /*

  */
  instance.id = 'radio.' + id;

  instance.volume = function (params) {
    return sendCommandForAction('player.volume', params);
  };

  instance.add = function (params) {
    return sendCommandForAction('playlist.add', params);
  };

  instance.play = function (params) {
    return sendCommandForAction('player.play', params);
  };

  instance.updateDatabase = function () {
    return sendCommandForAction('database.update');
  };

  instance.sendCommandForAction = sendCommandForAction;

  client.on('message', handleClientMessage);

  function handleClientMessage(data) {
    var deferred,
        content = data.content;

    logger.debug('Incoming message', data.content);

    if (content && content.correlationId) {
      deferred = pendingMessages[content.correlationId];

      if (content.error) {
        deferred.reject(content.error);
      } else {
        deferred.resolve(content);
      }

      delete pendingMessages[content.correlationId];
    }
  }

  function sendCommandForAction(action, params) {
    params = params || {};

    var deferred = Q.defer(),
        sendPromise;

    params.action = action;
    sendPromise = client.createExchange({ exchangeName: 'radiodan' })
                        .then(function () {
                           return sendCommandToExchange(params);
                          })
                        .then(null, utils.failedPromiseHandler());

    sendPromise.then(function (command) {
      pendingMessages[command.correlationId] = deferred;
    });

    return deferred.promise;
  }

  function sendCommandToExchange(params) {
    return client.sendCommand({
      exchangeName : 'radiodan',
      topicKey     : instance.id + '.command',
      command      : params
    });
  }

  return instance;
};
<|endoftext|>


<commit_before>/**
 * Created by Awesome on 1/30/2016.
 */

// use strict
'use strict';

// require dependencies
var os           = require ('os');
var path         = require ('path');
var http         = require ('http');
var child        = require ('child_process');
var redis        = require ('redis-eventemitter');
var events       = require ('events');
var colors       = require ('colors');
var express      = require ('express');
var session      = require ('express-session');
var winston      = require ('winston');
var portastic    = require ('portastic');
var mongorito    = require ('mongorito');
var bodyParser   = require ('body-parser');
var redisStore   = require ('connect-redis') (session);
var prettyError  = require ('pretty-error');
var responseTime = require ('response-time');
var cookieParser = require ('cookie-parser');

// require local dependencies
var log      = require ('lib/utilities/log');
var config   = require ('app/config');
var engine   = require ('lib/utilities/engine');
var daemons  = require ('app/cache/daemons.json');
var compiled = require ('app/cache/config.json');

/**
 * build bootstrap class
 */
class eden {
    /**
     * construct bootstrap class
     */
    constructor (opts) {
        // bind variables
        this.app    = false;
        this.port   = false;
        this.server = false;
        this.router = false;
        this.pubsub = false;
        this.events = false;

        // bind event emitter functions
        this.on   = this.on.bind (this);
        this.off  = this.off.bind (this);
        this.emit = this.emit.bind (this);

        // bind private variables
        this._ctrl   = {};
        this._daemon = {};

        // bind methods
        this.require    = this.require.bind (this);
        this.onError    = this.onError.bind (this);
        this.onListen   = this.onListen.bind (this);
        this.controller = this.controller.bind (this);

        // bind private methods
        this._getPort = this._getPort.bind (this);

        // set in global scope
        global.eden = this;

        // bind registration methods
        this._register = [
            '_registerEventEmitter',
            '_registerLogger',
            '_registerDatabase'
        ];

        // bind and run register methods
        for (var i = 0; i < this._register.length; i ++) {
            this[this._register[i]] = this[this._register[i]].bind (this);
            this[this._register[i]] (opts);
        }

        // bind build methods
        this._build = [
            '_buildApp',
            '_buildLocals',
            '_buildView',
            '_buildRouter',
            '_buildDaemons'
        ];

        // build app and server
        this._getPort ().then (() => {
            // loop build to bind build methods
            for (var i = 0; i < this._build.length; i ++) {
                // bind build method
                this[this._build[i]] = this[this._build[i]].bind (this);
                // execute build method
                this[this._build[i]] ();
            }

            // server events
            this.server.on ('error',     this.onError);
            this.server.on ('listening', this.onListen);
        });
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    //  REGISTER FUNCTIONS
    //
    ////////////////////////////////////////////////////////////////////////////

    /**
     * registers logger
     *
     * @param {Object} opts
     *
     * @private
     */
    _registerLogger (opts) {
        // set logger
        this.logger = (opts.logger || new winston.Logger ({
            level      : config.logLevel  || 'info',
            transports : [
              new (winston.transports.Console) ({
                  colorize  : true,
                  formatter : log,
                  timestamp : true
              })
            ]
        }));

        // register as global
        global.logger = this.logger;
    }

    /**
     * registers eden event emitter
     *
     * @private
     */
    _registerEventEmitter () {
        // create events redis instance
        this.pubsub = redis ({
            prefix : config.domain + ':eden:',
        });

        // create new events class
        this.events = new events ();
    }

    /**
     * registers mongodb database
     *
     * @private
     */
    _registerDatabase () {
        // connects to mongo database
        this.database = mongorito.connect (config.database[config.environment].host + '/' + config.database[config.environment].db);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    //  BUILD FUNCTIONS
    //
    ////////////////////////////////////////////////////////////////////////////

    /**
     * builds app
     *
     * @private
     */
    _buildApp () {
        // log building app
        this.logger.log ('debug', 'building express app');

        // create express app
        this.app = (this.app ? this.app : express ());

        // set port
        this.app.set ('port', this.port);

        // create http server
        this.server = (this.server ? this.server : http.createServer (this.app));

        // set port
        this.server.listen (this.port);

        // set default uses
        this.app.use (responseTime ());
        this.app.use (bodyParser.json ());
        this.app.use (bodyParser.urlencoded ({
            extended : true
        }));
        this.app.use (cookieParser (config.session));
        this.app.use (express.static ('www'));
        this.app.use (session ({
            store             : new redisStore (),
            secret            : config.session,
            resave            : false,
            saveUninitialized : true,
            key               : 'eden.session.id',
            cookie            : {
                httpOnly : false,
                secure   : false
            }
        }));

        // set error handler
        this.app.use ((err, req, res, next) => {
            // set status
            res.status (err.status || 500);

            // print error
            this.error (err);

            // render error page
            res.render ('error', {
                error   : {},
                message : err.message
            });
        });

        // set default locals
        this.app.use ((req, res, next) => {
            // set headers
            res.header ('X-Powered-By', 'EdenFrame');

            // set variables
            res.locals.route  = req.originalUrl.replace ('ajx/', '');
            res.locals.domain = config.domain;

            // set route start
            res.locals.routeStart = new Date ().getTime ();

            // go to next
            next ();
        });

        // set CORS on api
        this.app.all ('/api/*', (req, res, next) => {
            // set headers
            res.header ('Access-Control-Allow-Origin',  '*');
            res.header ('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');

            // go to next
            next ();
        });

        // set CORS on api
        this.app.all ('/ajx/*', (req, res, next) => {
            // set response to json
            res.locals.isJSON = true;

            // set header
            res.setHeader ('Content-Type', 'application/json');

            // set res.redirect
            res.redirect = (url) => {
                // render json
                res.json ({
                    'redirect' : url
                });
            };

            // go to next
            next ();
        });

        // log building app
        this.logger.log ('debug', 'finished building express app');
    }

    /**
     * builds local variables
     *
     * @private
     */
    _buildLocals () {
        // log building app
        this.logger.log ('debug', 'building app locals');

        // set locals
        this.app.locals.title  = config.title;
    }

    /**
     * builds view engine
     *
     * @private
     */
    _buildView () {
        // build view engine
        this.app.set ('views', global.appRoot + '/app/cache/views');
        this.app.set ('view engine', 'tag');
        this.app.engine ('tag', new engine ().render);
    }

    /**
     * router
     *
     * @private
     */
    async _buildRouter () {
        // set variables
        var routes = compiled.routes;

        // build router
        this.router = (this.router ? this.router : express.Router ());

        // empty controller register
        this._ctrl = {};

        // sort priorities
        var priorities = Object.keys (routes).sort ();

        // require user controller first
        var userCtrl = '/lib/bundles/user/controllers/main';
        var UserCtrl = await this.require (global.appRoot + userCtrl);

        // build required user controller
        this._ctrl[userCtrl] = new UserCtrl (this);

        // loop priorities
        for (var i = 0; i < priorities.length; i ++) {
            // let types
            let types = routes[priorities[i]];

            for (var type in types) {
                // let route type
                let routeType = types[type];

                // loop for routes
                for (var route in routeType) {
                    // await controller
                    var ctrl = await this.controller (routeType[route].controller);

                    // assign route to controller function
                    this.router[type] (route, ctrl[routeType[route].action]);
                }
            }
        }

        // set routes to app
        this.app.use ('/',    this.router);
        this.app.use ('/ajx', this.router);

        // use 404 handler
        this.app.use ((req, res) => {
            // set status 404
            res.status (404);

            // render 404 page
            res.render ('error', {
                'message' : '404 page not found'
            });
        });
    }

    /**
     * builds daemons
     *
     * @private
     */
    async _buildDaemons () {
        // set that
        var that = this;

        // loop daemons
        for (var i = 0; i < daemons.length; i++) {
            // require daemon
            var daemon = await that.require (global.appRoot + daemons[i]);

            // run daemon
            try {
                that._daemon[daemons[i]] = new daemon (that);

                // log daemon
                that.logger.log ('info', 'running daemon ' + that._daemon[daemons[i]].constructor.name);
            } catch (e) {
                // print error
                that.error (e);

                // log daemon
                that.logger.log ('error', 'daemon ' + that._daemon[daemons[i]].constructor.name + ' failed!');
            }
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    //  Getters and Setters
    //
    ////////////////////////////////////////////////////////////////////////////

    /**
     * registers port
     *
     * @private
     */
    async _getPort () {
        // return true port if clustered
        if (process.env.port) return process.env.port;

        // set variables
        var length = config.threads ? config.threads : os.cpus ().length;
        var start  = parseInt ((process.env.PORT || config.port), 10);
        var end    = start + (length - 1);

        // return promise
        var ports = await portastic.find ({
            'min' : start,
            'max' : end
        });

        // check if available port or exit
        if (!ports.length) {
            process.exit ();
        }

        // set port
        this.port = ports[0];

        // log port
        this.logger.log ('info', 'using express on port ' + this.port);

        // resolve
        return this.port;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    //  Event Functions
    //
    ////////////////////////////////////////////////////////////////////////////

    /**
     * on function
     *
     * @param  {String}   str
     * @param  {Function} fn
     * @param  {Boolean}  all
     */
    on (str, fn, all) {
        // on str/fn
        return this[all ? 'pubsub' : 'events'].on (str, fn);
    }

    /**
     * on function
     *
     * @param  {String}   str
     * @param  {Function} fn
     * @param  {Boolean}  all
     */
    off (str, fn, all) {
        // remove listener
        return this[all ? 'pubsub' : 'events'][(fn ? 'removeListener' : 'removeAllListeners')] (str, fn);
    }

    /**
     * on function
     *
     * @param  {String}  str
     * @param  {Object}  obj
     * @param  {Boolean} all
     */
    emit (str, obj, all) {
        // emit function
        return this[all ? 'pubsub' : 'events'].emit (str, obj);
    }

    /**
     * on error function
     *
     * @param error
     */
    onError (error) {
        if (error.syscall !== 'listen') {
            throw error;
        }

        // bind check
        var bind = typeof this.port === 'string' ? 'Pipe ' + this.port : 'Port ' + this.port;

        // handle specific listen errors with friendly messages
        switch (error.code) {
            case 'EACCES':
                console.error (bind + ' requires elevated privileges');
                process.exit (1);
                break;
            case 'EADDRINUSE':
                console.error (bind + ' is already in use');
                process.exit (1);
                break;
            default:
                throw error;
        }
    }

    /**
     * on listen function
     */
    onListen () {
        // get server address
        var addr = this.server.address ();

        // check if string
        var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    //  Misc Fucntions
    //
    ////////////////////////////////////////////////////////////////////////////

    /**
     * require file
     *
     * @param  {String}   file
     * @param  {Boolean}  doNew
     *
     * @return {Promise}
     */
    async require (file, doNew) {
        // log which file to require
        this.logger.log ('debug', 'requiring ' + file);

        // try catch
        try {
            // try require
            var req = require (file);

            // resolve
            return req;
        } catch (e) {
            // print error
            this.error (e);

            // exit process
            process.exit ();
        }
    }

    /**
     * run controller
     *
     * @param  {String} file
     *
     * @return {[type]}      [description]
     */
    async controller (file) {
        // set that
        var that = this;

        // try catch
        try {
            // check if controller registered
            if (!that._ctrl[file]) {
                // require controller
                var ctrl = await that.require (global.appRoot + file);

                // register controller
                that._ctrl[file] = new ctrl (this);
            }

            // resolve
            return that._ctrl[file];
        } catch (e) {
            // print error
            that.error (e);

            // exit process
            process.exit ();
        }
    }

    /**
     * pretty prints error
     *
     * @param  {Error} e
     */
    error (e) {
        // log error
        var pe = new prettyError ();

        // log error
        console.log (pe.render (e));
    }
}

/**
 * export eden bootstrap
 *
 * @type {eden}
 */
module.exports = eden;
<commit_msg>debug port config<commit_after>/**
 * Created by Awesome on 1/30/2016.
 */

// use strict
'use strict';

// require dependencies
var os           = require ('os');
var path         = require ('path');
var http         = require ('http');
var child        = require ('child_process');
var redis        = require ('redis-eventemitter');
var events       = require ('events');
var colors       = require ('colors');
var express      = require ('express');
var session      = require ('express-session');
var winston      = require ('winston');
var portastic    = require ('portastic');
var mongorito    = require ('mongorito');
var bodyParser   = require ('body-parser');
var redisStore   = require ('connect-redis') (session);
var prettyError  = require ('pretty-error');
var responseTime = require ('response-time');
var cookieParser = require ('cookie-parser');

// require local dependencies
var log      = require ('lib/utilities/log');
var config   = require ('app/config');
var engine   = require ('lib/utilities/engine');
var daemons  = require ('app/cache/daemons.json');
var compiled = require ('app/cache/config.json');

/**
 * build bootstrap class
 */
class eden {
    /**
     * construct bootstrap class
     */
    constructor (opts) {
        // bind variables
        this.app    = false;
        this.port   = false;
        this.server = false;
        this.router = false;
        this.pubsub = false;
        this.events = false;

        // bind event emitter functions
        this.on   = this.on.bind (this);
        this.off  = this.off.bind (this);
        this.emit = this.emit.bind (this);

        // bind private variables
        this._ctrl   = {};
        this._daemon = {};

        // bind methods
        this.require    = this.require.bind (this);
        this.onError    = this.onError.bind (this);
        this.onListen   = this.onListen.bind (this);
        this.controller = this.controller.bind (this);

        // bind private methods
        this._getPort = this._getPort.bind (this);

        // set in global scope
        global.eden = this;

        // bind registration methods
        this._register = [
            '_registerEventEmitter',
            '_registerLogger',
            '_registerDatabase'
        ];

        // bind and run register methods
        for (var i = 0; i < this._register.length; i ++) {
            this[this._register[i]] = this[this._register[i]].bind (this);
            this[this._register[i]] (opts);
        }

        // bind build methods
        this._build = [
            '_buildApp',
            '_buildLocals',
            '_buildView',
            '_buildRouter',
            '_buildDaemons'
        ];

        // build app and server
        this._getPort ().then (() => {
            // loop build to bind build methods
            for (var i = 0; i < this._build.length; i ++) {
                // bind build method
                this[this._build[i]] = this[this._build[i]].bind (this);
                // execute build method
                this[this._build[i]] ();
            }

            // server events
            this.server.on ('error',     this.onError);
            this.server.on ('listening', this.onListen);
        });
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    //  REGISTER FUNCTIONS
    //
    ////////////////////////////////////////////////////////////////////////////

    /**
     * registers logger
     *
     * @param {Object} opts
     *
     * @private
     */
    _registerLogger (opts) {
        // set logger
        this.logger = (opts.logger || new winston.Logger ({
            level      : config.logLevel  || 'info',
            transports : [
              new (winston.transports.Console) ({
                  colorize  : true,
                  formatter : log,
                  timestamp : true
              })
            ]
        }));

        // register as global
        global.logger = this.logger;
    }

    /**
     * registers eden event emitter
     *
     * @private
     */
    _registerEventEmitter () {
        // create events redis instance
        this.pubsub = redis ({
            prefix : config.domain + ':eden:',
        });

        // create new events class
        this.events = new events ();
    }

    /**
     * registers mongodb database
     *
     * @private
     */
    _registerDatabase () {
        // connects to mongo database
        this.database = mongorito.connect (config.database[config.environment].host + '/' + config.database[config.environment].db);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    //  BUILD FUNCTIONS
    //
    ////////////////////////////////////////////////////////////////////////////

    /**
     * builds app
     *
     * @private
     */
    _buildApp () {
        // log building app
        this.logger.log ('debug', 'building express app');

        // create express app
        this.app = (this.app ? this.app : express ());

        // set port
        this.app.set ('port', this.port);

        // create http server
        this.server = (this.server ? this.server : http.createServer (this.app));

        // set port
        this.server.listen (this.port);

        // set default uses
        this.app.use (responseTime ());
        this.app.use (bodyParser.json ());
        this.app.use (bodyParser.urlencoded ({
            extended : true
        }));
        this.app.use (cookieParser (config.session));
        this.app.use (express.static ('www'));
        this.app.use (session ({
            store             : new redisStore (),
            secret            : config.session,
            resave            : false,
            saveUninitialized : true,
            key               : 'eden.session.id',
            cookie            : {
                httpOnly : false,
                secure   : false
            }
        }));

        // set error handler
        this.app.use ((err, req, res, next) => {
            // set status
            res.status (err.status || 500);

            // print error
            this.error (err);

            // render error page
            res.render ('error', {
                error   : {},
                message : err.message
            });
        });

        // set default locals
        this.app.use ((req, res, next) => {
            // set headers
            res.header ('X-Powered-By', 'EdenFrame');

            // set variables
            res.locals.route  = req.originalUrl.replace ('ajx/', '');
            res.locals.domain = config.domain;

            // set route start
            res.locals.routeStart = new Date ().getTime ();

            // go to next
            next ();
        });

        // set CORS on api
        this.app.all ('/api/*', (req, res, next) => {
            // set headers
            res.header ('Access-Control-Allow-Origin',  '*');
            res.header ('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');

            // go to next
            next ();
        });

        // set CORS on api
        this.app.all ('/ajx/*', (req, res, next) => {
            // set response to json
            res.locals.isJSON = true;

            // set header
            res.setHeader ('Content-Type', 'application/json');

            // set res.redirect
            res.redirect = (url) => {
                // render json
                res.json ({
                    'redirect' : url
                });
            };

            // go to next
            next ();
        });

        // log building app
        this.logger.log ('debug', 'finished building express app');
    }

    /**
     * builds local variables
     *
     * @private
     */
    _buildLocals () {
        // log building app
        this.logger.log ('debug', 'building app locals');

        // set locals
        this.app.locals.title  = config.title;
    }

    /**
     * builds view engine
     *
     * @private
     */
    _buildView () {
        // build view engine
        this.app.set ('views', global.appRoot + '/app/cache/views');
        this.app.set ('view engine', 'tag');
        this.app.engine ('tag', new engine ().render);
    }

    /**
     * router
     *
     * @private
     */
    async _buildRouter () {
        // set variables
        var routes = compiled.routes;

        // build router
        this.router = (this.router ? this.router : express.Router ());

        // empty controller register
        this._ctrl = {};

        // sort priorities
        var priorities = Object.keys (routes).sort ();

        // require user controller first
        var userCtrl = '/lib/bundles/user/controllers/main';
        var UserCtrl = await this.require (global.appRoot + userCtrl);

        // build required user controller
        this._ctrl[userCtrl] = new UserCtrl (this);

        // loop priorities
        for (var i = 0; i < priorities.length; i ++) {
            // let types
            let types = routes[priorities[i]];

            for (var type in types) {
                // let route type
                let routeType = types[type];

                // loop for routes
                for (var route in routeType) {
                    // await controller
                    var ctrl = await this.controller (routeType[route].controller);

                    // assign route to controller function
                    this.router[type] (route, ctrl[routeType[route].action]);
                }
            }
        }

        // set routes to app
        this.app.use ('/',    this.router);
        this.app.use ('/ajx', this.router);

        // use 404 handler
        this.app.use ((req, res) => {
            // set status 404
            res.status (404);

            // render 404 page
            res.render ('error', {
                'message' : '404 page not found'
            });
        });
    }

    /**
     * builds daemons
     *
     * @private
     */
    async _buildDaemons () {
        // set that
        var that = this;

        // loop daemons
        for (var i = 0; i < daemons.length; i++) {
            // require daemon
            var daemon = await that.require (global.appRoot + daemons[i]);

            // run daemon
            try {
                that._daemon[daemons[i]] = new daemon (that);

                // log daemon
                that.logger.log ('info', 'running daemon ' + that._daemon[daemons[i]].constructor.name);
            } catch (e) {
                // print error
                that.error (e);

                // log daemon
                that.logger.log ('error', 'daemon ' + that._daemon[daemons[i]].constructor.name + ' failed!');
            }
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    //  Getters and Setters
    //
    ////////////////////////////////////////////////////////////////////////////

    /**
     * registers port
     *
     * @private
     */
    async _getPort () {
        // return true port if clustered
        console.log (process.env.port);
        if (process.env.port) return process.env.port;

        // set variables
        var length = config.threads ? config.threads : os.cpus ().length;
        var start  = parseInt ((process.env.PORT || config.port), 10);
        var end    = start + (length - 1);

        // return promise
        var ports = await portastic.find ({
            'min' : start,
            'max' : end
        });

        // check if available port or exit
        if (!ports.length) {
            process.exit ();
        }

        // set port
        this.port = ports[0];

        // log port
        this.logger.log ('info', 'using express on port ' + this.port);

        // resolve
        return this.port;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    //  Event Functions
    //
    ////////////////////////////////////////////////////////////////////////////

    /**
     * on function
     *
     * @param  {String}   str
     * @param  {Function} fn
     * @param  {Boolean}  all
     */
    on (str, fn, all) {
        // on str/fn
        return this[all ? 'pubsub' : 'events'].on (str, fn);
    }

    /**
     * on function
     *
     * @param  {String}   str
     * @param  {Function} fn
     * @param  {Boolean}  all
     */
    off (str, fn, all) {
        // remove listener
        return this[all ? 'pubsub' : 'events'][(fn ? 'removeListener' : 'removeAllListeners')] (str, fn);
    }

    /**
     * on function
     *
     * @param  {String}  str
     * @param  {Object}  obj
     * @param  {Boolean} all
     */
    emit (str, obj, all) {
        // emit function
        return this[all ? 'pubsub' : 'events'].emit (str, obj);
    }

    /**
     * on error function
     *
     * @param error
     */
    onError (error) {
        if (error.syscall !== 'listen') {
            throw error;
        }

        // bind check
        var bind = typeof this.port === 'string' ? 'Pipe ' + this.port : 'Port ' + this.port;

        // handle specific listen errors with friendly messages
        switch (error.code) {
            case 'EACCES':
                console.error (bind + ' requires elevated privileges');
                process.exit (1);
                break;
            case 'EADDRINUSE':
                console.error (bind + ' is already in use');
                process.exit (1);
                break;
            default:
                throw error;
        }
    }

    /**
     * on listen function
     */
    onListen () {
        // get server address
        var addr = this.server.address ();

        // check if string
        var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    //  Misc Fucntions
    //
    ////////////////////////////////////////////////////////////////////////////

    /**
     * require file
     *
     * @param  {String}   file
     * @param  {Boolean}  doNew
     *
     * @return {Promise}
     */
    async require (file, doNew) {
        // log which file to require
        this.logger.log ('debug', 'requiring ' + file);

        // try catch
        try {
            // try require
            var req = require (file);

            // resolve
            return req;
        } catch (e) {
            // print error
            this.error (e);

            // exit process
            process.exit ();
        }
    }

    /**
     * run controller
     *
     * @param  {String} file
     *
     * @return {[type]}      [description]
     */
    async controller (file) {
        // set that
        var that = this;

        // try catch
        try {
            // check if controller registered
            if (!that._ctrl[file]) {
                // require controller
                var ctrl = await that.require (global.appRoot + file);

                // register controller
                that._ctrl[file] = new ctrl (this);
            }

            // resolve
            return that._ctrl[file];
        } catch (e) {
            // print error
            that.error (e);

            // exit process
            process.exit ();
        }
    }

    /**
     * pretty prints error
     *
     * @param  {Error} e
     */
    error (e) {
        // log error
        var pe = new prettyError ();

        // log error
        console.log (pe.render (e));
    }
}

/**
 * export eden bootstrap
 *
 * @type {eden}
 */
module.exports = eden;
<|endoftext|>