<commit_before>'use strict'

var uuid = require('node-uuid')

var OrSet = module.exports = function(pairs) {
  Object.defineProperties(this, {
    _elements: { value: [] },
    _utags:    { value: Object.create(null) }
  })

  if (pairs) this.reset(pairs)
}

OrSet.prototype = Object.create({

  get size() {
    return this._elements.length
  },

  has: function(e) {
    return this._elements.indexOf(e) > -1
  },

  add: function(e, u) {
    var utag, index

    // downstream
    if (u) {
      utag  = u
      index = this._elements.indexOf(e)
      if (index === -1) {
        index = this._elements.push(this._transform(e)) - 1
      }
    }
    // local
    else {
      if (this.has(e)) return
      utag = uuid.v4()
      index = this._elements.push(this._transform(e)) - 1
    }

    this._utags[utag] = index

    if (typeof Object.getNotifier === 'function') {
      Object.getNotifier(this).notify({
        type: 'add', name: utag, value: e
      })
    }
  },

  remove: function(e, u) {
    var index, utag

    // downstream
    if (u) {
      if (!(u in this._utags)) return
      index = this._utags[u]
      delete this._utags[u]

      if (typeof Object.getNotifier === 'function') {
        Object.getNotifier(this).notify({
          type: 'delete', name: u, oldValue: this._elements[index]
        })
      }

      for (utag in this._utags) {
        if (this._utags[utag] === index) {
          return
        }
      }
    }
    // local
    else {
      // var remove = []
      for (utag in this._utags) {
        var idx = this._utags[utag]
        if (this._elements[idx] === e) {
          // remove.push({
          //   e: this._elements[index],
          //   u: utag
          // })
          delete this._utags[utag]
          index = idx

          if (typeof Object.getNotifier === 'function') {
            Object.getNotifier(this).notify({
              type: 'delete', name: utag, oldValue: e
            })
          }
        }
      }
    }

    if (index >= 0) {
      this._elements.splice(index, 1)
      for (utag in this._utags) {
        if (this._utags[utag] > index) {
          this._utags[utag]--
        }
      }
    }
  },

  reset: function(pairs) {
    // empty
    this._elements.length = 0
    for (var u in this._utags) {
      delete this._utags[u]
    }

    // add pairs, represented as follows:
    // [[e, u], [e, u], ...]
    //   e: ..., // element
    //   u: ...  // unique tag
    pairs.forEach(function(pair) {
      if (Array.isArray(pair) && pair.length === 2) {
        var length = this._elements.push(this._transform(pair[0]))
        this._utags[pair[1]] = length - 1
      } else {
        this.add(pair)
      }
    }, this)
  },

  _transform: function(el) {
    return el
  }

}, { constructor: { value: OrSet } })

var arrayMethods = ['forEach', 'filter', 'map', 'reduce']
arrayMethods.forEach(function(method) {
  OrSet.prototype[method] = function() {
    return Array.prototype[method].apply(this._elements, arguments)
  }
})

OrSet.typed = function(T) {
  var TypedOrSet = function(pairs) {
    OrSet.call(this, pairs)
  }

  TypedOrSet.prototype = Object.create(OrSet.prototype, {
    constructor: { value: OrSet }
  })

  TypedOrSet.prototype._transform = function(el) {
    if (el && el instanceof T) {
      return el
    } else {
      return new T(el)
    }
  }

  return TypedOrSet
}
<commit_msg>make or-set iteratable<commit_after>'use strict'

var uuid = require('node-uuid')

var OrSet = module.exports = function(pairs) {
  Object.defineProperties(this, {
    _elements: { value: [] },
    _utags:    { value: Object.create(null) }
  })

  if (pairs) this.reset(pairs)
}

OrSet.prototype = Object.create({

  get size() {
    return this._elements.length
  },

  has: function(e) {
    return this._elements.indexOf(e) > -1
  },

  get: function(u) {
    if (!(u in this._utags)) {
      return undefined
    }

    return this._elements[this._utags[u]]
  },

  add: function(e, u) {
    var utag, index

    // downstream
    if (u) {
      utag  = u
      index = this._elements.indexOf(e)
      if (index === -1) {
        index = this._elements.push(this._transform(e)) - 1
      }
    }
    // local
    else {
      if (this.has(e)) return
      utag = uuid.v4()
      index = this._elements.push(this._transform(e)) - 1
    }

    this._utags[utag] = index

    if (typeof Object.getNotifier === 'function') {
      Object.getNotifier(this).notify({
        type: 'add', name: utag, value: e
      })
    }
  },

  remove: function(e, u) {
    var index, utag

    // downstream
    if (u) {
      if (!(u in this._utags)) return
      index = this._utags[u]
      delete this._utags[u]

      if (typeof Object.getNotifier === 'function') {
        Object.getNotifier(this).notify({
          type: 'delete', name: u, oldValue: this._elements[index]
        })
      }

      for (utag in this._utags) {
        if (this._utags[utag] === index) {
          return
        }
      }
    }
    // local
    else {
      // var remove = []
      for (utag in this._utags) {
        var idx = this._utags[utag]
        if (this._elements[idx] === e) {
          // remove.push({
          //   e: this._elements[index],
          //   u: utag
          // })
          delete this._utags[utag]
          index = idx

          if (typeof Object.getNotifier === 'function') {
            Object.getNotifier(this).notify({
              type: 'delete', name: utag, oldValue: e
            })
          }
        }
      }
    }

    if (index >= 0) {
      this._elements.splice(index, 1)
      for (utag in this._utags) {
        if (this._utags[utag] > index) {
          this._utags[utag]--
        }
      }
    }
  },

  reset: function(pairs) {
    // empty
    this._elements.length = 0
    for (var u in this._utags) {
      delete this._utags[u]
    }

    // add pairs, represented as follows:
    // [[e, u], [e, u], ...]
    //   e: ..., // element
    //   u: ...  // unique tag
    pairs.forEach(function(pair) {
      if (Array.isArray(pair) && pair.length === 2) {
        var length = this._elements.push(this._transform(pair[0]))
        this._utags[pair[1]] = length - 1
      } else {
        this.add(pair)
      }
    }, this)
  },

  entries: function() {
    var utags = Object.keys(this._utags)
    var self = this
    return {
      next: function() {
        var utag = utags.shift()
        return utag
          ? { value: [utag, self._elements[self._utags[utag]]], done: false }
          : { done: true }
      }
    }
  },

  values: function() {
    var utags = Object.keys(this._utags)
    var self = this
    return {
      next: function() {
        var utag = utags.shift()
        return utag
          ? { value: self._elements[self._utags[utag]], done: false }
          : { done: true }
      }
    }
  },

  _transform: function(el) {
    return el
  }

}, { constructor: { value: OrSet } })

var arrayMethods = ['forEach', 'filter', 'map', 'reduce']
arrayMethods.forEach(function(method) {
  OrSet.prototype[method] = function() {
    return Array.prototype[method].apply(this._elements, arguments)
  }
})

OrSet.typed = function(T) {
  var TypedOrSet = function(pairs) {
    OrSet.call(this, pairs)
  }

  TypedOrSet.prototype = Object.create(OrSet.prototype, {
    constructor: { value: OrSet }
  })

  TypedOrSet.prototype._transform = function(el) {
    if (el && el instanceof T) {
      return el
    } else {
      return new T(el)
    }
  }

  return TypedOrSet
}
<|endoftext|>


<commit_before>var mongoose = require('mongoose'),
    _ = require('underscore'),
    async = require('async');

var options = {
  depth: 2
};

var findFP = function (query, opts, cb) {
  if(opts.depth === 0) return cb(null, []);
  if(_.isUndefined(this.schema.filterFields)) return cb('Model ' + that.modelName + ' is missing a filter');

  // //  Check for passed in include/exlude fi
  // if(!_.isUndefined(opts.includeFields)) {
  //   opts.depth = _getFilterDepth(opts.includeFields);
  // }

  // //  Check for passed in include/exlude fi
  // if(!_.isUndefined(opts.excludeFields)) {
  //   opts.depth = _getFilterDepth(opts.excludeFields);
  // }

  var opts = _.clone(_.defaults(opts, options));

  opts.schemaFilter = this.schema.filterFields
  opts.schemaFields = this.schema.paths;

  var modelRefFields = _getModelReferences(this);
  // Find the fields that need to be populated
  opts.populateFields = _.filter(modelRefFields, function(item){ return item.field !== '_id'; });
  this.find(query, null, opts.paging).lean().exec(function(err, results) {
    if(err) return cb(err);
    if(results == null || results.length === 0) return cb(null, null);

    //  Our recursion check point
    if(opts.depth > 1) {
      _queryChildren(results, _.clone(opts), function(err, results){
        // If we're back up to the root call do final root filter
        if(_.isUndefined(opts.isChild) && !_.isUndefined(opts.hasFiltering)) return cb(err, _rootFilter(results, opts));

        cb(err, _filterResults(results, opts.schemaFilter));
      });
    } else {
      // If we're back up to the root call do final root filter
      if(_.isUndefined(opts.isChild) && !_.isUndefined(opts.hasFiltering)) return cb(err, _rootFilter(results, opts));

      cb(err, _filterResults(results, opts.schemaFilter));
    }
  });
};

var findOneFP = function (query, opts, cb) {
  this.findFP(query, opts, function(err, results){
    if(results == null) return cb(err, null)
    cb(err, results[0]);
  });
};

var findByIdAndUpdateFP = function (id, body, opts, callback) {

  var model = this;

  model.findByIdAndUpdate(id, body, function(err, result){
    if (err) return callback (err, null);
    if (result === null) return callback (null, null);
    //  Filter and Populate
    model.findFP({ _id: result.id }, opts, function(err, results){
      if(results == null) return callback(err, null)
      callback(err, results[0]);
    });
  });
};

var saveFP = function(opts, callback) {

  var model = this;

  model.save()
  .then(function(instance) {
    _recursiveFindFP(opts, model, instance, callback)
  })
  .catch(function(err) {
    callback (err, null);
  });
};

var _recursiveFindFP = function(opts, model, instance, callback) {

  mongoose.model(model.constructor.modelName)
  .findFP({ _id : instance._id}, opts, function(err, results){
    if(err) return callback(err);

    if(results) {
      callback(null, results[0])
    } else {
      return _recursiveFindFP(opts, model, instance, callback)
    }
  });
};

var _queryChildren = function(results, opts, callback) {

  async.map(results, function(result, callback){

    _queryChild(result, opts, callback);

  }, function(err, results){
    callback(err, results);
  });
};

var _queryChild = function(result, opts, callback) {

  async.each(opts.populateFields, function(field, callback){
    if(_.isUndefined(mongoose.model(field.model).schema.filterFields)) return callback('Model ' + field.model + ' is missing a filter');

    var childFields = mongoose.model(field.model).schema.filterFields;

    var options = {
      depth: opts.depth - 1,
      populateFields: childFields
    };

    options.isChild = true;

    var query = { _id : result[field.field] }
    if(_.isArray(result[field.field])) {
      query = { _id : { $in : result[field.field]} }
    }

    mongoose.model(field.model).findFP(query, _.clone(options), function(err, instances){
      if(err) return callback(err);
      if(instances === null) return callback(null);

      if (_.isArray(result[field.field])) {
        instances = _.map(result[field.field], function(item) {
          return _.find(instances, function(instance) {
            if(!instance || !instance.id) return false;
            return instance.id.toString() === item.toString();
          }) || {};
        });
      }

      result[field.field] = _.map(instances, function(instance){ return _.pick(instance, childFields); });

      // If it is not an array, return single instance
      if(!_isModelRefArray(opts.schemaFields[field.field])) result[field.field] = result[field.field][0];

      callback(null);
    });
  }, function(err){

    callback(err, result)

  });
};

var _rootFilter = function(results, opts) {

  if(opts.excludeFields) {
    return _.map(results, function(result) {
      var schemaFilter = opts.schemaFilter;
      return _.pick(stepThroughFilterExclude([result], opts.excludeFields)[0], schemaFilter);
    });
  } else {
    return _.map(results, function(result) {
      return stepThroughFilter([result], opts.includeFields)[0];
    });
  }
}

var stepThroughFilter = function(results, currentFields) {

  return _.map(results, function(result){

    if(result.id == null)
      result.id = result._id;

    // Check for wildcard, if so return all
    if(_.contains(currentFields, '*')) {
      return result;
    } else {
      var filterFields = _getCurrentFields(currentFields);
    }

    _.each(filterFields, function(field){
      var childFields = _getChildFields(currentFields, field);
      if(childFields.length > 0 && !_.isUndefined(result[field])) {
        if(_.isArray(result[field]))
          result[field] = stepThroughFilter(result[field], childFields);
        else
          result[field] = stepThroughFilter([result[field]], childFields)[0];
      } else {

        //  Field filter doesn't define children, so if is a Obj Ref field, just return the ids
        if(_.isArray(result[field]) && _.isObject(result[field][0]) && field !== 'id') {
          result[field] = _.map(result[field], function(field){
            return field.id;
          });
        } else if (_.isObject(result[field]) && !_.isArray(result[field]) && !_.isDate(result[field])  && field !== 'id' && !/^[0-9a-fA-F]{24}$/.test(result[field].toString()) ) {
          result[field] = result[field].id;
        }
      }
    });
    return _.pick(result, filterFields);
  });
};

var stepThroughFilterExclude = function(results, currentFields) {

  return _.map(results, function(result){

    if(result.id == null)
      result.id = result._id;

    var filterFields = _getCurrentFields(currentFields);

    _.each(filterFields, function(field){

      var childFields = _getChildFields(currentFields, field);

      if(childFields.length > 0 && !_.isUndefined(result[field])) {
        if(_.isArray(result[field]))
          result[field] = stepThroughFilterExclude(result[field], childFields);
        else
          result[field] = stepThroughFilterExclude([result[field]], childFields)[0];
      }
    });

    //  Check if it is an unpopluated field, resulting from relationship to entity no longer existing
    if(/^[0-9a-fA-F]{24}$/.test(result.toString())) return {};
    return _.omit(result, _getCurrentFieldsExclude(currentFields));
  });
};

var _filterResults = function(results, filterFields) {

  return _.map(results, function(result){
    if(result.id == null)
      result.id = result._id;

    return _.pick(result, filterFields);
  });
};

var _filterResult = function(result, filterFields) {
  if(result.id == null)
    result.id = result._id;
  return _.pick(result, filterFields);
};

var _getCurrentFields = function(dotNotionFields) {
  return _.uniq(_.compact(_.map(dotNotionFields, function(field){
    var field = field.split('.');
    return field[0];
  })));
};

var _getCurrentFieldsExclude = function(dotNotionFields) {

  return _.uniq(_.compact(_.filter(dotNotionFields, function(field){
    return field.indexOf('.') === -1
  })));
};

var _getChildFields = function(dotNotionFields, parentField) {
  return _.compact(_.map(dotNotionFields, function(field){
    var fields = field.split('.');
    if(fields[0] === parentField) {
      fields.shift();
      return fields.join('.');
    } else {
      return null;
    }
  }));
};

//  Check if the model ref an array
var _isModelRefArray = function(schemaField) {
  return _.isObject(schemaField.caster);
};

var _getModelReferences = function (model) {

  var fields = model.schema.paths;

  return _.flatten(_.compact(_.map(_.pairs(fields), function(field) {
    //  If it's an array, get actual field attrs
    if(_isModelRefArray(field[1])) field = [field[0], field[1].caster];
    //  We don't want type mixed fields, or non Model Refs or _id
    if(_.isUndefined(field[1].instance) || field[1].instance !== 'ObjectID' || field[0] === '_id') return null;

    //  Check for special multiple model ref
    if(field[1].options.refs) {
      return _.map(field[1].options.refs, function(ref){
        return { field: field[0], model: ref }
      });
    }

    return { field: field[0], model: field[1].options.ref };
  })));
};

var _getFilterDepth = function(filterFields) {
  if(filterFields.length === 0) return 0;

  //  This is a hack, need to look at nicer way
  return 7;

  return _.max(filterFields, function(field){
    return field.split('.').length;
  }).split('.').length;
};

module.exports = exports = function mongooseExpandPlugin(schema, options) {

  schema.statics.findFP = findFP;
  schema.statics.findOneFP = findOneFP;
  schema.statics.findByIdAndUpdateFP = findByIdAndUpdateFP;
  schema.methods.saveFP = saveFP;

};
<commit_msg>non existing schema fix<commit_after>var mongoose = require('mongoose'),
    _ = require('underscore'),
    async = require('async');

var options = {
  depth: 2
};

var findFP = function (query, opts, cb) {
  if(opts.depth === 0) return cb(null, []);
  if(_.isUndefined(this.schema.filterFields)) return cb('Model ' + that.modelName + ' is missing a filter');

  // //  Check for passed in include/exlude fi
  // if(!_.isUndefined(opts.includeFields)) {
  //   opts.depth = _getFilterDepth(opts.includeFields);
  // }

  // //  Check for passed in include/exlude fi
  // if(!_.isUndefined(opts.excludeFields)) {
  //   opts.depth = _getFilterDepth(opts.excludeFields);
  // }

  var opts = _.clone(_.defaults(opts, options));

  opts.schemaFilter = this.schema.filterFields
  opts.schemaFields = this.schema.paths;

  var modelRefFields = _getModelReferences(this);
  // Find the fields that need to be populated
  opts.populateFields = _.filter(modelRefFields, function(item){ return item.field !== '_id'; });
  this.find(query, null, opts.paging).lean().exec(function(err, results) {
    if(err) return cb(err);
    if(results == null || results.length === 0) return cb(null, null);

    //  Our recursion check point
    if(opts.depth > 1) {
      _queryChildren(results, _.clone(opts), function(err, results){
        // If we're back up to the root call do final root filter
        if(_.isUndefined(opts.isChild) && !_.isUndefined(opts.hasFiltering)) return cb(err, _rootFilter(results, opts));

        cb(err, _filterResults(results, opts.schemaFilter));
      });
    } else {
      // If we're back up to the root call do final root filter
      if(_.isUndefined(opts.isChild) && !_.isUndefined(opts.hasFiltering)) return cb(err, _rootFilter(results, opts));

      cb(err, _filterResults(results, opts.schemaFilter));
    }
  });
};

var findOneFP = function (query, opts, cb) {
  this.findFP(query, opts, function(err, results){
    if(results == null) return cb(err, null)
    cb(err, results[0]);
  });
};

var findByIdAndUpdateFP = function (id, body, opts, callback) {

  var model = this;

  model.findByIdAndUpdate(id, body, function(err, result){
    if (err) return callback (err, null);
    if (result === null) return callback (null, null);
    //  Filter and Populate
    model.findFP({ _id: result.id }, opts, function(err, results){
      if(results == null) return callback(err, null)
      callback(err, results[0]);
    });
  });
};

var saveFP = function(opts, callback) {

  var model = this;

  model.save()
  .then(function(instance) {
    _recursiveFindFP(opts, model, instance, callback)
  })
  .catch(function(err) {
    callback (err, null);
  });
};

var _recursiveFindFP = function(opts, model, instance, callback) {

  mongoose.model(model.constructor.modelName)
  .findFP({ _id : instance._id}, opts, function(err, results){
    if(err) return callback(err);

    if(results) {
      callback(null, results[0])
    } else {
      return _recursiveFindFP(opts, model, instance, callback)
    }
  });
};

var _queryChildren = function(results, opts, callback) {

  async.map(results, function(result, callback){

    _queryChild(result, opts, callback);

  }, function(err, results){
    callback(err, results);
  });
};

var _queryChild = function(result, opts, callback) {

  async.each(opts.populateFields, function(field, callback){

    // If document has reference to Model that no longer exists
    try {
      mongoose.model(field.model)
    } catch(err) {
      return callback(null)
    }

    if(_.isUndefined(mongoose.model(field.model).schema.filterFields)) return callback('Model ' + field.model + ' is missing a filter');

    var childFields = mongoose.model(field.model).schema.filterFields;

    var options = {
      depth: opts.depth - 1,
      populateFields: childFields
    };

    options.isChild = true;

    var query = { _id : result[field.field] }
    if(_.isArray(result[field.field])) {
      query = { _id : { $in : result[field.field]} }
    }

    mongoose.model(field.model).findFP(query, _.clone(options), function(err, instances){
      if(err) return callback(err);
      if(instances === null) return callback(null);

      if (_.isArray(result[field.field])) {
        instances = _.map(result[field.field], function(item) {
          return _.find(instances, function(instance) {
            if(!instance || !instance.id) return false;
            return instance.id.toString() === item.toString();
          }) || {};
        });
      }

      result[field.field] = _.map(instances, function(instance){ return _.pick(instance, childFields); });

      // If it is not an array, return single instance
      if(!_isModelRefArray(opts.schemaFields[field.field])) result[field.field] = result[field.field][0];

      callback(null);
    });
  }, function(err){

    callback(err, result)

  });
};

var _rootFilter = function(results, opts) {

  if(opts.excludeFields) {
    return _.map(results, function(result) {
      var schemaFilter = opts.schemaFilter;
      return _.pick(stepThroughFilterExclude([result], opts.excludeFields)[0], schemaFilter);
    });
  } else {
    return _.map(results, function(result) {
      return stepThroughFilter([result], opts.includeFields)[0];
    });
  }
}

var stepThroughFilter = function(results, currentFields) {

  return _.map(results, function(result){

    if(result.id == null)
      result.id = result._id;

    // Check for wildcard, if so return all
    if(_.contains(currentFields, '*')) {
      return result;
    } else {
      var filterFields = _getCurrentFields(currentFields);
    }

    _.each(filterFields, function(field){
      var childFields = _getChildFields(currentFields, field);
      if(childFields.length > 0 && !_.isUndefined(result[field])) {
        if(_.isArray(result[field]))
          result[field] = stepThroughFilter(result[field], childFields);
        else
          result[field] = stepThroughFilter([result[field]], childFields)[0];
      } else {

        //  Field filter doesn't define children, so if is a Obj Ref field, just return the ids
        if(_.isArray(result[field]) && _.isObject(result[field][0]) && field !== 'id') {
          result[field] = _.map(result[field], function(field){
            return field.id;
          });
        } else if (_.isObject(result[field]) && !_.isArray(result[field]) && !_.isDate(result[field])  && field !== 'id' && !/^[0-9a-fA-F]{24}$/.test(result[field].toString()) ) {
          result[field] = result[field].id;
        }
      }
    });
    return _.pick(result, filterFields);
  });
};

var stepThroughFilterExclude = function(results, currentFields) {

  return _.map(results, function(result){

    if(result.id == null)
      result.id = result._id;

    var filterFields = _getCurrentFields(currentFields);

    _.each(filterFields, function(field){

      var childFields = _getChildFields(currentFields, field);

      if(childFields.length > 0 && !_.isUndefined(result[field])) {
        if(_.isArray(result[field]))
          result[field] = stepThroughFilterExclude(result[field], childFields);
        else
          result[field] = stepThroughFilterExclude([result[field]], childFields)[0];
      }
    });

    //  Check if it is an unpopluated field, resulting from relationship to entity no longer existing
    if(/^[0-9a-fA-F]{24}$/.test(result.toString())) return {};
    return _.omit(result, _getCurrentFieldsExclude(currentFields));
  });
};

var _filterResults = function(results, filterFields) {

  return _.map(results, function(result){
    if(result.id == null)
      result.id = result._id;

    return _.pick(result, filterFields);
  });
};

var _filterResult = function(result, filterFields) {
  if(result.id == null)
    result.id = result._id;
  return _.pick(result, filterFields);
};

var _getCurrentFields = function(dotNotionFields) {
  return _.uniq(_.compact(_.map(dotNotionFields, function(field){
    var field = field.split('.');
    return field[0];
  })));
};

var _getCurrentFieldsExclude = function(dotNotionFields) {

  return _.uniq(_.compact(_.filter(dotNotionFields, function(field){
    return field.indexOf('.') === -1
  })));
};

var _getChildFields = function(dotNotionFields, parentField) {
  return _.compact(_.map(dotNotionFields, function(field){
    var fields = field.split('.');
    if(fields[0] === parentField) {
      fields.shift();
      return fields.join('.');
    } else {
      return null;
    }
  }));
};

//  Check if the model ref an array
var _isModelRefArray = function(schemaField) {
  return _.isObject(schemaField.caster);
};

var _getModelReferences = function (model) {

  var fields = model.schema.paths;

  return _.flatten(_.compact(_.map(_.pairs(fields), function(field) {
    //  If it's an array, get actual field attrs
    if(_isModelRefArray(field[1])) field = [field[0], field[1].caster];
    //  We don't want type mixed fields, or non Model Refs or _id
    if(_.isUndefined(field[1].instance) || field[1].instance !== 'ObjectID' || field[0] === '_id') return null;

    //  Check for special multiple model ref
    if(field[1].options.refs) {
      return _.map(field[1].options.refs, function(ref){
        return { field: field[0], model: ref }
      });
    }

    return { field: field[0], model: field[1].options.ref };
  })));
};

var _getFilterDepth = function(filterFields) {
  if(filterFields.length === 0) return 0;

  //  This is a hack, need to look at nicer way
  return 7;

  return _.max(filterFields, function(field){
    return field.split('.').length;
  }).split('.').length;
};

module.exports = exports = function mongooseExpandPlugin(schema, options) {

  schema.statics.findFP = findFP;
  schema.statics.findOneFP = findOneFP;
  schema.statics.findByIdAndUpdateFP = findByIdAndUpdateFP;
  schema.methods.saveFP = saveFP;

};
<|endoftext|>


<commit_before>'use strict';

var _createClass = require('babel-runtime/helpers/create-class')['default'];

var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];

var _Number$isInteger = require('babel-runtime/core-js/number/is-integer')['default'];

var _Map = require('babel-runtime/core-js/map')['default'];

Object.defineProperty(exports, '__esModule', {
    value: true
});

var SpanishConvertor = (function () {
    function SpanishConvertor() {
        _classCallCheck(this, SpanishConvertor);
    }

    /**
     * @returns {Map}
     */

    _createClass(SpanishConvertor, null, [{
        key: 'convert',
        value: function convert(number) {
            if (!_Number$isInteger(number)) {
                throw new Error('Invalid number "' + number + '"');
            }

            var numberMap = getNumberMap();

            if (numberMap.has(number)) {
                return numberMap.get(number);
            }
        }
    }]);

    return SpanishConvertor;
})();

exports.SpanishConvertor = SpanishConvertor;
function getNumberMap() {
    var numbers = [[1, 'uno'], [2, 'dos'], [3, 'tres'], [4, 'cuatro'], [5, 'cinco'], [6, 'seis'], [7, 'siete'], [8, 'ocho'], [9, 'nueve'], [10, 'diez']];

    return new _Map(numbers);
}<commit_msg>hasta cien, build <ES6<commit_after>'use strict';

var _createClass = require('babel-runtime/helpers/create-class')['default'];

var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];

var _Number$isInteger = require('babel-runtime/core-js/number/is-integer')['default'];

var _Map = require('babel-runtime/core-js/map')['default'];

Object.defineProperty(exports, '__esModule', {
    value: true
});

var SpanishConvertor = (function () {
    function SpanishConvertor() {
        _classCallCheck(this, SpanishConvertor);
    }

    /**
     * @returns {Map}
     */

    _createClass(SpanishConvertor, null, [{
        key: 'convert',
        value: function convert(number) {
            if (!_Number$isInteger(number)) {
                throw new Error('Invalid number "' + number + '"');
            }

            var numberMap = getNumberMap();

            if (numberMap.has(number)) {
                return numberMap.get(number);
            }

            if (number >= 17 && number <= 19) {
                return 'dieci' + this.convert(number - 10);
            }
            if (number >= 21 && number <= 29) {
                return 'veinti' + this.convert(number - 20);
            }
            if (number > 30 && number < 100) {
                var decimal = Math.floor(number / 10) * 10;
                var misc = number % 10;
                return this.convert(decimal) + ' y ' + this.convert(misc);
            }
        }
    }]);

    return SpanishConvertor;
})();

exports.SpanishConvertor = SpanishConvertor;
function getNumberMap() {
    var numbers = [[1, 'uno'], [2, 'dos'], [3, 'tres'], [4, 'cuatro'], [5, 'cinco'], [6, 'seis'], [7, 'siete'], [8, 'ocho'], [9, 'nueve'], [10, 'diez'], [11, 'once'], [12, 'doce'], [13, 'trece'], [14, 'catorce'], [15, 'quince'], [16, 'dieciséis'], //has extra accent
    [20, 'veinte'], [22, 'veintidós'], //accent
    [23, 'veintitrés'], //accent
    [26, 'veintiséis'], //accent
    [30, 'treinta'], [40, 'cuarenta'], [50, 'cincuenta'], [60, 'sesenta'], [70, 'setenta'], [80, 'ochenta'], [90, 'noventa'], [100, 'cien']];

    return new _Map(numbers);
}<|endoftext|>


<commit_before>const logger = require('./logger')();
const Discord = require('discord.js');
const fs = require('fs');
const path = require('path');
const notify = require('./notify');
const now = require('performance-now');
const perms = require('./permissions')();
require('./memoryUsage');

logger.info('Starting Bot: PID:', process.pid);

// Create the Discord Client
const client = new Discord.Client({
  disableEveryone: true,
  disabledEvents: [
    'TYPING_START',
  ],
});

// Extent the client object
// circular Discord object for RichEmbeds
client.Discord = Discord;
client.config = require('../config');
client.utils = require('./utilities');
client.logger = logger;

client.commands = new Discord.Collection();
client.aliases = new Discord.Collection();
client.guildConfigs = new Discord.Collection();
client.commandPerms = new Discord.Collection();

// Shortcuts to different file paths
client.tempDir = path.join(process.cwd(), 'temp');
client.assetsDir = path.join(process.cwd(), 'assets');
client.commandsDir = path.join(process.cwd(), 'lib/cmds');

// Default Guild Config
client.defaultGuildConfig = {
  prefix: client.config.prefix,
};

// Create property to count items
client.count = {
  messages: 0,
  commands: 0,
  tweets: 0,
  twitch: 0,
};

// Instantiate other modules into the client object
client.mongo = require('./mongo')(client);
client.twitch = require('./twitch')(client);
client.twitter = require('./twitter')(client);

client.twitch.waiting = [];
client.twitch.count = { users: 0, channels: 0 };
client.twitter.mentions = [];
client.twitter.started = false;
client.twitter.reload = false;

client.utils.loadAllCommands(client)
  .then(() => {
    logger.info(`Loaded ${client.commands.size} command${client.commands.size === 1 ? '' : 's'}, ` +
      `with ${client.aliases.size} aliases.`);
  })
  .catch(err => {
    logger.error('Error loading commands', err);
  });

// Create a temp directory if one doesn't not exist on startup
if (!fs.existsSync(client.tempDir)) fs.mkdirSync(client.tempDir);

// Discord has disconnected
client.on('disconnect', () => {
  logger.warn('Discord disconnected');
});

// Discord general warning
client.on('warn', info => {
  logger.warn('Discord warning', info);
});

// Discord is reconnecting
client.on('reconnecting', () => {
  logger.info('Discord reconnecting');
});

// Discord has resumed
client.on('resume', replayed => {
  logger.info(`Discord has resumed, replayed ${replayed} item(s)`);
});

// Discord has erred
client.on('error', err => {
  logger.error('Discord client error:', err ? err.stack : '');
});

client.on('ready', () => {
  logger.info(`Successful connection to Discord as '${client.user.username}'`);
});

// Run only on once on script launch
client.once('ready', () => {
  // If this bot is not DBKynd's test Bot...
  if (client.user.id !== '120105547633524736') {
    // Send the Owner a message that the Bot has started, indicates a crash or restart
    const owner = client.users.get(client.config.owner_id);
    if (owner) {
      owner.send(`I just started running. Did I crash? :worried:\nPID:\`\`${process.pid}\`\``);
    }
  }
  // Start the Twitch and Twitter Clients
  client.twitch.start();
  client.twitter.start();
  // Websocket client to post Twitch bans from AnneMunition's channel
  require('./twitchMessages')(client);
  // Timers
  // Check if we need to restart MusicBot
  require('./timers')(client);
});

// The bot has joined a guild, notify the owner
client.on('guildCreate', guild => {
  notify.guildCreate(client, guild);
});

// The client has left a guild, notify the owner
client.on('guildDelete', guild => {
  notify.guildDelete(client, guild);
});

// A new member has joined a connected server
client.on('guildMemberAdd', member => {
  notify.guildMemberAdd(client, member);
});

// A member has left/kick/ban from a connected server
client.on('guildMemberRemove', member => {
  notify.guildMemberRemove(client, member);
});

// Received a message from a discord chat
client.on('message', msg => {
  // Don't listen to other Bots
  if (msg.author.bot) return;
  // Save time of message start process
  msg.startTime = now();
  // Increment the message count
  client.count.messages++;
  // Check for and run any prefixed commands
  prefixedCommands(msg);
  // Do other stuff with messages below...
});

// Connect to Discord
logger.debug('Connecting to Discord...');
client.login(client.config.bot_token).catch(logger.error);

setInterval(() => {
  // Ensure we are logged every 5 minutes
  // Relog if the client is not in good status
  if (client.status !== 0) relog();
}, 1000 * 60 * 5);

async function relog() {
  logger.info('Re-logging to Discord');
  try {
    await client.destroy();
    client.login(client.config.bot_token);
  } catch (e) {
    logger.error(e);
  }
  return null;
}

async function prefixedCommands(msg) {
  // Get the default guild config
  let conf = client.defaultGuildConfig;
  if (msg.channel.type === 'text') {
    // Get stored guild config if exists and not a dm channel
    if (client.guildConfigs.has(msg.guild.id)) {
      conf = client.guildConfigs.get(msg.guild.id);
    }
    // Exit if message doesn't use the guild prefix
    if (!msg.content.startsWith(conf.prefix)) return;
  } else if (msg.channel.type === 'dm') {
    if (!msg.content.startsWith(conf.prefix)) {
      // See if the prefix matches any stored from any guild ArmoryBot is in
      const anyConfig = client.guildConfigs.find(x => msg.content.startsWith(x.prefix));
      if (!anyConfig) return;
      // Store the prefix the DM user used
      conf.prefix = anyConfig.prefix;
    }
  }
  // Save the prefix used on this message so we can use it to build guild specific help commands
  msg.prefix = conf.prefix;
  // Split message into an array
  const params = msg.content.split(' ');
  // Pull first index and remove prefix
  const cmd = params.shift().slice(conf.prefix.length).toLowerCase();
  // Exit if no command was given (prefix only)
  if (!cmd) return;
  logger.debug('Command:', cmd, JSON.stringify(params));
  // Attempt to get command from Collection
  let command;
  if (client.commands.has(cmd)) {
    command = client.commands.get(cmd);
  } else if (client.aliases.has(cmd)) {
    command = client.aliases.get(cmd);
  }
  if (!command || !command.run || typeof command.run !== 'function') {
    logger.debug(`The cmd '${cmd}' does not exist, is not loaded, or is missing the function 'run'`);
    return;
  }
  // Store command name to message object
  msg.cmd = cmd;
  // Log the command usage
  if (msg.channel.type === 'dm') {
    logger.info(`[DM] <${msg.author.tag}>: ${msg.content}`);
  } else {
    logger.info(`[${msg.guild.name}] (#${msg.channel.name}) ` +
      `<${msg.author.tag}>: ${msg.content}`);
  }
  logger.debug('Command Required Permissions:', command.info.permissions);
  // Ensure the bot client has every permissions required to run this command on Discord's end
  if (msg.channel.type === 'text' && command.info.permissions &&
    !msg.channel.permissionsFor(client.user).has(command.info.permissions)) {
    client.utils.ownerError('Permission Requirement', client, null, msg, cmd);
    logger.error('We do not have the required permissions in this channel to execute the command to completion.');
    return;
  }
  const allowed = perms.check(client, msg, cmd);
  if (allowed) {
    logger.info(`ALLOWED '${msg.author.username}' ${cmd}' in channel ` +
      `'${msg.channel.type === 'dm' ? 'DM' : msg.channel.name}'`);
    // Increment the commands counter
    client.count.commands++;
    // Attempt to run the command
    command.run(client, msg, params)
      .then(() => {
        // If the command completes OK run cleanup tasks
        logger.info(`${cmd} finished in: ${(now() - msg.startTime).toFixed(3)}ms`);
      })
      .catch(err => {
        // If the command fails for some reason, message the bot owner about it
        client.utils.ownerError('Command', client, err, msg, cmd);
      });
  } else {
    logger.info(`DENIED '${msg.author.username}' ${cmd}' in channel ` +
      `'${msg.channel.type === 'dm' ? 'DM' : msg.channel.name}'`);
    msg.reply('You do not have permission to run this command here.');
  }
}

// Message the bot owner on any unhandled errors
process.on('unhandledRejection', err => {
  client.utils.ownerError('Unhandled', client, err);
});
<commit_msg>split args on any number of spaces<commit_after>const logger = require('./logger')();
const Discord = require('discord.js');
const fs = require('fs');
const path = require('path');
const notify = require('./notify');
const now = require('performance-now');
const perms = require('./permissions')();
require('./memoryUsage');

logger.info('Starting Bot: PID:', process.pid);

// Create the Discord Client
const client = new Discord.Client({
  disableEveryone: true,
  disabledEvents: [
    'TYPING_START',
  ],
});

// Extent the client object
// circular Discord object for RichEmbeds
client.Discord = Discord;
client.config = require('../config');
client.utils = require('./utilities');
client.logger = logger;

client.commands = new Discord.Collection();
client.aliases = new Discord.Collection();
client.guildConfigs = new Discord.Collection();
client.commandPerms = new Discord.Collection();

// Shortcuts to different file paths
client.tempDir = path.join(process.cwd(), 'temp');
client.assetsDir = path.join(process.cwd(), 'assets');
client.commandsDir = path.join(process.cwd(), 'lib/cmds');

// Default Guild Config
client.defaultGuildConfig = {
  prefix: client.config.prefix,
};

// Create property to count items
client.count = {
  messages: 0,
  commands: 0,
  tweets: 0,
  twitch: 0,
};

// Instantiate other modules into the client object
client.mongo = require('./mongo')(client);
client.twitch = require('./twitch')(client);
client.twitter = require('./twitter')(client);

// A list of people to
client.twitch.waiting = [];
client.twitch.count = { users: 0, channels: 0 };
// A list of members to @ mention when the twitter account they
// want posts for gets added to the monitored twitter client
client.twitter.mentions = [];
client.twitter.started = false;
client.twitter.reload = false;

client.utils.loadAllCommands(client)
  .then(() => {
    logger.info(`Loaded ${client.commands.size} command${client.commands.size === 1 ? '' : 's'}, ` +
      `with ${client.aliases.size} aliases.`);
  })
  .catch(err => {
    logger.error('Error loading commands', err);
  });

// Create a temp directory if one doesn't not exist on startup
if (!fs.existsSync(client.tempDir)) fs.mkdirSync(client.tempDir);

// Discord has disconnected
client.on('disconnect', () => {
  logger.warn('Discord disconnected');
});

// Discord general warning
client.on('warn', info => {
  logger.warn('Discord warning', info);
});

// Discord is reconnecting
client.on('reconnecting', () => {
  logger.info('Discord reconnecting');
});

// Discord has resumed
client.on('resume', replayed => {
  logger.info(`Discord has resumed, replayed ${replayed} item(s)`);
});

// Discord has erred
client.on('error', err => {
  logger.error('Discord client error:', err ? err.stack : '');
});

client.on('ready', () => {
  logger.info(`Successful connection to Discord as '${client.user.username}'`);
});

// Run only on once on script launch
client.once('ready', () => {
  // If this bot is not DBKynd's test Bot...
  if (client.user.id !== '120105547633524736') {
    // Send the Owner a message that the Bot has started, indicates a crash or restart
    const owner = client.users.get(client.config.owner_id);
    if (owner) {
      owner.send(`I just started running. Did I crash? :worried:\nPID:\`\`${process.pid}\`\``);
    }
  }
  // Start the Twitch and Twitter Clients
  client.twitch.start();
  client.twitter.start();
  // Websocket client to post Twitch bans from AnneMunition's channel
  require('./twitchMessages')(client);
  // Timers
  // Check if we need to restart MusicBot
  require('./timers')(client);
});

// The bot has joined a guild, notify the owner
client.on('guildCreate', guild => {
  notify.guildCreate(client, guild);
});

// The client has left a guild, notify the owner
client.on('guildDelete', guild => {
  notify.guildDelete(client, guild);
});

// A new member has joined a connected server
client.on('guildMemberAdd', member => {
  notify.guildMemberAdd(client, member);
});

// A member has left/kick/ban from a connected server
client.on('guildMemberRemove', member => {
  notify.guildMemberRemove(client, member);
});

// Received a message from a discord chat
client.on('message', msg => {
  // Don't listen to other Bots
  if (msg.author.bot) return;
  // Save time of message start process
  msg.startTime = now();
  // Increment the message count
  client.count.messages++;
  // Check for and run any prefixed commands
  prefixedCommands(msg);
  // Do other stuff with messages below...
});

// Connect to Discord
logger.debug('Connecting to Discord...');
client.login(client.config.bot_token).catch(logger.error);

setInterval(() => {
  // Ensure we are logged every 5 minutes
  // Relog if the client is not in good status
  if (client.status !== 0) relog();
}, 1000 * 60 * 5);

async function relog() {
  logger.info('Re-logging to Discord');
  try {
    await client.destroy();
    client.login(client.config.bot_token);
  } catch (e) {
    logger.error(e);
  }
  return null;
}

function prefixedCommands(msg) {
  // Get the default guild config
  let conf = client.defaultGuildConfig;
  if (msg.channel.type === 'text') {
    // Get stored guild config if exists and not a dm channel
    if (client.guildConfigs.has(msg.guild.id)) {
      conf = client.guildConfigs.get(msg.guild.id);
    }
    // Exit if message doesn't use the guild prefix
    if (!msg.content.startsWith(conf.prefix)) return;
  } else if (msg.channel.type === 'dm') {
    if (!msg.content.startsWith(conf.prefix)) {
      // See if the prefix matches any stored from any guild ArmoryBot is in
      const anyConfig = client.guildConfigs.find(x => msg.content.startsWith(x.prefix));
      if (!anyConfig) return;
      // Store the prefix the DM user used
      conf.prefix = anyConfig.prefix;
    }
  }
  // Save the prefix used on this message so we can use it to build guild specific help commands
  msg.prefix = conf.prefix;
  // Split message into an array on any number of spaces
  const params = msg.content.split(/ +/g);
  // Pull first index and remove prefix
  const cmd = params.shift().slice(conf.prefix.length).toLowerCase();
  // Exit if no command was given (prefix only)
  if (!cmd) return;
  logger.debug('Command:', cmd, JSON.stringify(params));
  // Attempt to get command from Collection
  let command;
  if (client.commands.has(cmd)) {
    command = client.commands.get(cmd);
  } else if (client.aliases.has(cmd)) {
    command = client.aliases.get(cmd);
  }
  if (!command || !command.run || typeof command.run !== 'function') {
    logger.debug(`The cmd '${cmd}' does not exist, is not loaded, or is missing the 'run' function`);
    return;
  }
  // Store command name to message object
  msg.cmd = cmd;
  // Log the command usage
  if (msg.channel.type === 'dm') {
    logger.info(`[DM] <${msg.author.tag}>: ${msg.content}`);
  } else {
    logger.info(`[${msg.guild.name}] (#${msg.channel.name}) ` +
      `<${msg.author.tag}>: ${msg.content}`);
  }
  logger.debug('Command Required Permissions:', command.info.permissions);
  // Ensure the bot client has every permissions required to run this command on Discord's end
  if (msg.channel.type === 'text' && command.info.permissions &&
    !msg.channel.permissionsFor(client.user).has(command.info.permissions)) {
    client.utils.ownerError('Permission Requirement', client, null, msg, cmd);
    logger.error('We do not have the required permissions in this channel to execute the command to completion.');
    return;
  }
  const allowed = perms.check(client, msg, cmd);
  if (allowed) {
    logger.info(`ALLOWED '${msg.author.username}' ${cmd}' in channel ` +
      `'${msg.channel.type === 'dm' ? 'DM' : msg.channel.name}'`);
    // Increment the commands counter
    client.count.commands++;
    // Attempt to run the command
    command.run(client, msg, params)
      .then(() => {
        // If the command completes OK run cleanup tasks
        logger.info(`${cmd} finished in: ${(now() - msg.startTime).toFixed(3)}ms`);
      })
      .catch(err => {
        // If the command fails for some reason, message the bot owner about it
        client.utils.ownerError('Command', client, err, msg, cmd);
      });
  } else {
    logger.info(`DENIED '${msg.author.username}' ${cmd}' in channel ` +
      `'${msg.channel.type === 'dm' ? 'DM' : msg.channel.name}'`);
    msg.reply('You do not have permission to run this command here.');
  }
}

// Message the bot owner on any unhandled errors
process.on('unhandledRejection', err => {
  client.utils.ownerError('Unhandled', client, err);
});
<|endoftext|>


<commit_before>const Game = require('./game')

var assets = allAssets()
var assetCount = Object.keys(assets).length
var loadedCount = 0
startGame(assets)

function allAssets() {
  var assets = {}
  assets.bulletUp = new Image()
  assets.bulletUp.src = '../graphics/bullet_up.gif'
  assets.bulletDown = new Image()
  assets.bulletDown.src = '../graphics/bullet_down.gif'

  return assets
}

function startGame(assets) {
  for(var key in assets){
    assets[key].onload = function(){
      loadedCount++
      if (loadedCount === assetCount) {
        new Game(assets)
      }
    }
  }
}
<commit_msg>Further improve asset loading<commit_after>const Game = require('./game')

var assets = allAssets()
var assetCount = Object.keys(assets).length
var loadedCount = 0
startGame(assets)

function allAssets() {
  var assets = {}
  assets.bulletUp = newImage('../graphics/bullet_up.gif')
  assets.bulletDown = newImage('../graphics/bullet_down.gif')

  return assets
}

function startGame(assets) {
  for(var key in assets){
    assets[key].onload = function(){
      loadedCount++
      if (loadedCount === assetCount) {
        new Game(assets)
      }
    }
  }
}

function newImage(src){
  var image = new Image()
  image.src = src
  return image
}

/*
-scoring
-automate random dropping of bullets from sky
-

*/
<|endoftext|>


<commit_before>/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
  'use strict';

  var InvalidInputError = require('./input-error.js'),
    transformationFunctions = require('./transforms'),
    transformationKeys = require('./keys'),
    regex =  /^(\d*)d(\d+|\%)(([\+\-\/\*bw])(\d+))?(([\+\-\/\*])(\d+|(\d*)d(\d+|\%)(([\+\-\/\*bw])(\d+))?))*$/gi,
    roll;

  roll = function (random) {
    this.random = random || function () {
      return Math.random();
    };
  };

  roll.prototype.validate = function (s) {
    return regex.test(s);
  };

  roll.prototype.parse = function (s) {
    if (!this.validate(s)) {
      throw new InvalidInputError(s);
    }

    var match = regex.exec(s),
      quantity = match[1], // 2d20+3 => 2
      sides = match[2], // 2d20+3 => 20
      hasTransformation = !!match[3], // 2d20+3 => true
      operator,
      transformationParameter,
      transforms = [],
      opIndex = 0,
      segLength,
      segments = s.split(/[\+\-\/\*]/);

      for (var seg = 1; seg < segments.length; seg++){
        opIndex += segments[seg - 1].length;
        operator = s[opIndex]; // 2d20+3 => "+"
        opIndex += 1;
        transformationParameter = segments[seg]; // 2d20+3 => 3
        if (transformationParameter.indexOf('d') > -1){
          transforms.push(transformationKeys[operator](parseInt(this.roll(transformationParameter).result, 10)));
        } else {
          transforms.push(transformationKeys[operator](parseInt(transformationParameter, 10)));
        }
      }

    return {
      quantity: quantity ? parseInt(quantity, 10) : 1,
      sides: sides === '%' ? 100 : parseInt(sides, 10),
      transformations: hasTransformation ? transformationKeys[operator](parseInt(transformationParameter, 10)) : ['sum'],
      toString: function () {
        return s;
      }
    };
  };

  roll.prototype.roll = function (input) {
    if (!input) {
      throw new InvalidInputError();
    } else if (typeof input === 'string') {
      input = this.parse(input);
    } else if (input instanceof Array) {
      var resultset = [];
      for (var i in input){
       resultset.push(this.roll(input[i]));
      }
      return resultset;
    }

    var rolled = [],
     calculations = [];

    while (rolled.length < input.quantity) {
      rolled.push(Math.floor((this.random() * input.sides) + 1));
    }

    calculations = input.transformations.reduce(function (previous, transformation) {
      var transformationFunction,
        transformationAdditionalParameter;

      if (typeof transformation === 'function') { // lets you pass something custom in
        transformationFunction = transformation;
      } else if (typeof transformation === 'string') { // "sum"
        transformationFunction = transformationFunctions[transformation];
      } else if (transformation instanceof Array) { // ["add", 3]
        transformationFunction = transformationFunctions[transformation[0]]; // fn for "add"
        transformationAdditionalParameter = transformation[1]; // 3
      }

      previous.unshift(transformationFunction(previous[0], transformationAdditionalParameter));
      return previous;
    }, [rolled]);

    return {
      input: input,
      calculations: calculations,
      rolled: calculations[calculations.length - 1],
      result: calculations[0]
    };
  };

  module.exports = roll;
  module.exports.InvalidInputError = InvalidInputError;

}());

<commit_msg>remove regex flags<commit_after>/*jslint indent: 2*/
/*global require: true, module: true*/
(function () {
  'use strict';

  var InvalidInputError = require('./input-error.js'),
    transformationFunctions = require('./transforms'),
    transformationKeys = require('./keys'),
    regex =  /^(\d*)d(\d+|\%)(([\+\-\/\*bw])(\d+))?(([\+\-\/\*])(\d+|(\d*)d(\d+|\%)(([\+\-\/\*bw])(\d+))?))*$/,
    roll;

  roll = function (random) {
    this.random = random || function () {
      return Math.random();
    };
  };

  roll.prototype.validate = function (s) {
    return regex.test(s);
  };

  roll.prototype.parse = function (s) {
    if (!this.validate(s)) {
      throw new InvalidInputError(s);
    }

    var match = regex.exec(s),
      quantity = match[1], // 2d20+3 => 2
      sides = match[2], // 2d20+3 => 20
      hasTransformation = !!match[3], // 2d20+3 => true
      operator,
      transformationParameter,
      transforms = [],
      opIndex = 0,
      segLength,
      segments = s.split(/[\+\-\/\*]/);

      for (var seg = 1; seg < segments.length; seg++){
        opIndex += segments[seg - 1].length;
        operator = s[opIndex]; // 2d20+3 => "+"
        opIndex += 1;
        transformationParameter = segments[seg]; // 2d20+3 => 3
        if (transformationParameter.indexOf('d') > -1){
          transforms.push(transformationKeys[operator](parseInt(this.roll(transformationParameter).result, 10)));
        } else {
          transforms.push(transformationKeys[operator](parseInt(transformationParameter, 10)));
        }
      }

    return {
      quantity: quantity ? parseInt(quantity, 10) : 1,
      sides: sides === '%' ? 100 : parseInt(sides, 10),
      transformations: hasTransformation ? transformationKeys[operator](parseInt(transformationParameter, 10)) : ['sum'],
      toString: function () {
        return s;
      }
    };
  };

  roll.prototype.roll = function (input) {
    if (!input) {
      throw new InvalidInputError();
    } else if (typeof input === 'string') {
      input = this.parse(input);
    } else if (input instanceof Array) {
      var resultset = [];
      for (var i in input){
       resultset.push(this.roll(input[i]));
      }
      return resultset;
    }

    var rolled = [],
     calculations = [];

    while (rolled.length < input.quantity) {
      rolled.push(Math.floor((this.random() * input.sides) + 1));
    }

    calculations = input.transformations.reduce(function (previous, transformation) {
      var transformationFunction,
        transformationAdditionalParameter;

      if (typeof transformation === 'function') { // lets you pass something custom in
        transformationFunction = transformation;
      } else if (typeof transformation === 'string') { // "sum"
        transformationFunction = transformationFunctions[transformation];
      } else if (transformation instanceof Array) { // ["add", 3]
        transformationFunction = transformationFunctions[transformation[0]]; // fn for "add"
        transformationAdditionalParameter = transformation[1]; // 3
      }

      previous.unshift(transformationFunction(previous[0], transformationAdditionalParameter));
      return previous;
    }, [rolled]);

    return {
      input: input,
      calculations: calculations,
      rolled: calculations[calculations.length - 1],
      result: calculations[0]
    };
  };

  module.exports = roll;
  module.exports.InvalidInputError = InvalidInputError;

}());

<|endoftext|>


<commit_before>let extend = require('extend')
let Helper = require('./helper')
let defaultRequestFn = getDefaultRequestFn()
let underscored = require('underscore.string/underscored')

class Wrapper {
  /**
   * Creates a new Wrapper given a client (function) and options object.
   * A function is required, over other values such as objects, so that
   * invocations may be proxied and trapped using apply.
   *
   * @constructor
   * @param {function} client
   * @param {object}   opts
   */
  constructor (client, opts) {
    this._client = client
    this._opts = opts
  }

  /**
   * Static factory method that, given an API URL and options object, returns
   * a new Proxy. An instance of the Wrapper class is applied as the handler
   * to a target function.
   *
   * @param   {string} url
   * @param   {object} opts
   * @returns {Proxy}
   */
  static create (url, opts) {
    let client = function () {}
    client._url = Helper.removeTrailingSlash(url)

    if (opts.whitelist) {
      let keys = opts.whitelist
      if (!(keys instanceof Array)) {
        keys = Object.keys(opts.whitelist)
      }

      keys.forEach((key) => {
        client[key] = function () {}
      })
    }

    let wrapper = new Wrapper(client, opts)

    let methods = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options']
    methods.forEach((method) => {
      client[method] = function () {
        return wrapper._invokeMethod(client, method.toUpperCase(), [...arguments])
      }
    })

    if (opts.aliases) {
      Object.keys(opts.aliases).forEach((key) => {
        let val = opts.aliases[key]
        client[key] = client[val].bind(client)
      })
    }

    return new Proxy(client, wrapper)
  }

  /**
   * Handles invocations outside of HTTP methods, e.g. client.users(1)
   *
   * @param   {function} client
   * @param   {function} context
   * @param   {*[]}      args
   * @returns {Proxy}
   */
  apply (client, context, args) {
    let str = args.join('/')
    let url = `${client._url}/${str}`
    return Wrapper.create(url, this._opts)
  }

  /**
   * Handles property accesses, e.g. client.users
   *
   * @param   {function} client
   * @param   {*}        name
   * @returns {Proxy|function|bool}
   * @throws  Will throw if a whitelist is supplied, and the property name
   *          is not in the list
   */
  get (client, name) {
    let whitelist = this._opts.whitelist

    let createUpdated = (str) => {
      let opts = Object.assign({}, this._opts)
      if (opts.camelCase) str = underscored(str)
      if (opts.whitelist) {
        opts.whitelist = opts.whitelist[str] || []
      }
      let url = `${client._url}/${str}`
      return Wrapper.create(url, opts)
    }

    if (typeof name === 'symbol') {
      // Likely inspecting the object in node where showProxy is false
      return true
    } else if (whitelist && this._inWhitelist(name)) {
      return createUpdated(name)
    } else if (name in client) {
      return client[name]
    } else if (whitelist) {
      let err = new Error(`${name} not listed in swaddle's whitelist`)
      throw err
    } else if (client === this._client) {
      return createUpdated(name)
    }
  }

  /**
   * Invokes the specified HTTP method with the supplied request library.
   * Handles modifications to the result, as well as response body.
   *
   * @param   {function} client
   * @param   {string}   method
   * @param   {*[]}      args
   * @returns {*}
   */
  _invokeMethod (client, method, args) {
    let part
    if (this._shouldWrapInBody(method, args)) {
      args[0] = {body: args[0]}
    } else if (this._isPartOfPath(args[0])) {
      part = args[0]
      args.shift()
    }

    if (!args[0] || typeof args[0] !== 'object') {
      args.unshift({})
    }

    let opts = args[0] = extend(true, {}, this._opts, args[0])
    opts.method = method

    let url = this._buildUrl(client._url, part, opts.extension)
    let returnBody = opts.returnBody
    let fn = opts.fn

    this._updateCamelCaseRequest(opts)
    this._updateJSONRequest(opts)

    let keys = ['returnBody', 'fn', 'whitelist', 'camelCase',
      'extension', 'aliases', 'sendAsBody']
    keys.forEach((key) => delete opts[key])

    args.unshift(url)

    if (returnBody && typeof args[args.length - 1] === 'function') {
      let cb = args[args.length - 1]
      args[args.length - 1] = (err, res) => cb(err, this._getBody(res))
    }

    let req = fn.apply(null, args)

    if (returnBody && req.then) {
      req = req.then(res => this._getBody(res))
    }

    return req
  }

  /**
   * Returns whether or not the string is in the whitelist.
   *
   * @param   {string} str
   * @returns {bool}
   */
  _inWhitelist (str) {
    let whitelist = this._opts.whitelist

    if (!whitelist) {
      return false
    } else if (whitelist instanceof Array) {
      return whitelist.indexOf(str) !== -1
    } else {
      return (str in whitelist)
    }
  }

  /**
   * Returns whether or not a value should be included in an URL's path.
   *
   * @param   {*}    x
   * @returns {bool}
   */
  _isPartOfPath (x) {
    return x != null && (!!x.substring || !!x.toFixed)
  }

  /**
   * Returns whether or not the data passed to the current invocation should
   * be nested in a body attribute, as specified by the sendAsBody option.
   *
   * @param   {string} method
   * @param   {*[]}    args
   * @returns {bool}
   */
  _shouldWrapInBody (method, args) {
    let methods = ['POST', 'PUT', 'PATCH']

    return args.length && this._opts.sendAsBody &&
      typeof args[0] !== 'function' && methods.indexOf(method) !== -1
  }

  /**
   * Returns the final request url, based on the current client url,
   * a part to append to the path, as well as an optional file extension.
   *
   * @param   {string} url
   * @param   {string} part
   * @param   {string} extensions
   * @returns {string}
   */
  _buildUrl (url, part, extension) {
    if (extension) {
      if (part == null) {
        url += `.${extension}`
      } else if (part.indexOf('?') === 0) {
        url += `.${extension}${part}`
      } else {
        url += `/${part}.${extension}`
      }
    } else if (part != null) {
      url += `/${part}`
    }
    return url
  }

  /**
   * If the camelCase option was enabled, renames all camelCase keys in the
   * request body object to be snake_case.
   *
   * @param {object} opts
   */
  _updateCamelCaseRequest (opts) {
    if (!opts.camelCase) return

    if (opts.json instanceof Object) {
      // request sets content-type to json when opts.json is an object
      return Helper.convertToSnakeCase(opts.json)
    } else if (!('body' in opts)) {
      return
    }

    Helper.convertToSnakeCase(opts.body)
  }

  /**
   * If the json option was enabled, serializes the request body and adds
   * a content-type header.
   *
   * @param {object} opts
   */
  _updateJSONRequest (opts) {
    if (!opts.json || !('body' in opts)) return

    if (opts.fn.name !== 'request') {
      // Need to stringify for got/fetch
      opts.body = JSON.stringify(opts.body)
    }

    // content-type needs to be set to json when using body
    opts.headers = opts.headers || {}
    opts.headers['content-type'] = 'application/json'
  }

  /**
   * Returns the body of the response. For fetch and fetch-compliant libraries,
   * returns a promise that resolves to the body. If the json opt is truthy,
   * the response body is parsed. If camelCase was true, it's also converted.
   *
   * @param   {*} res
   * @returns {*}
   */
  _getBody (res) {
    if (!res) return

    // Optionally normalize object keys
    let normalize = (res) => {
      return (this._opts.camelCase) ? Helper.convertToCamelCase(res) : res
    }

    // Handle whatwg spec fetch. Note that empty response body will cause
    // res.json() to throw, so use res.text() instead
    if (typeof res.text === 'function') {
      if (!this._opts.json) return res.text()
      return res.text().then((res) => {
        return res ? normalize(JSON.parse(res)) : res
      })
    }

    // Other request libraries, which serialize JSON automatically
    let body = this._isResponseObject(res) ? res.body : res
    return normalize(body)
  }

  /**
   * Returns whether or not an object is a response object, based on the
   * internal classes used by got, request, and fetch.
   *
   * @param   {*} res
   * @returns {bool}
   */
  _isResponseObject (res) {
    let hasReadableState = res._readableState instanceof Object &&
      res._readableState.constructor.name === 'ReadableState'
    let type = res.constructor.name

    return hasReadableState ||
      type === 'IncomingMessage' ||
      type.indexOf('Response') !== -1
  }
}

/**
 * Attempts to require and return request, got, or the window.fetch function,
 * in that order.
 *
 * @returns {function}
 */
function getDefaultRequestFn () {
  let libs = ['got', 'request-promise', 'request']
  for (let i = 0; i < libs.length; i++) {
    try {
      return require(libs[i])
    } catch (err) {
      // no-op
    }
  }

  if (typeof window !== 'undefined') {
    return window.fetch
  }
}

module.exports = function swaddle (url, opts = {}) {
  opts = opts || {}
  opts.fn = opts.fn || defaultRequestFn
  opts.headers = opts.headers || {}

  let defaultTrue = ['json', 'returnBody']
  defaultTrue.forEach((key) => {
    if (key in opts) return
    opts[key] = true
  })

  if (!url) {
    let err = new Error('swaddle requires an URL')
    throw err
  }

  if (opts.camelCase && (!opts.returnBody || !opts.json)) {
    let err = new Error('camelCase must be used with returnBody and json')
    throw err
  }

  if (!opts.fn) {
    let err = new Error(`swaddle requires one of the following:
      * request is installed (npm install --save request)
      * got is installed (npm install --save got)
      * that fetch be available in the browser
      * a request function is supplied via the "fn" option`)
    throw err
  }

  if (!opts.headers['User-Agent']) {
    opts.headers['User-Agent'] = 'swaddle'
  }

  return Wrapper.create(url, opts)
}
<commit_msg>Fix trailing slash in requests<commit_after>let extend = require('extend')
let Helper = require('./helper')
let defaultRequestFn = getDefaultRequestFn()
let underscored = require('underscore.string/underscored')

class Wrapper {
  /**
   * Creates a new Wrapper given a client (function) and options object.
   * A function is required, over other values such as objects, so that
   * invocations may be proxied and trapped using apply.
   *
   * @constructor
   * @param {function} client
   * @param {object}   opts
   */
  constructor (client, opts) {
    this._client = client
    this._opts = opts
  }

  /**
   * Static factory method that, given an API URL and options object, returns
   * a new Proxy. An instance of the Wrapper class is applied as the handler
   * to a target function.
   *
   * @param   {string} url
   * @param   {object} opts
   * @returns {Proxy}
   */
  static create (url, opts) {
    let client = function () {}
    client._url = Helper.removeTrailingSlash(url)

    if (opts.whitelist) {
      let keys = opts.whitelist
      if (!(keys instanceof Array)) {
        keys = Object.keys(opts.whitelist)
      }

      keys.forEach((key) => {
        client[key] = function () {}
      })
    }

    let wrapper = new Wrapper(client, opts)

    let methods = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options']
    methods.forEach((method) => {
      client[method] = function () {
        return wrapper._invokeMethod(client, method.toUpperCase(), [...arguments])
      }
    })

    if (opts.aliases) {
      Object.keys(opts.aliases).forEach((key) => {
        let val = opts.aliases[key]
        client[key] = client[val].bind(client)
      })
    }

    return new Proxy(client, wrapper)
  }

  /**
   * Handles invocations outside of HTTP methods, e.g. client.users(1)
   *
   * @param   {function} client
   * @param   {function} context
   * @param   {*[]}      args
   * @returns {Proxy}
   */
  apply (client, context, args) {
    let str = args.join('/')
    let url = `${client._url}/${str}`
    return Wrapper.create(url, this._opts)
  }

  /**
   * Handles property accesses, e.g. client.users
   *
   * @param   {function} client
   * @param   {*}        name
   * @returns {Proxy|function|bool}
   * @throws  Will throw if a whitelist is supplied, and the property name
   *          is not in the list
   */
  get (client, name) {
    let whitelist = this._opts.whitelist

    let createUpdated = (str) => {
      let opts = Object.assign({}, this._opts)
      if (opts.camelCase) str = underscored(str)
      if (opts.whitelist) {
        opts.whitelist = opts.whitelist[str] || []
      }
      let url = `${client._url}/${str}`
      return Wrapper.create(url, opts)
    }

    if (typeof name === 'symbol') {
      // Likely inspecting the object in node where showProxy is false
      return true
    } else if (whitelist && this._inWhitelist(name)) {
      return createUpdated(name)
    } else if (name in client) {
      return client[name]
    } else if (whitelist) {
      let err = new Error(`${name} not listed in swaddle's whitelist`)
      throw err
    } else if (client === this._client) {
      return createUpdated(name)
    }
  }

  /**
   * Invokes the specified HTTP method with the supplied request library.
   * Handles modifications to the result, as well as response body.
   *
   * @param   {function} client
   * @param   {string}   method
   * @param   {*[]}      args
   * @returns {*}
   */
  _invokeMethod (client, method, args) {
    let part
    if (this._shouldWrapInBody(method, args)) {
      args[0] = {body: args[0]}
    } else if (this._isPartOfPath(args[0])) {
      part = args[0]
      args.shift()
    }

    if (!args[0] || typeof args[0] !== 'object') {
      args.unshift({})
    }

    let opts = args[0] = extend(true, {}, this._opts, args[0])
    opts.method = method

    let url = this._buildUrl(client._url, part, opts.extension)
    let returnBody = opts.returnBody
    let fn = opts.fn

    this._updateCamelCaseRequest(opts)
    this._updateJSONRequest(opts)

    let keys = ['returnBody', 'fn', 'whitelist', 'camelCase',
      'extension', 'aliases', 'sendAsBody']
    keys.forEach((key) => delete opts[key])

    args.unshift(url)

    if (returnBody && typeof args[args.length - 1] === 'function') {
      let cb = args[args.length - 1]
      args[args.length - 1] = (err, res) => cb(err, this._getBody(res))
    }

    let req = fn.apply(null, args)

    if (returnBody && req.then) {
      req = req.then(res => this._getBody(res))
    }

    return req
  }

  /**
   * Returns whether or not the string is in the whitelist.
   *
   * @param   {string} str
   * @returns {bool}
   */
  _inWhitelist (str) {
    let whitelist = this._opts.whitelist

    if (!whitelist) {
      return false
    } else if (whitelist instanceof Array) {
      return whitelist.indexOf(str) !== -1
    } else {
      return (str in whitelist)
    }
  }

  /**
   * Returns whether or not a value should be included in an URL's path.
   *
   * @param   {*}    x
   * @returns {bool}
   */
  _isPartOfPath (x) {
    return x != null && (!!x.substring || !!x.toFixed)
  }

  /**
   * Returns whether or not the data passed to the current invocation should
   * be nested in a body attribute, as specified by the sendAsBody option.
   *
   * @param   {string} method
   * @param   {*[]}    args
   * @returns {bool}
   */
  _shouldWrapInBody (method, args) {
    let methods = ['POST', 'PUT', 'PATCH']

    return args.length && this._opts.sendAsBody &&
      typeof args[0] !== 'function' && methods.indexOf(method) !== -1
  }

  /**
   * Returns the final request url, based on the current client url,
   * a part to append to the path, as well as an optional file extension.
   *
   * @param   {string} url
   * @param   {string} part
   * @param   {string} extensions
   * @returns {string}
   */
  _buildUrl (url, part, extension) {
    if (extension) {
      if (part == null) {
        url += `.${extension}`
      } else if (part.indexOf('?') === 0) {
        url += `.${extension}${part}`
      } else {
        url += `/${part}.${extension}`
      }
    } else if (part != null) {
      if (part.indexOf('?') === 0) {
        url += part
      } else {
        url += `/${part}`
      }
    }
    return url
  }

  /**
   * If the camelCase option was enabled, renames all camelCase keys in the
   * request body object to be snake_case.
   *
   * @param {object} opts
   */
  _updateCamelCaseRequest (opts) {
    if (!opts.camelCase) return

    if (opts.json instanceof Object) {
      // request sets content-type to json when opts.json is an object
      return Helper.convertToSnakeCase(opts.json)
    } else if (!('body' in opts)) {
      return
    }

    Helper.convertToSnakeCase(opts.body)
  }

  /**
   * If the json option was enabled, serializes the request body and adds
   * a content-type header.
   *
   * @param {object} opts
   */
  _updateJSONRequest (opts) {
    if (!opts.json || !('body' in opts)) return

    if (opts.fn.name !== 'request') {
      // Need to stringify for got/fetch
      opts.body = JSON.stringify(opts.body)
    }

    // content-type needs to be set to json when using body
    opts.headers = opts.headers || {}
    opts.headers['content-type'] = 'application/json'
  }

  /**
   * Returns the body of the response. For fetch and fetch-compliant libraries,
   * returns a promise that resolves to the body. If the json opt is truthy,
   * the response body is parsed. If camelCase was true, it's also converted.
   *
   * @param   {*} res
   * @returns {*}
   */
  _getBody (res) {
    if (!res) return

    // Optionally normalize object keys
    let normalize = (res) => {
      return (this._opts.camelCase) ? Helper.convertToCamelCase(res) : res
    }

    // Handle whatwg spec fetch. Note that empty response body will cause
    // res.json() to throw, so use res.text() instead
    if (typeof res.text === 'function') {
      if (!this._opts.json) return res.text()
      return res.text().then((res) => {
        return res ? normalize(JSON.parse(res)) : res
      })
    }

    // Other request libraries, which serialize JSON automatically
    let body = this._isResponseObject(res) ? res.body : res
    return normalize(body)
  }

  /**
   * Returns whether or not an object is a response object, based on the
   * internal classes used by got, request, and fetch.
   *
   * @param   {*} res
   * @returns {bool}
   */
  _isResponseObject (res) {
    let hasReadableState = res._readableState instanceof Object &&
      res._readableState.constructor.name === 'ReadableState'
    let type = res.constructor.name

    return hasReadableState ||
      type === 'IncomingMessage' ||
      type.indexOf('Response') !== -1
  }
}

/**
 * Attempts to require and return request, got, or the window.fetch function,
 * in that order.
 *
 * @returns {function}
 */
function getDefaultRequestFn () {
  let libs = ['got', 'request-promise', 'request']
  for (let i = 0; i < libs.length; i++) {
    try {
      return require(libs[i])
    } catch (err) {
      // no-op
    }
  }

  if (typeof window !== 'undefined') {
    return window.fetch
  }
}

module.exports = function swaddle (url, opts = {}) {
  opts = opts || {}
  opts.fn = opts.fn || defaultRequestFn
  opts.headers = opts.headers || {}

  let defaultTrue = ['json', 'returnBody']
  defaultTrue.forEach((key) => {
    if (key in opts) return
    opts[key] = true
  })

  if (!url) {
    let err = new Error('swaddle requires an URL')
    throw err
  }

  if (opts.camelCase && (!opts.returnBody || !opts.json)) {
    let err = new Error('camelCase must be used with returnBody and json')
    throw err
  }

  if (!opts.fn) {
    let err = new Error(`swaddle requires one of the following:
      * request is installed (npm install --save request)
      * got is installed (npm install --save got)
      * that fetch be available in the browser
      * a request function is supplied via the "fn" option`)
    throw err
  }

  if (!opts.headers['User-Agent']) {
    opts.headers['User-Agent'] = 'swaddle'
  }

  return Wrapper.create(url, opts)
}
<|endoftext|>


<commit_before>(function () {
  "use strict";
  var jwt = require("jwt-simple");
  var rp = require("request-promise");
  var InputDataValidator = require("./validators");

  module.exports = function PayapiClient(params) {

    function print(key, value) {
      console.log(key + ":" + JSON.stringify(value, null, 2));
    }

    function toPaymentObject() {
      var jwtHeader64 = params.paymentToken.split(".")[0];
      var jwtHeader = JSON.parse(new Buffer(jwtHeader64, "base64").toString());
      var alg = jwtHeader.alg;
      if (false && alg === "HS512" ||
          alg === "HS384" ||
          alg === "HS256" ||
          alg === "RS256") {
        return jwt.decode(params.paymentToken, params.apiKey);
      } else {
        throw new Error("Only HS256, HS384, HS512 and RS256 algorithms are supported for JWT.");
      }
    }

    function sanitizeAndExtractBinAndLast4(apiKeyAndToken) {
      var paymentObject = toPaymentObject();
      if(params.optionalFields.indexOf("payment") < 0 &&
          params.optionalFields.indexOf("payment.creditCardNumber") < 0) {
        paymentObject.payment.creditCardNumber = paymentObject.payment.creditCardNumber.replace(/\s/g,"");
        paymentObject.payment.cardBin = paymentObject.payment.creditCardNumber
          .replace(/\s/g, "")
          .substring(0, 6);
      }
      return paymentObject;
    }

    function toPaymentToken() {
      return jwt.encode(params.payload, params.apiKey, "HS512");
    }

    if(params) {
      params.returningConsumer = params.returningConsumer || false;
      params.optionalFields = params.optionalFields || [];
      params.extra = params.extra || {};

      if(params.payment && params.payment.creditCardNumber) {
        params.payment.creditCardNumber = params.payment.creditCardNumber.replace(/\s/g, "");
        params.payment.cardBin = params.payment.creditCardNumber
          .replace(/\s/g, "")
          .substring(0, 6);
      }

      if(params.paymentToken) {
        // decode, sanitize and re-package
        params.payload = sanitizeAndExtractBinAndLast4();
        params.paymentToken = toPaymentToken();
      }
    }

    function validateApiKey(params) {
      return new Promise(function(resolve, reject) {
        if(!params.apiKey) {
          reject("apiKey must not be empty");
        } else {
          resolve(params);
        }
      });
    }

    // secret is unused in v1, but is here as a placeholder for v2
    function validateSecret(params) {
      return new Promise(function(resolve, reject) {
        if(!params.secret) {
          reject("secret must not be empty");
        } else {
          resolve(params);
        }
      });
    }

    function validatePayload(params) {
      return new Promise(function(resolve, reject) {
        var payload = {
          payment: params.payment,
          consumer: params.consumer,
          order: params.order,
          products: params.products,
          callbacks: params.callbacks,
          returningConsumer: params.returningConsumer || false,
          optionalFields: params.optionalFields || [],
          extra: params.extra || {}
        };
        var validationErrors = new InputDataValidator(payload).validate();
        if(validationErrors.length > 0) {
          reject(validationErrors);
        } else {
          params.payload = payload;
          resolve(params);
        }
      });
    }

    function createToken(params) {
      return new Promise(function(resolve, reject) {
        if(params.payload) {
          params.paymentToken = toPaymentToken(params.payload, params.apiKey, "HS512");
          resolve(params);
        } else if(params.paymentToken) {
          resolve(params);
        } else {
          reject(new Error("No payment token or payload to create a payment token from. Unable to continue."));
        }
      });
    }

    function doRestCall(params) {
      return new Promise(function(resolve, reject) {
        var uri;
        switch(process.env.NODE_ENV) {
          case "production":
            uri = "https://input.payapi.io/v1/api/authorized/payments";
            break;
          case "staging":
            uri = "https://staging-input.payapi.io/v1/api/authorized/payments";
            break;
          default:
            uri = "http://localhost:3000/v1/api/authorized/payments";
        }
        rp({
          method: "POST",
          uri: uri,
          body: {
            authenticationToken: params.authenticationToken,
            paymentToken: params.paymentToken
          },
          json: true
        }).then(function(params) {
          resolve(params);
        }).catch(function(err) {
          reject(err);
        });
      });
    }

    function handleError(err) {
      return new Promise(function(resolve, reject) {
        console.log("Payapi Client Error:" + JSON.stringify(err, null, 2));
        reject(err);
      });
    }

    function convertNumberToNumber(params) {
      return new Promise(function(resolve, reject) {
        for(var i = 0; i < params.payload.products.length; i++) {
          params.payload.products[i].quantity = Number(params.payload.products[i].quantity);
          params.payload.products[i].priceInCentsIncVat = Number(params.payload.products[i].priceInCentsIncVat);
          params.payload.products[i].priceInCentsExcVat = Number(params.payload.products[i].priceInCentsExcVat);
          params.payload.products[i].vatPercentage = Number(params.payload.products[i].vatPercentage);
        }
        params.payload.order.sumInCentsIncVat = Number(params.payload.order.sumInCentsIncVat);
        params.payload.order.sumInCentsExcVat = Number(params.payload.order.sumInCentsExcVat);
        resolve(params);
      });
    }

    return {
      call: function(callParams) {
        if(callParams) {
          return doRestCall(callParams).catch(handleError);
        } else {
          return validateApiKey(params)
            .then(validateSecret)
            .then(validatePayload)
            .then(convertNumberToNumber)
            .then(createToken)
            .then(doRestCall)
            .catch(handleError);
        }
      },
      paymentToken: function() {
        return validatePayload(params)
          .then(convertNumberToNumber)
          .then(createToken)
          .then(function(params) {
            return new Promise(function(resolve, reject) {
              resolve(params.paymentToken);
            });
          })
          .catch(function(err) {
            throw err;
          });
      },
      validate: function() {
        return validatePayload(params);
      },
      encodePaymentToken: function() {
        return toPaymentToken();
      },
      decodePaymentToken: function() {
        var paymentObject = toPaymentObject();
        paymentObject.optionalFields = params.optionalFields;
        return validatePayload(paymentObject)
          .then(convertNumberToNumber)
          .then(function(result) {
            return result;
          })
          .catch(function(err) {
            throw err;
          });
      },
    };
  };
}());
<commit_msg>Testing CI error handling: build success<commit_after>(function () {
  "use strict";
  var jwt = require("jwt-simple");
  var rp = require("request-promise");
  var InputDataValidator = require("./validators");

  module.exports = function PayapiClient(params) {

    function print(key, value) {
      console.log(key + ":" + JSON.stringify(value, null, 2));
    }

    function toPaymentObject() {
      var jwtHeader64 = params.paymentToken.split(".")[0];
      var jwtHeader = JSON.parse(new Buffer(jwtHeader64, "base64").toString());
      var alg = jwtHeader.alg;
      if (alg === "HS512" ||
          alg === "HS384" ||
          alg === "HS256" ||
          alg === "RS256") {
        return jwt.decode(params.paymentToken, params.apiKey);
      } else {
        throw new Error("Only HS256, HS384, HS512 and RS256 algorithms are supported for JWT.");
      }
    }

    function sanitizeAndExtractBinAndLast4(apiKeyAndToken) {
      var paymentObject = toPaymentObject();
      if(params.optionalFields.indexOf("payment") < 0 &&
          params.optionalFields.indexOf("payment.creditCardNumber") < 0) {
        paymentObject.payment.creditCardNumber = paymentObject.payment.creditCardNumber.replace(/\s/g,"");
        paymentObject.payment.cardBin = paymentObject.payment.creditCardNumber
          .replace(/\s/g, "")
          .substring(0, 6);
      }
      return paymentObject;
    }

    function toPaymentToken() {
      return jwt.encode(params.payload, params.apiKey, "HS512");
    }

    if(params) {
      params.returningConsumer = params.returningConsumer || false;
      params.optionalFields = params.optionalFields || [];
      params.extra = params.extra || {};

      if(params.payment && params.payment.creditCardNumber) {
        params.payment.creditCardNumber = params.payment.creditCardNumber.replace(/\s/g, "");
        params.payment.cardBin = params.payment.creditCardNumber
          .replace(/\s/g, "")
          .substring(0, 6);
      }

      if(params.paymentToken) {
        // decode, sanitize and re-package
        params.payload = sanitizeAndExtractBinAndLast4();
        params.paymentToken = toPaymentToken();
      }
    }

    function validateApiKey(params) {
      return new Promise(function(resolve, reject) {
        if(!params.apiKey) {
          reject("apiKey must not be empty");
        } else {
          resolve(params);
        }
      });
    }

    // secret is unused in v1, but is here as a placeholder for v2
    function validateSecret(params) {
      return new Promise(function(resolve, reject) {
        if(!params.secret) {
          reject("secret must not be empty");
        } else {
          resolve(params);
        }
      });
    }

    function validatePayload(params) {
      return new Promise(function(resolve, reject) {
        var payload = {
          payment: params.payment,
          consumer: params.consumer,
          order: params.order,
          products: params.products,
          callbacks: params.callbacks,
          returningConsumer: params.returningConsumer || false,
          optionalFields: params.optionalFields || [],
          extra: params.extra || {}
        };
        var validationErrors = new InputDataValidator(payload).validate();
        if(validationErrors.length > 0) {
          reject(validationErrors);
        } else {
          params.payload = payload;
          resolve(params);
        }
      });
    }

    function createToken(params) {
      return new Promise(function(resolve, reject) {
        if(params.payload) {
          params.paymentToken = toPaymentToken(params.payload, params.apiKey, "HS512");
          resolve(params);
        } else if(params.paymentToken) {
          resolve(params);
        } else {
          reject(new Error("No payment token or payload to create a payment token from. Unable to continue."));
        }
      });
    }

    function doRestCall(params) {
      return new Promise(function(resolve, reject) {
        var uri;
        switch(process.env.NODE_ENV) {
          case "production":
            uri = "https://input.payapi.io/v1/api/authorized/payments";
            break;
          case "staging":
            uri = "https://staging-input.payapi.io/v1/api/authorized/payments";
            break;
          default:
            uri = "http://localhost:3000/v1/api/authorized/payments";
        }
        rp({
          method: "POST",
          uri: uri,
          body: {
            authenticationToken: params.authenticationToken,
            paymentToken: params.paymentToken
          },
          json: true
        }).then(function(params) {
          resolve(params);
        }).catch(function(err) {
          reject(err);
        });
      });
    }

    function handleError(err) {
      return new Promise(function(resolve, reject) {
        console.log("Payapi Client Error:" + JSON.stringify(err, null, 2));
        reject(err);
      });
    }

    function convertNumberToNumber(params) {
      return new Promise(function(resolve, reject) {
        for(var i = 0; i < params.payload.products.length; i++) {
          params.payload.products[i].quantity = Number(params.payload.products[i].quantity);
          params.payload.products[i].priceInCentsIncVat = Number(params.payload.products[i].priceInCentsIncVat);
          params.payload.products[i].priceInCentsExcVat = Number(params.payload.products[i].priceInCentsExcVat);
          params.payload.products[i].vatPercentage = Number(params.payload.products[i].vatPercentage);
        }
        params.payload.order.sumInCentsIncVat = Number(params.payload.order.sumInCentsIncVat);
        params.payload.order.sumInCentsExcVat = Number(params.payload.order.sumInCentsExcVat);
        resolve(params);
      });
    }

    return {
      call: function(callParams) {
        if(callParams) {
          return doRestCall(callParams).catch(handleError);
        } else {
          return validateApiKey(params)
            .then(validateSecret)
            .then(validatePayload)
            .then(convertNumberToNumber)
            .then(createToken)
            .then(doRestCall)
            .catch(handleError);
        }
      },
      paymentToken: function() {
        return validatePayload(params)
          .then(convertNumberToNumber)
          .then(createToken)
          .then(function(params) {
            return new Promise(function(resolve, reject) {
              resolve(params.paymentToken);
            });
          })
          .catch(function(err) {
            throw err;
          });
      },
      validate: function() {
        return validatePayload(params);
      },
      encodePaymentToken: function() {
        return toPaymentToken();
      },
      decodePaymentToken: function() {
        var paymentObject = toPaymentObject();
        paymentObject.optionalFields = params.optionalFields;
        return validatePayload(paymentObject)
          .then(convertNumberToNumber)
          .then(function(result) {
            return result;
          })
          .catch(function(err) {
            throw err;
          });
      },
    };
  };
}());
<|endoftext|>


<commit_before>"use strict"

const request = require("jsonrequest")

/**
 * ipInfo
 * Makes requests to the ipinfo.io resources.
 *
 * @name ipInfo
 * @function
 * @param {String} type An optional string parameter that can be:
 *
 *  - An ip (e.g. `"8.8.8.8"`)
 *  - An ip and the a field (e.g. `"8.8.8.8/org"`)
 *
 * @param {String} token The token used if you have to make an authorized request.
 * @param {Function} callback The callback function.
 */
const ipInfo = module.exports = function (type, token, callback) {
    let url = null

    if (typeof token === "function") {
        callback = token
        token = undefined
    }

    if (typeof type === "function") {
        callback = type
        type = ""
    }

    if(typeof token === "string") {
        if (!type) {
            url = ipInfo.HOSTNAME_SSL  + "json" + ipInfo.TOKEN_PREFIX + token
        } else if (ipInfo.IP_REGEX.test(type)) {
            url = ipInfo.HOSTNAME_SSL  + type + "/json" + ipInfo.TOKEN_PREFIX + token
        } else {
            url = ipInfo.HOSTNAME_SSL  + type + ipInfo.TOKEN_PREFIX + token
        }
    } else {
        if (!type) {
            url = ipInfo.HOSTNAME + "json"
        } else if (ipInfo.IP_REGEX.test(type)) {
            url = ipInfo.HOSTNAME + type + "/json"
        } else {
            url = ipInfo.HOSTNAME + type
        }
    }

    return new Promise((resolve, reject) => {
        request(url, (err, body) => {
            if (err && err.message && err.message.startsWith("Unexpected token")) {
                err = null
            }
            if (err) {
                if (!callback) {
                    callback = reject
                }
                return callback(err)
            } else {
                if (callback) {
                    callback(null, body)
                } else {
                    resolve(body)
                }
            }
        })
    })
}

ipInfo.HOSTNAME = "http://ipinfo.io/"
ipInfo.HOSTNAME_SSL = "https://ipinfo.io/"
ipInfo.TOKEN_PREFIX = "?token="
ipInfo.IP_REGEX = /^\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b$/
<commit_msg>minor<commit_after>"use strict"

const request = require("jsonrequest")

/**
 * ipInfo
 * Makes requests to the ipinfo.io resources.
 *
 * @name ipInfo
 * @function
 * @param {String} type An optional string parameter that can be:
 *
 *  - An ip (e.g. `"8.8.8.8"`)
 *  - An ip and the a field (e.g. `"8.8.8.8/org"`)
 *
 * @param {String} token The token used if you have to make an authorized request.
 * @param {Function} callback The callback function.
 */
const ipInfo = module.exports = function (type, token, callback) {
    let url = null

    if (typeof token === "function") {
        callback = token
        token = undefined
    }

    if (typeof type === "function") {
        callback = type
        type = ""
    }

    if(typeof token === "string") {
        if (!type) {
            url = ipInfo.HOSTNAME_SSL + "json" + ipInfo.TOKEN_PREFIX + token
        } else if (ipInfo.IP_REGEX.test(type)) {
            url = ipInfo.HOSTNAME_SSL + type + "/json" + ipInfo.TOKEN_PREFIX + token
        } else {
            url = ipInfo.HOSTNAME_SSL + type + ipInfo.TOKEN_PREFIX + token
        }
    } else {
        if (!type) {
            url = ipInfo.HOSTNAME + "json"
        } else if (ipInfo.IP_REGEX.test(type)) {
            url = ipInfo.HOSTNAME + type + "/json"
        } else {
            url = ipInfo.HOSTNAME + type
        }
    }

    return new Promise((resolve, reject) => {
        request(url, (err, body) => {
            if (err && err.message && err.message.startsWith("Unexpected token")) {
                err = null
            }
            if (err) {
                if (!callback) {
                    callback = reject
                }
                return callback(err)
            } else {
                if (callback) {
                    callback(null, body)
                } else {
                    resolve(body)
                }
            }
        })
    })
}

ipInfo.HOSTNAME = "http://ipinfo.io/"
ipInfo.HOSTNAME_SSL = "https://ipinfo.io/"
ipInfo.TOKEN_PREFIX = "?token="
ipInfo.IP_REGEX = /^\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b$/
<|endoftext|>