<commit_before>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
"use strict";

//TODO moving cards and pulling in moved card
//TODO ability to dynamically add required columns from plugins
//TODO also keep reference of non-default columns for checking of those issues.

const Column = require("./column");
const Issues = require("./issues");
const TweetCard = require("./tweet-card");

const defaultColumnNames = {
    ideas: "Ideas",
    reactions: "Needs Reaction",
    events: "Events",
    toTweet: "To Tweet",
    tweeted: "Tweeted"
};

/**
 * Used in arrangeColumns. Internal column names get converted to
 * after:columnName, except that after is currently broken.
 *
 * @type {Object.<string, string>}
 * @readonly
 */
const columnOrder = {
    toTweet: "last",
    tweeted: "last",
    events: "first",
    reactions: "first",
    ideas: "first",
};

class ProjectNotFoundError extends Error {
    /**
     * @param {string} projectName - Name of the project that was not found.
     */
    constructor(projectName) {
        super(`Could not find project ${projectName}`);
    }
}

/**
 * Abstraction over the GitHub project.
 */
class Board {
    /**
     * @param {PromisifiedGitHub} githubClient - GitHub Client.
     * @param {Object} config - Config for the project board.
     * @constructs
     */
    constructor(githubClient, config) {
        this.githubClient = githubClient;
        this.config = config;
        this.columnNames = Object.assign(Object.assign({}, defaultColumnNames), config.columns);
        this.columns = {};
        this.columnIds = {};
        for(const column in this.columnNames) {
            this.columnIds[column] = 0;
        }

        let firstRun = true;

        this.updateInterval = setInterval(() => {
            firstRun = false;
            this.update().catch(console.error);
            //TODO move cards based on the results from this?
        }, 60000);

        this.setup().then(() => {
            this.issues = new Issues(githubClient, config);

            // Boards will be filled via this event handler, as that's how the
            // initial issue load works.
            this.issues.on("opened", async (issue) => {
                for(const column of Object.values(this.columns)) {
                    if(column && (await column.hasIssue(issue.number))) {
                        return column.addCard(new TweetCard(issue, config), firstRun);
                    }
                }
                if(this.columnIds.ideas !== 0) {
                    return this.columns[this.columnIds.ideas].addCard(new TweetCard(issue, config));
                }
            });

            this.issues.on("updated", (issue) => {
                for(const column of Object.values(this.columns)) {
                    if(column && column.hasCard(issue.id)) {
                        column.getCard(issue.id).checkValidity();
                        break;
                    }
                }
            });

            this.issues.on("closed", (issue) => {
                for(const column of Object.values(this.columns)) {
                    if(column && column.hasCard(issue.id)) {
                        column.removeCard(this.columnInstances.getCard(issue.id));
                        break;
                    }
                }
            });
        }).catch((e) => console.error(e));
    }

    /**
     * Updates all the things.
     *
     * @async
     * @returns {undefined}
     */
    update() {
        const promises = [];
        promises.push(this.issues.fetchOpenIssues());
        for(const column of Object.values(this.columns)) {
            promises.push(column.fetchCards());
        }
        return Promise.all(promises);
    }

    /**
     * Gets the ID of the project.
     *
     * @async
     * @returns {number} ID of the project.
     * @throws The project could not be found or another API error occured.
     */
    getBoardID() {
        if(!this.id) {
            return this.githubClient("projects", "getRepoProjects", {
                owner: this.config.owner,
                repo: this.config.repo
            }).then((res) => {
                const project = res.find((p) => p.name === this.config.projectName);
                if(project) {
                    this.id = project.id;
                    return project.id;
                }
                else {
                    throw new ProjectNotFoundError(this.config.projectName);
                }
            });
        }
        return Promise.resolve(this.id);
    }

    /**
     * Checks if the project exists.
     *
     * @async
     * @returns {boolean} Whether the board exists.
     */
    boardExists() {
        return this.getBoardID().then(() => true, (e) => {
            if(e instanceof ProjectNotFoundError) {
                return false;
            }
            throw e;
        });
    }

    /**
     * Fetches all the columns in the project.
     *
     * @async
     * @returns {undefined}
     */
    fetchColumns() {
        return this.getBoardID().then((id) => {
            return this.githubClient("projects", "getProjectColumns", {
                owner: this.config.owner,
                repo: this.config.repo,
                project_id: id
            });
        }).then((res) => {
            const builtinNames = Object.values(this.columnNames);
            for(const c in res) {
                if(c === "meta") {
                    continue;
                }
                const column = res[c];
                this.columns[column.id] = new Column(this.githubClient, column.id);

                if(builtinNames.includes(column.name)) {
                    let universalName;
                    for(const n in this.columnNames) {
                        if(this.columnNames[n] === column.name) {
                            universalName = n;
                            break;
                        }
                    }
                    this.columnIds[universalName] = column.id;
                }
            }
        });
    }

    /**
     * Gets missing columns for the project.
     *
     * @returns {Array.<string>} Columns that aren't in the project yet but are
     *                           required for the operation based on the config.
     */
    missingColumns() {
        const missingColumns = [];
        for(const c in this.columnIds) {
            if(this.columnIds[c] === 0) {
                missingColumns.push(c);
            }
        }
        return missingColumns;
    }

    /**
     * Checks if all the configured columns exist on GitHub.
     *
     * @async
     * @returns {boolean} Whether all required columns exist.
     * @throws API error.
     */
    columnsExist() {
        this.missingColumns().then((missingColumns) => missingColumns.length === 0);
    }

    /**
     * Creates the project board. Saves the id of the board.
     *
     * @async
     * @returns {undefined}
     * @throws API error.
     */
    createBoard() {
        return this.githubClient("projects", "createRepoProject", {
            owner: this.config.owner,
            repo: this.config.repo,
            name: this.config.projectName,
            body: "Twitter Content Queue"
        }).then((res) => {
            this.id = res.number;
        });
    }

    /**
     * Adds a column to the project and save its ID.
     *
     * @param {string} name - Name of the column to create.
     * @async
     * @returns {undefined}
     * @throws API error.
     */
    createColumn(name) {
        return Column.create(this.githubClient, this.id, name).then((res) => {
            for(const gn in this.columnNames) {
                if(this.columnNames[gn] === name) {
                    this.columnIds[gn] = res.id;
                    break;
                }
            }
            this.columns[res.id] = res;
        });
    }

    /**
     * Arranges the columns in a sensible order. Moves the input columns to the
     * left and the output columns to the right.
     *
     * @todo Fix after:id placement
     * @todo Last seems broken on GitHub's side.
     * @returns {undefined}
     * @throws API error.
     */
    async arrangeColumns() {
        for(const column in columnOrder) {
            if(column in this.columnIds && this.columnIds[column] !== 0) {
                let position = columnOrder[column];
                if(position != "first" && position != "last") {
                    position = `after:${this.columnIds[position]}`;
                }
                await this.columns[this.columnIds[column]].move(position);
            }
        }
    }

    /**
     * Creates a project and the required columns.
     *
     * @returns {undefined}
     */
    async setup() {
        if(!(await this.boardExists())) {
            await this.createBoard();
        }

        await this.fetchColumns();

        const missingColumns = this.missingColumns();

        await Promise.all(missingColumns.map((column) => {
            return this.createColumn(column);
        }));

        if(missingColumns.length > 0) {
            await this.arrangeColumns();
        }
    }

    /**
     * Creates a card in the incoming column.
     *
     * @param {TweetCard} card - Card to add.
     * @async
     * @returns {undefined}
     */
    createCard(card) {
        if(!(card instanceof TweetCard)) {
            return Promise.reject("Card must be instance of TweetCard");
        }

        return this.issues.createIssue(card.issue).then(() => {
            return this.columns[this.columnIds.reactions].addCard(card);
        }).then(() => {
            card.checkValidity();
        });
    }
}

module.exports = Board;
<commit_msg>Don't check already tweeted cards<commit_after>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
"use strict";

//TODO moving cards and pulling in moved card
//TODO ability to dynamically add required columns from plugins
//TODO also keep reference of non-default columns for checking of those issues.

const Column = require("./column");
const Issues = require("./issues");
const TweetCard = require("./tweet-card");

const defaultColumnNames = {
    ideas: "Ideas",
    reactions: "Needs Reaction",
    events: "Events",
    toTweet: "To Tweet",
    tweeted: "Tweeted"
};

/**
 * Used in arrangeColumns. Internal column names get converted to
 * after:columnName, except that after is currently broken.
 *
 * @type {Object.<string, string>}
 * @readonly
 */
const columnOrder = {
    toTweet: "last",
    tweeted: "last",
    events: "first",
    reactions: "first",
    ideas: "first",
};

class ProjectNotFoundError extends Error {
    /**
     * @param {string} projectName - Name of the project that was not found.
     */
    constructor(projectName) {
        super(`Could not find project ${projectName}`);
    }
}

/**
 * Abstraction over the GitHub project.
 */
class Board {
    /**
     * @param {PromisifiedGitHub} githubClient - GitHub Client.
     * @param {Object} config - Config for the project board.
     * @constructs
     */
    constructor(githubClient, config) {
        this.githubClient = githubClient;
        this.config = config;
        this.columnNames = Object.assign(Object.assign({}, defaultColumnNames), config.columns);
        this.columns = {};
        this.columnIds = {};
        for(const column in this.columnNames) {
            this.columnIds[column] = 0;
        }

        let firstRun = true;

        this.updateInterval = setInterval(() => {
            firstRun = false;
            this.update().catch(console.error);
            //TODO move cards based on the results from this?
        }, 60000);

        this.setup().then(() => {
            this.issues = new Issues(githubClient, config);

            // Boards will be filled via this event handler, as that's how the
            // initial issue load works.
            this.issues.on("opened", async (issue) => {
                for(const column of Object.values(this.columns)) {
                    if(column && (await column.hasIssue(issue.number))) {
                        return column.addCard(new TweetCard(issue, config), firstRun);
                    }
                }
                if(this.columnIds.ideas !== 0) {
                    return this.columns[this.columnIds.ideas].addCard(new TweetCard(issue, config));
                }
            });

            this.issues.on("updated", (issue) => {
                for(const column of Object.values(this.columns)) {
                    if(column && column.hasCard(issue.id) && column.id != this.columnIds.tweeted) {
                        column.getCard(issue.id).checkValidity();
                        break;
                    }
                }
            });

            this.issues.on("closed", (issue) => {
                for(const column of Object.values(this.columns)) {
                    if(column && column.hasCard(issue.id)) {
                        column.removeCard(this.columnInstances.getCard(issue.id));
                        break;
                    }
                }
            });
        }).catch((e) => console.error(e));
    }

    /**
     * Updates all the things.
     *
     * @async
     * @returns {undefined}
     */
    update() {
        const promises = [];
        promises.push(this.issues.fetchOpenIssues());
        for(const column of Object.values(this.columns)) {
            promises.push(column.fetchCards());
        }
        return Promise.all(promises);
    }

    /**
     * Gets the ID of the project.
     *
     * @async
     * @returns {number} ID of the project.
     * @throws The project could not be found or another API error occured.
     */
    getBoardID() {
        if(!this.id) {
            return this.githubClient("projects", "getRepoProjects", {
                owner: this.config.owner,
                repo: this.config.repo
            }).then((res) => {
                const project = res.find((p) => p.name === this.config.projectName);
                if(project) {
                    this.id = project.id;
                    return project.id;
                }
                else {
                    throw new ProjectNotFoundError(this.config.projectName);
                }
            });
        }
        return Promise.resolve(this.id);
    }

    /**
     * Checks if the project exists.
     *
     * @async
     * @returns {boolean} Whether the board exists.
     */
    boardExists() {
        return this.getBoardID().then(() => true, (e) => {
            if(e instanceof ProjectNotFoundError) {
                return false;
            }
            throw e;
        });
    }

    /**
     * Fetches all the columns in the project.
     *
     * @async
     * @returns {undefined}
     */
    fetchColumns() {
        return this.getBoardID().then((id) => {
            return this.githubClient("projects", "getProjectColumns", {
                owner: this.config.owner,
                repo: this.config.repo,
                project_id: id
            });
        }).then((res) => {
            const builtinNames = Object.values(this.columnNames);
            for(const c in res) {
                if(c === "meta") {
                    continue;
                }
                const column = res[c];
                this.columns[column.id] = new Column(this.githubClient, column.id);

                if(builtinNames.includes(column.name)) {
                    let universalName;
                    for(const n in this.columnNames) {
                        if(this.columnNames[n] === column.name) {
                            universalName = n;
                            break;
                        }
                    }
                    this.columnIds[universalName] = column.id;
                }
            }
        });
    }

    /**
     * Gets missing columns for the project.
     *
     * @returns {Array.<string>} Columns that aren't in the project yet but are
     *                           required for the operation based on the config.
     */
    missingColumns() {
        const missingColumns = [];
        for(const c in this.columnIds) {
            if(this.columnIds[c] === 0) {
                missingColumns.push(c);
            }
        }
        return missingColumns;
    }

    /**
     * Checks if all the configured columns exist on GitHub.
     *
     * @async
     * @returns {boolean} Whether all required columns exist.
     * @throws API error.
     */
    columnsExist() {
        this.missingColumns().then((missingColumns) => missingColumns.length === 0);
    }

    /**
     * Creates the project board. Saves the id of the board.
     *
     * @async
     * @returns {undefined}
     * @throws API error.
     */
    createBoard() {
        return this.githubClient("projects", "createRepoProject", {
            owner: this.config.owner,
            repo: this.config.repo,
            name: this.config.projectName,
            body: "Twitter Content Queue"
        }).then((res) => {
            this.id = res.number;
        });
    }

    /**
     * Adds a column to the project and save its ID.
     *
     * @param {string} name - Name of the column to create.
     * @async
     * @returns {undefined}
     * @throws API error.
     */
    createColumn(name) {
        return Column.create(this.githubClient, this.id, name).then((res) => {
            for(const gn in this.columnNames) {
                if(this.columnNames[gn] === name) {
                    this.columnIds[gn] = res.id;
                    break;
                }
            }
            this.columns[res.id] = res;
        });
    }

    /**
     * Arranges the columns in a sensible order. Moves the input columns to the
     * left and the output columns to the right.
     *
     * @todo Fix after:id placement
     * @todo Last seems broken on GitHub's side.
     * @returns {undefined}
     * @throws API error.
     */
    async arrangeColumns() {
        for(const column in columnOrder) {
            if(column in this.columnIds && this.columnIds[column] !== 0) {
                let position = columnOrder[column];
                if(position != "first" && position != "last") {
                    position = `after:${this.columnIds[position]}`;
                }
                await this.columns[this.columnIds[column]].move(position);
            }
        }
    }

    /**
     * Creates a project and the required columns.
     *
     * @returns {undefined}
     */
    async setup() {
        if(!(await this.boardExists())) {
            await this.createBoard();
        }

        await this.fetchColumns();

        const missingColumns = this.missingColumns();

        await Promise.all(missingColumns.map((column) => {
            return this.createColumn(column);
        }));

        if(missingColumns.length > 0) {
            await this.arrangeColumns();
        }
    }

    /**
     * Creates a card in the incoming column.
     *
     * @param {TweetCard} card - Card to add.
     * @async
     * @returns {undefined}
     */
    createCard(card) {
        if(!(card instanceof TweetCard)) {
            return Promise.reject("Card must be instance of TweetCard");
        }

        return this.issues.createIssue(card.issue).then(() => {
            return this.columns[this.columnIds.reactions].addCard(card);
        }).then(() => {
            card.checkValidity();
        });
    }
}

module.exports = Board;
<|endoftext|>


<commit_before>const EventEmitter = require('events');

const state = {
  type: 'Folder',
  content: {
    operationalState: {
      type: 'Variable',
      dataType: 'String',
      initValue: 'Idle'
    },
    stateTransition: {
      type: 'Folder',
      content: {
        start: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        pause: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        resume: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        suspend: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        unsuspend: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        hold: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        unhold: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        stop: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        abort: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        clear: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        reset: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        }
      }
    }
  }
};

const whiteCluster = ["Running", "Paused", "Pausing"];
const transparentCluster = [whiteCluster, "Suspending", "Suspended", "Unsuspending"];
const yellowCluster = [transparentCluster, "Idle", "Starting", "Completing", "Complete", "Resetting", "Holding", "Held", "Unholding"];
const redCluster = [yellowCluster, "Clearing", "Stopped", "Stopping"];

let StateTransitions = {
  Idle: {
    start: "Starting",
  },
  Starting: {
    done: "Running"
  },
  Running: {
    pause: "Pausing",
    done: "Completing"
  },
  Pausing: {
    suspend: "Suspending"
  }

}

class hasState extends EventEmitter{
  constructor(){
    super();
    this.state = "aborted";
  }

  start(){

    this.on('start', function(){

    });
    this.emit('start');

  }

  stop(){

  }

}<commit_msg>complete state transition list<commit_after>const EventEmitter = require('events');

const state = {
  type: 'Folder',
  content: {
    operationalState: {
      type: 'Variable',
      dataType: 'String',
      initValue: 'Idle'
    },
    stateTransition: {
      type: 'Folder',
      content: {
        start: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        pause: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        resume: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        suspend: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        unsuspend: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        hold: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        unhold: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        stop: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        abort: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        clear: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        },
        reset: {
          type: 'Variable',
          dataType: 'Boolean',
          initValue: false
        }
      }
    }
  }
};

const whiteCluster = ["Running", "Paused", "Pausing"];
const transparentCluster = [whiteCluster, "Suspending", "Suspended", "Unsuspending"];
const yellowCluster = [transparentCluster, "Idle", "Starting", "Completing", "Complete", "Resetting", "Holding", "Held", "Unholding"];
const redCluster = [yellowCluster, "Clearing", "Stopped", "Stopping"];

let StateTransitions = {
  Idle: {
      start: "Starting"
  },
  Starting: {
      done: "Running"
  },
  Running: {
    pause: "Pausing",
    done: "Completing"
  },
  Pausing: {
    suspend: "Suspending"
  },
  Paused: {
    resume: "Running"
  },
  Suspending: {
    done: "Suspended"
  },
  Suspended: {
    unsuspend: "Unsuspended"
  },
  Unsuspended: {
    done: "Running"
  },
  Completing: {
    done: "Complete"
  },
  Complete: {
    reset: "Resetting"
  },
  Aborting: {
    done: "Aborted"
  },
  Aborted: {
      clear: "Clearing",
      reset: "Resetting"
  },
  Stopping: {
      done: "Stopped"
  },
  Stopped: {
      reset: "Resetting"
  },
  Holding: {
      done: "Held"
  },
  Held: {
      unhold: "Unholding"
  },
  Unholding: {
      done: "Running"
  },
  Clearing: {
      done: "Stopped"
  },
  Resetting: {
      done: "Idle"
  }
}

class hasState extends EventEmitter{
  constructor(){
    super();
    this.state = "aborted";
  }

  start(){

    this.on('start', function(){

    });
    this.emit('start');

  }

  stop(){

  }

}<|endoftext|>


<commit_before>(function() {

	document.addEventListener('deviceready', onDeviceReady.bind(this), false);

	function onDeviceReady() {

		var push = PushNotification.init({ "android": {"senderID": "156322"}});
		 push.on('registration', function(data) {
		 console.log(data.registrationId);
		 document.getElementById("gcm_id").innerHTML = data.registrationId;
		 });

		 push.on('notification', function(data) {
		 alert(data.title+" Message: " +data.message);
		 });

		 push.on('error', function(e) {
		 alert(e);
		 });
	};

})();

<commit_msg>additions to push notification<commit_after>(function() {

	document.addEventListener('deviceready', onDeviceReady.bind(this), false);

	function onDeviceReady() {

		var push = PushNotification.init({ "android": {"senderID": "81511517930"}});
		 push.on('registration', function(data) {
		 console.log(data.registrationId);
		 document.getElementById("gcm_id").innerHTML = data.registrationId;
		 });

		 push.on('notification', function(data) {
		 alert(data.title+" Message: " +data.message);
		 });

		 push.on('error', function(e) {
		 alert(e);
		 });
	};

})();

<|endoftext|>


<commit_before>/*
 *	Initial setup
 */

"use strict" // Otherwise we're stuck with ES5

const LOCAL_HOST = 3000;

// Variables required for Markov Chain generation
var input = [];
var firstWords = [];
var currentWord = "";

// Variables required for bottom
var currentTopic;
var currentToken;

// Set up modules
var express = require("express");
var request = require("request");
var bodyParser = require("body-parser");
var cheerio = require('cheerio');
var client = require('redis').createClient(process.env.REDIS_URL);

// Set up app
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.cachedData = { "post": "" }; // Basic format for JSON response
app.loggedIn = false; // Indicates whether app has login cookie for ETI

// Set up routing for API
var routes = require("./routes/routes.js")(app);

// Set up server
var server = app.listen(process.env.PORT || LOCAL_HOST, () => {
	console.log(process.env.USERNAME + "-bot is sentient and ready to shitpost");
});

/**
	*		Redis db handler
	*/

client.on("connect", () => {
		// Attempt to get "quotes" array from Redis
		client.lrange("quotes", 0, -1, (error, items) => {
			if (error) {
				// TODO: Store emergency vesper.txt somewhere in repo and use that instead
				throw error;
			}
			else {
				input = items;
				app.generateMarkovChain();
				app.loginToBlueSite(
			}
		});
});

/**
  * Markov chain methods
	*/

app.generateMarkovChain = function(shouldReturn) {
	var output = [];

	app.createArrays();

	// Pick random first word to start with
	currentWord = firstWords[Math.floor((Math.random() * firstWords.length))];

	for (let i = 0, len = app.getPostLength(); i < len; i++) {
		output.push(currentWord);

		var nextWord = app.generateNextWord();

		if (nextWord) {
			currentWord = nextWord;
		}
		else {
			currentWord = "";
		}
	}

	app.cachedData.post = output.join(" ").trim();

	if (shouldReturn) {
		return app.cachedData.post;
	}
};

app.getPostLength = function() {
	return input[Math.floor((Math.random() * input.length))].length;
};

app.createArrays = function() {
	// Create array containing first word of each sentence
	for (let i = 0, len = input.length; i < len; i++) {
		let sentence = input[i];
		let firstWord = sentence.split(" ")[0];
		firstWords.push(firstWord);
	}
};

app.generateNextWord = function() {
	var tempArray = [];
	// Iterate over input array to find instances of current word
	for (let i = 0, len = input.length - 1; i < len; i++) {
		let sentenceToCheck = input[i].split(" ");

		for (let j = 0, len = sentenceToCheck.length; j < len; j++) {
			let wordToCheck = sentenceToCheck[j];
			// create array of words that are likely to follow current word
			if (wordToCheck === currentWord) {
				// Make sure that item at index i + 1 exists
				if (j < len - 1) {
					let nextWord = sentenceToCheck[j + 1];
					tempArray.push(nextWord);
				}
				else {
					// reached end of array - do nothing
				}
			}
		}
	}

	let rnd = Math.floor((Math.random() * tempArray.length));

	if (tempArray[rnd]) {
		return tempArray[rnd];
	}

	else {
		return;
	}
};

/**
  *		Method which allows us to add new quotes from pastebin raw links
  */

app.addNewQuotes = function(url) {
	request.get(url, ((error, response, body) => {
		if (!error && response.statusCode == 200) {
			// Ignore blank lines,
			var quotes = body.split("\r\n").filter((line) => line && line.indexOf("<") == -1 && line.charAt(0) !== "<");

			client.lrange("quotes", 0, -1, (error, items) => {
				if (error) {
					throw error;
				}
				else {
					// Update local & disk cache
					input = input.concat(quotes);
					client.rpush.apply(client, ["quotes"].concat(quotes).concat(() => { console.log("Quotes stored to redis") }));
				}
			});

		}

	}));
};

/**
  *		Log into ETI using environment vars as credentials
  */

app.loginToBlueSite = function() {
	const LOGIN_URL = "https://endoftheinter.net/";
	const formData = { b: process.env.USERNAME, p: process.env.PASSWORD };

	request.post({

		headers: {"content-type": "application/x-www-form-urlencoded"},
		url: LOGIN_URL,
		form: formData,
		jar: app.cookieJar

	}, (error, response, body) => {

			// After successful login, ETI will attempt to redirect you to homepage
			if (!error && response.statusCode === 302) {
					app.cookieJar.setCookie("userid=" + process.env.USER_ID);
					app.loggedIn = true;
			}

			else {
				console.log("Login failed.");
				throw error;
			}
	});
};

app.getTopicList = function() {
	var LUE_TOPICS = "https://boards.endoftheinter.net/topics/LUE";

	request({

		url: LUE_TOPICS
		jar: app.cookieJar

	}
	, (error, response, body) => {

		if (!error && response.statusCode === 200) {

			$ = cheerio.load(response);
			var topicList = $('.grid tbody tr');
			// Pick random topic to pester. The -1 / +1 here looks goofy, but is required to make sure we don't get 0 as output
			var target = topicList[Math(Math.floor((Math.random() * topicList.length - 1)) + 1)];
			var topicHref = target('td a')[0].attr('href');

			console.log("Scraped href: " + topicHref);

			app.getMessageList(topicHref);
		}

		else {
			console.log("Topic list GET failed.");
			throw error;
		}

	});
};

app.getTopicList = function(url) {

	request({

		url: url
		jar: app.cookieJar

	}, (error, response, body) => {

		if (!error && response.statusCode === 200) {

			$ = cheerio.load(response);
			// Can't make POST requests without the value of this token, scraped from quickpost area
			currentToken = $('.quickpost input[name="h"]').value();
			app.contributeToDiscussion();
		}

		else {
			console.log("Topic list GET failed.");
			throw error;
		}

	});
};

app.contributeToDiscussion = function() {
	const QUICKPOST_URL = "http://boards.endoftheinter.net/async-post.php";

	var formData = {};
			formData.topic = currentTopic;
			formData.h = currentToken;
			formData.message = app.generateMarkovChain(true); // Pass true to get return value

	request.post({

		url: QUICKPOST_URL,
		form: formData,

	}, (error, response, body) => {

			if (!error && response.statusCode === 200) {
				console.log("Post successful");
			}

			else {
				console.log("Post unsuccessful: code ", response.statusCode);
				throw error;
			}

	});
};<commit_msg>Fix syntax error<commit_after>/*
 *	Initial setup
 */

"use strict" // Otherwise we're stuck with ES5

const LOCAL_HOST = 3000;

// Variables required for Markov Chain generation
var input = [];
var firstWords = [];
var currentWord = "";

// Variables required for bottom
var currentTopic;
var currentToken;

// Set up modules
var express = require("express");
var request = require("request");
var bodyParser = require("body-parser");
var cheerio = require('cheerio');
var client = require('redis').createClient(process.env.REDIS_URL);

// Set up app
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.cachedData = { "post": "" }; // Basic format for JSON response
app.loggedIn = false; // Indicates whether app has login cookie for ETI

// Set up routing for API
var routes = require("./routes/routes.js")(app);

// Set up server
var server = app.listen(process.env.PORT || LOCAL_HOST, () => {
	console.log(process.env.USERNAME + "-bot is sentient and ready to shitpost");
});

/**
	*		Redis db handler
	*/

client.on("connect", () => {
		// Attempt to get "quotes" array from Redis
		client.lrange("quotes", 0, -1, (error, items) => {
			if (error) {
				// TODO: Store emergency vesper.txt somewhere in repo and use that instead
				throw error;
			}
			else {
				input = items;
				app.generateMarkovChain();
			}
		});
});

/**
  * Markov chain methods
	*/

app.generateMarkovChain = function(shouldReturn) {
	var output = [];

	app.createArrays();

	// Pick random first word to start with
	currentWord = firstWords[Math.floor((Math.random() * firstWords.length))];

	for (let i = 0, len = app.getPostLength(); i < len; i++) {
		output.push(currentWord);

		var nextWord = app.generateNextWord();

		if (nextWord) {
			currentWord = nextWord;
		}
		else {
			currentWord = "";
		}
	}

	app.cachedData.post = output.join(" ").trim();

	if (shouldReturn) {
		return app.cachedData.post;
	}
};

app.getPostLength = function() {
	return input[Math.floor((Math.random() * input.length))].length;
};

app.createArrays = function() {
	// Create array containing first word of each sentence
	for (let i = 0, len = input.length; i < len; i++) {
		let sentence = input[i];
		let firstWord = sentence.split(" ")[0];
		firstWords.push(firstWord);
	}
};

app.generateNextWord = function() {
	var tempArray = [];
	// Iterate over input array to find instances of current word
	for (let i = 0, len = input.length - 1; i < len; i++) {
		let sentenceToCheck = input[i].split(" ");

		for (let j = 0, len = sentenceToCheck.length; j < len; j++) {
			let wordToCheck = sentenceToCheck[j];
			// create array of words that are likely to follow current word
			if (wordToCheck === currentWord) {
				// Make sure that item at index i + 1 exists
				if (j < len - 1) {
					let nextWord = sentenceToCheck[j + 1];
					tempArray.push(nextWord);
				}
				else {
					// reached end of array - do nothing
				}
			}
		}
	}

	let rnd = Math.floor((Math.random() * tempArray.length));

	if (tempArray[rnd]) {
		return tempArray[rnd];
	}

	else {
		return;
	}
};

/**
  *		Method which allows us to add new quotes from pastebin raw links
  */

app.addNewQuotes = function(url) {
	request.get(url, ((error, response, body) => {
		if (!error && response.statusCode == 200) {
			// Ignore blank lines,
			var quotes = body.split("\r\n").filter((line) => line && line.indexOf("<") == -1 && line.charAt(0) !== "<");

			client.lrange("quotes", 0, -1, (error, items) => {
				if (error) {
					throw error;
				}
				else {
					// Update local & disk cache
					input = input.concat(quotes);
					client.rpush.apply(client, ["quotes"].concat(quotes).concat(() => { console.log("Quotes stored to redis") }));
				}
			});

		}

	}));
};

/**
  *		Log into ETI using environment vars as credentials
  */

app.loginToBlueSite = function() {
	const LOGIN_URL = "https://endoftheinter.net/";
	const formData = { b: process.env.USERNAME, p: process.env.PASSWORD };

	request.post({

		headers: {"content-type": "application/x-www-form-urlencoded"},
		url: LOGIN_URL,
		form: formData,
		jar: app.cookieJar

	}, (error, response, body) => {

			// After successful login, ETI will attempt to redirect you to homepage
			if (!error && response.statusCode === 302) {
					app.cookieJar.setCookie("userid=" + process.env.USER_ID);
					app.loggedIn = true;
			}

			else {
				console.log("Login failed.");
				throw error;
			}
	});
};

app.getTopicList = function() {
	var LUE_TOPICS = "https://boards.endoftheinter.net/topics/LUE";

	request({

		url: LUE_TOPICS
		jar: app.cookieJar

	}
	, (error, response, body) => {

		if (!error && response.statusCode === 200) {

			$ = cheerio.load(response);
			var topicList = $('.grid tbody tr');
			// Pick random topic to pester. The -1 / +1 here looks goofy, but is required to make sure we don't get 0 as output
			var target = topicList[Math(Math.floor((Math.random() * topicList.length - 1)) + 1)];
			var topicHref = target('td a')[0].attr('href');

			console.log("Scraped href: " + topicHref);

			app.getMessageList(topicHref);
		}

		else {
			console.log("Topic list GET failed.");
			throw error;
		}

	});
};

app.getTopicList = function(url) {

	request({

		url: url
		jar: app.cookieJar

	}, (error, response, body) => {

		if (!error && response.statusCode === 200) {

			$ = cheerio.load(response);
			// Can't make POST requests without the value of this token, scraped from quickpost area
			currentToken = $('.quickpost input[name="h"]').value();
			app.contributeToDiscussion();
		}

		else {
			console.log("Topic list GET failed.");
			throw error;
		}

	});
};

app.contributeToDiscussion = function() {
	const QUICKPOST_URL = "http://boards.endoftheinter.net/async-post.php";

	var formData = {};
			formData.topic = currentTopic;
			formData.h = currentToken;
			formData.message = app.generateMarkovChain(true); // Pass true to get return value
			formData.submit = "Post Message";

	request.post({

		url: QUICKPOST_URL,
		form: formData,

	}, (error, response, body) => {

			if (!error && response.statusCode === 200) {
				console.log("Post successful");
			}

			else {
				console.log("Post unsuccessful: code ", response.statusCode);
				throw error;
			}

	});
};<|endoftext|>


<commit_before>import React from 'react';
import { Trans } from 'react-i18next';
import { Link } from 'rebass/styled-components';
import defaultsDeep from 'lodash/defaultsDeep';

import i18next from '../../core/language';
import { MGDL_UNITS, MMOLL_UNITS } from '../../core/constants';
import { translateBg, roundBgTarget } from '../../core/utils';

const t = i18next.t.bind(i18next);

export const dateFormat = 'YYYY-MM-DD';
export const phoneRegex = /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/;

export const revisionStates = ['draft', 'pending', 'submitted'];

// TODO: placeholder device id's until provided by upcoming devices api
export const pumpDeviceOptions = [
  {
    value: 'omnipodId',
    label: t('Omnipod Horizon'),
    extraInfo: (
      <Trans>
        Find information on how to prescribe Omnipod products <Link href="#">here</Link>.
      </Trans>
    ),
  },
];

export const cgmDeviceOptions = [
  {
    value: 'dexcomId',
    label: t('Dexcom G6'),
    extraInfo: (
      <Trans>
        Find information on how to prescribe Dexcom G6 sensors and transmitters and more <Link href="#">here</Link>.
      </Trans>
    ),
  },
];

export const defaultUnits = {
  basalRate: 'Units/hour',
  bolusAmount: 'Units',
  insulinCarbRatio: 'g/U',
  bloodGlucose: MGDL_UNITS,
};

export const warningThresholds = (bgUnits = defaultUnits.bloodGlucose) => {
  const lowWarning = t('The value you have chosen is lower than Tidepool generally recommends.');
  const highWarning = t('The value you have chosen is higher than Tidepool generally recommends.');

  const thresholds = {
    basalRate: {
      low: { value: 0, message: lowWarning },
    },
    bloodGlucoseTarget: {
      low: { value: 70, message: lowWarning },
      high: { value: 120, message: highWarning },
    },
    bolusAmountMaximum: {
      high: { value: 20, message: highWarning },
    },
    carbRatio: {
      low: { value: 3, message: lowWarning },
      high: { value: 28, message: highWarning },
    },
    insulinSensitivityFactor: {
      low: { value: 15, message: lowWarning },
      high: { value: 400, message: highWarning },
    },
    suspendThreshold: {
      low: { value: 70, message: lowWarning },
      high: { value: 120, message: highWarning },
    },
  };

  if (bgUnits === MMOLL_UNITS) {
    thresholds.bloodGlucoseTarget.low.value = roundBgTarget(translateBg(thresholds.bloodGlucoseTarget.low.value, MMOLL_UNITS), MMOLL_UNITS);
    thresholds.bloodGlucoseTarget.high.value = roundBgTarget(translateBg(thresholds.bloodGlucoseTarget.high.value, MMOLL_UNITS), MMOLL_UNITS);

    thresholds.insulinSensitivityFactor.low.value = roundBgTarget(translateBg(thresholds.insulinSensitivityFactor.low.value, MMOLL_UNITS), MMOLL_UNITS);
    thresholds.insulinSensitivityFactor.high.value = roundBgTarget(translateBg(thresholds.insulinSensitivityFactor.high.value, MMOLL_UNITS), MMOLL_UNITS);

    thresholds.suspendThreshold.low.value = roundBgTarget(translateBg(thresholds.suspendThreshold.low.value, MMOLL_UNITS), MMOLL_UNITS);
    thresholds.suspendThreshold.high.value = roundBgTarget(translateBg(thresholds.suspendThreshold.high.value, MMOLL_UNITS), MMOLL_UNITS);
  }

  return thresholds;
};

export const defaultValues = (bgUnits = defaultUnits.bloodGlucose) => {
  const values = {
    basalRate: 0.05,
    basalRateMaximum: 0,
    bloodGlucoseTarget: {
      high: 125,
      low: 112,
    },
    bolusAmountMaximum: 10,
    carbRatio: 10,
    insulinSensitivityFactor: 100,
    suspendThreshold: 80,
  }

  if (bgUnits === MMOLL_UNITS) {
    values.bloodGlucoseTarget = {
      high: roundBgTarget(translateBg(values.bloodGlucoseTarget.high, MMOLL_UNITS), MMOLL_UNITS),
      low: roundBgTarget(translateBg(values.bloodGlucoseTarget.low, MMOLL_UNITS), MMOLL_UNITS),
    }

    values.insulinSensitivityFactor = roundBgTarget(translateBg(values.insulinSensitivityFactor, MMOLL_UNITS), MMOLL_UNITS);
    values.suspendThreshold = roundBgTarget(translateBg(values.suspendThreshold, MMOLL_UNITS), MMOLL_UNITS);
  }

  return values;
};

export const defaultRanges = (bgUnits = defaultUnits.bloodGlucose) => {
  const ranges = {
    basalRate: { min: 0, max: 35, step: 0.05 }, // will need to enforce step in case user types in invalid value
    basalRateMaximum: { min: 0, max: 35, step: 0.25 },
    bloodGlucoseTarget: { min: 60, max: 180, step: 1 },
    bolusAmountMaximum: { min: 0, max: 30, step: 1 },
    carbRatio: { min: 0, max: 250, step: 1 },
    insulinSensitivityFactor: { min: 0, max: 1000, step: 1 },
    suspendThreshold: { min: 54, max: 180, step: 1 },
  };

  if (bgUnits === MMOLL_UNITS) {
    ranges.bloodGlucoseTarget.min = roundBgTarget(translateBg(ranges.bloodGlucoseTarget.min, MMOLL_UNITS), MMOLL_UNITS);
    ranges.bloodGlucoseTarget.max = roundBgTarget(translateBg(ranges.bloodGlucoseTarget.max, MMOLL_UNITS), MMOLL_UNITS);
    ranges.bloodGlucoseTarget.step = 0.1;

    ranges.insulinSensitivityFactor.min = roundBgTarget(translateBg(ranges.insulinSensitivityFactor.min, MMOLL_UNITS), MMOLL_UNITS);
    ranges.insulinSensitivityFactor.max = roundBgTarget(translateBg(ranges.insulinSensitivityFactor.max, MMOLL_UNITS), MMOLL_UNITS);
    ranges.insulinSensitivityFactor.step = 0.1;

    ranges.suspendThreshold.min = roundBgTarget(translateBg(ranges.suspendThreshold.min, MMOLL_UNITS), MMOLL_UNITS);
    ranges.suspendThreshold.max = roundBgTarget(translateBg(ranges.suspendThreshold.max, MMOLL_UNITS), MMOLL_UNITS);
    ranges.suspendThreshold.step = 0.1;
  }

  return ranges;
};

// TODO: placeholder device-specific values until provided by the upcoming devices api.
export const deviceMeta = (deviceId, bgUnits = defaultUnits.bloodGlucose) => {
  const metaByDeviceId = {
    dexcomId: {
      manufacturerName: 'Dexcom',
    },
    omnipodId: {
      manufacturerName: 'Omnipod',
      ranges: defaultsDeep({
        basalRate: { max: 30 },
        basalRateMaximum: { max: 30 },
        carbRatio: { max: 150 },
      }, defaultRanges(bgUnits))
    },
  };

  return metaByDeviceId[deviceId] || {
    manufacturerName: 'Unknown',
    ranges: defaultRanges,
  };
};

export const typeOptions = [
  { value: 'patient', label: t('Patient') },
  { value: 'caregiver', label: t('Patient and caregiver') },
];

export const sexOptions = [
  { value: 'female', label: t('Female') },
  { value: 'male', label: t('Male') },
  { value: 'undisclosed', label: t('Prefer not to specify') },
];

export const trainingOptions = [
  { value: 'inPerson', label: t('Yes, Patient requires in-person CPT training') },
  { value: 'inModule', label: t('No, Patient can self start with Tidepool Loop in-app tutorial') },
];

export const insulinTypeOptions = [
  { value: 'rapidAdult', label: t('Rapid Acting Adult') },
  { value: 'rapidChild', label: t('Rapid Acting Child') },
];

export const validCountryCodes = [1];
<commit_msg>[WEB-821] Fix ranges for deviceMeta when passed unknown deviceId<commit_after>import React from 'react';
import { Trans } from 'react-i18next';
import { Link } from 'rebass/styled-components';
import defaultsDeep from 'lodash/defaultsDeep';

import i18next from '../../core/language';
import { MGDL_UNITS, MMOLL_UNITS } from '../../core/constants';
import { translateBg, roundBgTarget } from '../../core/utils';

const t = i18next.t.bind(i18next);

export const dateFormat = 'YYYY-MM-DD';
export const phoneRegex = /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/;

export const revisionStates = ['draft', 'pending', 'submitted'];

// TODO: placeholder device id's until provided by upcoming devices api
export const pumpDeviceOptions = [
  {
    value: 'omnipodId',
    label: t('Omnipod Horizon'),
    extraInfo: (
      <Trans>
        Find information on how to prescribe Omnipod products <Link href="#">here</Link>.
      </Trans>
    ),
  },
];

export const cgmDeviceOptions = [
  {
    value: 'dexcomId',
    label: t('Dexcom G6'),
    extraInfo: (
      <Trans>
        Find information on how to prescribe Dexcom G6 sensors and transmitters and more <Link href="#">here</Link>.
      </Trans>
    ),
  },
];

export const defaultUnits = {
  basalRate: 'Units/hour',
  bolusAmount: 'Units',
  insulinCarbRatio: 'g/U',
  bloodGlucose: MGDL_UNITS,
};

export const warningThresholds = (bgUnits = defaultUnits.bloodGlucose) => {
  const lowWarning = t('The value you have chosen is lower than Tidepool generally recommends.');
  const highWarning = t('The value you have chosen is higher than Tidepool generally recommends.');

  const thresholds = {
    basalRate: {
      low: { value: 0, message: lowWarning },
    },
    bloodGlucoseTarget: {
      low: { value: 70, message: lowWarning },
      high: { value: 120, message: highWarning },
    },
    bolusAmountMaximum: {
      high: { value: 20, message: highWarning },
    },
    carbRatio: {
      low: { value: 3, message: lowWarning },
      high: { value: 28, message: highWarning },
    },
    insulinSensitivityFactor: {
      low: { value: 15, message: lowWarning },
      high: { value: 400, message: highWarning },
    },
    suspendThreshold: {
      low: { value: 70, message: lowWarning },
      high: { value: 120, message: highWarning },
    },
  };

  if (bgUnits === MMOLL_UNITS) {
    thresholds.bloodGlucoseTarget.low.value = roundBgTarget(translateBg(thresholds.bloodGlucoseTarget.low.value, MMOLL_UNITS), MMOLL_UNITS);
    thresholds.bloodGlucoseTarget.high.value = roundBgTarget(translateBg(thresholds.bloodGlucoseTarget.high.value, MMOLL_UNITS), MMOLL_UNITS);

    thresholds.insulinSensitivityFactor.low.value = roundBgTarget(translateBg(thresholds.insulinSensitivityFactor.low.value, MMOLL_UNITS), MMOLL_UNITS);
    thresholds.insulinSensitivityFactor.high.value = roundBgTarget(translateBg(thresholds.insulinSensitivityFactor.high.value, MMOLL_UNITS), MMOLL_UNITS);

    thresholds.suspendThreshold.low.value = roundBgTarget(translateBg(thresholds.suspendThreshold.low.value, MMOLL_UNITS), MMOLL_UNITS);
    thresholds.suspendThreshold.high.value = roundBgTarget(translateBg(thresholds.suspendThreshold.high.value, MMOLL_UNITS), MMOLL_UNITS);
  }

  return thresholds;
};

export const defaultValues = (bgUnits = defaultUnits.bloodGlucose) => {
  const values = {
    basalRate: 0.05,
    basalRateMaximum: 0,
    bloodGlucoseTarget: {
      high: 125,
      low: 112,
    },
    bolusAmountMaximum: 10,
    carbRatio: 10,
    insulinSensitivityFactor: 100,
    suspendThreshold: 80,
  }

  if (bgUnits === MMOLL_UNITS) {
    values.bloodGlucoseTarget = {
      high: roundBgTarget(translateBg(values.bloodGlucoseTarget.high, MMOLL_UNITS), MMOLL_UNITS),
      low: roundBgTarget(translateBg(values.bloodGlucoseTarget.low, MMOLL_UNITS), MMOLL_UNITS),
    }

    values.insulinSensitivityFactor = roundBgTarget(translateBg(values.insulinSensitivityFactor, MMOLL_UNITS), MMOLL_UNITS);
    values.suspendThreshold = roundBgTarget(translateBg(values.suspendThreshold, MMOLL_UNITS), MMOLL_UNITS);
  }

  return values;
};

export const defaultRanges = (bgUnits = defaultUnits.bloodGlucose) => {
  const ranges = {
    basalRate: { min: 0, max: 35, step: 0.05 }, // will need to enforce step in case user types in invalid value
    basalRateMaximum: { min: 0, max: 35, step: 0.25 },
    bloodGlucoseTarget: { min: 60, max: 180, step: 1 },
    bolusAmountMaximum: { min: 0, max: 30, step: 1 },
    carbRatio: { min: 0, max: 250, step: 1 },
    insulinSensitivityFactor: { min: 0, max: 1000, step: 1 },
    suspendThreshold: { min: 54, max: 180, step: 1 },
  };

  if (bgUnits === MMOLL_UNITS) {
    ranges.bloodGlucoseTarget.min = roundBgTarget(translateBg(ranges.bloodGlucoseTarget.min, MMOLL_UNITS), MMOLL_UNITS);
    ranges.bloodGlucoseTarget.max = roundBgTarget(translateBg(ranges.bloodGlucoseTarget.max, MMOLL_UNITS), MMOLL_UNITS);
    ranges.bloodGlucoseTarget.step = 0.1;

    ranges.insulinSensitivityFactor.min = roundBgTarget(translateBg(ranges.insulinSensitivityFactor.min, MMOLL_UNITS), MMOLL_UNITS);
    ranges.insulinSensitivityFactor.max = roundBgTarget(translateBg(ranges.insulinSensitivityFactor.max, MMOLL_UNITS), MMOLL_UNITS);
    ranges.insulinSensitivityFactor.step = 0.1;

    ranges.suspendThreshold.min = roundBgTarget(translateBg(ranges.suspendThreshold.min, MMOLL_UNITS), MMOLL_UNITS);
    ranges.suspendThreshold.max = roundBgTarget(translateBg(ranges.suspendThreshold.max, MMOLL_UNITS), MMOLL_UNITS);
    ranges.suspendThreshold.step = 0.1;
  }

  return ranges;
};

// TODO: placeholder device-specific values until provided by the upcoming devices api.
export const deviceMeta = (deviceId, bgUnits = defaultUnits.bloodGlucose) => {
  const metaByDeviceId = {
    dexcomId: {
      manufacturerName: 'Dexcom',
    },
    omnipodId: {
      manufacturerName: 'Omnipod',
      ranges: defaultsDeep({
        basalRate: { max: 30 },
        basalRateMaximum: { max: 30 },
        carbRatio: { max: 150 },
      }, defaultRanges(bgUnits))
    },
  };

  return metaByDeviceId[deviceId] || {
    manufacturerName: 'Unknown',
    ranges: defaultRanges(bgUnits),
  };
};

export const typeOptions = [
  { value: 'patient', label: t('Patient') },
  { value: 'caregiver', label: t('Patient and caregiver') },
];

export const sexOptions = [
  { value: 'female', label: t('Female') },
  { value: 'male', label: t('Male') },
  { value: 'undisclosed', label: t('Prefer not to specify') },
];

export const trainingOptions = [
  { value: 'inPerson', label: t('Yes, Patient requires in-person CPT training') },
  { value: 'inModule', label: t('No, Patient can self start with Tidepool Loop in-app tutorial') },
];

export const insulinTypeOptions = [
  { value: 'rapidAdult', label: t('Rapid Acting Adult') },
  { value: 'rapidChild', label: t('Rapid Acting Child') },
];

export const validCountryCodes = [1];
<|endoftext|>


<commit_before>define( [ "Ember" ], function( Ember ) {

	var concat = [].concat;
	var makeArray = Ember.makeArray;
	var isNone = Ember.isNone;

	return function preload( withError, list ) {
		if ( list === undefined ) {
			list = withError;
			withError = false;
		}

		function promiseImage( src ) {
			if ( isNone( src ) ) {
				return Promise.resolve();
			}

			var defer = Promise.defer();
			var image = new Image();
			image.addEventListener( "load", defer.resolve, false );
			image.addEventListener( "error", withError ? defer.reject : defer.resolve, false );
			image.src = src;
			return defer.promise;
		}

		return function promisePreload( response ) {
			// create a new promise containing all image preload promises
			return Promise.all(
				// create a flat array out of all traversal strings
				makeArray( list ).reduce(function createPromiseList( promises, traverse ) {
					// traverse response data
					var resources = makeArray( response ).mapBy( traverse );

					// data instanceof Ember.Enumerable
					resources = resources && resources.toArray
						? resources.toArray()
						: makeArray( resources );

					// preload images
					return concat.apply( promises, resources.map( promiseImage ) );
				}, [] )
			)
				// return the original response
				.then(function preloadFulfilled() { return response; });
		};
	};

});
<commit_msg>Fix empty images sources in utils/preload<commit_after>define( [ "Ember" ], function( Ember ) {

	var concat = [].concat;
	var makeArray = Ember.makeArray;

	return function preload( withError, list ) {
		if ( list === undefined ) {
			list = withError;
			withError = false;
		}

		function promiseImage( src ) {
			if ( !src ) {
				return Promise.resolve();
			}

			var defer = Promise.defer();
			var image = new Image();
			image.addEventListener( "load", defer.resolve, false );
			image.addEventListener( "error", withError ? defer.reject : defer.resolve, false );
			image.src = src;
			return defer.promise;
		}

		return function promisePreload( response ) {
			// create a new promise containing all image preload promises
			return Promise.all(
				// create a flat array out of all traversal strings
				makeArray( list ).reduce(function createPromiseList( promises, traverse ) {
					// traverse response data
					var resources = makeArray( response ).mapBy( traverse );

					// data instanceof Ember.Enumerable
					resources = resources && resources.toArray
						? resources.toArray()
						: makeArray( resources );

					// preload images
					return concat.apply( promises, resources.map( promiseImage ) );
				}, [] )
			)
				// return the original response
				.then(function preloadFulfilled() { return response; });
		};
	};

});
<|endoftext|>


<commit_before>"use strict";
/* -------------------------------------------------------------------
 * Require Statements << Keep in alphabetical order >>
 * ---------------------------------------------------------------- */

var ChildProcess = require('child_process');
var debug = require('debug')('oshi:child');
var Enums = require('./enums');
var Fs = require('fs');
var Path = require('path');
var FlexEvents = require('flex-events');
var mkdirp = require('mkdirp');

var STATUS = Enums.STATUS;

/* =============================================================================
 *
 * Oshi.Child Class
 *
 * ========================================================================== */

module.exports = Child;

/**
 * @constructor
 * @param group {Group}
 * @param config {ChildConfig}
 */
function Child (group, config)
{
	/* -------------------------------------------------------------------
	 * Private Members Declaration << no methods >>
	 * ---------------------------------------------------------------- */

	var _this = this;

	var _events = FlexEvents.setup(this, group);
	/** @type {ChildProcess} */
	var _process = null;

	/* -------------------------------------------------------------------
	 * Public Members Declaration << no methods >>
	 * ---------------------------------------------------------------- */

	/** @member {Group} */
	this.group = group;
	/** @member {string} */
	this.groupName = config.groupName;
	/** @member {number} */
	this.port = config.port;
	/** @member {string} */
	this.shortName = this.groupName + ':' + this.port;
	/** @member {ChildConfig} */
	this.config = config;
	this.status = STATUS.STOPPED;
	this.errors = [];
	this.crashCount = 0;
	/** @type {Object.<string, fs.WriteStream>} */
	this.logStreams = {};

	Object.defineProperty(this, 'info', {
		/** @returns {{group: string, port: number, handled: boolean}} */
		get: function ()
		{
			return {
				group: _this.groupName,
				port: _this.port,
				handled: false
			};
		}
	});

	Object.defineProperty(this, 'process', {
		/** @returns {ChildProcess} */
		get: function ()
		{
			return _process;
		},
		/** @param proc {ChildProcess} */
		set: function (proc)
		{
			disconnectProcess.call(_this, _process);
			_process = proc;
			connectProcess.call(_this, _process);
		}
	});

	/* -------------------------------------------------------------------
	 * Public Methods << Keep in alphabetical order >>
	 * ---------------------------------------------------------------- */

	this.kill = function ()
	{
		if (_this.process !== null)
			_this.process.kill('SIGKILL');
	}

	this.destroy = function ()
	{
		if (this.running)
			throw new Error('Cannot destroy. Process is still running ' + this.groupName + ':' + this.port);

		_events.destroy();
		return true;
	};

	/**
	 * @param error {Error}
	 */
	this.onError = function (error)
	{
		try
		{
			var data =
			{
				error: error
			};

			_this.errors.push(error); // TODO: limit the number of errors to keep
			_this.invoke('error', _this.info, data);

			_this.status = STATUS.ERROR;
			_this.process = null;
		}
		catch (ex)
		{
			//TODO ?
			console.error(ex);
		}
	};

	/**
	 * @param code {number}
	 * @param signal {string}
	 */
	this.onExit = function (code, signal)
	{
		try
		{
			debug(_this.shortName + ' exited');

			var data =
			{
				code: code,
				signal: signal,
				expected: _this.status === STATUS.STOPPING
			};

			var info = _this.info;
			_this.invoke('exit', info, data);
			_this.process = null;
			_this.status = STATUS.STOPPED;

			if (!data.expected)
			{
				_this.crashCount++;
				debug(_this.shortName + ' exit was unexpected');
			}

			if (!info.handled)
			{
				debug(_this.shortName + ' attempting to restart');
				// exit event wasn't handled by another listener, so just try to restart
				_this.start(null, function (error, response)
				{
					if (error)
					{
						//TODO ?
						console.error(error);
					}

					debug(_this.shortName + ' restarted');
				});
			}

		}
		catch (ex)
		{
			//TODO ?
			console.error(ex);
		}
	};

	/**
	 * @param data {*}
	 */
	this.onMessage = function (data)
	{
		try
		{
			if (data && typeof data === 'string' && data === _this.group.config.readyEvent)
			{
				_this.invoke('ready', _this.info);
			}
			else if (data && typeof data === 'object' && data.type === 'broadcast')
			{
				_this.invoke('broadcast', _this.info, data);
			}
			else
			{
				_this.invoke('message', _this.info, data);
			}
		}
		catch (ex)
		{
			//TODO ?
			console.error(ex);
		}
	};
}

/* -------------------------------------------------------------------
 * Public Prototype Members Declaration << no methods >>
 * ---------------------------------------------------------------- */

Object.defineProperty(Child.prototype, 'running', {
	/** @returns {boolean} */
	get: function ()
	{
		return this.process ? (this.process.connected && this.status < STATUS.STOPPED) : false;
	}
});

/* -------------------------------------------------------------------
 * Public Prototype Methods << Keep in alphabetical order >>
 * ---------------------------------------------------------------- */

/**
 * @function
 * @param newConfig {ChildConfig}
 * @param callback {function(Error, StartResponse)}
 * @param [stopResponse] {StopResponse}
 */
Child.prototype.start = function (newConfig, callback, stopResponse)
{
	if (newConfig)
		this.config.update(newConfig);

	var childConf = this.config;
	var groupConf = this.group.config;

	var _this = this;
	var response = new StartResponse(childConf);

	if (stopResponse)
	{
		response.restarted = true; // should this actually be stopResponse.stopped?
		response.forceRestarted = stopResponse.forced;
	}

	// first check to see if this is already running
	if (this.running)
	{
		if (childConf.ifNotRunning)
		{
			callback(null, response);
			return;
		}

		if (!childConf.restart)
		{
			callback(new Error('Process is already running.'), null);
			return;
		}

		// need to stop the process first
		this.stop(null, function (error, res)
		{
			if (error)
			{
				callback(error, null);
				return;
			}

			try
			{
				_this.start(null, callback, res);
			}
			catch (ex)
			{
				callback(ex, null);
			}
		});

		return;
	}

	var args = groupConf.nodeArgs.concat(groupConf.script, groupConf.args);
	if (childConf.debug && groupConf.nodeArgs.indexOf('--debug') === -1)
		args.unshift('--debug');

	if (childConf.args instanceof Array)
		args = args.concat(childConf.args);

	var pOptions =
	{
		cwd: groupConf.cwd,
		env: groupConf.env,
		stdio: [ 'ignore', 'pipe', 'pipe', 'ipc' ]
	};

	this.status = STATUS.STARTING;
	this.process = ChildProcess.spawn(groupConf.nodeExec, args, pOptions);

	this.attach('error', startError);
	this.attach('exit', startExit);
	this.attach('ready', startReady);

	setTimeout(startTimeout, groupConf.initTimeout);

	function startError (e, info, data)
	{
		if (callback)
		{
			info.handled = true;
			done(data.error);
		}
	}

	function startExit (e, info, data)
	{
		if (callback)
		{
			info.handled = true;
			response.crashed = true;
			done();
		}
	}

	function startReady (e)
	{
		if (callback)
		{
			response.started = true;
			response.ready = true;
			done();
		}
	}

	function startTimeout ()
	{
		if (callback)
		{
			response.started = true;
			done();
		}
	}

	function done (error)
	{
		try
		{
			_this.detach('error', startError);
			_this.detach('exit', startExit);
			_this.detach('ready', startReady);

			_this.status = error ? STATUS.ERROR : STATUS.RUNNING;

			callback(error, response);
			callback = null;
		}
		catch (ex)
		{
			//TODO ?
			console.error(ex);
		}
	}
};

/**
 * @param newConfig {ChildConfig}
 * @param callback {function(Error, StopResponse)}
 */
Child.prototype.stop = function (newConfig, callback)
{
	if (newConfig)
		this.config.update(newConfig);

	var _this = this;
	var groupConf = this.group.config;
	var response = new StopResponse(this.config);

	if (!this.running)
	{
		debug(this.shortName + ' already stopped');
		setImmediate(callback, null, response);
		return;
	}

	// setup exit listener
	this.attach('exit', exitListener);

	// save a reference to the current process, since this.process could change before the graceful timeout
	var origProc = this.process;
	this.status = STATUS.STOPPING;

	//try to signal the process to gracefully stop
	if (process.platform === 'win32' && groupConf.useMessageOnWindows)
	{
		// special mode since Windows doesn't support signals
		debug('Using Windows graceful shutdown mode');
		this.process.send(groupConf.gracefulSignal);
	}
	else
	{
		this.process.kill(groupConf.gracefulSignal);
	}

	//give the process time to gracefully terminate
	debug(this.shortName + ' attempting graceful termination. Timeout ' + groupConf.gracefulTimeout);
	var interval = setInterval(killCheck, 50);
	var killAfter = Date.now() + groupConf.gracefulTimeout;

	function exitListener (e, info, data)
	{
		try
		{
			_this.detach('exit', exitListener);
			info.handled = true;
			response.stopped = true;
			if (callback)
				callback(null, response);
			callback = null;
		}
		catch (ex)
		{
			//TODO ?
			console.error(ex);
		}
	}

	function killCheck ()
	{
		try
		{
			if (!origProc.connected && _this.status >= STATUS.STOPPED) // process terminated, we don't need to force kill it
			{
				clearInterval(interval);

				if (callback)
				{
					callback(null, response);
					callback = null;
				}

				return;
			}

			if (Date.now() < killAfter)
				return;

			clearInterval(interval);

			debug(_this.shortName + ' attempting to force-kill');

			response.forced = true;
			origProc.kill('SIGKILL');
		}
		catch (ex)
		{
			if (typeof callback === 'function')
				callback(ex);
		}
	}
};

Child.prototype.rotateLogs = function (callback)
{
	if (!this.process)
	{
		debug('process property does not exist');
		callback(null);
		return;
	}

	try
	{
		debug('Disconnecting process');
		disconnectProcess.call(this, this.process);

		var logDir = this.group.daemon.config.logDir;
		debug('logDir: %s', logDir);

		var results = {};
		results.out = rotateLog.call(this, 'out', logDir);
		results.err = rotateLog.call(this, 'err', logDir);

		debug('Connecting process');
		connectProcess.call(this, this.process);

		setImmediate(callback, null, results);
	}
	catch (ex)
	{
		setImmediate(callback, ex);
	}
};

/* =============================================================================
 *
 * Private methods
 *
 * ========================================================================== */
function disconnectProcess (proc)
{
	if (!proc)
		return;

	proc.removeListener('error', this.onError);
	proc.removeListener('exit', this.onExit);
	proc.removeListener('message', this.onMessage);

	try
	{
		proc.stdout.unpipe();
		proc.stderr.unpipe();
	}
	catch (ex)
	{
		// don't really care if unpipe throws an exception... though I don't think it ever should
		console.error(ex);
	}

	try
	{
		this.logStreams.out.end();
		this.logStreams.err.end();
		this.logStreams = {};
	}
	catch (ex)
	{
		// don't really care if unpipe throws an exception... though I don't think it ever should
		console.error(ex);
	}
}

function connectProcess (proc)
{
	if (!proc)
		return;

	proc.on('error', this.onError);
	proc.on('exit', this.onExit);
	proc.on('message', this.onMessage);

	// setup log files
	// TODO: Make these rotatable
	var logDir = this.group.daemon.config.logDir;
	createLogfile.call(this, proc.stdout, 'out', logDir);
	createLogfile.call(this, proc.stderr, 'err', logDir);
}

function createLogfile (outStream, name, logDir)
{
	mkdirp(logDir);
	var filePath = getLogfilePath.call(this, logDir, name);
	var writeStream = Fs.createWriteStream(filePath, { flags: 'a' });
	this.logStreams[name] = writeStream;
	outStream.pipe(writeStream);
}

function getLogfilePath (logDir, name, ts)
{
	ts = ts === undefined ? '' : ('-' + ts);
	return Path.resolve(logDir, this.groupName + '_' + this.port + '.' + name + ts + '.log');
}

function getAvailableRotatedLogfilePath (logDir, name)
{
	var ts = new Date();
	ts = ts.toISOString().replace(/\W+/g, '');
	return getLogfilePath.call(this, logDir, name, ts);
}

function rotateLog (name, logDir)
{
	var src = getLogfilePath.call(this, logDir, name);
	var dst = getAvailableRotatedLogfilePath.call(this, logDir, name);

	Fs.renameSync(src, dst);

	return dst;
}

/* =============================================================================
 *
 * StartResponse Class
 *
 * ========================================================================== */

/**
 * @param conf {ChildConfig}
 * @constructor
 */
function StartResponse (conf)
{
	this.restarted = false;
	this.started = false;
	this.forceRestarted = false;
	this.ready = false;
	this.crashed = false;
	this.config = conf;
}

/* =============================================================================
 *
 * StopResponse Class
 *
 * ========================================================================== */

/**
 * @param conf {ChildConfig}
 * @constructor
 */
function StopResponse (conf)
{
	this.stopped = false;
	this.forced = false;
	this.config = conf;
}
<commit_msg>nullify child._process after sending SIGKILL<commit_after>"use strict";
/* -------------------------------------------------------------------
 * Require Statements << Keep in alphabetical order >>
 * ---------------------------------------------------------------- */

var ChildProcess = require('child_process');
var debug = require('debug')('oshi:child');
var Enums = require('./enums');
var Fs = require('fs');
var Path = require('path');
var FlexEvents = require('flex-events');
var mkdirp = require('mkdirp');

var STATUS = Enums.STATUS;

/* =============================================================================
 *
 * Oshi.Child Class
 *
 * ========================================================================== */

module.exports = Child;

/**
 * @constructor
 * @param group {Group}
 * @param config {ChildConfig}
 */
function Child (group, config)
{
	/* -------------------------------------------------------------------
	 * Private Members Declaration << no methods >>
	 * ---------------------------------------------------------------- */

	var _this = this;

	var _events = FlexEvents.setup(this, group);
	/** @type {ChildProcess} */
	var _process = null;

	/* -------------------------------------------------------------------
	 * Public Members Declaration << no methods >>
	 * ---------------------------------------------------------------- */

	/** @member {Group} */
	this.group = group;
	/** @member {string} */
	this.groupName = config.groupName;
	/** @member {number} */
	this.port = config.port;
	/** @member {string} */
	this.shortName = this.groupName + ':' + this.port;
	/** @member {ChildConfig} */
	this.config = config;
	this.status = STATUS.STOPPED;
	this.errors = [];
	this.crashCount = 0;
	/** @type {Object.<string, fs.WriteStream>} */
	this.logStreams = {};

	Object.defineProperty(this, 'info', {
		/** @returns {{group: string, port: number, handled: boolean}} */
		get: function ()
		{
			return {
				group: _this.groupName,
				port: _this.port,
				handled: false
			};
		}
	});

	Object.defineProperty(this, 'process', {
		/** @returns {ChildProcess} */
		get: function ()
		{
			return _process;
		},
		/** @param proc {ChildProcess} */
		set: function (proc)
		{
			disconnectProcess.call(_this, _process);
			_process = proc;
			connectProcess.call(_this, _process);
		}
	});

	/* -------------------------------------------------------------------
	 * Public Methods << Keep in alphabetical order >>
	 * ---------------------------------------------------------------- */

	this.kill = function ()
	{
		if (this.process !== null)
		{
			this.process.kill('SIGKILL');
			this._process = null;
		}
	}

	this.destroy = function ()
	{
		if (this.running)
			throw new Error('Cannot destroy. Process is still running ' + this.groupName + ':' + this.port);

		_events.destroy();
		return true;
	};

	/**
	 * @param error {Error}
	 */
	this.onError = function (error)
	{
		try
		{
			var data =
			{
				error: error
			};

			_this.errors.push(error); // TODO: limit the number of errors to keep
			_this.invoke('error', _this.info, data);

			_this.status = STATUS.ERROR;
			_this.process = null;
		}
		catch (ex)
		{
			//TODO ?
			console.error(ex);
		}
	};

	/**
	 * @param code {number}
	 * @param signal {string}
	 */
	this.onExit = function (code, signal)
	{
		try
		{
			debug(_this.shortName + ' exited');

			var data =
			{
				code: code,
				signal: signal,
				expected: _this.status === STATUS.STOPPING
			};

			var info = _this.info;
			_this.invoke('exit', info, data);
			_this.process = null;
			_this.status = STATUS.STOPPED;

			if (!data.expected)
			{
				_this.crashCount++;
				debug(_this.shortName + ' exit was unexpected');
			}

			if (!info.handled)
			{
				debug(_this.shortName + ' attempting to restart');
				// exit event wasn't handled by another listener, so just try to restart
				_this.start(null, function (error, response)
				{
					if (error)
					{
						//TODO ?
						console.error(error);
					}

					debug(_this.shortName + ' restarted');
				});
			}

		}
		catch (ex)
		{
			//TODO ?
			console.error(ex);
		}
	};

	/**
	 * @param data {*}
	 */
	this.onMessage = function (data)
	{
		try
		{
			if (data && typeof data === 'string' && data === _this.group.config.readyEvent)
			{
				_this.invoke('ready', _this.info);
			}
			else if (data && typeof data === 'object' && data.type === 'broadcast')
			{
				_this.invoke('broadcast', _this.info, data);
			}
			else
			{
				_this.invoke('message', _this.info, data);
			}
		}
		catch (ex)
		{
			//TODO ?
			console.error(ex);
		}
	};
}

/* -------------------------------------------------------------------
 * Public Prototype Members Declaration << no methods >>
 * ---------------------------------------------------------------- */

Object.defineProperty(Child.prototype, 'running', {
	/** @returns {boolean} */
	get: function ()
	{
		return this.process ? (this.process.connected && this.status < STATUS.STOPPED) : false;
	}
});

/* -------------------------------------------------------------------
 * Public Prototype Methods << Keep in alphabetical order >>
 * ---------------------------------------------------------------- */

/**
 * @function
 * @param newConfig {ChildConfig}
 * @param callback {function(Error, StartResponse)}
 * @param [stopResponse] {StopResponse}
 */
Child.prototype.start = function (newConfig, callback, stopResponse)
{
	if (newConfig)
		this.config.update(newConfig);

	var childConf = this.config;
	var groupConf = this.group.config;

	var _this = this;
	var response = new StartResponse(childConf);

	if (stopResponse)
	{
		response.restarted = true; // should this actually be stopResponse.stopped?
		response.forceRestarted = stopResponse.forced;
	}

	// first check to see if this is already running
	if (this.running)
	{
		if (childConf.ifNotRunning)
		{
			callback(null, response);
			return;
		}

		if (!childConf.restart)
		{
			callback(new Error('Process is already running.'), null);
			return;
		}

		// need to stop the process first
		this.stop(null, function (error, res)
		{
			if (error)
			{
				callback(error, null);
				return;
			}

			try
			{
				_this.start(null, callback, res);
			}
			catch (ex)
			{
				callback(ex, null);
			}
		});

		return;
	}

	var args = groupConf.nodeArgs.concat(groupConf.script, groupConf.args);
	if (childConf.debug && groupConf.nodeArgs.indexOf('--debug') === -1)
		args.unshift('--debug');

	if (childConf.args instanceof Array)
		args = args.concat(childConf.args);

	var pOptions =
	{
		cwd: groupConf.cwd,
		env: groupConf.env,
		stdio: [ 'ignore', 'pipe', 'pipe', 'ipc' ]
	};

	this.status = STATUS.STARTING;
	this.process = ChildProcess.spawn(groupConf.nodeExec, args, pOptions);

	this.attach('error', startError);
	this.attach('exit', startExit);
	this.attach('ready', startReady);

	setTimeout(startTimeout, groupConf.initTimeout);

	function startError (e, info, data)
	{
		if (callback)
		{
			info.handled = true;
			done(data.error);
		}
	}

	function startExit (e, info, data)
	{
		if (callback)
		{
			info.handled = true;
			response.crashed = true;
			done();
		}
	}

	function startReady (e)
	{
		if (callback)
		{
			response.started = true;
			response.ready = true;
			done();
		}
	}

	function startTimeout ()
	{
		if (callback)
		{
			response.started = true;
			done();
		}
	}

	function done (error)
	{
		try
		{
			_this.detach('error', startError);
			_this.detach('exit', startExit);
			_this.detach('ready', startReady);

			_this.status = error ? STATUS.ERROR : STATUS.RUNNING;

			callback(error, response);
			callback = null;
		}
		catch (ex)
		{
			//TODO ?
			console.error(ex);
		}
	}
};

/**
 * @param newConfig {ChildConfig}
 * @param callback {function(Error, StopResponse)}
 */
Child.prototype.stop = function (newConfig, callback)
{
	if (newConfig)
		this.config.update(newConfig);

	var _this = this;
	var groupConf = this.group.config;
	var response = new StopResponse(this.config);

	if (!this.running)
	{
		debug(this.shortName + ' already stopped');
		setImmediate(callback, null, response);
		return;
	}

	// setup exit listener
	this.attach('exit', exitListener);

	// save a reference to the current process, since this.process could change before the graceful timeout
	var origProc = this.process;
	this.status = STATUS.STOPPING;

	//try to signal the process to gracefully stop
	if (process.platform === 'win32' && groupConf.useMessageOnWindows)
	{
		// special mode since Windows doesn't support signals
		debug('Using Windows graceful shutdown mode');
		this.process.send(groupConf.gracefulSignal);
	}
	else
	{
		this.process.kill(groupConf.gracefulSignal);
	}

	//give the process time to gracefully terminate
	debug(this.shortName + ' attempting graceful termination. Timeout ' + groupConf.gracefulTimeout);
	var interval = setInterval(killCheck, 50);
	var killAfter = Date.now() + groupConf.gracefulTimeout;

	function exitListener (e, info, data)
	{
		try
		{
			_this.detach('exit', exitListener);
			info.handled = true;
			response.stopped = true;
			if (callback)
				callback(null, response);
			callback = null;
		}
		catch (ex)
		{
			//TODO ?
			console.error(ex);
		}
	}

	function killCheck ()
	{
		try
		{
			if (!origProc.connected && _this.status >= STATUS.STOPPED) // process terminated, we don't need to force kill it
			{
				clearInterval(interval);

				if (callback)
				{
					callback(null, response);
					callback = null;
				}

				return;
			}

			if (Date.now() < killAfter)
				return;

			clearInterval(interval);

			debug(_this.shortName + ' attempting to force-kill');

			response.forced = true;
			origProc.kill('SIGKILL');
			_this._process = null;
		}
		catch (ex)
		{
			if (typeof callback === 'function')
				callback(ex);
		}
	}
};

Child.prototype.rotateLogs = function (callback)
{
	if (!this.process)
	{
		debug('process property does not exist');
		callback(null);
		return;
	}

	try
	{
		debug('Disconnecting process');
		disconnectProcess.call(this, this.process);

		var logDir = this.group.daemon.config.logDir;
		debug('logDir: %s', logDir);

		var results = {};
		results.out = rotateLog.call(this, 'out', logDir);
		results.err = rotateLog.call(this, 'err', logDir);

		debug('Connecting process');
		connectProcess.call(this, this.process);

		setImmediate(callback, null, results);
	}
	catch (ex)
	{
		setImmediate(callback, ex);
	}
};

/* =============================================================================
 *
 * Private methods
 *
 * ========================================================================== */
function disconnectProcess (proc)
{
	if (!proc)
		return;

	proc.removeListener('error', this.onError);
	proc.removeListener('exit', this.onExit);
	proc.removeListener('message', this.onMessage);

	try
	{
		proc.stdout.unpipe();
		proc.stderr.unpipe();
	}
	catch (ex)
	{
		// don't really care if unpipe throws an exception... though I don't think it ever should
		console.error(ex);
	}

	try
	{
		this.logStreams.out.end();
		this.logStreams.err.end();
		this.logStreams = {};
	}
	catch (ex)
	{
		// don't really care if unpipe throws an exception... though I don't think it ever should
		console.error(ex);
	}
}

function connectProcess (proc)
{
	if (!proc)
		return;

	proc.on('error', this.onError);
	proc.on('exit', this.onExit);
	proc.on('message', this.onMessage);

	// setup log files
	// TODO: Make these rotatable
	var logDir = this.group.daemon.config.logDir;
	createLogfile.call(this, proc.stdout, 'out', logDir);
	createLogfile.call(this, proc.stderr, 'err', logDir);
}

function createLogfile (outStream, name, logDir)
{
	mkdirp(logDir);
	var filePath = getLogfilePath.call(this, logDir, name);
	var writeStream = Fs.createWriteStream(filePath, { flags: 'a' });
	this.logStreams[name] = writeStream;
	outStream.pipe(writeStream);
}

function getLogfilePath (logDir, name, ts)
{
	ts = ts === undefined ? '' : ('-' + ts);
	return Path.resolve(logDir, this.groupName + '_' + this.port + '.' + name + ts + '.log');
}

function getAvailableRotatedLogfilePath (logDir, name)
{
	var ts = new Date();
	ts = ts.toISOString().replace(/\W+/g, '');
	return getLogfilePath.call(this, logDir, name, ts);
}

function rotateLog (name, logDir)
{
	var src = getLogfilePath.call(this, logDir, name);
	var dst = getAvailableRotatedLogfilePath.call(this, logDir, name);

	Fs.renameSync(src, dst);

	return dst;
}

/* =============================================================================
 *
 * StartResponse Class
 *
 * ========================================================================== */

/**
 * @param conf {ChildConfig}
 * @constructor
 */
function StartResponse (conf)
{
	this.restarted = false;
	this.started = false;
	this.forceRestarted = false;
	this.ready = false;
	this.crashed = false;
	this.config = conf;
}

/* =============================================================================
 *
 * StopResponse Class
 *
 * ========================================================================== */

/**
 * @param conf {ChildConfig}
 * @constructor
 */
function StopResponse (conf)
{
	this.stopped = false;
	this.forced = false;
	this.config = conf;
}
<|endoftext|>


<commit_before>/*global app*/
"use strict";
var crypto = require('crypto');

module.exports.getGravatar = function (jid) {
    var gID = crypto.createHash('md5').update(jid).digest('hex');
    return {
        uri: 'https://gravatar.com/avatar/' + gID + '?s=80&d=mm'
    };
};

var getGravatar = module.exports.getGravatar;

module.exports.fetch = function (jid, id, type, source, cb) {
    if (!id) {
        return cb(getGravatar(jid));
    }

    app.storage.avatars.get(id, function (err, avatar) {
        if (!err) {
            return cb(avatar);
        }

        if (!type) {
            return cb(getGravatar(jid));
        }

        app.whenConnected(function () {
            if (source == 'vcard') {
                app.api.getVCard(jid, function (err, resp) {
                    if (err) {
                        return cb(getGravatar(jid));
                    }

                    type = resp.vCardTemp.photo.type || type;

                    var data = resp.vCardTemp.photo.data;
                    var uri = 'data:' + type + ';base64,' + data;

                    avatar = {
                        id: id,
                        type: type,
                        uri: uri
                    };

                    app.storage.avatars.add(avatar);
                    return cb(avatar);
                });
            } else {
                app.api.getAvatar(jid, id, function (err, resp) {
                    if (err) {
                        return cb(getGravatar(jid));
                    }

                    var data = resp.pubsub.retrieve.item.avatarData;
                    var uri = 'data:' + type + ';base64,' + data;

                    avatar = {
                        id: id,
                        type: type,
                        uri: uri
                    };

                    app.storage.avatars.add(avatar);
                    return cb(avatar);
                });
            }
        });
    });
};
<commit_msg>fix reference to absent vcard photo<commit_after>/*global app*/
"use strict";
var crypto = require('crypto');

module.exports.getGravatar = function (jid) {
    var gID = crypto.createHash('md5').update(jid).digest('hex');
    return {
        uri: 'https://gravatar.com/avatar/' + gID + '?s=80&d=mm'
    };
};

var getGravatar = module.exports.getGravatar;

module.exports.fetch = function (jid, id, type, source, cb) {
    if (!id) {
        return cb(getGravatar(jid));
    }

    app.storage.avatars.get(id, function (err, avatar) {
        if (!err) {
            return cb(avatar);
        }

        if (!type) {
            return cb(getGravatar(jid));
        }

        app.whenConnected(function () {
            if (source == 'vcard') {
                app.api.getVCard(jid, function (err, resp) {
                    if (err) {
                        return cb(getGravatar(jid));
                    }

		    if (!resp.vCardTemp.photo) return;

                    type = resp.vCardTemp.photo.type || type;

                    var data = resp.vCardTemp.photo.data;
                    var uri = 'data:' + type + ';base64,' + data;

                    avatar = {
                        id: id,
                        type: type,
                        uri: uri
                    };

                    app.storage.avatars.add(avatar);
                    return cb(avatar);
                });
            } else {
                app.api.getAvatar(jid, id, function (err, resp) {
                    if (err) {
                        return cb(getGravatar(jid));
                    }

                    var data = resp.pubsub.retrieve.item.avatarData;
                    var uri = 'data:' + type + ';base64,' + data;

                    avatar = {
                        id: id,
                        type: type,
                        uri: uri
                    };

                    app.storage.avatars.add(avatar);
                    return cb(avatar);
                });
            }
        });
    });
};
<|endoftext|>


<commit_before>const Block = require('./block');
const Data = require('./data');
const typeToConfig = require('./type-to-config');
const EventEmitter = require('events');

class Board extends EventEmitter {
	constructor(config) {
		super();

		this.height = config.height;
		this.width = config.width;

		this.executing = false;
		this.clock = 0;

		this.blocks = [];
		for (var i = 0; i < this.height; i++) {
			const row = [];
			this.blocks.push(row);

			for (var j = 0; j < this.width; j++) {
				row.push(null);
			}
		}

		for (var x = 0; x < this.width; x++) {
			for (var y = 0; y < this.height; y++) {
				this.placeBlock(x, y, 'empty');
			}
		}

		this.inputBlockX = config.inputX;
		this.inputBlockY = 0;
		this.outputBlockX = config.outputX;
		this.outputBlockY = config.height - 1;
	}

	get inputBlock() {
		if (typeof this.inputBlockX === "number") {
			return [this.blocks[this.inputBlockY][this.inputBlockX]];
		}
		else {
			return this.inputBlockX.map((x) =>  this.blocks[this.inputBlockY][x]);
		}
	}

	get outputBlock() {
		return this.blocks[this.outputBlockY][this.outputBlockX];
	}

	get datas() {
		return this.blocks.reduce((previousRow, row) => (
			previousRow.concat(row.reduce((previousBlock, block) => {
				let datas = [];
				for (let direction in block.inputQueues) {
					datas = datas.concat(block.inputQueues[direction]);
					datas = datas.concat(block.outputQueues[direction]);
				}
				return previousBlock.concat(datas);
			}, []))
		), []);
	}

	getBlock(x, y) {
		return this.blocks[y][x];
	}

	forBlocks(callback) {
		for (let x = 0; x < this.width; x++) {
			for (let y = 0; y < this.height; y++) {
				const block = this.getBlock(x, y);
				callback.call(this, block, x, y);
			}
		}
	}

	placeBlock(x, y, type, rotate) {
		const config = Object.assign({}, typeToConfig[type]);
		config.x = x;
		config.y = y;
		config.rotate = rotate;
		config.name = type;

		const block = new Block(this, config);
		block.on('erase', (data) => {
			this.emit('erase', block, data);
		});
		block.on('pass', (io) => {this.emit('pass', block, io)});
		this.blocks[y][x] = block;
	}

	input(value) {
		const nvarr = (typeof value === "number") ? [value] : value;

		const inputData = nvarr.map( (nv,i) => {
			const nd = new Data(this, nv);
			this.inputBlock[i].input('top', nd);
			return nd;
		});

		this.executing = true;
		this.clock = 0;
		return inputData;
	}

	output(value) {
		this.halt();

		// Wait for the next tick to successfully break the clockUp loop
		process.nextTick(() => {
			this.emit('output', value);
		});
	}

	clearData() {
		this.forBlocks((block) => block.clearData());
	}

	step() {
		this.forBlocks((block) => block.step());
		this.clock++;

		if (this.datas.length === 0) {
			this.halt();
		}
	}

	pass() {
		this.forBlocks((block) => block.pass());

		if (this.datas.length === 0) {
			this.halt();
		}
	}

	halt() {
		this.executing = false;
		this.clearData();
		this.emit('halt');
	}
}

module.exports = Board;
<commit_msg>make type of inputBlockX uniform<commit_after>const Block = require('./block');
const Data = require('./data');
const typeToConfig = require('./type-to-config');
const EventEmitter = require('events');

class Board extends EventEmitter {
	constructor(config) {
		super();

		this.height = config.height;
		this.width = config.width;

		this.executing = false;
		this.clock = 0;

		this.blocks = [];
		for (var i = 0; i < this.height; i++) {
			const row = [];
			this.blocks.push(row);

			for (var j = 0; j < this.width; j++) {
				row.push(null);
			}
		}

		for (var x = 0; x < this.width; x++) {
			for (var y = 0; y < this.height; y++) {
				this.placeBlock(x, y, 'empty');
			}
		}

		if (typeof config.inputX === "number") this.inputBlockX = [config.inputX];
		else this.inputBlockX = config.inputX;
		this.inputBlockY = 0;
		this.outputBlockX = config.outputX;
		this.outputBlockY = config.height - 1;
	}

	get inputBlock() {
		return this.inputBlockX.map((x) =>  this.blocks[this.inputBlockY][x]);
	}

	get outputBlock() {
		return this.blocks[this.outputBlockY][this.outputBlockX];
	}

	get datas() {
		return this.blocks.reduce((previousRow, row) => (
			previousRow.concat(row.reduce((previousBlock, block) => {
				let datas = [];
				for (let direction in block.inputQueues) {
					datas = datas.concat(block.inputQueues[direction]);
					datas = datas.concat(block.outputQueues[direction]);
				}
				return previousBlock.concat(datas);
			}, []))
		), []);
	}

	getBlock(x, y) {
		return this.blocks[y][x];
	}

	forBlocks(callback) {
		for (let x = 0; x < this.width; x++) {
			for (let y = 0; y < this.height; y++) {
				const block = this.getBlock(x, y);
				callback.call(this, block, x, y);
			}
		}
	}

	placeBlock(x, y, type, rotate) {
		const config = Object.assign({}, typeToConfig[type]);
		config.x = x;
		config.y = y;
		config.rotate = rotate;
		config.name = type;

		const block = new Block(this, config);
		block.on('erase', (data) => {
			this.emit('erase', block, data);
		});
		block.on('pass', (io) => {this.emit('pass', block, io)});
		this.blocks[y][x] = block;
	}

	input(value) {
		const nvarr = (typeof value === "number") ? [value] : value;

		const inputData = nvarr.map( (nv,i) => {
			const nd = new Data(this, nv);
			this.inputBlock[i].input('top', nd);
			return nd;
		});

		this.executing = true;
		this.clock = 0;
		return inputData;
	}

	output(value) {
		this.halt();

		// Wait for the next tick to successfully break the clockUp loop
		process.nextTick(() => {
			this.emit('output', value);
		});
	}

	clearData() {
		this.forBlocks((block) => block.clearData());
	}

	step() {
		this.forBlocks((block) => block.step());
		this.clock++;

		if (this.datas.length === 0) {
			this.halt();
		}
	}

	pass() {
		this.forBlocks((block) => block.pass());

		if (this.datas.length === 0) {
			this.halt();
		}
	}

	halt() {
		this.executing = false;
		this.clearData();
		this.emit('halt');
	}
}

module.exports = Board;
<|endoftext|>


<commit_before>'use strict';

const ordinal = require('../../lib/ordinal');

class Instance {
  constructor(instance) {
    this.State = instance.State;
    this.Quarter = instance.Quarter;
    this.Time = instance.Time;
    this.HomeScore = instance.HomeScore;
    this.AwayScore = instance.AwayScore;
  }
  timeNum() {
    return parseInt(this.Time.replace(/\D/g, ''));
  }
  scoreChanged(prevInstance) {
    return prevInstance.State !== this.State ||
      prevInstance.Quarter !== this.Quarter ||
      prevInstance.HomeScore !== this.HomeScore ||
      prevInstance.AwayScore !== this.AwayScore;
  }
  changeString(prevInstance, game) {
    let str = '';
    const scores = game.AwayTeam.Name + ' ' + this.AwayScore + ', ' +
      game.HomeTeam.Name + ' ' + this.HomeScore + '. ';
    const time = `${this.Time} ${ordinal(this.Quarter)} quarter.`;
    if (this.State !== prevInstance.State) {
      if (prevInstance.State === 'Pregame' && this.State === 'Progress') {
        str = `Start of game: ${game.AwayTeam.Name} vs ${game.HomeTeam.Name}`;
      }
      if (this.State === 'Final') {
        str = `Final${(prevInstance.Quarter === 'OT' ? ' OT' : '')}. ${scores}`;
      }
    } else if ((this.Quarter === prevInstance.Quarter && prevInstance.timeNum() >= this.timeNum()) || (prevInstance.Quarter < this.Quarter)) {
      if (prevInstance.AwayScore > this.AwayScore || prevInstance.HomeScore > this.HomeScore) {
        str = `Score correction. ${scores} ${time}`;
      } else if (prevInstance.AwayScore !== this.AwayScore) {
        str = `${game.AwayTeam.Name} score. ${scores} ${time}`;
      } else if (prevInstance.HomeScore !== this.HomeScore) {
        str = `${game.HomeTeam.Name} score. ${scores} ${time}`;
      }
    }
    return str ? (str.trim() + ' ' + game.makeGameLink()) : '';
  }
}

Instance.parse = function(raw) {
  const instanceObj = {
    Time: raw.clock,
    HomeScore: raw.home.score.T,
    AwayScore: raw.away.score.T
  };
  if (!raw.qtr) {
    instanceObj.State = 'Pregame';
  } else if (!isNaN(parseInt(raw.qtr))) {
    instanceObj.State = 'Progress';
    instanceObj.Quarter = parseInt(raw.qtr) > 4 ? 'OT' : parseInt(raw.qtr);
  } else if (raw.qtr.toLocaleLowerCase().indexOf('final') !== -1) {
    instanceObj.State = 'Final';
  } else {
    instanceObj.State = raw.qtr;
  }

  return new Instance(instanceObj);
};

module.exports = Instance;
<commit_msg>change NFL to lead change<commit_after>'use strict';

const ordinal = require('../../lib/ordinal');

class Instance {
  constructor(instance) {
    this.State = instance.State;
    this.Quarter = instance.Quarter;
    this.Time = instance.Time;
    this.HomeScore = instance.HomeScore;
    this.AwayScore = instance.AwayScore;
  }
  timeNum() {
    return parseInt(this.Time.replace(/\D/g, ''));
  }
  scoreChanged(prevInstance) {
    return prevInstance.State !== this.State ||
      prevInstance.Quarter !== this.Quarter ||
      prevInstance.HomeScore !== this.HomeScore ||
      prevInstance.AwayScore !== this.AwayScore;
  }
  changeString(prevInstance, game) {
    let str = '';
    const scores = game.AwayTeam.Name + ' ' + this.AwayScore + ', ' +
      game.HomeTeam.Name + ' ' + this.HomeScore + '. ';
    const leadString = ' take the lead. ';
    const time = `${this.Time} ${ordinal(this.Quarter)} quarter.`;
    if (this.State !== prevInstance.State) {
      if (prevInstance.State === 'Pregame' && this.State === 'Progress') {
        str = `Start of game: ${game.AwayTeam.Name} vs ${game.HomeTeam.Name}`;
      }
      if (this.State === 'Final') {
        str = `Final${(prevInstance.Quarter === 'OT' ? ' OT' : '')}. ${scores}`;
      }
    } else if ((this.Quarter === prevInstance.Quarter && prevInstance.timeNum() >= this.timeNum()) || (prevInstance.Quarter < this.Quarter)) {
      if (prevInstance.AwayScore > this.AwayScore || prevInstance.HomeScore > this.HomeScore) {
        str = `Score correction. ${scores} ${time}`;
      } else if (prevInstance.AwayScore === prevInstance.HomeScore) {
        if (this.AwayScore > this.HomeScore) {
          str = game.AwayTeam.Name + leadString + scores + time;
        } else if (this.HomeScore > this.AwayScore){
          str = game.HomeTeam.Name + leadString + scores + time;
        }
      } else if (this.AwayScore === this.HomeScore) {
        if (prevInstance.AwayScore < prevInstance.HomeScore) {
          str = game.AwayTeam.Name + ' tie it up. ' + scores + time;
        } else if (prevInstance.HomeScore < prevInstance.AwayScore) {
          str = game.HomeTeam.Name + ' tie it up. ' + scores + time;
        }
      } else if (prevInstance.AwayScore < prevInstance.HomeScore && this.HomeScore < this.AwayScore) {
        str = game.AwayTeam.Name + leadString + scores + time;
      } else if (prevInstance.HomeScore < prevInstance.AwayScore && this.AwayScore < this.HomeScore) {
        str = game.HomeTeam.Name + leadString + scores + time;
      }
    }
    return str ? (str.trim() + ' ' + game.makeGameLink()) : '';
  }
}

Instance.parse = function(raw) {
  const instanceObj = {
    Time: raw.clock,
    HomeScore: raw.home.score.T,
    AwayScore: raw.away.score.T
  };
  if (!raw.qtr) {
    instanceObj.State = 'Pregame';
  } else if (!isNaN(parseInt(raw.qtr))) {
    instanceObj.State = 'Progress';
    instanceObj.Quarter = parseInt(raw.qtr) > 4 ? 'OT' : parseInt(raw.qtr);
  } else if (raw.qtr.toLocaleLowerCase().indexOf('final') !== -1) {
    instanceObj.State = 'Final';
  } else {
    instanceObj.State = raw.qtr;
  }

  return new Instance(instanceObj);
};

module.exports = Instance;
<|endoftext|>