<commit_before>const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const session = require('express-session');
const passport = require('passport');
const FitbitStrategy = require( 'passport-fitbit-oauth2' ).FitbitOAuth2Strategy;
const app = express();
require('dotenv').config()

app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({
  secret: process.env.PASSPORT_SECRET,
  resave: false,
  saveUninitialized: true
 }));

app.use(passport.initialize());
app.use(passport.session());

// Serve static files from the React app
app.use(express.static(path.join(__dirname, 'client/build')));

//passport-fitbit-oauth2 routing
passport.use(new FitbitStrategy({
    clientID: process.env.FITBIT_OAUTH2_CLIENT_ID,
    clientSecret: process.env.FITBIT_OAUTH2_CLIENT_SECRET,
    callbackURL: "http://localhost:3000/auth/fitbit/callback"
  },
  function(accessToken, refreshToken, profile, done) {
    User.findOrCreate({ fitbitId: profile.id }, function (err, user) {
      return done(err, user);
    });
  }
));

passport.serializeUser(function(user, done) {
  done(null, user);
});

passport.deserializeUser(function(obj, done) {
  done(null, obj);
});

app.get('/auth/fitbit/success', function(req, res, next) {
  // res.send(req.user);
  res.send('Successful login!')
});

app.get('/auth/fitbit',
  passport.authenticate('fitbit', {scope: ['activity','heartrate','location','profile']}
));

// app.get('/auth/fitbit', (req,res)=> {
//   res.json('123abc!');
// });

// app.get('/auth/fitbit/callback',
//   passport.authenticate('fitbit', {
//     successRedirect: '/auth/fitbit/success',
//     failureRedirect: '/auth/fitbit/failure'
//    }
// ));

app.get('auth/fitbit/callback', (req, res)=>{
  res.json('123abc!')
})

// The "catchall" handler: for any request that doesn't
// match one above, send back React's index.html file.
app.get('*', (req, res) => {
  console.log('catchall');
  res.sendFile(path.join(__dirname+'/client/build/index.html'));
  // res.send('welcome');
});

const port = process.env.PORT || 5000;
app.listen(port);

console.log(`Listening on ${port}`);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  const err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});
<commit_msg>get Passport on Heroku working<commit_after>const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const session = require('express-session');
const passport = require('passport');
const FitbitStrategy = require( 'passport-fitbit-oauth2' ).FitbitOAuth2Strategy;
const app = express();
require('dotenv').config()

app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({
  secret: process.env.PASSPORT_SECRET,
  resave: false,
  saveUninitialized: true
 }));

app.use(passport.initialize());
app.use(passport.session());

// Serve static files from the React app
app.use(express.static(path.join(__dirname, 'client/build')));

//passport-fitbit-oauth2 routing
passport.use(new FitbitStrategy({
    clientID: process.env.FITBIT_OAUTH2_CLIENT_ID,
    clientSecret: process.env.FITBIT_OAUTH2_CLIENT_SECRET,
    callbackURL: "http://serene-green.herokuapp.com/auth/fitbit/callback"
  },
  function(accessToken, refreshToken, profile, done) {
    User.findOrCreate({ fitbitId: profile.id }, function (err, user) {
      return done(err, user);
    });
  }
));

passport.serializeUser(function(user, done) {
  done(null, user);
});

passport.deserializeUser(function(obj, done) {
  done(null, obj);
});

app.get('/auth/fitbit/success', function(req, res, next) {
  // res.send(req.user);
  res.send('Successful login!')
});

app.get('/auth/fitbit',
  passport.authenticate('fitbit', {scope: ['activity','heartrate','location','profile']}
));

// app.get('/auth/fitbit', (req,res)=> {
//   res.json('123abc!');
// });

// app.get('/auth/fitbit/callback',
//   passport.authenticate('fitbit', {
//     successRedirect: '/auth/fitbit/success',
//     failureRedirect: '/auth/fitbit/failure'
//    }
// ));

app.get('auth/fitbit/callback', (req, res)=>{
  res.json('123abc!')
})

// The "catchall" handler: for any request that doesn't
// match one above, send back React's index.html file.
app.get('*', (req, res) => {
  console.log('catchall');
  res.sendFile(path.join(__dirname+'/client/build/index.html'));
  // res.send('welcome');
});

const port = process.env.PORT || 5000;
app.listen(port);

console.log(`Listening on ${port}`);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  const err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});
<|endoftext|>


<commit_before>
//     express-paginate
//     Copyright (c) 2014- Nick Baugh <niftylettuce@gmail.com> (http://niftylettuce.com)
//     MIT Licensed

// Node.js pagination middleware and view helpers.

// * Author: [@niftylettuce](https://twitter.com/#!/niftylettuce)
// * Source: <https://github.com/niftylettuce/express-paginate>

var querystring = require('querystring');
var url = require('url');
var assign = require('lodash.assign');
var clone = require('lodash.clone');
var isObject = require('lodash.isobject');
var util = require('util');

exports = module.exports;

exports.href = function href(req) {

  return function(prev, params) {

    var query = clone(req.query);

    if (typeof prev === 'object') {
      params = prev;
      prev = false;
    } else {
      prev = (typeof prev === 'boolean') ? prev : false;
      query.page = prev ? query.page-= 1 : query.page += 1;
      query.page = (query.page < 1) ? 1 : query.page;
    }

    // allow overriding querystring params
    // (useful for sorting and filtering)
    // another alias for `_.assign` is `_.extend`
    if (isObject(params))
      query = assign(query, params);

    return url.parse(req.originalUrl).pathname + '?' + querystring.stringify(query);

  };
};

exports.hasNextPages = function hasNextPages(req) {
  return function(pageCount) {
    if (typeof pageCount !== 'number' || pageCount < 0)
      throw new Error('express-paginate: `pageCount` is not a number >= 0');
    return req.query.page < pageCount;
  };
};

exports.getArrayPages = function(req) {
  return function(limit, pageCount, currentPage) {
    var maxPage = pageCount;

    // limit default is 3
    limit = limit || 3;

    if (typeof limit !== 'number' || limit < 0)
      throw new Error('express-paginate: `limit` is not a number >= 0');

    if (typeof pageCount !== 'number' || pageCount < 0)
      throw new Error('express-paginate: `pageCount` is not a number >= 0');

    if (typeof currentPage !== 'number' || currentPage < 0)
      throw new Error('express-paginate: `currentPage` is not a number >= 0');

    if (limit > 0) {
      var start = currentPage - limit > 0 ? currentPage - limit : 1;
      var end = currentPage + limit > maxPage ?
        maxPage : currentPage + limit;
      var pages = [];
      for (var i = start; i <= end; i++) {
        pages.push({
          number: i,
          url: exports.href(req)()
          .replace('page=' + (currentPage + 1), 'page=' + i)
        });
      }

      return pages;
    }
  }
}

exports.middleware = function middleware(limit, maxLimit) {

  var _limit = (typeof limit === 'number') ? parseInt(limit, 10) || 10 : 10;

  var _maxLimit = (typeof maxLimit === 'number') ? parseInt(maxLimit, 10) || 10 : 50;

  if (_limit < 1)
    throw new Error('express-paginate: `limit` cannot be less than 1');

  if (_maxLimit < 1)
    throw new Error('express-paginate: `maxLimit` cannot be less than 1');

  return function _middleware(req, res, next) {

    req.query.page = (typeof req.query.page === 'string') ? parseInt(req.query.page, 10) || 1 : 1;

    req.query.limit = (typeof req.query.limit === 'string') ? parseInt(req.query.limit, 10) || _limit : _limit;

    req.skip = req.offset = (req.query.page * req.query.limit) - req.query.limit;

    if (req.query.limit > _maxLimit)
      req.query.limit = _maxLimit;

    if (req.query.page < 1)
      req.query.page = 1;

    if (req.query.limit < 1)
      req.query.limit = 1;

    res.locals.paginate = {};
    res.locals.paginate.page = req.query.page;
    res.locals.paginate.limit = req.query.limit;
    res.locals.paginate.href = exports.href(req);
    res.locals.paginate.hasPreviousPages = req.query.page > 1;
    res.locals.paginate.hasNextPages = exports.hasNextPages(req);
    res.locals.paginate.getArrayPages = exports.getArrayPages(req);

    next();

  };

};
<commit_msg>by forcing the limit to be greater than 0, and by using boolean logic to default the limit to 10 twice in the same conditional, we have effectively eliminated limit=0 and so every use of paginate MUST be paginated.  It is now possible to say limit=0 in a query param to override the APIs limit<commit_after>
//     express-paginate
//     Copyright (c) 2014- Nick Baugh <niftylettuce@gmail.com> (http://niftylettuce.com)
//     MIT Licensed

// Node.js pagination middleware and view helpers.

// * Author: [@niftylettuce](https://twitter.com/#!/niftylettuce)
// * Source: <https://github.com/niftylettuce/express-paginate>

var querystring = require('querystring');
var url = require('url');
var assign = require('lodash.assign');
var clone = require('lodash.clone');
var isObject = require('lodash.isobject');
var util = require('util');

exports = module.exports;

exports.href = function href(req) {

  return function(prev, params) {

    var query = clone(req.query);

    if (typeof prev === 'object') {
      params = prev;
      prev = false;
    } else {
      prev = (typeof prev === 'boolean') ? prev : false;
      query.page = prev ? query.page-= 1 : query.page += 1;
      query.page = (query.page < 1) ? 1 : query.page;
    }

    // allow overriding querystring params
    // (useful for sorting and filtering)
    // another alias for `_.assign` is `_.extend`
    if (isObject(params))
      query = assign(query, params);

    return url.parse(req.originalUrl).pathname + '?' + querystring.stringify(query);

  };
};

exports.hasNextPages = function hasNextPages(req) {
  return function(pageCount) {
    if (typeof pageCount !== 'number' || pageCount < 0)
      throw new Error('express-paginate: `pageCount` is not a number >= 0');
    return req.query.page < pageCount;
  };
};

exports.getArrayPages = function(req) {
  return function(limit, pageCount, currentPage) {
    var maxPage = pageCount;

    // limit default is 3
    limit = limit || 3;

    if (typeof limit !== 'number' || limit < 0)
      throw new Error('express-paginate: `limit` is not a number >= 0');

    if (typeof pageCount !== 'number' || pageCount < 0)
      throw new Error('express-paginate: `pageCount` is not a number >= 0');

    if (typeof currentPage !== 'number' || currentPage < 0)
      throw new Error('express-paginate: `currentPage` is not a number >= 0');

    if (limit > 0) {
      var start = currentPage - limit > 0 ? currentPage - limit : 1;
      var end = currentPage + limit > maxPage ?
        maxPage : currentPage + limit;
      var pages = [];
      for (var i = start; i <= end; i++) {
        pages.push({
          number: i,
          url: exports.href(req)()
          .replace('page=' + (currentPage + 1), 'page=' + i)
        });
      }

      return pages;
    }
  }
}

exports.middleware = function middleware(limit, maxLimit) {

  var _limit = (typeof limit === 'number') ? parseInt(limit, 10) : 10;

  var _maxLimit = (typeof maxLimit === 'number') ? parseInt(maxLimit, 10) : 50;

  return function _middleware(req, res, next) {

    req.query.page = (typeof req.query.page === 'string') ? parseInt(req.query.page, 10) || 1 : 1;

    req.query.limit = (typeof req.query.limit === 'string') ? parseInt(req.query.limit, 10) : _limit;

    req.skip = req.offset = (req.query.page * req.query.limit) - req.query.limit;

    if (req.query.limit > _maxLimit)
      req.query.limit = _maxLimit;

    if (req.query.page < 1)
      req.query.page = 1;

    if (req.query.limit < 1)
      req.query.limit = 1;

    res.locals.paginate = {};
    res.locals.paginate.page = req.query.page;
    res.locals.paginate.limit = req.query.limit;
    res.locals.paginate.href = exports.href(req);
    res.locals.paginate.hasPreviousPages = req.query.page > 1;
    res.locals.paginate.hasNextPages = exports.hasNextPages(req);
    res.locals.paginate.getArrayPages = exports.getArrayPages(req);

    next();

  };

};
<|endoftext|>


<commit_before>'use strict';

const path = require('path')
    , fs = require('fs')
    , env = process.env
    , cp = require('child_process')

const HOST_NON_EXISTENT = /host does not exist/i
    , ALREADY_RUNNING = /already running/i

class Machine {
  static options(opts) {
    if (typeof opts === 'string') return { name: opts }
    else return opts || {}
  }

  constructor(opts) {
    opts = Machine.options(opts)
    this.name = opts.name || env.DOCKER_MACHINE_NAME || 'default'
  }

  command(args, done) {
    if (typeof args === 'string') {
      args = [args, this.name]
    } else if (args.indexOf(this.name) < 0) {
      args = args.concat(this.name)
    }

    cp.execFile('docker-machine', args, {
      cwd: env.DOCKER_TOOLBOX_INSTALL_PATH || '.',
      encoding: 'utf8'
    }, done)
  }

  status(done) {
    this.command('status', (err, stdout) => {
      if (err) done(err)
      else done(null, stdout.trim().toLowerCase())
    })
  }

  isRunning(done) {
    this.status((err, status) => {
      done(err, status === 'running')
    })
  }

  start(done) {
    this.command('start', (err) => {
      if (HOST_NON_EXISTENT.test(err)) {
        done(new Error(`Docker host "${this.name}" does not exist`))
      } else if (ALREADY_RUNNING.test(err)) {
        done()
      } else {
        done(err)
      }
    })
  }

  env(opts, done) {
    if (typeof opts === 'function') done = opts, opts = {}

    const args = ['env']

    if (opts.json) args.push('--shell', 'bash')
    else if (opts.shell) args.push('--shell', opts.shell)

    this.command(args, function (err, stdout) {
      if (err) return done(err)
      if (!opts.json) return done(null, stdout.trim())

      const res = {}

      stdout.split(/\n+/).forEach(line => {
        const m = /^export (.+)="([^"]+)/i.exec(line)
        if (m) res[m[1]] = m[2]
      })

      done(null, res)
    })
  }

  ssh(cmd, done) {
    if (Array.isArray(cmd)) cmd = cmd.join(' ')
    this.command(['ssh', this.name, cmd], done)
  }
}

module.exports = Machine
<commit_msg>refactor: make command() static<commit_after>'use strict';

const path = require('path')
    , fs = require('fs')
    , env = process.env
    , cp = require('child_process')

const HOST_NON_EXISTENT = /host does not exist/i
    , ALREADY_RUNNING = /already running/i

class Machine {
  static options(opts) {
    if (typeof opts === 'string') return { name: opts }
    else return opts || {}
  }

  static command(args, done) {
    cp.execFile('docker-machine', [].concat(args), {
      cwd: env.DOCKER_TOOLBOX_INSTALL_PATH || '.',
      encoding: 'utf8'
    }, done)
  }

  constructor(opts) {
    opts = Machine.options(opts)
    this.name = opts.name || env.DOCKER_MACHINE_NAME || 'default'
  }

  status(done) {
    this.command(['status', this.name], (err, stdout) => {
      if (err) done(err)
      else done(null, stdout.trim().toLowerCase())
    })
  }

  isRunning(done) {
    this.status((err, status) => {
      done(err, status === 'running')
    })
  }

  start(done) {
    this.command(['start', this.name], (err) => {
      if (HOST_NON_EXISTENT.test(err)) {
        done(new Error(`Docker host "${this.name}" does not exist`))
      } else if (ALREADY_RUNNING.test(err)) {
        done()
      } else {
        done(err)
      }
    })
  }

  env(opts, done) {
    if (typeof opts === 'function') done = opts, opts = {}

    const args = ['env']

    if (opts.json) args.push('--shell', 'bash')
    else if (opts.shell) args.push('--shell', opts.shell)

    args.push(this.name)

    this.command(args, function (err, stdout) {
      if (err) return done(err)
      if (!opts.json) return done(null, stdout.trim())

      const res = {}

      stdout.split(/\n+/).forEach(line => {
        const m = /^export (.+)="([^"]+)/i.exec(line)
        if (m) res[m[1]] = m[2]
      })

      done(null, res)
    })
  }

  ssh(cmd, done) {
    if (Array.isArray(cmd)) cmd = cmd.join(' ')
    this.command(['ssh', this.name, cmd], done)
  }
}

module.exports = Machine
<|endoftext|>


<commit_before>var feedparser = require('feedparser');
var fb = require('fb');
var extend = require('extend');
var _ = require('lodash');
var request = require('request');
var chalk = require('chalk');
var cache = {};
var moment = require('moment');

module.exports = function(options, callback) {
  return new Construct(options, callback);
};

module.exports.Construct = Construct;

function Construct(options, callback) {
  var apos = options.apos;
  var app = options.app;

  if ((!options.fbAppId || !options.fbAppSecret) && !options.disabled) {
    console.error('WARNING: you must configure the fbAppId and fbAppSecret options to use the Facebook widget.');
  }

  var self = this;
  self._apos = apos;
  self._app = app;
  var limit;
  var lifetime = options.lifetime ? options.lifetime : 60000;

  var access_token = options.fbAppId+'|'+options.fbAppSecret;

  self._apos.mixinModuleAssets(self, 'facebook', __dirname, options);

  // This widget should be part of the default set of widgets for areas
  // (this isn't mandatory)
  apos.defaultControls.push('facebook');

  // Include our editor template in the markup when aposTemplates is called
  self.pushAsset('template', 'facebookEditor', { when: 'user' });
  //self.pushAsset('template', 'facebook', { when: 'always' });

  // Make sure that aposScripts and aposStylesheets summon our assets

  // We need the editor for RSS feeds. (TODO: consider separate script lists for
  // resources needed also by non-editing users.)
  self.pushAsset('script', 'editor', { when: 'user' });
  self.pushAsset('script', 'content', { when: 'always' });
  self.pushAsset('stylesheet', 'content', { when: 'always' });

  self.widget = true;
  self.label = options.label || 'Facebook';
  self.css = options.css || 'facebook';
  self.icon = options.icon || 'icon-facebook';

  self.sanitize = function(item) {
    if (!item.pageUrl.match(/^https?\:\/\//)) {
      item.pageUrl = 'http://' + item.pageUrl;
    }
    item.limit = parseInt(item.limit, 10);
  };

  var facebookCache = {};
  var pageUrl;

  self.sanitizeDate = function(date){
    var postDate = moment(date, 'MM-DD-YYYY'),
        postMonth = postDate.month() + 1,
        postDay = postDate.date(),
        postYear = postDate.year(),
        thisYear = moment().year();
    return ((postYear != thisYear ) ? postMonth +"/"+postDay+"/"+postYear: postMonth +"/"+postDay);
  }

  if (!options.disabled) {
    //This needs to return a better image.
    app.get('/apos-facebook/photo', function(req, res){
      //Grab the post ID and build out a request URL.
      var postId = req.query.id;
      var requestUrl = 'https://graph.facebook.com/' + postId + '?access_token=' +
      access_token + '&fields=source,name,created_time';

      request(requestUrl, function(err, response, body){
        if (err) {
          res.send(404);
          return console.error(chalk.red('[Apostrophe Facebook] ') + 'The error is: ', err);
        }
        if(response.statusCode === 200){
          //Let's parse and send the image's URL.
          var postObj = JSON.parse(body);
          return res.json(postObj);
        }
      })
    });

    app.get('/apos-facebook/feed', function(req, res) {
    var pageUrl = apos.sanitizeString(req.query.pageUrl);
    var limit = apos.sanitizeString(req.query.limit);
    var cacheKey = pageUrl + limit;

    if (!pageUrl.length) {
      res.statusCode = 404;
      console.error(chalk.red('[Apostrophe Facebook] ') + 'It looks like you forgot to enter a URL');
      return res.send('not found');
    }

    if (_.has(facebookCache, cacheKey)) {
      var cache = facebookCache[cacheKey];
      var now = (new Date()).getTime();
      if (now - cache.when > lifetime * 1000) {
        delete facebookCache[cacheKey];
      } else {
        return res.send(cache.results);
      }
    }

    if (self._apos._aposLocals.offline) {
      res.statusCode = 404;
      return res.send('offline');
    }

    var parsed = require('url').parse(pageUrl);
    var nameString = require('path').basename(parsed.pathname);
    if (!nameString) {
      res.statusCode = 404;
      console.error(chalk.red('[Apostrophe Facebook] ') + 'The url seems to be incorrect: ', pageUrl);
      return res.send('incorrect url');
    }

    var requestUrl = 'https://graph.facebook.com/' + nameString +
      '/posts?access_token=' + access_token +
      '&fields=message,picture,link,object_id,updated_time,type,name,caption,description';

    return request(requestUrl, function(err, response, body){
      if (err) {
        console.error(chalk.red('[Apostrophe Facebook] ') + 'The error is', response.error);
        return callback(response.error);
      }
      if (response.statusCode !== 200) {
        console.error(response.statusCode);
        console.error(body);
        return res.send('error from facebook');
      }
      var parsedBody = JSON.parse(body);

      // Unfortunately, we need to filter out trivial statuses on our end.
      var filteredPosts = _.filter(parsedBody.data, function(post){
        return post.message;
      });
      var posts = filteredPosts.slice(0, limit) || [];

      var results = posts.map(function(post) {
        return {
          id: post.id,
          object_id: post.object_id,
          photo: post.picture,
          body: post.message,
          date: self.sanitizeDate(moment(post.updated_time).format('MM/DD/YYYY')),
          link: post.link,
          type: post.type,
          name: post.name,
          caption: post.caption,
          description: post.description
        };
      });

      facebookCache[cacheKey] = {
        when: (new Date()).getTime(),
        results: results
      };

      return res.send(results);
    });
  });
  }

  self.renderWidget = function(data) {
    return self.render('facebook', data);
  };

  self._apos.addWidgetType('facebook', self);

  return setImmediate(function() { return callback(null); });
}
<commit_msg>Updates the option property name<commit_after>var feedparser = require('feedparser');
var fb = require('fb');
var extend = require('extend');
var _ = require('lodash');
var request = require('request');
var chalk = require('chalk');
var cache = {};
var moment = require('moment');

module.exports = function(options, callback) {
  return new Construct(options, callback);
};

module.exports.Construct = Construct;

function Construct(options, callback) {
  var apos = options.apos;
  var app = options.app;

  if ((!options.fbAppId || !options.fbAppSecret) && !options.apiDisabled) {
    console.error('WARNING: you must configure the fbAppId and fbAppSecret options to use the Facebook widget.');
  }

  var self = this;
  self._apos = apos;
  self._app = app;
  var limit;
  var lifetime = options.lifetime ? options.lifetime : 60000;

  var access_token = options.fbAppId+'|'+options.fbAppSecret;

  self._apos.mixinModuleAssets(self, 'facebook', __dirname, options);

  // This widget should be part of the default set of widgets for areas
  // (this isn't mandatory)
  apos.defaultControls.push('facebook');

  // Include our editor template in the markup when aposTemplates is called
  self.pushAsset('template', 'facebookEditor', { when: 'user' });
  //self.pushAsset('template', 'facebook', { when: 'always' });

  // Make sure that aposScripts and aposStylesheets summon our assets

  // We need the editor for RSS feeds. (TODO: consider separate script lists for
  // resources needed also by non-editing users.)
  self.pushAsset('script', 'editor', { when: 'user' });
  self.pushAsset('script', 'content', { when: 'always' });
  self.pushAsset('stylesheet', 'content', { when: 'always' });

  self.widget = true;
  self.label = options.label || 'Facebook';
  self.css = options.css || 'facebook';
  self.icon = options.icon || 'icon-facebook';

  self.sanitize = function(item) {
    if (!item.pageUrl.match(/^https?\:\/\//)) {
      item.pageUrl = 'http://' + item.pageUrl;
    }
    item.limit = parseInt(item.limit, 10);
  };

  var facebookCache = {};
  var pageUrl;

  self.sanitizeDate = function(date){
    var postDate = moment(date, 'MM-DD-YYYY'),
        postMonth = postDate.month() + 1,
        postDay = postDate.date(),
        postYear = postDate.year(),
        thisYear = moment().year();
    return ((postYear != thisYear ) ? postMonth +"/"+postDay+"/"+postYear: postMonth +"/"+postDay);
  }

  if (!options.apiDisabled) {
    //This needs to return a better image.
    app.get('/apos-facebook/photo', function(req, res){
      //Grab the post ID and build out a request URL.
      var postId = req.query.id;
      var requestUrl = 'https://graph.facebook.com/' + postId + '?access_token=' +
      access_token + '&fields=source,name,created_time';

      request(requestUrl, function(err, response, body){
        if (err) {
          res.send(404);
          return console.error(chalk.red('[Apostrophe Facebook] ') + 'The error is: ', err);
        }
        if(response.statusCode === 200){
          //Let's parse and send the image's URL.
          var postObj = JSON.parse(body);
          return res.json(postObj);
        }
      })
    });

    app.get('/apos-facebook/feed', function(req, res) {
    var pageUrl = apos.sanitizeString(req.query.pageUrl);
    var limit = apos.sanitizeString(req.query.limit);
    var cacheKey = pageUrl + limit;

    if (!pageUrl.length) {
      res.statusCode = 404;
      console.error(chalk.red('[Apostrophe Facebook] ') + 'It looks like you forgot to enter a URL');
      return res.send('not found');
    }

    if (_.has(facebookCache, cacheKey)) {
      var cache = facebookCache[cacheKey];
      var now = (new Date()).getTime();
      if (now - cache.when > lifetime * 1000) {
        delete facebookCache[cacheKey];
      } else {
        return res.send(cache.results);
      }
    }

    if (self._apos._aposLocals.offline) {
      res.statusCode = 404;
      return res.send('offline');
    }

    var parsed = require('url').parse(pageUrl);
    var nameString = require('path').basename(parsed.pathname);
    if (!nameString) {
      res.statusCode = 404;
      console.error(chalk.red('[Apostrophe Facebook] ') + 'The url seems to be incorrect: ', pageUrl);
      return res.send('incorrect url');
    }

    var requestUrl = 'https://graph.facebook.com/' + nameString +
      '/posts?access_token=' + access_token +
      '&fields=message,picture,link,object_id,updated_time,type,name,caption,description';

    return request(requestUrl, function(err, response, body){
      if (err) {
        console.error(chalk.red('[Apostrophe Facebook] ') + 'The error is', response.error);
        return callback(response.error);
      }
      if (response.statusCode !== 200) {
        console.error(response.statusCode);
        console.error(body);
        return res.send('error from facebook');
      }
      var parsedBody = JSON.parse(body);

      // Unfortunately, we need to filter out trivial statuses on our end.
      var filteredPosts = _.filter(parsedBody.data, function(post){
        return post.message;
      });
      var posts = filteredPosts.slice(0, limit) || [];

      var results = posts.map(function(post) {
        return {
          id: post.id,
          object_id: post.object_id,
          photo: post.picture,
          body: post.message,
          date: self.sanitizeDate(moment(post.updated_time).format('MM/DD/YYYY')),
          link: post.link,
          type: post.type,
          name: post.name,
          caption: post.caption,
          description: post.description
        };
      });

      facebookCache[cacheKey] = {
        when: (new Date()).getTime(),
        results: results
      };

      return res.send(results);
    });
  });
  }

  self.renderWidget = function(data) {
    return self.render('facebook', data);
  };

  self._apos.addWidgetType('facebook', self);

  return setImmediate(function() { return callback(null); });
}
<|endoftext|>


<commit_before>var sampleData =
	{ sumVendTop:
		[
			{ "_id" : "AGENCY FOR HEALTH CARE ADMIN", "total" : 20923882780.100025 },
			{ "_id" : "STATE BOARD OF ADMINISTRATION", "total" : 5585115582.389993 },
			{ "_id" : "MIAMI-DADE COUNTY SCHOOL BOARD", "total" : 1664458036.2000022 },
			{ "_id" : "FL CFO SPEC PURPOSE INVEST ACCT", "total" : 1596296192.7299998 },
			{ "_id" : "BROWARD COUNTY SCHOOL BOARD", "total" : 1263273147.2300034 },
			{ "_id" : "HILLSBOROUGH CO SCHOOL BOARD", "total" : 1180955374.4900005 },
			{ "_id" : "FL DFS TAX REVOLVING ACCOUNT", "total" : 1164839659.46 },
			{ "_id" : "ORANGE COUNTY SCHOOL BOARD", "total" : 1161125745.7300036 },
			{ "_id" : "MIAMI DADE COUNTY", "total" : 857798335.5299993 },
			{ "_id" : "DUVAL COUNTY SCHOOL BOARD", "total" : 710290560.6599996 },
			{ "_id" : "PALM BEACH COUNTY SCHOOL BOARD", "total" : 683468142.6599997 },
			{ "_id" : "POLK COUNTY SCHOOL BOARD", "total" : 625411137.5099989 },
			{ "_id" : "STATE OF FLORIDA EMPLOYEE'S", "total" : 617100000 },
			{ "_id" : "DMS SELF INSURED AVMED CLAIMS", "total" : 471000000 },
			{ "_id" : "MEDCO", "total" : 470088659.87999994 },
			{ "_id" : "PINELLAS COUNTY SCHOOL BOARD", "total" : 469469925.3399996 },
			{ "_id" : "CENTERS FOR MEDICARE & MEDICAID", "total" : 430777480.21 },
			{ "_id" : "PASCO COUNTY SCHOOL BOARD", "total" : 424303326.3099998 },
			{ "_id" : "UNIVERSITY OF SOUTH FLORIDA", "total" : 423745211.9399995 },
			{ "_id" : "HILLSBOROUGH CO BOCC", "total" : 397885099.3099999 },
			{ "_id" : "AGENCY FOR HEALTH CARE ADMIN.", "total" : 395932238.31 },
			{ "_id" : "BREVARD COUNTY SCHOOL BOARD", "total" : 370516888.8599997 },
			{ "_id" : "LEE COUNTY SCHOOL BOARD", "total" : 358684408.36999977 },
			{ "_id" : "OSCEOLA COUNTY SCHOOL BOARD", "total" : 343539505.30999964 },
			{ "_id" : "VOLUSIA COUNTY SCHOOL BOARD", "total" : 339323925.04999983 },
			{ "_id" : "CITY OF JACKSONVILLE, TREASURY", "total" : 336732720.1800006 },
			{ "_id" : "SEMINOLE COUNTY SCHOOL BOARD", "total" : 316862210.3299996 }
		]
	};

$(function() {
	InitChart();
});

function InitChart() {
	var diameter = 600;

	var svg = d3.select('#svgVisualize')
	   .attr('width', diameter)
	   .attr('height', diameter);

	var bubble = d3.layout.pack()
	   .size([diameter, diameter])
	   .padding(3)   // padding between adjacent circles
	   // new data will be loaded to bubble layout
	   .value(function(d) {return d.size;});

	var nodes = bubble.nodes(processData(sampleData))
	   // filter out the outer bubble
	   .filter(function(d) { return !d.children; });

      var vis = svg.selectAll('circle')
					.data(nodes);

  vis.enter().append('circle')
			.attr('transform', function(d) {
				return 'translate(' + d.x + ',' + d.y + ')';
			})
			.attr('r', function(d) {
				return d.r;
			})
			.attr('class', function(d) {
				return d.className;
			});
}

  function processData(data) {
    var obj = data.sumVendTop;

    var newDataSet = [];

    for(var prop in obj) {
      newDataSet.push({name: prop, className: prop.toLowerCase(), size: obj[prop]});
    }
    return {children: newDataSet};
  }<commit_msg>flattened out the data<commit_after>var sampleData =
	{ sumVendTop:
		[
			{ "AGENCY FOR HEALTH CARE ADMIN" : 20923882780.100025 },
			{ "STATE BOARD OF ADMINISTRATION" : 5585115582.389993 },
			{ "MIAMI-DADE COUNTY SCHOOL BOARD" : 1664458036.2000022 },
			{ "FL CFO SPEC PURPOSE INVEST ACCT" : 1596296192.7299998 },
			{ "BROWARD COUNTY SCHOOL BOARD" : 1263273147.2300034 },
			{ "HILLSBOROUGH CO SCHOOL BOARD" : 1180955374.4900005 },
			{ "FL DFS TAX REVOLVING ACCOUNT" : 1164839659.46 },
			{ "ORANGE COUNTY SCHOOL BOARD" : 1161125745.7300036 },
			{ "MIAMI DADE COUNTY" : 857798335.5299993 },
			{ "DUVAL COUNTY SCHOOL BOARD" : 710290560.6599996 },
			{ "PALM BEACH COUNTY SCHOOL BOARD" : 683468142.6599997 },
			{ "POLK COUNTY SCHOOL BOARD" : 625411137.5099989 },
			{ "STATE OF FLORIDA EMPLOYEE'S" : 617100000 },
			{ "DMS SELF INSURED AVMED CLAIMS" : 471000000 },
			{ "MEDCO" : 470088659.87999994 },
			{ "PINELLAS COUNTY SCHOOL BOARD" : 469469925.3399996 },
			{ "CENTERS FOR MEDICARE & MEDICAID" : 430777480.21 },
			{ "PASCO COUNTY SCHOOL BOARD" : 424303326.3099998 },
			{ "UNIVERSITY OF SOUTH FLORIDA" : 423745211.9399995 },
			{ "HILLSBOROUGH CO BOCC" : 397885099.3099999 },
			{ "AGENCY FOR HEALTH CARE ADMIN." : 395932238.31 },
			{ "BREVARD COUNTY SCHOOL BOARD" : 370516888.8599997 },
			{ "LEE COUNTY SCHOOL BOARD" : 358684408.36999977 },
			{ "OSCEOLA COUNTY SCHOOL BOARD" : 343539505.30999964 },
			{ "VOLUSIA COUNTY SCHOOL BOARD" : 339323925.04999983 },
			{ "CITY OF JACKSONVILLE, TREASURY" : 336732720.1800006 },
			{ "SEMINOLE COUNTY SCHOOL BOARD" : 316862210.3299996 }
		]
	};

$(function() {
	InitChart();
});

function InitChart() {
	var diameter = 600;

	var svg = d3.select('#svgVisualize')
	   .attr('width', diameter)
	   .attr('height', diameter);

	var bubble = d3.layout.pack()
	   .size([diameter, diameter])
	   .padding(3)   // padding between adjacent circles
	   // new data will be loaded to bubble layout
	   .value(function(d) {return d.size;});

	var nodes = bubble.nodes(processData(sampleData))
	   // filter out the outer bubble
	   .filter(function(d) { return !d.children; });

      var vis = svg.selectAll('circle')
					.data(nodes);

  vis.enter().append('circle')
			.attr('transform', function(d) {
				return 'translate(' + d.x + ',' + d.y + ')';
			})
			.attr('r', function(d) {
				return d.r;
			})
			.attr('class', function(d) {
				return d.className;
			});
}

  function processData(data) {
    var obj = data.sumVendTop;

    var newDataSet = [];

    for(var prop in obj) {
      newDataSet.push({name: prop, className: prop.toLowerCase(), size: obj[prop]});
    }
    return {children: newDataSet};
  }<|endoftext|>


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

console.log('taskboard');
var userArgs = process.argv.slice(2);
var searchPattern = userArgs[0];

<commit_msg>print out passed project name<commit_after>#! /usr/bin/env node

console.log('taskboard');
var userArgs = process.argv.slice(2);
var projectName = userArgs[0];
console.log('projectname', projectName);
<|endoftext|>


<commit_before>// 64 bit hashes
"use strict";
{
  test();
  evaluate();

  function evaluate() {
    if ( ! process.argv[2] ) {
      console.warn( "Filename for output required" );
      return;
    }
    const fs = require('fs');
    let batch = 1<<10;
    let i = 1<<24 >> 3;
    let str = '';
    let HASH = '';
    while( i-- ) {
      if ( str.length == batch ) {
        fs.appendFileSync( process.argv[2], str, 'binary' );
        str = '';
      }
      HASH = hash( HASH + i, { out_format : 'binary' } ); // 8 bytes
      str += HASH;
    }
  }

  function pad( width, str ) {
    const padding = new Array( Math.max( 0, width - str.length ) + 1 ).join('0');
    return padding + str;
  }

  // Continued Fractions Hashing
  function q( state, val, numerator, denominator ) {
    state[0] += numerator / denominator;
    state[0] = 1.0 / state[0];

    state[1] += val;
    state[1] = numerator / state[1];
  }

  function round( msg, state ) {

    let numerator = 1;
    msg.forEach( (val,index) => {
      const pos = index + 1.0;
      const denominator = val + pos;
      q(state, val, numerator, denominator);
      numerator = denominator;
    });

    /*
      q(state,last_val, 3,0);
      q(state,3,2, 1);
      q(state,2,1, 2);
    */

  }

  function setup( state ) {

    state[0] = 3;
    state[1] = 1/7;

  }

  function hash( msg = '', { out_format : out_format = 'hex' } = {}) {

    msg = msg.split('').map( v => v.charCodeAt(0) );
    const buf = new ArrayBuffer(16);
    const state = new Float64Array(buf);

    setup( state );
    round( msg, state );

    const output = new Uint8Array(buf);
    let bytes = '';
    if ( out_format == 'hex' ) {
      output.slice(0,4).forEach( v => bytes += pad( 2, v.toString(16) ) );
      output.slice(8,12).forEach( v => bytes += pad( 2, v.toString(16) ) );
    } else if ( out_format == 'binary' ) {
      output.slice(0,4).forEach( v => bytes += String.fromCharCode(v) );
      output.slice(8,12).forEach( v => bytes += String.fromCharCode(v) );
    }
    return bytes;

  }

  function test() {

    console.log( pad( 10, '' ), hash() );
    console.log( pad( 10, 'abc'), hash('abc') );
    console.log( pad( 10, 'abd'), hash('abd') );
    console.log( pad( 10, 'cris'), hash('cris') );

  }

  module.exports = {
    hash, evaluate
  };
}
<commit_msg>Added comments, a basic way to hash numbers and floats and some identified possible improvements It may work to do<commit_after>// 64 bit hashes
"use strict";
{
  const tifu = {
    test, evaluate, hash
  };

  // Node or browser, either is fine

  try { module.exports = tifu; } catch( e ) { Object.assign( self, { tifu } ); }

  // Continued Fractions Hashing - q function for state update

    function q( state, val, numerator, denominator ) {
      // Continued Fraction mixed with Egyptian fraction "Continued Egyptian Fraction"
      // with denominator = val + pos
      state[0] += numerator / denominator;
      state[0] = 1.0 / state[0];

      // Standard Continued Fraction with a_i = val, b_i = (a_i-1) + i + 1
      state[1] += val;
      state[1] = numerator / state[1];
    }

  // Continued Fractions Hashing - Hash round function

    function round( msg, state ) {

      let numerator = 1;
      msg.forEach( (val,index) => {
        const pos = index + 1.0;
        const denominator = val + pos;

        q(state, val, numerator, denominator);

        // Chain the Continued Egyptian fraction update step
        // So its a_i are a ratio of ( last_val + last_pos ) / ( val + pos )
        numerator = denominator;
      });

      // TODO: devise a new step here
        // The point of the following commented out code
        // was to handle the empty message
        // And do some final transformation after the message is complete
      /*
        q(state,last_val, 3,0);
        q(state,3,2, 1);
        q(state,2,1, 2);
      */

    }

  // Setup the state

    function setup( state ) {

      state[0] = 3;
      state[1] = 1/7;

    }

  // Hash Function

    function hash( msg = '', { out_format : out_format = 'hex' } = {}) {

      if ( typeof msg == 'string' ) {
        msg = msg.split('').map( v => v.charCodeAt(0) );
      } else if ( typeof msg == 'number' ) {
        // TODO: consider improving how we hash a number
        // perhaps by incorporation it into the state somehow
        msg = [ msg ];
      }

      const buf = new ArrayBuffer(16);
      const state = new Float64Array(buf);

      setup( state );
      round( msg, state );

      const output = new Uint8Array(buf);
      let bytes = '';
      if ( out_format == 'hex' ) {
        output.slice(0,4).forEach( v => bytes += pad( 2, v.toString(16) ) );
        output.slice(8,12).forEach( v => bytes += pad( 2, v.toString(16) ) );
      } else if ( out_format == 'binary' ) {
        output.slice(0,4).forEach( v => bytes += String.fromCharCode(v) );
        output.slice(8,12).forEach( v => bytes += String.fromCharCode(v) );
      }
      return bytes;

    }

  // Evaluation ( 16 Mb ) self iteration to output file

    function evaluate() {
      try {
        if ( ! process.argv[2] ) {
          console.warn( "Filename for output required" );
          return;
        }
      } catch(e) {
        console.warn("Evaluate only runs at the command line in Node.");
        return;
      }
      const fs = require('fs');
      let batch = 1<<10;
      let i = 1<<24 >> 3;
      let str = '';
      let HASH = '';
      while( i-- ) {
        if ( str.length == batch ) {
          fs.appendFileSync( process.argv[2], str, 'binary' );
          str = '';
        }
        HASH = hash( HASH + i, { out_format : 'binary' } ); // 8 bytes
        str += HASH;
      }
    }

  // Test

    function test() {

      console.log( pad( 10, '' ), hash() );
      console.log( pad( 10, 'abc'), hash('abc') );
      console.log( pad( 10, 'abd'), hash('abd') );
      console.log( pad( 10, 'cris'), hash('cris') );

    }

  // String padding

    function pad( width, str ) {
      const padding = new Array( Math.max( 0, width - str.length ) + 1 ).join('0');
      return padding + str;
    }
}
<|endoftext|>


<commit_before>var fs = require('fs');
var ReadStream = require('tty').ReadStream;

var _fs = process.binding('fs');
var _constants = process.binding('constants');
var _TTY = process.binding('tty_wrap').TTY;

module.exports = openTTY;

var rs = null;

function openTTY() {
    var fd;

    if (rs) {
        rs.setRawMode(true);
        return rs;
    }

    if (process.platform === 'win32') {
        fd = _fs.open('CONIN$', _constants.O_RDWR, 438);
    } else {
        fd = fs.openSync('/dev/tty', 'r');
    }

    if (!fd) {
        throw new Error('Cannot access console input buffer');
    }

    var tty = new _TTY(fd, true);
    tty.setRawMode(true);

    if (process.stdin.isTTY) {
        return rs = process.stdin;
    } else {
        return rs || (rs = new ReadStream(fd));
    }

    return rs;
}
<commit_msg>correct raw mode setting<commit_after>var fs = require('fs');
var ReadStream = require('tty').ReadStream;

var _fs = process.binding('fs');
var _constants = process.binding('constants');
var _TTY = process.binding('tty_wrap').TTY;

module.exports = openTTY;

var rs = null;

function openTTY() {
    var fd;

    if (rs && rs.isTTY) {
        return rs;
    }

    if (process.platform === 'win32') {
        fd = _fs.open('CONIN$', _constants.O_RDWR, 438);
    } else {
        fd = fs.openSync('/dev/tty', 'r');
    }

    if (!fd) {
        throw new Error('Cannot access console input buffer');
    }

    var tty = new _TTY(fd, true);
    tty.setRawMode(true);

    if (process.stdin.isTTY) {
        return rs = process.stdin;
    } else {
        return rs || (rs = new ReadStream(fd));
    }

    return rs;
}
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';

export class RadioGroup extends Component {
  constructor() {
    super();
    this.state = { checkedIndex: -1 };
    this.renderChild = this.renderChild.bind(this);
    this.onCheck = this.onCheck.bind(this);
  }

  get value() {
    const { checkedIndex } = this.state;
    const { children } = this.props;

    const child = children.find(c => c.props.index === checkedIndex);
    return child && child.props.value || '';
  }

  onCheck(index) {
    this.setState({ checkedIndex: index });
  }

  renderChild(child, index, checked) {
    return React.cloneElement(child, {
      index, checked, onCheck: this.onCheck, ...child.props
    });
  }

  render() {
    const { checkedIndex } = this.state;
    const { children, ...props } = this.props;
    return (

        {
          children.map((c, i) => (this.renderChild(c, i, i === checkedIndex)))
        }

    );
  }
}

RadioGroup.propTypes = {
  children: PropTypes.node,
  checkedIndex: PropTypes.integer,
};

export class RadioButton extends Component {
  constructor() {
    super();
    this.onClick = this.onClick.bind(this);
  }

  onClick() {
    const { onCheck, checked, index } = this.props;
    onCheck && onCheck(checked && -1 || index);
  }

  render() {
    const { checked, value, children } = this.props;
    return (
      <div className={ `btn-radio${checked && '-checked' || ''}` } onClick={ this.onClick }>

            { children }

          <RadioIcon checked={ checked } />

    );
  }
}

RadioButton.propTypes = {
  value: PropTypes.string,
  index: PropTypes.number,
  children: PropTypes.node,
  onCheck: PropTypes.func,
};

class RadioIcon extends Component {
  constructor() {
    super();
    this.getStyles = this.getStyles.bind(this);
  }
  getStyles() {
    const { size, rootColor, pointColor } = this.props;

    return {
      root: {
        width: size || 20,
        height: size || 20,
        backgroundColor: '#FFF',
        borderWidth: 2,
        borderRadius: '50%',
        borderStyle: 'solid',
        borderColor: rootColor || '#9E9E9E',
      },
      checked: {
        borderColor: pointColor || '#8CB9FD',
      },
      inner: {
        width: size ? size / 2 : 10,
        height: size ? size / 2 : 10,
        borderRadius: '50%',
        background: pointColor || '#8CB9FD',
      }
    }
  }

  render() {
    const { checked } = this.props;
    const style = this.getStyles();
    const iconStyle = Object.assign(style.root, checked ? style.checked : {});
    return (

        { checked &&
 }

    );
  }
}

RadioIcon.propTypes = {
  size: PropTypes.number,
  rootColor: PropTypes.string,
  pointColor: PropTypes.string,
  checked: PropTypes.boolean,
};
<commit_msg>Fix RadioIcon align<commit_after>import React, { Component, PropTypes } from 'react';

export class RadioGroup extends Component {
  constructor() {
    super();
    this.state = { checkedIndex: -1 };
    this.renderChild = this.renderChild.bind(this);
    this.onCheck = this.onCheck.bind(this);
  }

  get value() {
    const { checkedIndex } = this.state;
    const { children } = this.props;

    const child = children.find(c => c.props.index === checkedIndex);
    return child && child.props.value || '';
  }

  onCheck(index) {
    this.setState({ checkedIndex: index });
  }

  renderChild(child, index, checked) {
    return React.cloneElement(child, {
      index, checked, onCheck: this.onCheck, ...child.props
    });
  }

  render() {
    const { checkedIndex } = this.state;
    const { children, ...props } = this.props;
    return (

        {
          children.map((c, i) => (this.renderChild(c, i, i === checkedIndex)))
        }

    );
  }
}

RadioGroup.propTypes = {
  children: PropTypes.node,
  checkedIndex: PropTypes.integer,
};

export class RadioButton extends Component {
  constructor() {
    super();
    this.onClick = this.onClick.bind(this);
  }

  onClick() {
    const { onCheck, checked, index } = this.props;
    onCheck && onCheck(checked && -1 || index);
  }

  render() {
    const { checked, value, children } = this.props;
    return (
      <div className={ `btn-radio${checked && '-checked' || ''}` } onClick={ this.onClick }>

            { children }

          <RadioIcon checked={ checked } />

    );
  }
}

RadioButton.propTypes = {
  value: PropTypes.string,
  index: PropTypes.number,
  children: PropTypes.node,
  onCheck: PropTypes.func,
};

class RadioIcon extends Component {
  constructor() {
    super();
    this.getStyles = this.getStyles.bind(this);
  }

  getStyles() {
    const { size, rootColor, pointColor } = this.props;

    return {
      root: {
        width: size || 10,
        height: size || 10,
        padding: 3,
        backgroundColor: '#FFF',
        borderWidth: 2,
        borderRadius: '50%',
        borderStyle: 'solid',
        borderColor: rootColor || '#9E9E9E',
      },
      checked: {
        borderColor: pointColor || '#8CB9FD',
      },
      inner: {
        width: size || 10,
        height: size || 10,
        borderRadius: '50%',
        background: pointColor || '#8CB9FD',
      }
    }
  }

  render() {
    const { checked } = this.props;
    const style = this.getStyles();
    const iconStyle = Object.assign(style.root, checked ? style.checked : {});
    return (

        { checked &&
 }

    );
  }
}

RadioIcon.propTypes = {
  size: PropTypes.number,
  rootColor: PropTypes.string,
  pointColor: PropTypes.string,
  checked: PropTypes.boolean,
};
<|endoftext|>


<commit_before>// dependencies
var AWS = require('aws-sdk');
var gm = require('gm')
            .subClass({ imageMagick: true }); // Enable ImageMagick integration.
var ffmpeg = require('fluent-ffmpeg');
var q = require('q');
var fs = require('fs');
var path = require('path');
var mime = require('mime');

process.env['PATH'] = process.env['PATH'] + ':' + process.env['LAMBDA_TASK_ROOT']

if (!process.env.NODE_ENV || process.env.NODE_ENV != 'testing') {
  //production
  var handleS3Event = require('handle-s3-event');
  var tmpPrefix = '/tmp/';
} else {
  //local
  var handleS3Event = require('./local_modules/handle-s3-event');
  var tmpPrefix = './';
}

var s3 = new AWS.S3();

var moveAndChmodFfmpegBinary = function() {
  var def = q.defer()

  require('child_process').exec(
    'cp /var/task/ffmpeg /tmp/.; chmod 755 /tmp/ffmpeg',
    function (error, stdout, stderr) {
      if (error) {
        def.reject(error)
      } else {
        def.resolve()
      }
    }
  )

  return def.promise;
}

var printFormats = function() {
    var def = q.defer()

    ffmpeg.getAvailableFormats(function(err, formats) {
      if (err) {
        def.reject(err);
      } else {
        def.resolve()
      }
    });

    return def.promise;
}

//TODO: unit test
var isValidKey = function(key) {
  function endsWith(str, suffix) {
    return str.indexOf(suffix, str.length - suffix.length) !== -1;
  }

  return endsWith(key, '.gif')
}

exports.handler = function(event, context) {
  //assign these for prod – if ffmpeg-fluent doesn't find them,
  //it falls back to the machine's local `ffmpeg`
  process.env['FFMPEG_PATH'] = '/tmp/ffmpeg';
  process.env['FFPROBE_PATH'] = '/tmp/ffprobe';

  var promises = [];

  if (!process.env.NODE_ENV || process.env.NODE_ENV != 'testing') {
    promises.push(moveAndChmodFfmpegBinary);
  }

  promises.push(printFormats);
  promises.push(handleS3Event(event));

  promises.push(function(s3Data) {
    var def = q.defer()

    if (isValidKey(s3Data.srcKey)) {
      def.resolve(s3Data);
    } else {
      def.reject("Uploaded file does not end in `.gif`: " + s3Data.srcKey)
    }

    return def.promise
  });

  promises.push(function(s3Data) {
    //TODO: unit tests
    var def = q.defer();

    var params = {Bucket: s3Data.srcBucket, Key: s3Data.srcKey};
    var filePath = tmpPrefix + s3Data.srcKey
    var file = fs.createWriteStream(filePath);

    s3.getObject(params)
      .on('httpData', function(chunk) {
        file.write(chunk)
      })
      .on('httpDone', function() {
        file.end()
        def.resolve({
          s3Data: s3Data,
          file: file
        });
      })
      .send();

    return def.promise;
  });

  promises.push(function(options) {
    var def = q.defer()

    console.log('converting gif to mp4');
    console.log('options');
    console.log(options);

    var basename = path.basename(options.file.path, '.gif')
    console.log('basename');
    console.log(basename);
    //make dir w/ basename
    var dirPath = tmpPrefix + basename
    fs.mkdir(dirPath, function(err) {
      if (err) { def.reject(err) }
      else {
        console.log('created dir: ' + dirPath);

        console.log("trying to id image");
        gm(options.file.path).identify(function(err, data) {
          if (err) {
            console.log('error identifying');
            console.log(err);
          }
          console.log('data');
          console.log(data);
        });

        //convert to .png, drop into that folder
        var pngsPath = dirPath + '/' + basename + '.png';
        console.log('trying to write to pngsPath: ' + pngsPath);
        gm(options.file.path).write(pngsPath, function(err) {
          if (err) { def.reject(err) }
          else {
            console.log('png written');

            gm(options.file.path)
              .identify(function(err, data) {
                if (err) { def.reject(err) }
                else {
                  console.log('delay: ');
                  console.log(data.Delay)
                  //TODO: should probably split the string on the `x`
                  var speed = data.Delay.substring(0, 2);
                  speed = 100 / speed
                  console.log('speed: ' + speed);

                  //get number of frames from created dir
                  //create frames for 5 loops
                  //convert to mp4

                  var pngsBlurb = dirPath + '/' + basename + '-%d.png';
                  var mp4Path = dirPath + '/' + basename + '.mp4';
                  console.log(pngsBlurb);
                  ffmpeg(pngsBlurb)
                    .inputOptions([
                      '-y',
                    ])
                    .outputOptions([
                      '-r ' + speed,
                      '-pix_fmt yuv420p'
                    ])
                    .videoCodec('libx264')
                    .on('error', function(err) {
                      console.log('mp4 save error')
                      console.log(err);
                      def.reject(err);
                    })
                    .on('end', function() {
                      console.log('mp4 save end')
                      options.mp4Path = mp4Path;
                      def.resolve(options);
                    })
                    .save(mp4Path)

                }
              });

          }
        });
      }
    })

    return def.promise
  });

  promises.push(function(options) {
    //needs dstBucket, dstKey, file.path
    var def = q.defer();

    var stream = fs.createReadStream(options.mp4Path);

    console.log('srcKey');
    console.log(options.s3Data.srcKey);
    console.log('mp4 path');
    console.log(options.mp4Path);
    var newBase = path.basename(options.mp4Path);
    var dstKey = path.normalize(options.s3Data.srcKey) + newBase;
    //TODO: needs work
    console.log(dstKey);

    var params = {
      Bucket: options.s3Data.srcBucket + "-resized",
      Key: newBase,
      Body: stream,
      ContentType: mime.lookup(options.mp4Path)
    };

    s3.upload(params).send(function(err, data) {
      if (err) {
        def.reject(err);
      } else {
        def.resolve()
      }
    });

    return def.promise;
  });

  promises.push(function() {
    var def = q.defer()
    console.log('successful conversion and upload');
    def.resolve()
    context.done()
    return def.promise
  })

  promises.reduce(q.when, q())
    .fail(function(err){
      console.log('promise rejected with err');
      console.log(err);
      context.done(err);
    });

};
<commit_msg>much simpler rewrite. fuck imagemagick<commit_after>// dependencies
var AWS = require('aws-sdk');
var ffmpeg = require('fluent-ffmpeg');
var q = require('q');
var fs = require('fs');
var path = require('path');
var mime = require('mime');

process.env['PATH'] = process.env['PATH'] + ':' + process.env['LAMBDA_TASK_ROOT']

if (!process.env.NODE_ENV || process.env.NODE_ENV != 'testing') {
  //production
  var handleS3Event = require('handle-s3-event');
  var tmpPrefix = '/tmp/';
} else {
  //local
  var handleS3Event = require('./local_modules/handle-s3-event');
  var tmpPrefix = './';
}

var s3 = new AWS.S3();

var moveAndChmodFfmpegBinary = function() {
  var def = q.defer()

  require('child_process').exec(
    'cp /var/task/ffmpeg /tmp/.; chmod 755 /tmp/ffmpeg',
    function (error, stdout, stderr) {
      if (error) {
        def.reject(error)
      } else {
        def.resolve()
      }
    }
  )

  return def.promise;
}

var printFormats = function() {
    var def = q.defer()

    ffmpeg.getAvailableFormats(function(err, formats) {
      if (err) {
        def.reject(err);
      } else {
        def.resolve()
      }
    });

    return def.promise;
}

//TODO: unit test
var isValidKey = function(key) {
  function endsWith(str, suffix) {
    return str.indexOf(suffix, str.length - suffix.length) !== -1;
  }

  return endsWith(key, '.gif')
}

exports.handler = function(event, context) {
  //assign these for prod – if ffmpeg-fluent doesn't find them,
  //it falls back to the machine's local `ffmpeg`
  process.env['FFMPEG_PATH'] = '/tmp/ffmpeg';
  process.env['FFPROBE_PATH'] = '/tmp/ffprobe';

  var promises = [];

  if (!process.env.NODE_ENV || process.env.NODE_ENV != 'testing') {
    promises.push(moveAndChmodFfmpegBinary);
  }

  promises.push(printFormats);
  promises.push(handleS3Event(event));

  promises.push(function(s3Data) {
    var def = q.defer()

    if (isValidKey(s3Data.srcKey)) {
      def.resolve(s3Data);
    } else {
      def.reject("Uploaded file does not end in `.gif`: " + s3Data.srcKey)
    }

    return def.promise
  });

  promises.push(function(s3Data) {
    //TODO: unit tests
    var def = q.defer();

    var params = {Bucket: s3Data.srcBucket, Key: s3Data.srcKey};
    var filePath = tmpPrefix + s3Data.srcKey
    var file = fs.createWriteStream(filePath);

    s3.getObject(params)
      .on('httpData', function(chunk) {
        file.write(chunk)
      })
      .on('httpDone', function() {
        file.end()
        def.resolve({
          s3Data: s3Data,
          file: file
        });
      })
      .send();

    return def.promise;
  });

  promises.push(function(options) {
    var def = q.defer()

    var basename = path.basename(options.file.path, '.gif')
    var mp4Path = tmpPrefix + basename + '.mp4';

    ffmpeg(options.file.path)
      .inputOptions([
        '-y',
        '-f gif',
      ])
      .outputOptions([
        '-pix_fmt yuv420p'
      ])
      .videoCodec('libx264')
      .on('error', function(err) {
        console.log('mp4 save error')
        def.reject(err);
      })
      .on('end', function(data) {
        options.mp4Path = mp4Path;
        def.resolve(options);
      })
      .save(mp4Path)

    return def.promise;
  });

  promises.push(function(options) {
    //needs dstBucket, dstKey, file.path
    var def = q.defer();

    var stream = fs.createReadStream(options.mp4Path);

    var newBase = path.basename(options.mp4Path);
    var dstKey = path.normalize(options.s3Data.srcKey) + newBase;
    //TODO: needs work
    console.log(dstKey);

    var params = {
      Bucket: options.s3Data.srcBucket + "-resized",
      Key: newBase,
      Body: stream,
      ContentType: mime.lookup(options.mp4Path)
    };

    s3.upload(params).send(function(err, data) {
      if (err) {
        def.reject(err);
      } else {
        def.resolve()
      }
    });

    return def.promise;
  });

  promises.push(function() {
    var def = q.defer()
    console.log('successful conversion and upload');
    def.resolve()
    context.done()
    return def.promise
  })

  promises.reduce(q.when, q())
    .fail(function(err){
      console.log('promise rejected with err');
      console.log(err);
      context.done(err);
    });

};
<|endoftext|>


<commit_before>'use strict';

const Twitter = require('twitter');
const express = require('express');
const cors = require('cors');
const LRU = require('lru-cache');
const babel = require('babel-core');
const ms = require('ms');

const searchTerm = 't.d3fc.io';
const babelOptions = { presets: ['es2015', 'stage-2'] };

const example = 'z=3,y=d=>w/4*s(d*t/1e3/z),j(rn(0,z),C).a({r:w/4,cx:d=>y(d)*c(d*π/z),cy:d=>y(d)*s(d*π/z)})';

const app = express();

app.set('view engine', 'jade');

const client = new Twitter({
  consumer_key: process.env.consumer_key,
  consumer_secret: process.env.consumer_secret,
  access_token_key: process.env.access_token_key,
  access_token_secret: process.env.access_token_secret
});

const cache = LRU({
  length: 1000
});

app.use(express.static('public', { maxAge: '1h' }));
app.use('/lazyload', express.static('node_modules/lazyloadjs/build', { maxAge: '1d' }));
app.use('/bootstrap', express.static('node_modules/bootstrap/dist', { maxAge: '1d' }));
app.use('/babel-standalone.min.js', express.static('node_modules/babel-standalone/babel.min.js'));

app.get('/', function (req, res) {
  console.log('Request', req.ip);
  const statuses = cache.get('statuses');
  if (statuses == null) {
    console.warn('Results cache miss');
    return res.status(503).render('error');
  }
  res.set({ 'Cache-Control': 'public, max-age=' + ms('1m') });
  res.render('index', {
    statuses: statuses
  });
});

app.get('/playground', function(req, res) {
  res.set({ 'Cache-Control': 'public, max-age=' + ms('1h') });
  res.render('playground');
});

app.get('/loading', function (req, res) {
  res.set({ 'Cache-Control': 'public, max-age=' + ms('1h') });
  res.render('loading');
});

app.get('/:id_str', function (req, res) {
  const status = cache.get(req.params.id_str);
  if (status == null) {
    console.warn('Item cache miss');
    return res.status(404).render('error');
  }
  res.set({ 'Cache-Control': 'public, max-age=' + ms('1h') });
  res.render('item', status);
});

const updateSearchResults = () => {
  console.log('Updating search results');
  client.get('search/tweets', {q: searchTerm}, function(error, tweets, response) {
    if (error) {
      return console.warn(error);
    }
    console.log('Search completed', tweets.statuses.length);
    const statuses = tweets.statuses.map(function(status) {
        const cached = cache.get(status.id_str);
        if (cached != null) {
          return cached;
        }
        const es6 = status.entities.urls.reduce(function(text, url) {
          return text.substring(0, url.indices[0]) + text.substring(url.indices[1]);
        }, status.text);
        let es5 = null;
        try {
           es5 = babel.transform(es6, babelOptions).code;
        } catch (e) {
          console.log('Failed to transform', status.id_str, e);
        }
        const processed = Object.assign({}, status, {
          es5: es5,
          es6: es6
        });
        cache.set(status.id_str, processed);
        return processed;
      });
    const validStatuses = statuses.filter(function(status) {
      return status.es5;
    });
    cache.set('statuses', validStatuses);
    console.log('Search results updated', validStatuses.length);
  });
}
updateSearchResults();
setInterval(updateSearchResults, ms('30s'));

app.listen(3000);
<commit_msg>Revert example code<commit_after>'use strict';

const Twitter = require('twitter');
const express = require('express');
const cors = require('cors');
const LRU = require('lru-cache');
const babel = require('babel-core');
const ms = require('ms');

const searchTerm = 't.d3fc.io';
const babelOptions = { presets: ['es2015', 'stage-2'] };

const example = 'z=3,y=d=>w/4*s(d*t/1e3/z),j(rn(0,z),C).a({r:w/4,cx:d=>y(d)*c(d*π/z),cy:d=>y(d)*s(d*π/z)})';

const app = express();

app.set('view engine', 'jade');

const client = new Twitter({
  consumer_key: process.env.consumer_key,
  consumer_secret: process.env.consumer_secret,
  access_token_key: process.env.access_token_key,
  access_token_secret: process.env.access_token_secret
});

const cache = LRU({
  length: 1000
});

app.use(express.static('public', { maxAge: '1h' }));
app.use('/lazyload', express.static('node_modules/lazyloadjs/build', { maxAge: '1d' }));
app.use('/bootstrap', express.static('node_modules/bootstrap/dist', { maxAge: '1d' }));
app.use('/babel-standalone.min.js', express.static('node_modules/babel-standalone/babel.min.js'));

app.get('/', function (req, res) {
  console.log('Request', req.ip);
  const statuses = cache.get('statuses');
  if (statuses == null) {
    console.warn('Results cache miss');
    return res.status(503).render('error');
  }
  res.set({ 'Cache-Control': 'public, max-age=' + ms('1m') });
  res.render('index', {
    statuses: statuses
  });
});

app.get('/playground', function(req, res) {
  res.set({ 'Cache-Control': 'public, max-age=' + ms('1h') });
  res.render('playground');
});

app.get('/loading', function (req, res) {
  res.set({ 'Cache-Control': 'public, max-age=' + ms('1h') });
  res.render('loading');
});

app.get('/:id_str', function (req, res) {
  const status = cache.get(req.params.id_str);
  if (status == null) {
    console.warn('Item cache miss');
    return res.status(404).render('error');
  }
  res.set({ 'Cache-Control': 'public, max-age=' + ms('1h') });
  res.render('item', status);
});

const updateSearchResults = () => {
  console.log('Updating search results');
  client.get('search/tweets', {q: searchTerm}, function(error, tweets, response) {
    if (error) {
      return console.warn(error);
    }
    console.log('Search completed', tweets.statuses.length);
    const statuses = tweets.statuses.map(function(status) {
        const cached = cache.get(status.id_str);
        if (cached != null) {
          return cached;
        }
        const es6 = status.entities.urls.reduce(function(text, url) {
          return text.substring(0, url.indices[0]) + text.substring(url.indices[1]);
        }, status.text);
        // const es6 = example;
        let es5 = null;
        try {
           es5 = babel.transform(es6, babelOptions).code;
        } catch (e) {
          console.log('Failed to transform', status.id_str, e);
        }
        const processed = Object.assign({}, status, {
          es5: es5,
          es6: es6
        });
        cache.set(status.id_str, processed);
        return processed;
      });
    const validStatuses = statuses.filter(function(status) {
      return status.es5;
    });
    cache.set('statuses', validStatuses);
    console.log('Search results updated', validStatuses.length);
  });
}
updateSearchResults();
setInterval(updateSearchResults, ms('30s'));

app.listen(3000);
<|endoftext|>


<commit_before>/*!
 * node-tvdb
 *
 * Node.js library for accessing TheTVDB API at <https://api.thetvdb.com/swagger>
 *
 * Copyright (c) 2014-2016 Edward Wellbrook <edwellbrook@gmail.com>
 * MIT Licensed
 */

'use strict';

const url = require('url');
const request = require('node-fetch');
const flatten = require('lodash/flatten');

const BASE_URL = 'https://api.thetvdb.com';
const LIB_VERSION = require('./package.json').version;
const API_VERSION = 'v2.1.1';
const AV_HEADER = `application/vnd.thetvdb.${API_VERSION}`;

const DEFAULT_OPTS = {
    getAllPages: true,
    headers: {
        'User-Agent': `node-tvdb/${LIB_VERSION} (+https://github.com/edwellbrook/node-tvdb)`
    }
};

//
// API Client
//

class Client {

    /**
     * @param {String} apiKey
     * @param {String} [language]
     */

    constructor(apiKey, language = 'en') {
        if (!apiKey) {
            throw new Error('API key is required');
        }

        this.apiKey = apiKey;
        this.language = language;

        // store and manage auth token
        let tokenPromise = undefined;

        this.getToken = function() {
            if (tokenPromise === undefined) {
                tokenPromise = logIn(this.apiKey);
            }

            return tokenPromise;
        };
    }

    /**
     * Get available languages useable by TheTVDB API
     *
     * ``` javascript
     * tvdb.getLanguages()
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Object}  [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Languages/get_languages
     * @public
     */

    getLanguages(opts = {}) {
        return this.sendRequest('languages', opts);
    }

    /**
     * Get episode by episode id
     *
     * ``` javascript
     * tvdb.getEpisodeById(4768125)
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Number|String} episodeId
     * @param   {Object}        [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Episodes/get_episodes_id
     * @public
     */

    getEpisodeById(episodeId, opts = {}) {
        return this.sendRequest(`episodes/${episodeId}`, opts);
    }

    /**
     * Get all episodes by series id
     *
     * ``` javascript
     * tvdb.getEpisodesBySeriesId(153021)
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Number|String} seriesId
     * @param   {Object}        [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Series/get_series_id_episodes
     * @public
     */

    getEpisodesBySeriesId(seriesId, opts = {}) {
        return this.sendRequest(`series/${seriesId}/episodes`, opts);
    }

    /**
     * Get basic series information by id
     *
     * ``` javascript
     * tvdb.getSeriesById(73255)
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Number|String} seriesId
     * @param   {Object}        [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Series/get_series_id
     * @public
     */

    getSeriesById(seriesId, opts = {}) {
        return this.sendRequest(`series/${seriesId}`, opts);
    }

    /**
     * Get series episode by air date
     *
     * ``` javascript
     * tvdb.getEpisodeByAirDate(153021, '2011-10-03')
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Number|String} seriesId
     * @param   {String}        airDate
     * @param   {Object}        [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Series/get_series_id_episodes_query
     * @public
     */

    getEpisodesByAirDate(seriesId, airDate, opts = {}) {
        return this.sendRequest(`series/${seriesId}/episodes/query?firstAired=${airDate}`, opts);
    }

    /**
     * Get basic series information by name
     *
     * ``` javascript
     * tvdb.getSeriesByName('Breaking Bad')
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {String}  name
     * @param   {Object}  [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Search/get_search_series
     * @public
     */

    getSeriesByName(name, opts = {}) {
        return this.sendRequest(`search/series?name=${name}`, opts);
    }

    /**
     * Get series actors by series id
     *
     * ``` javascript
     * tvdb.getActors(73255)
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Number|String} seriesId
     * @param   {Object}        [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Series/get_series_id_actors
     * @public
     */

    getActors(seriesId, opts = {}) {
        return this.sendRequest(`series/${seriesId}/actors`, opts);
    }

    /**
     * Get basic series information by imdb id
     *
     * ``` javascript
     * tvdb.getSeriesByImdbId('tt0903747')
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {String}  imdbId
     * @param   {Object}  [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Search/get_search_series
     * @public
     */

    getSeriesByImdbId(imdbId, opts = {}) {
        return this.sendRequest(`search/series?imdbId=${imdbId}`, opts);
    }

    /**
     * Get basic series information by zap2it id
     *
     * ``` javascript
     * tvdb.getSeriesByZap2ItId('EP00018693')
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {String}  zap2ItId
     * @param   {Object}  [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Search/get_search_series
     * @public
     */

    getSeriesByZap2ItId(zap2ItId, opts = {}) {
        return this.sendRequest(`search/series?zap2itId=${zap2ItId}`, opts);
    }

    /**
     * Get series banner by series id
     *
     * ``` javascript
     * tvdb.getSeriesBanner(73255)
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Number|String} seriesId
     * @param   {Object}        [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Series/get_series_id_filter
     * @public
     */

    getSeriesBanner(seriesId, opts = {}) {
        return this.sendRequest(`series/${seriesId}/filter?keys=banner`, opts)
            .then(response => response.banner);
    }

    /**
     * Get a list of series updated since a given unix timestamp (and, if given,
     * between a second timestamp).
     *
     * ``` javascript
     * tvdb.getUpdates(1400611370, 1400621370)
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Number}  fromTime - timestamp to get series updates from
     * @param   {Number}  [toTime] - timestamp to get series updates to
     * @param   {Object}  [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Updates/get_updated_query
     * @public
     */

    getUpdates(fromTime, toTime, opts = {}) {
        let uri = `updated/query?fromTime=${fromTime}`;
        if (toTime) {
            uri += `&toTime=${toTime}`;
        }
        return this.sendRequest(uri, opts);
    }

    /**
     * Get series and episode information by series id. Helper for calling
     * `getSeriesById` and `getEpisodesBySeriesId` at the same time.
     *
     * ``` javascript
     * tvdb.getSeriesAllById(73255)
     *     .then(response => {
     *         console.log(response); // response contains series data (e.g. `response.id`, `response.seriesName`)
     *         console.log(response.episodes); // response contains an array of episodes
     *     })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Number|String} seriesId
     * @param   {Object}        [opts] - additional options for request
     * @returns {Promise}
     *
     * @public
     */

    getSeriesAllById(seriesId, opts = {}) {
        return Promise.all([
            this.getSeriesById(seriesId, opts),
            this.getEpisodesBySeriesId(seriesId, opts)
        ])
        .then(results => {
            let series = results[0];
            series.episodes = results[1];
            return series;
        });
    }

    /**
    * Runs a get request with the given options, useful for running custom requests
    *
    * ``` javascript
    * tvdb.sendRequest('custom/endpoint', { custom: 'options' })
    *     .then(response => { handle response })
    *     .catch(error => { handle error });
    * ```
    *
    * @param   {String}  path   - path for http resource
    * @param   {Object}  [opts] - additional options for request
    * @returns {Promise}
    *
    * @public
    */

    sendRequest(path, opts = {}) {
        const options = Object.assign({}, DEFAULT_OPTS, opts);
        const headers = Object.assign({
            'Accept':          AV_HEADER,
            'Accept-language': options.lang || this.language
        }, options.headers);

        return this.getToken()
            .then(token => {
                headers['Authorization'] = `Bearer ${token}`;

                return request(`${BASE_URL}/${path}`, { headers: headers });
            })
            .then(res => res.json())
            .then(res => checkError(res))
            .then(res => getNextPages(this, res, path, options))
            .then(res => res.data);
    }

}

/**
 * Returns the next page of a paged response.
 *
 * @param   {TVDB}    client   TVDB client to run next request with
 * @param   {Object}  res      response from previous request
 * @param   {String}  token    auth token for request
 * @param   {String}  path     path for previous request
 * @param   {String}  language
 * @returns {Promise}
 * @private
 */

function getNextPages(client, res, path, opts) {
    if (!hasNextPage(res) || !opts.getAllPages) {
        return Promise.resolve(res);
    }

    let urlObj = url.parse(path, true);
    urlObj.query.page = res.links.next;

    // remove urlObj.search to force url.format() to use urlObj.query
    urlObj.search = undefined;

    let newPath = url.format(urlObj);

    return client.sendRequest(newPath, opts)
        .then(nextRes => [res.data, nextRes])
        .then(dataArr => {
            return { data: flatten(dataArr) };
        });
}

/**
 * Check response for error. Return a rejected promise if there's an error
 * otherwise resolve the response
 *
 * @param   {Object}  json JSON response
 * @returns {Promise}
 * @private
 */

function checkError(json) {
    if (json.Error) {
        return Promise.reject(new Error(json.Error));
    }

    return Promise.resolve(json);
}

/**
 * Perform login flow with given API Key
 *
 * @param   {String}  apiKey
 * @returns {Promise}
 * @private
 */

function logIn(apiKey) {
    const opts = {
        method: 'POST',
        body: JSON.stringify({ apikey: apiKey }),
        headers: {
            'Accept':       AV_HEADER,
            'Content-Type': 'application/json'
        }
    };

    return request(`${BASE_URL}/login`, opts)
        .then(res => res.json())
        .then(res => checkError(res))
        .then(json => json.token);
}

/**
 * Returns true if the response has additional pages, otherwise returns
 * false
 *
 * @param   {Object}  response
 * @returns {Boolean}
 * @private
 */

function hasNextPage(response) {
    return response && response.links && response.links.next;
}

//
// Exports
//

module.exports = Client;
<commit_msg>Tidy doc comments<commit_after>/*!
 * node-tvdb
 *
 * Node.js library for accessing TheTVDB API at <https://api.thetvdb.com/swagger>
 *
 * Copyright (c) 2014-2016 Edward Wellbrook <edwellbrook@gmail.com>
 * MIT Licensed
 */

'use strict';

const url = require('url');
const request = require('node-fetch');
const flatten = require('lodash/flatten');

const BASE_URL = 'https://api.thetvdb.com';
const LIB_VERSION = require('./package.json').version;
const API_VERSION = 'v2.1.1';
const AV_HEADER = `application/vnd.thetvdb.${API_VERSION}`;

const DEFAULT_OPTS = {
    getAllPages: true,
    headers: {
        'User-Agent': `node-tvdb/${LIB_VERSION} (+https://github.com/edwellbrook/node-tvdb)`
    }
};

//
// API Client
//

class Client {

    /**
     * @param {String} apiKey
     * @param {String} [language]
     */

    constructor(apiKey, language = 'en') {
        if (!apiKey) {
            throw new Error('API key is required');
        }

        this.apiKey = apiKey;
        this.language = language;

        // store and manage auth token
        let tokenPromise = undefined;

        this.getToken = function() {
            if (tokenPromise === undefined) {
                tokenPromise = logIn(this.apiKey);
            }

            return tokenPromise;
        };
    }

    /**
     * Get available languages useable by TheTVDB API
     *
     * ``` javascript
     * tvdb.getLanguages()
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Object}  [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Languages/get_languages
     * @public
     */

    getLanguages(opts = {}) {
        return this.sendRequest('languages', opts);
    }

    /**
     * Get episode by episode id
     *
     * ``` javascript
     * tvdb.getEpisodeById(4768125)
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Number|String} episodeId
     * @param   {Object}        [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Episodes/get_episodes_id
     * @public
     */

    getEpisodeById(episodeId, opts = {}) {
        return this.sendRequest(`episodes/${episodeId}`, opts);
    }

    /**
     * Get all episodes by series id
     *
     * ``` javascript
     * tvdb.getEpisodesBySeriesId(153021)
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Number|String} seriesId
     * @param   {Object}        [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Series/get_series_id_episodes
     * @public
     */

    getEpisodesBySeriesId(seriesId, opts = {}) {
        return this.sendRequest(`series/${seriesId}/episodes`, opts);
    }

    /**
     * Get basic series information by id
     *
     * ``` javascript
     * tvdb.getSeriesById(73255)
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Number|String} seriesId
     * @param   {Object}        [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Series/get_series_id
     * @public
     */

    getSeriesById(seriesId, opts = {}) {
        return this.sendRequest(`series/${seriesId}`, opts);
    }

    /**
     * Get series episode by air date
     *
     * ``` javascript
     * tvdb.getEpisodeByAirDate(153021, '2011-10-03')
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Number|String} seriesId
     * @param   {String}        airDate
     * @param   {Object}        [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Series/get_series_id_episodes_query
     * @public
     */

    getEpisodesByAirDate(seriesId, airDate, opts = {}) {
        return this.sendRequest(`series/${seriesId}/episodes/query?firstAired=${airDate}`, opts);
    }

    /**
     * Get basic series information by name
     *
     * ``` javascript
     * tvdb.getSeriesByName('Breaking Bad')
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {String}  name
     * @param   {Object}  [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Search/get_search_series
     * @public
     */

    getSeriesByName(name, opts = {}) {
        return this.sendRequest(`search/series?name=${name}`, opts);
    }

    /**
     * Get series actors by series id
     *
     * ``` javascript
     * tvdb.getActors(73255)
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Number|String} seriesId
     * @param   {Object}        [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Series/get_series_id_actors
     * @public
     */

    getActors(seriesId, opts = {}) {
        return this.sendRequest(`series/${seriesId}/actors`, opts);
    }

    /**
     * Get basic series information by imdb id
     *
     * ``` javascript
     * tvdb.getSeriesByImdbId('tt0903747')
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {String}  imdbId
     * @param   {Object}  [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Search/get_search_series
     * @public
     */

    getSeriesByImdbId(imdbId, opts = {}) {
        return this.sendRequest(`search/series?imdbId=${imdbId}`, opts);
    }

    /**
     * Get basic series information by zap2it id
     *
     * ``` javascript
     * tvdb.getSeriesByZap2ItId('EP00018693')
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {String}  zap2ItId
     * @param   {Object}  [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Search/get_search_series
     * @public
     */

    getSeriesByZap2ItId(zap2ItId, opts = {}) {
        return this.sendRequest(`search/series?zap2itId=${zap2ItId}`, opts);
    }

    /**
     * Get series banner by series id
     *
     * ``` javascript
     * tvdb.getSeriesBanner(73255)
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Number|String} seriesId
     * @param   {Object}        [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Series/get_series_id_filter
     * @public
     */

    getSeriesBanner(seriesId, opts = {}) {
        return this.sendRequest(`series/${seriesId}/filter?keys=banner`, opts)
            .then(response => response.banner);
    }

    /**
     * Get a list of series updated since a given unix timestamp (and, if given,
     * between a second timestamp).
     *
     * ``` javascript
     * tvdb.getUpdates(1400611370, 1400621370)
     *     .then(response => { handle response })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Number}  fromTime - timestamp to get series updates from
     * @param   {Number}  [toTime] - timestamp to get series updates to
     * @param   {Object}  [opts] - additional options for request
     * @returns {Promise}
     *
     * @see     https://api.thetvdb.com/swagger#!/Updates/get_updated_query
     * @public
     */

    getUpdates(fromTime, toTime, opts = {}) {
        let uri = `updated/query?fromTime=${fromTime}`;
        if (toTime) {
            uri += `&toTime=${toTime}`;
        }
        return this.sendRequest(uri, opts);
    }

    /**
     * Get series and episode information by series id. Helper for calling
     * `getSeriesById` and `getEpisodesBySeriesId` at the same time.
     *
     * ``` javascript
     * tvdb.getSeriesAllById(73255)
     *     .then(response => {
     *         console.log(response); // response contains series data (e.g. `response.id`, `response.seriesName`)
     *         console.log(response.episodes); // response contains an array of episodes
     *     })
     *     .catch(error => { handle error });
     * ```
     *
     * @param   {Number|String} seriesId
     * @param   {Object}        [opts] - additional options for request
     * @returns {Promise}
     *
     * @public
     */

    getSeriesAllById(seriesId, opts = {}) {
        return Promise.all([
            this.getSeriesById(seriesId, opts),
            this.getEpisodesBySeriesId(seriesId, opts)
        ])
        .then(results => {
            let series = results[0];
            series.episodes = results[1];
            return series;
        });
    }

    /**
    * Runs a get request with the given options, useful for running custom
    * requests
    *
    * ``` javascript
    * tvdb.sendRequest('custom/endpoint', { custom: 'options' })
    *     .then(response => { handle response })
    *     .catch(error => { handle error });
    * ```
    *
    * @param   {String}  path   - path for http resource
    * @param   {Object}  [opts] - additional options for request
    * @returns {Promise}
    *
    * @public
    */

    sendRequest(path, opts = {}) {
        const options = Object.assign({}, DEFAULT_OPTS, opts);
        const headers = Object.assign({
            'Accept':          AV_HEADER,
            'Accept-language': options.lang || this.language
        }, options.headers);

        return this.getToken()
            .then(token => {
                headers['Authorization'] = `Bearer ${token}`;

                return request(`${BASE_URL}/${path}`, { headers: headers });
            })
            .then(res => res.json())
            .then(res => checkError(res))
            .then(res => getNextPages(this, res, path, options))
            .then(res => res.data);
    }

}

/**
 * Returns the next page of a paged response.
 *
 * @param   {TVDB}    client   TVDB client to run next request with
 * @param   {Object}  res      response from previous request
 * @param   {String}  path     path from previous request
 * @param   {Object}  opts     additional options from previous request
 * @returns {Promise}
 * @private
 */

function getNextPages(client, res, path, opts) {
    if (!hasNextPage(res) || !opts.getAllPages) {
        return Promise.resolve(res);
    }

    let urlObj = url.parse(path, true);
    urlObj.query.page = res.links.next;

    // remove urlObj.search to force url.format() to use urlObj.query
    urlObj.search = undefined;

    let newPath = url.format(urlObj);

    return client.sendRequest(newPath, opts)
        .then(nextRes => [res.data, nextRes])
        .then(dataArr => {
            return { data: flatten(dataArr) };
        });
}

/**
 * Check response for error. Return a rejected promise if there's an error
 * otherwise resolve the response
 *
 * @param   {Object}  json JSON response
 * @returns {Promise}
 * @private
 */

function checkError(json) {
    if (json.Error) {
        return Promise.reject(new Error(json.Error));
    }

    return Promise.resolve(json);
}

/**
 * Perform login flow with given API Key
 *
 * @param   {String}  apiKey
 * @returns {Promise}
 * @private
 */

function logIn(apiKey) {
    const opts = {
        method: 'POST',
        body: JSON.stringify({ apikey: apiKey }),
        headers: {
            'Accept':       AV_HEADER,
            'Content-Type': 'application/json'
        }
    };

    return request(`${BASE_URL}/login`, opts)
        .then(res => res.json())
        .then(res => checkError(res))
        .then(json => json.token);
}

/**
 * Returns true if the response has additional pages, otherwise returns false
 *
 * @param   {Object}  response
 * @returns {Boolean}
 * @private
 */

function hasNextPage(response) {
    return response && response.links && response.links.next;
}

//
// Exports
//

module.exports = Client;
<|endoftext|>