<commit_before>var request      = require('request'),
    cheerio      = require('cheerio'),
    _            = require('underscore'),
    dsv          = require('dsv');

/* Rate limiting */

// Rate limit ensures a function is never called more than every [rate]ms
// Unlike underscore's _.throttle function, function calls are queued so that
//   requests are never lost and simply deferred until some other time
//
// Parameters
// * func - function to rate limit
// * rate - mdinimum time to wait between function calls (ms)
// * async - if async is true, we won't wait (rate) for the function to complete before queueing the next request
//
// Example
// function showStatus(i) {
//   console.log(i);
// }
// var showStatusRateLimited = _.rateLimit(showStatus, 200);
// for (var i = 0; i < 10; i++) {
//   showStatusRateLimited(i);
// }
//
// Dependencies
// * underscore.js

_.rateLimit = function(func, rate, async) {
  var queue_rl = [];
  var currentlyEmptyingQueue = false;

  var emptyQueue = function() {
    if (queue_rl.length) {
      currentlyEmptyingQueue = true;
      _.delay(function() {
        if (async) {
          _.defer(function() { queue_rl.shift().call(); });
        } else {
          queue_rl.shift().call();
        }
        emptyQueue();
      }, rate);
    } else {
      currentlyEmptyingQueue = false;
    }
  };
  return function() {
    var args = _.values(arguments); // get arguments into an array
    queue_rl.push( _.bind.apply(this, [func, this].concat(args)) ); // call apply so that we can pass in arguments as parameters as opposed to an array
    if (!currentlyEmptyingQueue) emptyQueue();
  };
};

function scraper_fn(){
  var data_obj,
      url_string,
      urlAccessor,
      extractorAccessor,
      extractorFn,
      request_numb = 0,
      request_total,
      result_arr = [],
      rl_rate = 0,
      scrapeTargetFormat = 'webpage',
      resultFormat = 'json';

  function scraper(){ }

  scraper.data = function(value){
    if (!arguments.length) return data_obj;
    data_obj = value;
    request_total  = _.size(data_obj);
    return scraper;
  }

  scraper.url = function(value, accessor){
    if (!arguments.length) return url_string;
    url_string   = value;
    urlAccessor  = accessor;
    return scraper;
  }

  scraper.extractor = function(value){
    if (!arguments.length) return extractorFn;
    extractorFn        = value;
    return scraper;
  }

  scraper.rateLimit = function(rate){
    if (!arguments.length) return rl_rate;
    rl_rate = rate;
    return scraper;
  }

  scraper.resultFormat = function(value){
    if (!arguments.length) return resultFormat;
    resultFormat = value;
    return scraper;
  }

  scraper.scrapeTargetFormat = function(value){
    if (!arguments.length) return scrapeTargetFormat;
    scrapeTargetFormat = value;
    return scraper;
  }

  scraper.start = function(cb){
    var that = this,
        opts;

    scraper.processRated = _.rateLimit(scraper.process, rl_rate);

    cb = _.after(request_total, cb); // Only fire the callback after all the calls are done
    data_obj.forEach(function(datum){
      opts = urlAccessor(datum)
      that.processRated(url_string + opts, datum, cb);
    })
  }

  scraper.process = function(url, datum, cb, async){
    var that = this;
    request(url, function (err, response, body) {
      if (err) throw err;
      var progress = 'Fetching ' + (request_numb + 1) + '/' + request_total;

      if (scrapeTargetFormat == 'webpage') {
        body = cheerio.load(body);
      } else if (scrapeTargetFormat == 'json') {
        body = JSON.parse(body);
      }
      extractorFn(body, datum, function(result){
        result_arr.push(result);
        request_numb++;
        if (resultFormat == 'csv') { cb(dsv.csv.format(_.flatten(result_arr))); return false; }
        cb(result_arr);
      }, progress);
    });
  }

  return scraper;

}

// Create a rateLimited scraper
scraper_fn.rateLimit = function(rate){
  var fn = _.rateLimit(scraper_fn, rate);
  return fn
}

module.exports = scraper_fn;

<commit_msg>more accurate var names<commit_after>var request      = require('request'),
    cheerio      = require('cheerio'),
    _            = require('underscore'),
    dsv          = require('dsv');

/* Rate limiting */

// Rate limit ensures a function is never called more than every [rate]ms
// Unlike underscore's _.throttle function, function calls are queued so that
//   requests are never lost and simply deferred until some other time
//
// Parameters
// * func - function to rate limit
// * rate - mdinimum time to wait between function calls (ms)
// * async - if async is true, we won't wait (rate) for the function to complete before queueing the next request
//
// Example
// function showStatus(i) {
//   console.log(i);
// }
// var showStatusRateLimited = _.rateLimit(showStatus, 200);
// for (var i = 0; i < 10; i++) {
//   showStatusRateLimited(i);
// }
//
// Dependencies
// * underscore.js

_.rateLimit = function(func, rate, async) {
  var queue_rl = [];
  var currentlyEmptyingQueue = false;

  var emptyQueue = function() {
    if (queue_rl.length) {
      currentlyEmptyingQueue = true;
      _.delay(function() {
        if (async) {
          _.defer(function() { queue_rl.shift().call(); });
        } else {
          queue_rl.shift().call();
        }
        emptyQueue();
      }, rate);
    } else {
      currentlyEmptyingQueue = false;
    }
  };
  return function() {
    var args = _.values(arguments); // get arguments into an array
    queue_rl.push( _.bind.apply(this, [func, this].concat(args)) ); // call apply so that we can pass in arguments as parameters as opposed to an array
    if (!currentlyEmptyingQueue) emptyQueue();
  };
};

function scraper_fn(){
  var data_list,
      url_string,
      urlAccessor,
      extractorAccessor,
      extractorFn,
      request_numb = 0,
      request_total,
      result_arr = [],
      rl_rate = 0,
      scrapeTargetFormat = 'webpage',
      resultFormat = 'json';

  function scraper(){ }

  scraper.data = function(value){
    if (!arguments.length) return data_list;
    data_list = value;
    request_total  = data_list.length;
    return scraper;
  }

  scraper.url = function(value, accessor){
    if (!arguments.length) return url_string;
    url_string   = value;
    urlAccessor  = accessor;
    return scraper;
  }

  scraper.extractor = function(value){
    if (!arguments.length) return extractorFn;
    extractorFn        = value;
    return scraper;
  }

  scraper.rateLimit = function(rate){
    if (!arguments.length) return rl_rate;
    rl_rate = rate;
    return scraper;
  }

  scraper.resultFormat = function(value){
    if (!arguments.length) return resultFormat;
    resultFormat = value;
    return scraper;
  }

  scraper.scrapeTargetFormat = function(value){
    if (!arguments.length) return scrapeTargetFormat;
    scrapeTargetFormat = value;
    return scraper;
  }

  scraper.start = function(cb){
    var that = this,
        opts;

    scraper.processRated = _.rateLimit(scraper.process, rl_rate);

    cb = _.after(request_total, cb); // Only fire the callback after all the calls are done
    data_list.forEach(function(datum){
      opts = urlAccessor(datum);
      that.processRated(url_string + opts, datum, cb);
    })
  }

  scraper.process = function(url, datum, cb, async){
    var that = this;
    request(url, function (err, response, body) {
      if (err) throw err;
      var progress = 'Fetching ' + (request_numb + 1) + '/' + request_total;

      if (scrapeTargetFormat == 'webpage') {
        body = cheerio.load(body);
      } else if (scrapeTargetFormat == 'json') {
        body = JSON.parse(body);
      }
      extractorFn(body, datum, function(result){
        result_arr.push(result);
        request_numb++;
        if (resultFormat == 'csv') { cb(dsv.csv.format(_.flatten(result_arr))); return false; }
        cb(result_arr);
      }, progress);
    });
  }

  return scraper;

}

// Create a rateLimited scraper
scraper_fn.rateLimit = function(rate){
  var fn = _.rateLimit(scraper_fn, rate);
  return fn
}

module.exports = scraper_fn;

<|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).lean().exec(function(err, results) {
    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, cb) {

  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 cb(err, null)
      cb(err, results[0]);
    });
  });
};

var saveFP = function(opts, callback) {

  var model = this;

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

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);

      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 {};

    console.log(_.omit(result, _getCurrentFieldsExclude(currentFields)));

    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 10;

  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>remove console log<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).lean().exec(function(err, results) {
    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, cb) {

  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 cb(err, null)
      cb(err, results[0]);
    });
  });
};

var saveFP = function(opts, callback) {

  var model = this;

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

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);

      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 10;

  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>var redis = require('redis');

var internals = {};

internals.defaults = {
  global:{limit: -1 , bucketLength: 1},
  routes: {},
  redis:{}
};

exports.register = function(plugin, options, next) {
  var settings = plugin.hapi.utils.applyToDefaults(internals.defaults, options);
  var redisClient  = redis.createClient(options.redis.port, options.redis.host, options.redis.options);
  plugin.state('cl-hapi-rate',{});
  plugin.ext('onRequest', function(request, callback) {
    var ipts = request.info.remoteAddress+':'+Math.floor((new Date()).getTime()/1000)
    redisClient.get(ipts,function(err, token){
      if(token && token > 1) {
        return request.reply(429);
      }else{
        rediClient.multi([['INCR',ipts],['EXPIRE', 120]]).exec(function(err) {
          return callback();
        });
      }
    });
  });
  next();
};
<commit_msg>Again?<commit_after>var redis = require('redis');

var internals = {};

internals.defaults = {
  global:{limit: -1 , bucketLength: 1},
  routes: {},
  redis:{}
};

exports.register = function(plugin, options, next) {
  var settings = plugin.hapi.utils.applyToDefaults(internals.defaults, options);
  var redisClient  = redis.createClient(options.redis.port, options.redis.host, options.redis.options);
  plugin.state('cl-hapi-rate',{});
  plugin.ext('onRequest', function(request, callback) {
    var ipts = request.info.remoteAddress+':'+Math.floor((new Date()).getTime()/1000)
    redisClient.get(ipts,function(err, token){
      if(token && token > 1) {
        return request.reply(429);
      }else{
        redisClient.multi([['INCR',ipts],['EXPIRE', 120]]).exec(function(err) {
          return callback();
        });
      }
    });
  });
  next();
};
<|endoftext|>


<commit_before>"use strict";
const EventEmitter = require("events");
const https = require("https");
const querystring = require("querystring");
const aws4 = require("aws4");

class PollyTTS {
  constructor(credentials) {
    super();
    this.credentials = credentials;
  }

  textToSpeech(options, callback) {
    if(!options) {
      return callback(new Error('Options are missing'));
    }
    let qs = {
      Text: options.text,
      TextType: "text",
      VoiceId: options.voiceId || "Vicki",
      SampleRate: options.sampleRate || 22050,
      OutputFormat: options.outputFormat || "mp3"
    };
    let opts = {
      service: "polly",
      region: options.region || "eu-west-1",
      path: "/v1/speech?" + querystring.stringify(qs),
      signQuery: true
    };

    // you can also pass AWS credentials in explicitly (otherwise taken from process.env)
    aws4.sign(opts, this.credentials);
    https
      .get(opts, res => {
        if (res.statusCode !== 200) {
          return callback(
            new Error(`Request Failed. Status Code: ${res.statusCode}`)
          );
        }
        callback(null, res);
      })
      .on("error", e => {
        callback(e);
      });
  }
}

module.exports = PollyTTS;
<commit_msg>formated<commit_after>"use strict";
const EventEmitter = require("events");
const https = require("https");
const querystring = require("querystring");
const aws4 = require("aws4");

class PollyTTS {
  constructor(credentials) {
    super();
    this.credentials = credentials;
  }

  textToSpeech(options, callback) {
    if (!options) {
      return callback(new Error("Options are missing"));
    }
    let qs = {
      Text: options.text,
      TextType: "text",
      VoiceId: options.voiceId || "Vicki",
      SampleRate: options.sampleRate || 22050,
      OutputFormat: options.outputFormat || "mp3"
    };
    let opts = {
      service: "polly",
      region: options.region || "eu-west-1",
      path: "/v1/speech?" + querystring.stringify(qs),
      signQuery: true
    };

    // you can also pass AWS credentials in explicitly (otherwise taken from process.env)
    aws4.sign(opts, this.credentials);
    https
      .get(opts, res => {
        if (res.statusCode !== 200) {
          return callback(
            new Error(`Request Failed. Status Code: ${res.statusCode}`)
          );
        }
        callback(null, res);
      })
      .on("error", e => {
        callback(e);
      });
  }
}

module.exports = PollyTTS;
<|endoftext|>


<commit_before>var GoPro = require('./constant.js');
var when = require('when');
var request = require('request');
var dgram = require('dgram');

GoPro = GoPro || {};

GoPro.Camera = function (options) {

    'use strict';

    var options = options || {};

    this._ip = options.ip || '10.5.5.9';

    this._broadcastip = options.broadcastip || '10.5.5.255';

    this._status = {};

    this._mac = options.mac;

    if (!this._mac)
        retrieveMacAddress(this);
};

function retrieveMacAddress(cam) {

    'use strict';

    request('http://' + cam._ip + '/gp/gpControl', function (error, response, body) {
        if (!error && response.statusCode == 200) {
            try
            {
                var infos = JSON.parse(body);
                var mac = infos.info.ap_mac;
                for (var bytes = [], c = 0; c < mac.length; c += 2) {
                    bytes.push(parseInt(mac.substr(c, 2), 16));
                }
                cam._mac = bytes;
            } catch (e) {
            }
        }
    });
}

GoPro.Camera.prototype.status = function (id) {

    'use strict';

    var self = this;
    var promise = when.promise(function (resolve, reject, notify) {
        request('http://' + self._ip + '/gp/gpControl/status', function (error, response, body) {
            if (!error && response.statusCode == 200) {
                try
                {
                    self._status = JSON.parse(body);
                    if (id !== undefined) {
                        resolve(self._status.status[id]);
                        return;
                    }
                    resolve(self._status);
                } catch (e) {
                    reject(e);
                }
                return;
            }
            reject(error);
        });
    });
    return (promise);
};

GoPro.Camera.prototype.mode = function (mode, submode) {

    'use strict';

    var self = this;
    var promise = when.promise(function (resolve, reject, notify) {
        if (mode && submode) {
            request('http://' + self._ip + '/gp/gpControl/command/sub_mode?mode=' + mode + '&sub_mode=' + submode, function (error, response, body) {
                if (!error && response.statusCode == 200) {
                    resolve();
                    return;
                }
                reject(error);
            });
        } else {
            request('http://' + self._ip + '/gp/gpControl/command/mode?p=' + mode, function (error, response, body) {
                if (!error && response.statusCode == 200) {
                    resolve();
                    return;
                }
                reject(error);
            });
        }
    });
    return (promise);
};

GoPro.Camera.prototype.start = function () {

    'use strict';

    var self = this;
    var promise = when.promise(function (resolve, reject, notify) {
        request('http://' + self._ip + '/gp/gpControl/command/shutter?p=1', function (error, response, body) {
            if (!error && response.statusCode == 200) {
                resolve();
            }
            reject(error);
        });
    });
    return (promise);
};

GoPro.Camera.prototype.stop = function () {

    'use strict';

    var self = this;
    var promise = when.promise(function (resolve, reject, notify) {
        request('http://' + self._ip + '/gp/gpControl/command/shutter?p=0', function (error, response, body) {
            if (!error && response.statusCode == 200) {
                resolve();
            }
            reject(error);
        });
    });
    return (promise);
};

GoPro.Camera.prototype.set = function (setting_id, setting_value) {

    'use strict';

    if (setting_id === undefined && setting_value === undefined)
        return (undefined);

    var self = this;
    var promise = when.promise(function (resolve, reject, notify) {
        if (setting_id !== undefined && setting_value !== undefined) {
            request('http://' + self._ip + '/gp/gpControl/setting/' + setting_id + '/' + setting_value, function (error, response, body) {
                if (!error && response.statusCode == 200) {
                    resolve();
                    return;
                }
                reject(error);
            });
        } else {
            request('http://' + self._ip + '/gp/gpControl/status', function (error, response, body) {
                if (!error && response.statusCode == 200) {
                    try
                    {
                        self._status = JSON.parse(body);
                        resolve(self._status.settings[setting_id]);
                    } catch (e) {
                        reject(e);
                    }
                    return;
                }
                reject(error);
            });
        }
    });
    return (promise);
};

GoPro.Camera.prototype.powerOn = function () {

    'use strict';

    if (this._mac === undefined) {
        console.log('[failure] MAC address unknown');
        return;
    }

    var message = new Buffer(102);
    message[0] = 0xff;
    message[1] = 0xff;
    message[2] = 0xff;
    message[3] = 0xff;
    message[4] = 0xff;
    message[5] = 0xff;

    for (var j = 0; j < 16; ++j) {
        var c = 6 + j * this._mac.length;
        for (var i = 0; i < this._mac.length; ++i) {
            message[c + i] = this._mac[i];
        }
    }

    var client = dgram.createSocket("udp4");

    for (var i=0;i < 10; ++i) {
        client.send(message, 0, message.length, 9, "10.5.5.255");
    }
};

GoPro.Camera.prototype.powerOff = function () {

    'use strict';

    var self = this;
    var promise = when.promise(function (resolve, reject, notify) {
        request('http://' + self._ip + '/gp/gpControl/command/system/sleep', function (error, response, body) {
            if (!error && response.statusCode == 200) {
                resolve();
            }
            reject(error);
        });
    });
    return (promise);
};

GoPro.Camera.prototype.listMedia = function () {

    'use strict';

    var self = this;
    var promise = when.promise(function (resolve, reject, notify) {
        request('http://' + self._ip + '/gp/gpMediaList', function (error, response, body) {
            if (!error && response.statusCode == 200) {
                try {
                    resolve(JSON.parse(body));
                } catch (e) {
                    reject(e);
                }
            }
            reject(error);
        });
    });
    return (promise);
};

GoPro.Camera.prototype.videoInfo = function (video_path) {

    'use strict';

    var self = this;
    var promise = when.promise(function (resolve, reject, notify) {
        request('http://' + self._ip + '/gp/gpMediaMetadata?p=' + video_path + '&t=videoinfo', function (error, response, body) {
            if (!error && response.statusCode == 200) {
                try {
                    resolve(JSON.parse(body));
                } catch (e) {
                    reject(e);
                }
            }
            reject(error);
        });
    });
    return (promise);
};

module.exports = GoPro;<commit_msg>Fix, use of broadcast ip setting<commit_after>var GoPro = require('./constant.js');
var when = require('when');
var request = require('request');
var dgram = require('dgram');

GoPro = GoPro || {};

GoPro.Camera = function (options) {

    'use strict';

    var options = options || {};

    this._ip = options.ip || '10.5.5.9';

    this._broadcastip = options.broadcastip || '10.5.5.255';

    this._status = {};

    this._mac = options.mac;

    if (!this._mac)
        retrieveMacAddress(this);
};

function retrieveMacAddress(cam) {

    'use strict';

    request('http://' + cam._ip + '/gp/gpControl', function (error, response, body) {
        if (!error && response.statusCode == 200) {
            try
            {
                var infos = JSON.parse(body);
                var mac = infos.info.ap_mac;
                for (var bytes = [], c = 0; c < mac.length; c += 2) {
                    bytes.push(parseInt(mac.substr(c, 2), 16));
                }
                cam._mac = bytes;
            } catch (e) {
            }
        }
    });
}

GoPro.Camera.prototype.status = function (id) {

    'use strict';

    var self = this;
    var promise = when.promise(function (resolve, reject, notify) {
        request('http://' + self._ip + '/gp/gpControl/status', function (error, response, body) {
            if (!error && response.statusCode == 200) {
                try
                {
                    self._status = JSON.parse(body);
                    if (id !== undefined) {
                        resolve(self._status.status[id]);
                        return;
                    }
                    resolve(self._status);
                } catch (e) {
                    reject(e);
                }
                return;
            }
            reject(error);
        });
    });
    return (promise);
};

GoPro.Camera.prototype.mode = function (mode, submode) {

    'use strict';

    var self = this;
    var promise = when.promise(function (resolve, reject, notify) {
        if (mode && submode) {
            request('http://' + self._ip + '/gp/gpControl/command/sub_mode?mode=' + mode + '&sub_mode=' + submode, function (error, response, body) {
                if (!error && response.statusCode == 200) {
                    resolve();
                    return;
                }
                reject(error);
            });
        } else {
            request('http://' + self._ip + '/gp/gpControl/command/mode?p=' + mode, function (error, response, body) {
                if (!error && response.statusCode == 200) {
                    resolve();
                    return;
                }
                reject(error);
            });
        }
    });
    return (promise);
};

GoPro.Camera.prototype.start = function () {

    'use strict';

    var self = this;
    var promise = when.promise(function (resolve, reject, notify) {
        request('http://' + self._ip + '/gp/gpControl/command/shutter?p=1', function (error, response, body) {
            if (!error && response.statusCode == 200) {
                resolve();
            }
            reject(error);
        });
    });
    return (promise);
};

GoPro.Camera.prototype.stop = function () {

    'use strict';

    var self = this;
    var promise = when.promise(function (resolve, reject, notify) {
        request('http://' + self._ip + '/gp/gpControl/command/shutter?p=0', function (error, response, body) {
            if (!error && response.statusCode == 200) {
                resolve();
            }
            reject(error);
        });
    });
    return (promise);
};

GoPro.Camera.prototype.set = function (setting_id, setting_value) {

    'use strict';

    if (setting_id === undefined && setting_value === undefined)
        return (undefined);

    var self = this;
    var promise = when.promise(function (resolve, reject, notify) {
        if (setting_id !== undefined && setting_value !== undefined) {
            request('http://' + self._ip + '/gp/gpControl/setting/' + setting_id + '/' + setting_value, function (error, response, body) {
                if (!error && response.statusCode == 200) {
                    resolve();
                    return;
                }
                reject(error);
            });
        } else {
            request('http://' + self._ip + '/gp/gpControl/status', function (error, response, body) {
                if (!error && response.statusCode == 200) {
                    try
                    {
                        self._status = JSON.parse(body);
                        resolve(self._status.settings[setting_id]);
                    } catch (e) {
                        reject(e);
                    }
                    return;
                }
                reject(error);
            });
        }
    });
    return (promise);
};

GoPro.Camera.prototype.powerOn = function () {

    'use strict';

    if (this._mac === undefined) {
        console.log('[failure] MAC address unknown');
        return;
    }

    var message = new Buffer(102);
    message[0] = 0xff;
    message[1] = 0xff;
    message[2] = 0xff;
    message[3] = 0xff;
    message[4] = 0xff;
    message[5] = 0xff;

    for (var j = 0; j < 16; ++j) {
        var c = 6 + j * this._mac.length;
        for (var i = 0; i < this._mac.length; ++i) {
            message[c + i] = this._mac[i];
        }
    }

    var client = dgram.createSocket("udp4");

    for (var i=0;i < 10; ++i) {
        client.send(message, 0, message.length, 9, this._broadcastip);
    }
};

GoPro.Camera.prototype.powerOff = function () {

    'use strict';

    var self = this;
    var promise = when.promise(function (resolve, reject, notify) {
        request('http://' + self._ip + '/gp/gpControl/command/system/sleep', function (error, response, body) {
            if (!error && response.statusCode == 200) {
                resolve();
            }
            reject(error);
        });
    });
    return (promise);
};

GoPro.Camera.prototype.listMedia = function () {

    'use strict';

    var self = this;
    var promise = when.promise(function (resolve, reject, notify) {
        request('http://' + self._ip + '/gp/gpMediaList', function (error, response, body) {
            if (!error && response.statusCode == 200) {
                try {
                    resolve(JSON.parse(body));
                } catch (e) {
                    reject(e);
                }
            }
            reject(error);
        });
    });
    return (promise);
};

GoPro.Camera.prototype.videoInfo = function (video_path) {

    'use strict';

    var self = this;
    var promise = when.promise(function (resolve, reject, notify) {
        request('http://' + self._ip + '/gp/gpMediaMetadata?p=' + video_path + '&t=videoinfo', function (error, response, body) {
            if (!error && response.statusCode == 200) {
                try {
                    resolve(JSON.parse(body));
                } catch (e) {
                    reject(e);
                }
            }
            reject(error);
        });
    });
    return (promise);
};

module.exports = GoPro;
<|endoftext|>


<commit_before>var Boom      = require('boom'); // error handling https://github.com/hapijs/boom
var assert    = require('assert');
var JWT       = require('jsonwebtoken'); // https://github.com/docdis/learn-json-web-tokens
var extract   = require('./extract');    // extract token from Auth Header, URL or Coookie
var pkg       = require('../package.json');
var internals = {}; // Declare internals >> see: http://hapijs.com/styleguide

exports.register = function (server, options, next) {
  server.auth.scheme('jwt', internals.implementation);
  next();
};

exports.register.attributes = { // hapi requires attributes for a plugin.
  pkg: pkg                      // See: http://hapijs.com/tutorials/plugins
};

internals.isFunction = function (functionToCheck) {
 var getType = {};
 return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
};

internals.implementation = function (server, options) {
  assert(options, 'options are required for jwt auth scheme'); // pre-auth checks
  assert(options.validateFunc || options.verifyFunc,  'validateFunc OR verifyFunc function is required!')

  return {
    authenticate: function (request, reply) {

      var token = extract(request, options); // extract token from Header, Cookie or Query param

      if (!token) {
        return reply(Boom.unauthorized(null, 'Token'));
      }

      if (!extract.isValid(token)) { // quick check for validity of token format
        return reply(Boom.unauthorized('Invalid token format', 'Token'));
      } // verification is done later, but we want to avoid decoding if malformed
      request.auth.token = token; // keep encoded JWT available in the request lifecycle
      // otherwise use the same key (String) to validate all JWTs
      var decoded;
      try {
        decoded = JWT.decode(token); // decode is non-io and fast enough to not have to be async
      }
      catch(e) { // request should still FAIL if the token does not decode.
        return reply(Boom.unauthorized('Invalid token format', 'Token'));
      }

      if(options.key && typeof options.validateFunc === 'function') {
        // if the keyFunc is a function it allows dynamic key lookup see: https://github.com/dwyl/hapi-auth-jwt2/issues/16
        var keyFunc = (internals.isFunction(options.key)) ? options.key : function (decoded, callback) { callback(null, options.key); };

        keyFunc(decoded, function (err, key, extraInfo) {
          if (err) {
            return reply(Boom.wrap(err));
          }
          if (extraInfo) {
            request.plugins[pkg.name] = { extraInfo: extraInfo };
          }
          var verifyOptions = options.verifyOptions || {};
          JWT.verify(token, key, verifyOptions, function (err, decoded) {
            if (err && err.name === 'TokenExpiredError') {
              return reply(Boom.unauthorized('Token expired', 'Token'), null, { credentials: null });
            }
            else if (err) {
              return reply(Boom.unauthorized('Invalid token', 'Token'), null, { credentials: null });
            }
            else { // see: http://hapijs.com/tutorials/auth for validateFunc signature
              options.validateFunc(decoded, request, function (err, valid, credentials) { // bring your own checks
                if (err) {
                  return reply(Boom.wrap(err));
                }
                else if (!valid) {
                  return reply(Boom.unauthorized('Invalid credentials', 'Token'), null, { credentials: credentials || decoded });
                }
                else {
                  return reply.continue({ credentials: credentials || decoded, artifacts: token });
                }
              });
            }
          });
        }); // END keyFunc
      } // END check for (secret) key and validateFunc
      else { // see: https://github.com/dwyl/hapi-auth-jwt2/issues/130
        options.verifyFunc(decoded, request, function(err, valid, credentials) {
          if (err) {
            return reply(Boom.wrap(err));
          }
          else if (!valid) {
            return reply(Boom.unauthorized('Invalid credentials', 'Token'), null, { credentials: decoded });
          } else {
            return reply.continue({ credentials: credentials, artifacts: token });
          }
        });
      }
      // else { // warn the person that the plugin requires either a validateFunc or verifyFunc!
      //   return reply(Boom.notImplemented('please specify a hapi-auth-jwt2 validateFunc or verifyFunc.', 'Token'), null, { credentials: decoded });
      // }
    },
    response: function(request, reply) {
      if(options.responseFunc && typeof options.responseFunc === 'function') {
        options.responseFunc(request, reply, function(err) {
          if (err) {
            return reply(Boom.wrap(err));
          }
          else {
            reply.continue();
          }
        })
      }
      else {
        reply.continue();
      }
    }
  };
};
<commit_msg>Added completeToken option<commit_after>var Boom      = require('boom'); // error handling https://github.com/hapijs/boom
var assert    = require('assert');
var JWT       = require('jsonwebtoken'); // https://github.com/docdis/learn-json-web-tokens
var extract   = require('./extract');    // extract token from Auth Header, URL or Coookie
var pkg       = require('../package.json');
var internals = {}; // Declare internals >> see: http://hapijs.com/styleguide

exports.register = function (server, options, next) {
  server.auth.scheme('jwt', internals.implementation);
  next();
};

exports.register.attributes = { // hapi requires attributes for a plugin.
  pkg: pkg                      // See: http://hapijs.com/tutorials/plugins
};

internals.isFunction = function (functionToCheck) {
 var getType = {};
 return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
};

internals.implementation = function (server, options) {
  assert(options, 'options are required for jwt auth scheme'); // pre-auth checks
  assert(options.validateFunc || options.verifyFunc,  'validateFunc OR verifyFunc function is required!')

  return {
    authenticate: function (request, reply) {

      var token = extract(request, options); // extract token from Header, Cookie or Query param

      if (!token) {
        return reply(Boom.unauthorized(null, 'Token'));
      }

      if (!extract.isValid(token)) { // quick check for validity of token format
        return reply(Boom.unauthorized('Invalid token format', 'Token'));
      } // verification is done later, but we want to avoid decoding if malformed
      request.auth.token = token; // keep encoded JWT available in the request lifecycle
      // otherwise use the same key (String) to validate all JWTs
      var decoded;
      try {
		if (options.completeToken === true) {
			// We want the complete token in decoded.header and decoded.payload
			decoded = JWT.decode(token, { complete: true }); // decode is non-io and fast enough to not have to be async
;		} else {
			// We just want the token payload in decoded
			decoded = JWT.decode(token); // decode is non-io and fast enough to not have to be async
		}
      }
      catch(e) { // request should still FAIL if the token does not decode.
        return reply(Boom.unauthorized('Invalid token format', 'Token'));
      }

      if(options.key && typeof options.validateFunc === 'function') {
        // if the keyFunc is a function it allows dynamic key lookup see: https://github.com/dwyl/hapi-auth-jwt2/issues/16
        var keyFunc = (internals.isFunction(options.key)) ? options.key : function (decoded, callback) { callback(null, options.key); };

        keyFunc(decoded, function (err, key, extraInfo) {
          if (err) {
            return reply(Boom.wrap(err));
          }
          if (extraInfo) {
            request.plugins[pkg.name] = { extraInfo: extraInfo };
          }
          var verifyOptions = options.verifyOptions || {};
          JWT.verify(token, key, verifyOptions, function (err, decoded) {
            if (err && err.name === 'TokenExpiredError') {
              return reply(Boom.unauthorized('Token expired', 'Token'), null, { credentials: null });
            }
            else if (err) {
              return reply(Boom.unauthorized('Invalid token', 'Token'), null, { credentials: null });
            }
            else { // see: http://hapijs.com/tutorials/auth for validateFunc signature
              options.validateFunc(decoded, request, function (err, valid, credentials) { // bring your own checks
                if (err) {
                  return reply(Boom.wrap(err));
                }
                else if (!valid) {
                  return reply(Boom.unauthorized('Invalid credentials', 'Token'), null, { credentials: credentials || decoded });
                }
                else {
                  return reply.continue({ credentials: credentials || decoded, artifacts: token });
                }
              });
            }
          });
        }); // END keyFunc
      } // END check for (secret) key and validateFunc
      else { // see: https://github.com/dwyl/hapi-auth-jwt2/issues/130
        options.verifyFunc(decoded, request, function(err, valid, credentials) {
          if (err) {
            return reply(Boom.wrap(err));
          }
          else if (!valid) {
            return reply(Boom.unauthorized('Invalid credentials', 'Token'), null, { credentials: decoded });
          } else {
            return reply.continue({ credentials: credentials, artifacts: token });
          }
        });
      }
      // else { // warn the person that the plugin requires either a validateFunc or verifyFunc!
      //   return reply(Boom.notImplemented('please specify a hapi-auth-jwt2 validateFunc or verifyFunc.', 'Token'), null, { credentials: decoded });
      // }
    },
    response: function(request, reply) {
      if(options.responseFunc && typeof options.responseFunc === 'function') {
        options.responseFunc(request, reply, function(err) {
          if (err) {
            return reply(Boom.wrap(err));
          }
          else {
            reply.continue();
          }
        })
      }
      else {
        reply.continue();
      }
    }
  };
};
<|endoftext|>


<commit_before>'use strict';

var Q = require('q');

var STATES = {
  OPEN : 'OPEN',
  CLOSED: 'CLOSED',
  HALF: 'HALF-OPEN'
};

var CircuitBreaker = module.exports = function (func, options) {
  this.options = options || {};
  this._func = func;
  this.timeout = this.options.timeout || 10000; // default 10 second timeout (ms)
  this.resetTimeout = this.options.resetTimeout || 60000; // default 1 minute reset timeout (ms)
  this.maxFailures = this.options.maxFailures || 5; // default max number of failures to open circuit

  // Initially circuit is always closed
  this.forceClosed();
};

CircuitBreaker.prototype.invoke = function () {
  var self = this;

  if(self.isOpen()) {
    return self._fastFail();
  } else if (self.isHalfOpen() && self._halfOpenCallPending) {
    return self._fastFail();
  }
  else if(self.isHalfOpen() && !self._halfOpenCallPending) {
    self._halfOpenCallPending = true;
    return self._callFunction.apply(self, arguments).fin(function () {
      self._halfOpenCallPending = false;
    });
  } else {
    return self._callFunction.apply(self, arguments);
  }
};

CircuitBreaker.prototype._fastFail = function () {
  return Q.fcall(function () {
    throw new Error('Circuit Breaker open');
  });
};

CircuitBreaker.prototype._callFunction = function () {
  var self = this;

  var deferred = Q.defer();

  var args = [];
  for(var i = 0; i < arguments.length; i++) {
    args.push(arguments[i]);
  }

  args.push(deferred.makeNodeResolver());

  setTimeout(deferred.reject, self.timeout);

  var promise = deferred.promise;

  promise.then(function () {
    self.handleSuccess();
  }, function(err) {
    self.handleFailure(err);
  });

  self._func.apply(self._func, args);

  return promise;
};

CircuitBreaker.prototype.handleSuccess = function () {
  this.forceClosed();
};

CircuitBreaker.prototype.handleFailure = function () {
  ++this._numFailures;

  if(this.isHalfOpen() || this._numFailures >= this.maxFailures) {
    this.forceOpen();
  }
};

CircuitBreaker.prototype.isOpen = function () {
  return this.state === STATES.OPEN;
};

CircuitBreaker.prototype.isHalfOpen = function () {
  return this.state === STATES.HALF;
};

CircuitBreaker.prototype.isClosed = function () {
  return this.state === STATES.CLOSED;
};

CircuitBreaker.prototype.forceOpen = function () {
  var self = this;

  if(this.state === STATES.OPEN) {
    return;
  }

  this.state = STATES.OPEN;

  // After reset timeout circuit should enter half open state
  setTimeout(function () {
    self.state = STATES.HALF;
  }, self.resetTimeout);

};

CircuitBreaker.prototype.forceClosed = function () {
  this.state = STATES.CLOSED;
  this._numFailures = 0;
};

CircuitBreaker.prototype.forceHalfOpen = function () {
  this.state = STATES.HALF;
};
<commit_msg>refactoring invoke logic<commit_after>'use strict';

var Q = require('q');

var STATES = {
  OPEN : 'OPEN',
  CLOSED: 'CLOSED',
  HALF: 'HALF-OPEN'
};

var CircuitBreaker = module.exports = function (func, options) {
  this.options = options || {};
  this._func = func;
  this.timeout = this.options.timeout || 10000; // default 10 second timeout (ms)
  this.resetTimeout = this.options.resetTimeout || 60000; // default 1 minute reset timeout (ms)
  this.maxFailures = this.options.maxFailures || 5; // default max number of failures to open circuit

  // Initially circuit is always closed
  this.forceClosed();
};

CircuitBreaker.prototype.invoke = function () {
  var self = this;

  if(self.isOpen() || (self.isHalfOpen() && self._halfOpenCallPending)) {
    return self._fastFail();
  } else if(self.isHalfOpen() && !self._halfOpenCallPending) {
    self._halfOpenCallPending = true;
    return self._callFunction.apply(self, arguments).fin(function () {
      self._halfOpenCallPending = false;
    });
  } else {
    return self._callFunction.apply(self, arguments);
  }
};

CircuitBreaker.prototype._fastFail = function () {
  return Q.fcall(function () {
    throw new Error('Circuit Breaker open');
  });
};

CircuitBreaker.prototype._callFunction = function () {
  var self = this;

  var deferred = Q.defer();

  var args = [];
  for(var i = 0; i < arguments.length; i++) {
    args.push(arguments[i]);
  }

  args.push(deferred.makeNodeResolver());

  setTimeout(deferred.reject, self.timeout);

  var promise = deferred.promise;

  promise.then(function () {
    self.handleSuccess();
  }, function(err) {
    self.handleFailure(err);
  });

  self._func.apply(self._func, args);

  return promise;
};

CircuitBreaker.prototype.handleSuccess = function () {
  this.forceClosed();
};

CircuitBreaker.prototype.handleFailure = function () {
  ++this._numFailures;

  if(this.isHalfOpen() || this._numFailures >= this.maxFailures) {
    this.forceOpen();
  }
};

CircuitBreaker.prototype.isOpen = function () {
  return this.state === STATES.OPEN;
};

CircuitBreaker.prototype.isHalfOpen = function () {
  return this.state === STATES.HALF;
};

CircuitBreaker.prototype.isClosed = function () {
  return this.state === STATES.CLOSED;
};

CircuitBreaker.prototype.forceOpen = function () {
  var self = this;

  if(this.state === STATES.OPEN) {
    return;
  }

  this.state = STATES.OPEN;

  // After reset timeout circuit should enter half open state
  setTimeout(function () {
    self.state = STATES.HALF;
  }, self.resetTimeout);

};

CircuitBreaker.prototype.forceClosed = function () {
  this.state = STATES.CLOSED;
  this._numFailures = 0;
};

CircuitBreaker.prototype.forceHalfOpen = function () {
  this.state = STATES.HALF;
};
<|endoftext|>


<commit_before>#! /usr/bin/env node

var SE = require('stackexchange');
var prompt = require('prompt');
var htmlToText = require('html-to-text');

var context = new SE({
  clientId: 3502,
  version: 2.2,
  site: process.argv[3] || 'stackoverflow',
});

function showAnswers(question, originalAnswers) {
  console.log('Fetching answers');
  context.questions.answers({
    key: 'zUBS9XBccJH*ZodpviQbgw((',
    sort: 'votes',
    order: 'desc',
    filter: '!)skMacgeg0ntWAi.VWv5'
  }, function(err, answers){
    console.log('ANSWERS    ('+ answers.items.length +')\n-------------------------------------------------------------');
    answers.items.forEach(function(answer, index){

      console.log('\n\x1B[1mANSWER '+index + '          score: ' + answer.score + '\x1B[22m');
      console.log('\n'+htmlToText.fromString(answer.body));
      console.log('\n-- '+answer.owner.display_name);

    });

  }, [question.question_id]);
}

function answerSelection(question, originalAnswers) {
  var text = 'Would you like to see the answers to this question? (acceptable responses: y, yes, n, no, back, quit)';
  prompt.get(text, function(err, result) {
    if (err) throw err;

    answer = result[text];
    if (answer === 'yes' || answer === 'y') {

      showAnswers(question, originalAnswers);

    } else if (answer ==='no' || answer === 'n') {

      prompt.get('Would you like to go back to the list?', function(err, results){
        if (results['Would you like to go back to the list?'] === 'yes' || results['Would you like to go back to the list?'] === 'y' ){
          questionSelection(originalAnswers);
        } else {
          console.log("Goodbye");
        }
      });

    } else if (answer === 'back') {
      questionSelection(originalAnswers);
    } else {
      console.log('Quitting');
    }
  });
}

function showBody (question, originalAnswers) {
  context.questions.questions({
    key: 'zUBS9XBccJH*ZodpviQbgw((',
    sort: 'activity',
    order: 'asc',
    filter: '!bA1coBfqSKVk-5'
  }, function(err, result){
    if (err) throw err;
    console.log('\x1B[1m\x1B[4m'+htmlToText.fromString(question.title)+'\x1B[24m\x1B[22m');
    console.log('\n'+htmlToText.fromString(result.items[0].body));
    console.log('\n-- '+question.owner.display_name);

    answerSelection(question, originalAnswers);

  }, [question.question_id]);
}

function questionSelection(answers) {

      prompt.start();

      answers.items.forEach(function(item, index){
        console.log('\n['+index+']: ' + htmlToText.fromString(item.title));
      });

      var message = 'Which post would you like to see?';

      prompt.get(message, function(err, result) {
        var question = answers.items[result[message]];
        if (err) throw err;

        showBody(question, answers);

      });

}

function stack() {
  var criteria = {

    key: 'zUBS9XBccJH*ZodpviQbgw((',
    pagesize: 10,
    sort: 'relevance',
    order: 'asc',
    q: process.argv[2],

  };
  context.search.advanced(criteria, function(err, answers){
    if (err) throw err;
    console.log(answers.quota_remaining);
    if (answers.items.length === 0) {

      console.log('\x1B[31mYour search returned no matches\x1B[39m');

    } else {

      questionSelection(answers);

    }
  });
}

stack();<commit_msg>pruning api call to only required fields<commit_after>#! /usr/bin/env node

var SE = require('stackexchange');
var prompt = require('prompt');
var htmlToText = require('html-to-text');

var context = new SE({
  clientId: 3502,
  version: 2.2,
  site: process.argv[3] || 'stackoverflow',
});

function showAnswers(question, originalAnswers) {
  console.log('Fetching answers');
  context.questions.answers({
    key: 'zUBS9XBccJH*ZodpviQbgw((',
    sort: 'votes',
    order: 'desc',
    filter: '!)Q2ANGPRb(JF95187*15)KtD'
  }, function(err, results){
    console.log('ANSWERS    ('+ results.items.length +')\n-------------------------------------------------------------');
    results.items.forEach(function(answer, index){

      console.log('\n\x1B[1mANSWER '+(index+1) + '          score: ' + answer.score + '\x1B[22m');
      console.log('\n'+htmlToText.fromString(answer.body));
      console.log('\n-- '+answer.owner.display_name);

    });

  }, [question.question_id]);
}

function answerSelection(question, originalAnswers) {
  var text = 'Would you like to see the answers to this question? (acceptable responses: y, yes, n, no, back, quit)';
  prompt.get(text, function(err, result) {
    if (err) throw err;

    answer = result[text];
    if (answer === 'yes' || answer === 'y') {

      showAnswers(question, originalAnswers);

    } else if (answer ==='no' || answer === 'n') {

      prompt.get('Would you like to go back to the list?', function(err, results){
        if (results['Would you like to go back to the list?'] === 'yes' || results['Would you like to go back to the list?'] === 'y' ){
          questionSelection(originalAnswers);
        } else {
          console.log("Goodbye");
        }
      });

    } else if (answer === 'back') {
      questionSelection(originalAnswers);
    } else {
      console.log('Quitting');
    }
  });
}

function showBody (question, originalAnswers) {
  context.questions.questions({
    key: 'zUBS9XBccJH*ZodpviQbgw((',
    sort: 'activity',
    order: 'asc',
    filter: '!BHMsVcaG7J3bAGdyhbvtS8X)M(dQoa'
  }, function(err, result){
    if (err) throw err;
    console.log('\x1B[1m\x1B[4m'+htmlToText.fromString(question.title)+'\x1B[24m\x1B[22m');
    console.log('\n'+htmlToText.fromString(result.items[0].body));
    console.log('\n-- '+question.owner.display_name);

    answerSelection(question, originalAnswers);

  }, [question.question_id]);
}

function questionSelection(answers) {

      prompt.start();

      answers.items.forEach(function(item, index){
        console.log('\n['+index+']: ' + htmlToText.fromString(item.title));
      });

      var message = 'Which post would you like to see?';

      prompt.get(message, function(err, result) {
        var question = answers.items[result[message]];
        if (err) throw err;

        showBody(question, answers);

      });

}

function stack() {
  var criteria = {

    key: 'zUBS9XBccJH*ZodpviQbgw((',
    pagesize: 10,
    sort: 'relevance',
    order: 'asc',
    q: process.argv[2],
    filter: '!5-e4GbzLCOHI37**URcMxPLbYq5McHnPC-I4s8'
  };
  context.search.advanced(criteria, function(err, answers){
    if (err) throw err;

    if (answers.items.length === 0) {

      console.log('\x1B[31mYour search returned no matches\x1B[39m');

    } else {

      questionSelection(answers);

    }
  });
}

stack();<|endoftext|>


<commit_before>'use strict';

/**
* NOTE: the original C code, the long comment, copyright, license, and the constants are from [netlib]{http://www.netlib.org/fdlibm/s_log1p.c}.
*
* The implementation follows the original, but has been modified for JavaScript.
*/

/**
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunSoft, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/

/**
* double log1p(double x)
*
*  Method :
*    1. Argument Reduction: find k and f such that
*
*        1+x = 2^k * (1+f)
*
*      where  sqrt(2)/2 < 1+f < sqrt(2).
*
*      Note: if k=0, then f=x is exact. However, if k!=0, then f may not be representable exactly. In that case, a correction term is needed. Let u=1+x rounded. Let c = (1+x)-u, then
*
*        log(1+x) - log(u) ~ c/u
*
*      Thus, we proceed to compute log(u), and add back the correction term c/u.
*
*      Note: when x > 2**53, one can simply return log(x).
*
*   2. Approximation of log1p(f).
*     Let s = f/(2+f); based on
*
*        log(1+f) = log(1+s) - log(1-s)
*                 = 2s + 2/3 s**3 + 2/5 s**5 + ...
*                 = 2s + s*R
*
*     We use a special Reme algorithm on [0,0.1716] to generate a polynomial of degree 14 to approximate R The maximum error of this polynomial approximation is bounded by 2**-58.45. In other words,
*
*                 2      4      6      8      10      12      14
*        R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s  +Lp6*s  +Lp7*s
*
*     (Note: the values of Lp1 to Lp7 are listed in the program.)
*
*     and
*
*        |      2          14          |     -58.45
*        | Lp1*s +...+Lp7*s    -  R(z) | <= 2
*        |                             |
*
*     Note that
*
*        2s = f - s*f
*           = f - hfsq + s*hfsq
*
*     where hfsq = f*f/2.
*
*     In order to guarantee error in log below 1 ulp, we compute log by
*
*        log1p(f) = f - (hfsq - s*(hfsq+R)).
*
*	3. Finally,
*
*        log1p(x) = k*ln2 + log1p(f).
*                 = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo)))
*
*     Here ln2 is split into two floating point number:
*
*        ln2_hi + ln2_lo
*
*     where n*ln2_hi is always exact for |n| < 2000.
*
*   Special cases:
*     log1p(x) is NaN with signal if x < -1 (including -INF)
*     log1p(+INF) is +INF
*     log1p(-1) is -INF with signal
*     log1p(NaN) is NaN with no signal
*
*   Accuracy:
*     according to an error analysis, the error is always less than 1 ulp (unit in the last place).
*
*   Constants:
*     The hexadecimal values are the intended ones for the following constants. The decimal values may be used, provided that the compiler will convert from decimal to binary accurately enough to produce the hexadecimal values shown.
*
*   Note:
*     Assuming log() returns accurate answer, the following algorithm can be used to compute log1p(x) to within a few ULP:
*
*        u = 1+x;
*        if(u==1.0) return x;
*        else       return log(u)*(x/(u-1.0));
*
*     See HP-15C Advanced Functions Handbook, p.193.
*/

// MODULES //

var highWord = require( 'math-float64-get-high-word' );
var setHighWord = require( 'math-float64-set-high-word' );

// CONSTANTS //

var PINF = require( 'const-pinf-float64' );
var NINF = require( 'const-ninf-float64' );

var BIAS = 1023;

// High and low words of ln(2):
var LN2_HI = 6.93147180369123816490e-01; // 0x3fe62e42 0xfee00000
var LN2_LO = 1.90821492927058770002e-10; // 0x3dea39ef 0x35793c76

// sqrt(2)-1:
var SQRT2M1 = 4.142135623730950488017e-01;  // 0x3fda8279 0x99fcef34

// sqrt(2)/2-1:
var SQRT2HALFM1 = -2.928932188134524755992e-01; // 0xbfd2bec3 0x33018866

// 2**-29:
var SMALL = 1.862645149230957e-09; // 0x3e200000 0x00000000

// 2**-54:
var TINY = 5.551115123125783e-17;

// Max unsafe integer => 2**53:
var TWO53 = 9007199254740992;

// Polynomial coefficients:
var Lp1 = 6.666666666666735130e-01; // 0x3FE55555 0x55555593
var Lp2 = 3.999999999940941908e-01; // 0x3FD99999 0x9997FA04
var Lp3 = 2.857142874366239149e-01; // 0x3FD24924 0x94229359
var Lp4 = 2.222219843214978396e-01; // 0x3FCC71C5 0x1D8E78AF
var Lp5 = 1.818357216161805012e-01; // 0x3FC74664 0x96CB03DE
var Lp6 = 1.531383769920937332e-01; // 0x3FC39A09 0xD078C69F
var Lp7 = 1.479819860511658591e-01; // 0x3FC2F112 0xDF3E5244

// LOG1P //

/**
* FUNCTION: log1p( x )
*	Computes the natural logarithm of `1+x`.
*
* @param {Number} x - input value
* @returns {Number} function value
*/
function log1p( x ) {
	var hfsq;
	var hu;
	var y;
	var f;
	var c;
	var s;
	var z;
	var R;
	var u;
	var k;

	if ( x < -1 || x !== x ) {
		return NaN;
	}
	if ( x === -1 ) {
		return NINF;
	}
	if ( x === PINF ) {
		return PINF;
	}
	if ( x === 0 ) {
		return 0;
	}
	// Set y = |x|:
	if ( x < 0 ) {
		y = -x;
	} else {
		y = x;
	}
	// Argument reduction...
	k = 1;
	if ( y < SQRT2M1 ) { // if |x| < sqrt(2)-1 => ~0.41422
		if ( y < SMALL ) { // if |x| < 2**-29
			if( y < TINY ) { // if |x| < 2**-54
				return x;
			}
			return x - x*x*0.5;
		}
		if ( x > SQRT2HALFM1 ) { // if x > sqrt(2)/2-1 => ~-0.2929
			// => -0.2929 < x < 0.41422
			k = 0;
			f = x; // exact
			hu = 1;
		}
	}
	// Address case where `f` cannot be represented exactly...
	if ( k !== 0 ) {
		if ( y < TWO53 ) {
			u = 1.0 + x;
			hu = highWord( u );

			// Bit shift to isolate the exponent and then subtract the bias:
			k = (hu>>20) - BIAS;

			// Correction term...
			if ( k > 0 ) { // positive unbiased exponent
				c = 1.0 - (u-x);
			} else { // nonpositive unbiased exponent
				c = x - (u-1.0);
			}
			c /= u;
		} else {
			u = x;
			hu = highWord( u );

			// Bit shift to isolate the exponent and then subtract the bias:
			k = (hu>>20) - BIAS;

			// Correction term is zero:
			c = 0;
		}
		// Apply a bit mask (0 00000000000 11111111111111111111) to remove the exponent:
		hu &= 0x000fffff; // max value => 1048575

		// Check if u significand is less than sqrt(2) significand => 0x6a09e => 01101010000010011110
		if ( hu < 434334 ) {
			// Normalize u by setting the exponent to 1023 (bias) => 0x3ff00000 => 0 01111111111 00000000000000000000
			u = setHighWord( u, hu|0x3ff00000 );
		} else {
			k += 1;

			// Normalize u/2 by setting the exponent to 1022 (bias-1 => 2**-1 = 1/2) => 0x3fe00000 => 0 01111111110 00000000000000000000
			u = setHighWord( u, hu|0x3fe00000 );

			// Subtract hu significand from next largest hu => 0 00000000001 00000000000000000000 => 0x00100000 => 1048576
			hu = (1048576-hu)>>2;
		}
		f = u - 1.0;
	}
	// Approximation of log1p(f)...
	hfsq = 0.5 * f * f;
	if( hu === 0 ) { // if |f| < 2**-20
		if ( f === 0 ) {
			if ( k === 0 ) {
				return 0;
			}
			c += k * LN2_LO;
			return k * LN2_HI + c;
		}
		R = hfsq * (1.0 - 0.66666666666666666*f); // avoid division
		if ( k === 0 ) {
			return f - R;
		}
		return k*LN2_HI - ( (R - (k*LN2_LO + c)) - f );
	}
	s = f / (2.0 + f);
	z = s * s;

	// TODO: use math-polyval
	R = z * (Lp1+z*(Lp2+z*(Lp3+z*(Lp4+z*(Lp5+z*(Lp6+z*Lp7))))));

	if ( k === 0 ) {
		return f - ( hfsq - s*(hfsq+R) );
	}
	return k*LN2_HI - ( (hfsq - (s*(hfsq+R) + (k*LN2_LO + c))) - f );
} // end FUNCTION log1p()

// EXPORTS //

module.exports = log1p;
<commit_msg>Add code comment<commit_after>'use strict';

/**
* NOTE: the original C code, the long comment, copyright, license, and the constants are from [netlib]{http://www.netlib.org/fdlibm/s_log1p.c}.
*
* The implementation follows the original, but has been modified for JavaScript.
*/

/**
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunSoft, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/

/**
* double log1p(double x)
*
*  Method :
*    1. Argument Reduction: find k and f such that
*
*        1+x = 2^k * (1+f)
*
*      where  sqrt(2)/2 < 1+f < sqrt(2).
*
*      Note: if k=0, then f=x is exact. However, if k!=0, then f may not be representable exactly. In that case, a correction term is needed. Let u=1+x rounded. Let c = (1+x)-u, then
*
*        log(1+x) - log(u) ~ c/u
*
*      Thus, we proceed to compute log(u), and add back the correction term c/u.
*
*      Note: when x > 2**53, one can simply return log(x).
*
*   2. Approximation of log1p(f).
*     Let s = f/(2+f); based on
*
*        log(1+f) = log(1+s) - log(1-s)
*                 = 2s + 2/3 s**3 + 2/5 s**5 + ...
*                 = 2s + s*R
*
*     We use a special Reme algorithm on [0,0.1716] to generate a polynomial of degree 14 to approximate R The maximum error of this polynomial approximation is bounded by 2**-58.45. In other words,
*
*                 2      4      6      8      10      12      14
*        R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s  +Lp6*s  +Lp7*s
*
*     (Note: the values of Lp1 to Lp7 are listed in the program.)
*
*     and
*
*        |      2          14          |     -58.45
*        | Lp1*s +...+Lp7*s    -  R(z) | <= 2
*        |                             |
*
*     Note that
*
*        2s = f - s*f
*           = f - hfsq + s*hfsq
*
*     where hfsq = f*f/2.
*
*     In order to guarantee error in log below 1 ulp, we compute log by
*
*        log1p(f) = f - (hfsq - s*(hfsq+R)).
*
*	3. Finally,
*
*        log1p(x) = k*ln2 + log1p(f).
*                 = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo)))
*
*     Here ln2 is split into two floating point number:
*
*        ln2_hi + ln2_lo
*
*     where n*ln2_hi is always exact for |n| < 2000.
*
*   Special cases:
*     log1p(x) is NaN with signal if x < -1 (including -INF)
*     log1p(+INF) is +INF
*     log1p(-1) is -INF with signal
*     log1p(NaN) is NaN with no signal
*
*   Accuracy:
*     according to an error analysis, the error is always less than 1 ulp (unit in the last place).
*
*   Constants:
*     The hexadecimal values are the intended ones for the following constants. The decimal values may be used, provided that the compiler will convert from decimal to binary accurately enough to produce the hexadecimal values shown.
*
*   Note:
*     Assuming log() returns accurate answer, the following algorithm can be used to compute log1p(x) to within a few ULP:
*
*        u = 1+x;
*        if(u==1.0) return x;
*        else       return log(u)*(x/(u-1.0));
*
*     See HP-15C Advanced Functions Handbook, p.193.
*/

// MODULES //

var highWord = require( 'math-float64-get-high-word' );
var setHighWord = require( 'math-float64-set-high-word' );

// CONSTANTS //

var PINF = require( 'const-pinf-float64' );
var NINF = require( 'const-ninf-float64' );

var BIAS = 1023;

// High and low words of ln(2):
var LN2_HI = 6.93147180369123816490e-01; // 0x3fe62e42 0xfee00000
var LN2_LO = 1.90821492927058770002e-10; // 0x3dea39ef 0x35793c76

// sqrt(2)-1:
var SQRT2M1 = 4.142135623730950488017e-01;  // 0x3fda8279 0x99fcef34

// sqrt(2)/2-1:
var SQRT2HALFM1 = -2.928932188134524755992e-01; // 0xbfd2bec3 0x33018866

// 2**-29:
var SMALL = 1.862645149230957e-09; // 0x3e200000 0x00000000

// 2**-54:
var TINY = 5.551115123125783e-17;

// Max unsafe integer => 2**53:
var TWO53 = 9007199254740992;

// Polynomial coefficients:
var Lp1 = 6.666666666666735130e-01; // 0x3FE55555 0x55555593
var Lp2 = 3.999999999940941908e-01; // 0x3FD99999 0x9997FA04
var Lp3 = 2.857142874366239149e-01; // 0x3FD24924 0x94229359
var Lp4 = 2.222219843214978396e-01; // 0x3FCC71C5 0x1D8E78AF
var Lp5 = 1.818357216161805012e-01; // 0x3FC74664 0x96CB03DE
var Lp6 = 1.531383769920937332e-01; // 0x3FC39A09 0xD078C69F
var Lp7 = 1.479819860511658591e-01; // 0x3FC2F112 0xDF3E5244

// LOG1P //

/**
* FUNCTION: log1p( x )
*	Computes the natural logarithm of `1+x`.
*
* @param {Number} x - input value
* @returns {Number} function value
*/
function log1p( x ) {
	var hfsq;
	var hu;
	var y;
	var f;
	var c;
	var s;
	var z;
	var R;
	var u;
	var k;

	if ( x < -1 || x !== x ) {
		return NaN;
	}
	if ( x === -1 ) {
		return NINF;
	}
	if ( x === PINF ) {
		return PINF;
	}
	if ( x === 0 ) {
		return 0;
	}
	// Set y = |x|:
	if ( x < 0 ) {
		y = -x;
	} else {
		y = x;
	}
	// Argument reduction...
	k = 1;
	if ( y < SQRT2M1 ) { // if |x| < sqrt(2)-1 => ~0.41422
		if ( y < SMALL ) { // if |x| < 2**-29
			if( y < TINY ) { // if |x| < 2**-54
				return x;
			}
			// Use a simple two-term Taylor series...
			return x - x*x*0.5;
		}
		if ( x > SQRT2HALFM1 ) { // if x > sqrt(2)/2-1 => ~-0.2929
			// => -0.2929 < x < 0.41422
			k = 0;
			f = x; // exact
			hu = 1;
		}
	}
	// Address case where `f` cannot be represented exactly...
	if ( k !== 0 ) {
		if ( y < TWO53 ) {
			u = 1.0 + x;
			hu = highWord( u );

			// Bit shift to isolate the exponent and then subtract the bias:
			k = (hu>>20) - BIAS;

			// Correction term...
			if ( k > 0 ) { // positive unbiased exponent
				c = 1.0 - (u-x);
			} else { // nonpositive unbiased exponent
				c = x - (u-1.0);
			}
			c /= u;
		} else {
			u = x;
			hu = highWord( u );

			// Bit shift to isolate the exponent and then subtract the bias:
			k = (hu>>20) - BIAS;

			// Correction term is zero:
			c = 0;
		}
		// Apply a bit mask (0 00000000000 11111111111111111111) to remove the exponent:
		hu &= 0x000fffff; // max value => 1048575

		// Check if u significand is less than sqrt(2) significand => 0x6a09e => 01101010000010011110
		if ( hu < 434334 ) {
			// Normalize u by setting the exponent to 1023 (bias) => 0x3ff00000 => 0 01111111111 00000000000000000000
			u = setHighWord( u, hu|0x3ff00000 );
		} else {
			k += 1;

			// Normalize u/2 by setting the exponent to 1022 (bias-1 => 2**-1 = 1/2) => 0x3fe00000 => 0 01111111110 00000000000000000000
			u = setHighWord( u, hu|0x3fe00000 );

			// Subtract hu significand from next largest hu => 0 00000000001 00000000000000000000 => 0x00100000 => 1048576
			hu = (1048576-hu)>>2;
		}
		f = u - 1.0;
	}
	// Approximation of log1p(f)...
	hfsq = 0.5 * f * f;
	if( hu === 0 ) { // if |f| < 2**-20
		if ( f === 0 ) {
			if ( k === 0 ) {
				return 0;
			}
			c += k * LN2_LO;
			return k * LN2_HI + c;
		}
		R = hfsq * (1.0 - 0.66666666666666666*f); // avoid division
		if ( k === 0 ) {
			return f - R;
		}
		return k*LN2_HI - ( (R - (k*LN2_LO + c)) - f );
	}
	s = f / (2.0 + f);
	z = s * s;

	// TODO: use math-polyval
	R = z * (Lp1+z*(Lp2+z*(Lp3+z*(Lp4+z*(Lp5+z*(Lp6+z*Lp7))))));

	if ( k === 0 ) {
		return f - ( hfsq - s*(hfsq+R) );
	}
	return k*LN2_HI - ( (hfsq - (s*(hfsq+R) + (k*LN2_LO + c))) - f );
} // end FUNCTION log1p()

// EXPORTS //

module.exports = log1p;
<|endoftext|>