<commit_before>// Internal modules
var path = require("path");
var fs = require("fs");

// NPM modules
var request = require("request");
var lunr = require("lunr");
var rimraf = require("rimraf");
var mkdirp = require("mkdirp");
var router = require("express").Router();
var showdown  = require("showdown");
var converter = new showdown.Converter();
var fs2obj = require('fs2obj');

// Project modules
var config = require("../lib/config");

// Define base folder for dcos/examples repo
var dcosExamplesFolder = path.join(__dirname, "../", "dcos-examples");

// Clean dcos/examples repo folder
rimraf.sync(dcosExamplesFolder);

// Global git object
var git = {};

// Create cache object for the dcos/example packages
var exampleCache = {};

// Placeholder for the pull interval
var pullInterval = {};

// Placeholder for the repository interval
var repositoryInterval = {};

// Create clean folder for dcos/examples repo
mkdirp(dcosExamplesFolder, function (err) {
    if (err) {
        console.error(err);
        process.exit(1);
    } else {
        console.log("Created folder for repo dcos/examples");
        // Instantiate git
        git = require('simple-git')(dcosExamplesFolder);
        console.log("Instantiated git");
        // Clone the repo
        git.clone("https://github.com/dcos/examples.git", dcosExamplesFolder, {}, function (err, result) {
            if (err) {
                console.log(err);
                process.exit(1);
            } else {
                console.log("Successfully cloned dcos/example repository");
                // Fill the examplesCache with the rendered results of the markdown files
                fillExamples();
                console.log("Generated and cached HTML from the dcos/example repository");
                // Load the repository initially
                loadRepository();
                console.log("Initially loaded the DC/OS repository information");
                // Set the periodical "git pull" as interval, every 5 minutes
                pullInterval = setInterval(function () {
                    var startTime = new Date().getTime();
                    git.pull("origin", "master", function(err, result) {
                        var endTime = new Date().getTime();
                        if (err) {
                            console.log("Pull from origin/master failed in " + (endTime-startTime) + "ms");
                            console.log(err);
                            //process.exit(1);
                        } else {
                            console.log("Successfully pulled from origin/master in " + (endTime-startTime) + "ms");
                            // Fill the examplesCache with the rendered results of the markdown files
                            fillExamples();
                            console.log("Updated and cached HTML from the dcos/example repository");
                        }
                    });
                }, config.application.refresh.examples);
                console.log("Started dcos/example pull interval (" + (config.application.refresh.examples/1000) + " secs)!");
                // Set periodical for repository refresh as interval
                repositoryInterval = setInterval(loadRepository, config.application.refresh.universe);
                console.log("Started load repository interval (" + (config.application.refresh.universe/1000) + " secs)!");
            }
        });
    }
});

function fillExamples() {

    // Read the folder/file structure
    var folderStructure = fs2obj(dcosExamplesFolder + "/1.8");

    // RegExp for finding anchor links in the markdown files
    var relativeLinkRegExp = /\(#(.*?)\)/g; // Matches "(#my-test-link)" as "my-test-link"

    // Reset exampleCache
    exampleCache = {};

    // Iterate over items
    folderStructure.items.forEach(function (item) {
        // Use folders
        if (item.type === "folder") {
            var packageName = item.name.toLowerCase();
            // Set baseUrl
            var baseUrl = "https://raw.githubusercontent.com/dcos/examples/master/1.8/" + packageName;
            // Read README.md contents
            var exampleContents = fs.readFileSync(dcosExamplesFolder + "/1.8/" + packageName + "/README.md", "utf8").toString();
            // Get relative links
            var relativeLinks = exampleContents.match(relativeLinkRegExp);
            // Check if anchor links found
            if (relativeLinks && relativeLinks.length > 0) {
                // Handle relative links -> Replace with full link including the anchor
                relativeLinks.forEach(function (relativeLink) {
                    var bareLink = relativeLink.replace("(", "").replace(")", "").replace(/-/g, "");
                    var replaceRelativeLink = "(/#/package/" + packageName + "/docs" + bareLink + ")";
                    exampleContents = exampleContents.replace(relativeLink, replaceRelativeLink)
                });
            }
            // Convert to HTML and replace image sources
            var htmlCode = converter.makeHtml(exampleContents).replace(/img\//g, baseUrl + "/img/"); // Replace relative URL with absolute URL

            // There is an example for this package
            exampleCache[packageName] = {
                renderedHtml: htmlCode,
                enabled: true
            };
        }
    });

}

// Sample call to DC/OS universe
// curl -X GET -L -H "user-agent: dcos/1.8" -H "accept: application/vnd.dcos.universe.repo+json;charset=utf-8;version=v3" "https://universe.mesosphere.com/repo"

// Set request options
var options = {
    "headers": {
        "user-agent": "dcos/1.8",
        "accept": "application/vnd.dcos.universe.repo+json;charset=utf-8;version=v3"
    },
    "uri": "https://universe.mesosphere.com/repo",
    "method": "GET"
};

// Create packages singleton
var packages = {
    index: lunr(function () {
        this.field("name");
        this.field("tags", {boost: 10});
        this.field("description");
        this.field("releaseVersion");
        this.ref("name")
    }),
    map: {},
    list: []
};

//
var resetPackages = function () {
    packages.index = lunr(function () {
        this.field("name");
        this.field("tags", {boost: 10});
        this.field("description");
        this.field("releaseVersion");
        this.ref("name")
    });
    // Reset other properties
    packages.list.length = 0;
    packages.map = {};
};

function generateSortFn(prop, reverse) {
    return function (a, b) {
        if (a[prop] < b[prop]) return reverse ? 1 : -1;
        if (a[prop] > b[prop]) return reverse ? -1 : 1;
        return 0;
    };
}

var loadRepository = function () {
    request(options, function (error, response, body) {
        if (!error && response.statusCode == 200) {

            var universeResponse = JSON.parse(body);

            // Reset package singleton
            resetPackages();

            // Create temporary array
            var tempPackagesArray = [];

            // Iterate over the packages/package versions
            universeResponse.packages.forEach(function (packageObj) {

                // Initialize the package if it doesn't exist yet
                if (!packages.map.hasOwnProperty(packageObj.name)) {
                    packages.map[packageObj.name] = {
                        versions: {},
                        releaseVersions: {},
                        latest: null,
                        latestVersion: -1
                    }
                }

                var imagesObj = {};
                var screenshots = {};

                // Replace plain http image urls with https urls, to get rid of mixed-content warnings
                // Workaround for excluding erroneous packages"dynatrace", "sysdig-cloud" -> Remove once they're fixed
                if (packageObj.resource.images && Object.getOwnPropertyNames(packageObj.resource.images).length > 0 && ["dynatrace", "sysdig-cloud"].indexOf(packageObj.name) === -1) {
                    if (packageObj.resource.images.screenshots) {
                        screenshots = packageObj.resource.images.screenshots;
                        delete packageObj.resource.images.screenshots;
                    }
                    Object.getOwnPropertyNames(packageObj.resource.images).forEach(function (imageType) {
                        imagesObj[imageType] = packageObj.resource.images[imageType].replace(/^http:\/\//i, 'https://');
                    });
                } else {
                    // Assign placeholder images in the correct dimensions
                    imagesObj = {
                        "icon-small": "https://placehold.it/48x48&text=" + packageObj.name,
                        "icon-medium": "https://placehold.it/96x96&text=" + packageObj.name,
                        "icon-large": "https://placehold.it/256x256&text=" + packageObj.name
                    }
                }

                // Create smaller packageObj
                var smallPackageObj = {
                    id: packageObj.name + "-" + packageObj.releaseVersion,
                    name: packageObj.name,
                    description: packageObj.description,
                    tags: packageObj.tags,
                    version: packageObj.version,
                    releaseVersion: packageObj.releaseVersion,
                    packagingVersion: packageObj.packagingVersion,
                    minDcosReleaseVersion: packageObj.minDcosReleaseVersion,
                    maintainer: packageObj.maintainer || null,
                    website: packageObj.website || null,
                    scm: packageObj.scm || null,
                    isFramework: packageObj.framework || false,
                    preInstallNotes: packageObj.preInstallNotes || null,
                    postInstallNotes: packageObj.postInstallNotes || null,
                    postUninstallNotes: packageObj.postUninstallNotes || null,
                    licenses: packageObj.licenses || null,
                    images: imagesObj,
                    screenshots: screenshots || null,
                    hasExample : ((exampleCache[packageObj.name] && exampleCache[packageObj.name].hasOwnProperty("enabled")) ? exampleCache[packageObj.name].enabled : false)
                };

                // Create version map entry
                packages.map[packageObj.name].releaseVersions[packageObj.releaseVersion.toString()] = smallPackageObj;
                packages.map[packageObj.name].versions[packageObj.version.toString()] = smallPackageObj;

                // Check if it's the latest release version, if so sure the version number
                if (packages.map[packageObj.name].latestVersion < packageObj.releaseVersion) {
                    // Set the latestVersion
                    packages.map[packageObj.name].latestVersion = packageObj.releaseVersion;
                    // Set the packageObj as current latest
                    packages.map[packageObj.name].latest = smallPackageObj;
                }

            });

            // Get latest versions per package, and store them (for search)
            Object.getOwnPropertyNames(packages.map).forEach(function (packageName) {
                // Get current package
                var latestPackageVersion = packages.map[packageName].latest;
                // Add to tempPackagesArray
                tempPackagesArray.push(latestPackageVersion);
                // Index the current package
                packages.index.add({
                    name: latestPackageVersion.name,
                    description: latestPackageVersion.description,
                    tags: latestPackageVersion.tags,
                    releaseVersion: latestPackageVersion.releaseVersion
                });
            });

            // Store the array as pac
            packages.list = tempPackagesArray.sort(generateSortFn("name", false));

        }
    });
};

router.get("/search", function(req, res) {

    var searchString = req.query.q;

    var searchResult = packages.index.search(searchString);

    var resultArray = [];

    searchResult.forEach(function (searchResult) {
        resultArray.push(packages.map[searchResult.ref].latest);
    });

    res.json({ results: resultArray });

});

router.get("/packages", function(req, res) {

    res.json(packages.list);

});

router.get("/package/:packageName/docs", function(req, res) {

    if (packages.map[req.params.packageName]) {

        // Check if there there's already a renderedHtml property
        if (exampleCache.hasOwnProperty(req.params.packageName) && exampleCache[req.params.packageName].renderedHtml) {

            // Send renderedHtml
            res.send(exampleCache[req.params.packageName].renderedHtml);

        } else {
            res.status(404).json({ error: "No rendered example found for " + req.params.packageName });
        }

    } else {
        res.status(404).json({ error: "No matching package found!" });
    }

});

router.get("/package/:packageName/version/:packageVersion", function(req, res) {

    if (packages.map[req.params.packageName]) {

        if (req.params.packageVersion === "latest") {
            res.json(packages.map[req.params.packageName].latest)
        } else {
            if (packages.map[req.params.packageName].versions.hasOwnProperty(req.params.packageVersion)) {
                res.json(packages.map[req.params.packageName].versions[req.params.packageVersion])
            } else {
                res.status(404).json({ error: "No matching package version found!" });
            }
        }

    } else {
        res.status(404).json({ error: "No matching package found!" });
    }

});

router.get("/package/:packageName/releaseVersion/:packageReleaseVersion", function(req, res) {

    if (packages.map[req.params.packageName]) {

        if (req.params.packageReleaseVersion === "latest") {
            res.json(packages.map[req.params.packageName].latest)
        } else {
            if (packages.map[req.params.packageName].releaseVersions.hasOwnProperty(req.params.packageReleaseVersion)) {
                res.json(packages.map[req.params.packageName].releaseVersions[req.params.packageReleaseVersion])
            } else {
                res.status(404).json({ error: "No matching package version found!" });
            }
        }

    } else {
        res.status(404).json({ error: "No matching package found!" });
    }

});

router.get("/package/:packageName/versions", function(req, res) {

    if (packages.map[req.params.packageName]) {

        res.json({
            name: req.params.packageName,
            versions: Object.getOwnPropertyNames(packages.map[req.params.packageName].versions).sort()
        });

    } else {
        res.json({ error: "No matching package found!" });
    }

});

router.get("/package/:packageName/releaseVersions", function(req, res) {

    if (packages.map[req.params.packageName]) {

        res.json({
            name: req.params.packageName,
            releaseVersions: Object.getOwnPropertyNames(packages.map[req.params.packageName].releaseVersions).sort()
        });

    } else {
        res.json({ error: "No matching package found!" });
    }

});

module.exports = router;<commit_msg>Implemented changed dcos/example folder structure<commit_after>// Internal modules
var path = require("path");
var fs = require("fs");

// NPM modules
var request = require("request");
var lunr = require("lunr");
var rimraf = require("rimraf");
var mkdirp = require("mkdirp");
var router = require("express").Router();
var showdown  = require("showdown");
var converter = new showdown.Converter();
var fs2obj = require('fs2obj');

// Project modules
var config = require("../lib/config");

// Define base folder for dcos/examples repo
var dcosExamplesFolder = path.join(__dirname, "../", "dcos-examples");

// Clean dcos/examples repo folder
rimraf.sync(dcosExamplesFolder);

// Global git object
var git = {};

// Create cache object for the dcos/example packages
var exampleCache = {};

// Placeholder for the pull interval
var pullInterval = {};

// Placeholder for the repository interval
var repositoryInterval = {};

// Create clean folder for dcos/examples repo
mkdirp(dcosExamplesFolder, function (err) {
    if (err) {
        console.error(err);
        process.exit(1);
    } else {
        console.log("Created folder for repo dcos/examples");
        // Instantiate git
        git = require('simple-git')(dcosExamplesFolder);
        console.log("Instantiated git");
        // Clone the repo
        git.clone("https://github.com/dcos/examples.git", dcosExamplesFolder, {}, function (err, result) {
            if (err) {
                console.log(err);
                process.exit(1);
            } else {
                console.log("Successfully cloned dcos/example repository");
                // Fill the examplesCache with the rendered results of the markdown files
                fillExamples();
                console.log("Generated and cached HTML from the dcos/example repository");
                // Load the repository initially
                loadRepository();
                console.log("Initially loaded the DC/OS repository information");
                // Set the periodical "git pull" as interval, every 5 minutes
                pullInterval = setInterval(function () {
                    var startTime = new Date().getTime();
                    git.pull("origin", "master", function(err, result) {
                        var endTime = new Date().getTime();
                        if (err) {
                            console.log("Pull from origin/master failed in " + (endTime-startTime) + "ms");
                            console.log(err);
                            //process.exit(1);
                        } else {
                            console.log("Successfully pulled from origin/master in " + (endTime-startTime) + "ms");
                            // Fill the examplesCache with the rendered results of the markdown files
                            fillExamples();
                            console.log("Updated and cached HTML from the dcos/example repository");
                        }
                    });
                }, config.application.refresh.examples);
                console.log("Started dcos/example pull interval (" + (config.application.refresh.examples/1000) + " secs)!");
                // Set periodical for repository refresh as interval
                repositoryInterval = setInterval(loadRepository, config.application.refresh.universe);
                console.log("Started load repository interval (" + (config.application.refresh.universe/1000) + " secs)!");
            }
        });
    }
});

function fillExamples() {

    // Read the folder/file structure
    var folderStructure = fs2obj(dcosExamplesFolder + "/1.8");

    // RegExp for finding anchor links in the markdown files
    var relativeLinkRegExp = /\(#(.*?)\)/g; // Matches "(#my-test-link)" as "my-test-link"

    // Reset exampleCache
    exampleCache = {};

    // Iterate over items
    folderStructure.items.forEach(function (item) {
        // Use folders
        if (item.type === "folder") {
            var packageName = item.name.toLowerCase();
            // Set baseUrl
            var baseUrl = "https://raw.githubusercontent.com/dcos/examples/master/" + packageName + "/1.8/";
            // Read README.md contents
            var exampleContents = fs.readFileSync(dcosExamplesFolder + "/" + packageName + "/1.8/README.md", "utf8").toString();
            // Get relative links
            var relativeLinks = exampleContents.match(relativeLinkRegExp);
            // Check if anchor links found
            if (relativeLinks && relativeLinks.length > 0) {
                // Handle relative links -> Replace with full link including the anchor
                relativeLinks.forEach(function (relativeLink) {
                    var bareLink = relativeLink.replace("(", "").replace(")", "").replace(/-/g, "");
                    var replaceRelativeLink = "(/#/package/" + packageName + "/docs" + bareLink + ")";
                    exampleContents = exampleContents.replace(relativeLink, replaceRelativeLink)
                });
            }
            // Convert to HTML and replace image sources
            var htmlCode = converter.makeHtml(exampleContents).replace(/img\//g, baseUrl + "/img/"); // Replace relative URL with absolute URL

            // There is an example for this package
            exampleCache[packageName] = {
                renderedHtml: htmlCode,
                enabled: true
            };
        }
    });

}

// Sample call to DC/OS universe
// curl -X GET -L -H "user-agent: dcos/1.8" -H "accept: application/vnd.dcos.universe.repo+json;charset=utf-8;version=v3" "https://universe.mesosphere.com/repo"

// Set request options
var options = {
    "headers": {
        "user-agent": "dcos/1.8",
        "accept": "application/vnd.dcos.universe.repo+json;charset=utf-8;version=v3"
    },
    "uri": "https://universe.mesosphere.com/repo",
    "method": "GET"
};

// Create packages singleton
var packages = {
    index: lunr(function () {
        this.field("name");
        this.field("tags", {boost: 10});
        this.field("description");
        this.field("releaseVersion");
        this.ref("name")
    }),
    map: {},
    list: []
};

//
var resetPackages = function () {
    packages.index = lunr(function () {
        this.field("name");
        this.field("tags", {boost: 10});
        this.field("description");
        this.field("releaseVersion");
        this.ref("name")
    });
    // Reset other properties
    packages.list.length = 0;
    packages.map = {};
};

function generateSortFn(prop, reverse) {
    return function (a, b) {
        if (a[prop] < b[prop]) return reverse ? 1 : -1;
        if (a[prop] > b[prop]) return reverse ? -1 : 1;
        return 0;
    };
}

var loadRepository = function () {
    request(options, function (error, response, body) {
        if (!error && response.statusCode == 200) {

            var universeResponse = JSON.parse(body);

            // Reset package singleton
            resetPackages();

            // Create temporary array
            var tempPackagesArray = [];

            // Iterate over the packages/package versions
            universeResponse.packages.forEach(function (packageObj) {

                // Initialize the package if it doesn't exist yet
                if (!packages.map.hasOwnProperty(packageObj.name)) {
                    packages.map[packageObj.name] = {
                        versions: {},
                        releaseVersions: {},
                        latest: null,
                        latestVersion: -1
                    }
                }

                var imagesObj = {};
                var screenshots = {};

                // Replace plain http image urls with https urls, to get rid of mixed-content warnings
                // Workaround for excluding erroneous packages"dynatrace", "sysdig-cloud" -> Remove once they're fixed
                if (packageObj.resource && packageObj.resource.images && Object.getOwnPropertyNames(packageObj.resource.images).length > 0 && ["dynatrace", "sysdig-cloud"].indexOf(packageObj.name) === -1) {
                    if (packageObj.resource.images.screenshots) {
                        screenshots = packageObj.resource.images.screenshots;
                        delete packageObj.resource.images.screenshots;
                    }
                    Object.getOwnPropertyNames(packageObj.resource.images).forEach(function (imageType) {
                        imagesObj[imageType] = packageObj.resource.images[imageType].replace(/^http:\/\//i, 'https://');
                    });
                } else {
                    // Assign placeholder images in the correct dimensions
                    imagesObj = {
                        "icon-small": "https://placehold.it/48x48&text=" + packageObj.name,
                        "icon-medium": "https://placehold.it/96x96&text=" + packageObj.name,
                        "icon-large": "https://placehold.it/256x256&text=" + packageObj.name
                    }
                }

                // Create smaller packageObj
                var smallPackageObj = {
                    id: packageObj.name + "-" + packageObj.releaseVersion,
                    name: packageObj.name,
                    description: packageObj.description,
                    tags: packageObj.tags,
                    version: packageObj.version,
                    releaseVersion: packageObj.releaseVersion,
                    packagingVersion: packageObj.packagingVersion,
                    minDcosReleaseVersion: packageObj.minDcosReleaseVersion,
                    maintainer: packageObj.maintainer || null,
                    website: packageObj.website || null,
                    scm: packageObj.scm || null,
                    isFramework: packageObj.framework || false,
                    preInstallNotes: packageObj.preInstallNotes || null,
                    postInstallNotes: packageObj.postInstallNotes || null,
                    postUninstallNotes: packageObj.postUninstallNotes || null,
                    licenses: packageObj.licenses || null,
                    images: imagesObj,
                    screenshots: screenshots || null,
                    hasExample : ((exampleCache[packageObj.name] && exampleCache[packageObj.name].hasOwnProperty("enabled")) ? exampleCache[packageObj.name].enabled : false)
                };

                // Create version map entry
                packages.map[packageObj.name].releaseVersions[packageObj.releaseVersion.toString()] = smallPackageObj;
                packages.map[packageObj.name].versions[packageObj.version.toString()] = smallPackageObj;

                // Check if it's the latest release version, if so sure the version number
                if (packages.map[packageObj.name].latestVersion < packageObj.releaseVersion) {
                    // Set the latestVersion
                    packages.map[packageObj.name].latestVersion = packageObj.releaseVersion;
                    // Set the packageObj as current latest
                    packages.map[packageObj.name].latest = smallPackageObj;
                }

            });

            // Get latest versions per package, and store them (for search)
            Object.getOwnPropertyNames(packages.map).forEach(function (packageName) {
                // Get current package
                var latestPackageVersion = packages.map[packageName].latest;
                // Add to tempPackagesArray
                tempPackagesArray.push(latestPackageVersion);
                // Index the current package
                packages.index.add({
                    name: latestPackageVersion.name,
                    description: latestPackageVersion.description,
                    tags: latestPackageVersion.tags,
                    releaseVersion: latestPackageVersion.releaseVersion
                });
            });

            // Store the array as pac
            packages.list = tempPackagesArray.sort(generateSortFn("name", false));

        }
    });
};

router.get("/search", function(req, res) {

    var searchString = req.query.q;

    var searchResult = packages.index.search(searchString);

    var resultArray = [];

    searchResult.forEach(function (searchResult) {
        resultArray.push(packages.map[searchResult.ref].latest);
    });

    res.json({ results: resultArray });

});

router.get("/packages", function(req, res) {

    res.json(packages.list);

});

router.get("/package/:packageName/docs", function(req, res) {

    if (packages.map[req.params.packageName]) {

        // Check if there there's already a renderedHtml property
        if (exampleCache.hasOwnProperty(req.params.packageName) && exampleCache[req.params.packageName].renderedHtml) {

            // Send renderedHtml
            res.send(exampleCache[req.params.packageName].renderedHtml);

        } else {
            res.status(404).json({ error: "No rendered example found for " + req.params.packageName });
        }

    } else {
        res.status(404).json({ error: "No matching package found!" });
    }

});

router.get("/package/:packageName/version/:packageVersion", function(req, res) {

    if (packages.map[req.params.packageName]) {

        if (req.params.packageVersion === "latest") {
            res.json(packages.map[req.params.packageName].latest)
        } else {
            if (packages.map[req.params.packageName].versions.hasOwnProperty(req.params.packageVersion)) {
                res.json(packages.map[req.params.packageName].versions[req.params.packageVersion])
            } else {
                res.status(404).json({ error: "No matching package version found!" });
            }
        }

    } else {
        res.status(404).json({ error: "No matching package found!" });
    }

});

router.get("/package/:packageName/releaseVersion/:packageReleaseVersion", function(req, res) {

    if (packages.map[req.params.packageName]) {

        if (req.params.packageReleaseVersion === "latest") {
            res.json(packages.map[req.params.packageName].latest)
        } else {
            if (packages.map[req.params.packageName].releaseVersions.hasOwnProperty(req.params.packageReleaseVersion)) {
                res.json(packages.map[req.params.packageName].releaseVersions[req.params.packageReleaseVersion])
            } else {
                res.status(404).json({ error: "No matching package version found!" });
            }
        }

    } else {
        res.status(404).json({ error: "No matching package found!" });
    }

});

router.get("/package/:packageName/versions", function(req, res) {

    if (packages.map[req.params.packageName]) {

        res.json({
            name: req.params.packageName,
            versions: Object.getOwnPropertyNames(packages.map[req.params.packageName].versions).sort()
        });

    } else {
        res.json({ error: "No matching package found!" });
    }

});

router.get("/package/:packageName/releaseVersions", function(req, res) {

    if (packages.map[req.params.packageName]) {

        res.json({
            name: req.params.packageName,
            releaseVersions: Object.getOwnPropertyNames(packages.map[req.params.packageName].releaseVersions).sort()
        });

    } else {
        res.json({ error: "No matching package found!" });
    }

});

module.exports = router;<|endoftext|>


<commit_before>/**
 *
 */

var boardSize = 5
var pad = 10
var rx = 25
var ry = (2 / 3) * rx * Math.sqrt(3)
var rAtom = 7
var fields = []
var atoms = []
var atomShapes
var player1 = {
	color : "yellow"
}
var player2 = {
	color : "blue"
}
var players = [ player1, player2 ]
var iCurrentPlayer = 0
var waitingToMove = false

function hexagonPoints(cxy) {
	var xyStrings = []
	for (var ic = 0; ic < 6; ic++) {
		var angle = (2 * ic + 1) * Math.PI / 6
		xyStrings.push((cxy.x + ry * Math.cos(angle)) + ","
				+ (cxy.y + ry * Math.sin(angle)))
	}
	return xyStrings.join(" ")
}

function isInBoard(ix, iy) {
	return ix + iy >= boardSize && ix + iy <= 3 * boardSize
}

function areNeighbours(ix1, iy1, ix2, iy2) {
	var ixd = ix1 - ix2
	if (ixd < -1 || ixd > 1) {
		return false
	}
	var iyd = iy1 - iy2
	if (iyd < -1 || iyd > 1) {
		return false
	}
	if (ixd == 0 && iyd == 0) {
		return false
	}
	var d = ixd + iyd
	return d >= -1 && d <= 1
}

function initFields() {
	for (var ix = 0; ix < 2 * boardSize + 1; ix++) {
		for (var iy = 0; iy < 2 * boardSize + 1; iy++) {
			if (isInBoard(ix, iy)) {
				fields.push({
					x : pad + rx + (2 * ix - boardSize) * rx + iy * rx,
					y : pad + rx + iy * 1.5 * ry,
					ix : ix,
					iy : iy,
					iField : fields.length,
					iNeighbours : [],
					atoms : [],
					fill : "#aaaaaa",
					hasOwner : false
				})
			}
		}
	}
	for (var i1 = 0; i1 < fields.length; i1++) {
		var field1 = fields[i1]
		for (var i2 = 0; i2 < fields.length; i2++) {
			var field2 = fields[i2]
			if (areNeighbours(field1.ix, field1.iy, field2.ix, field2.iy)) {
				field1.iNeighbours.push(i2)
			}
		}
	}
	// alert(fields.toSource())
	var fieldShapes = d3.select("svg").attr("width",
			2 * pad + rx * (4 * boardSize + 2)).attr("height",
			(2 * pad + rx + ry * (3 * boardSize + 1.5)))
}

function onFieldClick(event) {
	if (waitingToMove) {
		waitingToMove = false
		var mousePos = d3.mouse(this)
		var iField = parseInt(this.getAttribute("iField"))
		var field = fields[iField]
		var currentPlayer = players[iCurrentPlayer]
		if (!field.hasOwner || (field.owner == currentPlayer)) {
			field.hasOwner = true
			field.owner = currentPlayer
			var atom = {}
			atom.x = mousePos[0]
			atom.y = mousePos[1]
			atom.iField = iField
			atom.owner = currentPlayer
			atoms.push(atom)
			field.atoms.push(atom)
			refreshAtomShapes()
			drawAtoms()
			packAtomsOnField(field)
			drawAtoms()
			redistributeAtoms()
			iCurrentPlayer = (iCurrentPlayer + 1) % players.length
		}
	}
	waitingToMove = true
}

function drawFields() {
	var fieldShapes = d3.select("svg").selectAll("polygon").data(fields)
	fieldShapes.enter().append("polygon").on("click", onFieldClick)
			.transition().attr("points", function(d) {
				return hexagonPoints(d)
			}).attr("stroke", "#ffffff").attr("stroke-width", "3").attr("fill",
					function(d) {
						return d.fill
					}).attr("iField", function(d) {
				return d.iField
			})
	fieldShapes = fieldShapes.on("click", onFieldClick).transition().attr(
			"points", function(d) {
				return hexagonPoints(d)
			}).attr("stroke", "#ffffff").attr("stroke-width", "3").attr("fill",
			function(d) {
				return d.fill
			}).attr("iField", function(d) {
		return d.iField
	})

}

function refreshAtomShapes() {
	atomShapes = d3.select("svg").selectAll("circle").data(atoms)
	atomShapes.enter().append("circle")
}

function drawAtoms() {
	atomShapes = atomShapes.transition().attr("cx", function(d) {
		return d.x
	}).attr("cy", function(d) {
		return d.y
	}).attr("r", rAtom).attr("stroke", "#000000").attr("stroke-width", 1).attr(
			"fill", function(d) {
				return d.owner.color
			})
}

function drawNewBoard() {
	initFields()
	drawFields()
	refreshAtomShapes()
	drawAtoms()
	waitingForMove = true
}

function shiftAtomToR(atom, anchor, r, shiftIfCloser) {
	var dx = atom.x - anchor.x
	var dy = atom.y - anchor.y
	var distSq = dx * dx + dy * dy
	if (shiftIfCloser && distSq < r * r) {
		if (distSq == 0) {
			atom.x = atom.x + 1
			dx = atom.x - anchor.x
			dy = atom.y - anchor.y
			distSq = dx * dx + dy * dy
		}
		var dist = Math.sqrt(distSq)
		atom.x = anchor.x + (r / dist) * (atom.x - anchor.x)
		atom.y = anchor.y + (r / dist) * (atom.y - anchor.y)
	} else if (!shiftIfCloser && distSq > r * r) {
		var dist = Math.sqrt(distSq)
		atom.x = anchor.x + (r / dist) * (atom.x - anchor.x)
		atom.y = anchor.y + (r / dist) * (atom.y - anchor.y)
	}
}

function packAtomsOnField(field) {
	var atoms = field.atoms
	var dAtom = 2 * rAtom + 2
	var rField = rx - rAtom - 3
	if (atoms.length > 0) {
		for (var i = 0; i < 3; i++) {
			for (var i1 = 0; i1 < atoms.length; i1++) {
				var atom1 = atoms[i1]
				for (var i2 = 0; i2 < atoms.length; i2++) {
					if (i1 != i2) {
						var atom2 = atoms[i2]
						shiftAtomToR(atom1, atom2, dAtom, true)
					}
				}
				shiftAtomToR(atom1, field, rField, false)
			}
		}
	}
}

function redistributeAtoms() {
	var durationPack = 100
	var durationSpread = 1000
	var delay = 200
	var currentPlayer = players[iCurrentPlayer]
	var keepGoing = true
	var nDistributions = 0
	var nDistributionsMax = 700
	while (keepGoing && nDistributions <= nDistributionsMax) {
		nDistributions++
		keepGoing = false
		for (var iField = 0; iField < fields.length; iField++) {
			var field = fields[iField]
			if (field.atoms.length >= field.iNeighbours.length) {
				for (var iFieldAtom = 0; iFieldAtom < field.iNeighbours.length; iFieldAtom++) {
					var iNeighbour = field.iNeighbours[iFieldAtom]
					field.atoms[iFieldAtom].iField = iNeighbour
					var neighbour = fields[iNeighbour]
					neighbour.hasOwner = true
					neighbour.owner = currentPlayer
				}
				keepGoing = true
			}
			field.atoms = []
		}
		for (var iAtom = 0; iAtom < atoms.length; iAtom++) {
			var atom = atoms[iAtom]
			var field = fields[atom.iField]
			field.atoms.push(atom)
		}
		for (var iField = 0; iField < fields.length; iField++) {
			var field = fields[iField]
			if (field.atoms.length > 0) {
				for (var iFieldAtom = 0; iFieldAtom < field.atoms.length; iFieldAtom++) {
					var atom = field.atoms[iFieldAtom]
					atom.owner = field.owner
				}
				packAtomsOnField(field)
			} else {
				field.hasOwner = false
			}
		}
		drawAtoms()
	}
}
<commit_msg>Disable next move until previous move is completed, including spreats.<commit_after>/**
 *
 */

var boardSize = 5
var pad = 10
var rx = 25
var ry = (2 / 3) * rx * Math.sqrt(3)
var rAtom = 7
var fields = []
var atoms = []
var atomShapes
var player1 = {
	color : "yellow"
}
var player2 = {
	color : "blue"
}
var players = [ player1, player2 ]
var iCurrentPlayer = 0
var waitingToMove = true

function hexagonPoints(cxy) {
	var xyStrings = []
	for (var ic = 0; ic < 6; ic++) {
		var angle = (2 * ic + 1) * Math.PI / 6
		xyStrings.push((cxy.x + ry * Math.cos(angle)) + ","
				+ (cxy.y + ry * Math.sin(angle)))
	}
	return xyStrings.join(" ")
}

function isInBoard(ix, iy) {
	return ix + iy >= boardSize && ix + iy <= 3 * boardSize
}

function areNeighbours(ix1, iy1, ix2, iy2) {
	var ixd = ix1 - ix2
	if (ixd < -1 || ixd > 1) {
		return false
	}
	var iyd = iy1 - iy2
	if (iyd < -1 || iyd > 1) {
		return false
	}
	if (ixd == 0 && iyd == 0) {
		return false
	}
	var d = ixd + iyd
	return d >= -1 && d <= 1
}

function initFields() {
	for (var ix = 0; ix < 2 * boardSize + 1; ix++) {
		for (var iy = 0; iy < 2 * boardSize + 1; iy++) {
			if (isInBoard(ix, iy)) {
				fields.push({
					x : pad + rx + (2 * ix - boardSize) * rx + iy * rx,
					y : pad + rx + iy * 1.5 * ry,
					ix : ix,
					iy : iy,
					iField : fields.length,
					iNeighbours : [],
					atoms : [],
					fill : "#aaaaaa",
					hasOwner : false
				})
			}
		}
	}
	for (var i1 = 0; i1 < fields.length; i1++) {
		var field1 = fields[i1]
		for (var i2 = 0; i2 < fields.length; i2++) {
			var field2 = fields[i2]
			if (areNeighbours(field1.ix, field1.iy, field2.ix, field2.iy)) {
				field1.iNeighbours.push(i2)
			}
		}
	}
	// alert(fields.toSource())
	var fieldShapes = d3.select("svg").attr("width",
			2 * pad + rx * (4 * boardSize + 2)).attr("height",
			(2 * pad + rx + ry * (3 * boardSize + 1.5)))
}

function onFieldClick(event) {
	if (waitingToMove) {
		waitingToMove = false
		var mousePos = d3.mouse(this)
		var iField = parseInt(this.getAttribute("iField"))
		var field = fields[iField]
		var currentPlayer = players[iCurrentPlayer]
		if (!field.hasOwner || (field.owner == currentPlayer)) {
			field.hasOwner = true
			field.owner = currentPlayer
			var atom = {}
			atom.x = mousePos[0]
			atom.y = mousePos[1]
			atom.iField = iField
			atom.owner = currentPlayer
			atoms.push(atom)
			field.atoms.push(atom)
			refreshAtomShapes()
			drawAtoms()
			packAtomsOnField(field)
			drawAtoms()
			redistributeAtoms()
			iCurrentPlayer = (iCurrentPlayer + 1) % players.length
		}
		if (atomShapes.length > 0) {
			atomShapes = atomShapes.each("end", function() {
				waitingToMove = true
			})
		} else {
			waitingToMove = true
		}
	}
}

function drawFields() {
	var fieldShapes = d3.select("svg").selectAll("polygon").data(fields)
	fieldShapes.enter().append("polygon").on("click", onFieldClick)
			.transition().attr("points", function(d) {
				return hexagonPoints(d)
			}).attr("stroke", "#ffffff").attr("stroke-width", "3").attr("fill",
					function(d) {
						return d.fill
					}).attr("iField", function(d) {
				return d.iField
			})
	fieldShapes = fieldShapes.on("click", onFieldClick).transition().attr(
			"points", function(d) {
				return hexagonPoints(d)
			}).attr("stroke", "#ffffff").attr("stroke-width", "3").attr("fill",
			function(d) {
				return d.fill
			}).attr("iField", function(d) {
		return d.iField
	})

}

function refreshAtomShapes() {
	atomShapes = d3.select("svg").selectAll("circle").data(atoms)
	atomShapes.enter().append("circle")
}

function drawAtoms() {
	atomShapes = atomShapes.transition().attr("cx", function(d) {
		return d.x
	}).attr("cy", function(d) {
		return d.y
	}).attr("r", rAtom).attr("stroke", "#000000").attr("stroke-width", 1).attr(
			"fill", function(d) {
				return d.owner.color
			})
}

function drawNewBoard() {
	initFields()
	drawFields()
	refreshAtomShapes()
	drawAtoms()
	waitingForMove = true
}

function shiftAtomToR(atom, anchor, r, shiftIfCloser) {
	var dx = atom.x - anchor.x
	var dy = atom.y - anchor.y
	var distSq = dx * dx + dy * dy
	if (shiftIfCloser && distSq < r * r) {
		if (distSq == 0) {
			atom.x = atom.x + 1
			dx = atom.x - anchor.x
			dy = atom.y - anchor.y
			distSq = dx * dx + dy * dy
		}
		var dist = Math.sqrt(distSq)
		atom.x = anchor.x + (r / dist) * (atom.x - anchor.x)
		atom.y = anchor.y + (r / dist) * (atom.y - anchor.y)
	} else if (!shiftIfCloser && distSq > r * r) {
		var dist = Math.sqrt(distSq)
		atom.x = anchor.x + (r / dist) * (atom.x - anchor.x)
		atom.y = anchor.y + (r / dist) * (atom.y - anchor.y)
	}
}

function packAtomsOnField(field) {
	var atoms = field.atoms
	var dAtom = 2 * rAtom + 2
	var rField = rx - rAtom - 3
	if (atoms.length > 0) {
		for (var i = 0; i < 3; i++) {
			for (var i1 = 0; i1 < atoms.length; i1++) {
				var atom1 = atoms[i1]
				for (var i2 = 0; i2 < atoms.length; i2++) {
					if (i1 != i2) {
						var atom2 = atoms[i2]
						shiftAtomToR(atom1, atom2, dAtom, true)
					}
				}
				shiftAtomToR(atom1, field, rField, false)
			}
		}
	}
}

function redistributeAtoms() {
	var durationPack = 100
	var durationSpread = 1000
	var delay = 200
	var currentPlayer = players[iCurrentPlayer]
	var keepGoing = true
	var nDistributions = 0
	var nDistributionsMax = 700
	while (keepGoing && nDistributions <= nDistributionsMax) {
		nDistributions++
		keepGoing = false
		for (var iField = 0; iField < fields.length; iField++) {
			var field = fields[iField]
			if (field.atoms.length >= field.iNeighbours.length) {
				for (var iFieldAtom = 0; iFieldAtom < field.iNeighbours.length; iFieldAtom++) {
					var iNeighbour = field.iNeighbours[iFieldAtom]
					field.atoms[iFieldAtom].iField = iNeighbour
					var neighbour = fields[iNeighbour]
					neighbour.hasOwner = true
					neighbour.owner = currentPlayer
				}
				keepGoing = true
			}
			field.atoms = []
		}
		for (var iAtom = 0; iAtom < atoms.length; iAtom++) {
			var atom = atoms[iAtom]
			var field = fields[atom.iField]
			field.atoms.push(atom)
		}
		for (var iField = 0; iField < fields.length; iField++) {
			var field = fields[iField]
			if (field.atoms.length > 0) {
				for (var iFieldAtom = 0; iFieldAtom < field.atoms.length; iFieldAtom++) {
					var atom = field.atoms[iFieldAtom]
					atom.owner = field.owner
				}
				packAtomsOnField(field)
			} else {
				field.hasOwner = false
			}
		}
		drawAtoms()
	}
}
<|endoftext|>


<commit_before>define([
  'jquery',
  'underscore',
  'backbone',
  'views/KeyView',
  'router'
], function ($, _, Backbone, KeyView, Router) {

  var KeyboardView = Backbone.View.extend({
    el: $('#keyboard'),

    initialize: function (options) {
      var self = this;
      this.router = options.router;

      $('#keyboard > .key').each(function (note) {
        new KeyView({
          el: $('#' + note)[0],
          model: self.model
        });
      });

      this.listenTo(this.model, 'change', this.render);
      this.listenTo(this.model, 'change', this.updateUrl);
    },

    render: function () {
      $('.selected').each(function (index, note) {
        //$(note).removeClass('selected');
        var element = $(note);
        var currentClass = element.attr('class');
        element.attr('class', currentClass.split(' selected')[0]);
      });

      var notes = this.model.get('notes');

      notes.forEach(function (note) {
        //$('#' + note).addClass('selected');
        var element = $('#' + note);
        var currentClass = element.attr('class');
        element.attr('class', currentClass + ' selected');
      });

      return this;
    },

    updateUrl: function () {
      this.router.navigate(this.model.url());
    }
  });

  return KeyboardView;
});<commit_msg>Extract methods in keyboard view<commit_after>define([
  'jquery',
  'underscore',
  'backbone',
  'views/KeyView',
  'router'
], function ($, _, Backbone, KeyView, Router) {

  var KeyboardView = Backbone.View.extend({
    el: $('#keyboard'),

    initialize: function (options) {
      var self = this;
      this.router = options.router;

      $('#keyboard > .key').each(function (note) {
        new KeyView({
          el: $('#' + note)[0],
          model: self.model
        });
      });

      this.listenTo(this.model, 'change', this.render);
      this.listenTo(this.model, 'change', this.updateUrl);
    },

    render: function () {
      var self = this;

      $('.selected').each(function (index, note) {
        this.removeClass($(note), 'selected');
      });

      var notes = this.model.get('notes');

      notes.forEach(function (note) {
        self.addClass($('#' + note), 'selected');
      });

      return this;
    },

    removeClass: function (element, className) {
      var currentClass = element.attr('class');
      element.attr('class', currentClass.split(className)[0]);
    },

    addClass: function (element, className) {
      var currentClass = element.attr('class');
      element.attr('class', currentClass + ' ' + className);
    },

    updateUrl: function () {
      this.router.navigate(this.model.url());
    }
  });

  return KeyboardView;
});<|endoftext|>


<commit_before>(function(utils, undefined){

  var activated = false;

  // Have the verify function called when the hash changes.
  // We need the opportunity to build the search widget
  // later if we don't start on the ancestor view
  $(document).ready(function(){
    window.onhashchange = verify;

    verify()
  });

  // Builds the search widget if we're on the ancestor view
  function verify() {
    if ( utils.getHashParts()['view'] == 'ancestor' ) {
      try {
        setup();
      } catch(e) {
        utils.reportError(e, window.location.href);
      }
    }
  }

  // Remove the widget if we navigate away from an
  // ancestor view on a hashchange (no page reload)
  function teardown() {
    activated = false;
    window.onhashchange = verify;
    chrome.extension.sendRequest({
      'type': 'hide'
    });
  }

  // Builds the search widget on the person page
  function setup() {
    activated = true;

    // Bind to hashchange event
    window.onhashchange = processPersonHash;

    // Initial setup
    processPersonHash();
  }

  // Called when the person page loads and when the hash changes on the person page
  function processPersonHash() {

    // Remove previous search links and show the ajax loader
    var hashParts = utils.getHashParts();

    if( hashParts['view'] == 'ancestor' ) {

      // Get personId and spouseId
      var personId = hashParts['person'];
      var spouseId = hashParts['spouse'];

      // If we have a personId and we are in the ancestor view, build the urls
      if(personId) {

        // Get person info; process it when both ajax calls return
        $.when(getPersonSummary(personId), getRelationships(personId,spouseId)).done(function(summary, relationships) {

          // Get actual return data
          summary = summary[0];
          relationships = relationships[0];
          var personData = normalizeData(summary, relationships);

          chrome.extension.sendRequest({
            'type': 'person_info',
            'data': personData
          });
        });
      }
    }

    // If we're no longer viewing an ancestor, teardown the widget
    else {
      teardown();
    }
  }

  function normalizeData(summary, relationships) {
    var gender = summary.data.gender,
        fatherName = ['',''],
        motherName = ['',''],
        spouseName = ['',''];

    // Process parents if there is a relationship
    if( relationships.data.parents.length ) {
      var fatherName = utils.splitName(relationships.data.parents[0].husband.name);
      var motherName = utils.splitName(relationships.data.parents[0].wife.name);
    }

    // Process spouse if there is a spouse relationship
    if(relationships.data.spouses.length) {
      if(gender == 'MALE' && relationships.data.spouses[0].wife) {
        spouseName = utils.splitName(relationships.data.spouses[0].wife.name);
      } else if(gender == 'FEMALE' && relationships.data.spouses[0].husband) {
        spouseName = utils.splitName(relationships.data.spouses[0].husband.name);
      }
    }

    var data = {
      'givenName': getSummaryInfo(summary, ['data', 'nameConclusion', 'details', 'nameForms', 0, 'givenPart']),
      'familyName': getSummaryInfo(summary, ['data', 'nameConclusion', 'details', 'nameForms', 0, 'familyPart']),
      'birthPlace': getSummaryInfo(summary, ['data', 'birthConclusion', 'details', 'place', 'normalizedText']),
      'birthDate': getSummaryInfo(summary, ['data', 'birthConclusion', 'details', 'date', 'normalizedText']),
      'deathPlace': getSummaryInfo(summary, ['data', 'deathConclusion', 'details', 'place', 'normalizedText']),
      'deathDate': getSummaryInfo(summary, ['data', 'deathConclusion', 'details', 'date', 'normalizedText']),
      'fatherGivenName': fatherName[0],
      'fatherFamilyName': fatherName[1],
      'motherGivenName': motherName[0],
      'motherFamilyName': motherName[1],
      'spouseGivenName': spouseName[0],
      'spouseFamilyName': spouseName[1]
    };

    if(relationships.data.spouses[0].event) {
      data.marriageDate = relationships.data.spouses[0].event.standardDate;
      data.marriagePlace = relationships.data.spouses[0].event.standardPlace;
    }

    return data;
  }

  // Iterate over a list of attributes which define the path
  // through an object to a desired piece of data. Stop when
  // we reach the last attribute (and return its value)
  // or when we find something that is undefined (return undefined)
  function getSummaryInfo(summary, attributes) {
    var current = summary;
    for(var i = 0; i < attributes.length; i++) {
      current = current[attributes[i]];
      if( current == undefined ) {
        return undefined;
      }
    }
    return current;
  }

  // Makes an ajax call to retrieve the persons summary data and returns a promise
  function getPersonSummary(personId) {
    return $.getJSON('https://familysearch.org/tree-data/person/'+personId+'/summary');
  }

  // Makes an ajax call to retrieve relationship info and returns a promise
  function getRelationships(personId, spouseId) {
    var url = 'https://familysearch.org/tree-data/family-members/person/'+personId;
    if(spouseId)
      url += '?spouseId='+spouseId;
    return $.getJSON(url);
  }
}(utils));<commit_msg>Fix FS bug; closes #42<commit_after>(function(utils, undefined){

  $(document).ready(function(){
    window.onhashchange = processHash;

    processHash()
  });

  // Called when the hash changes
  function processHash() {

    // Always hide the page action icon when the hash change
    // If we navigated to a new person we need time to load their data
    chrome.extension.sendRequest({
      'type': 'hide'
    });

    // Remove previous search links and show the ajax loader
    var hashParts = utils.getHashParts();

    if( hashParts['view'] == 'ancestor' ) {

      // Get personId and spouseId
      var personId = hashParts['person'];
      var spouseId = hashParts['spouse'];

      // If we have a personId and we are in the ancestor view, build the urls
      if(personId) {

        // Get person info; process it when both ajax calls return
        $.when(getPersonSummary(personId), getRelationships(personId,spouseId)).done(function(summary, relationships) {

          try {
            // Get actual return data
            summary = summary[0];
            relationships = relationships[0];
            var personData = normalizeData(summary, relationships);

            chrome.extension.sendRequest({
              'type': 'person_info',
              'data': personData
            });
          } catch(e) {
            utils.reportError(e, window.location.href);
          }
        });
      }
    }
  }

  function normalizeData(summary, relationships) {
    var gender = summary.data.gender,
        fatherName = ['',''],
        motherName = ['',''],
        spouseName = ['',''];

    // Process parents if there is a relationship
    if( relationships.data.parents.length ) {
      var fatherName = utils.splitName(relationships.data.parents[0].husband.name);
      var motherName = utils.splitName(relationships.data.parents[0].wife.name);
    }

    // Process spouse if there is a spouse relationship
    if(relationships.data.spouses.length) {
      if(gender == 'MALE' && relationships.data.spouses[0].wife) {
        spouseName = utils.splitName(relationships.data.spouses[0].wife.name);
      } else if(gender == 'FEMALE' && relationships.data.spouses[0].husband) {
        spouseName = utils.splitName(relationships.data.spouses[0].husband.name);
      }
    }

    var data = {
      'givenName': getSummaryInfo(summary, ['data', 'nameConclusion', 'details', 'nameForms', 0, 'givenPart']),
      'familyName': getSummaryInfo(summary, ['data', 'nameConclusion', 'details', 'nameForms', 0, 'familyPart']),
      'birthPlace': getSummaryInfo(summary, ['data', 'birthConclusion', 'details', 'place', 'normalizedText']),
      'birthDate': getSummaryInfo(summary, ['data', 'birthConclusion', 'details', 'date', 'normalizedText']),
      'deathPlace': getSummaryInfo(summary, ['data', 'deathConclusion', 'details', 'place', 'normalizedText']),
      'deathDate': getSummaryInfo(summary, ['data', 'deathConclusion', 'details', 'date', 'normalizedText']),
      'fatherGivenName': fatherName[0],
      'fatherFamilyName': fatherName[1],
      'motherGivenName': motherName[0],
      'motherFamilyName': motherName[1],
      'spouseGivenName': spouseName[0],
      'spouseFamilyName': spouseName[1]
    };

    if(relationships.data.spouses[0].event) {
      data.marriageDate = relationships.data.spouses[0].event.standardDate;
      data.marriagePlace = relationships.data.spouses[0].event.standardPlace;
    }

    return data;
  }

  // Iterate over a list of attributes which define the path
  // through an object to a desired piece of data. Stop when
  // we reach the last attribute (and return its value)
  // or when we find something that is undefined (return undefined)
  function getSummaryInfo(summary, attributes) {
    var current = summary;
    for(var i = 0; i < attributes.length; i++) {
      current = current[attributes[i]];
      if( current == undefined ) {
        return undefined;
      }
    }
    return current;
  }

  // Makes an ajax call to retrieve the persons summary data and returns a promise
  function getPersonSummary(personId) {
    return $.getJSON('https://familysearch.org/tree-data/person/'+personId+'/summary');
  }

  // Makes an ajax call to retrieve relationship info and returns a promise
  function getRelationships(personId, spouseId) {
    var url = 'https://familysearch.org/tree-data/family-members/person/'+personId;
    if(spouseId)
      url += '?spouseId='+spouseId;
    return $.getJSON(url);
  }
}(utils));<|endoftext|>


<commit_before>var express = require('express');
var _ = require('lodash');

var router = express.Router();

router.data = {
  users: []
};

router.get('/users', function(req, res, next) {
  res.send(router.data.users);
});

router.get('/users/:id', function(req, res, next) {
  var cond = {id: req.params.id};
  var user = _.find(router.data.users, cond);
  if (!user) {
    res.sendStatus(404);
  } else {
    res.send(user);
  }
});

router.post('/users', function(req, res, next) {
  var user = req.body;
  var cond = {id: user.id};
  var foundUser = _.find(router.data.users, cond);
  if (foundUser) {
    res.sendStatus(400);
  } else {
    router.data.users.push(user);
    res.sendStatus(200);
  }
});

module.exports = router;
<commit_msg>POST for existant user: green<commit_after>var express = require('express');
var _ = require('lodash');

var router = express.Router();

router.data = {
  users: []
};

router.get('/users', function(req, res, next) {
  res.send(router.data.users);
});

router.get('/users/:id', function(req, res, next) {
  var cond = {id: req.params.id};
  var user = _.find(router.data.users, cond);
  if (!user) {
    res.sendStatus(404);
  } else {
    res.send(user);
  }
});

router.post('/users', function(req, res, next) {
  var user = req.body;
  var cond = {id: user.id};
  var found = _.find(router.data.users, cond);
  if (found) {
    res.sendStatus(400);
  } else {
    router.data.users.push(user);
    res.sendStatus(200);
  }
});

module.exports = router;
<|endoftext|>


<commit_before>$(document).ready(function(){
    $(".thread-tree-link").click(function(){
	$(this).parent().children(".intree-post").toggle();
	return false;
    });

    $(".expand-all a").click(function(){
	$(".intree-post").show();
	$(".expand-all").hide();
	$(".collapse-all").show();
	return false;
    });

    $(".collapse-all a").click(function(){
	$(".intree-post").hide();
	$(".collapse-all").hide();
	$(".expand-all").show();
	return false;
    });

    $(".collapse-all").show();

    $("a.delete-post").click(function(){
	var post = $(this).parents(".post");

	jQuery.ajax({
	    url: $(this).attr("href"),
	    method: "DELETE",
	    success: function(){
		post.remove();
	    },
	    error: function() {
		alert("Deletion failed.");
	    }
	});

	return false;
    });
});
<commit_msg>Toggle an entire thread subtree.<commit_after>$(document).ready(function(){
    $(".thread-tree-link").click(function(){
	$(this).parent().find(".intree-post").toggle();
	return false;
    });

    $(".expand-all a").click(function(){
	$(".intree-post").show();
	$(".expand-all").hide();
	$(".collapse-all").show();
	return false;
    });

    $(".collapse-all a").click(function(){
	$(".intree-post").hide();
	$(".collapse-all").hide();
	$(".expand-all").show();
	return false;
    });

    $(".collapse-all").show();

    $("a.delete-post").click(function(){
	var post = $(this).parents(".post");

	jQuery.ajax({
	    url: $(this).attr("href"),
	    method: "DELETE",
	    success: function(){
		post.remove();
	    },
	    error: function() {
		alert("Deletion failed.");
	    }
	});

	return false;
    });
});
<|endoftext|>


<commit_before>// routes/web.js
//
// Spurtin' out pumpy goodness all over your browser window
//
// Copyright 2011-2012, StatusNet Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var databank = require("databank"),
    url = require("url"),
    path = require("path"),
    fs = require("fs"),
    Step = require("step"),
    _ = require("underscore"),
    FilteredStream = require("../lib/filteredstream").FilteredStream,
    publicOnly = require("../lib/filters").publicOnly,
    objectPublicOnly = require("../lib/filters").objectPublicOnly,
    Activity = require("../lib/model/activity").Activity,
    ActivityObject = require("../lib/model/activityobject").ActivityObject,
    AccessToken = require("../lib/model/accesstoken").AccessToken,
    User = require("../lib/model/user").User,
    mw = require("../lib/middleware"),
    he = require("../lib/httperror"),
    HTTPError = he.HTTPError,
    maybeAuth = mw.maybeAuth,
    reqUser = mw.reqUser,
    mustAuth = mw.mustAuth,
    sameUser = mw.sameUser,
    noUser = mw.noUser,
    userAuth = mw.userAuth,
    clientAuth = mw.clientAuth,
    NoSuchThingError = databank.NoSuchThingError;

var addRoutes = function(app) {

    app.get("/", showMain);

    app.get("/main/register", showRegister);

    app.get("/main/login", showLogin);
    app.post("/main/login", clientAuth, handleLogin);

    app.post("/main/logout", userAuth, handleLogout);

    app.get("/:nickname", reqUser, showStream);
    app.get("/:nickname/favorites", reqUser, showFavorites);
    app.get("/:nickname/followers", reqUser, showFollowers);
    app.get("/:nickname/following", reqUser, showFollowing);

    app.get("/:nickname/activity/:uuid", reqUser, showActivity);

    // For things that you can only see if you're logged in,
    // we redirect to the login page, then let you go there

    app.get("/main/settings", loginRedirect("/main/settings"));
    app.get("/main/account", loginRedirect("/main/account"));
};

var loginRedirect = function(rel) {
    return function(req, res, next) {
        res.redirect('/main/login?continue='+rel);
    };
};

var showMain = function(req, res, next) {
    res.render("main", {title: "Welcome"});
};

var showRegister = function(req, res, next) {
    res.render("register", {title: "Register"});
};

var showLogin = function(req, res, next) {
    res.render("login", {title: "Login"});
};

var handleLogout = function(req, res, next) {
    Step(
	function() {
	    AccessToken.search({"consumer_key": req.client.consumer_key,
				"username": req.remoteUser.nickname},
			       this);
	},
	function(err, tokens) {
	    var i, group = this.group();
	    if (err) throw err;
	    for (i = 0; i < tokens.length; i++) {
		// XXX: keep for auditing?
		tokens[i].del(group());
	    }
	},
	function(err) {
	    if (err) {
		next(err);
	    } else {
		req.remoteUser = null;
		res.json("OK");
	    }
	}
    );
};

var showActivity = function(req, res, next) {
    var uuid = req.params.uuid,
        user = req.user;

    Step(
        function() {
            Activity.search({"uuid": req.params.uuid}, this);
        },
        function(err, activities) {
            if (err) throw err;
            if (activities.length === 0) {
                next(new NoSuchThingError("activity", uuid));
            }
            if (activities.length > 1) {
                next(new Error("Too many activities with ID = " + req.params.uuid));
            }
            activities[0].expand(this);
        },
        function(err, activity) {
            if (err) {
                next(err);
            } else {
                res.render("activity", {title: "Welcome",
                                        user: req.remoteUser,
                                        activity: activity});
            }
        }
    );
};

var templates = {};

var compileTemplate = function(name, callback) {

    if (_.has(templates, name)) {
        callback(null, templates[name]);
        return;
    }

    Step(
        function() {
            var filename = path.join(__dirname, "..", "public", "template", name + ".utml");
            fs.readFile(filename, this);
        },
        function(err, data) {
            var fn;
            if (err) {
                callback(err, null);
            } else {
                try {
                    fn = _.template(data.toString());
                    templates[name] = fn;
                    callback(null, fn);
                } catch (e) {
                    callback(e, null);
                }
            }
        }
    );
};

var getFiltered = function(stream, filter, start, end, callback) {
    var filtered = new FilteredStream(stream, filter);
    Step(
        function() {
            filtered.getIDs(0, 20, this);
        },
        function(err, ids) {
            if (err) throw err;
            Activity.readAll(ids, this);
        },
        function(err, activities) {
            if (err) {
                callback(err, null);
            } else {
                callback(null, activities);
            }
        }
    );
};

var getHelpers = function(helpers, callback) {
    Step(
        function() {
            var group = this.group();
            _.each(helpers, function(templateName) {
                compileTemplate(templateName, group());
            });
        },
        function(err, functions) {
            if (err) {
                callback(err, null);
            } else {
                callback(null, _.object(_.keys(helpers), functions));
            }
        }
    );
};

var showStream = function(req, res, next) {
    var pump = this,
        helperNames = {"profileBlock": "profile-block",
                       "majorStream": "major-stream",
                       "sidebar": "sidebar",
                       "majorActivity": "major-activity-headless",
                       "minorActivity": "minor-activity-headless"},
        getData = function(callback) {
            Step(
                function() {
                    req.user.getMajorOutboxStream(this.parallel());
                    req.user.getMinorOutboxStream(this.parallel());
                },
                function(err, majorStream, minorStream) {
                    if (err) throw err;
                    getFiltered(majorStream, publicOnly, 0, 20, this.parallel());
                    getFiltered(minorStream, publicOnly, 0, 20, this.parallel());
                },
                function(err, majorActivities, minorActivities) {
                    if (err) {
                        callback(err, null);
                    } else {
                        callback(null, {major: majorActivities,
                                        minor: minorActivities,
                                        profile: req.user.profile});
                    }
                }
            );
        };

    Step(
        function() {
            getData(this.parallel());
            getHelpers(helperNames, this.parallel());
        },
        function(err, data, helpers) {
            if (err) {
                next(err);
            } else {
                res.render("user", _.extend({title: req.user.nickname},
                                            data,
                                            helpers));
            }
        }
    );
};

var showFavorites = function(req, res, next) {

    var pump = this,
        helperNames = {"profileBlock": "profile-block",
                       "objectStream": "object-stream",
                       "majorObject": "major-object"},
        getData = function(callback) {
            Step(
                function() {
                    req.user.favoritesStream(this);
                },
                function(err, faveStream) {
                    var filtered;
                    if (err) throw err;
                    filtered = new FilteredStream(faveStream, objectPublicOnly);
                    filtered.getObjects(0, 20, this);
                },
                function(err, refs) {
                    var group = this.group();
                    if (err) throw err;
                    _.each(refs, function(ref) {
                        ActivityObject.getObject(ref.objectType, ref.id, group());
                    });
                },
                function(err, objects) {
                    if (err) {
                        callback(err, null);
                    } else {
                        callback(null, {objects: objects,
                                        profile: req.user.profile});
                    }
                }
            );
        };

    Step(
        function() {
            getData(this.parallel());
            getHelpers(helperNames, this.parallel());
        },
        function(err, data, helpers) {
            if (err) {
                next(err);
            } else {
                res.render("favorites", _.extend({title: req.user.nickname},
                                                 data,
                                                 helpers));
            }
        }
    );
};

var showFollowers = function(req, res, next) {

    var pump = this,
        helperNames = {"profileBlock": "profile-block",
                       "peopleStream": "people-stream",
                       "majorPerson": "major-person"},
        getData = function(callback) {
            Step(
                function() {
                    req.user.getFollowers(0, 20, this);
                },
                function(err, followers) {
                    if (err) throw err;
                    if (err) {
                        callback(err, null);
                    } else {
                        callback(null, {people: followers,
                                        profile: req.user.profile});
                    }
                }
            );
        };

    Step(
        function() {
            getData(this.parallel());
            getHelpers(helperNames, this.parallel());
        },
        function(err, data, helpers) {
            if (err) {
                next(err);
            } else {
                res.render("followers", _.extend({title: req.user.nickname},
                                                 data,
                                                 helpers));
            }
        }
    );
};

var showFollowing = function(req, res, next) {

    var pump = this,
        helperNames = {"profileBlock": "profile-block",
                       "peopleStream": "people-stream",
                       "majorPerson": "major-person"},
        getData = function(callback) {
            Step(
                function() {
                    req.user.getFollowing(0, 20, this);
                },
                function(err, following) {
                    if (err) throw err;
                    if (err) {
                        callback(err, null);
                    } else {
                        callback(null, {people: following,
                                        profile: req.user.profile});
                    }
                }
            );
        };

    Step(
        function() {
            getData(this.parallel());
            getHelpers(helperNames, this.parallel());
        },
        function(err, data, helpers) {
            if (err) {
                next(err);
            } else {
                res.render("following", _.extend({title: req.user.nickname},
                                                 data,
                                                 helpers));
            }
        }
    );
};

var handleLogin = function(req, res, next) {

    var user = null;

    Step(
        function () {
            User.checkCredentials(req.body.nickname, req.body.password, this);
        },
        function(err, result) {
            if (err) throw err;
            if (!result) {
                throw new HTTPError("Incorrect username or password", 401);
            }
            user = result;
            user.expand(this);
        },
        function(err) {
            if (err) throw err;
            req.app.provider.newTokenPair(req.client, user, this);
        },
        function(err, pair) {
            if (err) {
                next(err);
            } else {
                user.sanitize();
                user.token = pair.access_token;
                user.secret = pair.token_secret;
                res.json(user);
            }
        }
    );
};

exports.addRoutes = addRoutes;
<commit_msg>Include favorites/followers/following in page title<commit_after>// routes/web.js
//
// Spurtin' out pumpy goodness all over your browser window
//
// Copyright 2011-2012, StatusNet Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var databank = require("databank"),
    url = require("url"),
    path = require("path"),
    fs = require("fs"),
    Step = require("step"),
    _ = require("underscore"),
    FilteredStream = require("../lib/filteredstream").FilteredStream,
    publicOnly = require("../lib/filters").publicOnly,
    objectPublicOnly = require("../lib/filters").objectPublicOnly,
    Activity = require("../lib/model/activity").Activity,
    ActivityObject = require("../lib/model/activityobject").ActivityObject,
    AccessToken = require("../lib/model/accesstoken").AccessToken,
    User = require("../lib/model/user").User,
    mw = require("../lib/middleware"),
    he = require("../lib/httperror"),
    HTTPError = he.HTTPError,
    maybeAuth = mw.maybeAuth,
    reqUser = mw.reqUser,
    mustAuth = mw.mustAuth,
    sameUser = mw.sameUser,
    noUser = mw.noUser,
    userAuth = mw.userAuth,
    clientAuth = mw.clientAuth,
    NoSuchThingError = databank.NoSuchThingError;

var addRoutes = function(app) {

    app.get("/", showMain);

    app.get("/main/register", showRegister);

    app.get("/main/login", showLogin);
    app.post("/main/login", clientAuth, handleLogin);

    app.post("/main/logout", userAuth, handleLogout);

    app.get("/:nickname", reqUser, showStream);
    app.get("/:nickname/favorites", reqUser, showFavorites);
    app.get("/:nickname/followers", reqUser, showFollowers);
    app.get("/:nickname/following", reqUser, showFollowing);

    app.get("/:nickname/activity/:uuid", reqUser, showActivity);

    // For things that you can only see if you're logged in,
    // we redirect to the login page, then let you go there

    app.get("/main/settings", loginRedirect("/main/settings"));
    app.get("/main/account", loginRedirect("/main/account"));
};

var loginRedirect = function(rel) {
    return function(req, res, next) {
        res.redirect('/main/login?continue='+rel);
    };
};

var showMain = function(req, res, next) {
    res.render("main", {title: "Welcome"});
};

var showRegister = function(req, res, next) {
    res.render("register", {title: "Register"});
};

var showLogin = function(req, res, next) {
    res.render("login", {title: "Login"});
};

var handleLogout = function(req, res, next) {
    Step(
	function() {
	    AccessToken.search({"consumer_key": req.client.consumer_key,
				"username": req.remoteUser.nickname},
			       this);
	},
	function(err, tokens) {
	    var i, group = this.group();
	    if (err) throw err;
	    for (i = 0; i < tokens.length; i++) {
		// XXX: keep for auditing?
		tokens[i].del(group());
	    }
	},
	function(err) {
	    if (err) {
		next(err);
	    } else {
		req.remoteUser = null;
		res.json("OK");
	    }
	}
    );
};

var showActivity = function(req, res, next) {
    var uuid = req.params.uuid,
        user = req.user;

    Step(
        function() {
            Activity.search({"uuid": req.params.uuid}, this);
        },
        function(err, activities) {
            if (err) throw err;
            if (activities.length === 0) {
                next(new NoSuchThingError("activity", uuid));
            }
            if (activities.length > 1) {
                next(new Error("Too many activities with ID = " + req.params.uuid));
            }
            activities[0].expand(this);
        },
        function(err, activity) {
            if (err) {
                next(err);
            } else {
                res.render("activity", {title: "Welcome",
                                        user: req.remoteUser,
                                        activity: activity});
            }
        }
    );
};

var templates = {};

var compileTemplate = function(name, callback) {

    if (_.has(templates, name)) {
        callback(null, templates[name]);
        return;
    }

    Step(
        function() {
            var filename = path.join(__dirname, "..", "public", "template", name + ".utml");
            fs.readFile(filename, this);
        },
        function(err, data) {
            var fn;
            if (err) {
                callback(err, null);
            } else {
                try {
                    fn = _.template(data.toString());
                    templates[name] = fn;
                    callback(null, fn);
                } catch (e) {
                    callback(e, null);
                }
            }
        }
    );
};

var getFiltered = function(stream, filter, start, end, callback) {
    var filtered = new FilteredStream(stream, filter);
    Step(
        function() {
            filtered.getIDs(0, 20, this);
        },
        function(err, ids) {
            if (err) throw err;
            Activity.readAll(ids, this);
        },
        function(err, activities) {
            if (err) {
                callback(err, null);
            } else {
                callback(null, activities);
            }
        }
    );
};

var getHelpers = function(helpers, callback) {
    Step(
        function() {
            var group = this.group();
            _.each(helpers, function(templateName) {
                compileTemplate(templateName, group());
            });
        },
        function(err, functions) {
            if (err) {
                callback(err, null);
            } else {
                callback(null, _.object(_.keys(helpers), functions));
            }
        }
    );
};

var showStream = function(req, res, next) {
    var pump = this,
        helperNames = {"profileBlock": "profile-block",
                       "majorStream": "major-stream",
                       "sidebar": "sidebar",
                       "majorActivity": "major-activity-headless",
                       "minorActivity": "minor-activity-headless"},
        getData = function(callback) {
            Step(
                function() {
                    req.user.getMajorOutboxStream(this.parallel());
                    req.user.getMinorOutboxStream(this.parallel());
                },
                function(err, majorStream, minorStream) {
                    if (err) throw err;
                    getFiltered(majorStream, publicOnly, 0, 20, this.parallel());
                    getFiltered(minorStream, publicOnly, 0, 20, this.parallel());
                },
                function(err, majorActivities, minorActivities) {
                    if (err) {
                        callback(err, null);
                    } else {
                        callback(null, {major: majorActivities,
                                        minor: minorActivities,
                                        profile: req.user.profile});
                    }
                }
            );
        };

    Step(
        function() {
            getData(this.parallel());
            getHelpers(helperNames, this.parallel());
        },
        function(err, data, helpers) {
            if (err) {
                next(err);
            } else {
                res.render("user", _.extend({title: req.user.nickname},
                                            data,
                                            helpers));
            }
        }
    );
};

var showFavorites = function(req, res, next) {

    var pump = this,
        helperNames = {"profileBlock": "profile-block",
                       "objectStream": "object-stream",
                       "majorObject": "major-object"},
        getData = function(callback) {
            Step(
                function() {
                    req.user.favoritesStream(this);
                },
                function(err, faveStream) {
                    var filtered;
                    if (err) throw err;
                    filtered = new FilteredStream(faveStream, objectPublicOnly);
                    filtered.getObjects(0, 20, this);
                },
                function(err, refs) {
                    var group = this.group();
                    if (err) throw err;
                    _.each(refs, function(ref) {
                        ActivityObject.getObject(ref.objectType, ref.id, group());
                    });
                },
                function(err, objects) {
                    if (err) {
                        callback(err, null);
                    } else {
                        callback(null, {objects: objects,
                                        profile: req.user.profile});
                    }
                }
            );
        };

    Step(
        function() {
            getData(this.parallel());
            getHelpers(helperNames, this.parallel());
        },
        function(err, data, helpers) {
            if (err) {
                next(err);
            } else {
                res.render("favorites", _.extend({title: req.user.nickname + " favorites"},
                                                 data,
                                                 helpers));
            }
        }
    );
};

var showFollowers = function(req, res, next) {

    var pump = this,
        helperNames = {"profileBlock": "profile-block",
                       "peopleStream": "people-stream",
                       "majorPerson": "major-person"},
        getData = function(callback) {
            Step(
                function() {
                    req.user.getFollowers(0, 20, this);
                },
                function(err, followers) {
                    if (err) throw err;
                    if (err) {
                        callback(err, null);
                    } else {
                        callback(null, {people: followers,
                                        profile: req.user.profile});
                    }
                }
            );
        };

    Step(
        function() {
            getData(this.parallel());
            getHelpers(helperNames, this.parallel());
        },
        function(err, data, helpers) {
            if (err) {
                next(err);
            } else {
                res.render("followers", _.extend({title: req.user.nickname + " followers"},
                                                 data,
                                                 helpers));
            }
        }
    );
};

var showFollowing = function(req, res, next) {

    var pump = this,
        helperNames = {"profileBlock": "profile-block",
                       "peopleStream": "people-stream",
                       "majorPerson": "major-person"},
        getData = function(callback) {
            Step(
                function() {
                    req.user.getFollowing(0, 20, this);
                },
                function(err, following) {
                    if (err) throw err;
                    if (err) {
                        callback(err, null);
                    } else {
                        callback(null, {people: following,
                                        profile: req.user.profile});
                    }
                }
            );
        };

    Step(
        function() {
            getData(this.parallel());
            getHelpers(helperNames, this.parallel());
        },
        function(err, data, helpers) {
            if (err) {
                next(err);
            } else {
                res.render("following", _.extend({title: req.user.nickname + " following"},
                                                 data,
                                                 helpers));
            }
        }
    );
};

var handleLogin = function(req, res, next) {

    var user = null;

    Step(
        function () {
            User.checkCredentials(req.body.nickname, req.body.password, this);
        },
        function(err, result) {
            if (err) throw err;
            if (!result) {
                throw new HTTPError("Incorrect username or password", 401);
            }
            user = result;
            user.expand(this);
        },
        function(err) {
            if (err) throw err;
            req.app.provider.newTokenPair(req.client, user, this);
        },
        function(err, pair) {
            if (err) {
                next(err);
            } else {
                user.sanitize();
                user.token = pair.access_token;
                user.secret = pair.token_secret;
                res.json(user);
            }
        }
    );
};

exports.addRoutes = addRoutes;
<|endoftext|>


<commit_before>class Connection {
    constructor($log, $rootScope, $websocket, $mdToast) {
        this.dataStream = null;
        this.isConnected = false;
        this.loginFailed = false;
        this.$log = $log;
        this.$rootScope = $rootScope;
        this.$websocket = $websocket;
        this.$mdToast = $mdToast;
    }

    connect(host, port, password) {
        var ctrl = this,
            $rootScope = this.$rootScope;
        ctrl.dataStream = this.$websocket("ws://" + host + ":" + port);

        let fullResponse = {
            isStarted: false,
            value: ""

        };
        ctrl.dataStream.onMessage(function (message) {
            var response = message.data;

            // Parse various kinds of responses
            if (response === `Login by sending 'login!<PASSWORD>'`) {
                // This is the response on the first connection, do
                // nothing
                ctrl.isConnected = false;
            } else if (response === "Not authorized, login first by" +
                ` sending 'login!<PASSWORD>'`) {
                ctrl.isConnected = false;
                ctrl.loginFailed = true;
            } else {
                ctrl.isConnected = true;
                ctrl.loginFailed = false;
                $rootScope.$broadcast("EvalResponse", response);
            }
        });
        ctrl.dataStream.onOpen(function () {
            ctrl.dataStream.send("login!" + password);
        });
        ctrl.dataStream.onClose((event) => this.onClose(event));
    }

    onClose(event) {
        this.isConnected = false;
        let code = event.code;
        let reason = closeErrors[code] ? closeErrors[code] : "Unknown reason";

        this.toast("Connection closed: " + reason);

    }

    send(codeSnippet) {
        var ctrl = this;
        if (this.dataStream == null) {
            this.toast("Not connected");
        } else {
            ctrl.dataStream.send(codeSnippet);
        }
    }

    toast(message) {
        this.$mdToast.show(this.$mdToast.simple().content(message));
    }
}

Connection.$inject = ["$log", "$rootScope", "$websocket", "$mdToast"];

export default Connection;

// Error code mapping data
var closeErrors = {
    1000: `Normal closure, meaning that the purpose for which the
    connection was established has been fulfilled.`,
    1001: `An endpoint is "going away", such as a server going down or a
    browser having navigated away from a page.`,
    1002: `An endpoint is terminating the connection due to a protocol error`,
    1003: `An endpoint is terminating the connection because it has
     received a type of data it cannot accept (e.g., an endpoint that
     understands only text data MAY send this if it receives a binary
     message).`,
    1004: `Reserved. The specific meaning might be defined in the future.`,
    1005: `No status code was actually present.`,
    1006: `The connection was closed abnormally, e.g., without sending
    or receiving a Close control frame`,
    1007: `An endpoint is terminating the connection because it has
    received data within a message that was not consistent with the type
    of the message (e.g., non-UTF-8 [http://tools.ietf.org/html/rfc3629]
    data within a text message).`,
    1008: `An endpoint is terminating the connection because it has
    received a message that "violates its policy". This reason is
    given either if there is no other suitable reason, or if there is a
    need to hide specific details about the policy.`,
    1009: `An endpoint is terminating the connection because it has
    received a message that is too big for it to process.`,
    1010: `An endpoint (client) is terminating the connection because
    it has expected the server to negotiate one or more extension, but
    the server didn't return them in the response message of the
    WebSocket handshake.`,
    1011: `A server is terminating the connection because it encountered
    an unexpected condition that prevented it from fulfilling the
    request.`,
    1015: `The connection was closed due to a failure to perform a TLS handshake (e.g., the server certificate can't be verified).`

}<commit_msg>Refactor into invidual controller methods. Handle incorrect passwords.<commit_after>class Connection {
    constructor($log, $rootScope, $websocket, $mdToast) {
        this.dataStream = null;
        this.isConnected = false;
        this.loginFailed = false;
        this.$log = $log;
        this.$rootScope = $rootScope;
        this.$websocket = $websocket;
        this.$mdToast = $mdToast;
    }

    connect(host, port, password) {
        this.dataStream = this.$websocket("ws://" + host + ":" + port);

        this.dataStream.onMessage((message) => this.onMessage(message));
        this.dataStream.onOpen(() => this.onOpen(password));
        this.dataStream.onClose((event) => this.onClose(event));
    }

    onMessage(message) {
        // Dispatcher for different kinds of messages
        let response = message.data;

        // Parse various kinds of responses
        if (response === `Login by sending 'login!<PASSWORD>'`) {
            // This is the response on the first connection, do
            // nothing
            this.isConnected = false;
        }else if (response.startsWith("Incorrect password!")) {
            this.toast("Incorrect password");
        } else if (response.startsWith("Not authorized, login first")) {
            this.isConnected = false;
            this.loginFailed = true;
        } else {
            this.isConnected = true;
            this.loginFailed = false;
            this.$rootScope.$broadcast("EvalResponse", response);
        }
    }

    onOpen(password) {
        this.dataStream.send("login!" + password);
    }

    onClose(event) {
        this.isConnected = false;
        let code = event.code;
        let reason = closeErrors[code] ? closeErrors[code] : "Unknown reason";

        this.toast("Connection closed: " + reason);

    }

    send(codeSnippet) {
        var ctrl = this;
        if (this.dataStream == null) {
            this.toast("Not connected");
        } else {
            ctrl.dataStream.send(codeSnippet);
        }
    }

    toast(message) {
        this.$mdToast.show(this.$mdToast.simple().content(message));
    }
}

Connection.$inject = ["$log", "$rootScope", "$websocket", "$mdToast"];

export default Connection;

// Error code mapping data
var closeErrors = {
    1000: `Normal closure, meaning that the purpose for which the
    connection was established has been fulfilled.`,
    1001: `An endpoint is "going away", such as a server going down or a
    browser having navigated away from a page.`,
    1002: `An endpoint is terminating the connection due to a protocol error`,
    1003: `An endpoint is terminating the connection because it has
     received a type of data it cannot accept (e.g., an endpoint that
     understands only text data MAY send this if it receives a binary
     message).`,
    1004: `Reserved. The specific meaning might be defined in the future.`,
    1005: `No status code was actually present.`,
    1006: `The connection was closed abnormally, e.g., without sending
    or receiving a Close control frame`,
    1007: `An endpoint is terminating the connection because it has
    received data within a message that was not consistent with the type
    of the message (e.g., non-UTF-8 [http://tools.ietf.org/html/rfc3629]
    data within a text message).`,
    1008: `An endpoint is terminating the connection because it has
    received a message that "violates its policy". This reason is
    given either if there is no other suitable reason, or if there is a
    need to hide specific details about the policy.`,
    1009: `An endpoint is terminating the connection because it has
    received a message that is too big for it to process.`,
    1010: `An endpoint (client) is terminating the connection because
    it has expected the server to negotiate one or more extension, but
    the server didn't return them in the response message of the
    WebSocket handshake.`,
    1011: `A server is terminating the connection because it encountered
    an unexpected condition that prevented it from fulfilling the
    request.`,
    1015: `The connection was closed due to a failure to perform a TLS handshake (e.g., the server certificate can't be verified).`

}<|endoftext|>