<commit_before>'use strict';

var Promise = require('bluebird'),
  _ = require('lodash'),
  logger = require('../../core/Logger')('storage');

module.exports = function (sequelize, DataTypes) {

  var Room = sequelize.define('Room', {
    // globally unique xmpp room name within one domain
    name: {
      type: DataTypes.STRING,
      unique: true,
      allowNull: false,
      onDelete: 'cascade'
    },
    subject: {
      type: DataTypes.STRING
    },
    description: {
      type: DataTypes.TEXT
    }
  }, {
    associate: function (models) {
      models.Room.hasMany(models.User, {
        through: models.RoomMember,
        as: 'members',
        onDelete: 'cascade'
      });
      models.Room.hasMany(models.RoomMessage, {
        as: 'messages',
        onDelete: 'cascade'
      });
      models.Room.hasMany(models.RoomConfiguration, {
        as: 'configurations',
        onDelete: 'cascade'
      });
    },
    instanceMethods: {

      /**
       * Join will:
       * - add a membership if not already established
       * - verify that the membership is properly, if the user
       *   is already a member
       * - possibly reset the new nickname
       */
      join: function (user, params, options) {
        logger.debug('user ' + JSON.stringify(user) + ' joins room ' + this.name);
        var self = this;
        options = options || {};

        // verify parameters
        if (!user ||  !params ||  !params.role ||  !params.affiliation || !params.nickname) {
          throw new Error('wrong parameters');
        } else {
          return self.getMembers({
            where: {
              'User.id': user.id
            }
          }, options).then(function (members) {
            logger.debug('found members: ' + JSON.stringify(members));

            // user is already part of this room
            if (members && members.length > 0) {
              var roomUser = members[0];
              logger.debug('user is already member: ' + JSON.stringify(roomUser))

              // update data
              // @see http://xmpp.org/extensions/xep-0045.html#roles-default
              switch (roomUser.RoomMember.affiliation) {

              case sequelize.model('RoomMember').Affiliation.Owner:
                roomUser.RoomMember.affiliation = sequelize.model('RoomMember').Affiliation.Owner;
                roomUser.RoomMember.role = sequelize.model('RoomMember').Role.Moderator;
                break;
              case sequelize.model('RoomMember').Affiliation.Admin:
                roomUser.RoomMember.affiliation = sequelize.model('RoomMember').Affiliation.Admin;
                roomUser.RoomMember.role = sequelize.model('RoomMember').Role.Moderator;
                break;
              default:
                roomUser.RoomMember.affiliation = sequelize.model('RoomMember').Affiliation.Participant;
                roomUser.RoomMember.role = sequelize.model('RoomMember').Role.Visitor;
                break;
              }

              roomUser.RoomMember.nickname = params.nickname;
              return roomUser.RoomMember.save(options).then(function () {
                return roomUser;
              })
            } else {
              logger.debug('add user as new member ' + user.jid);

              var opts = {
                'RoomMember.role': params.role,
                'RoomMember.affiliation': params.affiliation,
                'RoomMember.nickname': params.nickname
              }

              // add user to room
              return self.addMember(user, _.merge(options, opts)).then(function(){
                logger.debug('user added, retrieve updated user')
                return self.getMembers({
                  where: {
                    'User.id': user.id
                  }
                }, options)
              }).then(function(members){
                return members[0];
              })
            }
          })
        }
      },

      /**
       * leave the rum
       */
      leave: function (user, options) {
        return this.getMembers({
          where: {
            'User.id': user.id
          }
        }, options).then(function (members) {
          var roomUser = members[0];
          roomUser.RoomMember.role = sequelize.model('RoomMember').Role.None;
          roomUser.RoomMember.save()
        })
      },

      /**
       * verifies that the given user is a member of this room
       */
      isMember: function (user, options) {
        var self = this;

        // checkout if the current user is member
        return self.getMembers({
          where: {
            'User.id': user.id
          }
        }, options).then(function (users) {
          logger.debug('found users: ' + JSON.stringify(users));

          return new Promise(function (resolve, reject) {
            var user = _.first(users)
            if (user) {
              resolve(user);
            } else {
              reject();
            }
          });
        })
      },

      /**
       * returns the nickname of a user for this specific
       * room.
       */
      nickname: function (user, options) {
        var self = this;

        // checkout if the current user is member
        return self.getMembers({
          where: {
            'User.id': user.id
          }
        }, options).then(function (users) {
          return new Promise(function (resolve, reject) {
            var user = _.first(users)
            if (user) {
              resolve(user);
            } else {
              reject();
            }
          });
        })
      },

      addConfig : function (config, options) {
        var self = this;
        var promises = [];

        // iterate over attributes and add key values
        _.forEach(config, function(value, key) {

          promises.push(
            sequelize.model('RoomConfiguration').create({
              'key': key,
              'value': value
            }, options).then(function(configuration) {
              return self.addConfiguration(configuration, options);
            })
          );

        });

        return Promise.all(promises)
      },

      exportJSON: function () {

        var room = _.pick(this.toJSON(), ['name', 'subject', 'description', 'per_page', 'type']);

        // create event data, may not be availabe
        if (!_.isEmpty(this.event)) {
          room['event'] = this.event.toJSON();
        } else {
          logger.debug('event emtpy' + this.getDataValue('createdAt'));
          // recover date from creating time
          room['event'] = {
            'start': this.getDataValue('createdAt').getTime(), // creation date
            'end': this.getDataValue('createdAt').getTime() + 3600000 // creation date + 1h
          };
        }

        // create members data
        var owner = null;
        room['members'] = _.map(this.members, function (member) {
          if (member.RoomMember.affiliation === 'owner') {
            owner = member;
          }
          return {
            'name': member.name,
            'jid': member.jid,
            'nickname': member.RoomMember.nickname,
            'role': member.RoomMember.role,
            'affiliation': member.RoomMember.affiliation,
            'state': member.RoomMember.state
          }
        })

        // if we have information about the room
        if (owner && owner.jid) {
          room['owner'] = {
            'jid': owner.jid,
            'name': owner.name
          }
        }

        // export update information
        room['updatedAt'] = this.getDataValue('updatedAt').getTime()
        room['createdAt'] = this.getDataValue('createdAt').getTime()

        return room;
      }
    }
  });

  return Room;
};
<commit_msg>remove test code<commit_after>'use strict';

var Promise = require('bluebird'),
  _ = require('lodash'),
  logger = require('../../core/Logger')('storage');

module.exports = function (sequelize, DataTypes) {

  var Room = sequelize.define('Room', {
    // globally unique xmpp room name within one domain
    name: {
      type: DataTypes.STRING,
      unique: true,
      allowNull: false,
      onDelete: 'cascade'
    },
    subject: {
      type: DataTypes.STRING
    },
    description: {
      type: DataTypes.TEXT
    }
  }, {
    associate: function (models) {
      models.Room.hasMany(models.User, {
        through: models.RoomMember,
        as: 'members',
        onDelete: 'cascade'
      });
      models.Room.hasMany(models.RoomMessage, {
        as: 'messages',
        onDelete: 'cascade'
      });
      models.Room.hasMany(models.RoomConfiguration, {
        as: 'configurations',
        onDelete: 'cascade'
      });
    },
    instanceMethods: {

      /**
       * Join will:
       * - add a membership if not already established
       * - verify that the membership is properly, if the user
       *   is already a member
       * - possibly reset the new nickname
       */
      join: function (user, params, options) {
        logger.debug('user ' + JSON.stringify(user) + ' joins room ' + this.name);
        var self = this;
        options = options || {};

        // verify parameters
        if (!user ||  !params ||  !params.role ||  !params.affiliation || !params.nickname) {
          throw new Error('wrong parameters');
        } else {
          return self.getMembers({
            where: {
              'User.id': user.id
            }
          }, options).then(function (members) {
            logger.debug('found members: ' + JSON.stringify(members));

            // user is already part of this room
            if (members && members.length > 0) {
              var roomUser = members[0];
              logger.debug('user is already member: ' + JSON.stringify(roomUser))

              // update data
              // @see http://xmpp.org/extensions/xep-0045.html#roles-default
              switch (roomUser.RoomMember.affiliation) {

              case sequelize.model('RoomMember').Affiliation.Owner:
                roomUser.RoomMember.affiliation = sequelize.model('RoomMember').Affiliation.Owner;
                roomUser.RoomMember.role = sequelize.model('RoomMember').Role.Moderator;
                break;
              case sequelize.model('RoomMember').Affiliation.Admin:
                roomUser.RoomMember.affiliation = sequelize.model('RoomMember').Affiliation.Admin;
                roomUser.RoomMember.role = sequelize.model('RoomMember').Role.Moderator;
                break;
              default:
                roomUser.RoomMember.affiliation = sequelize.model('RoomMember').Affiliation.Participant;
                roomUser.RoomMember.role = sequelize.model('RoomMember').Role.Visitor;
                break;
              }

              roomUser.RoomMember.nickname = params.nickname;
              return roomUser.RoomMember.save(options).then(function () {
                return roomUser;
              })
            } else {
              logger.debug('add user as new member ' + user.jid);

              var opts = {
                'RoomMember.role': params.role,
                'RoomMember.affiliation': params.affiliation,
                'RoomMember.nickname': params.nickname
              }

              // add user to room
              return self.addMember(user, _.merge(options, opts)).then(function(){
                logger.debug('user added, retrieve updated user')
                return self.getMembers({
                  where: {
                    'User.id': user.id
                  }
                }, options)
              }).then(function(members){
                return members[0];
              })
            }
          })
        }
      },

      /**
       * leave the rum
       */
      leave: function (user, options) {
        return this.getMembers({
          where: {
            'User.id': user.id
          }
        }, options).then(function (members) {
          var roomUser = members[0];
          roomUser.RoomMember.role = sequelize.model('RoomMember').Role.None;
          roomUser.RoomMember.save()
        })
      },

      /**
       * verifies that the given user is a member of this room
       */
      isMember: function (user, options) {
        var self = this;

        // checkout if the current user is member
        return self.getMembers({
          where: {
            'User.id': user.id
          }
        }, options).then(function (users) {
          logger.debug('found users: ' + JSON.stringify(users));

          return new Promise(function (resolve, reject) {
            var user = _.first(users)
            if (user) {
              resolve(user);
            } else {
              reject();
            }
          });
        })
      },

      /**
       * returns the nickname of a user for this specific
       * room.
       */
      nickname: function (user, options) {
        var self = this;

        // checkout if the current user is member
        return self.getMembers({
          where: {
            'User.id': user.id
          }
        }, options).then(function (users) {
          return new Promise(function (resolve, reject) {
            var user = _.first(users)
            if (user) {
              resolve(user);
            } else {
              reject();
            }
          });
        })
      },

      addConfig : function (config, options) {
        var self = this;
        var promises = [];

        // iterate over attributes and add key values
        _.forEach(config, function(value, key) {

          promises.push(
            sequelize.model('RoomConfiguration').create({
              'key': key,
              'value': value
            }, options).then(function(configuration) {
              return self.addConfiguration(configuration, options);
            })
          );

        });

        return Promise.all(promises)
      },

      exportJSON: function () {

        var room = _.pick(this.toJSON(), ['name', 'subject', 'description', 'per_page', 'type']);

        // create members data
        var owner = null;
        room['members'] = _.map(this.members, function (member) {
          if (member.RoomMember.affiliation === 'owner') {
            owner = member;
          }
          return {
            'name': member.name,
            'jid': member.jid,
            'nickname': member.RoomMember.nickname,
            'role': member.RoomMember.role,
            'affiliation': member.RoomMember.affiliation,
            'state': member.RoomMember.state
          }
        })

        // if we have information about the room
        if (owner && owner.jid) {
          room['owner'] = {
            'jid': owner.jid,
            'name': owner.name
          }
        }

        // export update information
        room['updatedAt'] = this.getDataValue('updatedAt').getTime()
        room['createdAt'] = this.getDataValue('createdAt').getTime()

        return room;
      }
    }
  });

  return Room;
};
<|endoftext|>


<commit_before>(function(global) {
    'use strict';

    if (typeof module === 'object' && module.exports)
        module.exports = new checkProto();
    else if (!global.check)
        global.check = new checkProto();

    function checkProto() {
        /**
         * Check is all arguments with names present
         *
         * @param name
         * @param arg
         * @param type
         */
        var check = function check(args, names) {
            var template    = ' could not be empty!';

            if (names)
                checkArray(args, names, template);
            else
                Object.keys(args).forEach(function(name) {
                    if (typeof args[name] === 'undefined')
                        throw Error(name + template);
                });

            return check;
        };

        function checkArray(args, names, template) {
            var name,
                isEmpty,
                indexOf     = Array.prototype.indexOf,
                lenNames    = names.length,
                lenArgs     = args.length,
                lessArgs    = lenArgs < lenNames,
                emptyIndex  = indexOf.call(args);

                if (~emptyIndex)
                    isEmpty     = emptyIndex + 1 <= lenNames;

                if (lessArgs || isEmpty) {
                    if (lessArgs)
                        name        = names[lenNames - 1];
                    else
                        name        = names[emptyIndex];

                    throw Error(name + template);
                }
        }

        check.check = check;

        /**
         * Check is type of arg with name is equal to type
         *
         * @param name
         * @param arg
         * @param type
         */
        check.type  = function(name, arg, type) {
            var is = getType(arg) === type;

            if (!is)
                throw Error(name + ' should be ' + type);

            return check;
        };

        function getType(variable) {
            var regExp      = new RegExp('\\s([a-zA-Z]+)'),
                str         = {}.toString.call(variable),
                typeBig     = str.match(regExp)[1],
                result      = typeBig.toLowerCase();

            return result;
        }

        return check;
    }

})(this);
<commit_msg>feature(check) crash process when error occure<commit_after>(function(global) {
    'use strict';

    if (typeof module === 'object' && module.exports)
        module.exports = new checkProto();
    else if (!global.check)
        global.check = new checkProto();

    function checkProto() {
        /**
         * Check is all arguments with names present
         *
         * @param name
         * @param arg
         * @param type
         */
        var check = function check(args, names) {
            var template    = ' could not be empty!';

            if (names)
                checkArray(args, names, template);
            else
                Object.keys(args).forEach(function(name) {
                    if (typeof args[name] === 'undefined')
                        throwError(name + template);
                });

            return check;
        };

        function checkArray(args, names, template) {
            var name,
                isEmpty,
                indexOf     = Array.prototype.indexOf,
                lenNames    = names.length,
                lenArgs     = args.length,
                lessArgs    = lenArgs < lenNames,
                emptyIndex  = indexOf.call(args);

                if (~emptyIndex)
                    isEmpty     = emptyIndex + 1 <= lenNames;

                if (lessArgs || isEmpty) {
                    if (lessArgs)
                        name        = names[lenNames - 1];
                    else
                        name        = names[emptyIndex];

                    throwError(name + template);
                }
        }

        function throwError(msg) {
            var error = Error(msg);

            if (typeof process === 'undefined') {
                throw error;
            } else {
                console.error(error.message, error.stack);
                process.exit(-1);
            }
        }

        check.check = check;

        /**
         * Check is type of arg with name is equal to type
         *
         * @param name
         * @param arg
         * @param type
         */
        check.type  = function(name, arg, type) {
            var is = getType(arg) === type;

            if (!is)
                throw Error(name + ' should be ' + type);

            return check;
        };

        function getType(variable) {
            var regExp      = new RegExp('\\s([a-zA-Z]+)'),
                str         = {}.toString.call(variable),
                typeBig     = str.match(regExp)[1],
                result      = typeBig.toLowerCase();

            return result;
        }

        return check;
    }

})(this);
<|endoftext|>


<commit_before>var BinaryExpression = require('./BinaryExpression');
var BlockStatement = require('./BlockStatement');
var CallExpression = require('./CallExpression');
var ExpressionStatement = require('./ExpressionStatement');
var FunctionExpression = require('./FunctionExpression');
var Identifier = require('./Identifier');
var IfStatement = require('./IfStatement');
var ReturnStatement = require('./ReturnStatement');
var traverse = require('../traverse');

var TryStatement = module.exports = function(block, handlers, finalizer) {
  this.type = 'TryStatement';
  this.block = block;
  this.handlers = handlers;
  this.finalizer = finalizer;
  this.async = false;
};

TryStatement.prototype.normalize = function (place) {
  this.block.normalize();
  for (var i = 0; i < this.handlers.length; i++) {
    this.handlers[i].normalize();
  }
  if (this.finalizer) {
    this.finalizer.normalize();
  }
  place.push(this);
};

TryStatement.prototype.transform = function (place) {
  innerPlace = this.block.transform(place);

  //Not transform if not async calls inside
  if (!this.block.async) {
    place.push(this);
    return innerPlace;
  }

  var contIdentifier = new Identifier('cont');

  //Transform throw statement into continuation break
  traverse(this.block, function (node) {
    if (node.type === 'ThrowStatement') {
      node = new ReturnStatement(new CallExpression(
        contIdentifier,
        [node.argument]
      ));
    }
    return node;
  });

  //Push explicit continuation statement
  innerPlace.push(new ExpressionStatement(new CallExpression(contIdentifier)));

  //Generate try handler
  //TODO multiple handlers
  var handler = this.handlers[0];
  var handlerWrap = new IfStatement(
    new BinaryExpression('!==', handler.param, new Identifier('undefined')),
    handler.body,
    null
  );
  var actualHandler = new FunctionExpression(
    null,
    [handler.param],
    new BlockStatement(handlerWrap)
  );

  var newStatement = new ExpressionStatement(new CallExpression(
    new FunctionExpression(null, [contIdentifier], this.block),
    [actualHandler]
  ));

  place.push(newStatement);
  return handler.body;
};
<commit_msg>Added try…finally support<commit_after>var BinaryExpression = require('./BinaryExpression');
var BlockStatement = require('./BlockStatement');
var CallExpression = require('./CallExpression');
var ExpressionStatement = require('./ExpressionStatement');
var FunctionExpression = require('./FunctionExpression');
var Identifier = require('./Identifier');
var IfStatement = require('./IfStatement');
var ReturnStatement = require('./ReturnStatement');
var traverse = require('../traverse');

var TryStatement = module.exports = function(block, handlers, finalizer) {
  this.type = 'TryStatement';
  this.block = block;
  this.handlers = handlers;
  this.finalizer = finalizer;
  this.async = false;
};

TryStatement.prototype.normalize = function (place) {
  this.block.normalize();
  for (var i = 0; i < this.handlers.length; i++) {
    this.handlers[i].normalize();
  }
  if (this.finalizer) {
    this.finalizer.normalize();
  }
  place.push(this);
};

TryStatement.prototype.transform = function (place) {
  innerPlace = this.block.transform(place);

  //Not transform if not async calls inside
  if (!this.block.async) {
    place.push(this);
    return innerPlace;
  }

  var contIdentifier = new Identifier('cont');

  //Transform throw statement into continuation break
  traverse(this.block, function (node) {
    if (node.type === 'ThrowStatement') {
      node = new ReturnStatement(new CallExpression(
        contIdentifier,
        [node.argument]
      ));
    }
    return node;
  });

  //Push explicit continuation statement
  innerPlace.push(new ExpressionStatement(new CallExpression(contIdentifier)));

  //Generate try handler
  //TODO multiple handlers
  var handler = this.handlers[0];
  var handlerWrap = new IfStatement(
    new BinaryExpression('!==', handler.param, new Identifier('undefined')),
    handler.body,
    null
  );
  var actualHandler = new FunctionExpression(
    null,
    [handler.param],
    new BlockStatement(handlerWrap)
  );
  var newPlace = actualHandler.body.body;
  if (this.finalizer !== null) {
    newPlace.push(this.finalizer);
  }

  var newStatement = new ExpressionStatement(new CallExpression(
    new FunctionExpression(null, [contIdentifier], this.block),
    [actualHandler]
  ));

  place.push(newStatement);
  return newPlace;
};
<|endoftext|>


<commit_before>var util = require('util');
var traverse = require('../traverse');
var helpers = require('../helpers');
var BinaryExpression = require('./BinaryExpression');
var BlockStatement = require('./BlockStatement');
var CatchClause = require('./CatchClause');
var CallExpression = require('./CallExpression');
var ExpressionStatement = require('./ExpressionStatement');
var FunctionExpression = require('./FunctionExpression');
var Identifier = require('./Identifier');
var IfStatement = require('./IfStatement');
var ReturnStatement = require('./ReturnStatement');

var TryStatement = module.exports = function(block, handlers, finalizer) {
  this.type = 'TryStatement';
  this.block = block;
  this.handlers = handlers;
  this.finalizer = finalizer;
  if (!util.isArray(this.handlers)) {
    this.handlers = [this.handlers];
  }
  if (!this.finalizer) {
    this.finalizer = null;
  }
  this.async = false;
};

TryStatement.prototype.normalize = function (place) {
  this.block.normalize();
  for (var i = 0; i < this.handlers.length; i++) {
    this.handlers[i].normalize();
  }
  if (this.finalizer) {
    this.finalizer.normalize();
  }
  place.push(this);
};

TryStatement.prototype.transform = function (place) {
  var innerPlace = this.block.transform(place);

  //TODO multiple handlers
  var handler = this.handlers[0];
  var handlerPlace = handler.transform(place);

  //Not transform if not async calls inside
  if (!this.block.async && !handler.async) {
    place.push(this);
    return innerPlace;
  }

  var contIdentifier = new Identifier(helpers.continuationIdentifier);
  var errIdentifier = new Identifier(helpers.errName);
  var cont = new ExpressionStatement(new CallExpression(contIdentifier));
  var breakCont = new ExpressionStatement(new CallExpression(contIdentifier, errIdentifier));

  if (this.block.async) {
    //Push explicit continuation statement
    innerPlace.push(cont);

    //Make function for try block
    var tryFunction = new FunctionExpression(null, [contIdentifier], this.block);

    //Add try..catch block into every inner functions
    traverse(tryFunction, function (node) {
      if (node.type === 'FunctionExpression') {
        var innerTry = new TryStatement(node.body, new CatchClause(
          errIdentifier,
          null,
          new BlockStatement(breakCont)
        ));
        node.body = new BlockStatement(innerTry);
      }
      return node;
    });

    //Generate try handler function
    var judgeIfException = new IfStatement(
      new BinaryExpression('!==', handler.param, new Identifier('undefined')),
      handler.body,
      null
    );
    var handlerFunction = new FunctionExpression(
      null,
      [handler.param],
      new BlockStatement(judgeIfException)
    );
    var tryCatch = new ExpressionStatement(new CallExpression(tryFunction, handlerFunction));
  } else {
    var tryCatch = this;
  }

  if (handler.async) {
    //If there's async call in catch clause, do CPS transformation on catch function
    var continuationFunction = new FunctionExpression(null, [], new BlockStatement());
    var catchFunction = new FunctionExpression(
      null,
      [contIdentifier],
      new BlockStatement(tryCatch)
    );

    var tryCatchContinuation = new ExpressionStatement(new CallExpression(
      catchFunction,
      [continuationFunction]
    ));
    place.push(tryCatchContinuation);

    handlerPlace.push(cont);
    if (this.block.async) {
      //Set continuation for non-exception condition
      judgeIfException.alternate = cont;
    } else {
      //Terminate execution after async call
      handler.body.body.push(new ReturnStatement());
      //Add continuation call for non-exception condition
      catchFunction.body.body.push(cont);
    }
    var continuationPlace = continuationFunction.body.body;
  } else {
    //Or else directly add catch function
    place.push(tryCatch);
    var continuationPlace = handlerFunction.body.body;
  }

  //If finally clause found, push it into continuation place
  if (this.finalizer !== null) {
    for (var i = 0; i < this.finalizer.body.length; i++) {
      continuationPlace.push(this.finalizer.body[i]);
    }
  }

  return continuationPlace;
};
<commit_msg>Fix a bug if no catch found in try statement<commit_after>var util = require('util');
var traverse = require('../traverse');
var helpers = require('../helpers');
var BinaryExpression = require('./BinaryExpression');
var BlockStatement = require('./BlockStatement');
var CatchClause = require('./CatchClause');
var CallExpression = require('./CallExpression');
var ExpressionStatement = require('./ExpressionStatement');
var FunctionExpression = require('./FunctionExpression');
var Identifier = require('./Identifier');
var IfStatement = require('./IfStatement');
var ReturnStatement = require('./ReturnStatement');

var TryStatement = module.exports = function(block, handlers, finalizer) {
  this.type = 'TryStatement';
  this.block = block;
  this.handlers = handlers;
  this.finalizer = finalizer;
  if (!util.isArray(this.handlers)) {
    this.handlers = [this.handlers];
  }
  if (!this.finalizer) {
    this.finalizer = null;
  }
  this.async = false;
};

TryStatement.prototype.normalize = function (place) {
  this.block.normalize();
  for (var i = 0; i < this.handlers.length; i++) {
    this.handlers[i].normalize();
  }
  if (this.finalizer) {
    this.finalizer.normalize();
  }
  place.push(this);
};

TryStatement.prototype.transform = function (place) {
  var innerPlace = this.block.transform(place);

  //TODO multiple handlers
  var handler = this.handlers[0];

  if (handler) {
    var handlerPlace = handler.transform(place);
  }

  //Not transform if not async calls inside
  if (!this.block.async && ((handler && !handler.async) || !handler) ) {
    place.push(this);
    return innerPlace;
  }

  var contIdentifier = new Identifier(helpers.continuationIdentifier);
  var errIdentifier = new Identifier(helpers.errName);
  var cont = new ExpressionStatement(new CallExpression(contIdentifier));
  var breakCont = new ExpressionStatement(new CallExpression(contIdentifier, errIdentifier));

  if (this.block.async) {
    //Push explicit continuation statement
    innerPlace.push(cont);

    //Make function for try block
    var tryFunction = new FunctionExpression(null, [contIdentifier], this.block);

    //Add try..catch block into every inner functions
    traverse(tryFunction, function (node) {
      if (node.type === 'FunctionExpression') {
        var innerTry = new TryStatement(node.body, new CatchClause(
          errIdentifier,
          null,
          new BlockStatement(breakCont)
        ));
        node.body = new BlockStatement(innerTry);
      }
      return node;
    });

    //Generate try handler function
    var judgeIfException = new IfStatement(
      new BinaryExpression('!==', handler.param, new Identifier('undefined')),
      handler.body,
      null
    );
    var handlerFunction = new FunctionExpression(
      null,
      [handler.param],
      new BlockStatement(judgeIfException)
    );
    var tryCatch = new ExpressionStatement(new CallExpression(tryFunction, handlerFunction));
  } else {
    var tryCatch = this;
  }

  if (handler) {
    if (handler.async) {
      //If there's async call in catch clause, do CPS transformation on catch function
      var continuationFunction = new FunctionExpression(null, [], new BlockStatement());
      var catchFunction = new FunctionExpression(
        null,
        [contIdentifier],
        new BlockStatement(tryCatch)
      );

      var tryCatchContinuation = new ExpressionStatement(new CallExpression(
        catchFunction,
        [continuationFunction]
      ));
      place.push(tryCatchContinuation);

      handlerPlace.push(cont);
      if (this.block.async) {
        //Set continuation for non-exception condition
        judgeIfException.alternate = cont;
      } else {
        //Terminate execution after async call
        handler.body.body.push(new ReturnStatement());
        //Add continuation call for non-exception condition
        catchFunction.body.body.push(cont);
      }
      var continuationPlace = continuationFunction.body.body;
    } else {
      //Or else directly add catch function
      place.push(tryCatch);
      var continuationPlace = handlerFunction.body.body;
    }
  }

  //If finally clause found, push it into continuation place
  if (this.finalizer !== null) {
    for (var i = 0; i < this.finalizer.body.length; i++) {
      continuationPlace.push(this.finalizer.body[i]);
    }
  }

  return continuationPlace;
};
<|endoftext|>


<commit_before>module.exports = count

function count(seq) {
  if (!seq) return 0

  const t = type(seq)

  if (t === 'string' ||  t === 'array') return seq.length
  if (t === 'object') return Object.keys(seq).length

}

const type = require('./type')
    , abs  = Math.abs<commit_msg>Count should return enumerable length for objects<commit_after>module.exports = count

function count(seq) {
  if (!seq) return 0

  const t = type(seq)

  if (t === 'string' ||  t === 'array') return seq.length

  return keys(seq).length
}

const type = require('./type')
    , keys = Object.keys
    , abs  = Math.abs<|endoftext|>


<commit_before>// --------------------
// Overlook
// Mailer
// --------------------

// modules
var mandrill = require('mandrill-api/mandrill'),
	Promise = require('bluebird-extra'),
	_ = require('overlook-utils');

// exports

// Mailer constructor
// when called with new Mailer(options), initializes Mandrill client
var Mailer = module.exports = function(options) {
	// save options to obj
	options = this.options = _.extend({
		async: true,
		message: {}
	}, options || {});

	if (options.from) {
		options.message.from = options.from;
		delete options.from;
	}

	// create mandrill client
	if (!options.apiKey) throw new Error('You must provide a Mandrill API key');

	this.client = new mandrill.Mandrill(options.apiKey);
};

_.extend(Mailer.prototype, {
	// sends a message
	// returns a Promise, resolved or rejected depending on whether sending succeeds
	send: function(message, options) {
		// parse options
		options = _.extend(this.options, options || {});

		// prepare message
		message = _.extend({}, options.message, message);

		if (_.isString(message.from)) message.from = {email: message.from};

		if (!Array.isArray(message.to)) message.to = [message.to];

		message.to.forEach(function(to) {
			if (!to.type) to.type = 'to';
		});

		// make all line returns \r\n
		message.text = message.text.replace(/\n/g, '\r\n').replace(/\r\r/g, '\r');

		// compile message in mandrill format
		message.from_email = message.from.email; // jshint ignore:line
		message.from_name = message.from.name; // jshint ignore:line
		delete message.from;

		// send message
		return new Promise(function(resolve, reject) {
			this.client.messages.send({message: message, async: options.async}, function(result) {
			    resolve(result);
			}, function(err) {
				reject(err);
			});
		}.bind(this));
	}
});
<commit_msg>Mailer#sendBatch method<commit_after>// --------------------
// Overlook
// Mailer
// --------------------

// modules
var mandrill = require('mandrill-api/mandrill'),
	Promise = require('bluebird-extra'),
	_ = require('overlook-utils');

// exports

// Mailer constructor
// when called with new Mailer(options), initializes Mandrill client
var Mailer = module.exports = function(options) {
	// save options to obj
	options = this.options = _.extend({
		async: true,
		message: {}
	}, options || {});

	if (options.from) {
		options.message.from = options.from;
		delete options.from;
	}

	// create mandrill client
	if (!options.apiKey) throw new Error('You must provide a Mandrill API key');

	this.client = new mandrill.Mandrill(options.apiKey);
};

_.extend(Mailer.prototype, {
	// sends a message
	// returns a Promise, resolved or rejected depending on whether sending succeeds
	send: function(message, options) {
		// parse options
		options = _.extend(this.options, options || {});

		// prepare message
		message = _.extend({}, options.message, message);

		if (_.isString(message.from)) message.from = {email: message.from};

		if (!Array.isArray(message.to)) message.to = [message.to];

		message.to.forEach(function(to) {
			if (!to.type) to.type = 'to';
		});

		// make all line returns \r\n
		message.text = message.text.replace(/\n/g, '\r\n').replace(/\r\r/g, '\r');

		// compile message in mandrill format
		message.from_email = message.from.email; // jshint ignore:line
		message.from_name = message.from.name; // jshint ignore:line
		delete message.from;

		// send message
		return new Promise(function(resolve, reject) {
			this.client.messages.send({message: message, async: options.async}, function(result) {
			    resolve(result);
			}, function(err) {
				reject(err);
			});
		}.bind(this));
	},

	sendBatch: function(message, recipients, options) {
		// parse recipients
		if (!Array.isArray(recipients)) recipients = [recipients];

		// loop through recipients, sending mail to each
		return Promise.bind(this)
		.return(recipients)
		.each(function(recipient) {
			// replace placeholders in text
			var text = message.text;

			_.forIn(recipient, function(value, key) {
				text = text.replace('[' + key + ']', value);
			});

			// compile message obj
			var thisMessage = _.clone(message);
			thisMessage.text = text;
			thisMessage.to = {
				name: recipient.name,
				email: recipient.email
			};

			// send message
			return this.send(thisMessage, options);
		});
	}
});
<|endoftext|>


<commit_before>"use strict";

import os from 'os';
import EnumItem from './enumItem';
import { isString } from './isType';
import { indexOf } from './indexOf';

const endianness = os.endianness();

/**
 * Represents an Enum with enum items.
 * @param {Array || Object}  map     This are the enum items.
 * @param {String || Object} options This are options. [optional]
 */
export default class Enum {
  constructor(map, options) {
    /* implement the "ref type interface", so that Enum types can
     * be used in `node-ffi` function declarations and invokations.
     * In C, these Enums act as `uint32_t` types.
     *
     * https://github.com/TooTallNate/ref#the-type-interface
     */
    this.size = 4;
    this.indirection = 1;

    if (options && isString(options)) {
      options = { name: options };
    }

    this._options = options || {};
    this._options.separator = this._options.separator || " | ";
    this._options.endianness = this._options.endianness || endianness;
    this._options.ignoreCase = this._options.ignoreCase || false;

    this.enums = [];

    if (map.length) {
      var array = map;
      map = {};

      for (var i = 0; i < array.length; i++) {
        map[array[i]] = Math.pow(2, i);
      }
    }

    for (var member in map) {
      guardReservedKeys(this._options.name, member);
      this[member] = new EnumItem(member, map[member], { ignoreCase: this._options.ignoreCase });
      this.enums.push(this[member]);
    }

    if (this._options.ignoreCase) {
      this.getLowerCaseEnums = function () {
        var res = {};
        for (var i = 0, len = this.enums.length; i < len; i++) {
          res[this.enums[i].key.toLowerCase()] = this.enums[i];
        }
        return res;
      };
    }

    if (this._options.name) {
      this.name = this._options.name;
    }

    const isFlaggable = () => {
      for (var i = 0, len = this.enums.length; i < len; i++) {
        var e = this.enums[i];

        if (!(e.value !== 0 && !(e.value & e.value - 1))) {
          return false;
        }
      }
      return true;
    }

    this.isFlaggable = isFlaggable();
    this.freezeEnums(); //this will make instances of Enum non-extensible
  }

  /**
   * Returns the appropriate EnumItem key.
   * @param  {EnumItem || String || Number} key The object to get with.
   * @return {String}                           The get result.
   */
  getKey(value) {
    var item = this.get(value);
    if (item) {
      return item.key;
    }
  }

  /**
   * Returns the appropriate EnumItem value.
   * @param  {EnumItem || String || Number} key The object to get with.
   * @return {Number}                           The get result.
   */
  getValue(key) {
    var item = this.get(key);
    if (item) {
      return item.value;
    }
  }

  /**
   * Returns the appropriate EnumItem.
   * @param  {EnumItem || String || Number} key The object to get with.
   * @return {EnumItem}                         The get result.
   */
  get(key, offset) {
    if (key === null || key === undefined) {
      return;
    } // Buffer instance support, part of the ref Type interface
    if (Buffer.isBuffer(key)) {
      key = key["readUInt32" + this._options.endianness](offset || 0);
    }

    if (EnumItem.isEnumItem(key)) {
      var foundIndex = indexOf.call(this.enums, key);
      if (foundIndex >= 0) {
        return key;
      }
      if (!this.isFlaggable || this.isFlaggable && key.key.indexOf(this._options.separator) < 0) {
        return;
      }
      return this.get(key.key);
    } else if (isString(key)) {

      var enums = this;
      if (this._options.ignoreCase) {
        enums = this.getLowerCaseEnums();
        key = key.toLowerCase();
      }

      if (key.indexOf(this._options.separator) > 0) {
        var parts = key.split(this._options.separator);

        var value = 0;
        for (var i = 0; i < parts.length; i++) {
          var part = parts[i];

          value |= enums[part].value;
        }

        return new EnumItem(key, value);
      } else {
        return enums[key];
      }
    } else {
      for (var m in this) {
        if (this.hasOwnProperty(m)) {
          if (this[m].value === key) {
            return this[m];
          }
        }
      }

      var result = null;

      if (this.isFlaggable) {
        for (var n in this) {
          if (this.hasOwnProperty(n)) {
            if ((key & this[n].value) !== 0) {
              if (result) {
                result += this._options.separator;
              } else {
                result = "";
              }
              result += n;
            }
          }
        }
      }

      return this.get(result || null);
    }
  }

  /**
   * Sets the Enum "value" onto the give `buffer` at the specified `offset`.
   * Part of the ref "Type interface".
   *
   * @param  {Buffer} buffer The Buffer instance to write to.
   * @param  {Number} offset The offset in the buffer to write to. Default 0.
   * @param  {EnumItem || String || Number} value The EnumItem to write.
   */
  set(buffer, offset, value) {
    var item = this.get(value);
    if (item) {
      return buffer["writeUInt32" + this._options.endianness](item.value, offset || 0);
    }
  }

  /**
   * Define freezeEnums() as a property of the prototype.
   * make enumerable items nonconfigurable and deep freeze the properties. Throw Error on property setter.
   */
  freezeEnums() {
    function envSupportsFreezing() {
      return (
        Object.isFrozen && Object.isSealed &&
        Object.getOwnPropertyNames && Object.getOwnPropertyDescriptor &&
        Object.defineProperties && Object.__defineGetter__ && Object.__defineSetter__
      );
    }

    function freezer(o) {
      var props = Object.getOwnPropertyNames(o);
      props.forEach(function (p) {
        if (!Object.getOwnPropertyDescriptor(o, p).configurable) {
          return;
        }

        Object.defineProperties(o, p, { writable: false, configurable: false });
      });
      return o;
    }

    function getPropertyValue(value) {
      return value;
    }

    function deepFreezeEnums(o) {
      if (typeof o !== "object" || o === null || Object.isFrozen(o) || Object.isSealed(o)) {
        return;
      }
      for (var key in o) {
        if (o.hasOwnProperty(key)) {
          o.__defineGetter__(key, getPropertyValue.bind(null, o[key]));
          o.__defineSetter__(key, function throwPropertySetError(value) {
            throw TypeError("Cannot redefine property; Enum Type is not extensible.");
          });
          deepFreezeEnums(o[key]);
        }
      }
      if (Object.freeze) {
        Object.freeze(o);
      } else {
        freezer(o);
      }
    }

    if (envSupportsFreezing() ) {
      deepFreezeEnums(this);
    }

    return this;
  }

  /**
   * Registers the Enum Type globally in node.js.
   * @param  {String} key Global variable. [optional]
   */
  static register(key='Enum') {
    if (!global[key]) {
      global[key] = Enum;
    }
  }
};

// private

var reservedKeys = ['_options', 'get', 'getKey', 'getValue', 'enums', 'isFlaggable'];

function guardReservedKeys(customName, key) {
  if ((customName && key === 'name') || indexOf.call(reservedKeys, key) >= 0) {
    throw new Error("Enum key \"" + key + "\" is a reserved word!");
  }
}
<commit_msg>es6 template literal<commit_after>"use strict";

import os from 'os';
import EnumItem from './enumItem';
import { isString } from './isType';
import { indexOf } from './indexOf';

const endianness = os.endianness();

/**
 * Represents an Enum with enum items.
 * @param {Array || Object}  map     This are the enum items.
 * @param {String || Object} options This are options. [optional]
 */
export default class Enum {
  constructor(map, options) {
    /* implement the "ref type interface", so that Enum types can
     * be used in `node-ffi` function declarations and invokations.
     * In C, these Enums act as `uint32_t` types.
     *
     * https://github.com/TooTallNate/ref#the-type-interface
     */
    this.size = 4;
    this.indirection = 1;

    if (options && isString(options)) {
      options = { name: options };
    }

    this._options = options || {};
    this._options.separator = this._options.separator || " | ";
    this._options.endianness = this._options.endianness || endianness;
    this._options.ignoreCase = this._options.ignoreCase || false;

    this.enums = [];

    if (map.length) {
      var array = map;
      map = {};

      for (var i = 0; i < array.length; i++) {
        map[array[i]] = Math.pow(2, i);
      }
    }

    for (var member in map) {
      guardReservedKeys(this._options.name, member);
      this[member] = new EnumItem(member, map[member], { ignoreCase: this._options.ignoreCase });
      this.enums.push(this[member]);
    }

    if (this._options.ignoreCase) {
      this.getLowerCaseEnums = function () {
        var res = {};
        for (var i = 0, len = this.enums.length; i < len; i++) {
          res[this.enums[i].key.toLowerCase()] = this.enums[i];
        }
        return res;
      };
    }

    if (this._options.name) {
      this.name = this._options.name;
    }

    const isFlaggable = () => {
      for (var i = 0, len = this.enums.length; i < len; i++) {
        var e = this.enums[i];

        if (!(e.value !== 0 && !(e.value & e.value - 1))) {
          return false;
        }
      }
      return true;
    }

    this.isFlaggable = isFlaggable();
    this.freezeEnums(); //this will make instances of Enum non-extensible
  }

  /**
   * Returns the appropriate EnumItem key.
   * @param  {EnumItem || String || Number} key The object to get with.
   * @return {String}                           The get result.
   */
  getKey(value) {
    var item = this.get(value);
    if (item) {
      return item.key;
    }
  }

  /**
   * Returns the appropriate EnumItem value.
   * @param  {EnumItem || String || Number} key The object to get with.
   * @return {Number}                           The get result.
   */
  getValue(key) {
    var item = this.get(key);
    if (item) {
      return item.value;
    }
  }

  /**
   * Returns the appropriate EnumItem.
   * @param  {EnumItem || String || Number} key The object to get with.
   * @return {EnumItem}                         The get result.
   */
  get(key, offset) {
    if (key === null || key === undefined) {
      return;
    } // Buffer instance support, part of the ref Type interface
    if (Buffer.isBuffer(key)) {
      key = key["readUInt32" + this._options.endianness](offset || 0);
    }

    if (EnumItem.isEnumItem(key)) {
      var foundIndex = indexOf.call(this.enums, key);
      if (foundIndex >= 0) {
        return key;
      }
      if (!this.isFlaggable || this.isFlaggable && key.key.indexOf(this._options.separator) < 0) {
        return;
      }
      return this.get(key.key);
    } else if (isString(key)) {

      var enums = this;
      if (this._options.ignoreCase) {
        enums = this.getLowerCaseEnums();
        key = key.toLowerCase();
      }

      if (key.indexOf(this._options.separator) > 0) {
        var parts = key.split(this._options.separator);

        var value = 0;
        for (var i = 0; i < parts.length; i++) {
          var part = parts[i];

          value |= enums[part].value;
        }

        return new EnumItem(key, value);
      } else {
        return enums[key];
      }
    } else {
      for (var m in this) {
        if (this.hasOwnProperty(m)) {
          if (this[m].value === key) {
            return this[m];
          }
        }
      }

      var result = null;

      if (this.isFlaggable) {
        for (var n in this) {
          if (this.hasOwnProperty(n)) {
            if ((key & this[n].value) !== 0) {
              if (result) {
                result += this._options.separator;
              } else {
                result = "";
              }
              result += n;
            }
          }
        }
      }

      return this.get(result || null);
    }
  }

  /**
   * Sets the Enum "value" onto the give `buffer` at the specified `offset`.
   * Part of the ref "Type interface".
   *
   * @param  {Buffer} buffer The Buffer instance to write to.
   * @param  {Number} offset The offset in the buffer to write to. Default 0.
   * @param  {EnumItem || String || Number} value The EnumItem to write.
   */
  set(buffer, offset, value) {
    var item = this.get(value);
    if (item) {
      return buffer["writeUInt32" + this._options.endianness](item.value, offset || 0);
    }
  }

  /**
   * Define freezeEnums() as a property of the prototype.
   * make enumerable items nonconfigurable and deep freeze the properties. Throw Error on property setter.
   */
  freezeEnums() {
    function envSupportsFreezing() {
      return (
        Object.isFrozen && Object.isSealed &&
        Object.getOwnPropertyNames && Object.getOwnPropertyDescriptor &&
        Object.defineProperties && Object.__defineGetter__ && Object.__defineSetter__
      );
    }

    function freezer(o) {
      var props = Object.getOwnPropertyNames(o);
      props.forEach(function (p) {
        if (!Object.getOwnPropertyDescriptor(o, p).configurable) {
          return;
        }

        Object.defineProperties(o, p, { writable: false, configurable: false });
      });
      return o;
    }

    function getPropertyValue(value) {
      return value;
    }

    function deepFreezeEnums(o) {
      if (typeof o !== "object" || o === null || Object.isFrozen(o) || Object.isSealed(o)) {
        return;
      }
      for (var key in o) {
        if (o.hasOwnProperty(key)) {
          o.__defineGetter__(key, getPropertyValue.bind(null, o[key]));
          o.__defineSetter__(key, function throwPropertySetError(value) {
            throw TypeError("Cannot redefine property; Enum Type is not extensible.");
          });
          deepFreezeEnums(o[key]);
        }
      }
      if (Object.freeze) {
        Object.freeze(o);
      } else {
        freezer(o);
      }
    }

    if (envSupportsFreezing() ) {
      deepFreezeEnums(this);
    }

    return this;
  }

  /**
   * Registers the Enum Type globally in node.js.
   * @param  {String} key Global variable. [optional]
   */
  static register(key='Enum') {
    if (!global[key]) {
      global[key] = Enum;
    }
  }
};

// private

var reservedKeys = ['_options', 'get', 'getKey', 'getValue', 'enums', 'isFlaggable'];

function guardReservedKeys(customName, key) {
  if ((customName && key === 'name') || indexOf.call(reservedKeys, key) >= 0) {
    throw new Error(`Enum key ${key} is a reserved word!`);
  }
}
<|endoftext|>


<commit_before>/**
 * @module error
 */

'use strict';

var process = require('process');
var utils = require('./utils');

class APIerror extends Error {
  /**
   * Creates a new APIerror.
   * @param {Error} err API request error
   * @constructor
   */
  constructor(err) {
    super();

    this.name = this.constructor.name;

    this.platform = process.browser ? 'browser' : 'node ' + process.version;

    this.request = {
      parameters: utils.camelize(err.options.qs),
      verb: err.options.method,
      url: err.options.uri
    };
    this.response = {
      statusCode: err.response.statusCode,
      date: err.response.headers.date,
      requestId: err.response.headers['x-request-id']
    };
    this.errors = [];

    var errorMessages;
    try {
       errorMessages = utils.camelize(JSON.parse(err.error)).errorMessages;
    } catch (ex) {
       errorMessages = {
         base:{
           code:'badly_formatted_error_response',
           message:err.error
         }
       };
    }
    for (var field of Object.keys(errorMessages)) {
      if (typeof errorMessages[field][Symbol.iterator] === 'function') {
        for (var fieldError of errorMessages[field]) {
          this.errors.push({
            field: field,
            code: fieldError.code,
            message: fieldError.message,
            params: fieldError.params
          });
        }
      } else {
        this.errors.push({
          field: field,
          code: errorMessages[field].code,
          message: errorMessages[field].message,
          params: errorMessages[field].params
        });
      }
    }
  }

  /**
   * Creates YAML representation of the API error.
   * @returns {String} YAML string
   */
  toYAML() {
    var err = Object.assign({}, this);
    delete err.name;

    return this.name + '\n' + utils.toYAML(err);
  }
}

class BadRequestError extends APIerror {
}

class AuthenticationError extends APIerror {
}

class ForbiddenError extends APIerror {
}

class NotFoundError extends APIerror {
}

class TooManyRequestsError extends APIerror {
}

class InternalApplicationError extends APIerror {
}

class UndefinedError extends APIerror {
}

module.exports = function (err) {
  if (!err.statusCode) {
    return err;
  }

  switch (err.statusCode) {
    case 400:
      return new BadRequestError(err);
    case 401:
      return new AuthenticationError(err);
    case 403:
      return new ForbiddenError(err);
    case 404:
      return new NotFoundError(err);
    case 429:
      return new TooManyRequestsError(err);
    case 500:
      return new InternalApplicationError(err);
    default:
      return new UndefinedError(err);
  }
};
module.exports.APIerror = APIerror;
module.exports.AuthenticationError = AuthenticationError;
module.exports.BadRequestError = BadRequestError;
module.exports.ForbiddenError = ForbiddenError;
module.exports.NotFoundError = NotFoundError;
module.exports.TooManyRequestsError = TooManyRequestsError;
module.exports.InternalApplicationError = InternalApplicationError;
module.exports.UndefinedError = UndefinedError;<commit_msg>Fix indentation<commit_after>/**
 * @module error
 */

'use strict';

var process = require('process');
var utils = require('./utils');

class APIerror extends Error {
  /**
   * Creates a new APIerror.
   * @param {Error} err API request error
   * @constructor
   */
  constructor(err) {
    super();

    this.name = this.constructor.name;

    this.platform = process.browser ? 'browser' : 'node ' + process.version;

    this.request = {
      parameters: utils.camelize(err.options.qs),
      verb: err.options.method,
      url: err.options.uri
    };
    this.response = {
      statusCode: err.response.statusCode,
      date: err.response.headers.date,
      requestId: err.response.headers['x-request-id']
    };
    this.errors = [];

    var errorMessages;
    try {
      errorMessages = utils.camelize(JSON.parse(err.error)).errorMessages;
    } catch (ex) {
      errorMessages = {
        base: {
          code: 'badly_formatted_error_response',
          message: err.error
        }
      };
    }
    for (var field of Object.keys(errorMessages)) {
      if (typeof errorMessages[field][Symbol.iterator] === 'function') {
        for (var fieldError of errorMessages[field]) {
          this.errors.push({
            field: field,
            code: fieldError.code,
            message: fieldError.message,
            params: fieldError.params
          });
        }
      } else {
        this.errors.push({
          field: field,
          code: errorMessages[field].code,
          message: errorMessages[field].message,
          params: errorMessages[field].params
        });
      }
    }
  }

  /**
   * Creates YAML representation of the API error.
   * @returns {String} YAML string
   */
  toYAML() {
    var err = Object.assign({}, this);
    delete err.name;

    return this.name + '\n' + utils.toYAML(err);
  }
}

class BadRequestError extends APIerror {
}

class AuthenticationError extends APIerror {
}

class ForbiddenError extends APIerror {
}

class NotFoundError extends APIerror {
}

class TooManyRequestsError extends APIerror {
}

class InternalApplicationError extends APIerror {
}

class UndefinedError extends APIerror {
}

module.exports = function (err) {
  if (!err.statusCode) {
    return err;
  }

  switch (err.statusCode) {
    case 400:
      return new BadRequestError(err);
    case 401:
      return new AuthenticationError(err);
    case 403:
      return new ForbiddenError(err);
    case 404:
      return new NotFoundError(err);
    case 429:
      return new TooManyRequestsError(err);
    case 500:
      return new InternalApplicationError(err);
    default:
      return new UndefinedError(err);
  }
};
module.exports.APIerror = APIerror;
module.exports.AuthenticationError = AuthenticationError;
module.exports.BadRequestError = BadRequestError;
module.exports.ForbiddenError = ForbiddenError;
module.exports.NotFoundError = NotFoundError;
module.exports.TooManyRequestsError = TooManyRequestsError;
module.exports.InternalApplicationError = InternalApplicationError;
module.exports.UndefinedError = UndefinedError;<|endoftext|>


<commit_before>/*
 * NTwitBot - event.js
 * Copyright (C) 2016 Jordan Sne
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

const timers = require('timers');

module.exports = class Event {

    constructor(twitterPkg, actionHandler, dataHandler, utils) {
        this.twitterPkg = twitterPkg;
        this.actionHandler = actionHandler;
        this.dataHandler = dataHandler;
        this.utils = utils;

        this.TWEETS_TO_TRACK = 2000;

        // File-update-required flags
        this.updateTweets = false;
        this.updateStatus = false;

        // Object containing trackedUsers dictionary & last mention data
        this.botStatus = this.dataHandler.readStatus();

        // If no existing bot status
        if (this.botStatus === null) {
            this.botStatus = {
                // Dictionary with key & value: userID (int) : LastTweetIDProcessed' (int)
                trackedUsers:  {},

                // Last mention (tweet ID) that was processed
                lastMention: 0
            };
        }
    }

    /*
     * Starts the event handler. Occurs when the app has finished setup.
     */
    start() {
        // 15 min interval
        timers.setInterval(() => {
            this.runUpdate();
        }, 900000);
    }

    /*
     * Scheduled 15 min interval bot update.
     */
    runUpdate() {
        this.utils.log("");
        this.utils.log("******************* Running update ******************* ");
        this.utils.log("");

        // Reset update flags
        this.updateTweets = false;
        this.updateStatus = false;

        this.checkMentions();
        this.checkTrackedTweets();
    }

    /*
     * Retrieves and processed mentions of the bot.
     */
    checkMentions() {
        const mentionData = {};

        // Retrieve since last mention or last 200
        if (this.botStatus.lastMention !== 0) {
            mentionData.since_id = this.botStatus.lastMention;
        } else {
            mentionData.count = 200;
        }

        this.twitterPkg.get('statuses/mentions_timeline', mentionData, (error, mentions, response) => {
            if (error) {
                this.utils.logError("Failed to retrieve mentions");
                this.utils.logError("    Error: " +  error[0].message);
                return;
            }

            // Process new mentions, if there is any
            if (mentions.length > 0) {
                this.utils.log(mentions.length + " new mentions found");

                for (let i = 0; i < mentions.length; i++) {
                    this.actionHandler.handleMention(mentions[i]);
                }

                this.updateStatus = true;
            } else {
                this.utils.log("No new mentions found");
            }
        });
    }

    /*
     * Retrieves and processed new tweets of tracked users.
     */
    checkTrackedTweets() {
        const tweetsToProcess = [];
        const tweetRetrievals = [];
        const data = {
            user_id: this.dataHandler.ownID,
            stringify_ids: true
        };

        // Retrieve a list of users that the bot is following
        this.twitterPkg.get('friends/ids', data, (error, result, response) => {
            if (error) {
                this.utils.logError("Failed to retrieve following list of bot");
                this.utils.logError("    Error: " + error[0].message);
                return;
            }

            const following = result.ids;

            // Add any new follows to the trackedUsers list
            for (let i = 0; i < following.length; i++) {
                let user = following[i];

                if (!(user in this.botStatus.trackedUsers)) {
                    this.botStatus.trackedUsers[user] = 0;
                }
            }

            // Remove any trackedUsers that the bot is no longer following
            for (let userID in this.botStatus.trackedUsers) {
                if (!(currentTrackedUsers.indexOf(userID) > -1)) {
                    delete this.botStatus.trackedUsers[userID];
                }
            }

            // Process any new tweets that users have made
            for (let userID in this.botStatus.trackedUsers) {
                const lastTweetID = this.botStatus.trackedUsers[userID];
                const userData = {
                    user_id: userID,
                    include_rts: false,
                    count: 200
                };

                // If the user has been processed before
                if (lastTweetID !== 0) {
                    userData.since_id = lastTweetID;

                    this.utils.log("Retrieving new tweets from user with ID: '" + userData.user_id + "'");

                    tweetRetrievals.push(
                        new Promise(
                            (resolve) => this.retrieveTweetsSince(userData, tweetsToProcess, resolve),
                            (reject)  => this.utils.logError("Failed to retrieve new tweets (PROMISE REJECTED)")
                        )
                    );

                // If the user has NOT been processed before
                } else {
                    // Retrieve defined number of tweets if less than 200
                    userData.count = this.TWEETS_TO_TRACK >= 200 ? 200 : this.TWEETS_TO_TRACK;

                    this.utils.log("New user with ID: '" + userData.user_id + "' added");
                    this.utils.log("    Retrieving most recent " + this.TWEETS_TO_TRACK + " tweets");

                    // Add retrieval promise to list
                    tweetRetrievals.push(
                        new Promise(
                            (resolve) => this.retrieveTweetsNew(userData, tweetsToProcess, this.TWEETS_TO_TRACK, resolve),
                            (reject)  => this.utils.logError("Failed to retrieve recent tweets (PROMISE REJECTED)")
                        )
                    );
                }
            }

            // When all retrievals are finished, process the tweets & save trackedUsers to file
            Promise.all(tweetRetrievals).then(() => {
                if (this.updateTweets) {
                    this.dataHandler.processTrackedTweets(tweetsToProcess);
                } else {
                    this.utils.log("No new tweets to process");
                }

                if (this.updateTweets || this.updateStatus) {
                    this.dataHandler.saveStatus(this.botStatus);
                }
            });
        });
    }

    /*
     * Retrieves new tweets from a user and adds tweets to tweetsToProcess.
     *   userData        - Object: User information to retrieve tweets from.
     *   tweetsToProcess - Array:  To be filled with retrieved tweets.
     *   setDone         - Object: Callback function.
     */
    retrieveTweetsSince(userData, tweetsToProcess, setDone) {
        this.twitterPkg.get('statuses/user_timeline', userData, (error, tweets, response) => {
            if (error) {
                this.utils.logError("Failed to retrieve tweets from user: " + userData.user_id);
                this.utils.logError("    Error: " + error[0].message);
                return;
            }

            if (tweets.length <= 200 && tweets.length > 0) {
                // Add tweets to processing list
                for (let i = 0; i < tweets.length; i++) {
                    let tweet = tweets[i];

                    // Do not include retweets
                    if (!tweet.retweetedStatus) {
                        tweetsToProcess.push(tweets[i]);
                    }
                }

                // Save the most recent tweet ID in case the final batch adds to 200 perfectly and ..
                this.botStatus.trackedUsers[userData.user_id] = tweets[0].id_str;
                // .. and set the update flag for the same reason.
                this.updateTweets = true;

                // Continue processing the rest of the tweets
                userData.since_id = tweets[0].id_str;
                this.retrieveTweetsSince(userData, tweetsToProcess, setDone);

            // Reached end of tweets
            } else {
                setDone();
            }
        });
    }

    /*
     * Retrieves new tweets from a user and adds tweets to tweetsToProcess.
     *   userData        - Object: User information to retrieve tweets from.
     *   tweetsToProcess - Array:  To be filled with retrieved tweets.
     *   numOfTweets     - Int:    Numer of tweets to retrieve.
     *   setDone         - Object: Callback function.
     */
    retrieveTweetsNew(userData, tweetsToProcess, numOfTweets, setDone) {
        this.twitterPkg.get('statuses/user_timeline', userData, (error, tweets, response) => {
            if (error) {
                this.utils.logError("Failed to retrieve tweets from user: " + userData.user_id);
                this.utils.logError("    Error: " + error[0]);
                return;
            }

            // If it is the first time calling the method (in which max_id would not be set)
            if (!userData.max_id && tweets.length > 0) {
                // Set the most recent tweet as last
                this.botStatus.trackedUsers[userData.user_id] = tweets[0].id_str;
            }

            if (tweets.length <= 200 && tweets.length > 0) {
                let tweetsAdded = 0;

                // Add tweets to processing list
                // Exclude final tweet as it will be used in next method iteration
                for (let i = 0; i < tweets.length; i++) {
                    let tweet = tweets[i];

                    // Do not include retweets.
                    if (!tweet.retweetedStatus) {
                        tweetsToProcess.push(tweets[i]);
                        tweetsAdded++;
                    }
                }

                if (tweetsAdded > 0) {
                    this.updateTweets = true;
                }

                numOfTweets -= tweetsAdded;

                if (numOfTweets > 0) {
                    // Continue processing the rest of the tweets
                    userData.max_id = tweets[tweets.length - 1].id_str;

                    // Avoid retrieving unneeded tweets
                    if (numOfTweets < 200) {
                        userData.count = numOfTweets;
                    }

                    this.retrieveTweetsNew(userData, tweetsToProcess, numOfTweets, setDone);
                } else {
                    setDone();
                }

            // Reached end of tweets
            } else {
                setDone();
            }
        });
    }

};
<commit_msg>Use new utility function<commit_after>/*
 * NTwitBot - event.js
 * Copyright (C) 2016 Jordan Sne
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

const timers = require('timers');

module.exports = class Event {

    constructor(twitterPkg, actionHandler, dataHandler, utils) {
        this.twitterPkg = twitterPkg;
        this.actionHandler = actionHandler;
        this.dataHandler = dataHandler;
        this.utils = utils;

        this.TWEETS_TO_TRACK = 2000;

        // File-update-required flags
        this.updateTweets = false;
        this.updateStatus = false;

        // Object containing trackedUsers dictionary & last mention data
        this.botStatus = this.dataHandler.readStatus();

        // If no existing bot status
        if (this.botStatus === null) {
            this.botStatus = {
                // Dictionary with key & value: userID (int) : LastTweetIDProcessed' (int)
                trackedUsers:  {},

                // Last mention (tweet ID) that was processed
                lastMention: 0
            };
        }
    }

    /*
     * Starts the event handler. Occurs when the app has finished setup.
     */
    start() {
        // 15 min interval
        timers.setInterval(() => {
            this.runUpdate();
        }, 900000);
    }

    /*
     * Scheduled 15 min interval bot update.
     */
    runUpdate() {
        this.utils.log("");
        this.utils.log("******************* Running update ******************* ");
        this.utils.log("");

        // Reset update flags
        this.updateTweets = false;
        this.updateStatus = false;

        this.checkMentions();
        this.checkTrackedTweets();
    }

    /*
     * Retrieves and processed mentions of the bot.
     */
    checkMentions() {
        const mentionData = {};

        // Retrieve since last mention or last 200
        if (this.botStatus.lastMention !== 0) {
            mentionData.since_id = this.botStatus.lastMention;
        } else {
            mentionData.count = 200;
        }

        this.twitterPkg.get('statuses/mentions_timeline', mentionData, (error, mentions, response) => {
            if (error) {
                this.utils.logError("Failed to retrieve mentions");
                this.utils.logError("    Error: " +  error[0].message);
                return;
            }

            // Process new mentions, if there is any
            if (mentions.length > 0) {
                this.utils.log(mentions.length + " new mentions found");

                for (let i = 0; i < mentions.length; i++) {
                    this.actionHandler.handleMention(mentions[i]);
                }

                this.updateStatus = true;
            } else {
                this.utils.log("No new mentions found");
            }
        });
    }

    /*
     * Retrieves and processed new tweets of tracked users.
     */
    checkTrackedTweets() {
        const tweetsToProcess = [];
        const tweetRetrievals = [];
        const data = {
            user_id: this.dataHandler.ownID,
            stringify_ids: true
        };

        // Retrieve a list of users that the bot is following
        this.twitterPkg.get('friends/ids', data, (error, result, response) => {
            if (error) {
                this.utils.logError("Failed to retrieve following list of bot");
                this.utils.logError("    Error: " + error[0].message);
                return;
            }

            const following = result.ids;

            // Add any new follows to the trackedUsers list
            for (let i = 0; i < following.length; i++) {
                let user = following[i];

                if (!(user in this.botStatus.trackedUsers)) {
                    this.botStatus.trackedUsers[user] = 0;
                }
            }

            // Remove any trackedUsers that the bot is no longer following
            for (let userID in this.botStatus.trackedUsers) {
                if (!this.utils.isInArray(following, userID)) {
                    delete this.botStatus.trackedUsers[userID];
                }
            }

            // Process any new tweets that users have made
            for (let userID in this.botStatus.trackedUsers) {
                const lastTweetID = this.botStatus.trackedUsers[userID];
                const userData = {
                    user_id: userID,
                    include_rts: false,
                    count: 200
                };

                // If the user has been processed before
                if (lastTweetID !== 0) {
                    userData.since_id = lastTweetID;

                    this.utils.log("Retrieving new tweets from user with ID: '" + userData.user_id + "'");

                    tweetRetrievals.push(
                        new Promise(
                            (resolve) => this.retrieveTweetsSince(userData, tweetsToProcess, resolve),
                            (reject)  => this.utils.logError("Failed to retrieve new tweets (PROMISE REJECTED)")
                        )
                    );

                // If the user has NOT been processed before
                } else {
                    // Retrieve defined number of tweets if less than 200
                    userData.count = this.TWEETS_TO_TRACK >= 200 ? 200 : this.TWEETS_TO_TRACK;

                    this.utils.log("New user with ID: '" + userData.user_id + "' added");
                    this.utils.log("    Retrieving most recent " + this.TWEETS_TO_TRACK + " tweets");

                    // Add retrieval promise to list
                    tweetRetrievals.push(
                        new Promise(
                            (resolve) => this.retrieveTweetsNew(userData, tweetsToProcess, this.TWEETS_TO_TRACK, resolve),
                            (reject)  => this.utils.logError("Failed to retrieve recent tweets (PROMISE REJECTED)")
                        )
                    );
                }
            }

            // When all retrievals are finished, process the tweets & save trackedUsers to file
            Promise.all(tweetRetrievals).then(() => {
                if (this.updateTweets) {
                    this.dataHandler.processTrackedTweets(tweetsToProcess);
                } else {
                    this.utils.log("No new tweets to process");
                }

                if (this.updateTweets || this.updateStatus) {
                    this.dataHandler.saveStatus(this.botStatus);
                }
            });
        });
    }

    /*
     * Retrieves new tweets from a user and adds tweets to tweetsToProcess.
     *   userData        - Object: User information to retrieve tweets from.
     *   tweetsToProcess - Array:  To be filled with retrieved tweets.
     *   setDone         - Object: Callback function.
     */
    retrieveTweetsSince(userData, tweetsToProcess, setDone) {
        this.twitterPkg.get('statuses/user_timeline', userData, (error, tweets, response) => {
            if (error) {
                this.utils.logError("Failed to retrieve tweets from user: " + userData.user_id);
                this.utils.logError("    Error: " + error[0].message);
                return;
            }

            if (tweets.length <= 200 && tweets.length > 0) {
                // Add tweets to processing list
                for (let i = 0; i < tweets.length; i++) {
                    let tweet = tweets[i];

                    // Do not include retweets
                    if (!tweet.retweetedStatus) {
                        tweetsToProcess.push(tweets[i]);
                    }
                }

                // Save the most recent tweet ID in case the final batch adds to 200 perfectly and ..
                this.botStatus.trackedUsers[userData.user_id] = tweets[0].id_str;
                // .. and set the update flag for the same reason.
                this.updateTweets = true;

                // Continue processing the rest of the tweets
                userData.since_id = tweets[0].id_str;
                this.retrieveTweetsSince(userData, tweetsToProcess, setDone);

            // Reached end of tweets
            } else {
                setDone();
            }
        });
    }

    /*
     * Retrieves new tweets from a user and adds tweets to tweetsToProcess.
     *   userData        - Object: User information to retrieve tweets from.
     *   tweetsToProcess - Array:  To be filled with retrieved tweets.
     *   numOfTweets     - Int:    Numer of tweets to retrieve.
     *   setDone         - Object: Callback function.
     */
    retrieveTweetsNew(userData, tweetsToProcess, numOfTweets, setDone) {
        this.twitterPkg.get('statuses/user_timeline', userData, (error, tweets, response) => {
            if (error) {
                this.utils.logError("Failed to retrieve tweets from user: " + userData.user_id);
                this.utils.logError("    Error: " + error[0]);
                return;
            }

            // If it is the first time calling the method (in which max_id would not be set)
            if (!userData.max_id && tweets.length > 0) {
                // Set the most recent tweet as last
                this.botStatus.trackedUsers[userData.user_id] = tweets[0].id_str;
            }

            if (tweets.length <= 200 && tweets.length > 0) {
                let tweetsAdded = 0;

                // Add tweets to processing list
                // Exclude final tweet as it will be used in next method iteration
                for (let i = 0; i < tweets.length; i++) {
                    let tweet = tweets[i];

                    // Do not include retweets.
                    if (!tweet.retweetedStatus) {
                        tweetsToProcess.push(tweets[i]);
                        tweetsAdded++;
                    }
                }

                if (tweetsAdded > 0) {
                    this.updateTweets = true;
                }

                numOfTweets -= tweetsAdded;

                if (numOfTweets > 0) {
                    // Continue processing the rest of the tweets
                    userData.max_id = tweets[tweets.length - 1].id_str;

                    // Avoid retrieving unneeded tweets
                    if (numOfTweets < 200) {
                        userData.count = numOfTweets;
                    }

                    this.retrieveTweetsNew(userData, tweetsToProcess, numOfTweets, setDone);
                } else {
                    setDone();
                }

            // Reached end of tweets
            } else {
                setDone();
            }
        });
    }

};
<|endoftext|>


<commit_before>'use strict';

var hopsBuild = require('hops-build');
var hopsExpress = require('hops-express');

module.exports = function startCommand (callback) {
  return {
    command: 'start',
    describe: 'Starts a development or production server, based on NODE_ENV',
    builder: {},
    handler: function startHandler (argv) {
      if (process.env.NODE_ENV === 'production') {
        hopsExpress.startServer(callback);
      } else {
        hopsBuild.runServer(argv, callback);
      }
    }
  };
};
<commit_msg>fix(local-cli): set clean option true for start command<commit_after>'use strict';

var hopsBuild = require('hops-build');
var hopsExpress = require('hops-express');

module.exports = function startCommand (callback) {
  return {
    command: 'start',
    describe: 'Starts a development or production server, based on NODE_ENV',
    builder: {
      clean: {
        alias: 'c',
        default: true,
        describe: 'Clean up artifacts in build / cache directories before building',
        type: 'boolean'
      }
    },
    handler: function startHandler (argv) {
      if (process.env.NODE_ENV === 'production') {
        hopsExpress.startServer(callback);
      } else {
        hopsBuild.runServer(argv, callback);
      }
    }
  };
};
<|endoftext|>


<commit_before>// Config Bar
jQuery(document).ready(function($){
	//Config Bar Control
	$('#config-bar ul > li a').click(function(){
		$(this).next().toggle();
		return false;
	});

	// Hide Config Bar
	$('#hide').click(function(){$('#config-bar').hide();return false;});
});<commit_msg>fixed js selector bug preenting logout button from working<commit_after>// Config Bar
jQuery(document).ready(function($){
	//Config Bar Control
	$('#config-nav > li a').click(function(){
		$(this).next().toggle();
		return false;
	});

	// Hide Config Bar
	$('#hide').click(function(){$('#config-bar').hide();return false;});
});<|endoftext|>


<commit_before>var stream;
var closeAndCallback;

var photo = new ReactiveVar(null);
var error = new ReactiveVar(null);
var waitingForPermission = new ReactiveVar(null);

var canvasWidth = 0;
var canvasHeight = 0;

var quality = 80;

Template.viewfinder.rendered = function() {
  var template = this;

  waitingForPermission.set(true);

  var video = template.find("video");

  // stream webcam video to the <video> element
  var success = function(newStream) {
    stream = newStream;

    if (navigator.mozGetUserMedia) {
      video.mozSrcObject = stream;
    } else {
      var vendorURL = window.URL || window.webkitURL;
      try {
        video.src = vendorURL.createObjectURL(stream);
      }catch (e) {
        //workaround based on https://bugzilla.mozilla.org/show_bug.cgi?id=1334564
        video.srcObject = stream;
      }
    }
    video.play();

    waitingForPermission.set(false);
  };

  // user declined or there was some other error
  var failure = function(err) {
    error.set(err);
  };

  // tons of different browser prefixes
  navigator.getUserMedia = (
    navigator.getUserMedia ||
    navigator.webkitGetUserMedia ||
    navigator.mozGetUserMedia ||
    navigator.msGetUserMedia
  );

  if (! navigator.getUserMedia) {
    // no browser support, sorry
    failure("BROWSER_NOT_SUPPORTED");
    return;
  }

  // initiate request for webcam
  navigator.getUserMedia({
      video: true,
      audio: false
  }, success, failure);

  // resize viewfinder to a reasonable size, not necessarily photo size
  var viewfinderWidth = 320;
  var viewfinderHeight = 240;
  var resized = false;
  video.addEventListener('canplay', function() {
    if (! resized) {
      viewfinderHeight = video.videoHeight / (video.videoWidth / viewfinderWidth);
      video.setAttribute('width', viewfinderWidth);
      video.setAttribute('height', viewfinderHeight);
      resized = true;
    }
  }, false);
};

// is the current error a permission denied error?
var permissionDeniedError = function () {
  return error.get() && (
    error.get().name === "PermissionDeniedError" || // Chrome and Opera
    error.get() === "PERMISSION_DENIED" // Firefox
  );
};

// is the current error a browser not supported error?
var browserNotSupportedError = function () {
  return error.get() && error.get() === "BROWSER_NOT_SUPPORTED";
};

var stopStream = function(st) {
  if(!st) {
    return;
  }

  if(st.stop) {
    st.stop();
    return;
  }

  if(st.getTracks) {
    var tracks = st.getTracks();
    for(var i = 0; i < tracks.length; i++) {
      var track = tracks[i];
      if(track && track.stop) {
        track.stop();
      }
    }
  }
};

Template.camera.helpers({
  photo: function () {
    return photo.get();
  },
  error: function () {
    return error.get();
  },
  permissionDeniedError: permissionDeniedError,
  browserNotSupportedError: browserNotSupportedError
});

Template.camera.events({
  "click .use-photo": function () {
    closeAndCallback(null, photo.get());
  },
  "click .new-photo": function () {
    photo.set(null);
  },
  "click .cancel": function () {
    if (permissionDeniedError()) {
      closeAndCallback(new Meteor.Error("permissionDenied", "Camera permissions were denied."));
    } else if (browserNotSupportedError()) {
      closeAndCallback(new Meteor.Error("browserNotSupported", "This browser isn't supported."));
    } else if (error.get()) {
      closeAndCallback(new Meteor.Error("unknownError", "There was an error while accessing the camera."));
    } else {
      closeAndCallback(new Meteor.Error("cancel", "Photo taking was cancelled."));
    }

    if (stream) {
      stopStream(stream);
    }
  }
});

Template.viewfinder.events({
  'click .shutter': function (event, template) {
    var video = template.find("video");
    var canvas = template.find("canvas");

    canvas.width = canvasWidth;
    canvas.height = canvasHeight;
    canvas.getContext('2d').drawImage(video, 0, 0, canvasWidth, canvasHeight);
    var data = canvas.toDataURL('image/jpeg', quality);
    photo.set(data);
    stopStream(stream);
  }
});

Template.viewfinder.helpers({
  "waitingForPermission": function () {
    return waitingForPermission.get();
  }
});

/**
 * @summary Get a picture from the device's default camera.
 * @param  {Object}   options  Options
 * @param {Number} options.height The minimum height of the image
 * @param {Number} options.width The minimum width of the image
 * @param {Number} options.quality [description]
 * @param  {Function} callback A callback that is called with two arguments:
 * 1. error, an object that contains error.message and possibly other properties
 * depending on platform
 * 2. data, a Data URI string with the image encoded in JPEG format, ready to
 * use as the `src` attribute on an `<img />` tag.
 */
MeteorCamera.getPicture = function (options, callback) {
  // if options are not passed
  if (! callback) {
    callback = options;
    options = {};
  }

  desiredHeight = options.height || 640;
  desiredWidth = options.width || 480;

  // Canvas#toDataURL takes the quality as a 0-1 value, not a percentage
  quality = (options.quality || 49) / 100;

  if (desiredHeight * 4 / 3 > desiredWidth) {
    canvasWidth = desiredHeight * 4 / 3;
    canvasHeight = desiredHeight;
  } else {
    canvasHeight = desiredWidth * 3 / 4;
    canvasWidth = desiredWidth;
  }

  canvasWidth = Math.round(canvasWidth);
  canvasHeight = Math.round(canvasHeight);

  var view;

  closeAndCallback = function () {
    var originalArgs = arguments;
    UI.remove(view);
    photo.set(null);
    callback.apply(null, originalArgs);
  };

  view = UI.renderWithData(Template.camera);
  UI.insert(view, document.body);
};
<commit_msg>fixes spacing<commit_after>var stream;
var closeAndCallback;

var photo = new ReactiveVar(null);
var error = new ReactiveVar(null);
var waitingForPermission = new ReactiveVar(null);

var canvasWidth = 0;
var canvasHeight = 0;

var quality = 80;

Template.viewfinder.rendered = function() {
  var template = this;

  waitingForPermission.set(true);

  var video = template.find("video");

  // stream webcam video to the <video> element
  var success = function(newStream) {
    stream = newStream;

    if (navigator.mozGetUserMedia) {
      video.mozSrcObject = stream;
    } else {
      var vendorURL = window.URL || window.webkitURL;
      try {
        video.src = vendorURL.createObjectURL(stream);
      } catch (e) {
        //workaround based on https://bugzilla.mozilla.org/show_bug.cgi?id=1334564
        video.srcObject = stream;
      }
    }
    video.play();

    waitingForPermission.set(false);
  };

  // user declined or there was some other error
  var failure = function(err) {
    error.set(err);
  };

  // tons of different browser prefixes
  navigator.getUserMedia = (
    navigator.getUserMedia ||
    navigator.webkitGetUserMedia ||
    navigator.mozGetUserMedia ||
    navigator.msGetUserMedia
  );

  if (! navigator.getUserMedia) {
    // no browser support, sorry
    failure("BROWSER_NOT_SUPPORTED");
    return;
  }

  // initiate request for webcam
  navigator.getUserMedia({
      video: true,
      audio: false
  }, success, failure);

  // resize viewfinder to a reasonable size, not necessarily photo size
  var viewfinderWidth = 320;
  var viewfinderHeight = 240;
  var resized = false;
  video.addEventListener('canplay', function() {
    if (! resized) {
      viewfinderHeight = video.videoHeight / (video.videoWidth / viewfinderWidth);
      video.setAttribute('width', viewfinderWidth);
      video.setAttribute('height', viewfinderHeight);
      resized = true;
    }
  }, false);
};

// is the current error a permission denied error?
var permissionDeniedError = function () {
  return error.get() && (
    error.get().name === "PermissionDeniedError" || // Chrome and Opera
    error.get() === "PERMISSION_DENIED" // Firefox
  );
};

// is the current error a browser not supported error?
var browserNotSupportedError = function () {
  return error.get() && error.get() === "BROWSER_NOT_SUPPORTED";
};

var stopStream = function(st) {
  if(!st) {
    return;
  }

  if(st.stop) {
    st.stop();
    return;
  }

  if(st.getTracks) {
    var tracks = st.getTracks();
    for(var i = 0; i < tracks.length; i++) {
      var track = tracks[i];
      if(track && track.stop) {
        track.stop();
      }
    }
  }
};

Template.camera.helpers({
  photo: function () {
    return photo.get();
  },
  error: function () {
    return error.get();
  },
  permissionDeniedError: permissionDeniedError,
  browserNotSupportedError: browserNotSupportedError
});

Template.camera.events({
  "click .use-photo": function () {
    closeAndCallback(null, photo.get());
  },
  "click .new-photo": function () {
    photo.set(null);
  },
  "click .cancel": function () {
    if (permissionDeniedError()) {
      closeAndCallback(new Meteor.Error("permissionDenied", "Camera permissions were denied."));
    } else if (browserNotSupportedError()) {
      closeAndCallback(new Meteor.Error("browserNotSupported", "This browser isn't supported."));
    } else if (error.get()) {
      closeAndCallback(new Meteor.Error("unknownError", "There was an error while accessing the camera."));
    } else {
      closeAndCallback(new Meteor.Error("cancel", "Photo taking was cancelled."));
    }

    if (stream) {
      stopStream(stream);
    }
  }
});

Template.viewfinder.events({
  'click .shutter': function (event, template) {
    var video = template.find("video");
    var canvas = template.find("canvas");

    canvas.width = canvasWidth;
    canvas.height = canvasHeight;
    canvas.getContext('2d').drawImage(video, 0, 0, canvasWidth, canvasHeight);
    var data = canvas.toDataURL('image/jpeg', quality);
    photo.set(data);
    stopStream(stream);
  }
});

Template.viewfinder.helpers({
  "waitingForPermission": function () {
    return waitingForPermission.get();
  }
});

/**
 * @summary Get a picture from the device's default camera.
 * @param  {Object}   options  Options
 * @param {Number} options.height The minimum height of the image
 * @param {Number} options.width The minimum width of the image
 * @param {Number} options.quality [description]
 * @param  {Function} callback A callback that is called with two arguments:
 * 1. error, an object that contains error.message and possibly other properties
 * depending on platform
 * 2. data, a Data URI string with the image encoded in JPEG format, ready to
 * use as the `src` attribute on an `<img />` tag.
 */
MeteorCamera.getPicture = function (options, callback) {
  // if options are not passed
  if (! callback) {
    callback = options;
    options = {};
  }

  desiredHeight = options.height || 640;
  desiredWidth = options.width || 480;

  // Canvas#toDataURL takes the quality as a 0-1 value, not a percentage
  quality = (options.quality || 49) / 100;

  if (desiredHeight * 4 / 3 > desiredWidth) {
    canvasWidth = desiredHeight * 4 / 3;
    canvasHeight = desiredHeight;
  } else {
    canvasHeight = desiredWidth * 3 / 4;
    canvasWidth = desiredWidth;
  }

  canvasWidth = Math.round(canvasWidth);
  canvasHeight = Math.round(canvasHeight);

  var view;

  closeAndCallback = function () {
    var originalArgs = arguments;
    UI.remove(view);
    photo.set(null);
    callback.apply(null, originalArgs);
  };

  view = UI.renderWithData(Template.camera);
  UI.insert(view, document.body);
};
<|endoftext|>