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

function TranslatorManager(grunt) {

    var self = this;

    /**
     * This regexp match any expression between two double brackets.
     * Each expression found represents a locale definition with a name or "key" and
     * optional a default text to be shown.
     * i.e: [[hello: this is text]]
     * Everything after the first ":" is considered text, and everything before is the
     * Definition's name, only letters and numbers allowed for the name.
     * @type {RegExp}
     */
    this.delimiters = /\[\[.+\]\]/g;

    /**
     * Stores a record of all the files to check there are no duplicated fileNames.
     * fNames = {
	 * 	  "hello":"/users/home/...../hello.html",
	 * 	  "world":"/users/home/...../world.html"
	 * }
     * @type {{}}
     */
    this.fNames = {};

    /**
     * String that stores a record of all the generated templates.
     * @type {Array}
     */
    this.generatedTemplates = "";

    /**
     * String that stores a record of all the generated Locales.
     * @type {Array}
     */
    this.generatedLocales = "";

    /**
     * Stores a record of all the locale Definitions and their lines in the files.
     * Check that there are no two Definitions with the same name or "key".
     * The key of each entry is formed by the filename+definitionName so it is
     * possible to use the same name or "key" in different files.
     * i.e: [[hello: Hello World]] and [[hello: What's Up]] has the same name "hello",
     * but it is ok until they are not in the same file.
     * defLines = {
	 * 	 "filename:hello":"this is the hello text",
	 * 	 "filename:person":"this is the person name"
	 * }
     * @type {{}}
     */
    this.defLines = {};

    /**
     * Stores all the templates and an the translated templates.
     * Each entry of this object is saved as json and contains all the templates, so they can be
     * easily accessed in javascript.
     * @type {{original: {}}}
     */
    this.templates = {original: {}/* ,en:{},es:{},de:{},fr:{} */};

    /**
     * Stores all the translations values.
     * Each entry of this object is saved as json and is a translation file used to generate
     * the translated templates.
     * @type {{original: {}}}
     */
    this.locales = {original: {}/* ,en:{},es:{},de:{},fr:{} */};

    /**
     * The folder's path where the locales files will be generated.
     * @type {null}
     */
    this.localesDest = "./locales";

    /**
     * The folder's path where the templates files will be generated.
     * @type {null}
     */
    this.templatesDest = "./templates";

    /**
     * Suffix used as suffix of the generated templates files.
     * template fileName = lang + "_" +  templatesSuffix
     * @type {string}
     */
    this.templatesSuffix = "templates.json";

    /**
     * Suffix used as suffix of the generated language files.
     * language fileName = lang + "_" +  localesSuffix
     * @type {string}
     */
    this.localesSuffix = "locales.json";

    // ########################################################################################
    // 										METHODS
    // ########################################################################################

    /**
     * Sets the languages to use.
     * @param locales array("es","en","de"...)
     */
    this.setLocales = function setLocales(locales) {
        for (var i = 0; i < locales.length; i++) {
            this.templates[locales[i]] = {};
            this.locales[locales[i]] = {};
        }
    };

    /**
     * Sets the folder where the translation files will be generated.
     * @param path
     */
    this.setLocalesFolder = function setLocalesFolder(path) {
        this.localesDest = path;
    };

    /**
     * Sets the folder where the templates files will be generated.
     * @param path
     */
    this.setTemplatesFolder = function setTemplatesFolder(path) {
        this.templatesDest = path;
    };

    /**
     * Parse one file to generates the json template files and the translation files.
     * @param filePath
     */
    this.parseFile = function parseFile(filePath) {
        if (!grunt.file.exists(filePath))
            return false;
        // Read and return the file's source.
        var content = grunt.file.read(filePath);
        var fileName = getFileNameNoExt(filePath);
        this.checkDuplicateName(fileName, filePath);
        this.parseLocales(filePath, fileName, content);
    };

    /**
     * Saves the locales and the templates to files.
     */
    this.save = function save() {
        this.overrideNewLocales();
        this.generateNewTemplates();
        this.saveLocales();
        this.saveTemplates();
        this.log();
    };

    /**
     * Logs to console the result of parsing the files.
     */
    this.log = function log() {
        var fnum = " " + Object.keys(this.fNames).length;
        var defnum = " " + Object.keys(this.defLines).length;
        console.log("-------------------------------------");
        console.log("Parsed " + fnum.green + " files => " + defnum.blue + " locale definitions.");
        console.log("Templates => " + self.generatedTemplates);
        console.log("Localizations => " + self.generatedLocales);
    }

    /**
     * Checks if the name has been used before as key in the templates object, if so
     * throws an error indicating the filePath of the two files with the same name.
     * To do that the function make use of the variable fNames that keep track of all the filePaths
     * and fileNames previously inserted.
     * @param fileName
     * @param filePath
     */
    this.checkDuplicateName = function checkDuplicateName(fileName, filePath) {
        if (typeof this.fNames[fileName] !== 'undefined') {
            var err = "Two files found that generates the same key '" + fileName + "' in templates.json. Filenames: '"
                + filePath + "' and '"  + this.fNames[fileName] + "'";
            grunt.fail.warn(err , 3);
        } else {
            this.fNames[fileName] = filePath;
        }
    };

    /**
     * Search in the fileContent for locale definitions, i.e: "[[key: this is the text]]"
     * and creates the key and values in this.locales[language] and this.templates[lang] objects.
     * The search is split by lines, so it is not possible to create multi-line
     * locale definitions i.e: "[[key: this is \n text]]".
     * @param filePath
     * @param fileName
     * @param fileContent
     */
    this.parseLocales = function parseLocales(filePath, fileName, fileContent) {
        var lines = fileContent.split("\n");
        var definitions = 0;
        for (var i = 0; i < lines.length; i++) {
            var line = lines[i];
            var match;
            while ((match = this.delimiters.exec(line)) !== null) {
                var definition = match[0].substring(2, match[0].length - 2);
                var sepIndex = definition.indexOf(":");
                var key, sentence;
                // [[key : text]]
                if (sepIndex > 0) {
                    key = fileName + ":" + definition.substring(0, sepIndex);
                    sentence = definition.substring(sepIndex + 1, definition.length);
                }
                // [[key]]
                else {
                    key = fileName + ":" + definition;
                    sentence = definition;
                }

                if (typeof this.defLines[key] === 'undefined') {
                    this.defLines[key] = i;
                    definitions++;
                } else {
                    var err = "Two locale definitions with the same name '" + key + "'. File: '" +
                        filePath + "' lines " + i + " and " + this.defLines[key];
                    grunt.fail.warn(err , 3);
                }

                Object.keys(this.locales).forEach(function (lang) {
                    self.templates[lang][fileName] = fileContent;
                    self.locales[lang][key] = sentence;
                });
            }
        }
        definitions += "";
        console.log("file " + fileName.green + " => " + definitions.blue + " locale definitions.")
    };

    /**
     * Generates the new templates based on the values read from the locales files and reemplacing
     * in the templates final files.
     */
    this.generateNewTemplates = function generateNewTemplates() {
        Object.keys(this.templates).forEach(function (lang) {
            Object.keys(self.templates[lang]).forEach(function (tplName) {
                var content = self.templates[lang][tplName];
                self.templates[lang][tplName] = content.replace(self.delimiters, function (match) {
                    var definition = match.substring(2, match.length - 2);
                    var sepIndex = definition.indexOf(":");
                    var key;
                    if (sepIndex > 0)
                        key = tplName + ":" + definition.substring(0, sepIndex);
                    else
                        key = tplName + ":" + definition;

                    var sentence = self.locales[lang][key];
                    if (!sentence) {
                        sentence = self.locales.original[key];
                        console.log("Key '" + key + " in "
                            + lang + self.localesSuffix + "' Not found, using value from the template.");
                    }
                    return sentence;
                });
            });
        });
    };

    /**
     * Reads the existent locale files and override the values obtained from parse the templates
     * with the values previously stores in the locales files, So the values in the locales files
     * has priority over the new values in the templates.
     */
    this.overrideNewLocales = function overrideNewLocales() {
        Object.keys(this.locales).forEach(function (lang) {
            if (lang !== "original") {
                var fName = path.join(self.localesDest, lang + self.localesSuffix);
                try {
                    var old = grunt.file.readJSON(fName);
                    Object.keys(old).forEach(function (attrName) {
                        self.locales[lang][attrName] = old[attrName];
                    });
                } catch (fileNotFound) {
                    // if the file does not exist locales is just not override.
                }
            }
        });
    };

    /**
     * Saves the objects contained in this.templates to a file.
     * Each field of this.templates object corresponds directly with one template file in a
     * different language.
     */
    this.saveTemplates = function saveTemplates() {
        Object.keys(this.locales).forEach(function (lang) {
            var prefix = (lang === "original") ? "" : lang + "_";
            var fName = path.join(self.templatesDest, prefix + self.templatesSuffix);
            grunt.file.write(fName, JSON.stringify(self.templates[lang], null, '\t'));
            if(prefix === "")
                self.generatedTemplates += (prefix + self.templatesSuffix).underline.cyan + " , ";
            else
                self.generatedTemplates += (prefix + self.templatesSuffix).cyan + " , ";
        });
    };

    /**
     * Saves the objects contained in this.locales to a file.
     * Each field of this.locales object corresponds directly with one translation file.
     */
    this.saveLocales = function saveLocales() {
        Object.keys(this.locales).forEach(function (lang) {
            if (lang !== "original") {
                var fName = path.join(self.localesDest, lang + "_" + self.localesSuffix);
                grunt.file.write(fName, JSON.stringify(self.locales[lang], null, '\t'));
                self.generatedLocales += (lang + "_" + self.localesSuffix).cyan + " ; ";
            }
        });
    };

    /**
     * Extracts the filename  and file extension from a given path and returns only the
     * filename.
     * @param filePath
     * @returns String filename
     */
    function getFileNameNoExt(filePath) {
        filePath = path.normalize(filePath);
        var split = filePath.split(path.sep);
        var fileName = split[split.length - 1];
        var dotPost = fileName.lastIndexOf(".");
        if (dotPost === 0)
            return fileName;
        else
            return fileName.substr(0, dotPost);
    }

}

module.exports = function (grunt) {
    return new TranslatorManager(grunt);
};<commit_msg>update log messages<commit_after>/**
 * Copyright (C) 2014 Ma-Jerez
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * Created by Ma-Jerez on 15/07/2014.
 */
'use strict';
var path = require('path');
var fs = require("fs");
require("colors");

function TranslatorManager(grunt) {

    var self = this;

    /**
     * This regexp match any expression between two double brackets.
     * Each expression found represents a locale definition with a name or "key" and
     * optional a default text to be shown.
     * i.e: [[hello: this is text]]
     * Everything after the first ":" is considered text, and everything before is the
     * Definition's name, only letters and numbers allowed for the name.
     * @type {RegExp}
     */
    this.delimiters = /\[\[.+\]\]/g;

    /**
     * Stores a record of all the files to check there are no duplicated fileNames.
     * fNames = {
	 * 	  "hello":"/users/home/...../hello.html",
	 * 	  "world":"/users/home/...../world.html"
	 * }
     * @type {{}}
     */
    this.fNames = {};

    /**
     * String that stores a record of all the generated templates.
     * @type {Array}
     */
    this.generatedTemplates = "";

    /**
     * String that stores a record of all the generated Locales.
     * @type {Array}
     */
    this.generatedLocales = "";

    /**
     * Stores a record of all the locale Definitions and their lines in the files.
     * Check that there are no two Definitions with the same name or "key".
     * The key of each entry is formed by the filename+definitionName so it is
     * possible to use the same name or "key" in different files.
     * i.e: [[hello: Hello World]] and [[hello: What's Up]] has the same name "hello",
     * but it is ok until they are not in the same file.
     * defLines = {
	 * 	 "filename:hello":"this is the hello text",
	 * 	 "filename:person":"this is the person name"
	 * }
     * @type {{}}
     */
    this.defLines = {};

    /**
     * Stores all the templates and an the translated templates.
     * Each entry of this object is saved as json and contains all the templates, so they can be
     * easily accessed in javascript.
     * @type {{original: {}}}
     */
    this.templates = {original: {}/* ,en:{},es:{},de:{},fr:{} */};

    /**
     * Stores all the translations values.
     * Each entry of this object is saved as json and is a translation file used to generate
     * the translated templates.
     * @type {{original: {}}}
     */
    this.locales = {original: {}/* ,en:{},es:{},de:{},fr:{} */};

    /**
     * The folder's path where the locales files will be generated.
     * @type {null}
     */
    this.localesDest = "./locales";

    /**
     * The folder's path where the templates files will be generated.
     * @type {null}
     */
    this.templatesDest = "./templates";

    /**
     * Suffix used as suffix of the generated templates files.
     * template fileName = lang + "_" +  templatesSuffix
     * @type {string}
     */
    this.templatesSuffix = "templates.json";

    /**
     * Suffix used as suffix of the generated language files.
     * language fileName = lang + "_" +  localesSuffix
     * @type {string}
     */
    this.localesSuffix = "locales.json";

    // ########################################################################################
    // 										METHODS
    // ########################################################################################

    /**
     * Sets the languages to use.
     * @param locales array("es","en","de"...)
     */
    this.setLocales = function setLocales(locales) {
        for (var i = 0; i < locales.length; i++) {
            this.templates[locales[i]] = {};
            this.locales[locales[i]] = {};
        }
    };

    /**
     * Sets the folder where the translation files will be generated.
     * @param path
     */
    this.setLocalesFolder = function setLocalesFolder(path) {
        this.localesDest = path;
    };

    /**
     * Sets the folder where the templates files will be generated.
     * @param path
     */
    this.setTemplatesFolder = function setTemplatesFolder(path) {
        this.templatesDest = path;
    };

    /**
     * Parse one file to generates the json template files and the translation files.
     * @param filePath
     */
    this.parseFile = function parseFile(filePath) {
        if (!grunt.file.exists(filePath))
            return false;
        // Read and return the file's source.
        var content = grunt.file.read(filePath);
        var fileName = getFileNameNoExt(filePath);
        this.checkDuplicateName(fileName, filePath);
        this.parseLocales(filePath, fileName, content);
    };

    /**
     * Saves the locales and the templates to files.
     */
    this.save = function save() {
        this.overrideNewLocales();
        this.generateNewTemplates();
        this.saveLocales();
        this.saveTemplates();
        this.log();
    };

    /**
     * Logs to console the result of parsing the files.
     */
    this.log = function log() {
        var fnum = " " + Object.keys(this.fNames).length;
        var defnum = " " + Object.keys(this.defLines).length;
        console.log("-------------------------------------");
        console.log("Parsed " + fnum.cyan + " files, found " + defnum.blue + " locale definitions.");
        console.log("Templates => " + self.generatedTemplates);
        console.log("Localizations => " + self.generatedLocales);
    }

    /**
     * Checks if the name has been used before as key in the templates object, if so
     * throws an error indicating the filePath of the two files with the same name.
     * To do that the function make use of the variable fNames that keep track of all the filePaths
     * and fileNames previously inserted.
     * @param fileName
     * @param filePath
     */
    this.checkDuplicateName = function checkDuplicateName(fileName, filePath) {
        if (typeof this.fNames[fileName] !== 'undefined') {
            var err = "Two files found that generates the same key '" + fileName + "' in templates.json. Filenames: '"
                + filePath + "' and '"  + this.fNames[fileName] + "'";
            grunt.fail.warn(err , 3);
        } else {
            this.fNames[fileName] = filePath;
        }
    };

    /**
     * Search in the fileContent for locale definitions, i.e: "[[key: this is the text]]"
     * and creates the key and values in this.locales[language] and this.templates[lang] objects.
     * The search is split by lines, so it is not possible to create multi-line
     * locale definitions i.e: "[[key: this is \n text]]".
     * @param filePath
     * @param fileName
     * @param fileContent
     */
    this.parseLocales = function parseLocales(filePath, fileName, fileContent) {
        var lines = fileContent.split("\n");
        var definitions = 0;
        for (var i = 0; i < lines.length; i++) {
            var line = lines[i];
            var match;
            while ((match = this.delimiters.exec(line)) !== null) {
                var definition = match[0].substring(2, match[0].length - 2);
                var sepIndex = definition.indexOf(":");
                var key, sentence;
                // [[key : text]]
                if (sepIndex > 0) {
                    key = fileName + ":" + definition.substring(0, sepIndex);
                    sentence = definition.substring(sepIndex + 1, definition.length);
                }
                // [[key]]
                else {
                    key = fileName + ":" + definition;
                    sentence = definition;
                }

                if (typeof this.defLines[key] === 'undefined') {
                    this.defLines[key] = i;
                    definitions++;
                } else {
                    var err = "Two locale definitions with the same name '" + key + "'. File: '" +
                        filePath + "' lines " + i + " and " + this.defLines[key];
                    grunt.fail.warn(err , 3);
                }

                Object.keys(this.locales).forEach(function (lang) {
                    self.templates[lang][fileName] = fileContent;
                    self.locales[lang][key] = sentence;
                });
            }
        }
        definitions += "";
        console.log("file " + fileName.cyan + ", found " + definitions.blue + " locale definitions.")
    };

    /**
     * Generates the new templates based on the values read from the locales files and reemplacing
     * in the templates final files.
     */
    this.generateNewTemplates = function generateNewTemplates() {
        Object.keys(this.templates).forEach(function (lang) {
            Object.keys(self.templates[lang]).forEach(function (tplName) {
                var content = self.templates[lang][tplName];
                self.templates[lang][tplName] = content.replace(self.delimiters, function (match) {
                    var definition = match.substring(2, match.length - 2);
                    var sepIndex = definition.indexOf(":");
                    var key;
                    if (sepIndex > 0)
                        key = tplName + ":" + definition.substring(0, sepIndex);
                    else
                        key = tplName + ":" + definition;

                    var sentence = self.locales[lang][key];
                    if (!sentence) {
                        sentence = self.locales.original[key];
                        console.log("Key '" + key + " in "
                            + lang + self.localesSuffix + "' Not found, using value from the template.");
                    }
                    return sentence;
                });
            });
        });
    };

    /**
     * Reads the existent locale files and override the values obtained from parse the templates
     * with the values previously stores in the locales files, So the values in the locales files
     * has priority over the new values in the templates.
     */
    this.overrideNewLocales = function overrideNewLocales() {
        Object.keys(this.locales).forEach(function (lang) {
            if (lang !== "original") {
                var fName = path.join(self.localesDest, lang + self.localesSuffix);
                try {
                    var old = grunt.file.readJSON(fName);
                    Object.keys(old).forEach(function (attrName) {
                        self.locales[lang][attrName] = old[attrName];
                    });
                } catch (fileNotFound) {
                    // if the file does not exist locales is just not override.
                }
            }
        });
    };

    /**
     * Saves the objects contained in this.templates to a file.
     * Each field of this.templates object corresponds directly with one template file in a
     * different language.
     */
    this.saveTemplates = function saveTemplates() {
        Object.keys(this.locales).forEach(function (lang) {
            var prefix = (lang === "original") ? "" : lang + "_";
            var fName = path.join(self.templatesDest, prefix + self.templatesSuffix);
            grunt.file.write(fName, JSON.stringify(self.templates[lang], null, '\t'));
            if(prefix === "")
                self.generatedTemplates += (prefix + self.templatesSuffix).underline.cyan + " , ";
            else
                self.generatedTemplates += (prefix + self.templatesSuffix).cyan + " , ";
        });
    };

    /**
     * Saves the objects contained in this.locales to a file.
     * Each field of this.locales object corresponds directly with one translation file.
     */
    this.saveLocales = function saveLocales() {
        Object.keys(this.locales).forEach(function (lang) {
            if (lang !== "original") {
                var fName = path.join(self.localesDest, lang + "_" + self.localesSuffix);
                grunt.file.write(fName, JSON.stringify(self.locales[lang], null, '\t'));
                self.generatedLocales += (lang + "_" + self.localesSuffix).blue + " ; ";
            }
        });
    };

    /**
     * Extracts the filename  and file extension from a given path and returns only the
     * filename.
     * @param filePath
     * @returns String filename
     */
    function getFileNameNoExt(filePath) {
        filePath = path.normalize(filePath);
        var split = filePath.split(path.sep);
        var fileName = split[split.length - 1];
        var dotPost = fileName.lastIndexOf(".");
        if (dotPost === 0)
            return fileName;
        else
            return fileName.substr(0, dotPost);
    }

}

module.exports = function (grunt) {
    return new TranslatorManager(grunt);
};<|endoftext|>


<commit_before>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that a<commit_msg>Logic error<commit_after>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that ac<|endoftext|>


<commit_before>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that achieves no real goal.

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  console.info("Writing from %s to %s, with up to %s seconds between commits", inFile, outFile, max_sleep)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    console.info("Bytes: %s", length)
    try {
      child_process.execFileSync('/usr/bin/git', ['add', outFile])
    } catch (e) {
      console.error("Couldn't add %s to git: %s", outFile, e)
    }
    var message = commit_messages[randomIntFromInterval(0, commit_messages.length - 1)]
    var args = ['commit', outFile, '-m', message]
    for (var counter = 0; counter < length; counter++)
    {
      var message = commit_messages[randomIntFromInterval(0, commit_messages.length - 1)]
      var args = ['commit', outFile, '-m', message]
      fs.writeSync(outFD, data.slice(counter, counter+1), 0, 1)
      sleep(Math.random() * max_sleep)
      child_process.execF<commit_msg>Logic error<commit_after>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that achieves no real goal.

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  console.info("Writing from %s to %s, with up to %s seconds between commits", inFile, outFile, max_sleep)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    console.info("Bytes: %s", length)
    try {
      child_process.execFileSync('/usr/bin/git', ['add', outFile])
    } catch (e) {
      console.error("Couldn't add %s to git: %s", outFile, e)
    }
    var message = commit_messages[randomIntFromInterval(0, commit_messages.length - 1)]
    var args = ['commit', outFile, '-m', message]
    for (var counter = 0; counter < length; counter++)
    {
      var message = commit_messages[randomIntFromInterval(0, commit_messages.length - 1)]
      var args = ['commit', outFile, '-m', message]
      fs.writeSync(outFD, data.slice(counter, counter+1), 0, 1)
      sleep(Math.random() * max_sleep)
      child_process.execFi<|endoftext|>


<commit_before>var hbs = require('express-hbs');
var fs = require('fs');
var path = require('path');
var slideshows = require('./slideshows.json');

/***
Articles should have a directory defined in slideshows.json or we check for a
directory with the same name as the articleId.
If the file "sprite.jpg" exists in the slideshow directory, it will be used to
make the pagination thumbnails.
***/
registerHelper = function (){
    hbs.registerHelper("slideshow", function(articleId) {
        if (slideshows.articles && slideshows.articles[articleId] &&
            slideshows.articles[articleId].imgDir) {
            var dir = slideshows.rootDir + slideshows.articles[articleId].imgDir;
            var urlPath = slideshows.urlPath + slideshows.articles[articleId].imgDir;
        } else {
            var dir = slideshows.rootDir + articleId + "/";
            var urlPath = slideshows.urlPath + articleId + "/";
        }

        if (!fs.existsSync(dir)) {
            return new hbs.SafeString(' ');
        }

        var dirList = fs.readdirSync(dir);
        if (fs.existsSync(dir + "sprite.jpg")) {
            var out = '
';
        } else {
            var out = '
';
        }

        for (var i=0, l=dirList.length; i<l; i++) {
            var fileName = dirList[i];
            var filePath = dir + fileName;
            var ext = path.extname(fileName).toLowerCase();
            //only print for jpg, gif, or png files
            if (!fs.lstatSync(filePath).isDirectory() &&
               (ext === ".jpg" || ext === ".gif" || ext === ".png")) {
                if (i == 0) {
                    out += '
\n';
                    out += '
\n';
                    out += '\n';
                    out += '
\n';
                } else {
                    out += '
\n';
                    out += '\n';
                    out += '
\n';
                }
            }
        }
        return new hbs.SafeString(out + '
\n
\n');
    });
}

module.exports = registerHelper;
<commit_msg>First should be a normal img element<commit_after>var hbs = require('express-hbs');
var fs = require('fs');
var path = require('path');

/***
Articles should have a directory defined in slideshows.json or we check for a
directory with the same name as the articleId.
If the file "sprite.jpg" exists in the slideshow directory, it will be used to
make the pagination thumbnails.
***/
registerHelper = function (){
    hbs.registerHelper("slideshow", function(articleId) {
        try {
            var slideshows = require('./slideshows.json');
        } catch (ex) {
            console.log("Couldn't open slideshow.json");
            return;
        }
        if (slideshows.articles && slideshows.articles[articleId] &&
            slideshows.articles[articleId].imgDir) {
            var dir = slideshows.rootDir + slideshows.articles[articleId].imgDir;
            var urlPath = slideshows.urlPath + slideshows.articles[articleId].imgDir;
        } else {
            var dir = slideshows.rootDir + articleId + "/";
            var urlPath = slideshows.urlPath + articleId + "/";
        }

        if (!fs.existsSync(dir)) {
            return new hbs.SafeString(' ');
        }

        var dirList = fs.readdirSync(dir);
        var sprite = fs.existsSync(dir + "sprite.jpg")  ?
                     ' data-pagination="sprite" data-sprite="' +
                       urlPath + 'sprite.jpg"' : '';

        var out = '
';

        out += '
\n';
        for (var i=0, first=true, l=dirList.length; i<l; i++) {
            var fileName = dirList[i];
            var filePath = dir + fileName;
            var ext = path.extname(fileName).toLowerCase();
            //only print for jpg, gif, or png files
            if (isImagePath(filePath)) {
                if (first) {
                    out += getSlideLI(urlPath + fileName, false, false);
                    first = false;
                } else {
                    out += getSlideLI(urlPath + fileName, true, false);
                }
            }
        }
        return new hbs.SafeString(out + '
\n
\n');
    });
}

function isImagePath(filePath) {
    var ext = path.extname(filePath).toLowerCase();
    if (!fs.lstatSync(filePath).isDirectory() &&
        (ext === '.jpg' || ext === '.gif' || ext === '.png')) {
        return true;
    } else {
        return false;
    }
}

function getSlideLI(imgURL, dataSRC, caption) {
    if (dataSRC !== false) {
        var src = ' data-src="' + imgURL + '"';
    } else {
        var src = ' src="' + imgURL + '"';
    }

    if (caption !== false) {
        var caption = ' data-caption="' + caption + '"';
    } else {
        var caption = '';

    }
    return '
\n' +
           '\t\n' +
           '
\n';
}
module.exports = registerHelper;
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
    events = require('events'),
    errorHandler = require('./errors.server.controller'),
    loadTester = require('./loadtester.server.controller'),
    results = require('./results.server.controller'),
    Launch = mongoose.model('Launch'),
    _ = require('lodash');

/**
 * Create a Launch Object
 */
var createLaunch = function(req, res) {
    var launch = new Launch({
        name: req.param('name'),
        server: req.param('server'),
        api: req.param('api'),
        duration: req.param('duration', 10) * 60, // In seconds
        nb_users: req.param('nb_users', 1)
    });
    launch.save(function(err, newLaunch) {
        if (err) {
            console.error(new Error('Unable to create launch: ' + err));
            res.status(500).send({
                message: errorHandler.getErrorMessage(err)
            });
        } else {
            res.jsonp(launch);
        }
    });
    return launch;
};

/**
 * Run Launcher
 */
exports.run = function(req, res) {
    // Init Launch
    var launch = createLaunch(req, res);
    // Run Load Testers
    var eventEmitter = new events.EventEmitter();
    for (var i = 0; i < launch.nb_users; i++) {
        loadTester.run(i, launch, eventEmitter);
    }
    setTimeout(function() {
        eventEmitter.emit('stopLoadTest');
        eventEmitter.removeAllListeners('stopLoadTest');
        Launch.update({
            start_date: launch.start_date
        }, {
            in_progress: false
        }, function(err, numberAffected, raw) {
            if (err) {
                console.error(new Error('Unable to update launch: ' + err));
            }
        });
        results.create(launch);
    }, launch.duration * 1000);
};

/**
 * List Running Launches
 */
exports.listRunning = function(req, res) {
    Launch.find({
        in_progress: true
    }).sort('-start_date').exec(function(err, results) {
        if (err) {
            return res.status(500).send({
                message: errorHandler.getErrorMessage(err)
            });
        } else {
            res.jsonp(results);
        }
    });
};
<commit_msg>fix(launcher): only run load test if launch object is correct<commit_after>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
    events = require('events'),
    errorHandler = require('./errors.server.controller'),
    loadTester = require('./loadtester.server.controller'),
    results = require('./results.server.controller'),
    Launch = mongoose.model('Launch'),
    _ = require('lodash');

/**
 * Create a Launch Object and Call Next
 */
var initLaunch = function(req, res, next) {
    var launch = new Launch({
        name: req.param('name'),
        server: req.param('server'),
        api: req.param('api'),
        duration: req.param('duration', 10) * 60, // In seconds
        nb_users: req.param('nb_users', 1)
    });
    launch.save(function(err, newLaunch) {
        if (err) {
            console.error(new Error('Unable to create launch: ' + err));
            res.status(500).send({
                message: errorHandler.getErrorMessage(err)
            });
        } else {
            res.jsonp(launch);
            next(launch);
        }
    });
};

/**
 * Run Launcher
 */
exports.run = function(req, res) {
    // Init Launch
    initLaunch(req, res, function(launch) {
        // Run Load Testers
        var eventEmitter = new events.EventEmitter();
        for (var i = 0; i < launch.nb_users; i++) {
            loadTester.run(i, launch, eventEmitter);
        }
        setTimeout(function() {
            eventEmitter.emit('stopLoadTest');
            eventEmitter.removeAllListeners('stopLoadTest');
            Launch.update({
                start_date: launch.start_date
            }, {
                in_progress: false
            }, function(err, numberAffected, raw) {
                if (err) {
                    console.error(new Error('Unable to update launch: ' + err));
                }
            });
            results.create(launch);
        }, launch.duration * 1000);
    });
};

/**
 * List Running Launches
 */
exports.listRunning = function(req, res) {
    Launch.find({
        in_progress: true
    }).sort('-start_date').exec(function(err, results) {
        if (err) {
            return res.status(500).send({
                message: errorHandler.getErrorMessage(err)
            });
        } else {
            res.jsonp(results);
        }
    });
};
<|endoftext|>


<commit_before>import debounce from "lodash/debounce";

const TIMELINE_SELECTOR = ".userContentWrapper";
const SIDEBAR_SELECTOR = ".ego_unit";
const DEBUG =
  (process.env.NODE_ENV === "dev" || process.env.NODE_ENV) === "development"
    ? "development"
    : "production";

// This function cleans all the elements that could leak user data
// before sending to the server. It also removes any attributes that
// could have personal data so we end up with a clean dom tree.
const selectors = [
  "video",
  "input",
  "button",
  "iframe",
  'a[href=""]',
  ".accessible_elem",
  ".uiLikePagebutton",
  ".uiPopOver",
  ".uiCloseButton",
  ".uiChevronSelectorButton",
  "h5._1qbu",
  ".commentable_item"
].join(", ");

const cleanAd = html => {
  let node = document.createElement("div");
  node.innerHTML = html;

  // We're not saving video ads for now, we don't need buttons, hidden forms,
  // or like links
  Array.from(node.querySelectorAll(selectors)).forEach(i => i.remove());

  // remove attributes
  const killAttrs = node => {
    Array.from(node.attributes).forEach(attr => {
      if (
        attr.name !== "id" &&
        attr.name !== "class" &&
        attr.name !== "src" &&
        attr.name !== "href"
      )
        node.removeAttribute(attr.name);
      // remove tracking get variables from links and l.facebook.com
      if (attr.name === "href") {
        try {
          let url = new URL(attr.value);
          if (url.host === "l.facebook.com")
            url = new URL(new URLSearchParams(url.search).get("u"));
          if (url.origin && url.pathname) {
            node.setAttribute(attr.name, url.origin + url.pathname);
          } else {
            node.removeAttribute(attr.name);
          }
        } catch (e) {
          node.removeAttribute(attr.name);
        }
      }
    });
    // walk through the tree.
    Array.from(node.children).forEach(killAttrs);
  };
  Array.from(node.children).forEach(killAttrs);
  return node.innerHTML.replace(/&/g, "&");
};

const checkSponsor = node => {
  return Array.from(node.querySelectorAll(".clearfix a, .ego_section a")).some(
    a => {
      a = a.cloneNode(true);
      const canary = Array.from(a.querySelectorAll("._2lgs"));
      Array.from(canary).forEach(canary => canary.remove());
      const text = a.textContent;
      const style = window
        .getComputedStyle(a, ":after")
        .getPropertyValue("content");
      return [
        "Gesponsord",
        "Sponsored",
        "Gesponsert",
        "Sponsrad",
        "Sponsorlu",
        "Sponsoroitu",
        "إعلان مُموَّل",
        "Sponsoreret",
        "Sponsorizzata",
        "Chartered"
      ].some(sponsor => {
        if (text === sponsor || style === `"${sponsor}"`) return true;
        return false;
      });
    }
  );
};

// We have to do this because content scripts can't read window variables
const grabVariable = (fn, args) => {
  let script = document.createElement("script");
  script.textContent =
    'localStorage.setItem("pageVariable", (' +
    fn +
    ").apply(this, " +
    JSON.stringify(args) +
    "));";
  script.setAttribute("id", "pageVariable");
  (document.head || document.documentElement).appendChild(script);
  script.remove();
  return localStorage.getItem("pageVariable");
};

// Getting the targeting information. We're careful to cache any results so as to avoid being
// blocked by facebook.
let targetingBlocked = false;
let targetingCache = new Map();
const getTargeting = ad => {
  if (ad.targeting) {
    if (targetingCache.has(ad.targeting))
      return Promise.resolve({
        ...ad,
        targeting: targetingCache.get(ad.targeting)
      });
    const url = ad.targeting;
    delete ad.targeting;
    if (targetingBlocked) return ad;
    return new Promise(resolve => {
      let req = new XMLHttpRequest();
      req.onreadystatechange = function() {
        if (req.readyState === 4) {
          try {
            const targeting = cleanAd(
              JSON.parse(req.response.replace("for (;;);", ""))["jsmods"][
                "markup"
              ][0][1]["__html"]
            );
            if (!targeting) {
              return resolve(ad);
            }
            targetingCache.set(url, targeting);
            resolve({
              ...ad,
              targeting
            });
          } catch (e) {
            targetingBlocked = true;
            setTimeout(() => (targetingBlocked = false), 15 * 60 * 100);
            ad.targeting = null;
            resolve(ad);
          }
        }
      };

      // This is all built out from a close reading of facebook's code.
      let built = grabVariable(
        url => {
          let parsed = new (window.require("URI"))(url);
          localStorage.setItem("url", url);
          let req = new window.AsyncRequest()
            .setURI(url)
            .setData(parsed)
            .setMethod("GET")
            .setRelativeTo(document.body)
            .setNectarModuleDataSafe(document.body)
            .setReadOnly(true);
          Object.assign(req.data, { __asyncDialog: 1 });
          Object.assign(req.data, window.require("getAsyncParams")(req.method));
          req._setUserActionID();
          req.setNewSerial();
          return req.uri.addQueryData(req.data).toString();
        },
        [url]
      );
      // AsyncRequest builds out the correct targeting url
      req.open("GET", "https://www.facebook.com" + built, true);
      req.send();
    });
  } else {
    ad.targeting = null;
    return Promise.resolve(ad);
  }
};

// We want to minimize the impact of a user's experience using facebook, so this function tries to
// restore the state of the page when the extension clicks around.
const refocus = cb => {
  const focus = document.activeElement;
  const ranges = [];
  for (var i = 0; i < window.getSelection().rangeCount; i++) {
    let range = window.getSelection().getRangeAt(i);
    ranges.push([
      range.startContainer,
      range.startOffset,
      range.endContainer,
      range.endOffset
    ]);
  }
  cb();
  if (focus) focus.focus();
  if (ranges.length > 0) {
    const newSelection = window.getSelection();
    newSelection.removeAllRanges();
    ranges.forEach(range_attrs => {
      const range = document.createRange();
      range.setStart(range_attrs[0], range_attrs[1]);
      range.setEnd(range_attrs[2], range_attrs[3]);
      newSelection.addRange(range);
    });
  }
};

// All of the menus on Facebook are asynchronously opened so we have to use a observer here to make
// sure we can grab the targeting urls. There's a ton of state here, so we should probably try and
// collapse it at some point.
let adCache = new Map();
const parseMenu = (ad, selector, toggle, toggleId, menuFilter, filter) => (
  resolve,
  reject
) => {
  let time = Date.now();
  let cb = (record, self) => {
    // give up if we haven't got anything after a second
    if (Date.now() - time > 1000) {
      self.disconnect();
      if (DEBUG) toggle.style.backgroundColor = "#630000"; // in debug, mark the button green if we failed to get the menu for this ad.
      return reject("no menu");
    }
    const menu = menuFilter();
    if (!menu) return null;
    const li = Array.from(menu.querySelectorAll("li")).filter(filter)[0];
    if (!li) return null;
    const endpoint = li.querySelector("a");
    if (!endpoint) return null;
    const url = endpoint.getAttribute("ajaxify");
    refocus(() => toggle.click());
    if (DEBUG) toggle.style.backgroundColor = "unset";
    self.disconnect();
    try {
      const resolved = {
        ...ad,
        id: new URL("https://facebook.com" + url).searchParams.get("id"),
        targeting: url
      };

      if (resolved.id) {
        adCache.set(toggleId, resolved);
        resolve(resolved);
      } else {
        reject("No ad id");
      }
    } catch (e) {
      reject(e);
    }
  };

  new MutationObserver(debounce(cb, 250)).observe(
    document.querySelector("#globalContainer"),
    {
      childList: true,
      subtree: true
    }
  );
  refocus(() => toggle.click());
};

// Grab an id from a timeline ad which is hidden in a async popup.
const getTimelineId = (parent, ad) => {
  const control = parent.querySelector(".uiPopover");
  if (!control && control.id === "") return Promise.resolve(ad);

  const toggle = control.querySelector("a");
  if (!toggle) return Promise.resolve(ad);

  if (adCache.has(toggle.id)) return Promise.resolve(adCache.get(toggle.id));

  // this is async, we have to wait until our popup shows up.
  let promise = new Promise(
    parseMenu(
      ad,
      ".uiLayer",
      toggle,
      toggle.id,
      () =>
        Array.from(document.querySelectorAll(".uiLayer")).filter(
          a => a.getAttribute("data-ownerid") === toggle.id
        )[0],
      it =>
        it.getAttribute("data-feed-option-name") === "FeedAdSeenReasonOption"
    )
  ).then(getTargeting);
  return promise;
};

// Similar to the above -- while we could just use the data-ego-fbid from before, it makes sense to
// use the one in the encoded url in case that the dom one goes away.
const getSidebarId = (parent, ad) => {
  const control = parent.querySelector(".uiSelector");
  if (!control) return Promise.resolve(ad);
  //Replicating getTimelineId.
  // Since the sidebar DOM structure is slightly different we need to pull out
  // the toggle Id from the data-gt attribute.
  const toggle = control.querySelector("a");
  if (!toggle) return Promise.resolve(ad);

  const toggleData = JSON.parse(toggle.getAttribute("data-gt"));
  if (!toggleData["data_to_log"]) return Promise.resolve(ad);

  const toggleId = toggleData["data_to_log"]["ad_id"];
  if (!toggleId) return Promise.resolve(ad);

  if (adCache.has(toggleId)) return Promise.resolve(adCache.get(toggleId));

  let promise = new Promise(
    parseMenu(
      ad,
      ".uiMenu",
      toggle,
      toggleId,
      () => control,
      it => it.getAttribute("data-label") === "Why am I seeing this?"
    )
  ).then(getTargeting);

  return promise;
};

const timeline = node => {
  const sponsor = checkSponsor(node);
  // First we check if it is actually a sponsored post
  if (!sponsor) return Promise.resolve(false);

  // And then we try to grab the parent container that has a hyperfeed id
  let parent = node;
  while (parent) {
    let id = parent.getAttribute("id");
    if (id && id.startsWith("hyperfeed")) break;
    parent = parent.parentElement;
  }

  // If we've walked off the top of the parent heirarchy that's an error
  if (!parent) return Promise.resolve(false);

  // Also if there's nothing to save that's an error
  if (node.children.length === 0) return Promise.resolve(false);

  // Check to see that we have the innermost fbUserContent, this cuts out like's
  // and shares.
  if (node.querySelector(TIMELINE_SELECTOR))
    node = node.querySelector(TIMELINE_SELECTOR);

  if (DEBUG) parent.style.color = "#006304"; // in debug, mark an ad green once we've selected it to be submitted (to help find ads that we don't recognize or posts we mistakenly believe are ads)

  // Finally we have something to save.
  return getTimelineId(parent, {
    html: cleanAd(node.children[0].outerHTML),
    created_at: new Date().toString()
  });
};

// Sidebar ads are a bit more complicated.
const sidebar = node => {
  // Although first we still need to make sure we are in a sponsored box;
  let parent = node;
  while (parent) {
    if (checkSponsor(parent)) break;
    parent = parent.parentElement;
  }

  // As before it is an error if we haven't found a sponsor node.
  if (!(parent && parent.classList && parent.classList.contains("ego_section")))
    return Promise.resolve(false);

  // Sanity check to make sure we have a salvageable id
  if (!node.hasAttribute("data-ego-fbid")) return Promise.resolve(false);

  // and we have childnodes
  if (!node.children.length === 0) return Promise.resolve(false);
  // Then we just need to send the cleaned ad
  return getSidebarId(parent, {
    html: cleanAd(node.outerHTML),
    created_at: new Date().toString()
  });
};

// We are careful here to only accept a valid timeline ad or sidebar ad
const parser = function(node) {
  const list = node.classList;
  if (list.contains("userContentWrapper") || list.contains("_5pcr")) {
    return timeline(node);
  } else if (list.contains("ego_unit")) {
    return sidebar(node);
  } else {
    return Promise.resolve(false);
  }
};

module.exports = {
  parser,
  TIMELINE_SELECTOR,
  SIDEBAR_SELECTOR
};
<commit_msg>wip on state machine<commit_after>import debounce from "lodash/debounce";

const TIMELINE_SELECTOR = ".userContentWrapper";
const SIDEBAR_SELECTOR = ".ego_unit";
const DEBUG =
  (process.env.NODE_ENV === "dev" || process.env.NODE_ENV) === "development"
    ? "development"
    : "production";

// create an ad for sending
const ad = node => ({
  html: cleanAd(node.outerHTML),
  created_at: new Date().toString()
});

// This is a simple state machine that aims to get rid of the promise mess of before in favor of a
// tick based approach. It should help us reason about the flow and transition states. And allow for
// easier testing
const states = {
  INITIAL: "INITIAL",
  TIMELINE: "TIMELINE",
  TIMELINE_ID: "TIMELINE_ID",
  SIDEBAR: "SIDEBAR",
  SIDEBAR_ID: "SIDEBAR_ID",
  DONE: "DONE",
  ERROR: "ERROR"
};
const errors = {
  NOT_AN_AD: "Not an ad",
  TIMEOUT: "Timeout transitioning states"
};
const TIMEOUT = 100000;
const POLL = 100;
export class Scraper {
  constructor(node, resolve, reject) {
    this.state = states.INITIAL;
    this.node = node;
    this.resolve = resolve;
    this.reject = reject;
  }

  start() {
    this.timer = setInterval(() => this.tick(), POLL);
    // cleanup after ourselves if we have been stuck for TIMEOUT millis
    this.timeout = setTimeout(() => {
      if (this.state !== states.DONE) {
        this.state = states.ERROR;
        this.message = errors.TIMEOUT;
      }
    }, TIMEOUT);
  }

  stop() {
    clearTimeout(this.timeout);
    clearInterval(this.timer);
  }

  tick() {
    switch (this.state) {
      case states.INITIAL:
        this.parse();
        break;
      case states.TIMELINE:
        this.timeline();
        break;
      case states.TIMELINE_ID:
        this.timelineId();
        break;
      case states.SIDEBAR:
        this.sidebar();
        break;
      case states.SIDEBAR_ID:
        this.sidebarId();
        break;
      case states.ERROR:
        this.error();
        break;
      case states.DONE:
        this.done();
        break;
    }
  }

  parse() {
    const list = this.node.classList;
    if (list.contains("userContentWrapper") || list.contains("_5pcr")) {
      this.state = states.TIMELINE;
    } else if (list.contains("ego_unit")) {
      this.state = states.SIDEBAR;
    } else {
      this.state = states.ERROR;
      this.message = errors.NOT_AN_AD;
    }
  }

  timeline() {
    const sponsor = checkSponsor(this.node);
    // First we check if it is actually a sponsored post
    if (!sponsor) return Promise.resolve(false);

    // And then we try to grab the parent container that has a hyperfeed id
    let parent = this.node;
    while (parent) {
      let id = parent.getAttribute("id");
      if (id && id.startsWith("hyperfeed")) break;
      parent = parent.parentElement;
    }

    // If we've walked off the top of the parent heirarchy that's an error
    if (!parent) return this.notAnAd();

    // Also if there's nothing to save that's an error
    if (this.node.children.length === 0) return this.notAnAd();

    // Check to see that we have the innermost fbUserContent, this cuts out like's
    // and shares.
    if (this.node.querySelector(TIMELINE_SELECTOR))
      this.node = this.node.querySelector(TIMELINE_SELECTOR);

    if (DEBUG) parent.style.color = "#006304";
    this.ad = ad(this.node.outerHTML);
    this.state = states.TIMELINE_ID;
  }

  timelineId() {}

  sidebar() {
    // First we still need to make sure we are in a sponsored box;
    let parent = this.node;
    while (parent) {
      if (checkSponsor(parent)) break;
      parent = parent.parentElement;
    }

    // As before it is an error if we haven't found a sponsor node.
    if (
      !(parent && parent.classList && parent.classList.contains("ego_section"))
    ) {
      return this.notAnAd();
    }
    // Sanity check to make sure we have a salvageable id
    if (!this.node.hasAttribute("data-ego-fbid")) {
      return this.notAnAd();
    }
    // and we have childnodes
    if (!this.node.children.length === 0) return this.notAnAd();
    this.ad = ad(this.node.outerHTML);
    // Move onto the next step
    this.state = states.SIDEBAR_ID;
  }

  sidebarId() {}

  notAnAd() {
    this.state = states.ERROR;
    this.message = errors.NOT_AN_AD;
  }

  done() {
    this.stop();
    this.resolve(this.ad, this);
  }

  error() {
    this.stop();
    this.reject(this.message, this);
  }
}

// This function cleans all the elements that could leak user data
// before sending to the server. It also removes any attributes that
// could have personal data so we end up with a clean dom tree.
const selectors = [
  "video",
  "input",
  "button",
  "iframe",
  'a[href=""]',
  ".accessible_elem",
  ".uiLikePagebutton",
  ".uiPopOver",
  ".uiCloseButton",
  ".uiChevronSelectorButton",
  "h5._1qbu",
  ".commentable_item"
].join(", ");

const cleanAd = html => {
  let node = document.createElement("div");
  node.innerHTML = html;

  // We're not saving video ads for now, we don't need buttons, hidden forms,
  // or like links
  Array.from(node.querySelectorAll(selectors)).forEach(i => i.remove());

  // remove attributes
  const killAttrs = node => {
    Array.from(node.attributes).forEach(attr => {
      if (
        attr.name !== "id" &&
        attr.name !== "class" &&
        attr.name !== "src" &&
        attr.name !== "href"
      )
        node.removeAttribute(attr.name);
      // remove tracking get variables from links and l.facebook.com
      if (attr.name === "href") {
        try {
          let url = new URL(attr.value);
          if (url.host === "l.facebook.com")
            url = new URL(new URLSearchParams(url.search).get("u"));
          if (url.origin && url.pathname) {
            node.setAttribute(attr.name, url.origin + url.pathname);
          } else {
            node.removeAttribute(attr.name);
          }
        } catch (e) {
          node.removeAttribute(attr.name);
        }
      }
    });
    // walk through the tree.
    Array.from(node.children).forEach(killAttrs);
  };
  Array.from(node.children).forEach(killAttrs);
  return node.innerHTML.replace(/&/g, "&");
};

const checkSponsor = node => {
  return Array.from(node.querySelectorAll(".clearfix a, .ego_section a")).some(
    a => {
      a = a.cloneNode(true);
      const canary = Array.from(a.querySelectorAll("._2lgs"));
      Array.from(canary).forEach(canary => canary.remove());
      const text = a.textContent;
      const style = window
        .getComputedStyle(a, ":after")
        .getPropertyValue("content");
      return [
        "Gesponsord",
        "Sponsored",
        "Gesponsert",
        "Sponsrad",
        "Sponsorlu",
        "Sponsoroitu",
        "إعلان مُموَّل",
        "Sponsoreret",
        "Sponsorizzata",
        "Chartered"
      ].some(sponsor => {
        if (text === sponsor || style === `"${sponsor}"`) return true;
        return false;
      });
    }
  );
};

// We have to do this because content scripts can't read window variables
const grabVariable = (fn, args) => {
  let script = document.createElement("script");
  script.textContent =
    'localStorage.setItem("pageVariable", (' +
    fn +
    ").apply(this, " +
    JSON.stringify(args) +
    "));";
  script.setAttribute("id", "pageVariable");
  (document.head || document.documentElement).appendChild(script);
  script.remove();
  return localStorage.getItem("pageVariable");
};

// Getting the targeting information. We're careful to cache any results so as to avoid being
// blocked by facebook.
let targetingBlocked = false;
let targetingCache = new Map();
const getTargeting = ad => {
  if (ad.targeting) {
    if (targetingCache.has(ad.targeting))
      return Promise.resolve({
        ...ad,
        targeting: targetingCache.get(ad.targeting)
      });
    const url = ad.targeting;
    delete ad.targeting;
    if (targetingBlocked) return ad;
    return new Promise(resolve => {
      let req = new XMLHttpRequest();
      req.onreadystatechange = function() {
        if (req.readyState === 4) {
          try {
            const targeting = cleanAd(
              JSON.parse(req.response.replace("for (;;);", ""))["jsmods"][
                "markup"
              ][0][1]["__html"]
            );
            if (!targeting) {
              return resolve(ad);
            }
            targetingCache.set(url, targeting);
            resolve({
              ...ad,
              targeting
            });
          } catch (e) {
            targetingBlocked = true;
            setTimeout(() => (targetingBlocked = false), 15 * 60 * 100);
            ad.targeting = null;
            resolve(ad);
          }
        }
      };

      // This is all built out from a close reading of facebook's code.
      let built = grabVariable(
        url => {
          let parsed = new (window.require("URI"))(url);
          localStorage.setItem("url", url);
          let req = new window.AsyncRequest()
            .setURI(url)
            .setData(parsed)
            .setMethod("GET")
            .setRelativeTo(document.body)
            .setNectarModuleDataSafe(document.body)
            .setReadOnly(true);
          Object.assign(req.data, { __asyncDialog: 1 });
          Object.assign(req.data, window.require("getAsyncParams")(req.method));
          req._setUserActionID();
          req.setNewSerial();
          return req.uri.addQueryData(req.data).toString();
        },
        [url]
      );
      // AsyncRequest builds out the correct targeting url
      req.open("GET", "https://www.facebook.com" + built, true);
      req.send();
    });
  } else {
    ad.targeting = null;
    return Promise.resolve(ad);
  }
};

// We want to minimize the impact of a user's experience using facebook, so this function tries to
// restore the state of the page when the extension clicks around.
const refocus = cb => {
  const focus = document.activeElement;
  const ranges = [];
  for (var i = 0; i < window.getSelection().rangeCount; i++) {
    let range = window.getSelection().getRangeAt(i);
    ranges.push([
      range.startContainer,
      range.startOffset,
      range.endContainer,
      range.endOffset
    ]);
  }
  cb();
  if (focus) focus.focus();
  if (ranges.length > 0) {
    const newSelection = window.getSelection();
    newSelection.removeAllRanges();
    ranges.forEach(range_attrs => {
      const range = document.createRange();
      range.setStart(range_attrs[0], range_attrs[1]);
      range.setEnd(range_attrs[2], range_attrs[3]);
      newSelection.addRange(range);
    });
  }
};

// All of the menus on Facebook are asynchronously opened so we have to use a observer here to make
// sure we can grab the targeting urls. There's a ton of state here, so we should probably try and
// collapse it at some point.
let adCache = new Map();
const parseMenu = (ad, selector, toggle, toggleId, menuFilter, filter) => (
  resolve,
  reject
) => {
  let time = Date.now();
  let cb = (record, self) => {
    // give up if we haven't got anything after a second
    if (Date.now() - time > 1000) {
      self.disconnect();
      if (DEBUG) toggle.style.backgroundColor = "#630000"; // in debug, mark the button green if we failed to get the menu for this ad.
      return reject("no menu");
    }
    const menu = menuFilter();
    if (!menu) return null;
    const li = Array.from(menu.querySelectorAll("li")).filter(filter)[0];
    if (!li) return null;
    const endpoint = li.querySelector("a");
    if (!endpoint) return null;
    const url = endpoint.getAttribute("ajaxify");
    refocus(() => toggle.click());
    if (DEBUG) toggle.style.backgroundColor = "unset";
    self.disconnect();
    try {
      const resolved = {
        ...ad,
        id: new URL("https://facebook.com" + url).searchParams.get("id"),
        targeting: url
      };

      if (resolved.id) {
        adCache.set(toggleId, resolved);
        resolve(resolved);
      } else {
        reject("No ad id");
      }
    } catch (e) {
      reject(e);
    }
  };

  new MutationObserver(debounce(cb, 250)).observe(
    document.querySelector("#globalContainer"),
    {
      childList: true,
      subtree: true
    }
  );
  refocus(() => toggle.click());
};

// Grab an id from a timeline ad which is hidden in a async popup.
const getTimelineId = (parent, ad) => {
  const control = parent.querySelector(".uiPopover");
  if (!control && control.id === "") return Promise.resolve(ad);

  const toggle = control.querySelector("a");
  if (!toggle) return Promise.resolve(ad);

  if (adCache.has(toggle.id)) return Promise.resolve(adCache.get(toggle.id));

  // this is async, we have to wait until our popup shows up.
  let promise = new Promise(
    parseMenu(
      ad,
      ".uiLayer",
      toggle,
      toggle.id,
      () =>
        Array.from(document.querySelectorAll(".uiLayer")).filter(
          a => a.getAttribute("data-ownerid") === toggle.id
        )[0],
      it =>
        it.getAttribute("data-feed-option-name") === "FeedAdSeenReasonOption"
    )
  ).then(getTargeting);
  return promise;
};

// Similar to the above -- while we could just use the data-ego-fbid from before, it makes sense to
// use the one in the encoded url in case that the dom one goes away.
const getSidebarId = (parent, ad) => {
  const control = parent.querySelector(".uiSelector");
  if (!control) return Promise.resolve(ad);
  //Replicating getTimelineId.
  // Since the sidebar DOM structure is slightly different we need to pull out
  // the toggle Id from the data-gt attribute.
  const toggle = control.querySelector("a");
  if (!toggle) return Promise.resolve(ad);

  const toggleData = JSON.parse(toggle.getAttribute("data-gt"));
  if (!toggleData["data_to_log"]) return Promise.resolve(ad);

  const toggleId = toggleData["data_to_log"]["ad_id"];
  if (!toggleId) return Promise.resolve(ad);

  if (adCache.has(toggleId)) return Promise.resolve(adCache.get(toggleId));

  let promise = new Promise(
    parseMenu(
      ad,
      ".uiMenu",
      toggle,
      toggleId,
      () => control,
      it => it.getAttribute("data-label") === "Why am I seeing this?"
    )
  ).then(getTargeting);

  return promise;
};

const timeline = node => {
  const sponsor = checkSponsor(node);
  // First we check if it is actually a sponsored post
  if (!sponsor) return Promise.resolve(false);

  // And then we try to grab the parent container that has a hyperfeed id
  let parent = node;
  while (parent) {
    let id = parent.getAttribute("id");
    if (id && id.startsWith("hyperfeed")) break;
    parent = parent.parentElement;
  }

  // If we've walked off the top of the parent heirarchy that's an error
  if (!parent) return Promise.resolve(false);

  // Also if there's nothing to save that's an error
  if (node.children.length === 0) return Promise.resolve(false);

  // Check to see that we have the innermost fbUserContent, this cuts out like's
  // and shares.
  if (node.querySelector(TIMELINE_SELECTOR))
    node = node.querySelector(TIMELINE_SELECTOR);

  if (DEBUG) parent.style.color = "#006304"; // in debug, mark an ad green once we've selected it to be submitted (to help find ads that we don't recognize or posts we mistakenly believe are ads)

  // Finally we have something to save.
  return getTimelineId(parent, {
    html: cleanAd(node.children[0].outerHTML),
    created_at: new Date().toString()
  });
};

// Sidebar ads are a bit more complicated.
const sidebar = node => {
  // Although first we still need to make sure we are in a sponsored box;
  let parent = node;
  while (parent) {
    if (checkSponsor(parent)) break;
    parent = parent.parentElement;
  }

  // As before it is an error if we haven't found a sponsor node.
  if (!(parent && parent.classList && parent.classList.contains("ego_section")))
    return Promise.resolve(false);

  // Sanity check to make sure we have a salvageable id
  if (!node.hasAttribute("data-ego-fbid")) return Promise.resolve(false);

  // and we have childnodes
  if (!node.children.length === 0) return Promise.resolve(false);
  // Then we just need to send the cleaned ad
  return getSidebarId(parent, {});
};

// We are careful here to only accept a valid timeline ad or sidebar ad
const parser = function(node) {
  const list = node.classList;
  if (list.contains("userContentWrapper") || list.contains("_5pcr")) {
    return timeline(node);
  } else if (list.contains("ego_unit")) {
    return sidebar(node);
  } else {
    return Promise.resolve(false);
  }
};

module.exports = {
  parser,
  TIMELINE_SELECTOR,
  SIDEBAR_SELECTOR
};
<|endoftext|>


<commit_before>/*
 Main server file for sendmail app
*/

/*** Application settings */
var port = process.env.PORT || 5000; // heroku port || local test port
var testing = (port == 5000); // the port tells us if we're testing
var host = 'https://sendmail4911.herokuapp.com';
if(testing) {
    host = 'http://localhost:5000';
}

/*** Required modules */
var express = require('express');
var request = require('request');
var RedisStore = require('connect-redis')(express);
var secrets = require('./secrets.json');   /* keys, codes, etc. */
var auth    = require('./lib/auth.js')(host);/* lib for auth routes */
var user    = require('./lib/user.js');    /* user routes, api calls */
var mail    = require('./lib/mail.js');    /* mail handling methods */
var error   = require('./lib/error.js');   /* error handling methods */

/*** Establish database connection */
var rtg, redis, redis_opts;
if (process.env.REDISTOGO_URL) {
    rtg   = require("url").parse(process.env.REDISTOGO_URL);
    redis = require("redis").createClient(rtg.port, rtg.hostname);
    redis.auth(rtg.auth.split(":")[1]);
} else {
    redis = require("redis").createClient();
}

/*** Configure express application */
var app = express();
// turn on default logging
app.use(express.logger());

// use ejs for templates
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');

// enable gzipping
app.use(express.compress());

// serve files out of static dir
app.use(express.static(__dirname + '/static', {maxAge: 31557600000}));

// middleware for post data processing
app.use(express.json());
app.use(express.urlencoded());

// cookie processing for session
app.use(express.cookieParser(secrets.cookie_pass || 'dumb pass'));

// use express's session handling for most of user management
app.use(express.session({
    store: new RedisStore({client: redis})
}));

/*** Endpoints */
app.get('/', function(req, res){
    var template = 'landing';
    var opts = {'gauth_url': auth.gauth_url};
    if (req.session.name) {
        if (req.session.error) {
            opts.err = "There was an error logging in";
        } else {
            template = 'main';
            opts = {'loading': (req.session.id_token === undefined)};
        }
    }
    res.render(template, opts);
});
app.get('/profile', user.profile);
app.get('/logout', auth.logout);
app.get(auth.authroute, auth.authorize);
app.post('/schedule', mail.schedule); /* TODO in terms of our api,
* should we accept all here (http://expressjs.com/3x/api.html#app.all)
* or would it just obfuscate things? thoughts?
*/

/*** Add error handlers */
require('./lib/error.js')(app);

/*** Start server */
app.listen(port, function() {
    console.log('Listening on ' + port);
});

<commit_msg>fixed max age to a year for static files<commit_after>/*
 Main server file for sendmail app
*/

/*** Application settings */
var port = process.env.PORT || 5000; // heroku port || local test port
var testing = (port == 5000); // the port tells us if we're testing
var host = 'https://sendmail4911.herokuapp.com';
if(testing) {
    host = 'http://localhost:5000';
}

/*** Required modules */
var express = require('express');
var request = require('request');
var RedisStore = require('connect-redis')(express);
var secrets = require('./secrets.json');   /* keys, codes, etc. */
var auth    = require('./lib/auth.js')(host);/* lib for auth routes */
var user    = require('./lib/user.js');    /* user routes, api calls */
var mail    = require('./lib/mail.js');    /* mail handling methods */
var error   = require('./lib/error.js');   /* error handling methods */

/*** Establish database connection */
var rtg, redis, redis_opts;
if (process.env.REDISTOGO_URL) {
    rtg   = require("url").parse(process.env.REDISTOGO_URL);
    redis = require("redis").createClient(rtg.port, rtg.hostname);
    redis.auth(rtg.auth.split(":")[1]);
} else {
    redis = require("redis").createClient();
}

/*** Configure express application */
var app = express();
// turn on default logging
app.use(express.logger());

// use ejs for templates
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');

// enable gzipping
app.use(express.compress());

// serve files out of static dir
app.use(express.static(__dirname + '/static', {maxAge: 31536000}));

// middleware for post data processing
app.use(express.json());
app.use(express.urlencoded());

// cookie processing for session
app.use(express.cookieParser(secrets.cookie_pass || 'dumb pass'));

// use express's session handling for most of user management
app.use(express.session({
    store: new RedisStore({client: redis})
}));

/*** Endpoints */
app.get('/', function(req, res){
    var template = 'landing';
    var opts = {'gauth_url': auth.gauth_url};
    if (req.session.name) {
        if (req.session.error) {
            opts.err = "There was an error logging in";
        } else {
            template = 'main';
            opts = {'loading': (req.session.id_token === undefined)};
        }
    }
    res.render(template, opts);
});
app.get('/profile', user.profile);
app.get('/logout', auth.logout);
app.get(auth.authroute, auth.authorize);
app.post('/schedule', mail.schedule); /* TODO in terms of our api,
* should we accept all here (http://expressjs.com/3x/api.html#app.all)
* or would it just obfuscate things? thoughts?
*/

/*** Add error handlers */
require('./lib/error.js')(app);

/*** Start server */
app.listen(port, function() {
    console.log('Listening on ' + port);
});

<|endoftext|>


<commit_before>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that achieves no real goal.

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar f<commit_msg>Who you calling a fool?<commit_after>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that achieves no real goal.

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fi<|endoftext|>


<commit_before>var _parse = require('babel-core').parse;

module.exports = {
  parse: function(code){
    var ast = null
    try {
      ast = _parse(code, { locations: true, ranges: true }).body[0] //unwrap body
    }
    catch (err){
      console.warn(err)
    }

    return ast
  }
}<commit_msg>remove helpers.js, which is unused<commit_after><|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
    Message = mongoose.model('Message'),
    Syndicate = mongoose.model('Syndicate'),
    _ = require('lodash');

// Load the twilio module
var twilio = require('twilio');

// Create a new REST API client to make authenticated requests against the twilio back end
var twilio_client = new twilio.RestClient();

var twilioConfig = {
    sid : 'TWILIO_ACCOUNT_SID',
    token : 'TWILIO_AUTH_TOKEN',
    number : 'TWILIO_NUMBER'
};

var helpInfo = {
    'action':'help',
    'message':'Commands: START [NAME] or STOP [NAME].  You are subscribed to [TODO]'
};

exports.parseMessage = function(text) {
    var a = text.split(' ');
    var command = a[0].toUpperCase();
    if (a.length < 2 ||  command == 'HELP'){
        return helpInfo;
    }

    switch (command) {
        case 'START':
            return {
                'action': 'start ' + a[1],
                'message': 'You have subscribed to ' + a[1] + ' messages.'
            };
        case 'STOP':
            return {
                'action': 'stop '+a[1],
                'message': 'You have unsubscribed.  To resubscribe text START '+a[1]+' to this number'
            };
        default:
            return {
                'action': 'error '+a[1],
                'message': 'I don\'t know what to do with that command.  '+helpInfo.message
            };
    }

/*
    Syndicate.findOne({'syndicate': }).sort('-created').populate('user', 'displayName').exec(function(err, syndicates) {
        if (err) {
            return res.send(400, {
                message: getErrorMessage(err)
            });
        } else {
            res.jsonp(syndicates);
        }
    });
*/

};

/**
 * Create a Message
 */
exports.receive = function(req, res) {

    console.log('\nTwilio service request received: '+req.url);
    var resp = new twilio.TwimlResponse();
    //var twilio_client = new twilio.RestClient();

    var text = req.body.Body;

    var parsed = parseMessage(text);

    //var reversed = text.split('').reverse().join('') + '...' + req.body.From;

    var message = new Message({
        'text': req.body.Body,
        'number': req.body.From,
        'outgoing': false,
        'response':parsed.message
    });
    message.save();

    resp.sms(reversed);
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.end(resp.toString());
};

/**
 * Outoing Message
 */
exports.send = function(req, res) {

    var sendTo = '3037154487',
        sentFrom = '7203167666',
        user = req.user;

    var message = new Message({
        'text': req.body.text,
        'number': sendTo,
        'outgoing': true,
        'sender': {
            'displayName':user.displayName,
            'username':user.username
        }
    });
    message.save();

    // Pass in parameters to the REST API using an object literal notation. The
    // REST client will handle authentication and response serialzation for you.
    twilio_client.sms.messages.create({
        to: sendTo,
        from: sentFrom,
        body:message.text
    }, function(error, messageInfo) {
        // The HTTP request to Twilio will run asynchronously. This callback
        // function will be called when a response is received from Twilio
        // The 'error" variable will contain error information, if any.
        // If the request was successful, this value will be "falsy"
        if (!error) {
            // The second argument to the callback will contain the information
            // sent back by Twilio for the request. In this case, it is the
            // information about the text messsage you just sent:
            console.log('Success! The SID for this SMS message is:');
            console.log(messageInfo.sid);

            console.log('Message sent on:');
            console.log(messageInfo.dateCreated);
        } else {
            console.log('Oops! There was an error.');
        }
    });
};

/**
 * Get the error message from error object
 */
var getErrorMessage = function(err) {
    var message = '';
    if (err.code) {
        switch (err.code) {
            case 11000:
            case 11001:
                message = 'Message already exists';
                break;
            default:
                message = 'Something went wrong';
        }
    } else {
        for (var errName in err.errors) {
            if (err.errors[errName].message) message = err.errors[errName].message;
        }
    }
    return message;
};

/**
 * Save an Message

exports.save = function(req, res) {
console.log("saving");
    console.dir("xsaving");
}; */

/**
 * Show the current Message
 */
exports.read = function(req, res) {

};

/**
 * Update a Message
 */
exports.update = function(req, res) {

};

/**
 * Delete an Message
 */
exports.delete = function(req, res) {

};

/**
 * List of Messages
 */
exports.list = function(req, res) {
    //newest on top
    Message.find().sort({'created':-1}).exec(function(err, messages) {
        if (err) {
            return res.send(400, {
                message: getErrorMessage(err)
            });
        } else {
            res.jsonp(messages);
        }
    });
};<commit_msg>parsing messages from user and returning mock stop/start/help messages<commit_after>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
    Message = mongoose.model('Message'),
    Syndicate = mongoose.model('Syndicate'),
    _ = require('lodash');

// Load the twilio module
var twilio = require('twilio');

// Create a new REST API client to make authenticated requests against the twilio back end
var twilio_client = new twilio.RestClient();

var twilioConfig = {
    sid : 'TWILIO_ACCOUNT_SID',
    token : 'TWILIO_AUTH_TOKEN',
    number : 'TWILIO_NUMBER'
};

var helpInfo = {
    'action':'help',
    'message':'Commands: START [NAME] or STOP [NAME].  You are subscribed to [TODO]'
};

exports.parseMessage = function(text) {
    var a = text.split(' ');
    var command = a[0].toUpperCase();
    if (a.length < 2 ||  command === 'HELP'){
        return helpInfo;
    }

    switch (command) {
        case 'START':
            return {
                'action': 'start ' + a[1],
                'message': 'You have subscribed to ' + a[1] + ' messages.'
            };
        case 'STOP':
            return {
                'action': 'stop '+a[1],
                'message': 'You have unsubscribed.  To resubscribe text START '+a[1]+' to this number'
            };
        default:
            return {
                'action': 'error '+a[1],
                'message': 'I don\'t know what to do with that command.  '+helpInfo.message
            };
    }

/*
    Syndicate.findOne({'syndicate': }).sort('-created').populate('user', 'displayName').exec(function(err, syndicates) {
        if (err) {
            return res.send(400, {
                message: getErrorMessage(err)
            });
        } else {
            res.jsonp(syndicates);
        }
    });
*/

};

/**
 * Create a Message
 */
exports.receive = function(req, res) {

    console.log('\nTwilio service request received: '+req.url);
    var resp = new twilio.TwimlResponse();
    //var twilio_client = new twilio.RestClient();

    var text = req.body.Body;

    var parsed = exports.parseMessage(text);

    //var reversed = text.split('').reverse().join('') + '...' + req.body.From;

    var message = new Message({
        'text': req.body.Body,
        'number': req.body.From,
        'outgoing': false,
        'response':parsed.message
    });
    message.save();

    resp.sms(message.response);
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.end(resp.toString());
};

/**
 * Outoing Message
 */
exports.send = function(req, res) {

    var sendTo = '3037154487',
        sentFrom = '7203167666',
        user = req.user;

    var message = new Message({
        'text': req.body.text,
        'number': sendTo,
        'outgoing': true,
        'sender': {
            'displayName':user.displayName,
            'username':user.username
        }
    });
    message.save();

    // Pass in parameters to the REST API using an object literal notation. The
    // REST client will handle authentication and response serialzation for you.
    twilio_client.sms.messages.create({
        to: sendTo,
        from: sentFrom,
        body:message.text
    }, function(error, messageInfo) {
        // The HTTP request to Twilio will run asynchronously. This callback
        // function will be called when a response is received from Twilio
        // The 'error" variable will contain error information, if any.
        // If the request was successful, this value will be "falsy"
        if (!error) {
            // The second argument to the callback will contain the information
            // sent back by Twilio for the request. In this case, it is the
            // information about the text messsage you just sent:
            console.log('Success! The SID for this SMS message is:');
            console.log(messageInfo.sid);

            console.log('Message sent on:');
            console.log(messageInfo.dateCreated);
        } else {
            console.log('Oops! There was an error.');
        }
    });
};

/**
 * Get the error message from error object
 */
var getErrorMessage = function(err) {
    var message = '';
    if (err.code) {
        switch (err.code) {
            case 11000:
            case 11001:
                message = 'Message already exists';
                break;
            default:
                message = 'Something went wrong';
        }
    } else {
        for (var errName in err.errors) {
            if (err.errors[errName].message) message = err.errors[errName].message;
        }
    }
    return message;
};

/**
 * Save an Message

exports.save = function(req, res) {
console.log("saving");
    console.dir("xsaving");
}; */

/**
 * Show the current Message
 */
exports.read = function(req, res) {

};

/**
 * Update a Message
 */
exports.update = function(req, res) {

};

/**
 * Delete an Message
 */
exports.delete = function(req, res) {

};

/**
 * List of Messages
 */
exports.list = function(req, res) {
    //newest on top
    Message.find().sort({'created':-1}).exec(function(err, messages) {
        if (err) {
            return res.send(400, {
                message: getErrorMessage(err)
            });
        } else {
            res.jsonp(messages);
        }
    });
};<|endoftext|>


<commit_before>"use strict";

const
    ESLINT = "../node_modules/eslint/",
    RULES = ESLINT + "lib/rules",
    CONFIGURATION = "../.eslintrc",

    fs = require("fs"),
    path = require("path"),

    levels = [
        "ignore",
        "warning",
        "error",
        "-"
    ],

    gpfConfiguration = JSON.parse(fs.readFileSync(path.join(__dirname, CONFIGURATION)).toString()),
    ruleFilenames = fs.readdirSync(path.join(__dirname, RULES)),
    orderOfCategories = [
        "Possible Errors",
        "Best Practices",
        "Strict Mode",
        "Variables",
        "Node.js and CommonJS",
        "Stylistic Issues",
        "ECMAScript 6"
    ]
;

// Assess that all rules specified in the .eslintrc actually exists !
Object.keys(gpfConfiguration.rules)
    .filter(name => !ruleFilenames.includes(`${name}.js`))
    .forEach(name => {
        console.error(`Unknown rule name: ${name}`);
    });

ruleFilenames
    .map(ruleFilename => {
        const
            name = path.basename(ruleFilename, ".js"),
            rule = require(path.join(__dirname, "../node_modules/eslint/lib/rules", ruleFilename)),
            docs = rule.meta.docs,
            category = docs.category,
            description = docs.description,
            recommended = docs.recommended,
            hasProperties = rule.meta.schema.length ? rule.meta.schema[0].properties : false,
            config = gpfConfiguration.rules[name];
        let
            level = levels.length - 1,
            hasPropertiesSet = false;
        if (Array.isArray(config)) {
            level = config[0];
        } else if (undefined !== config) {
            level = config;
        }
        return {
            name,
            category,
            description,
            recommended,
            hasProperties,
            config,
            level,
            hasPropertiesSet
        };
    })
    .sort((rule1, rule2) => {
        const
            rule1CategoryOrder = orderOfCategories.indexOf(rule1.category),
            rule2CategoryOrder = orderOfCategories.indexOf(rule2.category);
        if (rule1CategoryOrder !== rule2CategoryOrder) {
            return rule1CategoryOrder - rule2CategoryOrder;
        }
        return rule1.name.localeCompare(rule2.name);
    })
    .forEach((rule, index, rules) => {
        const flag = (value, mark) => value ? mark : " ";
        if (0 === index || rules[index - 1].category !== rule.category) {
            console.log(rule.category);
        }
        if (rule.hasPropertiesSet && !rule.hasProperties) {
            console.error(`Unexpected parameters for ${rule.name}`);
        }
        console.log(
            "\t",
            rule.name.padEnd(40, " "),
            flag(rule.recommended, "R"),
            levels[rule.level].padEnd(7),
            flag(rule.hasPropertiesSet, "C")
        );
    });
<commit_msg>Code simplification (#280)<commit_after>"use strict";

const
    fs = require("fs"),
    path = require("path"),

    ESLINT = path.join(__dirname, "../node_modules/eslint/"),
    ESLINT_RULES = path.join(ESLINT, "lib/rules"),
    ESLINT_CATEGORIES = path.join(ESLINT, "conf/category-list.json"),
    ESLINTRC = path.join(__dirname, "../.eslintrc"),
    DOCUMENTATION = path.join(__dirname, "linting"),

    RULE_NOT_SET = "-",

    LEVELS = [
        "ignore",
        "warning",
        "error",
        RULE_NOT_SET
    ],

    ruleFilenames = fs.readdirSync(ESLINT_RULES),
    orderOfCategories = JSON.parse(fs.readFileSync(ESLINT_CATEGORIES).toString()).categories
        .map(category => category.name),
    eslintrc = JSON.parse(fs.readFileSync(ESLINTRC).toString()),

    readLevel = ruleConfiguration => {
        if (Array.isArray(ruleConfiguration)) {
            return ruleConfiguration[0];
        }
        if (undefined !== ruleConfiguration) {
            return ruleConfiguration;
        }
        return LEVELS.length - 1;
    },

    hasDocumentation = ruleName => {
        try {
            fs.accessSync(path.join(DOCUMENTATION, `${ruleName}.md`), fs.constants.R_OK);
            return true;
        } catch (e) {
            return false;
        }
    },

    configuration = ruleName => {
        const
            ruleConfiguration = eslintrc.rules[ruleName],
            documented = hasDocumentation(ruleName);
        if (ruleConfiguration) {
            return {
                level: LEVELS[readLevel(ruleConfiguration)],
                parameterized: Array.isArray(ruleConfiguration),
                documented: documented
            };
        }
        return {
            level: RULE_NOT_SET,
            documented: documented
        };
    },

    checkForInvalidProperties = rule => {
        if (rule.meta.schema && Object.keys(rule.meta.schema).length) {
            return; // Whether an array or an object, it has properties
        }
        if (rule.parameterized) {
            console.error(`Unexpected parameters for ${rule.name}`);
        }
    },

    checkForDeprecatedRule = rule => {
        if (rule.meta.deprecated && rule.level !== RULE_NOT_SET) {
            console.error(
                "\t",
                rule.name.padEnd(40, " "),
                "is deprecated, should use:",
                rule.meta.docs.replacedBy.join(",")
            );
        }
    },

    checkForInvalidUse = rule => {
        checkForInvalidProperties(rule);
        checkForDeprecatedRule(rule);
    }
;

// Assess that all rules specified in the .eslintrc actually exists !
Object.keys(eslintrc.rules)
    .filter(name => !ruleFilenames.includes(`${name}.js`))
    .forEach(name => {
        console.error(`Unknown rule name: ${name}`);
    });

// List rules
ruleFilenames
    .map(ruleFilename => {
        const name = path.basename(ruleFilename, ".js");
        return Object.assign({
            name: name,
            meta: require(path.join(__dirname, "../node_modules/eslint/lib/rules", ruleFilename)).meta
        }, configuration(name));
    })
    .sort((rule1, rule2) => {
        const
            rule1CategoryOrder = orderOfCategories.indexOf(rule1.meta.docs.category),
            rule2CategoryOrder = orderOfCategories.indexOf(rule2.meta.docs.category);
        if (rule1CategoryOrder !== rule2CategoryOrder) {
            return rule1CategoryOrder - rule2CategoryOrder;
        }
        return rule1.name.localeCompare(rule2.name);
    })
    .forEach((rule, index, rules) => {
        const
            flag = (value, mark) => value ? mark : " ",
            category = rule.meta.docs.category;
        if (0 === index || rules[index - 1].meta.docs.category !== category) {
            console.log(category);
        }
        checkForInvalidUse(rule);
        console.log(
            "\t",
            rule.name.padEnd(40, " "),
            flag(rule.meta.docs.recommended, "R"),
            rule.level.padEnd(7),
            flag(rule.parameterized, "C"),
            flag(rule.documented, "*")
        );
    });
<|endoftext|>