<commit_before>var events = require('events');
var Q = require('q');
var util = require('util');
var sockjs = require('sockjs');

var auth = require('./auth.js');
var conf = require('./config.js');
var db = require('./db.js');
var error = require('./error.js');
var keyedset = require('./keyedset.js');
var msgid = require('./msgid.js');

// Thin wrapper over a SockJS's connection to parse things out as JSON
// and abort on error.
function Connection(conn) {
  events.EventEmitter.call(this);

  this.conn_ = conn;
  this.conn_.on('close', function() {
    this.emit('close');
  }.bind(this));
  this.conn_.on('data', function(data) {
    try {
      var message = JSON.parse(data);
    } catch (err) {
      this.conn_.close(4000, 'Bad message format');
      return;
    }
    this.emit('message', message);
  }.bind(this));
};
util.inherits(Connection, events.EventEmitter);
Connection.prototype.sockjs = function() {
  // Bah. Too lazy to expose all the properties.
  return this.conn_;
};
Connection.prototype.close = function(code, reason) {
  this.conn_.close(code, reason);
};
Connection.prototype.send = function(message) {
  this.conn_.write(JSON.stringify(message));
};

// TODO(davidben): Version the socket API.

function ConnectionManager(server, subscriber) {
  this.sockServer_ = sockjs.createServer({
    sockjs_url: '/sockjs.min.js'
  });
  this.activeUsers_ = { };
  this.subscriber_ = subscriber;

  this.sockServer_.installHandlers(server, { prefix: '/v1/socket' });

  this.sockServer_.on('connection', this.onConnection_.bind(this));
  this.subscriber_.on('message', this.onMessage_.bind(this));
}

ConnectionManager.prototype.onConnection_ = function(conn) {
  conn = new Connection(conn);
  conn.once('message', function(msg) {
    if (msg.type !== 'auth') {
      conn.close(4001, 'Auth message expected');
      return;
    }
    var token = msg.token;
    if (typeof token !== 'string') {
      conn.close(4002, 'Bad auth token');
      return;
    }
    auth.checkAuthToken(token).then(function(user) {
      if (!this.activeUsers_[user.id])
        this.activeUsers_[user.id] = new ActiveUser(this, user);

      this.activeUsers_[user.id].addSocket(conn);
      conn.send({type: 'ready'});
    }.bind(this), function(err) {
      if (err instanceof error.UserError) {
        conn.close(4003, err.msg);
      } else {
        conn.close(4004, 'Internal error');
      }
    }.bind(this)).done();
  }.bind(this));
};

ConnectionManager.prototype.onMessage_ = function(msg, userIds) {
  // Only compute the sealed id once.
  var sealedId = msgid.seal(msg.id);
  // Deliver the message to anyone who might care.
  userIds.forEach(function(userId) {
    if (!this.activeUsers_[userId])
      return;
    this.activeUsers_[userId].onMessage(msg, sealedId);
  }.bind(this));
};

function ActiveUser(parent, user) {
  this.parent_ = parent;
  this.user_ = user;

  this.ref_ = 0;
  this.sockets_ = { };
  this.activeTails_ = new keyedset.KeyedSet();
}

ActiveUser.prototype.addSocket = function(socket) {
  this.sockets_[socket.id] = socket;
  this.ref_++;

  // This stuff should possibly be enclosed in YET ANOTHER class
  // rather than a closure...
  var tails = {};

  socket.on('close', function() {
    // Shut off all the tails, so we stop sending messages through
    // them.
    for (var key in tails) {
      tails[key].close();
      delete tails[key];
    }

    delete this.sockets_[socket.id];
    // Bah.
    if (--this.ref_ <= 0)
      delete this.parent_.activeUsers_[this.user_.id];
  }.bind(this));

  socket.on('message', function(msg) {
    if (msg.type === 'new-tail') {
      var id = msg.id, start = msg.start, inclusive = msg.inclusive;
      if (typeof id !== 'number' ||
          (start != null && typeof start !== 'string')) {
        socket.close(4005, 'Bad message');
        return;
      }

      if (start != null) {
        start = msgid.unseal(start);
        if (inclusive)
          start--;
      } else {
        start = 0;
      }

      if (tails[id]) {
        // Uh, you shouldn't reuse ids, but okay...
        tails[id].close();
      }
      tails[id] = new Tail(this, socket, id, start);
    } else if (msg.type === 'extend-tail') {
      var id = msg.id, count = msg.count;
      if (typeof id !== 'number' || typeof count !== 'number') {
        socket.close(4005, 'Bad message');
        return;
      }

      if (tails[id])
        tails[id].extend(count);
    } else if (msg.type === 'close-tail') {
      var id = msg.id;
      if (typeof id !== 'number') {
        socket.close(4005, 'Bad message');
        return;
      }

      if (tails[id]) {
        tails[id].close();
        delete tails[id];
      }
    }
  }.bind(this));
};

ActiveUser.prototype.onMessage = function(msg, sealedId) {
  // Forward to each tail that is listening.
  this.activeTails_.forEach(function(tail) {
    tail.onMessage(msg, sealedId);
  });
};

function Tail(user, socket, id, lastSent) {
  // Possible states:
  //
  // - FULL-TAIL : |messagesWanted_| = 0, so there's no need to
  //   request new messages. |active_| and |messageBuffer_| should
  //   both be null. When |messagesWanted_| becomes non-zero, we fire
  //   a DB query and go into DB-WAIT.
  //
  // - DB-WAIT : |active_| is not null and |messageBuffer_| is not
  //   null. Whenever we are doing a DB query, we are also listening
  //   for live messages to do the handoff properly. They end up in
  //   |messageBuffer_|. When the DB query returns, we either go to
  //   FULL-TAIL, another instance of DB-WAIT (because the user raced
  //   with us in calling extend or we hit |db.getMessage|'s result
  //   size limit), or LIVE-STREAM
  //
  // - LIVE-STREAM : |active_| is not null and |messageBuffer_| is
  //   null. This means that our most recent DB query was done but
  //   there was still room in |messagesWanted_|. In that case, we
  //   switch to just forwarding messages straight from the
  //   subscriber. We stay in this state until |messagesWanted_| is 0
  //   and go into FULL-TAIL.

  this.user_ = user;
  this.socket_ = socket;
  this.id_ = id;

  this.active_ = null;
  this.messageBuffer_ = null;

  this.lastSent_ = lastSent;
  this.messagesSent_ = 0;
  this.messagesWanted_ = 0;
}

Tail.prototype.close = function() {
  this.socket_ = null;
  this.deactivate_();
};

Tail.prototype.extend = function(count) {
  this.messagesWanted_ = Math.max(count - this.messagesSent_,
                                  this.messagesWanted_);
  this.fireQuery_();
};

Tail.prototype.activate_ = function() {
  if (this.active_ == null) {
    this.active_ = this.user_.activeTails_.add(this);
  }
};

Tail.prototype.deactivate_ = function() {
  if (this.active_) {
    this.user_.activeTails_.removeKey(this.active_);
    this.active_ = null;
  }
};

Tail.prototype.fireQuery_ = function() {
  if (this.socket_ == null)
    return;

  // We're either in LIVE-STREAM or already in DB-WAIT. Do nothing.
  if (this.active_ != null)
    return;

  // We're in FULL-TAIL and should stay that way.
  if (this.messagesWanted_ == 0)
    return;

  // Activate live stream in buffer-messages mode.
  this.activate_();
  this.messageBuffer_ = [];
  // Make the DB query.
  db.getMessages(this.user_.user_, this.lastSent_, {
    limit: this.messagesWanted_,
    reverse: false,
    inclusive: false
  }).then(function(result) {
    if (this.socket_ == null)
      return;

    // First, send the result along.
    if (result.messages.length) {
      var lastId = result.messages[result.messages.length - 1].id;
      result.messages.forEach(function(msg) {
        msg.id = msgid.seal(msg.id);
      });
      this.emitMessages_(result.messages, result.isDone);
      this.lastSent_ = lastId;
    } else {
      this.emitMessages_([], result.isDone);
    }

    // This was (at query time) the end of the database. Now we
    // transition to LIVE-STREAM mode.
    if (result.isDone && this.messagesWanted_) {
      var messageBuffer = this.messageBuffer_;
      this.messageBuffer_ = null;

      // But first, to make the hand-off atomic, we send what messages
      // in the buffer weren't seen yet.
      var start;
      for (start = 0; start < messageBuffer.length; start++) {
        if (messageBuffer[start][0].id > this.lastSent_)
          break;
      }
      messageBuffer = messageBuffer.slice(start);
      if (messageBuffer.length > 0) {
        var sealedMsgs = messageBuffer.map(function(entry) {
          var msg = entry[0], sealedId = entry[1];
          var sealedMsg = { };
          for (var key in msg) {
            sealedMsg[key] = msg[key];
          }
          sealedMsg.id = sealedId;
          return sealedMsg;
        });
        this.emitMessages_(sealedMsgs, true);
        this.lastSent_ = messageBuffer[messageBuffer.length - 1][0].id;
      }
    } else {
      // Otherwise... we deactivate everything and check if we need to
      // fire a query again.
      this.messageBuffer_ = null;
      this.deactivate_();
      this.fireQuery_();
    }
  }.bind(this)).done();
  // TODO(davidben): Error handling!
};

Tail.prototype.onMessage = function(msg, sealedId) {
  if (!this.socket_)
    return;

  if (this.messageBuffer_) {
    this.messageBuffer_.push([msg, sealedId]);
    return;
  }
  // We're active and not in message buffering mode. Forward them
  // through the socket. isDone is true since the tail is caught up.
  var sealedMsg = { };
  for (var key in msg) {
    sealedMsg[key] = msg[key];
  }
  sealedMsg.id = sealedId;

  this.emitMessages_([sealedMsg], true);
  this.lastSent_ = msg.id;
  // Transition out of LIVE-STREAM mode if needbe.
  if (this.messagesWanted_ <= 0) {
    this.deactivate_();
  }
};

Tail.prototype.emitMessages_ = function(msgs, isDone) {
  this.socket_.send({
    type: 'messages',
    id: this.id_,
    messages: msgs,
    isDone: isDone
  });
  this.messagesSent_ += msgs.length;
  this.messagesWanted_ -= msgs.length;
};

exports.listen = function(server, messageQueue) {
  return new ConnectionManager(server, messageQueue);
};<commit_msg>Don't track anything in ActiveUsers#sockets_<commit_after>var events = require('events');
var Q = require('q');
var util = require('util');
var sockjs = require('sockjs');

var auth = require('./auth.js');
var conf = require('./config.js');
var db = require('./db.js');
var error = require('./error.js');
var keyedset = require('./keyedset.js');
var msgid = require('./msgid.js');

// Thin wrapper over a SockJS's connection to parse things out as JSON
// and abort on error.
function Connection(conn) {
  events.EventEmitter.call(this);

  this.conn_ = conn;
  this.conn_.on('close', function() {
    this.emit('close');
  }.bind(this));
  this.conn_.on('data', function(data) {
    try {
      var message = JSON.parse(data);
    } catch (err) {
      this.conn_.close(4000, 'Bad message format');
      return;
    }
    this.emit('message', message);
  }.bind(this));
};
util.inherits(Connection, events.EventEmitter);
Connection.prototype.sockjs = function() {
  // Bah. Too lazy to expose all the properties.
  return this.conn_;
};
Connection.prototype.close = function(code, reason) {
  this.conn_.close(code, reason);
};
Connection.prototype.send = function(message) {
  this.conn_.write(JSON.stringify(message));
};

// TODO(davidben): Version the socket API.

function ConnectionManager(server, subscriber) {
  this.sockServer_ = sockjs.createServer({
    sockjs_url: '/sockjs.min.js'
  });
  this.activeUsers_ = { };
  this.subscriber_ = subscriber;

  this.sockServer_.installHandlers(server, { prefix: '/v1/socket' });

  this.sockServer_.on('connection', this.onConnection_.bind(this));
  this.subscriber_.on('message', this.onMessage_.bind(this));
}

ConnectionManager.prototype.onConnection_ = function(conn) {
  conn = new Connection(conn);
  conn.once('message', function(msg) {
    if (msg.type !== 'auth') {
      conn.close(4001, 'Auth message expected');
      return;
    }
    var token = msg.token;
    if (typeof token !== 'string') {
      conn.close(4002, 'Bad auth token');
      return;
    }
    auth.checkAuthToken(token).then(function(user) {
      if (!this.activeUsers_[user.id])
        this.activeUsers_[user.id] = new ActiveUser(this, user);

      this.activeUsers_[user.id].addSocket(conn);
      conn.send({type: 'ready'});
    }.bind(this), function(err) {
      if (err instanceof error.UserError) {
        conn.close(4003, err.msg);
      } else {
        conn.close(4004, 'Internal error');
      }
    }.bind(this)).done();
  }.bind(this));
};

ConnectionManager.prototype.onMessage_ = function(msg, userIds) {
  // Only compute the sealed id once.
  var sealedId = msgid.seal(msg.id);
  // Deliver the message to anyone who might care.
  userIds.forEach(function(userId) {
    if (!this.activeUsers_[userId])
      return;
    this.activeUsers_[userId].onMessage(msg, sealedId);
  }.bind(this));
};

function ActiveUser(parent, user) {
  this.parent_ = parent;
  this.user_ = user;

  this.ref_ = 0;
  this.activeTails_ = new keyedset.KeyedSet();
}

ActiveUser.prototype.addSocket = function(socket) {
  this.ref_++;

  // This stuff should possibly be enclosed in YET ANOTHER class
  // rather than a closure...
  var tails = {};

  socket.on('close', function() {
    // Shut off all the tails, so we stop sending messages through
    // them.
    for (var key in tails) {
      tails[key].close();
      delete tails[key];
    }

    // Bah.
    if (--this.ref_ <= 0)
      delete this.parent_.activeUsers_[this.user_.id];
  }.bind(this));

  socket.on('message', function(msg) {
    if (msg.type === 'new-tail') {
      var id = msg.id, start = msg.start, inclusive = msg.inclusive;
      if (typeof id !== 'number' ||
          (start != null && typeof start !== 'string')) {
        socket.close(4005, 'Bad message');
        return;
      }

      if (start != null) {
        start = msgid.unseal(start);
        if (inclusive)
          start--;
      } else {
        start = 0;
      }

      if (tails[id]) {
        // Uh, you shouldn't reuse ids, but okay...
        tails[id].close();
      }
      tails[id] = new Tail(this, socket, id, start);
    } else if (msg.type === 'extend-tail') {
      var id = msg.id, count = msg.count;
      if (typeof id !== 'number' || typeof count !== 'number') {
        socket.close(4005, 'Bad message');
        return;
      }

      if (tails[id])
        tails[id].extend(count);
    } else if (msg.type === 'close-tail') {
      var id = msg.id;
      if (typeof id !== 'number') {
        socket.close(4005, 'Bad message');
        return;
      }

      if (tails[id]) {
        tails[id].close();
        delete tails[id];
      }
    }
  }.bind(this));
};

ActiveUser.prototype.onMessage = function(msg, sealedId) {
  // Forward to each tail that is listening.
  this.activeTails_.forEach(function(tail) {
    tail.onMessage(msg, sealedId);
  });
};

function Tail(user, socket, id, lastSent) {
  // Possible states:
  //
  // - FULL-TAIL : |messagesWanted_| = 0, so there's no need to
  //   request new messages. |active_| and |messageBuffer_| should
  //   both be null. When |messagesWanted_| becomes non-zero, we fire
  //   a DB query and go into DB-WAIT.
  //
  // - DB-WAIT : |active_| is not null and |messageBuffer_| is not
  //   null. Whenever we are doing a DB query, we are also listening
  //   for live messages to do the handoff properly. They end up in
  //   |messageBuffer_|. When the DB query returns, we either go to
  //   FULL-TAIL, another instance of DB-WAIT (because the user raced
  //   with us in calling extend or we hit |db.getMessage|'s result
  //   size limit), or LIVE-STREAM
  //
  // - LIVE-STREAM : |active_| is not null and |messageBuffer_| is
  //   null. This means that our most recent DB query was done but
  //   there was still room in |messagesWanted_|. In that case, we
  //   switch to just forwarding messages straight from the
  //   subscriber. We stay in this state until |messagesWanted_| is 0
  //   and go into FULL-TAIL.

  this.user_ = user;
  this.socket_ = socket;
  this.id_ = id;

  this.active_ = null;
  this.messageBuffer_ = null;

  this.lastSent_ = lastSent;
  this.messagesSent_ = 0;
  this.messagesWanted_ = 0;
}

Tail.prototype.close = function() {
  this.socket_ = null;
  this.deactivate_();
};

Tail.prototype.extend = function(count) {
  this.messagesWanted_ = Math.max(count - this.messagesSent_,
                                  this.messagesWanted_);
  this.fireQuery_();
};

Tail.prototype.activate_ = function() {
  if (this.active_ == null) {
    this.active_ = this.user_.activeTails_.add(this);
  }
};

Tail.prototype.deactivate_ = function() {
  if (this.active_) {
    this.user_.activeTails_.removeKey(this.active_);
    this.active_ = null;
  }
};

Tail.prototype.fireQuery_ = function() {
  if (this.socket_ == null)
    return;

  // We're either in LIVE-STREAM or already in DB-WAIT. Do nothing.
  if (this.active_ != null)
    return;

  // We're in FULL-TAIL and should stay that way.
  if (this.messagesWanted_ == 0)
    return;

  // Activate live stream in buffer-messages mode.
  this.activate_();
  this.messageBuffer_ = [];
  // Make the DB query.
  db.getMessages(this.user_.user_, this.lastSent_, {
    limit: this.messagesWanted_,
    reverse: false,
    inclusive: false
  }).then(function(result) {
    if (this.socket_ == null)
      return;

    // First, send the result along.
    if (result.messages.length) {
      var lastId = result.messages[result.messages.length - 1].id;
      result.messages.forEach(function(msg) {
        msg.id = msgid.seal(msg.id);
      });
      this.emitMessages_(result.messages, result.isDone);
      this.lastSent_ = lastId;
    } else {
      this.emitMessages_([], result.isDone);
    }

    // This was (at query time) the end of the database. Now we
    // transition to LIVE-STREAM mode.
    if (result.isDone && this.messagesWanted_) {
      var messageBuffer = this.messageBuffer_;
      this.messageBuffer_ = null;

      // But first, to make the hand-off atomic, we send what messages
      // in the buffer weren't seen yet.
      var start;
      for (start = 0; start < messageBuffer.length; start++) {
        if (messageBuffer[start][0].id > this.lastSent_)
          break;
      }
      messageBuffer = messageBuffer.slice(start);
      if (messageBuffer.length > 0) {
        var sealedMsgs = messageBuffer.map(function(entry) {
          var msg = entry[0], sealedId = entry[1];
          var sealedMsg = { };
          for (var key in msg) {
            sealedMsg[key] = msg[key];
          }
          sealedMsg.id = sealedId;
          return sealedMsg;
        });
        this.emitMessages_(sealedMsgs, true);
        this.lastSent_ = messageBuffer[messageBuffer.length - 1][0].id;
      }
    } else {
      // Otherwise... we deactivate everything and check if we need to
      // fire a query again.
      this.messageBuffer_ = null;
      this.deactivate_();
      this.fireQuery_();
    }
  }.bind(this)).done();
  // TODO(davidben): Error handling!
};

Tail.prototype.onMessage = function(msg, sealedId) {
  if (!this.socket_)
    return;

  if (this.messageBuffer_) {
    this.messageBuffer_.push([msg, sealedId]);
    return;
  }
  // We're active and not in message buffering mode. Forward them
  // through the socket. isDone is true since the tail is caught up.
  var sealedMsg = { };
  for (var key in msg) {
    sealedMsg[key] = msg[key];
  }
  sealedMsg.id = sealedId;

  this.emitMessages_([sealedMsg], true);
  this.lastSent_ = msg.id;
  // Transition out of LIVE-STREAM mode if needbe.
  if (this.messagesWanted_ <= 0) {
    this.deactivate_();
  }
};

Tail.prototype.emitMessages_ = function(msgs, isDone) {
  this.socket_.send({
    type: 'messages',
    id: this.id_,
    messages: msgs,
    isDone: isDone
  });
  this.messagesSent_ += msgs.length;
  this.messagesWanted_ -= msgs.length;
};

exports.listen = function(server, messageQueue) {
  return new ConnectionManager(server, messageQueue);
};<|endoftext|>


<commit_before>/* jshint browser: true */
/* jshint unused: false */
/* global Backbone, document, _, arangoHelper, window, setTimeout, $, templateEngine, frontendConfig*/

(function () {
  'use strict';
  window.LoginView = Backbone.View.extend({
    el: '#content',
    el2: '.header',
    el3: '.footer',
    loggedIn: false,
    loginCounter: 0,

    events: {
      'keyPress #loginForm input': 'keyPress',
      'click #submitLogin': 'validate',
      'submit #dbForm': 'goTo',
      'click #logout': 'logout',
      'change #loginDatabase': 'renderDBS'
    },

    template: templateEngine.createTemplate('loginView.ejs'),

    render: function (loggedIn) {
      var self = this;

      $(this.el).html(this.template.render({}));
      $(this.el2).hide();
      $(this.el3).hide();

      if (frontendConfig.authenticationEnabled && loggedIn !== true) {
        window.setTimeout(function () {
          $('#loginUsername').focus();
        }, 300);
      } else {
        var url = arangoHelper.databaseUrl('/_api/database/user');

        if (frontendConfig.authenticationEnabled === false) {
          $('#logout').hide();
          $('.login-window #databases').css('height', '90px');
        }

        $('#loginForm').hide();
        $('.login-window #databases').show();

        $.ajax(url).success(function (permissions) {
          // enable db select and login button
          $('#loginDatabase').html('');
          // fill select with allowed dbs

          _.each(permissions.result, function (db) {
            $('#loginDatabase').append(
              '<option>' + db + '</option>'
            );
          });

          self.renderDBS();
        }).error(function () {
          console.log('could not fetch user db data');
        });
      }

      $('.bodyWrapper').show();

      window.setTimeout(function () {
        var a = document.getElementById('loginSVG');
        var svgDoc = a.contentDocument;
        var svgItem;

        if (window.isEnterprise) {
          svgItem = svgDoc.getElementById('logo-enterprise');
        } else {
          svgItem = svgDoc.getElementById('logo-community');
        }
        svgItem.setAttribute('visibility', 'visible');
      }, 300);

      return this;
    },

    clear: function () {
      $('#loginForm input').removeClass('form-error');
      $('.wrong-credentials').hide();
    },

    keyPress: function (e) {
      if (e.ctrlKey && e.keyCode === 13) {
        e.preventDefault();
        this.validate();
      } else if (e.metaKey && e.keyCode === 13) {
        e.preventDefault();
        this.validate();
      }
    },

    validate: function (event) {
      event.preventDefault();
      this.clear();

      var username = $('#loginUsername').val();
      var password = $('#loginPassword').val();

      if (!username) {
        // do not send unneccessary requests if no user is given
        return;
      }

      this.collection.login(username, password, this.loginCallback.bind(this, username, password));
    },

    loginCallback: function (username, password, error) {
      var self = this;

      if (error) {
        if (self.loginCounter === 0) {
          self.loginCounter++;
          self.collection.login(username, password, this.loginCallback.bind(this, username));
          return;
        }
        self.loginCounter = 0;
        $('.wrong-credentials').show();
        $('#loginDatabase').html('');
        $('#loginDatabase').append(
          '<option>_system</option>'
        );
      } else {
        var url = arangoHelper.databaseUrl('/_api/user/' + encodeURIComponent(username) + '/database', '_system');

        if (frontendConfig.authenticationEnabled === false) {
          url = arangoHelper.databaseUrl('/_api/database/user');
        }

        $('.wrong-credentials').hide();
        self.loggedIn = true;

        // get list of allowed dbs
        $.ajax(url).success(function (permissions) {
          // HANDLE PERMISSIONS
          _.each(permissions.result, function (value, key) {
            if (value !== 'rw') {
              delete permissions.result[key];
            }
          });

          $('#loginForm').hide();
          $('.login-window #databases').show();

          // enable db select and login button
          $('#loginDatabase').html('');

          // fill select with allowed dbs
          _.each(permissions.result, function (db, key) {
            $('#loginDatabase').append(
              '<option>' + key + '</option>'
            );
          });

          self.renderDBS();
        }).error(function () {
          $('.wrong-credentials').show();
        });
      }
    },

    renderDBS: function () {
      if ($('#loginDatabase').children().length === 0) {
        $('#dbForm').remove();
        $('.login-window #databases').prepend(
          '
You do not have permission to a database.
'
        );
      } else {
        var db = $('#loginDatabase').val();
        $('#goToDatabase').html('Select DB: ' + db);
        window.setTimeout(function () {
          $('#goToDatabase').focus();
        }, 300);
      }
    },

    logout: function () {
      this.collection.logout();
    },

    goTo: function (e) {
      e.preventDefault();
      var username = $('#loginUsername').val();
      var database = $('#loginDatabase').val();
      window.App.dbSet = database;

      var callback2 = function (error) {
        if (error) {
          arangoHelper.arangoError('User', 'Could not fetch user settings');
        }
      };

      var path = window.location.protocol + '//' + window.location.host +
        frontendConfig.basePath + '/_db/' + database + '/_admin/aardvark/index.html';

      window.location.href = path;

      // show hidden divs
      $(this.el2).show();
      $(this.el3).show();
      $('.bodyWrapper').show();
      $('.navbar').show();

      $('#currentUser').text(username);
      this.collection.loadUserSettings(callback2);
    }

  });
}());
<commit_msg>fixed display of wrong version within the login screen<commit_after>/* jshint browser: true */
/* jshint unused: false */
/* global Backbone, document, _, arangoHelper, window, setTimeout, $, templateEngine, frontendConfig*/

(function () {
  'use strict';
  window.LoginView = Backbone.View.extend({
    el: '#content',
    el2: '.header',
    el3: '.footer',
    loggedIn: false,
    loginCounter: 0,

    events: {
      'keyPress #loginForm input': 'keyPress',
      'click #submitLogin': 'validate',
      'submit #dbForm': 'goTo',
      'click #logout': 'logout',
      'change #loginDatabase': 'renderDBS'
    },

    template: templateEngine.createTemplate('loginView.ejs'),

    render: function (loggedIn) {
      var self = this;

      $(this.el).html(this.template.render({}));
      $(this.el2).hide();
      $(this.el3).hide();

      if (frontendConfig.authenticationEnabled && loggedIn !== true) {
        window.setTimeout(function () {
          $('#loginUsername').focus();
        }, 300);
      } else {
        var url = arangoHelper.databaseUrl('/_api/database/user');

        if (frontendConfig.authenticationEnabled === false) {
          $('#logout').hide();
          $('.login-window #databases').css('height', '90px');
        }

        $('#loginForm').hide();
        $('.login-window #databases').show();

        $.ajax(url).success(function (permissions) {
          // enable db select and login button
          $('#loginDatabase').html('');
          // fill select with allowed dbs

          _.each(permissions.result, function (db) {
            $('#loginDatabase').append(
              '<option>' + db + '</option>'
            );
          });

          self.renderDBS();
        }).error(function () {
          console.log('could not fetch user db data');
        });
      }

      $('.bodyWrapper').show();
      self.checkVersion();

      return this;
    },

    checkVersion: function () {
      var self = this;
      window.setTimeout(function () {
        var a = document.getElementById('loginSVG');
        var svgDoc = a.contentDocument;
        var svgItem;

        if (frontendConfig.isEnterprise !== undefined) {
          if (frontendConfig.isEnterprise) {
            svgItem = svgDoc.getElementById('logo-enterprise');
          } else {
            svgItem = svgDoc.getElementById('logo-community');
          }
          svgItem.setAttribute('visibility', 'visible');
        } else {
          self.checkVersion();
        }
      }, 150);
    },

    clear: function () {
      $('#loginForm input').removeClass('form-error');
      $('.wrong-credentials').hide();
    },

    keyPress: function (e) {
      if (e.ctrlKey && e.keyCode === 13) {
        e.preventDefault();
        this.validate();
      } else if (e.metaKey && e.keyCode === 13) {
        e.preventDefault();
        this.validate();
      }
    },

    validate: function (event) {
      event.preventDefault();
      this.clear();

      var username = $('#loginUsername').val();
      var password = $('#loginPassword').val();

      if (!username) {
        // do not send unneccessary requests if no user is given
        return;
      }

      this.collection.login(username, password, this.loginCallback.bind(this, username, password));
    },

    loginCallback: function (username, password, error) {
      var self = this;

      if (error) {
        if (self.loginCounter === 0) {
          self.loginCounter++;
          self.collection.login(username, password, this.loginCallback.bind(this, username));
          return;
        }
        self.loginCounter = 0;
        $('.wrong-credentials').show();
        $('#loginDatabase').html('');
        $('#loginDatabase').append(
          '<option>_system</option>'
        );
      } else {
        var url = arangoHelper.databaseUrl('/_api/user/' + encodeURIComponent(username) + '/database', '_system');

        if (frontendConfig.authenticationEnabled === false) {
          url = arangoHelper.databaseUrl('/_api/database/user');
        }

        $('.wrong-credentials').hide();
        self.loggedIn = true;

        // get list of allowed dbs
        $.ajax(url).success(function (permissions) {
          // HANDLE PERMISSIONS
          _.each(permissions.result, function (value, key) {
            if (value !== 'rw') {
              delete permissions.result[key];
            }
          });

          $('#loginForm').hide();
          $('.login-window #databases').show();

          // enable db select and login button
          $('#loginDatabase').html('');

          // fill select with allowed dbs
          _.each(permissions.result, function (db, key) {
            $('#loginDatabase').append(
              '<option>' + key + '</option>'
            );
          });

          self.renderDBS();
        }).error(function () {
          $('.wrong-credentials').show();
        });
      }
    },

    renderDBS: function () {
      if ($('#loginDatabase').children().length === 0) {
        $('#dbForm').remove();
        $('.login-window #databases').prepend(
          '
You do not have permission to a database.
'
        );
      } else {
        var db = $('#loginDatabase').val();
        $('#goToDatabase').html('Select DB: ' + db);
        window.setTimeout(function () {
          $('#goToDatabase').focus();
        }, 300);
      }
    },

    logout: function () {
      this.collection.logout();
    },

    goTo: function (e) {
      e.preventDefault();
      var username = $('#loginUsername').val();
      var database = $('#loginDatabase').val();
      window.App.dbSet = database;

      var callback2 = function (error) {
        if (error) {
          arangoHelper.arangoError('User', 'Could not fetch user settings');
        }
      };

      var path = window.location.protocol + '//' + window.location.host +
        frontendConfig.basePath + '/_db/' + database + '/_admin/aardvark/index.html';

      window.location.href = path;

      // show hidden divs
      $(this.el2).show();
      $(this.el3).show();
      $('.bodyWrapper').show();
      $('.navbar').show();

      $('#currentUser').text(username);
      this.collection.loadUserSettings(callback2);
    }

  });
}());
<|endoftext|>


<commit_before>/**
 * Created by sunil on 12/25/2014.
 */

var settingsHandler = require('settingsHandler');
var uiHandler = require('uiHandler');
var storageSystem = require('storageSystem');
var self = require("sdk/self");
var data = self.data;

var jiraUrlList = {};

exports.pageController = function (message, params) {
    switch (message) {
        case 'start' :
            if(!params.spannQueryCounter)
                params.spannQueryCounter = storageSystem.getQueryCounter();

            var jiraPageUrl = params.url;
            uiHandler.openTab(jiraPageUrl, params.spannQueryCounter);
            break;

        case 'getSettings' :
            var settings = settingsHandler.getSettings();
            params.worker.port.emit('settings', settings);
            params.worker.port.emit('startProcessing');
            break;

        case 'processingComplete' :
            storageSystem.addJiraData(params.spannQueryCounter, params.data.jiraData);
            params.jiraId = params.data.jiraData.id;
            exports.throttlingController('completedJira', params);
            break;

        case 'exportToExcel' :
            exports.exportController('exportToExcel', params);
            break;

        default :
            break;
    }
};

exports.listController = function (message, params) {
    switch (message) {
        case 'start' :
            var jiraListPageUrl = params.url;
            uiHandler.openListTab(jiraListPageUrl);
            break;

        case 'getSettings' :
            var settings = settingsHandler.getSettings();
            params.worker.port.emit('settings', settings);
            params.worker.port.emit('startProcessing');
            break;

        case 'processingComplete' :
            /* empty the previously saved data */
            storageSystem.removeListDetails(params.spannQueryCounter);
            storageSystem.addJiraList(params.spannQueryCounter, params.data.jiraList);

            params.data.jiraUrlList = generateUrlList(params.data.baseUrl, params.data.jiraList);
            exports.throttlingController('start', params);
            break;

        case 'exportToExcel' :
            exports.exportController('exportToExcel', params);
            break;

        default :
            break;
    }
};

exports.throttlingController = function(message, params) {
    switch (message) {
        case 'start':
            var settings = settingsHandler.getSettings();
            jiraUrlList[params.spannQueryCounter] = params.data.jiraUrlList.slice(0);
            for (var i = 0; i < params.data.jiraUrlList.length; i++) {
                if (i === settings.numberOfJiraToProcessTogether)
                    break;
                exports.pageController('start', {url: params.data.jiraUrlList[i], spannQueryCounter: params.spannQueryCounter});
                //console.log("ForLoop: Removing "+params.data.jiraUrlList[i]+" from "+jiraUrlList[params.spannQueryCounter].toString());
                var index = jiraUrlList[params.spannQueryCounter].indexOf(params.data.jiraUrlList[i]);
                if (index > -1) {
                    jiraUrlList[params.spannQueryCounter].splice(index, 1);
                }
            }
            break;

        case 'completedJira' :
            /* removing the completed jira from remaining jira List */
            if (settingsHandler.getSettings().isEnabled) {
                if(jiraUrlList[params.spannQueryCounter] != undefined && jiraUrlList[params.spannQueryCounter].length > 0) {
                    exports.pageController('start', {url: jiraUrlList[params.spannQueryCounter][0], spannQueryCounter: params.spannQueryCounter});
                    //console.log("CompletedJira: Removing "+jiraUrlList[params.spannQueryCounter][0]+" from "+jiraUrlList[params.spannQueryCounter].toString());
                    var index = jiraUrlList[params.spannQueryCounter].indexOf(jiraUrlList[params.spannQueryCounter][0]);
                    if (index > -1) {
                        jiraUrlList[params.spannQueryCounter].splice(index, 1);
                    }
                }
            }
            break;
    }

};

exports.exportController = function (message, params) {
    switch (message) {
        case 'exportToExcel' :
            var tableHtml = storageSystem.getFinalDataHtml(params.spannQueryCounter);
            params.worker.port.emit('exportToExcel', tableHtml);
            break;
    }
};

exports.uiController = function (message, params) {
    switch (message) {
        case 'startProcessing' :
            if (settingsHandler.getSettings().isEnabled) {
                storageSystem.emptyJiraDetails();
                var dataCommaDelimited = params.data.split('\n').join(',');
                var urls = dataCommaDelimited.split(',');
                for (var i in urls) {
                    var url = urls[i];
                    var listRegex = new RegExp(".*jira.*(?:Dashboard|issues|jql|filter).*");
                    var singleJiraUrlRegex = new RegExp(".*jira.*?browse.*");
                    if (listRegex.test(url)) {
                        exports.listController('start', {url: url});
                    } else if (singleJiraUrlRegex.test(url)) {
                        exports.pageController('start', {url: url});
                    }
                }
            }
            break;

        case 'exportToExcel' :
            exports.pageController('exportToExcel', {
                worker: params.mainPanel
            });
            break;
    }
};

function generateUrlList(baseUrl, jiraList) {
    var result = [];
    for(var i in jiraList) {
        result.push(baseUrl + "browse/" + jiraList[i]);
    }
    return result;
}<commit_msg>added throttling in jiraUrlList given as input<commit_after>/**
 * Created by sunil on 12/25/2014.
 */

var settingsHandler = require('settingsHandler');
var uiHandler = require('uiHandler');
var storageSystem = require('storageSystem');
var self = require("sdk/self");
var data = self.data;

var jiraUrlList = {};

exports.pageController = function (message, params) {
    switch (message) {
        case 'start' :
            if(!params.spannQueryCounter)
                params.spannQueryCounter = storageSystem.getQueryCounter();

            var jiraPageUrl = params.url;
            uiHandler.openTab(jiraPageUrl, params.spannQueryCounter);
            break;

        case 'getSettings' :
            var settings = settingsHandler.getSettings();
            params.worker.port.emit('settings', settings);
            params.worker.port.emit('startProcessing');
            break;

        case 'processingComplete' :
            storageSystem.addJiraData(params.spannQueryCounter, params.data.jiraData);
            exports.throttlingController('completedJira', params);
            break;

        case 'exportToExcel' :
            exports.exportController('exportToExcel', params);
            break;

        default :
            break;
    }
};

exports.listController = function (message, params) {
    switch (message) {
        case 'start' :
            var jiraListPageUrl = params.url;
            uiHandler.openListTab(jiraListPageUrl);
            break;

        case 'getSettings' :
            var settings = settingsHandler.getSettings();
            params.worker.port.emit('settings', settings);
            params.worker.port.emit('startProcessing');
            break;

        case 'processingComplete' :
            /* empty the previously saved data */
            storageSystem.removeListDetails(params.spannQueryCounter);
            storageSystem.addJiraList(params.spannQueryCounter, params.data.jiraList);

            params.data.jiraUrlList = generateUrlList(params.data.baseUrl, params.data.jiraList);
            exports.throttlingController('start', params);
            break;

        case 'exportToExcel' :
            exports.exportController('exportToExcel', params);
            break;

        default :
            break;
    }
};

exports.throttlingController = function(message, params) {
    switch (message) {
        case 'start':
            var settings = settingsHandler.getSettings();
            jiraUrlList[params.spannQueryCounter] = params.data.jiraUrlList.slice(0);
            for (var i = 0; i < params.data.jiraUrlList.length; i++) {
                if (i === settings.numberOfJiraToProcessTogether)
                    break;
                exports.pageController('start', {url: params.data.jiraUrlList[i], spannQueryCounter: params.spannQueryCounter});
                //console.log("ForLoop: Removing "+params.data.jiraUrlList[i]+" from "+jiraUrlList[params.spannQueryCounter].toString());
                var index = jiraUrlList[params.spannQueryCounter].indexOf(params.data.jiraUrlList[i]);
                if (index > -1) {
                    jiraUrlList[params.spannQueryCounter].splice(index, 1);
                }
            }
            break;

        case 'completedJira' :
            /* removing the completed jira from remaining jira List */
            if (settingsHandler.getSettings().isEnabled) {
                if(jiraUrlList[params.spannQueryCounter] != undefined && jiraUrlList[params.spannQueryCounter].length > 0) {
                    exports.pageController('start', {url: jiraUrlList[params.spannQueryCounter][0], spannQueryCounter: params.spannQueryCounter});
                    //console.log("CompletedJira: Removing "+jiraUrlList[params.spannQueryCounter][0]+" from "+jiraUrlList[params.spannQueryCounter].toString());
                    var index = jiraUrlList[params.spannQueryCounter].indexOf(jiraUrlList[params.spannQueryCounter][0]);
                    if (index > -1) {
                        jiraUrlList[params.spannQueryCounter].splice(index, 1);
                    }
                }
            }
            break;
    }

};

exports.exportController = function (message, params) {
    switch (message) {
        case 'exportToExcel' :
            var tableHtml = storageSystem.getFinalDataHtml(params.spannQueryCounter);
            params.worker.port.emit('exportToExcel', tableHtml);
            break;
    }
};

exports.uiController = function (message, params) {
    switch (message) {
        case 'startProcessing' :
            if (settingsHandler.getSettings().isEnabled) {

                storageSystem.emptyJiraDetails();
                var dataCommaDelimited = params.data.split('\n').join(',');
                var urls = dataCommaDelimited.split(',');

                var inputJiraUrlList = [];
                for (var i in urls) {
                    var url = urls[i];

                    if (/.*jira.*(?:Dashboard|issues|jql|filter).*/.test(url)) {
                        exports.listController('start', {url: url});
                    } else if (/.*jira.*?browse.*/.test(url)) {
                        inputJiraUrlList.push(url);
                    }
                }
                exports.throttlingController('start', {
                    data: {
                        jiraUrlList: inputJiraUrlList
                    },
                    spannQueryCounter: storageSystem.getQueryCounter()
                });
            }
            break;

        case 'exportToExcel' :
            exports.pageController('exportToExcel', {
                worker: params.mainPanel
            });
            break;
    }
};

function generateUrlList(baseUrl, jiraList) {
    var result = [];
    for(var i in jiraList) {
        result.push(baseUrl + "browse/" + jiraList[i]);
    }
    return result;
}<|endoftext|>


<commit_before>/* convolution layer

	Weblas Functions Used

	* slokn
	* sgemm
*/
var weblas = require('weblas'),
	Tensor = weblas.pipeline.Tensor,
	type = require('type-detect');

function Convolution(kernels, size, count, stride, bias, margin){
	var N, K;

	if(type(bias) === "Tensor"){
		this.bias = bias;
		console.assert(this.bias.shape[0] === 1, "Bias Tensor must have first dimension of length one.");
		K = this.bias.shape[1];
	} else {
		K = bias.length;
		this.bias = new Tensor([1, K], bias);
	}

	// transpose weights are required
	if(type(kernels) === "Tensor"){
		console.assert(this.kernels.shape[1] === K, "Kernels and bias Tensors must have same second dimension.");
		this.kernels = kernels.transpose();
	} else {
		N = (kernels.length / K) | 0;
		console.assert(K * N === kernels.length, "Weights array must have length equal to a multiple of bias array length.");

		this.kernels = new Tensor([N, K], kernels).transpose();
	}

	// kernel patch size
	this.size = size;
	// kernel count
	this.count = count;

	this.stride = stride;
	this.margin = margin;
}

module.exports = Convolution;

Convolution.prototype.forward = function(input, M, N, channels){

	var T_in;
	// is the input a Tensor?
	if(type(input) === "Tensor"){
		// yes, use or reshape it
		if(input.shape[0] !== M || input.shape[1] !== N * channels) {
			T_in = input.reshape([M, N * channels]);
		} else {
			T_in = input;
		}
	} else {
		// no, create a Tensor (uploads data to GPU)
		T_in = new weblas.pipeline.Tensor([M, N * channels], input);
	}

	// linearize onto kernels
	var T_lin = weblas.pipeline.slokn(channels, this.size, this.stride, this.margin, T_in);

	// do the matrix multiply
	//sgemm(n, m, k, scale, bBuffer, aBuffer, scale, bias);
	var output = weblas.pipeline.sgemm(1.0, T_lin, this.kernels, 1.0, this.bias);

	// if we created the Tensor, delete it
	if(type(input) !== "Tensor"){
		T_in.delete();
	}

	return output;
}
<commit_msg>require transpose kernels<commit_after>/* convolution layer

	Weblas Functions Used

	* slokn
	* sgemm
*/
var weblas = require('weblas'),
	Tensor = weblas.pipeline.Tensor,
	type = require('type-detect');
/* Create a convolution layer
 *
 * kernels must be input in transposed form
 */
function Convolution(kernels, size, count, stride, bias, margin){
	var N, K;

	if(type(bias) === "Tensor"){
		this.bias = bias;
		console.assert(this.bias.shape[0] === 1, "Bias Tensor must have first dimension of length one.");
		K = this.bias.shape[1];
	} else {
		K = bias.length;
		this.bias = new Tensor([1, K], bias);
	}

	// transpose kernels are required
	if(type(kernels) === "Tensor"){
		console.assert(this.kernels.shape[0] === K, "Kernel Tensor first dimension must equal Bias Tensor second dimension.");
		this.kernels = kernels;
	} else {
		N = (kernels.length / K) | 0;
		console.assert(K * N === kernels.length, "Kernel Array must have length equal to a multiple of bias array length.");

		this.kernels = new Tensor([K, N], kernels);
	}

	// kernel patch size
	this.size = size;
	// kernel count
	this.count = count;

	// stride between kernel patches
	this.stride = stride;
	// margin to add
	this.margin = margin;
}

module.exports = Convolution;

Convolution.prototype.forward = function(input, M, N, channels){

	var T_in;
	// is the input a Tensor?
	if(type(input) === "Tensor"){
		// yes, use or reshape it
		if(input.shape[0] !== M || input.shape[1] !== N * channels) {
			T_in = input.reshape([M, N * channels]);
		} else {
			T_in = input;
		}
	} else {
		// no, create a Tensor (uploads data to GPU)
		T_in = new weblas.pipeline.Tensor([M, N * channels], input);
	}

	// linearize onto kernels
	var T_lin = weblas.pipeline.slokn(channels, this.size, this.stride, this.margin, T_in);

	// do the matrix multiply
	//sgemm(n, m, k, scale, bBuffer, aBuffer, scale, bias);
	var output = weblas.pipeline.sgemm(1.0, T_lin, this.kernels, 1.0, this.bias);

	// if we created the Tensor, delete it
	if(type(input) !== "Tensor"){
		T_in.delete();
	}

	return output;
}
<|endoftext|>


<commit_before>({
    mainConfigFile: 'main.js',
    optimize: 'uglify',
    removeCombined: true,
    modules: [
        {name: 'backbone-tastypie'},
        {name: 'lakaxita/app'},
    ],
})
<commit_msg>include backbone in optimized backbone-tastypie<commit_after>({
    mainConfigFile: 'main.js',
    optimize: 'uglify',
    removeCombined: true,
    modules: [
        {
            name: 'backbone-tastypie',
            include: ['backbone'],
        },
        {
            name: 'lakaxita/app',
        },
    ],
})
<|endoftext|>


<commit_before>import { isString, isArray, isFunction } from './util';

const resolved = {};

/**
 * Load javascript url asynchronously.
 *
 * @param {*} url               The javascript url to load.
 * @param {*} callback          Callback function to invoke on success.
 * @param {*} errorCallback     Callback function to invoke on error.
 * @returns {void}
 */
function loadScript(url, callback, errorCallback) {
  const invokeCallback = () => {
    resolved[url] = true;

    if (isFunction(callback)) {
      callback();
    }
  };

  if (resolved[url]) {
    invokeCallback();

    return;
  }

  const script = document.createElement('script');

  script.type = 'text/javascript';

  if (script.readyState) {
    // IE
    script.onreadystatechange = function() {
      if (script.readyState == 'loaded' || script.readyState == 'complete') {
        script.onreadystatechange = null;
        invokeCallback();
      }
    };
  } else {
    // Others
    script.onload = function() {
      invokeCallback();
    };
  }

  script.onerror = function(e) {
    resolved[url] = false;
    console.error(e);

    if (isFunction(errorCallback)) {
      errorCallback();
    }
  };

  script.src = url;
  const parent = document.body || document.head || document;

  parent.appendChild(script);
}

/**
 * Load javascript url (or a list of urls) asynchronously
 * and return a Promise instance.
 *
 * @param {*} src
 * @returns {Promise}
 */
function get(src) {
  if (isString(src)) {
    return new Promise((resolve, reject) => {
      loadScript(src, () => resolve(true), () => reject());
    });
  }

  if (isArray(src)) {
    let p = Promise.resolve(true);

    src.forEach(url => {
      p = p.then(() => get(url));
    });

    return p;
  }

  throw new Error('Invalid argument for get()');
}

export default get;
<commit_msg>Use arrow functions<commit_after>import { isString, isArray, isFunction } from './util';

const resolved = {};

/**
 * Load javascript url asynchronously.
 *
 * @param {*} url               The javascript url to load.
 * @param {*} callback          Callback function to invoke on success.
 * @param {*} errorCallback     Callback function to invoke on error.
 * @returns {void}
 */
function loadScript(url, callback, errorCallback) {
  const invokeCallback = () => {
    resolved[url] = true;

    if (isFunction(callback)) {
      callback();
    }
  };

  if (resolved[url]) {
    invokeCallback();

    return;
  }

  const script = document.createElement('script');

  script.type = 'text/javascript';

  if (script.readyState) {
    // IE
    script.onreadystatechange = () => {
      if (script.readyState == 'loaded' || script.readyState == 'complete') {
        script.onreadystatechange = null;
        invokeCallback();
      }
    };
  } else {
    // Others
    script.onload = () => {
      invokeCallback();
    };
  }

  script.onerror = e => {
    resolved[url] = false;
    console.error(e);

    if (isFunction(errorCallback)) {
      errorCallback();
    }
  };

  script.src = url;
  const parent = document.body || document.head || document;

  parent.appendChild(script);
}

/**
 * Load javascript url (or a list of urls) asynchronously
 * and return a Promise instance.
 *
 * @param {*} src
 * @returns {Promise}
 */
function get(src) {
  if (isString(src)) {
    return new Promise((resolve, reject) => {
      loadScript(src, () => resolve(true), () => reject());
    });
  }

  if (isArray(src)) {
    let p = Promise.resolve(true);

    src.forEach(url => {
      p = p.then(() => get(url));
    });

    return p;
  }

  throw new Error('Invalid argument for get()');
}

export default get;
<|endoftext|>


<commit_before>#!/usr/bin/env node
// Usage:  ghw <input> <output dir>
// where <input> is either a file or a dir.
var VERSION = '0.0.1'

var fs = require('fs');
var marked = require('marked');

var args = process.argv.splice(2);
var input = args[0];
var output = args[1];

console.log('ghw ' + VERSION);

if(!input) {
    console.log('Missing input and output!');

    return;
}

if(!output) {
    console.log('Missing output!');

    return;
}

console.log(input, output);

// TODO: load them to some nice data structure
// TODO: define conversions
// TODO: write them to HTML

function parseFile(path) {
    fs.readFile(path, 'utf-8', function(err, data) {
        if (err) throw err;

        // TODO: convert on parse
        // TODO: once parsing has been done, load to data structure
        // using marked.lexer
        // TODO: once that's ok, write to output
    });
}

<commit_msg>Hook up marked lexer<commit_after>#!/usr/bin/env node
// Usage:  ghw <input> <output dir>
// where <input> is either a file or a dir.
var VERSION = '0.0.1'

var fs = require('fs');
var marked = require('marked');

var args = process.argv.splice(2);
var input = args[0];
var output = args[1];

console.log('ghw ' + VERSION);

if(!input) {
    console.log('Missing input and output!');

    return;
}

if(!output) {
    console.log('Missing output!');

    return;
}

console.log(input, output);

parseFile(input);

function parseFile(path) {
    fs.readFile(path, 'utf-8', function(err, data) {
        if (err) throw err;

        var tokens = marked.lexer(data);
        console.log(tokens);

        // TODO: convert links to something sensible
        // TODO: write to HTML
    });
}

<|endoftext|>


<commit_before>(function() {

   var ChildProcess = require('child_process');

   /**
    * Git handling for node. All public functions can be chained and all `then` handlers are optional.
    *
    * @param {String} baseDir
    * @constructor
    */
   function Git(baseDir) {
      this._baseDir = baseDir;
      this._runCache = [];
   }

   /**
    * Initalize a git repo
    *
    * @param {Function} [then]
    */
   Git.prototype.init = function(then) {
      return this._run('git init', function(err) {
         then && then(err);
      });
   };

   /**
    * Clone a git repo
    *
    * @param {String} repoPath
    * @param {String} localPath
    * @param {Function} [then]
    */
   Git.prototype.clone = function(repoPath, localPath, then ) {
      return this._run('git clone ' + repoPath + ' ' + localPath, function(err) {
         then && then(err);
      });
   };

   /**
    * Internally uses pull and tags to get the list of tags then checks out the latest tag.
    *
    * @param {Function} [then]
    */
   Git.prototype.checkoutLatestTag = function(then) {
      var git = this;
      return this.pull().tags(function(err, tags) {
         git.checkout(tags.latest, then);
      });
   };

   /**
    * Adds one or more files to source control
    *
    * @param {String|String[]} files
    * @param {Function} [then]
    */
   Git.prototype.add = function(files, then) {
      return this._run('git add "' + [].concat(files).join('" "') + '"', function(err, data) {
         then && then(err);
      });
   };

   /**
    * Commits changes in the current working directory - when specific file paths are supplied, only changes on those
    * files will be committed.
    *
    * @param {String} message
    * @param {String|String[]} [files]
    * @param {Function} [then]
    */
   Git.prototype.commit = function(message, files, then) {
      var git = this;
      if(!then && typeof files === "function") {
         then = files;
         files = [];
      }

      files = files ? ' "' + [].concat(files).join('" "') + '"' : '';

      return this._run('git commit -m "' + message.replace(/"/g, '\\"') + '"' + files, function(err, data) {
         then && then(err, !err && git._parseCommit(data));
      });
   };

   /**
    * Pull the updated contents of the current repo
    * @param {String} [remote]
    * @param {String} [branch]
    * @param {Function} [then]
    */
   Git.prototype.pull = function(remote, branch, then) {
      var command = "git pull";
      if (typeof remote === 'string' && typeof branch === 'string') {
         command += ' "' + remote + '" "' + branch + '"';
      }
      if (typeof arguments[arguments.length - 1] === 'function') {
         then = arguments[arguments.length - 1];
      }

      return this._run(command, function(err, data) {
         then && then(err, !err && this._parsePull(data));
      });
   };

   /**
    * Fetch the updated contents of the current repo.
    *
    * @example
    *   .fetch('upstream', 'master') // fetches from master on remote named upstream
    *   .fetch(function () {}) // runs fetch against default remote and branch and calls function
    *
    * @param {String} [remote]
    * @param {String} [branch]
    * @param {Function} [then]
    */
   Git.prototype.fetch = function(remote, branch, then) {
      var command = "git fetch";
      if (typeof remote === 'string' && typeof branch === 'string') {
         command += ' "' + remote + '" "' + branch + '"';
      }
      if (typeof arguments[arguments.length - 1] === 'function') {
         then = arguments[arguments.length - 1];
      }

      return this._run(command, function(err, data) {
         then && then(err, !err && this._parsePush(data));
      });
   };

   /**
    * List all tags
    *
    * @param {Function} [then]
    */
   Git.prototype.tags = function(then) {
      return this._run('git tag -l', function(err, data) {
         then && then(err, !err && this._parseListTags(data));
      });
   };

   /**
    * Check out a tag or revision
    *
    * @param {String} what
    * @param {Function} [then]
    */
   Git.prototype.checkout = function(what, then) {
      return this._run('git checkout "' + what + '"', function(err, data) {
         then && then(err, !err && this._parseCheckout(data));
      });
   };

   /**
    * Add a submodule
    *
    * @param {String} repo
    * @param {String} path
    * @param {Function} [then]
    */
   Git.prototype.submoduleAdd = function(repo, path, then) {
      return this._run('git submodule add "' + repo + '" "' + path + '"', function(err) {
         then && then(err);
      });
   };

   /**
    * List remote
    *
    * @param {String} [args]
    * @param {Function} [then]
    */
   Git.prototype.listRemote = function(args, then) {
      if (!then && typeof args === "function") {
         then = args;
         args = '';
      }
      return this._run('git ls-remote ' + args, function(err, data) {
         then && then(err, data);
      });
   };

   /**
    * Adds a remote to the list of remotes.
    *
    * @param {String} remoteName Name of the repository - eg "upstream"
    * @param {String} remoteRepo Fully qualified SSH or HTTP(S) path to the remote repo
    * @param {Function} [then]
    * @returns {*}
    */
   Git.prototype.addRemote = function(remoteName, remoteRepo, then) {
      return this._run('git remote add "' + remoteName + '" "' + remoteRepo + '"', function (err) {
         then && then(err);
      });
   };

   /**
    * Removes an entry from the list of remotes.
    *
    * @param {String} remoteName Name of the repository - eg "upstream"
    * @param {Function} [then]
    * @returns {*}
    */
   Git.prototype.removeRemote = function(remoteName, then) {
      return this._run('git remote remove "' + remoteName + '"', function (err) {
         then && then(err);
      });
   };

   /**
    * Pushes the current committed changes to a remote, optionally specify the names of the remote and branch to use
    * when pushing.
    *
    * @param {String} [remote]
    * @param {String} [branch]
    * @param {Function} [then]
    */
   Git.prototype.push = function(remote, branch, then) {
      var command = "git push";
      if (typeof remote === 'string' && typeof branch === 'string') {
         command += ' "' + remote + '" "' + branch + '"';
      }
      if (typeof arguments[arguments.length - 1] === 'function') {
         then = arguments[arguments.length - 1];
      }

      return this._run(command, function(err, data) {
         then && then(err, !err && this._parsePush(data));
      });
   };

   /**
    * Removes the named files from source control.
    *
    * @param {String|String[]} files
    * @param {Function} [then]
    */
   Git.prototype.rm = function(files, then) {
      return this._rm(files, '-f', then);
   };

   /**
    * Removes the named files from source control but keeps them on disk rather than deleting them entirely. To
    * completely remove the files, use `rm`.
    *
    * @param {String|String[]} files
    * @param {Function} [then]
    */
   Git.prototype.rmKeepLocal = function(files, then) {
      return this._rm(files, '--cached', then);
   };

   /**
    * Return repository changes.
    *
    * @param {String} [options]
    * @param {Function} [then]
    */
   Git.prototype.diff = function(options, then) {
      var command = 'git diff';

      if (typeof options === 'string' && typeof then === 'function') {
         command += ' ' + options;
      }

      if (typeof arguments[arguments.length - 1] === 'function') {
         then = arguments[arguments.length - 1];
      }

      return this._run(command, function(err, data) {
         then && then(err, data);
      });
   };

   Git.prototype._rm = function(files, options, then) {
      return this._run('git rm ' + options + ' "' + [].concat(files).join('" "') + '"', function(err) {
         then && then(err);
      });
   };

   Git.prototype._parsePull = function(pull) {
      var changes = {
         files: [],
         insertions: {},
         deletions: {},
         summary: {
            changes: 0,
            insertions: 0,
            deletions: 0
         }
      };

      var fileUpdateRegex = /^\s*(.+)\s\|\s(\d+)\s([\+]+)/;
      for(var lines = pull.split('\n'), i = 0, l = lines.length; i < l; i++) {
         var update = fileUpdateRegex.exec(lines[i]);

         // search for update statement for each file
         if(update) {
            changes.files.push(update[1]);

            var insertions = update[3].length;
            if(insertions) {
               changes.insertions[update[1]] = insertions;
            }
            if(update[2] > insertions) {
               changes.deletions[update[1]] = update[2] - insertions;
            }
         }

         // summary appears after updates
         else if(changes.files.length && (update = /(\d+)\D+(\d+)\D+(\d+)/.exec(lines[i]))) {
            changes.summary.changes = +update[1];
            changes.summary.insertions = +update[2];
            changes.summary.deletions = +update[3];
         }
      }

      return changes;
   };

   Git.prototype._parseListTags = function(tags) {
      var tagList = tags.split('\n').sort(function(tagA, tagB) {
         var partsA = tagA.split('.');
         var partsB = tagB.split('.');

         for(var i = 0, l = Math.max(partsA.length, partsB.length); i < l; i++) {
            var diff = partsA[i] - partsB[i];
            if(diff) {
               return diff > 0 ? 1 : -1;
            }
         }

         return 0;
      });

      return {
         latest: tagList.length && tagList[tagList.length - 1],
         all: tagList
      };
   };

   Git.prototype._parseCommit = function(commit) {
      var lines = commit.trim().split('\n');
      var branch = /\[([^\s]+) ([^\]]+)/.exec(lines.shift());
      var summary = /(\d+)[^,]*(?:,\s*(\d+)[^,]*)?(?:,\s*(\d+))?/g.exec(lines.shift()) || [];

      return {
         branch: branch[1],
         commit: branch[2],
         summary: {
            changes: (typeof summary[1] !== 'undefined') ? summary[1] : 0,
            insertions: (typeof summary[2] !== 'undefined') ? summary[2] : 0,
            deletions: (typeof summary[3] !== 'undefined') ? summary[3] : 0
         }
      };
   };

   Git.prototype._parseCheckout = function(checkout) {
      // TODO
   };

   Git.prototype._parseFetch = function(fetch) {
      return data;
   };

   Git.prototype._run = function(command, then) {
      this._runCache.push([command, then]);
      this._schedule();

      return this;
   };

   Git.prototype._schedule = function() {
      if(!this._childProcess && this._runCache.length) {
         var task = this._runCache.shift();
         var command = task[0];
         var then = task[1];

         this._childProcess = ChildProcess.exec(
             command,
             {cwd: this._baseDir},
             function(err, stdout, stderr) {
                delete this._childProcess;

                if(err) {
                   console.error(stderr);
                   this._runCache = [];
                   then.call(this, err, null);
                }
                else {
                   then.call(this, null, stdout);
                }

                process.nextTick(this._schedule.bind(this));
             }.bind(this));
      }
   };

   module.exports = function(baseDir) {
      return new Git(baseDir || process.cwd());
   };

}());
<commit_msg>Update git.js<commit_after>(function() {

   var ChildProcess = require('child_process');

   /**
    * Git handling for node. All public functions can be chained and all `then` handlers are optional.
    *
    * @param {String} baseDir
    * @constructor
    */
   function Git(baseDir) {
      this._baseDir = baseDir;
      this._runCache = [];
   }

   /**
    * Initalize a git repo
    *
    * @param {Function} [then]
    */
   Git.prototype.init = function(then) {
      return this._run('git init', function(err) {
         then && then(err);
      });
   };

   /**
    * Clone a git repo
    *
    * @param {String} repoPath
    * @param {String} localPath
    * @param {Function} [then]
    */
   Git.prototype.clone = function(repoPath, localPath, then ) {
      return this._run('git clone ' + repoPath + ' ' + localPath, function(err) {
         then && then(err);
      });
   };

   /**
    * Internally uses pull and tags to get the list of tags then checks out the latest tag.
    *
    * @param {Function} [then]
    */
   Git.prototype.checkoutLatestTag = function(then) {
      var git = this;
      return this.pull().tags(function(err, tags) {
         git.checkout(tags.latest, then);
      });
   };

   /**
    * Adds one or more files to source control
    *
    * @param {String|String[]} files
    * @param {Function} [then]
    */
   Git.prototype.add = function(files, then) {
      return this._run('git add "' + [].concat(files).join('" "') + '"', function(err, data) {
         then && then(err);
      });
   };

   /**
    * Commits changes in the current working directory - when specific file paths are supplied, only changes on those
    * files will be committed.
    *
    * @param {String} message
    * @param {String|String[]} [files]
    * @param {Function} [then]
    */
   Git.prototype.commit = function(message, files, then) {
      var git = this;
      if(!then && typeof files === "function") {
         then = files;
         files = [];
      }

      files = files ? ' "' + [].concat(files).join('" "') + '"' : '';

      return this._run('git commit -m "' + message.replace(/"/g, '\\"') + '"' + files, function(err, data) {
         then && then(err, !err && git._parseCommit(data));
      });
   };

   /**
    * Pull the updated contents of the current repo
    * @param {String} [remote]
    * @param {String} [branch]
    * @param {Function} [then]
    */
   Git.prototype.pull = function(remote, branch, then) {
      var command = "git pull";
      if (typeof remote === 'string' && typeof branch === 'string') {
         command += ' "' + remote + '" "' + branch + '"';
      }
      if (typeof arguments[arguments.length - 1] === 'function') {
         then = arguments[arguments.length - 1];
      }

      return this._run(command, function(err, data) {
         then && then(err, !err && this._parsePull(data));
      });
   };

   /**
    * Fetch the updated contents of the current repo.
    *
    * @example
    *   .fetch('upstream', 'master') // fetches from master on remote named upstream
    *   .fetch(function () {}) // runs fetch against default remote and branch and calls function
    *
    * @param {String} [remote]
    * @param {String} [branch]
    * @param {Function} [then]
    */
   Git.prototype.fetch = function(remote, branch, then) {
      var command = "git fetch";
      if (typeof remote === 'string' && typeof branch === 'string') {
         command += ' "' + remote + '" "' + branch + '"';
      }
      if (typeof arguments[arguments.length - 1] === 'function') {
         then = arguments[arguments.length - 1];
      }

      return this._run(command, function(err, data) {
         then && then(err, !err && this._parsePush(data));
      });
   };

   /**
    * List all tags
    *
    * @param {Function} [then]
    */
   Git.prototype.tags = function(then) {
      return this._run('git tag -l', function(err, data) {
         then && then(err, !err && this._parseListTags(data));
      });
   };

   /**
    * Check out a tag or revision
    *
    * @param {String} what
    * @param {Function} [then]
    */
   Git.prototype.checkout = function(what, then) {
      return this._run('git checkout "' + what + '"', function(err, data) {
         then && then(err, !err && this._parseCheckout(data));
      });
   };

   /**
    * Add a submodule
    *
    * @param {String} repo
    * @param {String} path
    * @param {Function} [then]
    */
   Git.prototype.submoduleAdd = function(repo, path, then) {
      return this._run('git submodule add "' + repo + '" "' + path + '"', function(err) {
         then && then(err);
      });
   };

   /**
    * List remote
    *
    * @param {String} [args]
    * @param {Function} [then]
    */
   Git.prototype.listRemote = function(args, then) {
      if (!then && typeof args === "function") {
         then = args;
         args = '';
      }
      return this._run('git ls-remote ' + args, function(err, data) {
         then && then(err, data);
      });
   };

   /**
    * Adds a remote to the list of remotes.
    *
    * @param {String} remoteName Name of the repository - eg "upstream"
    * @param {String} remoteRepo Fully qualified SSH or HTTP(S) path to the remote repo
    * @param {Function} [then]
    * @returns {*}
    */
   Git.prototype.addRemote = function(remoteName, remoteRepo, then) {
      return this._run('git remote add "' + remoteName + '" "' + remoteRepo + '"', function (err) {
         then && then(err);
      });
   };

   /**
    * Removes an entry from the list of remotes.
    *
    * @param {String} remoteName Name of the repository - eg "upstream"
    * @param {Function} [then]
    * @returns {*}
    */
   Git.prototype.removeRemote = function(remoteName, then) {
      return this._run('git remote remove "' + remoteName + '"', function (err) {
         then && then(err);
      });
   };

   /**
    * Pushes the current committed changes to a remote, optionally specify the names of the remote and branch to use
    * when pushing.
    *
    * @param {String} [remote]
    * @param {String} [branch]
    * @param {Function} [then]
    */
   Git.prototype.push = function(remote, branch, then) {
      var command = "git push";
      if (typeof remote === 'string' && typeof branch === 'string') {
         command += ' "' + remote + '" "' + branch + '"';
      }
      if (typeof arguments[arguments.length - 1] === 'function') {
         then = arguments[arguments.length - 1];
      }

      return this._run(command, function(err, data) {
         then && then(err, !err && this._parsePush(data));
      });
   };

   /**
    * Removes the named files from source control.
    *
    * @param {String|String[]} files
    * @param {Function} [then]
    */
   Git.prototype.rm = function(files, then) {
      return this._rm(files, '-f', then);
   };

   /**
    * Removes the named files from source control but keeps them on disk rather than deleting them entirely. To
    * completely remove the files, use `rm`.
    *
    * @param {String|String[]} files
    * @param {Function} [then]
    */
   Git.prototype.rmKeepLocal = function(files, then) {
      return this._rm(files, '--cached', then);
   };

   /**
    * Return repository changes.
    *
    * @param {String} [options]
    * @param {Function} [then]
    */
   Git.prototype.diff = function(options, then) {
      var command = 'git diff';

      if (typeof options === 'string' && typeof then === 'function') {
         command += ' ' + options;
      }

      if (typeof arguments[arguments.length - 1] === 'function') {
         then = arguments[arguments.length - 1];
      }

      return this._run(command, function(err, data) {
         then && then(err, data);
      });
   };

   Git.prototype._rm = function(files, options, then) {
      return this._run('git rm ' + options + ' "' + [].concat(files).join('" "') + '"', function(err) {
         then && then(err);
      });
   };

   Git.prototype._parsePull = function(pull) {
      var changes = {
         files: [],
         insertions: {},
         deletions: {},
         summary: {
            changes: 0,
            insertions: 0,
            deletions: 0
         }
      };

      var fileUpdateRegex = /^\s*(.+)\s\|\s(\d+)\s([\+]+)/;
      for(var lines = pull.split('\n'), i = 0, l = lines.length; i < l; i++) {
         var update = fileUpdateRegex.exec(lines[i]);

         // search for update statement for each file
         if(update) {
            changes.files.push(update[1]);

            var insertions = update[3].length;
            if(insertions) {
               changes.insertions[update[1]] = insertions;
            }
            if(update[2] > insertions) {
               changes.deletions[update[1]] = update[2] - insertions;
            }
         }

         // summary appears after updates
         else if(changes.files.length && (update = /(\d+)\D+(\d+)\D+(\d+)/.exec(lines[i]))) {
            changes.summary.changes = +update[1];
            changes.summary.insertions = +update[2];
            changes.summary.deletions = +update[3];
         }
      }

      return changes;
   };

   Git.prototype._parseListTags = function(tags) {
      var tagList = tags.split('\n').sort(function(tagA, tagB) {
         var partsA = tagA.split('.');
         var partsB = tagB.split('.');

         for(var i = 0, l = Math.max(partsA.length, partsB.length); i < l; i++) {
            var diff = partsA[i] - partsB[i];
            if(diff) {
               return diff > 0 ? 1 : -1;
            }
         }

         return 0;
      });

      return {
         latest: tagList.length && tagList[tagList.length - 1],
         all: tagList
      };
   };

   Git.prototype._parseCommit = function(commit) {
      var lines = commit.trim().split('\n');
      var branch = /\[([^\s]+) ([^\]]+)/.exec(lines.shift());
      var summary = /(\d+)[^,]*(?:,\s*(\d+)[^,]*)?(?:,\s*(\d+))?/g.exec(lines.shift()) || [];

      return {
         branch: branch[1],
         commit: branch[2],
         summary: {
            changes: (typeof summary[1] !== 'undefined') ? summary[1] : 0,
            insertions: (typeof summary[2] !== 'undefined') ? summary[2] : 0,
            deletions: (typeof summary[3] !== 'undefined') ? summary[3] : 0
         }
      };
   };

   Git.prototype._parseCheckout = function(checkout) {
      return checkout;
   };

   Git.prototype._parseFetch = function(fetch) {
      return data;
   };

   Git.prototype._run = function(command, then) {
      this._runCache.push([command, then]);
      this._schedule();

      return this;
   };

   Git.prototype._schedule = function() {
      if(!this._childProcess && this._runCache.length) {
         var task = this._runCache.shift();
         var command = task[0];
         var then = task[1];

         this._childProcess = ChildProcess.exec(
             command,
             {cwd: this._baseDir},
             function(err, stdout, stderr) {
                delete this._childProcess;

                if(err) {
                   console.error(stderr);
                   this._runCache = [];
                   then.call(this, err, null);
                }
                else {
                   then.call(this, null, stdout);
                }

                process.nextTick(this._schedule.bind(this));
             }.bind(this));
      }
   };

   module.exports = function(baseDir) {
      return new Git(baseDir || process.cwd());
   };

}());
<|endoftext|>


<commit_before>'use strict';

const upath = require('upath');
const symbols = require('./symbols');

/**
 * A lightweight configuration system for Ravel, allowing
 * clients, as well as other parts of the application, to
 * define expected configuration parameters and get/set
 * their values
 * @external Ravel
 */
module.exports = function (Ravel) {
  /**
   * Stores default configuration values, specified at parameter creation-time
   * @api private
   */
  const defaults = Object.create(null);

  /**
   * Load parameters from a `.ravelrc.json` file during `init()`
   * This file must be located beside `app.js` or in any parent directory of `app.js`.
   * @memberof Ravel
   * @api private
   */
  Ravel.prototype[symbols.loadParameters] = function () {
    let fromFile = Object.create(null);
    let nextDir = this.cwd;
    let currentDir;

    do {
      currentDir = nextDir;
      // load parameters from config file into an empty object
      // search upwards until we run out of path components or find something
      // don't require extension; node will search for .js/.json/.node
      const search = upath.join(currentDir, '.ravelrc');
      try {
        fromFile = require(search);
        if (typeof fromFile === 'string') {
          fromFile = JSON.parse(fromFile);
        }
        break;
      } catch (err) {
        this.once('post init', () => {
          this.log.trace(`Could not locate .ravelrc.json at ${search}`, err.message);
        });
        nextDir = upath.join(currentDir, '..');
      }
    } while (nextDir !== currentDir);

    // verify all parameters in fromFile are known
    for (let key of Object.keys(fromFile)) {
      if (!this[symbols.knownParameters][key]) {
        throw new this.ApplicationError.IllegalValue(
          `Attempted to set unknown parameter: ${key.toString()}`);
      }
    }

    // merge params from file into defaults, so file params take precendence over defaults
    Object.assign(defaults, fromFile);
    // now merge with this[symbols.params], allowing programmatically set params to take precedence
    Object.assign(defaults, this[symbols.params]);
    // now defaults contains what we want, so make it this[symbols.params]
    this[symbols.params] = defaults;
    // done!
    this[symbols.parametersLoaded] = true;
  };

  /**
   * Register a parameter
   *
   * @memberof Ravel
   * @param {String} key the key for the parameter
   * @param {Boolean} required true, iff the parameter is required. false otherwise.
   * @param {(Any | undefined)} defaultValue the default value for the parameter
   */
  Ravel.prototype.registerParameter = function (key, required, defaultValue) {
    this[symbols.knownParameters][key] = {
      required: required
    };
    if (defaultValue !== undefined) {
      defaults[key] = JSON.parse(JSON.stringify(defaultValue));
    }
  };

  /**
   * Set a parameter
   *
   * @memberof Ravel
   * @param {String} key the key for the parameter
   * @param {Object} value the value for the parameter
   * @throws ApplicationError.IllegalValue if key refers to an unregistered parameter
   * @return {Object} the parameter value
   */
  Ravel.prototype.set = function (key, value) {
    if (this[symbols.knownParameters][key]) {
      this[symbols.params][key] = value;
    } else {
      throw new this.ApplicationError.IllegalValue(`Attempted to set unknown parameter: ${key}.`);
    }
  };

  /**
   * Get a parameter
   *
   * @param {String} key the key for the parameter
   * @throws ApplicationError.NotFound if the parameter is required and not set
   * @return {Object} the parameter value, or undefined if it is not required and not set
   */
  Ravel.prototype.get = function (key) {
    if (!this[symbols.parametersLoaded]) {
      throw new this.ApplicationError.General('Cannot get() parameters until after app.init()');
    } else if (!this[symbols.knownParameters][key]) {
      throw new this.ApplicationError.NotFound(`Parameter ${key} was requested, but is unknown.`);
    } else if (this[symbols.knownParameters][key].required && this[symbols.params][key] === undefined) {
      throw new this.ApplicationError.NotFound(
        `Known required parameter ${key} was requested, but hasn't been defined yet.`);
    } else if (this[symbols.params][key] === undefined) {
      this.log.trace(`Optional parameter ${key} was requested, but is not defined.`);
      return undefined;
    } else {
      return JSON.parse(JSON.stringify(this[symbols.params][key]));
    }
  };

  /**
   * Ravel.config getter
   * @return {Object} full Ravel configuration
   */
  Object.defineProperty(Ravel.prototype, 'config', {
    get: function () { return this[symbols.params]; }
  });
};
<commit_msg>Temporary log statement for AppVeyor test<commit_after>'use strict';

const upath = require('upath');
const symbols = require('./symbols');

/**
 * A lightweight configuration system for Ravel, allowing
 * clients, as well as other parts of the application, to
 * define expected configuration parameters and get/set
 * their values
 * @external Ravel
 */
module.exports = function (Ravel) {
  /**
   * Stores default configuration values, specified at parameter creation-time
   * @api private
   */
  const defaults = Object.create(null);

  /**
   * Load parameters from a `.ravelrc.json` file during `init()`
   * This file must be located beside `app.js` or in any parent directory of `app.js`.
   * @memberof Ravel
   * @api private
   */
  Ravel.prototype[symbols.loadParameters] = function () {
    let fromFile = Object.create(null);
    let nextDir = this.cwd;
    let currentDir;

    do {
      currentDir = nextDir;
      // load parameters from config file into an empty object
      // search upwards until we run out of path components or find something
      // don't require extension; node will search for .js/.json/.node
      const search = upath.join(currentDir, '.ravelrc');
      try {
        fromFile = require(search);
        if (typeof fromFile === 'string') {
          fromFile = JSON.parse(fromFile);
        }
        break;
      } catch (err) {
        console.log(`Could not locate .ravelrc.json at ${search}`);
        this.once('post init', () => {
          this.log.trace(`Could not locate .ravelrc.json at ${search}`, err.message);
        });
        nextDir = upath.join(currentDir, '..');
      }
    } while (nextDir !== currentDir);

    // verify all parameters in fromFile are known
    for (let key of Object.keys(fromFile)) {
      if (!this[symbols.knownParameters][key]) {
        throw new this.ApplicationError.IllegalValue(
          `Attempted to set unknown parameter: ${key.toString()}`);
      }
    }

    // merge params from file into defaults, so file params take precendence over defaults
    Object.assign(defaults, fromFile);
    // now merge with this[symbols.params], allowing programmatically set params to take precedence
    Object.assign(defaults, this[symbols.params]);
    // now defaults contains what we want, so make it this[symbols.params]
    this[symbols.params] = defaults;
    // done!
    this[symbols.parametersLoaded] = true;
  };

  /**
   * Register a parameter
   *
   * @memberof Ravel
   * @param {String} key the key for the parameter
   * @param {Boolean} required true, iff the parameter is required. false otherwise.
   * @param {(Any | undefined)} defaultValue the default value for the parameter
   */
  Ravel.prototype.registerParameter = function (key, required, defaultValue) {
    this[symbols.knownParameters][key] = {
      required: required
    };
    if (defaultValue !== undefined) {
      defaults[key] = JSON.parse(JSON.stringify(defaultValue));
    }
  };

  /**
   * Set a parameter
   *
   * @memberof Ravel
   * @param {String} key the key for the parameter
   * @param {Object} value the value for the parameter
   * @throws ApplicationError.IllegalValue if key refers to an unregistered parameter
   * @return {Object} the parameter value
   */
  Ravel.prototype.set = function (key, value) {
    if (this[symbols.knownParameters][key]) {
      this[symbols.params][key] = value;
    } else {
      throw new this.ApplicationError.IllegalValue(`Attempted to set unknown parameter: ${key}.`);
    }
  };

  /**
   * Get a parameter
   *
   * @param {String} key the key for the parameter
   * @throws ApplicationError.NotFound if the parameter is required and not set
   * @return {Object} the parameter value, or undefined if it is not required and not set
   */
  Ravel.prototype.get = function (key) {
    if (!this[symbols.parametersLoaded]) {
      throw new this.ApplicationError.General('Cannot get() parameters until after app.init()');
    } else if (!this[symbols.knownParameters][key]) {
      throw new this.ApplicationError.NotFound(`Parameter ${key} was requested, but is unknown.`);
    } else if (this[symbols.knownParameters][key].required && this[symbols.params][key] === undefined) {
      throw new this.ApplicationError.NotFound(
        `Known required parameter ${key} was requested, but hasn't been defined yet.`);
    } else if (this[symbols.params][key] === undefined) {
      this.log.trace(`Optional parameter ${key} was requested, but is not defined.`);
      return undefined;
    } else {
      return JSON.parse(JSON.stringify(this[symbols.params][key]));
    }
  };

  /**
   * Ravel.config getter
   * @return {Object} full Ravel configuration
   */
  Object.defineProperty(Ravel.prototype, 'config', {
    get: function () { return this[symbols.params]; }
  });
};
<|endoftext|>