<commit_before>// authentication and authorization functions
var Client = require('cas.js');
var url = require('url');
var ad = require('../config/ad.json');
var auth = require('../config/auth.json');
var pause = require('pause');

var apiUsers = require('../config/api.json');

var ldapClient = require('../lib/ldap-client');

var mongoose = require('mongoose');
var User = mongoose.model('User');

// validation of ticket is with the lan, and therefore url does not need to be proxied.
var cas = new Client({
  base_url: auth.cas,
  service: auth.login_service,
  version: 1.0
});

function proxied(req, res, next) {
  if (req.get('x-forwarded-host') && req.get('x-forwarded-host') === auth.proxy) {
    req.proxied = true;
    req.proxied_prefix = url.parse(auth.proxied_service).pathname;
  }
  next();
}

function cn(s) {
  var first = s.split(',', 1)[0];
  return first.substr(3).toLowerCase();
}

function filterGroup(a) {
  var output = [];
  var i;
  var group;
  for (i = 0; i < a.length; i += 1) {
    group = cn(a[i]);
    if (group.indexOf('lab.frib') === 0) {
      output.push(group);
    }
  }
  return output;
}

function ensureAuthenticated(req, res, next) {
  // console.log(req.session);
  var ticketUrl = url.parse(req.url, true);
  if (req.session.userid) {
    // logged in already
    if (req.query.ticket) {
      // remove the ticket query param
      delete ticketUrl.query.ticket;
      return res.redirect(301, url.format({
        pathname: req.proxied ? url.resolve(auth.proxied_service + '/', '.' + ticketUrl.pathname) : ticketUrl.pathname,
        query: ticketUrl.query
      }));
    }
    next();
  } else if (req.query.ticket) {
    // just kicked back by CAS
    // var halt = pause(req);
    if (req.proxied) {
      cas.service = auth.login_proxied_service;
    } else {
      cas.service = auth.login_service;
    }
    // validate the ticket
    cas.validate(req.query.ticket, function (err, casresponse, result) {
      if (err) {
        console.error(err.message);
        return res.send(401, err.message);
      }
      if (result.validated) {
        var userid = result.username;
        // set userid in session
        req.session.userid = userid;
        var searchFilter = ad.searchFilter.replace('_id', userid);
        var opts = {
          filter: searchFilter,
          attributes: ad.memberAttributes,
          scope: 'sub'
        };

        // query ad about other attribute
        ldapClient.search(ad.searchBase, opts, false, function (err, result) {
          if (err) {
            console.error(err.name + ' : ' + err.message);
            return res.send(500, 'something wrong with ad');
          }
          if (result.length === 0) {
            console.warn('cannot find ' + userid);
            return res.send(500, userid + ' is not found!');
          }
          if (result.length > 1) {
            return res.send(500, userid + ' is not unique!');
          }

          // set username and memberof in session
          req.session.username = result[0].displayName;
          req.session.memberOf = filterGroup(result[0].memberOf);

          // load others from db
          User.findOne({
            _id: userid
          }).lean().exec(function (err, user) {
            if (err) {
              console.error(err.msg);
            }
            if (user) {
              req.session.roles = user.roles;
              // update user last visited on
              User.findByIdAndUpdate(user._id, {
                lastVisitedOn: Date.now()
              }, function (err, update) {
                if (err) {
                  console.error(err.message);
                }
              });
            } else {
              // create a new user
              req.session.roles = [];
              var first = new User({
                _id: userid,
                name: result[0].displayName,
                email: result[0].mail,
                office: result[0].physicalDeliveryOfficeName,
                phone: result[0].telephoneNumber,
                mobile: result[0].mobile,
                roles: [],
                lastVisitedOn: Date.now()
              });

              first.save(function (err, newUser) {
                if (err) {
                  console.error(err.msg);
                }
                console.info('A new user created : ' + newUser.name);
              });
            }
            if (req.session.landing && req.session.landing !== '/login') {
              res.redirect(req.proxied ? url.resolve(auth.proxied_service + '/', '.' + req.session.landing) : req.session.landing);
            } else {
              // has a ticket but not landed before, must copy the ticket from somewhere ...
              res.redirect(req.proxied ? auth.proxied_service : '/');
            }
            // halt.resume();
          });
        });
      } else {
        console.error('CAS reject this ticket');
        return res.redirect(req.proxied ? auth.login_proxied_service : auth.login_service);
      }
    });
  } else {
    // if this is ajax call, then send 401 without redirect
    if (req.xhr) {
      // TODO: might need to properly set the WWW-Authenticate header
      res.set('WWW-Authenticate', 'CAS realm="' + (req.proxied ? auth.proxied_service : auth.service) + '"');
      res.send(401, 'xhr cannot be authenticated');
    } else {
      // set the landing, the first unauthenticated url
      req.session.landing = req.url;
      if (req.proxied) {
        res.redirect(auth.proxied_cas + '/login?service=' + encodeURIComponent(auth.login_proxied_service));
      } else {
        res.redirect(auth.cas + '/login?service=' + encodeURIComponent(auth.login_service));
      }
    }
  }
}

function sessionLocals(req, res, next) {
  res.locals({
    session: req.session,
    prefix: req.proxied ? req.proxied_prefix : ''
  });
  next();
}

function checkAuth(req, res, next) {
  if (req.query.ticket) {
    ensureAuthenticated(req, res, next);
  } else {
    next();
  }
}

function verifyRole(role) {
  return function (req, res, next) {
    // console.log(req.session);
    if (req.session.roles) {
      if (req.session.roles.indexOf(role) > -1) {
        next();
      } else {
        res.send(403, "You are not authorized to access this resource. ");
      }
    } else {
      console.log("Cannot find the user's role.");
      res.send(500, "something wrong for the user's session");
    }
  };
}

var basic = require('basic-auth');

function notKnown(cred) {
  if (apiUsers.hasOwnProperty(cred.name)) {
    if (apiUsers[cred.name] === cred.pass) {
      return false;
    }
  }
  return true;
}

function basicAuth(req, res, next) {
  var cred = basic(req);
  if (!cred || notKnown(cred)) {
    res.set('WWW-Authenticate', 'Basic realm="api"');
    return res.send(401);
  }
  next();
}

module.exports = {
  ensureAuthenticated: ensureAuthenticated,
  verifyRole: verifyRole,
  checkAuth: checkAuth,
  sessionLocals: sessionLocals,
  basicAuth: basicAuth,
  proxied: proxied
};
<commit_msg>enforce lower case for use id, send too if new user is not saved, better logging<commit_after>// authentication and authorization functions
var Client = require('cas.js');
var url = require('url');
var ad = require('../config/ad.json');
var auth = require('../config/auth.json');
var pause = require('pause');

var apiUsers = require('../config/api.json');

var ldapClient = require('../lib/ldap-client');

var mongoose = require('mongoose');
var User = mongoose.model('User');

// validation of ticket is with the lan, and therefore url does not need to be proxied.
var cas = new Client({
  base_url: auth.cas,
  service: auth.login_service,
  version: 1.0
});

function proxied(req, res, next) {
  if (req.get('x-forwarded-host') && req.get('x-forwarded-host') === auth.proxy) {
    req.proxied = true;
    req.proxied_prefix = url.parse(auth.proxied_service).pathname;
  }
  next();
}

function cn(s) {
  var first = s.split(',', 1)[0];
  return first.substr(3).toLowerCase();
}

function filterGroup(a) {
  var output = [];
  var i;
  var group;
  for (i = 0; i < a.length; i += 1) {
    group = cn(a[i]);
    if (group.indexOf('lab.frib') === 0) {
      output.push(group);
    }
  }
  return output;
}

function ensureAuthenticated(req, res, next) {
  // console.log(req.session);
  var ticketUrl = url.parse(req.url, true);
  if (req.session.userid) {
    // logged in already
    if (req.query.ticket) {
      // remove the ticket query param
      delete ticketUrl.query.ticket;
      return res.redirect(301, url.format({
        pathname: req.proxied ? url.resolve(auth.proxied_service + '/', '.' + ticketUrl.pathname) : ticketUrl.pathname,
        query: ticketUrl.query
      }));
    }
    next();
  } else if (req.query.ticket) {
    // just kicked back by CAS
    // var halt = pause(req);
    if (req.proxied) {
      cas.service = auth.login_proxied_service;
    } else {
      cas.service = auth.login_service;
    }
    // validate the ticket
    cas.validate(req.query.ticket, function (err, casresponse, result) {
      if (err) {
        console.error(err.message);
        return res.send(401, err.message);
      }
      if (result.validated) {
        var userid = result.username.toLowerCase();
        // set userid in session
        req.session.userid = userid;
        var searchFilter = ad.searchFilter.replace('_id', userid);
        var opts = {
          filter: searchFilter,
          attributes: ad.memberAttributes,
          scope: 'sub'
        };

        // query ad about other attribute
        ldapClient.search(ad.searchBase, opts, false, function (err, result) {
          if (err) {
            console.error(err.name + ' : ' + err.message);
            return res.send(500, 'something wrong with ad');
          }
          if (result.length === 0) {
            console.warn('cannot find ' + userid);
            return res.send(500, userid + ' is not found!');
          }
          if (result.length > 1) {
            return res.send(500, userid + ' is not unique!');
          }

          // set username and memberof in session
          req.session.username = result[0].displayName;
          req.session.memberOf = filterGroup(result[0].memberOf);

          // load others from db
          User.findOne({
            _id: userid
          }).lean().exec(function (err, user) {
            if (err) {
              console.error(err);
            }
            if (user) {
              req.session.roles = user.roles;
              // update user last visited on
              User.findByIdAndUpdate(user._id, {
                lastVisitedOn: Date.now()
              }, function (err, update) {
                if (err) {
                  console.error(err.message);
                }
              });
            } else {
              // create a new user
              // TODO: need to load the user properties using ad.objAttributes
              req.session.roles = [];
              var first = new User({
                _id: userid,
                name: result[0].displayName,
                email: result[0].mail,
                office: result[0].physicalDeliveryOfficeName,
                phone: result[0].telephoneNumber,
                mobile: result[0].mobile,
                roles: [],
                lastVisitedOn: Date.now()
              });

              first.save(function (err, newUser) {
                if (err) {
                  console.error(err);
                  console.error(newUser);
                  return res.send(500, 'Cannot log in. Please contact the admin. Thanks.');
                }
                console.info('A new user created : ' + newUser);
              });
            }
            if (req.session.landing && req.session.landing !== '/login') {
              res.redirect(req.proxied ? url.resolve(auth.proxied_service + '/', '.' + req.session.landing) : req.session.landing);
            } else {
              // has a ticket but not landed before, must copy the ticket from somewhere ...
              res.redirect(req.proxied ? auth.proxied_service : '/');
            }
            // halt.resume();
          });
        });
      } else {
        console.error('CAS reject this ticket');
        return res.redirect(req.proxied ? auth.login_proxied_service : auth.login_service);
      }
    });
  } else {
    // if this is ajax call, then send 401 without redirect
    if (req.xhr) {
      // TODO: might need to properly set the WWW-Authenticate header
      res.set('WWW-Authenticate', 'CAS realm="' + (req.proxied ? auth.proxied_service : auth.service) + '"');
      res.send(401, 'xhr cannot be authenticated');
    } else {
      // set the landing, the first unauthenticated url
      req.session.landing = req.url;
      if (req.proxied) {
        res.redirect(auth.proxied_cas + '/login?service=' + encodeURIComponent(auth.login_proxied_service));
      } else {
        res.redirect(auth.cas + '/login?service=' + encodeURIComponent(auth.login_service));
      }
    }
  }
}

function sessionLocals(req, res, next) {
  res.locals({
    session: req.session,
    prefix: req.proxied ? req.proxied_prefix : ''
  });
  next();
}

function checkAuth(req, res, next) {
  if (req.query.ticket) {
    ensureAuthenticated(req, res, next);
  } else {
    next();
  }
}

function verifyRole(role) {
  return function (req, res, next) {
    // console.log(req.session);
    if (req.session.roles) {
      if (req.session.roles.indexOf(role) > -1) {
        next();
      } else {
        res.send(403, "You are not authorized to access this resource. ");
      }
    } else {
      console.log("Cannot find the user's role.");
      res.send(500, "something wrong for the user's session");
    }
  };
}

var basic = require('basic-auth');

function notKnown(cred) {
  if (apiUsers.hasOwnProperty(cred.name)) {
    if (apiUsers[cred.name] === cred.pass) {
      return false;
    }
  }
  return true;
}

function basicAuth(req, res, next) {
  var cred = basic(req);
  if (!cred || notKnown(cred)) {
    res.set('WWW-Authenticate', 'Basic realm="api"');
    return res.send(401);
  }
  next();
}

module.exports = {
  ensureAuthenticated: ensureAuthenticated,
  verifyRole: verifyRole,
  checkAuth: checkAuth,
  sessionLocals: sessionLocals,
  basicAuth: basicAuth,
  proxied: proxied
};
<|endoftext|>


<commit_before>var CollisionDetector = require('./collision-detector')

var Ball = function(coords, context) {
  this.x = coords.x;
  this.y = coords.y;
  this.radius = coords.radius || 10;;
  this.moving = false
  this.context = context
  this.xSpeed = 5
  this.ySpeed = 5
  this.xDirection = 1
  this.yDirection = 1
  this.color = 'white'
  this.inHole = false
}

Ball.prototype.draw = function () {
  this.context.beginPath();
  this.context.fillStyle = this.color;
  this.context.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
  this.context.closePath();
  this.context.fill();
  return this;
};

Ball.prototype.move = function (bumpers) {
  this.collisionCheck(bumpers);
  this.x = this.x + (this.xSpeed * this.xDirection);
  this.y = this.y + (this.ySpeed * this.yDirection);
  this.xSpeed  *= 0.989
  this.ySpeed  *= 0.989
  this.stopCheck();

  return this;
}

Ball.prototype.stopCheck = function() {
  if (Math.abs(this.xSpeed) < 0.15 && Math.abs(this.ySpeed) < 0.15) {
    this.xSpeed = 1;
    this.ySpeed = 1;
    this.xDirection = 1;
    this.yDirection = 1;
    this.moving = false;
  }
}

Ball.prototype.holeCheck = function(puttHole) {
  if(Math.abs((puttHole.x - this.x)) <= (this.radius) && Math.abs((puttHole.y - this.y)) <= (this.radius)) {
    this.x = puttHole.x;
    this.y = puttHole.y;
    this.moving = false;
    this.inHole = true;
  }
}

Ball.prototype.collisionCheck = function(bumpers) {
  bumpers.forEach(function (bumper, index) {
    if ((this.y + this.radius >= bumper.minY && this.y - this.radius <= bumper.maxY) && (this.x + this.radius >= bumper.minX && this.x - this.radius <= bumper.maxX) ) {
      if (bumper.type == "bumper") {
        this.bounceAgainst(bumper);
      }
      else if (bumper.type == "sand") {
        this.slowDownAgainst(bumper);
      }
      this.xSpeed *= 0.89;
      this.ySpeed *= 0.89;
    }
  }, this);
  return this;
}

Ball.prototype.bounceAgainst = function(bumper) {
  var prevX = this.x - this.xSpeed * this.xDirection;
  var prevY = this.y - this.ySpeed * this.yDirection;

  if (this.changeXDirection(bumper.minX, bumper.maxX, prevX, this)){
    this.xDirection *= -1;
  }
  if (this.changeYDirection(bumper.minY, bumper.maxY, prevY, this)){
    this.yDirection *= -1;
  }
  this.xSpeed *= 0.8;
  this.ySpeed *= 0.8;
}

Ball.prototype.slowDownAgainst = function(bumper) {
  this.xSpeed *= 0.9;
  this.ySpeed *= 0.9;
}

Ball.prototype.changeXDirection = function(xMin, xMax, prevX, that){
  return (prevX - that.radius > xMax &&  that.x - that.radius <= xMax) || // check for collision in x direction on right
  (prevX + that.radius < xMin && that.x + that.radius >= xMin)  // check for collision in x direction on left
}

Ball.prototype.changeYDirection = function(yMin, yMax, prevY, that){
  return (prevY - that.radius > yMax &&  that.y - that.radius <= yMax) || // check for collision in y direction on bottom
  (prevY + that.radius < yMin && that.y + that.radius >= yMin)  // check for collision in y direction on top
}

module.exports = Ball;
<commit_msg>Swap back collision check order<commit_after>var CollisionDetector = require('./collision-detector')

var Ball = function(coords, context) {
  this.x = coords.x;
  this.y = coords.y;
  this.radius = coords.radius || 10;;
  this.moving = false
  this.context = context
  this.xSpeed = 5
  this.ySpeed = 5
  this.xDirection = 1
  this.yDirection = 1
  this.color = 'white'
  this.inHole = false
}

Ball.prototype.draw = function () {
  this.context.beginPath();
  this.context.fillStyle = this.color;
  this.context.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
  this.context.closePath();
  this.context.fill();
  return this;
};

Ball.prototype.move = function (bumpers) {
  this.x = this.x + (this.xSpeed * this.xDirection);
  this.y = this.y + (this.ySpeed * this.yDirection);
  this.collisionCheck(bumpers);
  this.xSpeed  *= 0.989
  this.ySpeed  *= 0.989
  this.stopCheck();

  return this;
}

Ball.prototype.stopCheck = function() {
  if (Math.abs(this.xSpeed) < 0.15 && Math.abs(this.ySpeed) < 0.15) {
    this.xSpeed = 1;
    this.ySpeed = 1;
    this.xDirection = 1;
    this.yDirection = 1;
    this.moving = false;
  }
}

Ball.prototype.holeCheck = function(puttHole) {
  if(Math.abs((puttHole.x - this.x)) <= (this.radius) && Math.abs((puttHole.y - this.y)) <= (this.radius)) {
    this.x = puttHole.x;
    this.y = puttHole.y;
    this.moving = false;
    this.inHole = true;
  }
}

Ball.prototype.collisionCheck = function(bumpers) {
  bumpers.forEach(function (bumper, index) {
    if ((this.y + this.radius >= bumper.minY && this.y - this.radius <= bumper.maxY) && (this.x + this.radius >= bumper.minX && this.x - this.radius <= bumper.maxX) ) {
      if (bumper.type == "bumper") {
        this.bounceAgainst(bumper);
      }
      else if (bumper.type == "sand") {
        this.slowDownAgainst(bumper);
      }
      this.xSpeed *= 0.89;
      this.ySpeed *= 0.89;
    }
  }, this);
  return this;
}

Ball.prototype.bounceAgainst = function(bumper) {
  var prevX = this.x - this.xSpeed * this.xDirection;
  var prevY = this.y - this.ySpeed * this.yDirection;

  if (this.changeXDirection(bumper.minX, bumper.maxX, prevX, this)){
    this.xDirection *= -1;
  }
  if (this.changeYDirection(bumper.minY, bumper.maxY, prevY, this)){
    this.yDirection *= -1;
  }
  this.xSpeed *= 0.8;
  this.ySpeed *= 0.8;
}

Ball.prototype.slowDownAgainst = function(bumper) {
  this.xSpeed *= 0.9;
  this.ySpeed *= 0.9;
}

Ball.prototype.changeXDirection = function(xMin, xMax, prevX, that){
  return (prevX - that.radius > xMax &&  that.x - that.radius <= xMax) || // check for collision in x direction on right
  (prevX + that.radius < xMin && that.x + that.radius >= xMin)  // check for collision in x direction on left
}

Ball.prototype.changeYDirection = function(yMin, yMax, prevY, that){
  return (prevY - that.radius > yMax &&  that.y - that.radius <= yMax) || // check for collision in y direction on bottom
  (prevY + that.radius < yMin && that.y + that.radius >= yMin)  // check for collision in y direction on top
}

module.exports = Ball;
<|endoftext|>


<commit_before>'use strict';

var crypto = require('crypto')

var Base = function() {
    this.manager = false
    this.socket  = false
    this.client  = false
}

Base.prototype.init = function(manager, ignoreEvents) {
    this.manager = manager
    this.socket  = manager.socket
    this.client  = manager.client
    if (!ignoreEvents) this.registerEvents()
}

Base.prototype.registerEvents = function() {
    if (!this._events) return
    var self = this
    Object.keys(this._events).forEach(function(event) {
        self.socket.on(event, function(data, callback) {
            self[self._events[event]](data, callback)
        })
    })
}

Base.prototype.unregisterEvents = function() {
    if (!this._events) return
    var self = this
    Object.keys(this._events).forEach(function(event) {
        self.socket.removeAllListeners(event)
    })
}

Base.prototype.handles = function() {}
Base.prototype.handle  = function() {}

/* Namespaces */
Base.prototype.NS_REGISTER = 'jabber:iq:register'
Base.prototype.NS_DATA     = 'jabber:x:data'

Base.prototype.NS_ERROR    = 'urn:ietf:params:xml:ns:xmpp-stanzas'

Base.prototype._getJid = function(jid) {
    var node   = jid.split('@')[0]
    var domain = jid.split('/')[0]
    if (domain.indexOf('@' !== -1)) {
        domain = domain.split('@')[1]
    }
    var resource = jid.split('/')[1]
    var result = { domain: domain }
    if (node) {
        result.user = node
    }
    if (resource) {
        result.resource = resource
    }
    return result
}

Base.prototype._getId = function() {
        if (!Base.prototype.id) {
            var identifier = crypto.createHash('md5')
                .update(this.manager.jid + new Date(), 'utf8')
                .digest('hex')
                .toString()
                .substring(23, 28)
            Base.prototype.id = {
                identifier: identifier,
                counter: 1
            }
        } else {
            ++Base.prototype.id.counter;
        }
        return Base.prototype.id.identifier + ':' + Base.prototype.id.counter
    }

Base.prototype._parseError = function(stanza) {
    var error  = { type: stanza.getChild('error').attrs.type }
    var errorMessage = stanza.getChild('error').children
    if (errorMessage)
        error.condition = errorMessage[0].getName().toLowerCase()
    errorMessage.forEach(function(message) {
        if (message.getChild('text'))
            error.description = message.getChild('text').getText()
    })
    return error
}

Base.prototype._clientError = function(message, original, callback) {
    var error = {
        type:        'modify',
        condition:   'client-error',
        description: message,
        request:     original
    }
    if (callback) return callback(error)
    this.socket.send('xmpp.error.client', error)
    return false
}

module.exports = Base
<commit_msg>Clear listeners in case init() is called multiple times<commit_after>'use strict';

var crypto = require('crypto')

var Base = function() {
    this.manager = false
    this.socket  = false
    this.client  = false
}

Base.prototype.init = function(manager, ignoreEvents) {
    this.manager = manager
    this.socket  = manager.socket
    this.client  = manager.client
    if (!ignoreEvents) this.registerEvents()
}

Base.prototype.registerEvents = function() {
    if (!this._events) return
    var self = this
    Object.keys(this._events).forEach(function(event) {
        self.socket.removeAllListeners(event)
        self.socket.on(event, function(data, callback) {
            self[self._events[event]](data, callback)
        })
    })
}

Base.prototype.unregisterEvents = function() {
    if (!this._events) return
    var self = this
    Object.keys(this._events).forEach(function(event) {
        self.socket.removeAllListeners(event)
    })
}

Base.prototype.handles = function() {}
Base.prototype.handle  = function() {}

/* Namespaces */
Base.prototype.NS_REGISTER = 'jabber:iq:register'
Base.prototype.NS_DATA     = 'jabber:x:data'

Base.prototype.NS_ERROR    = 'urn:ietf:params:xml:ns:xmpp-stanzas'

Base.prototype._getJid = function(jid) {
    var node   = jid.split('@')[0]
    var domain = jid.split('/')[0]
    if (domain.indexOf('@' !== -1)) {
        domain = domain.split('@')[1]
    }
    var resource = jid.split('/')[1]
    var result = { domain: domain }
    if (node) {
        result.user = node
    }
    if (resource) {
        result.resource = resource
    }
    return result
}

Base.prototype._getId = function() {
        if (!Base.prototype.id) {
            var identifier = crypto.createHash('md5')
                .update(this.manager.jid + new Date(), 'utf8')
                .digest('hex')
                .toString()
                .substring(23, 28)
            Base.prototype.id = {
                identifier: identifier,
                counter: 1
            }
        } else {
            ++Base.prototype.id.counter;
        }
        return Base.prototype.id.identifier + ':' + Base.prototype.id.counter
    }

Base.prototype._parseError = function(stanza) {
    var error  = { type: stanza.getChild('error').attrs.type }
    var errorMessage = stanza.getChild('error').children
    if (errorMessage)
        error.condition = errorMessage[0].getName().toLowerCase()
    errorMessage.forEach(function(message) {
        if (message.getChild('text'))
            error.description = message.getChild('text').getText()
    })
    return error
}

Base.prototype._clientError = function(message, original, callback) {
    var error = {
        type:        'modify',
        condition:   'client-error',
        description: message,
        request:     original
    }
    if (callback) return callback(error)
    this.socket.send('xmpp.error.client', error)
    return false
}

module.exports = Base
<|endoftext|>


<commit_before>'use strict';

var uuid = require('node-uuid')

var Base = function() {
    this.manager = false
    this.socket  = false
    this.client  = false
}

Base.prototype.init = function(manager, ignoreEvents) {
    this.manager = manager
    this.socket  = manager.socket
    this.client  = manager.client
    if (!ignoreEvents) this.registerEvents()
}

Base.prototype.registerEvents = function() {
    if (!this._events) return
    var self = this
    Object.keys(this._events).forEach(function(event) {
        self.socket.removeAllListeners(event)
        self.socket.on(event, function(data, callback) {
            self[self._events[event]](data, callback)
        })
    })
}

Base.prototype.unregisterEvents = function() {
    if (!this._events) return
    var self = this
    Object.keys(this._events).forEach(function(event) {
        self.socket.removeAllListeners(event)
    })
}

Base.prototype.handles = function() {}
Base.prototype.handle  = function() {}

/* Namespaces */
Base.prototype.NS_REGISTER = 'jabber:iq:register'
Base.prototype.NS_DATA     = 'jabber:x:data'

Base.prototype.NS_ERROR    = 'urn:ietf:params:xml:ns:xmpp-stanzas'

Base.prototype._getJid = function(jid) {
    var node   = jid.split('@')[0]
    var domain = jid.split('/')[0]
    if (domain.indexOf('@' !== -1)) {
        domain = domain.split('@')[1]
    }
    var resource = jid.split('/')[1]
    var result = { domain: domain }
    if (node) {
        result.user = node
    }
    if (resource) {
        result.resource = resource
    }
    return result
}

Base.prototype._getId = function() {
        if (!Base.prototype.id) {
            Base.prototype.id = {
                counter: 0
            }
        }
        ++Base.prototype.id.counter
        return uuid.v4()
    }

Base.prototype._parseError = function(stanza) {
    var error  = { type: stanza.getChild('error').attrs.type }
    var errorMessage = stanza.getChild('error').children
    if (errorMessage)
        error.condition = errorMessage[0].getName().toLowerCase()
    errorMessage.forEach(function(message) {
        if (message.getChild('text'))
            error.description = message.getChild('text').getText()
    })
    return error
}

Base.prototype._clientError = function(message, original, callback) {
    var error = {
        type:        'modify',
        condition:   'client-error',
        description: message,
        request:     original
    }
    if (callback) return callback(error)
    this.socket.send('xmpp.error.client', error)
    return false
}

Base.prototype._hasValidCallback = function(callback, data) {
    if (typeof callback !== 'function') {
        return this._clientError(
            'Missing callback', data
        )
    }
}

module.exports = Base
<commit_msg>Formatting<commit_after>'use strict';

var uuid = require('node-uuid')

var Base = function() {
    this.manager = false
    this.socket  = false
    this.client  = false
}

Base.prototype.init = function(manager, ignoreEvents) {
    this.manager = manager
    this.socket  = manager.socket
    this.client  = manager.client
    if (!ignoreEvents) this.registerEvents()
}

Base.prototype.registerEvents = function() {
    if (!this._events) return
    var self = this
    Object.keys(this._events).forEach(function(event) {
        self.socket.removeAllListeners(event)
        self.socket.on(event, function(data, callback) {
            self[self._events[event]](data, callback)
        })
    })
}

Base.prototype.unregisterEvents = function() {
    if (!this._events) return
    var self = this
    Object.keys(this._events).forEach(function(event) {
        self.socket.removeAllListeners(event)
    })
}

Base.prototype.handles = function() {}
Base.prototype.handle  = function() {}

/* Namespaces */
Base.prototype.NS_REGISTER = 'jabber:iq:register'
Base.prototype.NS_DATA     = 'jabber:x:data'

Base.prototype.NS_ERROR    = 'urn:ietf:params:xml:ns:xmpp-stanzas'

Base.prototype._getJid = function(jid) {
    var node   = jid.split('@')[0]
    var domain = jid.split('/')[0]
    if (domain.indexOf('@' !== -1)) {
        domain = domain.split('@')[1]
    }
    var resource = jid.split('/')[1]
    var result = { domain: domain }
    if (node) {
        result.user = node
    }
    if (resource) {
        result.resource = resource
    }
    return result
}

Base.prototype._getId = function() {
    if (!Base.prototype.id) {
        Base.prototype.id = {
            counter: 0
        }
    }
    ++Base.prototype.id.counter
    return uuid.v4()
}

Base.prototype._parseError = function(stanza) {
    var error  = { type: stanza.getChild('error').attrs.type }
    var errorMessage = stanza.getChild('error').children
    if (errorMessage)
        error.condition = errorMessage[0].getName().toLowerCase()
    errorMessage.forEach(function(message) {
        if (message.getChild('text'))
            error.description = message.getChild('text').getText()
    })
    return error
}

Base.prototype._clientError = function(message, original, callback) {
    var error = {
        type:        'modify',
        condition:   'client-error',
        description: message,
        request:     original
    }
    if (callback) return callback(error)
    this.socket.send('xmpp.error.client', error)
    return false
}

Base.prototype._hasValidCallback = function(callback, data) {
    if (typeof callback !== 'function') {
        return this._clientError(
            'Missing callback', data
        )
    }
}

module.exports = Base
<|endoftext|>


<commit_before>var queue = require('queue-async');
var events = require('events');
var util = require('util');
var URL = require('url');
var qs = require('querystring');
var typeis = require('type-is').is;
var bufferEqual = require('buffer-equal');
var agent = require('./utils/agent');
var debug = require('debug')('raja:core');

module.exports = function(opts, cb) {
	var raja = new Raja(opts);
	raja.error = Raja.prototype.error.bind(raja);
	raja.agent = agent;

	raja.proxies = {};
	raja.builders = {};

	raja.proxies.local = require('./proxies/local')(raja, opts);
	raja.proxies.remote = require('./proxies/remote')(raja, opts);
	raja.proxies.express = require('./proxies/express')(raja, opts);
	raja.proxies.statics = opts.statics && require('./proxies/statics')(raja, opts);
	raja.proxies.dom = opts.dom && require('./proxies/dom')(raja, opts);

	raja.init(function(err) {
		cb(err, raja);
	});
	return raja;
};

module.exports.agent = agent;

function Raja(opts) {
	this.opts = opts || {};
	if (!this.opts.namespace) {
		this.opts.namespace = "";
		console.info("Raja expects a namespace, using empty string");
	}
	this.unsaved = {};
}
util.inherits(Raja, events.EventEmitter);

Raja.prototype.init = function(cb) {
	queue(2)
	.defer(require('./io'), this, this.opts)
	.defer(require('./store'), this, this.opts)
	.await(function(err, io, store) {
		if (err) return cb(err);
		if (this.opts.client && !io) return cb(new Error("raja failed to init io"));
		if (!store) return cb(new Error("raja failed to init store"));
		this.io = io;
		this.store = store;
		this.session = require('./store/session').bind(null, raja);
		this.emit('ready');
		cb(null, this);
	}.bind(this));
};

// internal function
Raja.prototype.receive = function(msg) {
	debug('receive', msg);
	if (!msg.key) return;
	if (!msg.parents) msg.parents = [];
	if (msg.mtime) msg.mtime = new Date(msg.mtime);
	this.emit('message', msg);
	this.store.invalidateParents(msg, function(err) {
		if (err) console.error(err);
	});
};

Raja.prototype.send = function(msg) {
	if (!msg.key) {
		return this.error(new Error("Missing key in " + JSON.stringify(msg)));
	}
	if (!msg.mtime) msg.mtime = Date.now();
	else if (msg.mtime instanceof Date) msg.mtime = msg.mtime.getTime();
	this.io.send(msg);
};

Raja.prototype.error = function(err) {
	// called when an error happened while using raja
	if (err) console.error(err);
};

Raja.prototype.shallow = shallow;

Raja.prototype.dataEqual = dataEqual;

Raja.prototype.create = function(obj, from) {
	if (typeof obj == "string") obj = {url: obj};
	if (from) {
		obj.headers = this.headers(from);
		if (from.builder) obj.builder = from.builder;
	}
	if (!obj.url) {
		if (from && from.url) {
			obj.url = from.url;
		} else if (obj.key) {
			obj.url = keyObj(obj.key).url;
		} else {
			throw new Error("Missing key in " + JSON.stringify(obj));
		}
	}
	obj.key = this.reskey(obj);
	var inst = this.unsaved[obj.key];
	if (!inst) {
		inst = Object.create(Resource.prototype, {});
		this.shallow(obj, inst);
		debug('create unsaved', inst.key);
		Object.defineProperty(inst, 'raja', {
			enumerable: false,
			value: this
		});
		this.unsaved[inst.key] = inst;
	} else {
		debug('update unsaved', obj.key);
		this.shallow(obj, inst);
	}
	return inst;
};

Raja.prototype.reskey = function(obj, havingHeaders) {
	if (typeof obj == "string") obj = {
		url: obj
	};
	if (!obj.headers && havingHeaders) obj.headers = this.headers(havingHeaders);
	var xRaja = obj.headers && obj.headers['X-Raja'];
	var url = obj.url;
	if (xRaja == this.opts.namespace) {
		url = URL.parse(url).path;
	}
	return this.key(url, this.vary(obj.headers));
};

Raja.prototype.reqkey = function(obj, havingHeaders) {
	if (typeof obj == "string") obj = {
		url: obj
	};
	if (!obj.headers && havingHeaders) obj.headers = this.headers(havingHeaders);
	var xRaja = obj.headers && obj.headers['X-Raja'];
	var url = obj.url;
	if (xRaja == this.opts.namespace) {
		url = URL.parse(url).path;
	}
	return this.key(url, this.variant(obj.headers));
};

Raja.prototype.retrieve = function(url, havingHeaders, cb) {
	if (!cb && typeof havingHeaders == "function") {
		cb = havingHeaders;
		havingHeaders = null;
	}
	var key = this.reskey(url, havingHeaders);
	this.store.get(key, cb);
};

Raja.prototype.upsert = function(obj, cb) {
	debug('upsert', obj);
	this.retrieve(obj.url, obj, function(err, resource) {
		if (err) return cb(err);
		if (!resource) {
			debug('upsert not found, creating');
			resource = this.create(obj);
		} else {
			debug('upsert found, updating', resource.key, resource.headers);
			raja.shallow(obj, resource);
			resource.headers = this.headers(resource);
		}
		cb(null, resource);
	}.bind(this));
};

function Resource() { /* initialization code will not be called by Object.create */ }

Resource.prototype.depend = function(resource, havingHeaders) {
	if (!this.resources) this.resources = {};
	if (!(resource instanceof Resource)) resource = this.raja.create(resource, havingHeaders);
	if (this.key == resource.key) console.warn('A resource cannot depend on itself', this.key);
	else this.resources[resource.key] = resource;
	debug('depend', this.key, resource.key);
};

Resource.prototype.undepend = function(resource) {
	if (!this.resources || !resource) return;
	var key;
	if (typeof resource == "string") key = resource;
	else key = this.raja.reskey(resource);
	delete this.resources[key];
};

Resource.prototype.belong = function(resource, havingHeaders) {
	if (!this.parents) this.parents = {};
	if (!(resource instanceof Resource)) resource = this.raja.create(resource, havingHeaders);
	if (this.key == resource.key) console.warn('A resource cannot belong to itself', this.key);
	else this.parents[resource.key] = resource;
	debug('belong', this.key, resource.key);
};

Resource.prototype.unbelong = function(resource) {
	if (!this.parents || !resource) return;
	var key;
	if (typeof resource == "string") key = resource;
	else key = this.raja.reskey(resource);
	delete this.parents[key];
};

Resource.prototype.save = function(cb) {
	var raja = this.raja;
	cb = cb || raja.error;
	if (!this.url) {
		if (this.key) {
			this.url = keyObj(this.key).url;
		} else {
			throw new Error("Missing key in " + JSON.stringify(this));
		}
	}
	var key = raja.reskey(this);
	if (this.key == key) {
		raja.store.set(key, this, function(err, resource) {
			delete raja.unsaved[key];
			cb(err, resource);
		});
	} else {
		var oldkey = this.key;
		this.key = key;
		var copy = raja.create(this);
		this.key = oldkey;
		if (this.page) delete this.page;
		this.remove(raja.error);
		if (copy.page && copy.page.unlock) copy.page.unlock = function(copy) {
			debug("call page copy unlock");
			delete copy.page;
		}.bind(this, copy);
		copy.save(cb);
	}
};

Resource.prototype.load = function(remote, opts, cb) {
	if (!this.resources) this.resources = {};
	if (!cb && typeof opts == "function") {
		cb = opts;
		opts = null;
	}
	if (!opts) opts = {};
	remote = agent.resolve(this.url, remote);
	var localns = this.headers && this.headers['X-Raja'];
	var remotens = opts.headers && opts.headers['X-Raja'];
	if (localns && !remotens && URL.parse(this.url).host == URL.parse(remote).host) {
		if (!opts.headers) opts.headers = {};
		opts.headers['X-Raja'] = localns;
	}
	this.raja.proxies.remote.get(remote, opts, function(err, resource) {
		if (err >= 400 && err < 500) {
			this.undepend(resource);
		} else if (!err) {
			this.depend(resource);
		}
		cb(err, resource);
	}.bind(this));
};

Resource.prototype.invalidate = function(cb) {
	this.raja.store.invalidate(this.key, cb);
};

Resource.prototype.is = function(mimetype) {
	return typeis(this.headers && this.headers['Content-Type'], mimetype);
};

Resource.prototype.remove = function(cb) {
	// remove from unsave, from limbo, from store, and orphan everywhere
	delete this.raja.unsaved[this.key];
	this.raja.store.del(this.key, cb);
};

Raja.prototype.headers = function(havingHeaders) {
	if (!havingHeaders) return {};
	var obj = {};
	var headers = havingHeaders.headers;
	var hasGet = !!havingHeaders.get;
	if (!headers && !hasGet) headers = havingHeaders;
	['Content-Type', 'Vary', 'ETag', 'Accept', 'X-Grant', 'X-Right', 'X-Author', 'X-Raja'].forEach(function(name) {
		var val = hasGet && havingHeaders.get(name) || headers && (headers[name] || headers[name.toLowerCase()]);
		if (val) obj[name] = val;
	});
	return obj;
};

Raja.prototype.vary = function(headers) {
	if (!headers) return;
	var vary = {};
	if (!headers.Vary) return vary;
	headers.Vary.split(',').forEach(function(header) {
		header = header.trim();
		var val = headers[header] || "";
		if (!val) return;
		if (header == 'Content-Type') {
			var type = typeis(val, ['html', 'json', 'xml']);
			if (type && type != 'html') vary.type = type; // store html as default
		} else if (header == 'X-Grant') {
			vary.right = normalizeList(val);
		} else if (header == 'X-Author') {
			vary.author = val;
		}
	});
	return vary;
};

Raja.prototype.variant = function(headers) {
	if (!headers) return;
	var variant = {};
	var accept = headers['Accept'] || "";
	if (accept) {
		// html is considered the default type
		var type = typeis(accept, ['html', 'json', 'xml']);
		if (type && type != 'html') variant.type = type;
	}
	var right = headers['X-Right'] || "";
	if (right) {
		variant.right = normalizeList(right);
	}
	var author = headers['X-Author'];
	if (author) {
		variant.author = author;
	}
	return variant;
};

Raja.prototype.key = function(url, obj) {
	if (!obj) return url;
	for (var k in obj) if (obj[k] == null) delete obj[k];
	var str = obj && qs.stringify(obj);
	if (str) url = str + ' ' + url;
	return url;
};

Raja.prototype.keyObj = keyObj;

function shallow(src, dst) {
	dst = dst || {};
	if (!src) return dst;
	Object.keys(src).forEach(function(key) {
		if (src.hasOwnProperty(key) == false) return;
		var val = src[key];
		if (val === undefined) return;
		if (key == "resources" || key == "parents" || key == "headers") {
			val = shallow(val, dst[key] || {});
		}
		if ((key == 'resources' || key == 'parents') && val && Object.keys(val).length == 0 && dst[key] && Object.keys(dst[key]).length > 0) {
			console.info("not overwriting", key, dst[key], "with empty object");
			return;
		}
		dst[key] = val;
	});
	return dst;
}

function keyObj(key) {
	if (!key) return {};
	var obj;
	var find = /^(.*\s)?((?:https?:\/\/|\/).+)$/.exec(key);
	if (find != null && find.length == 3) {
		var query = find[1];
		if (query) {
			obj = qs.parse(query);
		} else {
			obj = {};
		}
		obj.url = find[2];
	} else {
		obj = {url: key};
	}
	return obj;
}

function normalizeList(strlist) {
	return strlist.split(',').map(function(str) {return str.trim();}).sort().join(',');
}

function dataEqual(a, b) {
	if (a == null || b == null) return a === b;
	var bufa = Buffer.isBuffer(a);
	var bufb = Buffer.isBuffer(b);
	if (!bufa && !bufb) return a === b;
	if (!bufa) a = new Buffer(a);
	if (!bufb) b = new Buffer(b);
	return bufferEqual(a, b);
}

<commit_msg>Init dom proxy after socket is started<commit_after>var queue = require('queue-async');
var events = require('events');
var util = require('util');
var URL = require('url');
var qs = require('querystring');
var typeis = require('type-is').is;
var bufferEqual = require('buffer-equal');
var agent = require('./utils/agent');
var debug = require('debug')('raja:core');

module.exports = function(opts, cb) {
	var raja = new Raja(opts);
	raja.error = Raja.prototype.error.bind(raja);
	raja.agent = agent;

	raja.proxies = {};
	raja.builders = {};

	raja.proxies.local = require('./proxies/local')(raja, opts);
	raja.proxies.remote = require('./proxies/remote')(raja, opts);
	raja.proxies.express = require('./proxies/express')(raja, opts);
	raja.proxies.statics = opts.statics && require('./proxies/statics')(raja, opts);

	raja.init(function(err) {
		raja.proxies.dom = opts.dom && require('./proxies/dom')(raja, opts);
		cb(err, raja);
	});
	return raja;
};

module.exports.agent = agent;

function Raja(opts) {
	this.opts = opts || {};
	if (!this.opts.namespace) {
		this.opts.namespace = "";
		console.info("Raja expects a namespace, using empty string");
	}
	this.unsaved = {};
}
util.inherits(Raja, events.EventEmitter);

Raja.prototype.init = function(cb) {
	queue(2)
	.defer(require('./io'), this, this.opts)
	.defer(require('./store'), this, this.opts)
	.await(function(err, io, store) {
		if (err) return cb(err);
		if (this.opts.client && !io) return cb(new Error("raja failed to init io"));
		if (!store) return cb(new Error("raja failed to init store"));
		this.io = io;
		this.store = store;
		this.session = require('./store/session').bind(null, raja);
		this.emit('ready');
		cb(null, this);
	}.bind(this));
};

// internal function
Raja.prototype.receive = function(msg) {
	debug('receive', msg);
	if (!msg.key) return;
	if (!msg.parents) msg.parents = [];
	if (msg.mtime) msg.mtime = new Date(msg.mtime);
	this.emit('message', msg);
	this.store.invalidateParents(msg, function(err) {
		if (err) console.error(err);
	});
};

Raja.prototype.send = function(msg) {
	if (!msg.key) {
		return this.error(new Error("Missing key in " + JSON.stringify(msg)));
	}
	if (!msg.mtime) msg.mtime = Date.now();
	else if (msg.mtime instanceof Date) msg.mtime = msg.mtime.getTime();
	this.io.send(msg);
};

Raja.prototype.error = function(err) {
	// called when an error happened while using raja
	if (err) console.error(err);
};

Raja.prototype.shallow = shallow;

Raja.prototype.dataEqual = dataEqual;

Raja.prototype.create = function(obj, from) {
	if (typeof obj == "string") obj = {url: obj};
	if (from) {
		obj.headers = this.headers(from);
		if (from.builder) obj.builder = from.builder;
	}
	if (!obj.url) {
		if (from && from.url) {
			obj.url = from.url;
		} else if (obj.key) {
			obj.url = keyObj(obj.key).url;
		} else {
			throw new Error("Missing key in " + JSON.stringify(obj));
		}
	}
	obj.key = this.reskey(obj);
	var inst = this.unsaved[obj.key];
	if (!inst) {
		inst = Object.create(Resource.prototype, {});
		this.shallow(obj, inst);
		debug('create unsaved', inst.key);
		Object.defineProperty(inst, 'raja', {
			enumerable: false,
			value: this
		});
		this.unsaved[inst.key] = inst;
	} else {
		debug('update unsaved', obj.key);
		this.shallow(obj, inst);
	}
	return inst;
};

Raja.prototype.reskey = function(obj, havingHeaders) {
	if (typeof obj == "string") obj = {
		url: obj
	};
	if (!obj.headers && havingHeaders) obj.headers = this.headers(havingHeaders);
	var xRaja = obj.headers && obj.headers['X-Raja'];
	var url = obj.url;
	if (xRaja == this.opts.namespace) {
		url = URL.parse(url).path;
	}
	return this.key(url, this.vary(obj.headers));
};

Raja.prototype.reqkey = function(obj, havingHeaders) {
	if (typeof obj == "string") obj = {
		url: obj
	};
	if (!obj.headers && havingHeaders) obj.headers = this.headers(havingHeaders);
	var xRaja = obj.headers && obj.headers['X-Raja'];
	var url = obj.url;
	if (xRaja == this.opts.namespace) {
		url = URL.parse(url).path;
	}
	return this.key(url, this.variant(obj.headers));
};

Raja.prototype.retrieve = function(url, havingHeaders, cb) {
	if (!cb && typeof havingHeaders == "function") {
		cb = havingHeaders;
		havingHeaders = null;
	}
	var key = this.reskey(url, havingHeaders);
	this.store.get(key, cb);
};

Raja.prototype.upsert = function(obj, cb) {
	debug('upsert', obj);
	this.retrieve(obj.url, obj, function(err, resource) {
		if (err) return cb(err);
		if (!resource) {
			debug('upsert not found, creating');
			resource = this.create(obj);
		} else {
			debug('upsert found, updating', resource.key, resource.headers);
			raja.shallow(obj, resource);
			resource.headers = this.headers(resource);
		}
		cb(null, resource);
	}.bind(this));
};

function Resource() { /* initialization code will not be called by Object.create */ }

Resource.prototype.depend = function(resource, havingHeaders) {
	if (!this.resources) this.resources = {};
	if (!(resource instanceof Resource)) resource = this.raja.create(resource, havingHeaders);
	if (this.key == resource.key) console.warn('A resource cannot depend on itself', this.key);
	else this.resources[resource.key] = resource;
	debug('depend', this.key, resource.key);
};

Resource.prototype.undepend = function(resource) {
	if (!this.resources || !resource) return;
	var key;
	if (typeof resource == "string") key = resource;
	else key = this.raja.reskey(resource);
	delete this.resources[key];
};

Resource.prototype.belong = function(resource, havingHeaders) {
	if (!this.parents) this.parents = {};
	if (!(resource instanceof Resource)) resource = this.raja.create(resource, havingHeaders);
	if (this.key == resource.key) console.warn('A resource cannot belong to itself', this.key);
	else this.parents[resource.key] = resource;
	debug('belong', this.key, resource.key);
};

Resource.prototype.unbelong = function(resource) {
	if (!this.parents || !resource) return;
	var key;
	if (typeof resource == "string") key = resource;
	else key = this.raja.reskey(resource);
	delete this.parents[key];
};

Resource.prototype.save = function(cb) {
	var raja = this.raja;
	cb = cb || raja.error;
	if (!this.url) {
		if (this.key) {
			this.url = keyObj(this.key).url;
		} else {
			throw new Error("Missing key in " + JSON.stringify(this));
		}
	}
	var key = raja.reskey(this);
	if (this.key == key) {
		raja.store.set(key, this, function(err, resource) {
			delete raja.unsaved[key];
			cb(err, resource);
		});
	} else {
		var oldkey = this.key;
		this.key = key;
		var copy = raja.create(this);
		this.key = oldkey;
		if (this.page) delete this.page;
		this.remove(raja.error);
		if (copy.page && copy.page.unlock) copy.page.unlock = function(copy) {
			debug("call page copy unlock");
			delete copy.page;
		}.bind(this, copy);
		copy.save(cb);
	}
};

Resource.prototype.load = function(remote, opts, cb) {
	if (!this.resources) this.resources = {};
	if (!cb && typeof opts == "function") {
		cb = opts;
		opts = null;
	}
	if (!opts) opts = {};
	remote = agent.resolve(this.url, remote);
	var localns = this.headers && this.headers['X-Raja'];
	var remotens = opts.headers && opts.headers['X-Raja'];
	if (localns && !remotens && URL.parse(this.url).host == URL.parse(remote).host) {
		if (!opts.headers) opts.headers = {};
		opts.headers['X-Raja'] = localns;
	}
	this.raja.proxies.remote.get(remote, opts, function(err, resource) {
		if (err >= 400 && err < 500) {
			this.undepend(resource);
		} else if (!err) {
			this.depend(resource);
		}
		cb(err, resource);
	}.bind(this));
};

Resource.prototype.invalidate = function(cb) {
	this.raja.store.invalidate(this.key, cb);
};

Resource.prototype.is = function(mimetype) {
	return typeis(this.headers && this.headers['Content-Type'], mimetype);
};

Resource.prototype.remove = function(cb) {
	// remove from unsave, from limbo, from store, and orphan everywhere
	delete this.raja.unsaved[this.key];
	this.raja.store.del(this.key, cb);
};

Raja.prototype.headers = function(havingHeaders) {
	if (!havingHeaders) return {};
	var obj = {};
	var headers = havingHeaders.headers;
	var hasGet = !!havingHeaders.get;
	if (!headers && !hasGet) headers = havingHeaders;
	['Content-Type', 'Vary', 'ETag', 'Accept', 'X-Grant', 'X-Right', 'X-Author', 'X-Raja'].forEach(function(name) {
		var val = hasGet && havingHeaders.get(name) || headers && (headers[name] || headers[name.toLowerCase()]);
		if (val) obj[name] = val;
	});
	return obj;
};

Raja.prototype.vary = function(headers) {
	if (!headers) return;
	var vary = {};
	if (!headers.Vary) return vary;
	headers.Vary.split(',').forEach(function(header) {
		header = header.trim();
		var val = headers[header] || "";
		if (!val) return;
		if (header == 'Content-Type') {
			var type = typeis(val, ['html', 'json', 'xml']);
			if (type && type != 'html') vary.type = type; // store html as default
		} else if (header == 'X-Grant') {
			vary.right = normalizeList(val);
		} else if (header == 'X-Author') {
			vary.author = val;
		}
	});
	return vary;
};

Raja.prototype.variant = function(headers) {
	if (!headers) return;
	var variant = {};
	var accept = headers['Accept'] || "";
	if (accept) {
		// html is considered the default type
		var type = typeis(accept, ['html', 'json', 'xml']);
		if (type && type != 'html') variant.type = type;
	}
	var right = headers['X-Right'] || "";
	if (right) {
		variant.right = normalizeList(right);
	}
	var author = headers['X-Author'];
	if (author) {
		variant.author = author;
	}
	return variant;
};

Raja.prototype.key = function(url, obj) {
	if (!obj) return url;
	for (var k in obj) if (obj[k] == null) delete obj[k];
	var str = obj && qs.stringify(obj);
	if (str) url = str + ' ' + url;
	return url;
};

Raja.prototype.keyObj = keyObj;

function shallow(src, dst) {
	dst = dst || {};
	if (!src) return dst;
	Object.keys(src).forEach(function(key) {
		if (src.hasOwnProperty(key) == false) return;
		var val = src[key];
		if (val === undefined) return;
		if (key == "resources" || key == "parents" || key == "headers") {
			val = shallow(val, dst[key] || {});
		}
		if ((key == 'resources' || key == 'parents') && val && Object.keys(val).length == 0 && dst[key] && Object.keys(dst[key]).length > 0) {
			console.info("not overwriting", key, dst[key], "with empty object");
			return;
		}
		dst[key] = val;
	});
	return dst;
}

function keyObj(key) {
	if (!key) return {};
	var obj;
	var find = /^(.*\s)?((?:https?:\/\/|\/).+)$/.exec(key);
	if (find != null && find.length == 3) {
		var query = find[1];
		if (query) {
			obj = qs.parse(query);
		} else {
			obj = {};
		}
		obj.url = find[2];
	} else {
		obj = {url: key};
	}
	return obj;
}

function normalizeList(strlist) {
	return strlist.split(',').map(function(str) {return str.trim();}).sort().join(',');
}

function dataEqual(a, b) {
	if (a == null || b == null) return a === b;
	var bufa = Buffer.isBuffer(a);
	var bufb = Buffer.isBuffer(b);
	if (!bufa && !bufb) return a === b;
	if (!bufa) a = new Buffer(a);
	if (!bufb) b = new Buffer(b);
	return bufferEqual(a, b);
}

<|endoftext|>


<commit_before>'use strict';
var core = {};
var fs = require('fs');
var mkdirp = require('mkdirp');
var config = require('nconf');
var winston = require('winston');
var CronJob = require('cron').CronJob;
var getLoggerSettings = function(consoleLog) {
  var settings = {
    transports: [
      new winston.transports.File({ filename: process.env.HOME + "/.evac/evac.log", json: false})
    ],
    exceptionHandlers: [
      new winston.transports.File({ filename: process.env.HOME + "/.evac/evac.log", json: false})
    ]
  };

  if (consoleLog) {
    settings.transports.push(new winston.transports.Console({colorize: true, timestamp: true}));
    settings.exceptionHandlers.push(new winston.transports.Console({colorize: true, timestamp: true}));
  }

  return settings;
};

var logger = new (winston.Logger)(getLoggerSettings(false));
var pluginPaths = [__dirname + '/plugin'];

var parseRecipeFile = function(path) {
  try {
    config.use('file', {file: path});
  } catch(e) {
    logger.error("Error parsing your evac recipe file.");
    process.exit(1);
  }

  config.load(function (err, conf) {
    if (err) {
      logger.error('Failed to load configuration from ' + path);
      process.exit(1);
    }

    core.parseRecipeJson(conf, function(err, message){
      if (err) {
        logger.error(message + " plugin - raise error.");
        logger.error(err);
        process.exit(1);
      }
    });
  });
};

var parseCronFile = function(path) {
  try {
    config.use('file', {file: path});
  } catch(e) {
    logger.error("Error parsing your cron configuration file.");
    process.exit(1);
  }

  config.load(function (err, cronConfig) {
    if (err) {
      logger.error('Failed to load configuration from ' + path);
      process.exit(1);
    }

    var jobs = [];

    for(var config in cronConfig) {
      var oneConfig = cronConfig[config];

      for (var schedule in oneConfig) {
        var job = new CronJob({
          cronTime: schedule,
          onTick: function() {
            parseRecipeFile(oneConfig[schedule]);
          },
          start: true
        });
      }
    }
  });
};

core.parseRecipeJson = function(recipe, callback) {
  if (!recipe.filter) {
    recipe.filter = {
      "through": {}
    };
  }

  var pluginType = ['out', 'filter', 'in'];
  var plugins = {};
  var args = {};

  pluginPaths.forEach(function(path) {
    pluginType.forEach(function(type) {
      for (var plugin in recipe[type]) {
        var pluginFileName = path + '/' + type + '/' + plugin.toLowerCase() + '.js';
        logger.debug("Search plugin : " + pluginFileName);

        if (fs.existsSync(pluginFileName)) {
          plugins[type] = require(pluginFileName);
          args[type] = recipe[type][plugin];
        }
      }
    });
  });

  try {
    plugins['in'].load(args['in'], function(err, outputs){
      if (err) {
        callback(err, "input");
        return;
      }

      logger.info("Input plugin called.");

      for (var i=0; i<outputs.length; i++) {
        plugins['filter'].filter(args['filter'], outputs[i], function(err, output){
          if (err) {
            callback(err, "filter");
            return;
          }

          logger.info("Filter plugin called.");

          if (output == "") {
            logger.info("Filter plugin no output.");
          } else {
            plugins['out'].output(args['out'], output, function(err, output){
              if (err) {
                callback(err, "output");
                return;
              }

              logger.info("Output plugin received: " + output);
              logger.info("Output finished.");

              callback(false, null);
              return;
            });
          }

        });
      }
    });
  } catch(e) {
    logger.error(e.message);
    callback(e, e.message);
    return;
  }
};

core.start = function(program, args) {
  mkdirp.sync(process.env.HOME + "/.evac");

  logger = new (winston.Logger)(getLoggerSettings(program.verbose));

  if (program.path) {
    pluginPaths.push(program.path);
  }

  if (program.cron) {
    // cron mode
    parseCronFile(args[0]);
  } else {
    // normal mode
    logger.info('Settings are loaded from a file :' + args[0]);
    parseRecipeFile(args[0]);
  }
}

module.exports = core;
<commit_msg>プラグイン走査の順番を修正<commit_after>'use strict';
var core = {};
var fs = require('fs');
var mkdirp = require('mkdirp');
var config = require('nconf');
var winston = require('winston');
var CronJob = require('cron').CronJob;
var getLoggerSettings = function(consoleLog) {
  var settings = {
    transports: [
      new winston.transports.File({ filename: process.env.HOME + "/.evac/evac.log", json: false})
    ],
    exceptionHandlers: [
      new winston.transports.File({ filename: process.env.HOME + "/.evac/evac.log", json: false})
    ]
  };

  if (consoleLog) {
    settings.transports.push(new winston.transports.Console({colorize: true, timestamp: true}));
    settings.exceptionHandlers.push(new winston.transports.Console({colorize: true, timestamp: true}));
  }

  return settings;
};

var logger = new (winston.Logger)(getLoggerSettings(false));
var pluginPaths = [__dirname + '/plugin'];

var parseRecipeFile = function(path) {
  try {
    config.use('file', {file: path});
  } catch(e) {
    logger.error("Error parsing your evac recipe file.");
    process.exit(1);
  }

  config.load(function (err, conf) {
    if (err) {
      logger.error('Failed to load configuration from ' + path);
      process.exit(1);
    }

    core.parseRecipeJson(conf, function(err, message){
      if (err) {
        logger.error(message + " plugin - raise error.");
        logger.error(err);
        process.exit(1);
      }
    });
  });
};

var parseCronFile = function(path) {
  try {
    config.use('file', {file: path});
  } catch(e) {
    logger.error("Error parsing your cron configuration file.");
    process.exit(1);
  }

  config.load(function (err, cronConfig) {
    if (err) {
      logger.error('Failed to load configuration from ' + path);
      process.exit(1);
    }

    var jobs = [];

    for(var config in cronConfig) {
      var oneConfig = cronConfig[config];

      for (var schedule in oneConfig) {
        var job = new CronJob({
          cronTime: schedule,
          onTick: function() {
            parseRecipeFile(oneConfig[schedule]);
          },
          start: true
        });
      }
    }
  });
};

core.parseRecipeJson = function(recipe, callback) {
  if (!recipe.filter) {
    recipe.filter = {
      "through": {}
    };
  }

  var pluginType = ['out', 'filter', 'in'];
  var plugins = {};
  var args = {};

  pluginType.forEach(function(type) {
    for (var plugin in recipe[type]) {
      pluginPaths.forEach(function(path) {
        var pluginFileName = path + '/' + type + '/' + plugin.toLowerCase() + '.js';
        logger.debug("Search plugin : " + pluginFileName);

        if (fs.existsSync(pluginFileName)) {
          plugins[type] = require(pluginFileName);
          args[type] = recipe[type][plugin];
        }
      });
    }
  });

  try {
    plugins['in'].load(args['in'], function(err, outputs){
      if (err) {
        callback(err, "input");
        return;
      }

      logger.info("Input plugin called.");

      for (var i=0; i<outputs.length; i++) {
        plugins['filter'].filter(args['filter'], outputs[i], function(err, output){
          if (err) {
            callback(err, "filter");
            return;
          }

          logger.info("Filter plugin called.");

          if (output == "") {
            logger.info("Filter plugin no output.");
          } else {
            plugins['out'].output(args['out'], output, function(err, output){
              if (err) {
                callback(err, "output");
                return;
              }

              logger.info("Output plugin received: " + output);
              logger.info("Output finished.");

              callback(false, null);
              return;
            });
          }

        });
      }
    });
  } catch(e) {
    logger.error(e.message);
    callback(e, e.message);
    return;
  }
};

core.start = function(program, args) {
  mkdirp.sync(process.env.HOME + "/.evac");

  logger = new (winston.Logger)(getLoggerSettings(program.verbose));

  if (program.path) {
    pluginPaths.push(program.path);
  }

  if (program.cron) {
    // cron mode
    parseCronFile(args[0]);
  } else {
    // normal mode
    logger.info('Settings are loaded from a file :' + args[0]);
    parseRecipeFile(args[0]);
  }
}

module.exports = core;
<|endoftext|>


<commit_before>/**
 * Created by IntelliJ IDEA.
 * User: sbortman
 * Date: 1/30/12
 * Time: 4:57 PM
 * To change this template use File | Settings | File Templates.
 */

(function ()
{
    var Dom = YAHOO.util.Dom,
            Event = YAHOO.util.Event;

    Event.addListener(window, 'resize', bodyOnResize);

    Event.onDOMReady( function ()
    {
        var outerLayout = new YAHOO.widget.Layout( {
           // minWidth:1000,
           // minHeight:500,
            units:[
                { position:'top', height:50, body:'top1', scroll: null, zIndex: 2 },
                /*{ position:'right', header:'', width:200, resize:true, gutter:'0px', collapse:true, scroll:true, body:'right1', animate:false },*/
                { position:'bottom', height:25, body:'bottom1' },
                { position:'left', header:'', width:200, resize:true, body:'left1', gutter:'0px', collapse:true, scroll:true, animate:false },
                { position:'center', minWidth:400, minHeight:200 }
            ]
        } );
        outerLayout.on('resize', function(evt) {
          //  if(OMAR.imageManipulator)
          //  {
          //      OMAR.imageManipulator.checkResize();
           // }
          //bodyOnResize();
            if(OMAR.imageManipulator)
            {
                // all IE's seem to be shifted when just using bodyOnResize
                // I will force a reload by tacking on a variable at the end
                //
                if(OpenLayers.BROWSER_NAME == "msie")
                {
                    window.location.href = window.location.href;
                }
                else
                {
                    bodyOnResize();
                }
            }
        });
        outerLayout.on( 'render', function ()
        {
            var el = outerLayout.getUnitByPosition( 'center' ).get( 'wrap' );
            var innerLayout = new YAHOO.widget.Layout( el, {
                parent:outerLayout,
                minWidth:400,
                minHeight:200,
                units:[
                    { position:'top',  height:35, proxy:false, body:'top2' },
                    { position:'bottom',  height:30, proxy:false, body:'bottom2' },
                    { position:'center', body:'center2', gutter:'0px' }
                ]
            } );
            innerLayout.render();
            bodyOnResize();
            Dom.setStyle( document.body, 'visibility', 'visible' );
        } );
        outerLayout.render();
        outerLayout.getUnitByPosition( "left" ).on( "collapse", function ()
        {
            bodyOnResize();
        } );

        outerLayout.getUnitByPosition( "left" ).on( "expand", function ()
        {
            bodyOnResize();
        } );

        outerLayout.getUnitByPosition( "right" ).on( "collapse", function ()
        {
            bodyOnResize();
        } );

        outerLayout.getUnitByPosition( "right" ).on( "expand", function ()
        {
            bodyOnResize();
        } );
       init();

    } );
})();

var bodyOnResize = function ()
{
    if(OMAR.imageManipulator)
    {
        //window.location.reload()
        OMAR.imageManipulator.checkResize();
    }
    else
    {
        init();
    }
   // map.updateSize();
};
<commit_msg>Trying to add in IE hack<commit_after>/**
 * Created by IntelliJ IDEA.
 * User: sbortman
 * Date: 1/30/12
 * Time: 4:57 PM
 * To change this template use File | Settings | File Templates.
 */

(function ()
{
    var Dom = YAHOO.util.Dom,
            Event = YAHOO.util.Event;

    Event.addListener(window, 'resize', bodyOnResize);

    Event.onDOMReady( function ()
    {
        var outerLayout = new YAHOO.widget.Layout( {
           // minWidth:1000,
           // minHeight:500,
            units:[
                { position:'top', height:50, body:'top1', scroll: null, zIndex: 2 },
                /*{ position:'right', header:'', width:200, resize:true, gutter:'0px', collapse:true, scroll:true, body:'right1', animate:false },*/
                { position:'bottom', height:25, body:'bottom1' },
                { position:'left', header:'', width:200, resize:true, body:'left1', gutter:'0px', collapse:true, scroll:true, animate:false },
                { position:'center', minWidth:400, minHeight:200 }
            ]
        } );
        outerLayout.on('resize', function(evt) {
            if(OMAR.imageManipulator)
            {
                OMAR.imageManipulator.checkResize();
            }
        });
        outerLayout.on( 'render', function ()
        {
            var el = outerLayout.getUnitByPosition( 'center' ).get( 'wrap' );
            var innerLayout = new YAHOO.widget.Layout( el, {
                parent:outerLayout,
                minWidth:400,
                minHeight:200,
                units:[
                    { position:'top',  height:35, proxy:false, body:'top2' },
                    { position:'bottom',  height:30, proxy:false, body:'bottom2' },
                    { position:'center', body:'center2', gutter:'0px' }
                ]
            } );
            innerLayout.render();
            bodyOnResize();
            Dom.setStyle( document.body, 'visibility', 'visible' );
        } );
        outerLayout.render();
        outerLayout.getUnitByPosition( "left" ).on( "collapse", function ()
        {
            bodyOnResize();
        } );

        outerLayout.getUnitByPosition( "left" ).on( "expand", function ()
        {
            bodyOnResize();
        } );

        outerLayout.getUnitByPosition( "right" ).on( "collapse", function ()
        {
            bodyOnResize();
        } );

        outerLayout.getUnitByPosition( "right" ).on( "expand", function ()
        {
            bodyOnResize();
        } );
       init();

    } );
})();

var bodyOnResize = function ()
{
    if(OMAR.imageManipulator)
    {
        //window.location.reload()
        OMAR.imageManipulator.checkResize();
    }
    else
    {
        init();
    }
   // map.updateSize();
};
<|endoftext|>


<commit_before>var url = require('url')
  , path = require('path')
  , qs = require('querystring')
  , redis = require('redis')
  , _ = require('lodash')
  , async = require('async')
  , moment = require('moment')

  , jsonUtils = require('./json')

  , redisUrl = process.env.REDIS_URL
  , redisClient

  , DATE_FORMAT = 'YYYY-MM-DDTHH:mm:ss'

  , allPathKeys = require('./config').allPathKeys
  , staticPathKeys = require('./config').staticPathKeys
  , pathKeys = _.difference(allPathKeys, staticPathKeys)

  , csvKeys = ['DataAsOf', 'Speed', 'TravelTime']
  , detailKeys = ['Borough', 'linkName', 'linkPoints']
  ;

function isInt(value) {
    return ! isNaN(value)
        && parseInt(Number(value)) == value
        && ! isNaN(parseInt(value, 10));
}

function normalizeData(data) {
    var out = {};
    if (data.length == pathKeys.length) {
        _.each(pathKeys, function(key, i) {
            out[key] = data[i];
        });
    } else {
        // TODO: Remove this legacy code after all old data moves out of window.
        _.each(['Speed', 'TravelTime'], function(key, i) {
            out[key] = data[i + 1];
        });
    }
    out.DataAsOf = moment(new Date(parseInt(data.timestamp) * 1000))
                       .format(DATE_FORMAT);
    return out;
}

function fetchDetails(ids, callback) {
    var fetchers = {};
    _.each(ids, function(id) {
        fetchers[id] = function(localCallback) {
            redisClient.hgetall('routeProps:' + id, localCallback);
        };
    });
    async.parallel(fetchers, callback);
}

function fetchKeys(includeDetails, callback) {
    redisClient.keys('[0-9]*', function(error, keys) {
        if (error) return callback(error);
        if (includeDetails) {
            fetchDetails(keys, callback);
        } else {
            callback(null, _.map(keys, function(k) { return parseInt(k); }));
        }
    });
}

function renderKeys(query, res) {
    fetchKeys(query.includeDetails, function(err, keys) {
        if (err) {
            return res.status(404).send(err.message);
        }
        dataOut = {
            paths: keys
          , count: _.size(keys)
        }
        jsonUtils.render(dataOut, res, query.callback);
    });
}

function respondInJson(error, data, props, callback, res) {
    if (error) {
        jsonUtils.renderErrors([error], res, query.callback);
    } else {
        jsonUtils.render({
            path: data
          , properties: props
          , count: data.length
        }, res, callback);
    }
}

function respondInCsv(error, data, props, exclude, res) {
    var csv = ''
      , headers = []
      , excludes = [];
    if (exclude) {
        excludes = exclude.split(',');
    }
    res.setHeader('Content-Type', 'text');
    if (error) {
        res.statusCode = 400;
        res.end(error.message);
    } else {
        // Headers are the keys
        headers = _.filter(csvKeys, function(key) {
            return ! _.contains(excludes, key);
        });
        csv += headers.join(',') + '\n';
        _.each(data, function(pathData, index) {
            var rowOut = _.map(csvKeys, function(key) {
                if (! _.contains(excludes, key)) {
                    return pathData[key];
                }
            });
            rowOut = _.without(rowOut, undefined);
            csv += rowOut.join(',') + '\n';
        });
        res.end(csv);
    }
}

function unzipScores(redisData) {
    var out = [];
    _.each(redisData, function(data, index) {
        if (index % 2 != 0) {
            out[out.length - 1].timestamp = parseInt(data);
        } else {
            out.push(JSON.parse(data));
        }
    });
    return out.reverse();
}

function renderRouteData(id, query, extname, res) {
    var since = query.since || '-inf'
      , until = query.until || '+inf'
      , limit = query.limit
      , fetchArgs = [id, until, since, 'WITHSCORES']
      ;
    if (limit) {
        fetchArgs = fetchArgs.concat(['LIMIT', 0, parseInt(limit)])
    }
    redisClient.hgetall('routeProps:' + id, function(keyError, props) {
        // TODO: handle this keyError.
        redisClient.zrevrangebyscore(fetchArgs, function(error, values) {
            var data = unzipScores(values);
            data = _.map(data, normalizeData);
            if (extname == '.csv') {
                respondInCsv(error, data, props, query.exclude, res);
            } else {
                respondInJson(error, data, props, query.callback, res);
            }
        });
    });
}

function trafficData(req, res) {
    var trafficRouteId = req.params.trafficRoute
      , requestUrl = url.parse(req.url)
      , query = qs.parse(requestUrl.query)
      ;
    if (_.contains(trafficRouteId, '.')) {
        trafficRouteId = trafficRouteId.split('.').shift();
    }
    if (! req.params || ! req.params.trafficRoute) {
        res.status(404).send('Not found');
    } else if (req.params.trafficRoute == 'paths') {
        renderKeys(query, res);
    } else if (isInt(trafficRouteId)) {
        trafficRouteId = parseInt(req.params.trafficRoute);
        renderRouteData(trafficRouteId, query, path.extname(req.params.trafficRoute), res);
    } else {
        res.status(404).send('Not found');
    }
}

function initialize(callback) {
    console.log('Data service connecting to Redis at %s...', redisUrl);
    var connection = url.parse(redisUrl);
    redisClient = redis.createClient(connection.port, connection.hostname);
    if (connection.auth) {
        redisClient.auth(connection.auth.split(":")[1]);
    }
    redisClient.on('connect', function() {
        console.log('Data service connected to Redis!');
        callback(null, trafficData);
    });
    redisClient.on('error', function(error) {
        callback(error);
    });
}

// This service queries redis for traffic data in response to HTTP requests.
module.exports = initialize;
<commit_msg>Changed output date format (not UTC)<commit_after>var url = require('url')
  , path = require('path')
  , qs = require('querystring')
  , redis = require('redis')
  , _ = require('lodash')
  , async = require('async')
  , moment = require('moment')

  , jsonUtils = require('./json')

  , redisUrl = process.env.REDIS_URL
  , redisClient

  , DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss'

  , allPathKeys = require('./config').allPathKeys
  , staticPathKeys = require('./config').staticPathKeys
  , pathKeys = _.difference(allPathKeys, staticPathKeys)

  , csvKeys = ['DataAsOf', 'Speed', 'TravelTime']
  , detailKeys = ['Borough', 'linkName', 'linkPoints']
  ;

function isInt(value) {
    return ! isNaN(value)
        && parseInt(Number(value)) == value
        && ! isNaN(parseInt(value, 10));
}

function normalizeData(data) {
    var out = {};
    if (data.length == pathKeys.length) {
        _.each(pathKeys, function(key, i) {
            out[key] = data[i];
        });
    } else {
        // TODO: Remove this legacy code after all old data moves out of window.
        _.each(['Speed', 'TravelTime'], function(key, i) {
            out[key] = data[i + 1];
        });
    }
    out.DataAsOf = moment(new Date(parseInt(data.timestamp) * 1000))
                       .format(DATE_FORMAT);
    return out;
}

function fetchDetails(ids, callback) {
    var fetchers = {};
    _.each(ids, function(id) {
        fetchers[id] = function(localCallback) {
            redisClient.hgetall('routeProps:' + id, localCallback);
        };
    });
    async.parallel(fetchers, callback);
}

function fetchKeys(includeDetails, callback) {
    redisClient.keys('[0-9]*', function(error, keys) {
        if (error) return callback(error);
        if (includeDetails) {
            fetchDetails(keys, callback);
        } else {
            callback(null, _.map(keys, function(k) { return parseInt(k); }));
        }
    });
}

function renderKeys(query, res) {
    fetchKeys(query.includeDetails, function(err, keys) {
        if (err) {
            return res.status(404).send(err.message);
        }
        dataOut = {
            paths: keys
          , count: _.size(keys)
        }
        jsonUtils.render(dataOut, res, query.callback);
    });
}

function respondInJson(error, data, props, callback, res) {
    if (error) {
        jsonUtils.renderErrors([error], res, query.callback);
    } else {
        jsonUtils.render({
            path: data
          , properties: props
          , count: data.length
        }, res, callback);
    }
}

function respondInCsv(error, data, props, exclude, res) {
    var csv = ''
      , headers = []
      , excludes = [];
    if (exclude) {
        excludes = exclude.split(',');
    }
    res.setHeader('Content-Type', 'text');
    if (error) {
        res.statusCode = 400;
        res.end(error.message);
    } else {
        // Headers are the keys
        headers = _.filter(csvKeys, function(key) {
            return ! _.contains(excludes, key);
        });
        csv += headers.join(',') + '\n';
        _.each(data, function(pathData, index) {
            var rowOut = _.map(csvKeys, function(key) {
                if (! _.contains(excludes, key)) {
                    return pathData[key];
                }
            });
            rowOut = _.without(rowOut, undefined);
            csv += rowOut.join(',') + '\n';
        });
        res.end(csv);
    }
}

function unzipScores(redisData) {
    var out = [];
    _.each(redisData, function(data, index) {
        if (index % 2 != 0) {
            out[out.length - 1].timestamp = parseInt(data);
        } else {
            out.push(JSON.parse(data));
        }
    });
    return out.reverse();
}

function renderRouteData(id, query, extname, res) {
    var since = query.since || '-inf'
      , until = query.until || '+inf'
      , limit = query.limit
      , fetchArgs = [id, until, since, 'WITHSCORES']
      ;
    if (limit) {
        fetchArgs = fetchArgs.concat(['LIMIT', 0, parseInt(limit)])
    }
    redisClient.hgetall('routeProps:' + id, function(keyError, props) {
        // TODO: handle this keyError.
        redisClient.zrevrangebyscore(fetchArgs, function(error, values) {
            var data = unzipScores(values);
            data = _.map(data, normalizeData);
            if (extname == '.csv') {
                respondInCsv(error, data, props, query.exclude, res);
            } else {
                respondInJson(error, data, props, query.callback, res);
            }
        });
    });
}

function trafficData(req, res) {
    var trafficRouteId = req.params.trafficRoute
      , requestUrl = url.parse(req.url)
      , query = qs.parse(requestUrl.query)
      ;
    if (_.contains(trafficRouteId, '.')) {
        trafficRouteId = trafficRouteId.split('.').shift();
    }
    if (! req.params || ! req.params.trafficRoute) {
        res.status(404).send('Not found');
    } else if (req.params.trafficRoute == 'paths') {
        renderKeys(query, res);
    } else if (isInt(trafficRouteId)) {
        trafficRouteId = parseInt(req.params.trafficRoute);
        renderRouteData(trafficRouteId, query, path.extname(req.params.trafficRoute), res);
    } else {
        res.status(404).send('Not found');
    }
}

function initialize(callback) {
    console.log('Data service connecting to Redis at %s...', redisUrl);
    var connection = url.parse(redisUrl);
    redisClient = redis.createClient(connection.port, connection.hostname);
    if (connection.auth) {
        redisClient.auth(connection.auth.split(":")[1]);
    }
    redisClient.on('connect', function() {
        console.log('Data service connected to Redis!');
        callback(null, trafficData);
    });
    redisClient.on('error', function(error) {
        callback(error);
    });
}

// This service queries redis for traffic data in response to HTTP requests.
module.exports = initialize;
<|endoftext|>