<commit_before>'use strict';

var fetchUrl = require('fetch').fetchUrl;
var packageInfo = require('../package.json');
var httpStatusCodes = require('./http.json');
var urllib = require('url');
var mime = require('mime');

// Expose to the world
module.exports.resolve = resolve;
module.exports.removeParams = removeParams;

/**
 * Resolves an URL by stepping through all redirects
 *
 * @param {String} url The URL to be checked
 * @param {Object} options Optional options object
 * @param {Function} callback Callback function with error and url
 */
function resolve(url, options, callback) {
    var urlOptions = {};

    if (typeof options == 'function' && !callback) {
        callback = options;
        options = undefined;
    }
    options = options || {};

    urlOptions.method = options.method ||  'HEAD';
    urlOptions.disableGzip = true; // no need for gzipping with HEAD
    urlOptions.asyncDnsLoookup = true;
    urlOptions.timeout = options.timeout ||  10000;
    urlOptions.userAgent = options.userAgent ||  (packageInfo.name + '/' + packageInfo.version + ' (+' + packageInfo.homepage + ')');
    urlOptions.removeParams = [].concat(options.removeParams ||  [/^utm_/, 'ref']);
    urlOptions.agent = options.agent || false;
    urlOptions.rejectUnauthorized = false;
    urlOptions.headers = options.headers || {};

    fetchUrl(url, urlOptions, function(error, meta) {
        var err, url;
        if (error) {
            err = new Error(error.message || error);
            err.statusCode = 0;
            return callback(err);
        }
        if (meta.status != 200) {
            err = new Error('Server responded with ' + meta.status + ' ' + (httpStatusCodes[meta.status] || 'Invalid request'));
            err.statusCode = meta.status;
            return callback(err);
        }
        url = meta.finalUrl;

        if (urlOptions.removeParams && urlOptions.removeParams.length) {
            url = removeParams(url, urlOptions.removeParams);
        }

        var fileParams = detectFileParams(meta);

        return callback(null, url, fileParams.filename, fileParams.contentType);
    });
}

function detectFileParams(meta) {
    var urlparts = urllib.parse(meta.finalUrl);
    var filename = (urlparts.pathname || '').split('/').pop();
    var contentType = (meta.responseHeaders['content-type'] || 'application/octet-stream').toLowerCase().split(';').shift().trim();
    var fileParts;
    var extension = '';
    var contentExtension;

    (meta.responseHeaders['content-disposition'] || '').split(';').forEach(function(line) {
        var parts = line.trim().split('='),
            key = parts.shift().toLowerCase().trim();
        if (key == 'filename') {
            filename = parts.join('=').trim();
        }
    });

    if (contentType == 'application/octet-stream') {
        contentType = mime.lookup(filename) || 'application/octet-stream';
    } else {
        fileParts = filename.split('.');
        if (fileParts.length > 1) {
            extension = fileParts.pop().toLowerCase();
        }
        contentExtension = mime.extension(contentType);
        if (contentExtension && extension != contentExtension) {
            extension = contentExtension;
        }
        if (extension) {
            if (!fileParts.length ||  (fileParts.length == 1 && !fileParts[0])) {
                fileParts = ['index'];
            }
            fileParts.push(extension);
        }
        filename = fileParts.join('.');
    }

    return {
        filename: filename,
        contentType: contentType
    };
}

/**
 * Removes matching GET params from an URL
 *
 * @param {String} url URL to be checked
 * @param {Array} params An array of key matches to be removed
 * @return {String} URL
 */
function removeParams(url, params) {
    var parts, query = {},
        deleted = false;

    parts = urllib.parse(url, true, true);
    delete parts.search;

    if (parts.query) {
        Object.keys(parts.query).forEach(function(key) {
            for (var i = 0, len = params.length; i < len; i++) {
                if (params[i] instanceof RegExp && key.match(params[i])) {
                    deleted = true;
                    return;
                } else if (key == params[i]) {
                    deleted = true;
                    return;
                }
            }
            query[key] = parts.query[key];
        });
        parts.query = query;
    }

    return deleted ? urllib.format(parts) : url;
}<commit_msg>custom maxRedirects<commit_after>'use strict';

var fetchUrl = require('fetch').fetchUrl;
var packageInfo = require('../package.json');
var httpStatusCodes = require('./http.json');
var urllib = require('url');
var mime = require('mime');

// Expose to the world
module.exports.resolve = resolve;
module.exports.removeParams = removeParams;

/**
 * Resolves an URL by stepping through all redirects
 *
 * @param {String} url The URL to be checked
 * @param {Object} options Optional options object
 * @param {Function} callback Callback function with error and url
 */
function resolve(url, options, callback) {
    var urlOptions = {};

    if (typeof options == 'function' && !callback) {
        callback = options;
        options = undefined;
    }
    options = options || {};

    urlOptions.method = options.method ||  'HEAD';
    urlOptions.disableGzip = true; // no need for gzipping with HEAD
    urlOptions.asyncDnsLoookup = true;
    urlOptions.timeout = options.timeout ||  10000;
    urlOptions.userAgent = options.userAgent ||  (packageInfo.name + '/' + packageInfo.version + ' (+' + packageInfo.homepage + ')');
    urlOptions.removeParams = [].concat(options.removeParams ||  [/^utm_/, 'ref']);
    urlOptions.agent = options.agent || false;
    urlOptions.rejectUnauthorized = false;
    urlOptions.headers = options.headers || {};
    urlOptions.maxRedirects = options.maxRedirects || 10;

    fetchUrl(url, urlOptions, function(error, meta) {
        var err, url;
        if (error) {
            err = new Error(error.message || error);
            err.statusCode = 0;
            return callback(err);
        }
        if (meta.status != 200) {
            err = new Error('Server responded with ' + meta.status + ' ' + (httpStatusCodes[meta.status] || 'Invalid request'));
            err.statusCode = meta.status;
            return callback(err);
        }
        url = meta.finalUrl;

        if (urlOptions.removeParams && urlOptions.removeParams.length) {
            url = removeParams(url, urlOptions.removeParams);
        }

        var fileParams = detectFileParams(meta);

        return callback(null, url, fileParams.filename, fileParams.contentType);
    });
}

function detectFileParams(meta) {
    var urlparts = urllib.parse(meta.finalUrl);
    var filename = (urlparts.pathname || '').split('/').pop();
    var contentType = (meta.responseHeaders['content-type'] || 'application/octet-stream').toLowerCase().split(';').shift().trim();
    var fileParts;
    var extension = '';
    var contentExtension;

    (meta.responseHeaders['content-disposition'] || '').split(';').forEach(function(line) {
        var parts = line.trim().split('='),
            key = parts.shift().toLowerCase().trim();
        if (key == 'filename') {
            filename = parts.join('=').trim();
        }
    });

    if (contentType == 'application/octet-stream') {
        contentType = mime.lookup(filename) || 'application/octet-stream';
    } else {
        fileParts = filename.split('.');
        if (fileParts.length > 1) {
            extension = fileParts.pop().toLowerCase();
        }
        contentExtension = mime.extension(contentType);
        if (contentExtension && extension != contentExtension) {
            extension = contentExtension;
        }
        if (extension) {
            if (!fileParts.length ||  (fileParts.length == 1 && !fileParts[0])) {
                fileParts = ['index'];
            }
            fileParts.push(extension);
        }
        filename = fileParts.join('.');
    }

    return {
        filename: filename,
        contentType: contentType
    };
}

/**
 * Removes matching GET params from an URL
 *
 * @param {String} url URL to be checked
 * @param {Array} params An array of key matches to be removed
 * @return {String} URL
 */
function removeParams(url, params) {
    var parts, query = {},
        deleted = false;

    parts = urllib.parse(url, true, true);
    delete parts.search;

    if (parts.query) {
        Object.keys(parts.query).forEach(function(key) {
            for (var i = 0, len = params.length; i < len; i++) {
                if (params[i] instanceof RegExp && key.match(params[i])) {
                    deleted = true;
                    return;
                } else if (key == params[i]) {
                    deleted = true;
                    return;
                }
            }
            query[key] = parts.query[key];
        });
        parts.query = query;
    }

    return deleted ? urllib.format(parts) : url;
}<|endoftext|>


<commit_before>var nc = require('ncurses'),
        http = require('http'),
          win = new nc.Window(),
          config = require('./config.js'),
            request = require('superagent');
var data = {
    username:'arscanable',
    level:1,
    health:45,
    healthMax:50,
    exp: 16,
    expMax: 20,
    gold: 5,
    silver: 10,
    habits: [
        {name:'1 Hour Productive Work',up:1,down:0},
        ],
    daily: [
        {name:'Go to Gym',done:0},
        ],
    todos: [
        {name:'Ring Insurance', done:0},
    ],
    rewards: [   /* NOT IMPLEMENTED YET */
        { name: 'Leather Armor', 'price': 30, description: 'Helps with stuff'},
        ],

};
var refresh =  function(){
        request.get(config.apiurl + "/user").set('Accept', 'application/json').set('X-API-User', config.apiuser).set('X-API-Key', config.apitoken).end(function(res){
        data.username = res.body.auth.local.username;
        data.level = res.body.stats.lvl;
        data.health = Math.ceil(res.body.stats.hp);
        data.healthMax=50;
        data.exp = Math.floor(res.body.stats.exp);
        data.expMax = 200;
        data.habits = [];
        data.daily = [];
        data.todos = [];
        for(var i = 0; i<res.body.habitIds.length;i++){
            data.habits[i] = {};
            data.habits[i].id = res.body.habitIds[i]
            data.habits[i].name = res.body.tasks[res.body.habitIds[i]].text;
            data.habits[i].up = res.body.tasks[res.body.habitIds[i]].up;
            data.habits[i].down = res.body.tasks[res.body.habitIds[i]].down;
        }
        for(var i = 0; i<res.body.dailyIds.length;i++){
            data.daily[i] = {};
            data.daily[i].id = res.body.dailyIds[i]
            data.daily[i].name = res.body.tasks[res.body.dailyIds[i]].text;
            data.daily[i].up = res.body.tasks[res.body.dailyIds[i]].up;
            data.daily[i].down = res.body.tasks[res.body.dailyIds[i]].down;
            data.daily[i].done = res.body.tasks[res.body.dailyIds[i]].completed;
        }
        for(var i = 0; i<res.body.todoIds.length;i++){
            data.todos[i] = {};
            data.todos[i].id = res.body.dailyIds[i]
            data.todos[i].name = res.body.tasks[res.body.todoIds[i]].text;
            data.todos[i].up = res.body.tasks[res.body.todoIds[i]].up;
            data.todos[i].down = res.body.tasks[res.body.todoIds[i]].down;
            data.todos[i].done = res.body.tasks[res.body.todoIds[i]].completed;
        }
        drawFn();
        });

    }

var addtask = function(task){

//        request.get(config.apiurl + "/user").set('Accept', 'application/json').set('X-API-User', config.apiuser).set('X-API-Key', config.apitoken).end(function(res){

}

refresh();
setInterval(refresh,10000);

var items = [];

nc.colorPair(1,nc.colors.BLACK,nc.colors.WHITE);
nc.colorPair(2,nc.colors.WHITE,nc.colors.BLACK);
nc.colorPair(3,nc.colors.WHITE,nc.colors.CYAN);
var statusWindow = new nc.Window(7,nc.cols-4);
var currentIndex = 0;
var unsaved = false;

var drawFn = function(){
    items = [];

    /* draw the header */

    drawFooter(win,(unsaved?'unsaved':''));

        /* draw the status box */
    statusWindow.move(1,2);
    statusWindow.box();
    statusWindow.cursor(0,2);
    statusWindow.addstr(data.username);
    statusWindow.addstr(' [lvl ' + data.level + ']');
    statusWindow.refresh();
    drawBar(statusWindow,1,2,data.health,data.healthMax,'Health',2);
    drawBar(statusWindow,1,2,data.exp,data.expMax,'Exp',4);

    win.cursor(9,0);
    var habitsDone = 0;

    win.cursor(win.cury+1,0);
    for(var i = 0; i<data.habits.length;i++){
        win.cursor(win.cury+1,2);
        data.habits[i].cury = win.cury;
        data.habits[i].type = 'habits';
        items.push(data.habits[i]);
        win.addstr('[' + (data.habits[i].up - data.habits[i].down == 0?' ':'' + Math.abs(data.habits[i].up - data.habits[i].down)) + '] ' + data.habits[i].name.substr(0,win.width-5));

        if (data.habits[i].up - data.habits[i].down < 0){
            win.cursor(win.cury,3);
            win.addstr('-');
            //win.chgat(win.cury, 3, 1, nc.attrs.NORMAL, nc.colorPair(3));

        }
        habitsDone += data.habits[i].up;
        habitsDone -= data.habits[i].down;

    }
    win.cursor(win.cury-data.habits.length-1,0);
    drawHeader(win,"Habits [" + habitsDone + " today]");
    win.cursor(win.cury+data.habits.length+4,0);

    var dailyDone = 0;

    for(var i = 0; i<data.daily.length;i++){
        win.cursor(win.cury+1,2);
        data.daily[i].cury = win.cury;
        data.daily[i].type = 'daily';
        items.push(data.daily[i]);
        win.addstr('[' + (data.daily[i].done?'X':' ') + '] ' + data.daily[i].name.substr(0,win.width-5));
        dailyDone += data.daily[i].done;

    }
    win.cursor(win.cury-data.daily.length-1,0);
    drawHeader(win,"Daily [" + dailyDone + " complete]");
    win.cursor(win.cury+data.daily.length+4,0);

    var todosDone = 0;

    for(var i = 0; i<data.todos.length;i++){
        win.cursor(win.cury+1,2);
        data.todos[i].cury = win.cury;
        data.todos[i].type = 'todos';
        items.push(data.todos[i]);
        win.addstr('[' + (data.todos[i].done?'X':' ') + '] ' + data.todos[i].name.substr(0,win.width-5));
        todosDone += data.todos[i].done;
    }

    win.cursor(win.cury-data.todos.length-1,0);
    drawHeader(win,"Todos [" + todosDone + " completed today]");
    win.cursor(win.cury+data.todos.length+4,0);

    win.chgat(items[currentIndex].cury, 2, win.width-5, nc.attrs.STANDOUT, nc.colorPair(5));
    win.refresh();
}

//drawFn();

function drawHeader(mywin, title){
    mywin.hline(win.width-3, nc.ACS.HLINE);
    mywin.cursor(mywin.cury,0);
    mywin.addstr('  ');
    mywin.cursor(mywin.cury,3);
    mywin.addstr(' ' + title + ' ');
    mywin.cursor(mywin.cury,0);
}

var inputWindow = new nc.Window(1,nc.cols);
var mode = 'normal';
nc.showCursor = false;
inputWindow.move(win.height-1,0);
inputWindow.refresh();

inputWindow.on('inputChar', function (c, i) {

    if(mode == 'normal'){
        inputWindow.clear();

        if(i === 106){

            win.chgat(items[currentIndex].cury, 2, win.width-5, nc.attrs.NORMAL, nc.colorPair(0));
            currentIndex++;
            if(currentIndex > items.length -1){
                currentIndex = 0;
            }

            win.chgat(items[currentIndex].cury, 2, win.width-5, nc.attrs.STANDOUT, nc.colorPair(5));
            win.cursor(items[currentIndex].cury,2);
            win.refresh();

        } else if(i === 107){

            win.chgat(items[currentIndex].cury, 2, win.width-5, nc.attrs.NORMAL, nc.colorPair(0));
            currentIndex--;
            if(currentIndex < 0){
                currentIndex = items.length-1;
            }

            win.chgat(items[currentIndex].cury, 2, win.width-5, nc.attrs.STANDOUT, nc.colorPair(5));
            win.cursor(items[currentIndex].cury,2);
            win.refresh();

        } else if(i === 105){
            unsaved = true;
            if(items[currentIndex].type == 'daily' || items[currentIndex].type == 'todos'){
                items[currentIndex].done = 1;
                process.nextTick(drawFn);

            } else if(items[currentIndex].type == 'habits'){
                items[currentIndex].up += 1;
                process.nextTick(drawFn);
            }

        } else if(i === 120 || i == 32){
            if(items[currentIndex].type == 'daily' || items[currentIndex].type == 'todos'){
                unsaved = true;
                items[currentIndex].done = (items[currentIndex].done * -1) + 1;
                process.nextTick(drawFn);
            }

        } else if(i === 100){
            unsaved = true;
            if(items[currentIndex].type == 'daily' || items[currentIndex].type == 'todos'){
                items[currentIndex].done = 0;
                process.nextTick(drawFn);
            } else if(items[currentIndex].type == 'habits'){
                items[currentIndex].down += 1;
                process.nextTick(drawFn);
            }
        }  else if(i === 58){
            mode = 'command';
            inputWindow.inbuffer = '';
            nc.showCursor = true;

        }
    }

    if(mode == 'command'){
        if(i === 9){
            mode = 'normal';
            inputWindow.inbuffer = '';
            nc.showCursor = false;
            inputWindow.refresh();
        }else if(i === 330){

            var prev_x = inputWindow.curx;
            inputWindow.delch(inputWindow.height-1, inputWindow.curx);
            inputWindow.inbuffer = inputWindow.inbuffer.substring(0, inputWindow.curx-1) + inputWindow.inbuffer.substring(inputWindow.curx);
            inputWindow.cursor(inputWindow.height-1, prev_x);
            if(inputWindow.inbuffer.length == 0){
                mode = 'normal';
                nc.showCursor = false;
            }
            inputWindow.refresh();

        } else if (i === 127 && inputWindow.curx > 0) {
            var prev_x = inputWindow.curx-1;
            inputWindow.delch(inputWindow.height-1, prev_x);
            inputWindow.inbuffer = inputWindow.inbuffer.substring(0, prev_x) + inputWindow.inbuffer.substring(prev_x+1);
            inputWindow.cursor(inputWindow.height-1, prev_x);
            if(inputWindow.inbuffer.length == 0){
                mode = 'normal';
                nc.showCursor = false;
            }
            inputWindow.refresh();
        } else if (i === nc.keys.NEWLINE) {
            if (inputWindow.inbuffer.length) {

                if (inputWindow.inbuffer[0] === ':') {
                    var cmd = inputWindow.inbuffer.substring(1).split(' ', 1).join('').trim(),
                        args = inputWindow.inbuffer.substring(inputWindow.inbuffer.indexOf(cmd)+cmd.length+1).trim();
                    switch (cmd.toLowerCase()) {
                        case 'a':
                            if (args.length) {
                                data.todos.push({'name':args, done:0});
                                inputWindow.clear();
                                inputWindow.inbuffer = '';
                                process.nextTick(drawFn);
                                nc.showCursor = false;
                                mode = 'normal';
                                unsaved = true;

                            }
                            break;

                        case 'q':
                            nc.cleanup();
                            process.exit(0);
                            break;
                        default:
                            inputWindow.clear();
                            inputWindow.addstr('Unknown command: ' + cmd);
                            mode = 'normal';
                            nc.showCursor = false;

                    }
                }
            }

        } else if (i >= 32 && i <= 126 && inputWindow.curx < inputWindow.width-4) {
           inputWindow.echochar(i);
           inputWindow.inbuffer += c;
        }

    }
    inputWindow.refresh();

});

function drawFooter(mywin, state){
    mywin.cursor(mywin.height-2,0);
    mywin.clrtoeol();
    win.addstr("HabitRPG");
    mywin.addstr(mywin.height-2, mywin.width-(Math.min(state.length, mywin.width)), state, mywin.width);
    mywin.chgat(mywin.height-2, 0, mywin.width, nc.attrs.STANDOUT, nc.colorPair(5));
}

function drawBar(mywin, onColorPair, offColorPair, val, valMax, label, rowStart){
    var totalwidth = mywin.width - 6,
        onWidth = Math.floor(totalwidth * (val / valMax)),
        offWidth = totalwidth - onWidth,
        poststring = '' + val + '/' + valMax,
        paddedstring = label + Array(totalwidth - label.length + 1 - poststring.length).join(' ') + poststring;

    mywin.cursor(rowStart,3);
    mywin.attron(nc.colorPair(1));
    for (var i = 0; i < paddedstring.length; i++){
        if(i > onWidth){
            mywin.attroff(nc.colorPair(1));
            mywin.attron(nc.colorPair(2));
        }
        mywin.addstr(paddedstring.charAt(i));
    }
    mywin.attroff(nc.colorPair(2));
    mywin.refresh();
}
process.on('SIGWINCH',drawFn);
<commit_msg>added ability to add tasks, complete dailies and todos<commit_after>var nc = require('ncurses'),
        http = require('http'),
          win = new nc.Window(),
          config = require('./config.js'),
            request = require('superagent');
var data = {
    username:'arscanable',
    level:1,
    health:45,
    healthMax:50,
    exp: 16,
    expMax: 20,
    gold: 5,
    silver: 10,
    habits: [
        {name:'1 Hour Productive Work',up:1,down:0},
        ],
    daily: [
        {name:'Go to Gym',done:0},
        ],
    todos: [
        {name:'Ring Insurance', done:0},
    ],
    rewards: [   /* NOT IMPLEMENTED YET */
        { name: 'Leather Armor', 'price': 30, description: 'Helps with stuff'},
        ],

};
var refresh =  function(){
        request.get(config.apiurl + "/user").set('Accept', 'application/json').set('X-API-User', config.apiuser).set('X-API-Key', config.apitoken).end(function(res){
        data.username = res.body.auth.local.username;
        data.level = res.body.stats.lvl;
        data.health = Math.ceil(res.body.stats.hp);
        data.healthMax=50;
        data.exp = Math.floor(res.body.stats.exp);
        data.expMax = 200;
        data.habits = [];
        data.daily = [];
        data.todos = [];
        for(var i = 0; i<res.body.habitIds.length;i++){
            data.habits[i] = {};
            data.habits[i].id = res.body.habitIds[i]
            data.habits[i].name = res.body.tasks[res.body.habitIds[i]].text;
            data.habits[i].up = res.body.tasks[res.body.habitIds[i]].up;
            data.habits[i].down = res.body.tasks[res.body.habitIds[i]].down;
            // TODO: parse the array and figure out how many points in the last 24 hours
            //console.log(res.body.tasks[res.body.habitIds[i]].history);
            //process.exit(0);
        }
        for(var i = 0; i<res.body.dailyIds.length;i++){
            data.daily[i] = {};
            data.daily[i].id = res.body.dailyIds[i]
            data.daily[i].name = res.body.tasks[res.body.dailyIds[i]].text;
            data.daily[i].up = res.body.tasks[res.body.dailyIds[i]].up;
            data.daily[i].down = res.body.tasks[res.body.dailyIds[i]].down;
            data.daily[i].done = res.body.tasks[res.body.dailyIds[i]].completed;
        }
        for(var i = 0; i<res.body.todoIds.length;i++){
            data.todos[i] = {};
            data.todos[i].id = res.body.todoIds[i]
            data.todos[i].name = res.body.tasks[res.body.todoIds[i]].text;
            data.todos[i].up = res.body.tasks[res.body.todoIds[i]].up;
            data.todos[i].down = res.body.tasks[res.body.todoIds[i]].down;
            data.todos[i].done = res.body.tasks[res.body.todoIds[i]].completed;
        }
        drawFn();
        });

    }

var setTaskStatus = function(taskid, taskstatus){
    //console.log(taskid);
    //process.exit(0);

        request.put(config.apiurl + "/user/task/" + taskid).set('Accept', 'application/json').set('X-API-User', config.apiuser).set('X-API-Key', config.apitoken).send({completed:taskstatus}).end(function(res){
            refresh();
            //process.exit(0);

});
}

var doHabit = function(taskid, habitup){
    //console.log(taskid);
    //process.exit(0);

//        request.put(config.apiurl + "/user/task/" + taskid).set('Accept', 'application/json').set('X-API-User', config.apiuser).set('X-API-Key', config.apitoken).send({completed:taskstatus}).end(function(res){
 //           refresh();
            //process.exit(0);

//});
}

var createTask = function(tasktype, text){
        request.post(config.apiurl + "/user/task").set('Accept', 'application/json').set('X-API-User', config.apiuser).set('X-API-Key', config.apitoken).send({type:tasktype, text:text}).end(function(res){
            refresh();

});
}

refresh();
setInterval(refresh,10000);

var items = [];

nc.colorPair(1,nc.colors.BLACK,nc.colors.WHITE);
nc.colorPair(2,nc.colors.WHITE,nc.colors.BLACK);
nc.colorPair(3,nc.colors.WHITE,nc.colors.CYAN);
var statusWindow = new nc.Window(7,nc.cols-4);
var currentIndex = 0;
var unsaved = false;

var drawFn = function(){
    items = [];
    win.clear();

    /* draw the header */

    drawFooter(win,(unsaved?'unsaved':''));

        /* draw the status box */
    statusWindow.move(1,2);
    statusWindow.box();
    statusWindow.cursor(0,2);
    statusWindow.addstr(data.username);
    statusWindow.addstr(' [lvl ' + data.level + ']');
    statusWindow.refresh();
    drawBar(statusWindow,1,2,data.health,data.healthMax,'Health',2);
    drawBar(statusWindow,1,2,data.exp,data.expMax,'Exp',4);

    win.cursor(9,0);
    var habitsDone = 0;

    win.cursor(win.cury+1,0);
    for(var i = 0; i<data.habits.length;i++){
        win.cursor(win.cury+1,2);
        data.habits[i].cury = win.cury;
        data.habits[i].type = 'habits';
        items.push(data.habits[i]);
        win.addstr('[' + (data.habits[i].up - data.habits[i].down == 0?' ':'' + Math.abs(data.habits[i].up - data.habits[i].down)) + '] ' + data.habits[i].name.substr(0,win.width-5));

        if (data.habits[i].up - data.habits[i].down < 0){
            win.cursor(win.cury,3);
            win.addstr('-');
            //win.chgat(win.cury, 3, 1, nc.attrs.NORMAL, nc.colorPair(3));

        }
        habitsDone += data.habits[i].up;
        habitsDone -= data.habits[i].down;

    }
    win.cursor(win.cury-data.habits.length-1,0);
    drawHeader(win,"Habits [" + habitsDone + " today]");
    win.cursor(win.cury+data.habits.length+4,0);

    var dailyDone = 0;

    for(var i = 0; i<data.daily.length;i++){
        win.cursor(win.cury+1,2);
        data.daily[i].cury = win.cury;
        data.daily[i].type = 'daily';
        items.push(data.daily[i]);
        win.addstr('[' + (data.daily[i].done?'X':' ') + '] ' + data.daily[i].name.substr(0,win.width-5));
        dailyDone += data.daily[i].done;

    }
    win.cursor(win.cury-data.daily.length-1,0);
    drawHeader(win,"Daily [" + dailyDone + " complete]");
    win.cursor(win.cury+data.daily.length+4,0);

    var todosDone = 0;

    for(var i = 0; i<data.todos.length;i++){
        win.cursor(win.cury+1,2);
        data.todos[i].cury = win.cury;
        data.todos[i].type = 'todos';
        items.push(data.todos[i]);
        win.addstr('[' + (data.todos[i].done?'X':' ') + '] ' + data.todos[i].name.substr(0,win.width-5));
        todosDone += data.todos[i].done;
    }

    win.cursor(win.cury-data.todos.length-1,0);
    drawHeader(win,"Todos [" + todosDone + " completed today]");
    win.cursor(win.cury+data.todos.length+4,0);

    win.chgat(items[currentIndex].cury, 2, win.width-5, nc.attrs.STANDOUT, nc.colorPair(5));
    win.refresh();
}

//drawFn();

function drawHeader(mywin, title){
    mywin.hline(win.width-3, nc.ACS.HLINE);
    mywin.cursor(mywin.cury,0);
    mywin.addstr('  ');
    mywin.cursor(mywin.cury,3);
    mywin.addstr(' ' + title + ' ');
    mywin.cursor(mywin.cury,0);
}

var inputWindow = new nc.Window(1,nc.cols);
var mode = 'normal';
nc.showCursor = false;
inputWindow.move(win.height-1,0);
inputWindow.refresh();

inputWindow.on('inputChar', function (c, i) {

    if(mode == 'normal'){
        inputWindow.clear();

        if(i === 106){

            win.chgat(items[currentIndex].cury, 2, win.width-5, nc.attrs.NORMAL, nc.colorPair(0));
            currentIndex++;
            if(currentIndex > items.length -1){
                currentIndex = 0;
            }

            win.chgat(items[currentIndex].cury, 2, win.width-5, nc.attrs.STANDOUT, nc.colorPair(5));
            win.cursor(items[currentIndex].cury,2);
            win.refresh();

        } else if(i === 107){

            win.chgat(items[currentIndex].cury, 2, win.width-5, nc.attrs.NORMAL, nc.colorPair(0));
            currentIndex--;
            if(currentIndex < 0){
                currentIndex = items.length-1;
            }

            win.chgat(items[currentIndex].cury, 2, win.width-5, nc.attrs.STANDOUT, nc.colorPair(5));
            win.cursor(items[currentIndex].cury,2);
            win.refresh();

        } else if(i === 105){
            unsaved = true;
            if(items[currentIndex].type == 'daily' || items[currentIndex].type == 'todos'){
                items[currentIndex].done = 1;
                setTaskStatus(items[currentIndex].id,true);
                //process.nextTick(drawFn);

            } else if(items[currentIndex].type == 'habits'){
                items[currentIndex].up += 1;
                process.nextTick(drawFn);
            }

        } else if(i === 120 || i == 32){
            if(items[currentIndex].type == 'daily' || items[currentIndex].type == 'todos'){
                unsaved = true;
                items[currentIndex].done = (items[currentIndex].done * -1) + 1;
                setTaskStatus(items[currentIndex].id,items[currentIndex].done);
                //process.nextTick(drawFn);
            }

        } else if(i === 100){
            unsaved = true;
            if(items[currentIndex].type == 'daily' || items[currentIndex].type == 'todos'){
                items[currentIndex].done = 0;
                process.nextTick(drawFn);
            } else if(items[currentIndex].type == 'habits'){
                items[currentIndex].down += 1;
                process.nextTick(drawFn);
            }
        }  else if(i === 58){
            mode = 'command';
            inputWindow.inbuffer = '';
            nc.showCursor = true;

        }
    }

    if(mode == 'command'){
        if(i === 9){
            mode = 'normal';
            inputWindow.inbuffer = '';
            nc.showCursor = false;
            inputWindow.refresh();
        }else if(i === 330){

            var prev_x = inputWindow.curx;
            inputWindow.delch(inputWindow.height-1, inputWindow.curx);
            inputWindow.inbuffer = inputWindow.inbuffer.substring(0, inputWindow.curx-1) + inputWindow.inbuffer.substring(inputWindow.curx);
            inputWindow.cursor(inputWindow.height-1, prev_x);
            if(inputWindow.inbuffer.length == 0){
                mode = 'normal';
                nc.showCursor = false;
            }
            inputWindow.refresh();

        } else if (i === 127 && inputWindow.curx > 0) {
            var prev_x = inputWindow.curx-1;
            inputWindow.delch(inputWindow.height-1, prev_x);
            inputWindow.inbuffer = inputWindow.inbuffer.substring(0, prev_x) + inputWindow.inbuffer.substring(prev_x+1);
            inputWindow.cursor(inputWindow.height-1, prev_x);
            if(inputWindow.inbuffer.length == 0){
                mode = 'normal';
                nc.showCursor = false;
            }
            inputWindow.refresh();
        } else if (i === nc.keys.NEWLINE) {
            if (inputWindow.inbuffer.length) {

                if (inputWindow.inbuffer[0] === ':') {
                    var cmd = inputWindow.inbuffer.substring(1).split(' ', 1).join('').trim(),
                        args = inputWindow.inbuffer.substring(inputWindow.inbuffer.indexOf(cmd)+cmd.length+1).trim();
                    switch (cmd.toLowerCase()) {
                        case 't':
                            if (args.length) {
                                //data.todos.push({'name':args, done:0});
                                inputWindow.clear();
                                inputWindow.inbuffer = '';
                                //process.nextTick(drawFn);
                                nc.showCursor = false;
                                mode = 'normal';
                                unsaved = true;
                                createTask("todo",args);

                            }
                            break;
                        case 'h':
                            if (args.length) {
                                //data.todos.push({'name':args, done:0});
                                inputWindow.clear();
                                inputWindow.inbuffer = '';
                                //process.nextTick(drawFn);
                                nc.showCursor = false;
                                mode = 'normal';
                                unsaved = true;
                                createTask("habit",args);

                            }
                            break;
                        case 'd':
                            if (args.length) {
                                //data.todos.push({'name':args, done:0});
                                inputWindow.clear();
                                inputWindow.inbuffer = '';
                                //process.nextTick(drawFn);
                                nc.showCursor = false;
                                mode = 'normal';
                                unsaved = true;
                                createTask("daily",args);

                            }
                            break;

                        case 'q':
                            nc.cleanup();
                            process.exit(0);
                            break;
                        default:
                            inputWindow.clear();
                            inputWindow.addstr('Unknown command: ' + cmd);
                            mode = 'normal';
                            nc.showCursor = false;

                    }
                }
            }

        } else if (i >= 32 && i <= 126 && inputWindow.curx < inputWindow.width-4) {
           inputWindow.echochar(i);
           inputWindow.inbuffer += c;
        }

    }
    inputWindow.refresh();

});

function drawFooter(mywin, state){
    mywin.cursor(mywin.height-2,0);
    mywin.clrtoeol();
    win.addstr("HabitRPG");
    mywin.addstr(mywin.height-2, mywin.width-(Math.min(state.length, mywin.width)), state, mywin.width);
    mywin.chgat(mywin.height-2, 0, mywin.width, nc.attrs.STANDOUT, nc.colorPair(5));
}

function drawBar(mywin, onColorPair, offColorPair, val, valMax, label, rowStart){
    var totalwidth = mywin.width - 6,
        onWidth = Math.floor(totalwidth * (val / valMax)),
        offWidth = totalwidth - onWidth,
        poststring = '' + val + '/' + valMax,
        paddedstring = label + Array(totalwidth - label.length + 1 - poststring.length).join(' ') + poststring;

    mywin.cursor(rowStart,3);
    mywin.attron(nc.colorPair(1));
    for (var i = 0; i < paddedstring.length; i++){
        if(i > onWidth){
            mywin.attroff(nc.colorPair(1));
            mywin.attron(nc.colorPair(2));
        }
        mywin.addstr(paddedstring.charAt(i));
    }
    mywin.attroff(nc.colorPair(2));
    mywin.refresh();
}
process.on('SIGWINCH',drawFn);
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var _ = require('lodash');
var thriftrw = require('thriftrw');
var TYPE = thriftrw.TYPE;
var assert = require('assert');
var util = require('util');

function AField(id, name, type, required, defaultValue) {
    if (!(this instanceof AField)) {
        return new AField(id, name, type);
    }
    this.id = id;
    this.name = name;
    this.type = type;
    // TODO: handle required
    this.required = required || false;
    // TODO: handle defaultValue
    this.defaultValue = defaultValue || null;
}

function AStruct(fields) {
    if (!(this instanceof AStruct)) {
        return new AStruct(fields);
    }
    this.typeid = TYPE.STRUCT;
    this.fields = fields || [];
    this.fieldsById = _.indexBy(fields, 'id');
    this.fieldsByName = _.indexBy(fields, 'name');
}

AStruct.prototype.reify = function reify(tstruct) {
    var self = this;
    return _.reduce(tstruct.fields, function reduce(result, tfield) {
        var afield = self.fieldsById[tfield[1]];
        if (!afield) {
            return result;
        }
        if (afield.type.typeid !== tfield[0]) {
            throw new Error(
                'AStruct::reify expects field %d typeid %d; received %d',
                tfield[1], afield.type.typeid, tfield[0]);
        }
        result[afield.name] = afield.type.reify(tfield[2]);
        return result;
    }, {});
};

AStruct.prototype.uglify = function uglify(struct) {
    assert(_.isPlainObject(struct));
    var self = this;
    return _.reduce(struct, function reduce(tstruct, val, name) {
        var afield = self.fieldsByName[name];
        if (!afield) {
            throw new Error(util.format('unknown field name %s', name));
        }
        var tfield = [afield.type.typeid, afield.id, afield.type.uglify(val)];
        tstruct.fields.push(tfield);
        return tstruct;
    }, thriftrw.TStruct());
};

module.exports.AField = AField;
module.exports.AStruct = AStruct;
<commit_msg>verbose error in astruct::uglify<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var _ = require('lodash');
var thriftrw = require('thriftrw');
var TYPE = thriftrw.TYPE;
var assert = require('assert');
var util = require('util');

function AField(id, name, type, required, defaultValue) {
    if (!(this instanceof AField)) {
        return new AField(id, name, type);
    }
    this.id = id;
    this.name = name;
    this.type = type;
    // TODO: handle required
    this.required = required || false;
    // TODO: handle defaultValue
    this.defaultValue = defaultValue || null;
}

function AStruct(fields) {
    if (!(this instanceof AStruct)) {
        return new AStruct(fields);
    }
    this.typeid = TYPE.STRUCT;
    this.fields = fields || [];
    this.fieldsById = _.indexBy(fields, 'id');
    this.fieldsByName = _.indexBy(fields, 'name');
}

AStruct.prototype.reify = function reify(tstruct) {
    var self = this;
    return _.reduce(tstruct.fields, function reduce(result, tfield) {
        var afield = self.fieldsById[tfield[1]];
        if (!afield) {
            return result;
        }
        if (afield.type.typeid !== tfield[0]) {
            throw new Error(util.format(
                'AStruct::reify expects field %d typeid %d; received %d',
                tfield[1], afield.type.typeid, tfield[0]));
        }
        result[afield.name] = afield.type.reify(tfield[2]);
        return result;
    }, {});
};

AStruct.prototype.uglify = function uglify(struct) {
    assert(_.isPlainObject(struct));
    var self = this;
    return _.reduce(struct, function reduce(tstruct, val, name) {
        var afield = self.fieldsByName[name];
        if (!afield) {
            throw new Error(util.format('unknown field name %s', name));
        }
        try {
            var tfield = [afield.type.typeid, afield.id, afield.type.uglify(val)];
        } catch (e) {
            throw new Error(util.format(
                'AStruct: failed to uglify field %d typeid %d val %s innerError %s',
                afield.id, afield.type.typeid, util.inspect(val), e.message));
        }
        tstruct.fields.push(tfield);
        return tstruct;
    }, thriftrw.TStruct());
};

module.exports.AField = AField;
module.exports.AStruct = AStruct;
<|endoftext|>


<commit_before>import $ from 'jquery';
import d3 from 'd3';

// Color related utility functions go in this object
export const bnbColors = [
  '#9e0142',
  '#d53e4f',
  '#f46d43',
  '#fdae61',
  '#fee08b',
   '#ffffbf',
  '#e6f598',
  '#abdda4',
  '#66c2a5',
  '#3288bd',
  '#5e4fa2',
  '#8e0152',
  '#c51b7d',
  '#de77ae',
  '#f1b6da',
  '#fde0ef',
  '#f7f7f7',
  '#e6f5d0',
  '#b8e186',
  '#7fbc41',
  '#4d9221',
  '#276419',
];

const spectrums = {
  blue_white_yellow: [
    '#00d1c1',
    'white',
    '#ffb400',
  ],
  fire: [
    'white',
    'yellow',
    'red',
    'black',
  ],
  white_black: [
    'white',
    'black',
  ],
  black_white: [
    'black',
    'white',
  ],
  YlGnBu: [
    '#ffffd9',
    '#c7e9b4',
    '#41b6c4',
    '#225ea8',
    '#081d5',
    ],
};

export const category21 = (function () {
  // Color factory
  const seen = {};
  return function (s) {
    if (!s) {
      return;
    }
    let stringifyS = String(s);
    // next line is for superset series that should have the same color
    stringifyS = stringifyS.replace('---', '');
    if (seen[stringifyS] === undefined) {
      seen[stringifyS] = Object.keys(seen).length;
    }
    /* eslint consistent-return: 0 */
    return bnbColors[seen[stringifyS] % bnbColors.length];
  };
}());

export const colorScalerFactory = function (colors, data, accessor, country) {
  if (typeof country === 'undefined') { country = 'notmap'; }
  // Returns a linear scaler our of an array of color
  const map_colors;
  if (!Array.isArray(colors)) {
    /* eslint no-param-reassign: 0 */
    map_colors = spectrums[colors];
    colors = spectrums[colors];

  }
  let ext = [0, 1];
  if (data !== undefined) {
    ext = d3.extent(data, accessor);
  }
  console.log(ext);
  const points = [];
  const chunkSize = (ext[1] - ext[0]) / colors.length;
  $.each(colors, function (i) {
    points.push(i * chunkSize);
  });
  if (country == 'map'){
    console.log(ext)
    console.log(map_colors)
    //console.log(d3.scale.quantile().domain(points).range(colors));
    return d3.scale.quantile().domain(ext).range(map_colors);
  } else {
    return d3.scale.linear().domain(points).range(colors);
  }
  //return d3.scale.quantize().domain(points).range(colors);
};
<commit_msg>debugging<commit_after>import $ from 'jquery';
import d3 from 'd3';

// Color related utility functions go in this object
export const bnbColors = [
  '#9e0142',
  '#d53e4f',
  '#f46d43',
  '#fdae61',
  '#fee08b',
   '#ffffbf',
  '#e6f598',
  '#abdda4',
  '#66c2a5',
  '#3288bd',
  '#5e4fa2',
  '#8e0152',
  '#c51b7d',
  '#de77ae',
  '#f1b6da',
  '#fde0ef',
  '#f7f7f7',
  '#e6f5d0',
  '#b8e186',
  '#7fbc41',
  '#4d9221',
  '#276419',
];

const spectrums = {
  blue_white_yellow: [
    '#00d1c1',
    'white',
    '#ffb400',
  ],
  fire: [
    'white',
    'yellow',
    'red',
    'black',
  ],
  white_black: [
    'white',
    'black',
  ],
  black_white: [
    'black',
    'white',
  ],
  YlGnBu: [
    '#ffffd9',
    '#c7e9b4',
    '#41b6c4',
    '#225ea8',
    '#081d5',
    ],
};

export const category21 = (function () {
  // Color factory
  const seen = {};
  return function (s) {
    if (!s) {
      return;
    }
    let stringifyS = String(s);
    // next line is for superset series that should have the same color
    stringifyS = stringifyS.replace('---', '');
    if (seen[stringifyS] === undefined) {
      seen[stringifyS] = Object.keys(seen).length;
    }
    /* eslint consistent-return: 0 */
    return bnbColors[seen[stringifyS] % bnbColors.length];
  };
}());

export const colorScalerFactory = function (colors, data, accessor, country) {
  if (typeof country === 'undefined') { country = 'notmap'; }
  // Returns a linear scaler our of an array of color
  if (!Array.isArray(colors)) {
    /* eslint no-param-reassign: 0 */
    colors = spectrums[colors];

  }
  let ext = [0, 1];
  if (data !== undefined) {
    ext = d3.extent(data, accessor);
  }
  console.log(ext);
  const points = [];
  const chunkSize = (ext[1] - ext[0]) / colors.length;
  $.each(colors, function (i) {
    points.push(i * chunkSize);
  });
  if (country == 'map'){
    console.log(ext)
    //console.log(d3.scale.quantile().domain(points).range(colors));
    return d3.scale.quantile().domain(ext).range(colors);
  } else {
    return d3.scale.linear().domain(points).range(colors);
  }
  //return d3.scale.quantize().domain(points).range(colors);
};
<|endoftext|>


<commit_before>'use strict';

var Representation = require('./representation');
var url = require('url');
var FollowablePromise = require('./followable-promise');
var Promise = require('bluebird');

var Resource = function(client, uri) {

  this.client = client;
  this.uri = uri;
  this.rep = null;

};

Resource.prototype = {

  /**
   * Fetches the resource representation.
   * Returns a promise that resolves to a parsed json object.
   */
  get: function() {

    return this.representation().then(function(r) {
      return r.body;
    });

  },

  /**
   * Updates the resource representation with a new JSON object.
   */
  put: function(body) {

    return this.request({
      method: 'PUT',
      uri: this.uri,
      body: body
    }).then(function() {
      return null;
    });

  },

  /**
   * Updates the resource representation with a new JSON object.
   */
  delete: function(body) {

    return this.request({
      method: 'DELETE',
      uri: this.uri,
      body: body
    }).then(function() {
      return null;
    });

  },

  /**
   * Sends a POST request to the resource.
   *
   * This function assumes that POST is used to create new resources, and
   * that the response will be a 201 Created along with a Location header that
   * identifies the new resource location.
   *
   * This function returns a Promise that resolves into the newly created
   * Resource.
   *
   * If no Location header was given, it will resolve still, but with an empty
   * value.
   */
  post: function(body) {

    return this.request({
      method: 'POST',
      uri: this.uri,
      body: body
    }).then(function(response) {
      if (response.headers.location) {
        return new Resource(
           this.client,
           response.headers.uri
        );
      }
    });
  },

  /**
   * Refreshes the representation for this resource.
   * Returns an empty Promise.
   */
  refresh: function() {

    return this.request({
      method: 'GET',
      uri: this.uri
    }).then(function(response) {
      this.repr = new Representation(
        response.headers['content-type'],
        response.body
      );
    }.bind(this));

  },

  /**
   * Returns the links for this resource, as a promise.
   *
   * The rel argument is optional. If it's given, we will only return links
   * from that relationship type.
   */
  links: function(rel) {

    return this.representation().then(function(r) {
      if (!rel) return r.links;
      return r.links.filter( function(item) { return item.rel === rel; } );
    });

  },

  /**
   * Follows a relationship, based on its reltype. For example, this might be
   * 'alternate', 'item', 'edit' or a custom url-based one.
   *
   * Returns a new Resource object
   */
  follow: function(rel) {

    return new FollowablePromise(function(res, rej) {

      this.links(rel)
        .then(function(links) {

          if (links.length === 0) {
            throw new Error('Relation with type ' + rel + ' not found on resource ' + this.uri);
          }
          res(new Resource(
             this.client,
             url.resolve(this.uri, links[0].href)
          ));
        }.bind(this))
        .catch(function(reason) {
          rej(reason);
        });

    }.bind(this));

  },

  /**
   * Follows a relationship based on its reltype. This function returns a
   * Promise that resolves to an array of Resource objects.
   *
   * If no resources were found, the array will be empty.
   */
  followAll: function(rel) {

    return this.links(rel).then(function(links) {

      return links.map(function(link) {
        return new Resource(
          this.client,
          url.resolve(this.uri, link.href)
        );
      });
    });
  },

  /**
   * Returns the representation for the object.
   *
   * If it wasn't fetched yet, this function does the fetch as well.
   */
  representation: function() {

    if (this.repr) {
      return Promise.resolve(this.repr);
    } else {
      return this.refresh().then(function() {
        return this.repr;
      }.bind(this));
    }

  },

  /**
   * Does an arbitrary HTTP request on the resource, and returns the HTTP
   * response object from the Request library, wrapped in a Promise.
   */
  request: function(options) {

    return this.client.request(options);

  }

};

module.exports = Resource;
<commit_msg>Binding to the local scope.<commit_after>'use strict';

var Representation = require('./representation');
var url = require('url');
var FollowablePromise = require('./followable-promise');
var Promise = require('bluebird');

var Resource = function(client, uri) {

  this.client = client;
  this.uri = uri;
  this.rep = null;

};

Resource.prototype = {

  /**
   * Fetches the resource representation.
   * Returns a promise that resolves to a parsed json object.
   */
  get: function() {

    return this.representation().then(function(r) {
      return r.body;
    });

  },

  /**
   * Updates the resource representation with a new JSON object.
   */
  put: function(body) {

    return this.request({
      method: 'PUT',
      uri: this.uri,
      body: body
    }).then(function() {
      return null;
    });

  },

  /**
   * Updates the resource representation with a new JSON object.
   */
  delete: function(body) {

    return this.request({
      method: 'DELETE',
      uri: this.uri,
      body: body
    }).then(function() {
      return null;
    });

  },

  /**
   * Sends a POST request to the resource.
   *
   * This function assumes that POST is used to create new resources, and
   * that the response will be a 201 Created along with a Location header that
   * identifies the new resource location.
   *
   * This function returns a Promise that resolves into the newly created
   * Resource.
   *
   * If no Location header was given, it will resolve still, but with an empty
   * value.
   */
  post: function(body) {

    return this.request({
      method: 'POST',
      uri: this.uri,
      body: body
    }).then(function(response) {
      if (response.headers.location) {
        return new Resource(
           this.client,
           response.headers.uri
        );
      }
    }.bind(this));
  },

  /**
   * Refreshes the representation for this resource.
   * Returns an empty Promise.
   */
  refresh: function() {

    return this.request({
      method: 'GET',
      uri: this.uri
    }).then(function(response) {
      this.repr = new Representation(
        response.headers['content-type'],
        response.body
      );
    }.bind(this));

  },

  /**
   * Returns the links for this resource, as a promise.
   *
   * The rel argument is optional. If it's given, we will only return links
   * from that relationship type.
   */
  links: function(rel) {

    return this.representation().then(function(r) {
      if (!rel) return r.links;
      return r.links.filter( function(item) { return item.rel === rel; } );
    });

  },

  /**
   * Follows a relationship, based on its reltype. For example, this might be
   * 'alternate', 'item', 'edit' or a custom url-based one.
   *
   * Returns a new Resource object
   */
  follow: function(rel) {

    return new FollowablePromise(function(res, rej) {

      this.links(rel)
        .then(function(links) {

          if (links.length === 0) {
            throw new Error('Relation with type ' + rel + ' not found on resource ' + this.uri);
          }
          res(new Resource(
             this.client,
             url.resolve(this.uri, links[0].href)
          ));
        }.bind(this))
        .catch(function(reason) {
          rej(reason);
        });

    }.bind(this));

  },

  /**
   * Follows a relationship based on its reltype. This function returns a
   * Promise that resolves to an array of Resource objects.
   *
   * If no resources were found, the array will be empty.
   */
  followAll: function(rel) {

    return this.links(rel).then(function(links) {

      return links.map(function(link) {
        return new Resource(
          this.client,
          url.resolve(this.uri, link.href)
        );
      });
    });
  },

  /**
   * Returns the representation for the object.
   *
   * If it wasn't fetched yet, this function does the fetch as well.
   */
  representation: function() {

    if (this.repr) {
      return Promise.resolve(this.repr);
    } else {
      return this.refresh().then(function() {
        return this.repr;
      }.bind(this));
    }

  },

  /**
   * Does an arbitrary HTTP request on the resource, and returns the HTTP
   * response object from the Request library, wrapped in a Promise.
   */
  request: function(options) {

    return this.client.request(options);

  }

};

module.exports = Resource;
<|endoftext|>


<commit_before>
var sprintf = require("sprintf");
var track = require("./parcelforce/track.js");

function makeParcelForce() {
	return {

		gearmanFnsToRegister: ["getTracking"],

		gearmanWorkerPrefix: "ParcelForce",

		getTracking: function(gearmanPayloadData) {

			var shipmentNumber = gearmanPayloadData.shipmentNumber;
			var trackingLog = track(
				function () {
				},
				shipmentNumber
			);
			console.log(trackingLog);
			return trackingLog;
		}
	};
}

module.exports = makeParcelForce();<commit_msg>parcelforce tracking now works with updated scrappy<commit_after>
var sprintf = require("sprintf");
var track = require("./parcelforce/track.js");

function makeParcelForce() {
	return {

		gearmanFnsToRegister: ["getTracking"],

		gearmanWorkerPrefix: "ParcelForce",

		getTracking: function(gearmanPayloadData, worker, success) {
			var shipmentNumber = gearmanPayloadData.shipmentNumber;
			var trackingLog = track(
				success,
				shipmentNumber
			);
		}
	};
}

module.exports = makeParcelForce();

<|endoftext|>


<commit_before>'use strict';
const BPromise = require('bluebird');

const Http = require('./http');
const config = require('./config');
const ErrorResponse = require('./errors/error_response');
const { omit } = require('./helpers');

class Resource {
  constructor(path) {
    this.http = new Http(config.namespace + path);
  }

  action(name, params, method = 'post') {
    const req = this.http.method(method).send(params);
    req.url += `/${name}`;
    return this.__makeRequest(req);
  }

  list(params) {
    const req = this.http.method('get').query(params);
    return this.__makeRequest(req);
  }

  __makeRequest(req) {
    return new BPromise(function (resolve, reject) {
      req.end(function (err, res) {
        if (err) {
          reject(new ErrorResponse(err));
        } else {
          resolve(res);
        }
      });
    }).catch(function (err) {
      throw err;
    });
  }

  create(params) {
    const req = this.http.method('post').send(params);
    return this.__makeRequest(req);
  }

  get(params) {
    const req = this.http.method('get').query(omit(params, 'id'));
    req.url += `/${params.id}`;
    return this.__makeRequest(req);
  }

  insert(params) {
    const req = this.http.method('put').send(params);
    req.url += `/${params.id}`;
    return this.__makeRequest(req);
  }

  update(params) {
    const req = this.http.method('patch').send(omit(params, 'id'));
    req.url += `/${params.id}`;
    return this.__makeRequest(req);
  }

  destroy(params) {
    const req = this.http.method('delete').send(omit(params, 'id'));
    req.url += `/${params.id}`;
    return this.__makeRequest(req);
  }
}

module.exports = Resource;
<commit_msg>supporting get action with params<commit_after>'use strict';
const BPromise = require('bluebird');

const Http = require('./http');
const config = require('./config');
const ErrorResponse = require('./errors/error_response');
const { omit } = require('./helpers');

class Resource {
  constructor(path) {
    this.http = new Http(config.namespace + path);
  }

  action(name, params, method = 'post') {
    const req = this.http.method(method)
    if ('get' === method) {
      req.query(params);
    } else {
      req.send(params);
    }
    req.url += `/${name}`;
    return this.__makeRequest(req);
  }

  list(params) {
    const req = this.http.method('get').query(params);
    return this.__makeRequest(req);
  }

  __makeRequest(req) {
    return new BPromise(function (resolve, reject) {
      req.end(function (err, res) {
        if (err) {
          reject(new ErrorResponse(err));
        } else {
          resolve(res);
        }
      });
    }).catch(function (err) {
      throw err;
    });
  }

  create(params) {
    const req = this.http.method('post').send(params);
    return this.__makeRequest(req);
  }

  get(params) {
    const req = this.http.method('get').query(omit(params, 'id'));
    req.url += `/${params.id}`;
    return this.__makeRequest(req);
  }

  insert(params) {
    const req = this.http.method('put').send(params);
    req.url += `/${params.id}`;
    return this.__makeRequest(req);
  }

  update(params) {
    const req = this.http.method('patch').send(omit(params, 'id'));
    req.url += `/${params.id}`;
    return this.__makeRequest(req);
  }

  destroy(params) {
    const req = this.http.method('delete').send(omit(params, 'id'));
    req.url += `/${params.id}`;
    return this.__makeRequest(req);
  }
}

module.exports = Resource;
<|endoftext|>


<commit_before>/**
 *  Response Class
 *  Returns the response of each request to the client
 *  @params req  - request made by the client
 *  @params res  - contains the response object
 *  @params data - contains the response body and data to be returned
 *  @params http_code  - http_code of response, default to 200/Ok (see list of all available HTTP Response Codes)
 */

var response = function(application_config,util)
{
    console.log("Responder class finished loading......");
    return function (req, res, data, http_code)
        {

            data.memory_usage = util.inspect(process.memoryUsage());
            data.response_time = +new Date() - (+req.response_time);
            data.method = req.method.toUpperCase();
            data.action = req.action;
            data.object = req.obj_name;
            //refactor
            res.writeHead((http_code = http_code || 200), {
                "Access-Control-Allow-Origin" : "*",
                "Access-Control-Allow-Methods" : "GET,PUT,POST,DELETE",
                "Access-Control-Allow-Headers" : "Content-Type",
                "Access-Control-Allow-Credentials" : true,
                "Content-Type" : application_config.response_type,
                'Content-Length': JSON.stringify(data).length
            });

            console.log(JSON.stringify(data).length);
            console.log(JSON.stringify(data));
            res.end(JSON.stringify(data));
            return;
        }
}

module.exports = response<commit_msg>fix header<commit_after>/**
 *  Response Class
 *  Returns the response of each request to the client
 *  @params req  - request made by the client
 *  @params res  - contains the response object
 *  @params data - contains the response body and data to be returned
 *  @params http_code  - http_code of response, default to 200/Ok (see list of all available HTTP Response Codes)
 */

var response = function(application_config,util)
{
    console.log("Responder class finished loading......");
    return function (req, res, data, http_code)
        {

            data.memory_usage = util.inspect(process.memoryUsage());
            data.response_time = +new Date() - (+req.response_time);
            data.method = req.method.toUpperCase();
            data.action = req.action;
            data.object = req.obj_name;
            //refactor
            res.writeHead((http_code = http_code || 200), {
                "Access-Control-Allow-Origin" : "*",
                "Access-Control-Allow-Methods" : "GET,PUT,POST,DELETE",
                "Access-Control-Allow-Headers" : "Content-Type",
                "Access-Control-Allow-Credentials" : true,
                "Content-Type" : application_config.response_type
            });

            console.log(JSON.stringify(data).length);
            console.log(JSON.stringify(data));
            res.end(JSON.stringify(data));
            return;
        }
}

module.exports = response<|endoftext|>


<commit_before>const fs = require('fs');
const path = require('path');
const fileName = path.resolve(`${__dirname}/node_modules/spectron/lib/api.js`);

console.log('patching', fileName);

// see https://github.com/electron/spectron/issues/29
fs.readFile(fileName, 'utf-8', (err, data) => {
    if (data.indexOf('electronRequire') !== -1) {
        const fixed = data.replace(/ require\(/, ' electronRequire(');
        fs.writeFile(fileName, fixed);
        console.log('spectron hack performed');
    } else {
        console.log('spectron hack already done');
    }
});
<commit_msg>stupid<commit_after>const fs = require('fs');
const path = require('path');
const fileName = path.resolve(`${__dirname}/node_modules/spectron/lib/api.js`);

console.log('patching', fileName);

// see https://github.com/electron/spectron/issues/29
fs.readFile(fileName, 'utf-8', (err, data) => {
    if (data.indexOf(' require(\'') !== -1) {
        const fixed = data.replace(/ require\(/g, ' electronRequire(');
        fs.writeFile(fileName, fixed);
        console.log('spectron hack performed');
    } else {
        console.log('spectron hack already done');
    }
});
<|endoftext|>


<commit_before>'use strict';

var async = require('async');
var fs = require('fs');
var path = require('path');
var grpc = require('grpc');
var googleProtoFiles = require('google-proto-files');
var googleAuth = require('google-auto-auth');
var Transform = require('stream').Transform;
var record = require('node-record-lpcm16');

const config = require('../config');

var PROTO_ROOT_DIR = googleProtoFiles('..');
var protoDescriptor = grpc.load({
  root: PROTO_ROOT_DIR,
  file: path.relative(PROTO_ROOT_DIR, googleProtoFiles.speech.v1)
}, 'proto', {
  binaryAsBase64: true,
  convertFieldsToCamelCase: true
});
var speechProto = protoDescriptor.google.cloud.speech.v1;
var isListening = false;
var stopTimer = null;
console.log('STARTING STREAM.js');

function getSpeechService (callback) {
  var googleAuthClient = googleAuth({
    scopes: [
      'https://www.googleapis.com/auth/cloud-platform'
    ]
  });

  googleAuthClient.getAuthClient(function (err, authClient) {
    if (err) {
      return callback(err);
    }

    var credentials = grpc.credentials.combineChannelCredentials(
      grpc.credentials.createSsl(),
      grpc.credentials.createFromGoogleCredential(authClient)
    );

    console.log('Loading speech service...');
    var stub = new speechProto.Speech('speech.googleapis.com', credentials);
    return callback(null, stub);
  });
}
exports.listen = function(callback, done) {
  console.log('listen');
  if(isListening === true) return;
  async.waterfall([
    function (cb) {
      getSpeechService(cb);
    },
    function sendRequest (speechService, cb) {
      console.log('Analyzing speech...');
      var call = speechService.streamingRecognize()
        .on('error', cb)
        .on('data', function (recognizeResponse) {
          if (recognizeResponse.speechEventType == 'END_OF_SINGLE_UTTERANCE') {
            record.stop();
            clearTimeout(stopTimer);

            isListening = false;
          }
          callback(recognizeResponse);
        })
        .on('end', cb);

      // Write the initial recognize reqeust
      call.write({
        streamingConfig: {
          config: {
            encoding: 'LINEAR16',
            sampleRateHertz: 16000,
            languageCode: config.language
          },
          interimResults: false,
          singleUtterance: true,
        }
      });

      var toRecognizeRequest = new Transform({ objectMode: true });
      toRecognizeRequest._transform = function (chunk, encoding, done) {
        done(null, {
          audioContent: chunk
        });
      };
      isListening = true;
      // Start recording
      var readableMicrophoneStream = record.start({
        sampleRateHertz : 16000,
        verbose : true,
        recordProgram: 'arecord'
      });

      // Stream the microphone audio to the Speech API
      readableMicrophoneStream
        .pipe(toRecognizeRequest)
        .pipe(call);

      // In case no end of audio
      stopTimer = setTimeout(function () {
        console.log('_______STOOOOOP____');
        record.stop();
        isListening = false;
      }, 10000)
    }
  ], function (err) {
    if (err) {
      return console.error(err);
    }
    console.log('DONE');
    done();
  });
}<commit_msg>Fix bug where hotword detection was not restarted when speech recognition timed out.<commit_after>'use strict';

var async = require('async');
var fs = require('fs');
var path = require('path');
var grpc = require('grpc');
var googleProtoFiles = require('google-proto-files');
var googleAuth = require('google-auto-auth');
var Transform = require('stream').Transform;
var record = require('node-record-lpcm16');

const config = require('../config');

var PROTO_ROOT_DIR = googleProtoFiles('..');
var protoDescriptor = grpc.load({
  root: PROTO_ROOT_DIR,
  file: path.relative(PROTO_ROOT_DIR, googleProtoFiles.speech.v1)
}, 'proto', {
  binaryAsBase64: true,
  convertFieldsToCamelCase: true
});
var speechProto = protoDescriptor.google.cloud.speech.v1;
var isListening = false;
var stopTimer = null;
console.log('STARTING STREAM.js');

function getSpeechService (callback) {
  var googleAuthClient = googleAuth({
    scopes: [
      'https://www.googleapis.com/auth/cloud-platform'
    ]
  });

  googleAuthClient.getAuthClient(function (err, authClient) {
    if (err) {
      return callback(err);
    }

    var credentials = grpc.credentials.combineChannelCredentials(
      grpc.credentials.createSsl(),
      grpc.credentials.createFromGoogleCredential(authClient)
    );

    console.log('Loading speech service...');
    var stub = new speechProto.Speech('speech.googleapis.com', credentials);
    return callback(null, stub);
  });
}
exports.listen = function(callback, done) {
  console.log('listen');
  if(isListening === true) return;
  async.waterfall([
    function (cb) {
      getSpeechService(cb);
    },
    function sendRequest (speechService, cb) {
      console.log('Analyzing speech...');
      var call = speechService.streamingRecognize()
        .on('error', cb)
        .on('data', function (recognizeResponse) {
          if (recognizeResponse.speechEventType == 'END_OF_SINGLE_UTTERANCE') {
            record.stop();
            clearTimeout(stopTimer);

            isListening = false;
          }
          callback(recognizeResponse);
        })
        .on('end', cb);

      // Write the initial recognize reqeust
      call.write({
        streamingConfig: {
          config: {
            encoding: 'LINEAR16',
            sampleRateHertz: 16000,
            languageCode: config.language
          },
          interimResults: false,
          singleUtterance: true,
        }
      });

      var toRecognizeRequest = new Transform({ objectMode: true });
      toRecognizeRequest._transform = function (chunk, encoding, done) {
        done(null, {
          audioContent: chunk
        });
      };
      isListening = true;
      // Start recording
      var readableMicrophoneStream = record.start({
        sampleRateHertz : 16000,
        verbose : true,
        recordProgram: 'arecord'
      });

      // Stream the microphone audio to the Speech API
      readableMicrophoneStream
        .pipe(toRecognizeRequest)
        .pipe(call);

      // In case no end of audio
      stopTimer = setTimeout(function () {
        console.log('Stop recording, no voice recognized');
        record.stop();
        isListening = false;
        done();
      }, 10000)
    }
  ], function (err) {
    if (err) {
      return console.error(err);
    }
    console.log('DONE');
    done();
  });
}<|endoftext|>


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

/**
 * Data retriever class. Retrieves and pre-processes data from Twitter.
 */
module.exports = class Retrieve {

    /**
     * Initialize the class.
     * @param {Twitter} twitterHandler - The Twitter API object for making API calls.
     * @param {Util} utils - The utilities object needed for logging, etc.
     */
    constructor(twitterHandler, utils) {
        this.twitterHandler = twitterHandler;
        this.utils = utils;

        this.TWEETS_TO_TRACK = 3000;
    }

    /**
     * Retrieves new mentions of the bot, and saves the current state if needed.
     * @param {string} lastID - The last mention that was received.
     * @return {Promise} Resolves with any new mentions of the bot.
     */
    retrieveMentions(lastID) {
        const mentionData = {};

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

        return this.twitterHandler.getMentions(mentionData).then((mentions) => {
            return mentions;
        });
    }

    /**
     * Retrieves and processed new tweets of tracked users.
     * @param {Dictionary} trackedUsers - The tracked users data from the state.
     * @return {Promise} Resolves with the new tweets.
     */
    retrieveTweets(trackedUsers) {
        const tweetRetrievals = [];

        for (const userID in trackedUsers) {
            const lastTweetID = trackedUsers[userID];
            const requestData = {
                user_id: userID,
                trim_user: true
            };

            // If the user has been processed before
            if (lastTweetID !== 0) {
                requestData.count = 200;
                requestData.since_id = lastTweetID;
                tweetRetrievals.push(this.retrieveForExisting(requestData));
            // .. or the user has NOT been processed before
            } else {
                tweetRetrievals.push(this.retrieveForNew(requestData));
            }
        }

        // Wait until all retrievals are done
        return Promise.all(tweetRetrievals);
    }

    /**
     * Retrieves new tweets from an existing user. Only specify one argument.
     * @private
     * @param {Object} params - The data to be used for making twitter requests.
     * @return {Array} Resolves with an array of tweets.
     */
    retrieveForExisting(params, tweetsRetrieved) {
        // Copy object to allow for proper unit testing
        const request = Object.assign({}, params);
        const firstReq = !request.hasOwnProperty('max_id');

        // Initialize recursive variables
        if (firstReq) {
            tweetsRetrieved = [];
            this.utils.log('Retrieving most recent tweets from user with ID: "' + request.user_id + '"');
        }

        return this.twitterHandler.getTweets(request).then((tweets) => {
            // Do not count the last tweet as it might be the first tweet of the next request
            const numRetrieved = tweets.length - 1;

            if (tweets.length === 0) {
                this.utils.log('    No new tweets found');

                return {
                    done: true,
                    tweets: tweetsRetrieved,
                    totalRetrieved: 0
                };
            }

            // If the max ID from the request is the oldest tweet in the request (i.e. only one tweet returned)
            // or only one tweet was found on the first request..
            if (request.max_id === tweets[tweets.length - 1].id_str || firstReq && tweets.length === 1) {
                tweetsRetrieved.push(tweets[0]);

                this.utils.log('    Retrieved 1 tweet');
                this.utils.log('    Finished retrieving ' + tweetsRetrieved.length + ' tweets');

                return {
                    done: true,
                    tweets: tweetsRetrieved
                };
            }

            for (let i = 0; i < numRetrieved; i++) {
                tweetsRetrieved.push(tweets[i]);
            }

            this.utils.log('    Retrieved ' + numRetrieved + ' tweets');

            // Set the max ID of the next request to the oldest tweet in the previous request
            const nextRequest = Object.assign({}, request);
            nextRequest.max_id = tweets[tweets.length - 1].id_str;

            return {
                done: false,
                request: nextRequest,
                tweetsRetrieved: tweetsRetrieved
            };
        }).then((next) => {
            if (!next.done) {
                return this.retrieveForExisting(next.request, next.tweetsRetrieved);
            } else {
                return next.tweets;
            }
        });
    }

    /**
     * Retrieves new tweets from a newly added user. Only specify one argument.
     * @private
     * @param {Object} params - The data to be used for making twitter requests.
     * @return {Promise} Resolves with an object (The tweet data and total number of tweets retrieved).
     */
    retrieveForNew(params, tweetsLeft, tweetsRetrieved) {
        // Copy object to allow for proper unit testing
        const request = Object.assign({}, params);

        // If first request..
        if (!request.hasOwnProperty('count')) {
            request.count = this.TWEETS_TO_TRACK >= 200 ? 200 : this.TWEETS_TO_TRACK;
            tweetsLeft = this.TWEETS_TO_TRACK;
            tweetsRetrieved = [];

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

        return this.twitterHandler.getTweets(request).then((tweets) => {
            const numRetrieved = tweets.length - 1;
            tweetsLeft -= numRetrieved;

            // If only one tweet was returned and it was the max ID tweet requested..
            if (request.max_id === tweets[tweets.length - 1].id_str) {
                tweetsRetrieved.push(tweets[0]);

                this.utils.log('    Retrieved ' + tweetsRetrieved.length + '/' + this.TWEETS_TO_TRACK + ' tweets');
                this.utils.log('    Finished retrieving tweets');
                if (request.count > 1) {
                    this.utils.log('    Reached max tweet retrieval limit.');
                }

                return {
                    done: true,
                    tweets: tweetsRetrieved
                };
            }

            // Otherwise add retrieved tweets
            for (let i = 0; i < numRetrieved; i++) {
                tweetsRetrieved.push(tweets[i]);
            }

            this.utils.log('    Retrieved ' + tweetsRetrieved.length + '/' + this.TWEETS_TO_TRACK + ' tweets');

            // Set newest ID for next request to oldest ID of the previous request
            const nextRequest = Object.assign({}, request);
            nextRequest.max_id = tweets[tweets.length - 1].id_str;
            if (tweetsLeft < 200) {
                nextRequest.count = tweetsLeft;
            }

            return {
                done: false,
                request: nextRequest,
                tweetsLeft: tweetsLeft,
                tweetsRetrieved: tweetsRetrieved
            };
        }).then((next) => {
            if (!next.done) {
                return this.retrieveForNew(next.request, next.tweetsLeft, next.tweetsRetrieved);
            } else {
                return next.tweets;
            }
        });
    }

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

/**
 * Data retriever class. Retrieves and pre-processes data from Twitter.
 */
module.exports = class Retrieve {

    /**
     * Initialize the class.
     * @param {Twitter} twitterHandler - The Twitter API object for making API calls.
     * @param {Util} utils - The utilities object needed for logging, etc.
     */
    constructor(twitterHandler, utils) {
        this.twitterHandler = twitterHandler;
        this.utils = utils;

        this.TWEETS_TO_TRACK = 3000;
    }

    /**
     * Retrieves new mentions of the bot, and saves the current state if needed.
     * @param {string} lastID - The last mention that was received.
     * @return {Promise} Resolves with any new mentions of the bot.
     */
    retrieveMentions(lastID) {
        const mentionData = {};

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

        return this.twitterHandler.getMentions(mentionData).then((mentions) => {
            return mentions;
        });
    }

    /**
     * Retrieves and processed new tweets of tracked users.
     * @param {Dictionary} trackedUsers - The tracked users data from the state.
     * @return {Promise} Resolves with the new tweets.
     */
    retrieveTweets(trackedUsers) {
        const tweetRetrievals = [];

        for (const userID in trackedUsers) {
            const lastTweetID = trackedUsers[userID];
            const requestData = {
                user_id: userID,
                trim_user: true
            };

            // If the user has been processed before
            if (lastTweetID !== 0) {
                requestData.count = 200;
                requestData.since_id = lastTweetID;
                tweetRetrievals.push(this.retrieveForExisting(requestData));
            // .. or the user has NOT been processed before
            } else {
                tweetRetrievals.push(this.retrieveForNew(requestData));
            }
        }

        // Wait until all retrievals are done
        return Promise.all(tweetRetrievals);
    }

    /**
     * Retrieves new tweets from an existing user. Only specify one argument.
     * @private
     * @param {Object} params - The data to be used for making twitter requests.
     * @return {Array} Resolves with an array of tweets.
     */
    retrieveForExisting(params, tweetsRetrieved) {
        // Copy object to allow for proper unit testing
        const request = Object.assign({}, params);
        const firstReq = !request.hasOwnProperty('max_id');

        // Initialize recursive variables
        if (firstReq) {
            tweetsRetrieved = [];
            this.utils.log('Retrieving most recent tweets from user with ID: "' + request.user_id + '"');
        }

        return this.twitterHandler.getTweets(request).then((tweets) => {
            // Do not count the last tweet as it might be the first tweet of the next request
            const numRetrieved = tweets.length - 1;

            if (tweets.length === 0) {
                this.utils.log('    No new tweets found');

                return {
                    done: true,
                    tweets: tweetsRetrieved,
                    totalRetrieved: 0
                };
            }

            // If the max ID from the request is the oldest tweet in the request (i.e. only one tweet returned)
            // or only one tweet was found on the first request..
            if (request.max_id === tweets[tweets.length - 1].id_str || firstReq && tweets.length === 1) {
                tweetsRetrieved.push(tweets[0]);

                this.utils.log('    Retrieved 1 tweet');
                this.utils.log('    Finished retrieving ' + tweetsRetrieved.length + ' tweets');

                return {
                    done: true,
                    tweets: tweetsRetrieved
                };
            }

            for (let i = 0; i < numRetrieved; i++) {
                tweetsRetrieved.push(tweets[i]);
            }

            this.utils.log('    Retrieved ' + numRetrieved + ' tweets');

            // Set the max ID of the next request to the oldest tweet in the previous request
            const nextRequest = Object.assign({}, request);
            nextRequest.max_id = tweets[tweets.length - 1].id_str;

            return {
                done: false,
                request: nextRequest,
                tweetsRetrieved: tweetsRetrieved
            };
        }).then((next) => {
            if (!next.done) {
                return this.retrieveForExisting(next.request, next.tweetsRetrieved);
            } else {
                return next.tweets;
            }
        });
    }

    /**
     * Retrieves new tweets from a newly added user. Only specify one argument.
     * @private
     * @param {Object} params - The data to be used for making twitter requests.
     * @return {Promise} Resolves with an object (The tweet data and total number of tweets retrieved).
     */
    retrieveForNew(params, tweetsLeft, tweetsRetrieved) {
        // Copy object to allow for proper unit testing
        const request = Object.assign({}, params);

        // If first request..
        if (!tweetsLeft) {
            request.count = 200;
            tweetsLeft = this.TWEETS_TO_TRACK;
            tweetsRetrieved = [];

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

        return this.twitterHandler.getTweets(request).then((tweets) => {
            const numRetrieved = tweets.length - 1;

            // If only one tweet was returned and it was the max ID tweet requested..
            if (request.max_id === tweets[tweets.length - 1].id_str) {
                tweetsRetrieved.push(tweets[0]);

                this.utils.log('    Retrieved ' + tweetsRetrieved.length + '/' + this.TWEETS_TO_TRACK + ' tweets');
                this.utils.log('    Reached twitter\'s max tweet retrieval limit.');
                this.utils.log('    Finished retrieving tweets');

                return {
                    done: true,
                    tweets: tweetsRetrieved
                };
            }

            // If it retrieved more tweets than what was needed..
            if (numRetrieved + 1 >= tweetsLeft) {
                for (let i = 0; i < tweetsLeft; i++) {
                    tweetsRetrieved.push(tweets[i]);
                }

                this.utils.log('    Retrieved ' + tweetsRetrieved.length + '/' + this.TWEETS_TO_TRACK + ' tweets');
                this.utils.log('    Finished retrieving tweets');
                return {
                    done: true,
                    tweets: tweetsRetrieved
                };
            }

            for (let i = 0; i < numRetrieved; i++) {
                tweetsRetrieved.push(tweets[i]);
            }
            tweetsLeft -= numRetrieved;

            this.utils.log('    Retrieved ' + tweetsRetrieved.length + '/' + this.TWEETS_TO_TRACK + ' tweets');

            // Set newest ID for next request to oldest ID of the previous request
            const nextRequest = Object.assign({}, request);
            nextRequest.max_id = tweets[tweets.length - 1].id_str;

            return {
                done: false,
                request: nextRequest,
                tweetsLeft: tweetsLeft,
                tweetsRetrieved: tweetsRetrieved
            };
        }).then((next) => {
            if (!next.done) {
                return this.retrieveForNew(next.request, next.tweetsLeft, next.tweetsRetrieved);
            } else {
                return next.tweets;
            }
        });
    }

};
<|endoftext|>