<commit_before>// javascript-astar 0.3.0
// http://github.com/bgrins/javascript-astar
// Freely distributable under the MIT License.
// Implements the astar search algorithm in javascript using a Binary Heap.
// Includes Binary Heap (with modifications) from Marijn Haverbeke.
// http://eloquentjavascript.net/appendix2.html

(function(definition) {
    /* global module, define */
    if(typeof module === 'object' && typeof module.exports === 'object') {
        module.exports = definition();
    } else if(typeof define === 'function' && define.amd) {
        define([], definition);
    } else {
        var exports = definition();
        window.astar = exports.astar;
        window.Graph = exports.Graph;
    }
})(function() {

function pathTo(node){
    var curr = node,
        path = [];
    while(curr.parent) {
        path.push(curr);
        curr = curr.parent;
    }
    return path.reverse();
}

function getHeap() {
    return new BinaryHeap(function(node) {
        return node.f;
    });
}

var astar = {
    init: function(graph) {
        for (var i = 0, len = graph.nodes.length; i < len; ++i) {
            var node = graph.nodes[i];
            node.f = 0;
            node.g = 0;
            node.h = 0;
            node.visited = false;
            node.closed = false;
            node.parent = null;
        }
    },

    /**
    * Perform an A* Search on a graph given a start and end node.
    * @param {Graph} graph
    * @param {GridNode} start
    * @param {GridNode} end
    * @param {Object} [options]
    * @param {bool} [options.closest] Specifies whether to return the
               path to the closest node if the target is unreachable.
    * @param {Function} [options.heuristic] Heuristic function (see
    *          astar.heuristics).
    */
    search: function(graph, start, end, options) {

        options = options || {};
        var heuristic = options.heuristic || astar.heuristics.manhattan,
            closest = options.closest || false;

        var openHeap = getHeap(),
            closestNode = start; // set the start node to be the closest if required

        start.h = heuristic(start, end);

        openHeap.push(start);

        var cleaner = [];
        while(openHeap.size() > 0) {

            // Grab the lowest f(x) to process next.  Heap keeps this sorted for us.
            var currentNode = openHeap.pop();

            // End case -- result has been found, return the traced path.
            if(currentNode === end) {
                var path = pathTo(currentNode);
                for (var c = 0; c < cleaner.length; c++) {
                    var node = cleaner[c];
                    node.f = 0;
                    node.g = 0;
                    node.h = 0;
                    node.visited = false;
                    node.closed = false;
                    node.parent = null;
                }
                return path;
            }

            // Normal case -- move currentNode from open to closed, process each of its neighbors.
            currentNode.closed = true;

            // Find all neighbors for the current node.
            var neighbors = graph.neighbors(currentNode);

            for (var i = 0, il = neighbors.length; i < il; ++i) {
                var neighbor = neighbors[i];

                if (neighbor.closed || neighbor.isWall()) {
                    // Not a valid node to process, skip to next neighbor.
                    continue;
                }

                // The g score is the shortest distance from start to current node.
                // We need to check if the path we have arrived at this neighbor is the shortest one we have seen yet.
                var gScore = currentNode.g + neighbor.getCost(currentNode),
                    beenVisited = neighbor.visited;

                if (!beenVisited || gScore < neighbor.g) {

                    // Found an optimal (so far) path to this node.  Take score for node to see how good it is.
                    neighbor.visited = true;
                    neighbor.parent = currentNode;
                    neighbor.h = neighbor.h || heuristic(neighbor, end);
                    neighbor.g = gScore;
                    neighbor.f = neighbor.g + neighbor.h;
                    cleaner.push(neighbor);
                    if (closest) {
                        // If the neighbour is closer than the current closestNode or if it's equally close but has
                        // a cheaper path than the current closest node then it becomes the closest node
                        if (neighbor.h < closestNode.h || (neighbor.h === closestNode.h && neighbor.g < closestNode.g)) {
                            closestNode = neighbor;
                        }
                    }

                    if (!beenVisited) {
                        // Pushing to heap will put it in proper place based on the 'f' value.
                        openHeap.push(neighbor);
                    }
                    else {
                        // Already seen the node, but since it has been rescored we need to reorder it in the heap
                        openHeap.rescoreElement(neighbor);
                    }
                }
            }
        }

        if (closest) {
            return pathTo(closestNode);
        }

        // No result was found - empty array signifies failure to find path.
        return [];
    },
    // See list of heuristics: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html
    heuristics: {
        manhattan: function(pos0, pos1) {
            var d1 = Math.abs(pos1.x - pos0.x);
            var d2 = Math.abs(pos1.y - pos0.y);
            return d1 + d2;
        },
        diagonal: function(pos0, pos1) {
            var D = 1;
            var D2 = Math.sqrt(2);
            var d1 = Math.abs(pos1.x - pos0.x);
            var d2 = Math.abs(pos1.y - pos0.y);
            return (D * (d1 + d2)) + ((D2 - (2 * D)) * Math.min(d1, d2));
        }
    }
};

/**
* A graph memory structure
* @param {Array} gridIn 2D array of input weights
* @param {Object} [options]
* @param {bool} [options.diagonal] Specifies whether diagonal moves are allowed
*/
function Graph(gridIn, options) {
    options = options || {};
    this.nodes = [];
    this.diagonal = !!options.diagonal;
    this.grid = [];
    for (var x = 0; x < gridIn.length; x++) {
        this.grid[x] = [];

        for (var y = 0, row = gridIn[x]; y < row.length; y++) {
            var node = new GridNode(x, y, row[y]);
            this.grid[x][y] = node;
            this.nodes.push(node);
        }
    }
    astar.init(this);
}

Graph.prototype.neighbors = function(node) {
    var ret = [],
        x = node.x,
        y = node.y,
        grid = this.grid;

    // West
    if(grid[x-1] && grid[x-1][y]) {
        ret.push(grid[x-1][y]);
    }

    // East
    if(grid[x+1] && grid[x+1][y]) {
        ret.push(grid[x+1][y]);
    }

    // South
    if(grid[x] && grid[x][y-1]) {
        ret.push(grid[x][y-1]);
    }

    // North
    if(grid[x] && grid[x][y+1]) {
        ret.push(grid[x][y+1]);
    }

    if (this.diagonal) {
        // Southwest
        if(grid[x-1] && grid[x-1][y-1]) {
            ret.push(grid[x-1][y-1]);
        }

        // Southeast
        if(grid[x+1] && grid[x+1][y-1]) {
            ret.push(grid[x+1][y-1]);
        }

        // Northwest
        if(grid[x-1] && grid[x-1][y+1]) {
            ret.push(grid[x-1][y+1]);
        }

        // Northeast
        if(grid[x+1] && grid[x+1][y+1]) {
            ret.push(grid[x+1][y+1]);
        }
    }

    return ret;
};

Graph.prototype.toString = function() {
    var graphString = [],
        nodes = this.grid, // when using grid
        rowDebug, row, y, l;
    for (var x = 0, len = nodes.length; x < len; x++) {
        rowDebug = [];
        row = nodes[x];
        for (y = 0, l = row.length; y < l; y++) {
            rowDebug.push(row[y].weight);
        }
        graphString.push(rowDebug.join(" "));
    }
    return graphString.join("\n");
};

function GridNode(x, y, weight) {
    this.x = x;
    this.y = y;
    this.weight = weight;
}

GridNode.prototype.toString = function() {
    return "[" + this.x + " " + this.y + "]";
};

GridNode.prototype.getCost = function() {
    return this.weight;
};

GridNode.prototype.isWall = function() {
    return this.weight === 0;
};

function BinaryHeap(scoreFunction){
    this.content = [];
    this.scoreFunction = scoreFunction;
}

BinaryHeap.prototype = {
    push: function(element) {
        // Add the new element to the end of the array.
        this.content.push(element);

        // Allow it to sink down.
        this.sinkDown(this.content.length - 1);
    },
    pop: function() {
        // Store the first element so we can return it later.
        var result = this.content[0];
        // Get the element at the end of the array.
        var end = this.content.pop();
        // If there are any elements left, put the end element at the
        // start, and let it bubble up.
        if (this.content.length > 0) {
            this.content[0] = end;
            this.bubbleUp(0);
        }
        return result;
    },
    remove: function(node) {
        var i = this.content.indexOf(node);

        // When it is found, the process seen in 'pop' is repeated
        // to fill up the hole.
        var end = this.content.pop();

        if (i !== this.content.length - 1) {
            this.content[i] = end;

            if (this.scoreFunction(end) < this.scoreFunction(node)) {
                this.sinkDown(i);
            }
            else {
                this.bubbleUp(i);
            }
        }
    },
    size: function() {
        return this.content.length;
    },
    rescoreElement: function(node) {
        this.sinkDown(this.content.indexOf(node));
    },
    sinkDown: function(n) {
        // Fetch the element that has to be sunk.
        var element = this.content[n];

        // When at 0, an element can not sink any further.
        while (n > 0) {

            // Compute the parent element's index, and fetch it.
            var parentN = ((n + 1) >> 1) - 1,
                parent = this.content[parentN];
            // Swap the elements if the parent is greater.
            if (this.scoreFunction(element) < this.scoreFunction(parent)) {
                this.content[parentN] = element;
                this.content[n] = parent;
                // Update 'n' to continue at the new position.
                n = parentN;
            }
            // Found a parent that is less, no need to sink any further.
            else {
                break;
            }
        }
    },
    bubbleUp: function(n) {
        // Look up the target element and its score.
        var length = this.content.length,
            element = this.content[n],
            elemScore = this.scoreFunction(element);

        while(true) {
            // Compute the indices of the child elements.
            var child2N = (n + 1) << 1,
                child1N = child2N - 1;
            // This is used to store the new position of the element, if any.
            var swap = null,
                child1Score;
            // If the first child exists (is inside the array)...
            if (child1N < length) {
                // Look it up and compute its score.
                var child1 = this.content[child1N];
                child1Score = this.scoreFunction(child1);

                // If the score is less than our element's, we need to swap.
                if (child1Score < elemScore){
                    swap = child1N;
                }
            }

            // Do the same checks for the other child.
            if (child2N < length) {
                var child2 = this.content[child2N],
                    child2Score = this.scoreFunction(child2);
                if (child2Score < (swap === null ? elemScore : child1Score)) {
                    swap = child2N;
                }
            }

            // If the element needs to be moved, swap it, and continue.
            if (swap !== null) {
                this.content[n] = this.content[swap];
                this.content[swap] = element;
                n = swap;
            }
            // Otherwise, we are done.
            else {
                break;
            }
        }
    }
};

return {
    astar: astar,
    Graph: Graph
};

});
<commit_msg>Fixed missed use case of no path found<commit_after>// javascript-astar 0.3.0
// http://github.com/bgrins/javascript-astar
// Freely distributable under the MIT License.
// Implements the astar search algorithm in javascript using a Binary Heap.
// Includes Binary Heap (with modifications) from Marijn Haverbeke.
// http://eloquentjavascript.net/appendix2.html

(function(definition) {
    /* global module, define */
    if(typeof module === 'object' && typeof module.exports === 'object') {
        module.exports = definition();
    } else if(typeof define === 'function' && define.amd) {
        define([], definition);
    } else {
        var exports = definition();
        window.astar = exports.astar;
        window.Graph = exports.Graph;
    }
})(function() {

function pathTo(node){
    var curr = node,
        path = [];
    while(curr.parent) {
        path.push(curr);
        curr = curr.parent;
    }
    return path.reverse();
}

function getHeap() {
    return new BinaryHeap(function(node) {
        return node.f;
    });
}

var astar = {
    init: function(graph) {
        for (var i = 0, len = graph.nodes.length; i < len; ++i) {
            var node = graph.nodes[i];
            node.f = 0;
            node.g = 0;
            node.h = 0;
            node.visited = false;
            node.closed = false;
            node.parent = null;
        }
    },
    cleanNodes:function(hitNodes){
        for (var i = 0; i < hitNodes.length; i++) {
            var node = hitNodes[i];
            node.f = 0;
            node.g = 0;
            node.h = 0;
            node.visited = false;
            node.closed = false;
            node.parent = null;
        }
    },
    /**
    * Perform an A* Search on a graph given a start and end node.
    * @param {Graph} graph
    * @param {GridNode} start
    * @param {GridNode} end
    * @param {Object} [options]
    * @param {bool} [options.closest] Specifies whether to return the
               path to the closest node if the target is unreachable.
    * @param {Function} [options.heuristic] Heuristic function (see
    *          astar.heuristics).
    */
    search: function(graph, start, end, options) {

        options = options || {};
        var heuristic = options.heuristic || astar.heuristics.manhattan,
            closest = options.closest || false;

        var openHeap = getHeap(),
            closestNode = start; // set the start node to be the closest if required

        start.h = heuristic(start, end);

        openHeap.push(start);

        var hitNodes = [];
        while(openHeap.size() > 0) {

            // Grab the lowest f(x) to process next.  Heap keeps this sorted for us.
            var currentNode = openHeap.pop();

            // End case -- result has been found, return the traced path.
            if(currentNode === end) {
                var path = pathTo(currentNode);
                astar.cleanNodes(hitNodes);
                return path;
            }

            // Normal case -- move currentNode from open to closed, process each of its neighbors.
            currentNode.closed = true;

            // Find all neighbors for the current node.
            var neighbors = graph.neighbors(currentNode);

            for (var i = 0, il = neighbors.length; i < il; ++i) {
                var neighbor = neighbors[i];

                if (neighbor.closed || neighbor.isWall()) {
                    // Not a valid node to process, skip to next neighbor.
                    continue;
                }

                // The g score is the shortest distance from start to current node.
                // We need to check if the path we have arrived at this neighbor is the shortest one we have seen yet.
                var gScore = currentNode.g + neighbor.getCost(currentNode),
                    beenVisited = neighbor.visited;

                if (!beenVisited || gScore < neighbor.g) {

                    // Found an optimal (so far) path to this node.  Take score for node to see how good it is.
                    neighbor.visited = true;
                    neighbor.parent = currentNode;
                    neighbor.h = neighbor.h || heuristic(neighbor, end);
                    neighbor.g = gScore;
                    neighbor.f = neighbor.g + neighbor.h;
                    hitNodes.push(neighbor);
                    if (closest) {
                        // If the neighbour is closer than the current closestNode or if it's equally close but has
                        // a cheaper path than the current closest node then it becomes the closest node
                        if (neighbor.h < closestNode.h || (neighbor.h === closestNode.h && neighbor.g < closestNode.g)) {
                            closestNode = neighbor;
                        }
                    }

                    if (!beenVisited) {
                        // Pushing to heap will put it in proper place based on the 'f' value.
                        openHeap.push(neighbor);
                    }
                    else {
                        // Already seen the node, but since it has been rescored we need to reorder it in the heap
                        openHeap.rescoreElement(neighbor);
                    }
                }
            }
        }

        if (closest) {
            var closestPath = pathTo(closestNode);
            astar.cleanNodes(hitNodes);
            return closestPath;
        }

        astar.cleanNodes(hitNodes);
        // No result was found - empty array signifies failure to find path.
        return [];
    },
    // See list of heuristics: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html
    heuristics: {
        manhattan: function(pos0, pos1) {
            var d1 = Math.abs(pos1.x - pos0.x);
            var d2 = Math.abs(pos1.y - pos0.y);
            return d1 + d2;
        },
        diagonal: function(pos0, pos1) {
            var D = 1;
            var D2 = Math.sqrt(2);
            var d1 = Math.abs(pos1.x - pos0.x);
            var d2 = Math.abs(pos1.y - pos0.y);
            return (D * (d1 + d2)) + ((D2 - (2 * D)) * Math.min(d1, d2));
        }
    }
};

/**
* A graph memory structure
* @param {Array} gridIn 2D array of input weights
* @param {Object} [options]
* @param {bool} [options.diagonal] Specifies whether diagonal moves are allowed
*/
function Graph(gridIn, options) {
    options = options || {};
    this.nodes = [];
    this.diagonal = !!options.diagonal;
    this.grid = [];
    for (var x = 0; x < gridIn.length; x++) {
        this.grid[x] = [];

        for (var y = 0, row = gridIn[x]; y < row.length; y++) {
            var node = new GridNode(x, y, row[y]);
            this.grid[x][y] = node;
            this.nodes.push(node);
        }
    }
    astar.init(this);
}

Graph.prototype.neighbors = function(node) {
    var ret = [],
        x = node.x,
        y = node.y,
        grid = this.grid;

    // West
    if(grid[x-1] && grid[x-1][y]) {
        ret.push(grid[x-1][y]);
    }

    // East
    if(grid[x+1] && grid[x+1][y]) {
        ret.push(grid[x+1][y]);
    }

    // South
    if(grid[x] && grid[x][y-1]) {
        ret.push(grid[x][y-1]);
    }

    // North
    if(grid[x] && grid[x][y+1]) {
        ret.push(grid[x][y+1]);
    }

    if (this.diagonal) {
        // Southwest
        if(grid[x-1] && grid[x-1][y-1]) {
            ret.push(grid[x-1][y-1]);
        }

        // Southeast
        if(grid[x+1] && grid[x+1][y-1]) {
            ret.push(grid[x+1][y-1]);
        }

        // Northwest
        if(grid[x-1] && grid[x-1][y+1]) {
            ret.push(grid[x-1][y+1]);
        }

        // Northeast
        if(grid[x+1] && grid[x+1][y+1]) {
            ret.push(grid[x+1][y+1]);
        }
    }

    return ret;
};

Graph.prototype.toString = function() {
    var graphString = [],
        nodes = this.grid, // when using grid
        rowDebug, row, y, l;
    for (var x = 0, len = nodes.length; x < len; x++) {
        rowDebug = [];
        row = nodes[x];
        for (y = 0, l = row.length; y < l; y++) {
            rowDebug.push(row[y].weight);
        }
        graphString.push(rowDebug.join(" "));
    }
    return graphString.join("\n");
};

function GridNode(x, y, weight) {
    this.x = x;
    this.y = y;
    this.weight = weight;
}

GridNode.prototype.toString = function() {
    return "[" + this.x + " " + this.y + "]";
};

GridNode.prototype.getCost = function() {
    return this.weight;
};

GridNode.prototype.isWall = function() {
    return this.weight === 0;
};

function BinaryHeap(scoreFunction){
    this.content = [];
    this.scoreFunction = scoreFunction;
}

BinaryHeap.prototype = {
    push: function(element) {
        // Add the new element to the end of the array.
        this.content.push(element);

        // Allow it to sink down.
        this.sinkDown(this.content.length - 1);
    },
    pop: function() {
        // Store the first element so we can return it later.
        var result = this.content[0];
        // Get the element at the end of the array.
        var end = this.content.pop();
        // If there are any elements left, put the end element at the
        // start, and let it bubble up.
        if (this.content.length > 0) {
            this.content[0] = end;
            this.bubbleUp(0);
        }
        return result;
    },
    remove: function(node) {
        var i = this.content.indexOf(node);

        // When it is found, the process seen in 'pop' is repeated
        // to fill up the hole.
        var end = this.content.pop();

        if (i !== this.content.length - 1) {
            this.content[i] = end;

            if (this.scoreFunction(end) < this.scoreFunction(node)) {
                this.sinkDown(i);
            }
            else {
                this.bubbleUp(i);
            }
        }
    },
    size: function() {
        return this.content.length;
    },
    rescoreElement: function(node) {
        this.sinkDown(this.content.indexOf(node));
    },
    sinkDown: function(n) {
        // Fetch the element that has to be sunk.
        var element = this.content[n];

        // When at 0, an element can not sink any further.
        while (n > 0) {

            // Compute the parent element's index, and fetch it.
            var parentN = ((n + 1) >> 1) - 1,
                parent = this.content[parentN];
            // Swap the elements if the parent is greater.
            if (this.scoreFunction(element) < this.scoreFunction(parent)) {
                this.content[parentN] = element;
                this.content[n] = parent;
                // Update 'n' to continue at the new position.
                n = parentN;
            }
            // Found a parent that is less, no need to sink any further.
            else {
                break;
            }
        }
    },
    bubbleUp: function(n) {
        // Look up the target element and its score.
        var length = this.content.length,
            element = this.content[n],
            elemScore = this.scoreFunction(element);

        while(true) {
            // Compute the indices of the child elements.
            var child2N = (n + 1) << 1,
                child1N = child2N - 1;
            // This is used to store the new position of the element, if any.
            var swap = null,
                child1Score;
            // If the first child exists (is inside the array)...
            if (child1N < length) {
                // Look it up and compute its score.
                var child1 = this.content[child1N];
                child1Score = this.scoreFunction(child1);

                // If the score is less than our element's, we need to swap.
                if (child1Score < elemScore){
                    swap = child1N;
                }
            }

            // Do the same checks for the other child.
            if (child2N < length) {
                var child2 = this.content[child2N],
                    child2Score = this.scoreFunction(child2);
                if (child2Score < (swap === null ? elemScore : child1Score)) {
                    swap = child2N;
                }
            }

            // If the element needs to be moved, swap it, and continue.
            if (swap !== null) {
                this.content[n] = this.content[swap];
                this.content[swap] = element;
                n = swap;
            }
            // Otherwise, we are done.
            else {
                break;
            }
        }
    }
};

return {
    astar: astar,
    Graph: Graph
};

});
<|endoftext|>


<commit_before>// javascript-astar
// http://github.com/bgrins/javascript-astar
// Freely distributable under the MIT License.
// Implements the astar search algorithm in javascript using a binary heap.

var astar = {
    init: function(grid) {
        for(var x = 0, xl = grid.length; x < xl; x++) {
            for(var y = 0, yl = grid[x].length; y < yl; y++) {
                var node = grid[x][y];
                node.f = 0;
                node.g = 0;
                node.h = 0;
                node.cost = node.type;
                node.visited = false;
                node.closed = false;
                node.parent = null;
            }
        }
    },
    heap: function() {
        return new BinaryHeap(function(node) {
            return node.f;
        });
    },
    search: function(grid, start, end, diagonal, heuristic) {
        astar.init(grid);
        heuristic = heuristic || astar.manhattan;
        diagonal = !!diagonal;

        var openHeap = astar.heap();

        openHeap.push(start);

        while(openHeap.size() > 0) {

            // Grab the lowest f(x) to process next.  Heap keeps this sorted for us.
            var currentNode = openHeap.pop();

            // End case -- result has been found, return the traced path.
            if(currentNode === end) {
                var curr = currentNode;
                var ret = [];
                while(curr.parent) {
                    ret.push(curr);
                    curr = curr.parent;
                }
                return ret.reverse();
            }

            // Normal case -- move currentNode from open to closed, process each of its neighbors.
            currentNode.closed = true;

            // Find all neighbors for the current node. Optionally find diagonal neighbors as well (false by default).
            var neighbors = astar.neighbors(grid, currentNode, diagonal);

            for(var i=0, il = neighbors.length; i < il; i++) {
                var neighbor = neighbors[i];

                if(neighbor.closed || neighbor.isWall()) {
                    // Not a valid node to process, skip to next neighbor.
                    continue;
                }

                // The g score is the shortest distance from start to current node.
                // We need to check if the path we have arrived at this neighbor is the shortest one we have seen yet.
                var gScore = currentNode.g + neighbor.cost;
                var beenVisited = neighbor.visited;

                if(!beenVisited || gScore < neighbor.g) {

                    // Found an optimal (so far) path to this node.  Take score for node to see how good it is.
                    neighbor.visited = true;
                    neighbor.parent = currentNode;
                    neighbor.h = neighbor.h || heuristic(neighbor.pos, end.pos);
                    neighbor.g = gScore;
                    neighbor.f = neighbor.g + neighbor.h;

                    if (!beenVisited) {
                        // Pushing to heap will put it in proper place based on the 'f' value.
                        openHeap.push(neighbor);
                    }
                    else {
                        // Already seen the node, but since it has been rescored we need to reorder it in the heap
                        openHeap.rescoreElement(neighbor);
                    }
                }
            }
        }

        // No result was found - empty array signifies failure to find path.
        return [];
    },
    manhattan: function(pos0, pos1) {
        // See list of heuristics: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html

        var d1 = Math.abs (pos1.x - pos0.x);
        var d2 = Math.abs (pos1.y - pos0.y);
        return d1 + d2;
    },
    neighbors: function(grid, node, diagonals) {
        var ret = [];
        var x = node.x;
        var y = node.y;

        // West
        if(grid[x-1] && grid[x-1][y]) {
            ret.push(grid[x-1][y]);
        }

        // East
        if(grid[x+1] && grid[x+1][y]) {
            ret.push(grid[x+1][y]);
        }

        // South
        if(grid[x] && grid[x][y-1]) {
            ret.push(grid[x][y-1]);
        }

        // North
        if(grid[x] && grid[x][y+1]) {
            ret.push(grid[x][y+1]);
        }

        if (diagonals) {

            // Southwest
            if(grid[x-1] && grid[x-1][y-1]) {
                ret.push(grid[x-1][y-1]);
            }

            // Southeast
            if(grid[x+1] && grid[x+1][y-1]) {
                ret.push(grid[x+1][y-1]);
            }

            // Northwest
            if(grid[x-1] && grid[x-1][y+1]) {
                ret.push(grid[x-1][y+1]);
            }

            // Northeast
            if(grid[x+1] && grid[x+1][y+1]) {
                ret.push(grid[x+1][y+1]);
            }

        }

        return ret;
    }
};

<commit_msg>use umd for definition<commit_after>// javascript-astar
// http://github.com/bgrins/javascript-astar
// Freely distributable under the MIT License.
// Implements the astar search algorithm in javascript using a binary heap.

(function(definition) {
    if(typeof exports === 'object') {
        module.exports = definition();
    } else if(typeof define === 'function' && define.amd) {
        define([], definition);
    } else {
        window.astar = definition();
    }
})(function() {

var astar = {
    init: function(grid) {
        for(var x = 0, xl = grid.length; x < xl; x++) {
            for(var y = 0, yl = grid[x].length; y < yl; y++) {
                var node = grid[x][y];
                node.f = 0;
                node.g = 0;
                node.h = 0;
                node.cost = node.type;
                node.visited = false;
                node.closed = false;
                node.parent = null;
            }
        }
    },
    heap: function() {
        return new BinaryHeap(function(node) {
            return node.f;
        });
    },
    search: function(grid, start, end, diagonal, heuristic) {
        astar.init(grid);
        heuristic = heuristic || astar.manhattan;
        diagonal = !!diagonal;

        var openHeap = astar.heap();

        openHeap.push(start);

        while(openHeap.size() > 0) {

            // Grab the lowest f(x) to process next.  Heap keeps this sorted for us.
            var currentNode = openHeap.pop();

            // End case -- result has been found, return the traced path.
            if(currentNode === end) {
                var curr = currentNode;
                var ret = [];
                while(curr.parent) {
                    ret.push(curr);
                    curr = curr.parent;
                }
                return ret.reverse();
            }

            // Normal case -- move currentNode from open to closed, process each of its neighbors.
            currentNode.closed = true;

            // Find all neighbors for the current node. Optionally find diagonal neighbors as well (false by default).
            var neighbors = astar.neighbors(grid, currentNode, diagonal);

            for(var i=0, il = neighbors.length; i < il; i++) {
                var neighbor = neighbors[i];

                if(neighbor.closed || neighbor.isWall()) {
                    // Not a valid node to process, skip to next neighbor.
                    continue;
                }

                // The g score is the shortest distance from start to current node.
                // We need to check if the path we have arrived at this neighbor is the shortest one we have seen yet.
                var gScore = currentNode.g + neighbor.cost;
                var beenVisited = neighbor.visited;

                if(!beenVisited || gScore < neighbor.g) {

                    // Found an optimal (so far) path to this node.  Take score for node to see how good it is.
                    neighbor.visited = true;
                    neighbor.parent = currentNode;
                    neighbor.h = neighbor.h || heuristic(neighbor.pos, end.pos);
                    neighbor.g = gScore;
                    neighbor.f = neighbor.g + neighbor.h;

                    if (!beenVisited) {
                        // Pushing to heap will put it in proper place based on the 'f' value.
                        openHeap.push(neighbor);
                    }
                    else {
                        // Already seen the node, but since it has been rescored we need to reorder it in the heap
                        openHeap.rescoreElement(neighbor);
                    }
                }
            }
        }

        // No result was found - empty array signifies failure to find path.
        return [];
    },
    manhattan: function(pos0, pos1) {
        // See list of heuristics: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html

        var d1 = Math.abs (pos1.x - pos0.x);
        var d2 = Math.abs (pos1.y - pos0.y);
        return d1 + d2;
    },
    neighbors: function(grid, node, diagonals) {
        var ret = [];
        var x = node.x;
        var y = node.y;

        // West
        if(grid[x-1] && grid[x-1][y]) {
            ret.push(grid[x-1][y]);
        }

        // East
        if(grid[x+1] && grid[x+1][y]) {
            ret.push(grid[x+1][y]);
        }

        // South
        if(grid[x] && grid[x][y-1]) {
            ret.push(grid[x][y-1]);
        }

        // North
        if(grid[x] && grid[x][y+1]) {
            ret.push(grid[x][y+1]);
        }

        if (diagonals) {

            // Southwest
            if(grid[x-1] && grid[x-1][y-1]) {
                ret.push(grid[x-1][y-1]);
            }

            // Southeast
            if(grid[x+1] && grid[x+1][y-1]) {
                ret.push(grid[x+1][y-1]);
            }

            // Northwest
            if(grid[x-1] && grid[x-1][y+1]) {
                ret.push(grid[x-1][y+1]);
            }

            // Northeast
            if(grid[x+1] && grid[x+1][y+1]) {
                ret.push(grid[x+1][y+1]);
            }

        }

        return ret;
    }
};

return astar;

});
<|endoftext|>


<commit_before>import {
  depositDetails,
  borrowDetails,
  repayDetails,
  swapBorrowRateDetails,
  setUsageAsCollateralDetails,
  withdrawDetails
} from './graphQLHelpers.js';

import vuexStore from '@/core/store';
import { mapState, mapGetters } from 'vuex';
import {
  formatUserSummaryData,
  formatReserves,
  normalize
} from '@aave/protocol-js';
import moment from 'moment';
import BigNumber from 'bignumber.js';

const STABLE_COINS = ['TUSD', 'DAI', 'USDT', 'USDC', 'sUSD'];

export default class AaveHandler {
  constructor() {
    this.$store = vuexStore;
    Object.assign(this, mapState('wallet', ['web3', 'address']));
    Object.assign(this, mapGetters('wallet', ['tokensList', 'balanceInETH']));
    this.reservesData = [];
    this.rawReserveData = [];
    this.reservesStable = [];
    this.actionType = '';
    this.userReserveData = [];
    this.token = {};
    this.usdPriceEth = '';
    this.userSummary = {};
    this.rateHistory = { labels: [], stableRates: [], variableRates: [] };
    // this.pendingToken = {};
    this.compositionDeposit = [];
    this.compositionBorrow = [];
    this.compositionCollateral = [];
    this.percentageLeft = '';
  }

  sendTransaction(param) {
    if (param) {
      if (param.length > 1) {
        return this.web3().mew.sendBatchTransactions(param);
      }
      return this.web3().sendTransaction(param[0]);
    }
    return new Error('No Parameters sent!');
  }

  borrow(param) {
    param.referralCode = '14';
    try {
      return borrowDetails(param).then(res => {
        const txArr = [];
        res.data.borrow.forEach(data => {
          txArr.push(data.tx);
        });
        this.sendTransaction(txArr);
      });
    } catch (e) {
      throw new Error(e);
    }
  }

  deposit(param) {
    param.referralCode = '14';
    try {
      return depositDetails(param).then(res => {
        const txArr = [];
        res.data.deposit.forEach(data => {
          txArr.push(data.tx);
        });
        this.sendTransaction(txArr);
      });
    } catch (e) {
      throw new Error(e);
    }
  }

  withdraw(param) {
    try {
      return withdrawDetails(param).then(res => {
        const txArr = [];
        res.data.redeem.forEach(data => {
          txArr.push(data.tx);
        });
        this.sendTransaction(txArr);
      });
    } catch (e) {
      throw new Error(e);
    }
  }

  switchCollateral(param) {
    try {
      return setUsageAsCollateralDetails(param).then(res => {
        const txArr = [];
        res.data.setUsageAsCollateral.forEach(data => {
          txArr.push(data.tx);
        });

        this.sendTransaction(txArr);
      });
    } catch (e) {
      throw new Error(e);
    }
  }

  switchRate(param) {
    try {
      return swapBorrowRateDetails(param).then(res => {
        const txArr = [];
        res.data.swapBorrowRateMode.forEach(data => {
          txArr.push(data.tx);
        });

        this.sendTransaction(txArr);
      });
    } catch (e) {
      throw new Error(e);
    }
  }

  repay(param) {
    try {
      return repayDetails(param).then(res => {
        const txArr = [];
        res.data.repay.forEach(data => {
          txArr.push(data.tx);
        });

        this.sendTransaction(txArr);
      });
    } catch (e) {
      throw new Error(e);
    }
  }

  // aave calls

  getLiquidityRateHistoryUpdate(id, next) {
    this.aaveCalls.getLiquidityRateHistoryUpdate(id, next);
  }

  // setters (?)

  setFormatUserSummaryData() {
    if (
      this.reservesData.length > 0 &&
      this.userReserveData &&
      this.usdPriceEth
    ) {
      this.userSummary = formatUserSummaryData(
        this.rawReserveData,
        this.userReserveData,
        this.address().toLowerCase(),
        this.usdPriceEth,
        Number(moment().format('X'))
      );
      console.log(this.userSummary);
    }
  }

  mergeTheReserves() {
    if (this.userSummary.reservesData.length > 0) {
      this.userSummary.reservesData.forEach(data => {
        const foundReserve = this.reservesData.find(
          elem => elem.name === data.reserve.name
        );
        foundReserve.user = data;
      });
    }
    this.getReserveBalances();
  }

  getReserveBalances() {
    const utils = this.web3().utils;
    const accountBalance = utils.BN(this.balanceInETH());
    if (this.reservesData.length > 0) {
      this.reservesData.forEach(reserve => {
        reserve.tokenBalance = 0;
        reserve.user = !reserve.user ? {} : reserve.user;
        if (reserve.symbol === 'ETH') {
          reserve.tokenBalance = this.web3().utils.fromWei(
            accountBalance,
            'ether'
          );
        }

        const foundReserve = this.tokensList().find(
          elem => elem.symbol === reserve.symbol
        );
        if (foundReserve) {
          reserve.tokenBalance = foundReserve.balance;
        }

        if (this.reservesStable.length < 5) {
          if (STABLE_COINS.findIndex(coin => coin === reserve.symbol) >= 0) {
            this.reservesStable.push(reserve);
          }
        }
      });
    }
  }

  _liquidityRateHandler(res) {
    const data = res.data.userReserves;
    const rateHistory = { labels: [], stableRates: [], variableRates: [] };
    const rayDecimals = 27;
    const sortedData = data.sort((a, b) => a.timestamp - b.timestamp);
    sortedData.forEach(item => {
      const date = moment.unix(item.timestamp).format('MMM Do');
      rateHistory.labels.push(date);
      rateHistory.stableRates.push(
        new BigNumber(normalize(item.stableBorrowRate, rayDecimals))
          .times(100)
          .toFixed(2)
      );
      rateHistory.variableRates.push(
        new BigNumber(normalize(item.variableBorrowRate, rayDecimals))
          .times(100)
          .toFixed(2)
      );
    });

    this.rateHistory = rateHistory;
  }

  _usdPriceHandler(res) {
    const data = res.data.priceOracle;
    this.usdPriceEth = data;
    this.setFormatUserSummaryData();
  }

  _userDataHandler(res) {
    const data = res.data.userReserves;
    this.userReservData = data;
    this.setFormatUserSummaryData();
  }

  _reserveDataHandler(res) {
    const data = res.data.reserves;
    this.rawReserveData = data;
    this.reservesData = formatReserves(data).reverse();
    this.setFormatUserSummaryData();
  }
}
<commit_msg>devop: remove console<commit_after>import {
  depositDetails,
  borrowDetails,
  repayDetails,
  swapBorrowRateDetails,
  setUsageAsCollateralDetails,
  withdrawDetails
} from './graphQLHelpers.js';

import vuexStore from '@/core/store';
import { mapState, mapGetters } from 'vuex';
import {
  formatUserSummaryData,
  formatReserves,
  normalize
} from '@aave/protocol-js';
import moment from 'moment';
import BigNumber from 'bignumber.js';

const STABLE_COINS = ['TUSD', 'DAI', 'USDT', 'USDC', 'sUSD'];

export default class AaveHandler {
  constructor() {
    this.$store = vuexStore;
    Object.assign(this, mapState('wallet', ['web3', 'address']));
    Object.assign(this, mapGetters('wallet', ['tokensList', 'balanceInETH']));
    this.reservesData = [];
    this.rawReserveData = [];
    this.reservesStable = [];
    this.actionType = '';
    this.userReserveData = [];
    this.token = {};
    this.usdPriceEth = '';
    this.userSummary = {};
    this.rateHistory = { labels: [], stableRates: [], variableRates: [] };
    // this.pendingToken = {};
    this.compositionDeposit = [];
    this.compositionBorrow = [];
    this.compositionCollateral = [];
    this.percentageLeft = '';
  }

  sendTransaction(param) {
    if (param) {
      if (param.length > 1) {
        return this.web3().mew.sendBatchTransactions(param);
      }
      return this.web3().sendTransaction(param[0]);
    }
    return new Error('No Parameters sent!');
  }

  borrow(param) {
    param.referralCode = '14';
    try {
      return borrowDetails(param).then(res => {
        const txArr = [];
        res.data.borrow.forEach(data => {
          txArr.push(data.tx);
        });
        this.sendTransaction(txArr);
      });
    } catch (e) {
      throw new Error(e);
    }
  }

  deposit(param) {
    param.referralCode = '14';
    try {
      return depositDetails(param).then(res => {
        const txArr = [];
        res.data.deposit.forEach(data => {
          txArr.push(data.tx);
        });
        this.sendTransaction(txArr);
      });
    } catch (e) {
      throw new Error(e);
    }
  }

  withdraw(param) {
    try {
      return withdrawDetails(param).then(res => {
        const txArr = [];
        res.data.redeem.forEach(data => {
          txArr.push(data.tx);
        });
        this.sendTransaction(txArr);
      });
    } catch (e) {
      throw new Error(e);
    }
  }

  switchCollateral(param) {
    try {
      return setUsageAsCollateralDetails(param).then(res => {
        const txArr = [];
        res.data.setUsageAsCollateral.forEach(data => {
          txArr.push(data.tx);
        });

        this.sendTransaction(txArr);
      });
    } catch (e) {
      throw new Error(e);
    }
  }

  switchRate(param) {
    try {
      return swapBorrowRateDetails(param).then(res => {
        const txArr = [];
        res.data.swapBorrowRateMode.forEach(data => {
          txArr.push(data.tx);
        });

        this.sendTransaction(txArr);
      });
    } catch (e) {
      throw new Error(e);
    }
  }

  repay(param) {
    try {
      return repayDetails(param).then(res => {
        const txArr = [];
        res.data.repay.forEach(data => {
          txArr.push(data.tx);
        });

        this.sendTransaction(txArr);
      });
    } catch (e) {
      throw new Error(e);
    }
  }

  // aave calls

  getLiquidityRateHistoryUpdate(id, next) {
    this.aaveCalls.getLiquidityRateHistoryUpdate(id, next);
  }

  // setters (?)

  setFormatUserSummaryData() {
    if (
      this.reservesData.length > 0 &&
      this.userReserveData &&
      this.usdPriceEth
    ) {
      this.userSummary = formatUserSummaryData(
        this.rawReserveData,
        this.userReserveData,
        this.address().toLowerCase(),
        this.usdPriceEth,
        Number(moment().format('X'))
      );
    }
  }

  mergeTheReserves() {
    if (this.userSummary.reservesData.length > 0) {
      this.userSummary.reservesData.forEach(data => {
        const foundReserve = this.reservesData.find(
          elem => elem.name === data.reserve.name
        );
        foundReserve.user = data;
      });
    }
    this.getReserveBalances();
  }

  getReserveBalances() {
    const utils = this.web3().utils;
    const accountBalance = utils.BN(this.balanceInETH());
    if (this.reservesData.length > 0) {
      this.reservesData.forEach(reserve => {
        reserve.tokenBalance = 0;
        reserve.user = !reserve.user ? {} : reserve.user;
        if (reserve.symbol === 'ETH') {
          reserve.tokenBalance = this.web3().utils.fromWei(
            accountBalance,
            'ether'
          );
        }

        const foundReserve = this.tokensList().find(
          elem => elem.symbol === reserve.symbol
        );
        if (foundReserve) {
          reserve.tokenBalance = foundReserve.balance;
        }

        if (this.reservesStable.length < 5) {
          if (STABLE_COINS.findIndex(coin => coin === reserve.symbol) >= 0) {
            this.reservesStable.push(reserve);
          }
        }
      });
    }
  }

  _liquidityRateHandler(res) {
    const data = res.data.userReserves;
    const rateHistory = { labels: [], stableRates: [], variableRates: [] };
    const rayDecimals = 27;
    const sortedData = data.sort((a, b) => a.timestamp - b.timestamp);
    sortedData.forEach(item => {
      const date = moment.unix(item.timestamp).format('MMM Do');
      rateHistory.labels.push(date);
      rateHistory.stableRates.push(
        new BigNumber(normalize(item.stableBorrowRate, rayDecimals))
          .times(100)
          .toFixed(2)
      );
      rateHistory.variableRates.push(
        new BigNumber(normalize(item.variableBorrowRate, rayDecimals))
          .times(100)
          .toFixed(2)
      );
    });

    this.rateHistory = rateHistory;
  }

  _usdPriceHandler(res) {
    const data = res.data.priceOracle;
    this.usdPriceEth = data;
    this.setFormatUserSummaryData();
  }

  _userDataHandler(res) {
    const data = res.data.userReserves;
    this.userReservData = data;
    this.setFormatUserSummaryData();
  }

  _reserveDataHandler(res) {
    const data = res.data.reserves;
    this.rawReserveData = data;
    this.reservesData = formatReserves(data).reverse();
    this.setFormatUserSummaryData();
  }
}
<|endoftext|>


<commit_before>
function canHandleUrl(url) {
  return url.includes('itaubankline.itau.com.br')
}

function getTabularDataFromSelection(domSelectionRange) {
  // TODO: implement me
  throw new Error('TODO: implement me')
}

module.exports = {
  canHandleUrl: canHandleUrl,
  getTabularDataFromSelection: getTabularDataFromSelection
}
<commit_msg>itau-extractor implementation<commit_after>import $ from "jquery"

/**
 * Check if this extractor should be used based on the page URL.
 * @param  {string} url.
 * @return {boolean}
 */
function canHandleUrl(url) {
  return url.includes('itaubankline.itau.com.br')
}

/**
 * Extract tabular (rows and columns) from the selection.
 * @param  {DomSelectionRange} domSelectionRange - selection range.
 * @return {TabularData} Tabular data.
 * @throws {Error} If no tabular data was found in the selection.
 */
function getTabularDataFromSelection(domSelectionRange) {
  try {
    let start = $(domSelectionRange.start)
    let end = $(domSelectionRange.end)

    let firstRow = start.closest('tr')
    let lastRow = end.closest('tr')

    if(lastRow.index() < firstRow.index()) {
      let temp = lastRow
      lastRow = firstRow
      firstRow = temp
    }

    let lastRowEl = lastRow.get(0)
    let tabularData = []
    let row = firstRow
    while(row && row.get(0)) {
      tabularData.push(getTabularDataFromRow(row))
      if(row.get(0) === lastRowEl) {
        break
      }
      row = row.next()
    }

    return {
      header: ['date', 'payee', 'value'],
      data: tabularData
    }
  } catch (any) {
    throw Error('Could not find tabular data.')
  }
}
/**
 * Extract tabular from a table row.
 * @param {jQuery} row - Table row.
 * @return {string[]} Tabular data.
 */
function getTabularDataFromRow(row) {
  let date = row.find('td:first-child').text().trim()
  let payee = row.find('td:nth-child(4)').text().trim()
  let value = row.find('td:nth-child(6)').text().trim()
  let signal = row.find('td:nth-child(7)').text().trim()
  return [date, payee, `${signal}${value}`]
}

module.exports = {
  canHandleUrl: canHandleUrl,
  getTabularDataFromSelection: getTabularDataFromSelection
}
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

import PropTypes from "prop-types";
import ReactAudioPlayer from 'react-audio-player';
import axios from "axios";

class AvailableDatalist extends React.Component {

  render() {
    return(

{this.props.songs[0]['name']}

    )
  }
}

AvailableDatalist.propTypes = {
  songs: PropTypes.array.isRequired
};

class SongBoard extends React.Component {
state = {
  songs: [0:{name:"none", album:"none"}]
};

componentDidMount() {
  axios
  .get("http://192.168.1.107:8000/song/")
  .then(response => {
    const songs = response.data.map(c => {
      return{
        name: c.name,
        album: c.album
      };
    });

    const newState = Object.assign({}, this.state, {
          songs: songs
        });

    this.setState(newState);
    console.log(this.state.songs[0])

  })
  .catch(error => console.log(error));
}

  render() {

    return(

            <button className="song">
              Song
            </button>
            <button className="album">
              Album
            </button>

          <AvailableDatalist songs={this.state.songs}/> {/*songs={this.state.contacts}*/}

    )
  }
}

class Player extends React.Component {
  render() {
    return(
      <ReactAudioPlayer
        src="my_audio_file.ogg"
        autoPlay
        controls
      />
    )
  }
}

class Site extends React.Component {
  render() {
    return (

          <Player />

          <SongBoard />

    )
  }
}

ReactDOM.render(
  <Site />,
  document.getElementById('root')
);
<commit_msg>songs<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

import PropTypes from "prop-types";
import ReactAudioPlayer from 'react-audio-player';
import axios from "axios";

class AvailableDatalist extends React.Component {

  render() {
    var songArray = []
    for (var i = 0; i < this.props.songs.length; i++) {
      songArray.push(

{this.props.songs[i]['name']}

      )
    }

        return({songArray})
  }
}

AvailableDatalist.propTypes = {
  songs: PropTypes.array.isRequired
};

class SongBoard extends React.Component {
state = {
  songs: [0:{name:"none", album:"none"}]
};

componentDidMount() {
  axios
  .get("http://192.168.1.107:8000/song/")
  .then(response => {
    const songs = response.data.map(c => {
      return{
        name: c.name,
        album: c.album
      };
    });

    const newState = Object.assign({}, this.state, {
          songs: songs
        });

    this.setState(newState);
    console.log(this.state.songs[0])

  })
  .catch(error => console.log(error));
}

  render() {

    return(

            <button className="song">
              Song
            </button>
            <button className="album">
              Album
            </button>

          <AvailableDatalist songs={this.state.songs}/> {/*songs={this.state.contacts}*/}

    )
  }
}

class Player extends React.Component {
  render() {
    return(
      <ReactAudioPlayer
        src="my_audio_file.ogg"
        autoPlay
        controls
      />
    )
  }
}

class Site extends React.Component {
  render() {
    return (

          <Player />

          <SongBoard />

    )
  }
}

ReactDOM.render(
  <Site />,
  document.getElementById('root')
);
<|endoftext|>


<commit_before>
import Ember from 'ember'
import layout from './template'
import PropTypeMixin, { PropTypes } from 'ember-prop-types'

const { K } = Ember

export default Ember.Component.extend(PropTypeMixin, {
  layout,

  propTypes: {
    onOutsideClick: PropTypes.func
  },

  getDefaultProps() {
    return { onOutsideClick: K }
  },

  init() {
    this._super(...arguments)
    this.handle = this.handle.bind(this)
  },

  didInsertElement() {
    this._super(...arguments)
    document.addEventListener('click', this.handle, true)
  },

  willDestroyElement() {
    this._super(...arguments)
    document.removeEventListener('click', this.handle, true)
  },

  handle(e) {
    const el = this.$()[0];
    if (!el.contains(e.target)) this.get('onOutsideClick')(e)
  }
})

<commit_msg>should only call action when mousedown is outside<commit_after>
import Ember from 'ember'
import layout from './template'
import PropTypeMixin, { PropTypes } from 'ember-prop-types'

const { K, $ } = Ember

export default Ember.Component.extend(PropTypeMixin, {
  layout,

  propTypes: {
    onOutsideClick: PropTypes.func
  },

  getDefaultProps() {
    return { onOutsideClick: K }
  },

  init() {
    this._super(...arguments)
    this.handleDown = this.handleDown.bind(this)
    this.handleUp = this.handleUp.bind(this)
  },

  didInsertElement() {
    this._super(...arguments)
    document.addEventListener('mousedown', this.handleDown, true)
    document.addEventListener('mouseup', this.handleUp, true)
  },

  willDestroyElement() {
    this._super(...arguments)
    document.removeEventListener('mousedown', this.handleDown, true)
    document.removeEventListener('mouseup', this.handleUp, true)
  },

  isOutside: false,

  handleDown(e) {
    const el = this.$()[0];
    if (!el.contains(e.target)) this.set('isOutside', true)
  },

  handleUp(e) {
    if (this.get('isOutside')) this.get('onOutsideClick')(e)
    this.set('isOutside', false)
  }
})

<|endoftext|>


<commit_before>/*jshint esversion: 6 */

function changeFavicon(img) {
    var favicon = document.querySelector('link[rel="shortcut icon"]');

    if (!favicon) {
        favicon = document.createElement('link');
        favicon.setAttribute('rel', 'shortcut icon');
        var head = document.querySelector('head');
        head.appendChild(favicon);
    }

    favicon.setAttribute('type', 'image/png');
    favicon.setAttribute('href', img);
}

var hexas = [
  '7200FF',
  '0062FF',
  'FF3992',
  'DEA300',
  '00FFE4',
  '8A1E6E',
  'E43162',
  '1E2F8A'
];

var oks = [
  '👌', '👌🏻', '👌🏼', '👌🏽', '👌🏾', '👌🏿'
];
var currentRandom = Math.floor(Math.random() * oks.length);
var newOk = oks[currentRandom];
var ok = $('.side-headline').find('span');
ok.text(newOk);
changeFavicon('img/favicon'+currentRandom+'.png');

var currentHexa = 0;
var showEmployed = 1;

var delay = 0;

var isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);

function shuffle(a) {
  var j,
    x,
    i;
  for (i = a.length; i; i--) {
    j = Math.floor(Math.random() * i);
    x = a[i - 1];
    a[i - 1] = a[j];
    a[j] = x;
  }
}

var item = `

Website:

Worked with:

Currently:

Contact:

Show email

`;

function moveInsideLeft() {
  $('.inside').removeClass('insideright').addClass('insideleft');
}

function moveInsideRight() {
  $('.inside').removeClass('insideleft').addClass('insideright');
}

function getRandomGradient() {
  return 'linear-gradient(224deg, ' + getnextHexa() + ' 0%, rgba(9,17,28,0.00) 100%)';
}

function getnextHexa() {
  if (currentHexa === hexas.length - 1) {
    currentHexa = 0;
    return '#' + hexas[hexas.length - 1];
  } else {
    currentHexa++;
    return '#' + hexas[currentHexa - 1];
  }
}

function generateGradient() {
  return "#7200FF";
}

function createNewDiv(image, name, website, companies, status, email) {
  var div = document.createElement('div');
  var a = document.createElement('a');

  div.className = "item";
  div.innerHTML = item;

  var newName = div.getElementsByClassName("name")[0];
  var newWebsite = div.getElementsByClassName("website")[0].getElementsByTagName("a")[0];
  var newCompanies = div.getElementsByClassName("companies")[0].getElementsByTagName("p")[0];
  var newStatus = div.getElementsByClassName("status")[0].getElementsByTagName("p")[0];
  var newContact = div.getElementsByClassName("contact")[0].getElementsByClassName("email")[0];
  var newOverlay = div.getElementsByClassName("overlay")[0];

  //Name
  newName.append(name);

  //Website
  a.href = "http://" +website;
  $(a).addClass("linkOfItem");
  a.target = "_blank";
  newWebsite.append(website);
  // newWebsite.href = newWebsite;

  //Companies
  newCompanies.append(companies);

  //Current Status
  if (status === 'employed') {
    newStatus.append("Happily Employed 💼");
  } else if (status === 'freelance') {
    newStatus.append("Freelance 🎨");
  }
  else if (status === 'busyfreelance') {
    newStatus.append("Freelance, but busy 🏋");
  }
  else {
    newStatus.append("Looking for Opportunities 🖼");
  }

  //Email
  newContact.append(email);
  newContact.href = "mailto:" + email;

  //Done
  $(a).append(div);
  $('.items').append(a);

  //Background image + gradient

  if (isMobile){
    newBackground = '#2F3236';
  }
  else{
    newBackground = getRandomGradient() + ", url('img/designers/" + image + "') no-repeat center";
  }
  $(newOverlay).css("background", newBackground);
}

// createNewDiv("0002.gif", "Ariel Verber", "www.arielverber.com", "YO", "employed", "hi@arielverber.com");
// createNewDiv("0001.gif", "Artiom Dashinsky", "www.arielverber.com", "YO", "freelance", "hi@arielverber.com");

var list = [
  {
    img: "tevihirshhorn.jpg",
    name: "Tevi Hirschhorn",
    website: "www.tevster.com",
    companies: "-",
    current: "employed",
    contact: "tevi@tevster.com"
  }, {
    img: "liatabadi.jpg",
    name: "Liat Abadi",
    website: "abadiguard.com",
    companies: "Better Place, JPEGmini, Unomy",
    current: "employed",
    contact: "liat@abadiguard.com"
  }, {
    img: "hilapeleg.gif",
    name: "Hila Peleg",
    website: "www.behance.net/hilapeleg",
    companies: "Inkod Hypera ltd, Slidely, Playbuzz",
    current: "employed",
    contact: "hilapeleg1984@gmail.com "
  }, {
    img: "eyalzuri.png",
    name: "Eyal Zuri",
    website: "dribbble.com/eyalz",
    companies: "Muzli, InVision, Netcraft",
    current: "employed",
    contact: "zurieyal@gmail.com"
  }, {
    img: "robinkane.gif",
    name: "Robin Kane",
    website: "www.robinkane-art-house.com/",
    companies: "WIX, Horizon 2020, European Union",
    current: "employed",
    contact: "robinkane.art@gmail.com"
  }, {
    img: "edenvidal.png",
    name: "Eden Vidal",
    website: "edenvidal.com",
    companies: "YOTPO, Alooma, Zebra Medical",
    current: "freelance",
    contact: "edenvidal@gmail.com"
  }, {
    img: "lironcohen.png",
    name: "Liron Cohen",
    website: "lironic-design.com",
    companies: "BlueVine. Armis. Culture trip",
    current: "freelance",
    contact: "lironicdesign@gmail.com"
  }, {
    img: "adammorad.png",
    name: "Adam Morad",
    website: "dribbble.com/Adammorad",
    companies: "Netvertise, Yotpo, Wisdo",
    current: "employed",
    contact: "Adammorad7@gmail.com"
  }, {
    img: "roihiman.png",
    name: "Roi Himan",
    website: "roi-himan.com",
    companies: "Wix",
    current: "employed",
    contact: "roihim@gmail.com"
  }, {
    img: "izikrachman.png",
    name: "Izik Rachman",
    website: "www.izikrachman.com",
    companies: "Fiverr, Intel",
    current: "freelance",
    contact: "izik.rachman@gmail.com"
  }, {
    img: "nathanfreifeld.png",
    name: "Nathan Freifeld",
    website: "www.nathanfreifeld.com/",
    companies: "Mobilize, Spot.IM, Saatchi&Saatchi",
    current: "employed",
    contact: "nfreifeld@gmail.com"
  }, {
    img: "limorpeleg.png",
    name: "Limor Peleg",
    website: "www.behance.net/peleglimor0aeb",
    companies: "Pionet Group, JFrog, Kaminario",
    current: "freelance",
    contact: "peleg.limor@gmail.com"
  }, {
    img: "alextrue.jpg",
    name: "Alex True",
    website: "alex-true.com",
    companies: "Publicis Digital, Moburst and Freelance",
    current: "unemployed",
    contact: "trublenkov@gmail.com"
  }, {
    img: "omerfrank.png",
    name: "Omer Frank",
    website: "mrfrank.me",
    companies: "Waves, Polyverse",
    current: "freelance",
    contact: "omer@frankscreative.com"
  }, {
    img: "eladnaider.png",
    name: "Elad Naider",
    website: "dribbble.com/eladnaider",
    companies: "HP, SaferVPN",
    current: "employed",
    contact: "eladnaider@gmail.com"
  }, {
    img: "omerassa.gif",
    name: "Omer Assa",
    website: "dribbble.com/Omerico",
    companies: "Mreshet, Zeek, torii",
    current: "employed",
    contact: "omer@jointorii.co"
  }, {
    img: "artiomdashinsky.gif",
    name: "Artiom Dashinsky",
    website: "www.dashinsky.com",
    companies: "WeWork, InVision, SodaStream",
    current: "freelance",
    contact: "hvoostik@gmail.com"
  }, {
    img: "galshir.gif",
    name: "Gal Shir",
    website: "www.galshir.com",
    companies: "Lemonade",
    current: "employed",
    contact: "hello@galshir.com"
  },

  // {
  //   img: "arielverber.png",
  //   name: "Ariel Verber",
  //   website: "www.arielverber.com",
  //   companies: "Waze, Mobilize",
  //   current: "freelance",
  //   contact: "hi@arielverber.com"
  // },

  {
    img: "valburtakov.gif",
    name: "Val Burtakov",
    website: "daskulll.tumblr.com",
    companies: "YOTPO, eBay, Purple",
    current: "employed",
    contact: "valentinburtakov@gmail.com"
  },

  {
    img: "marinasokolov.png",
    name: "Marina Sokolov",
    website: "www.marinzart.com",
    companies: "Juno, eToro, Spot.IM",
    current: "employed",
    contact: "marinz.box@gmail.com"
  },
  {
    img: "urizur.jpg",
    name: "Uri Zur",
    website: "www.urizur.com",
    companies: "Sears, Waze, Zlango",
    current: "employed",
    contact: "uriandzur@gmail.com"
  },
  {
    img: "ransegall.png",
    name: "Ran Segall",
    website: "www.ransegall.com",
    companies: "Prospero, any.do, Nexar",
    current: "busyfreelance",
    contact: "ransegall@gmail.com"
  },
  {
    img: "sagischreiber.png",
    name: "Sagi Shrieber",
    website: "www.instagram.com/sagishrieber",
    companies: "Fiverr, SimilarWeb, Ebay",
    current: "freelance",
    contact: "sagishrieber@gmail.com"
  },
  {
    img: "avichaybaras.png",
    name: "Avichay Baras",
    website: "baras.co",
    companies: "Wix, Netcraft, Rollout",
    current: "unemployed",
    contact: "avichai@baras.co"
  },
  {
    img: "naorhazan.png",
    name: "Naor Hazan",
    website: "www.linkedin.com/in/naor-hazan-434069105/",
    companies: "Woo, glide, flok",
    current: "employed",
    contact: "Naorhazan22@gmail.com"
  }
];

function toggleEmployed() {
  if (showEmployed === 0) {
    showEmployed = 1;
    $('.border').removeClass("faded");
    $('.toggle').removeClass("faded");
    moveInsideRight();
    ga('send', 'event', 'Toggle', 'Toggle Off', '1');
  } else {
    showEmployed = 0;
    $('.border').addClass("faded");
    $('.toggle').addClass("faded");
    moveInsideLeft();
    ga('send', 'event', 'Toggle', 'Toggle On', '2');
  }
  loadElements();

  //ga('send', 'event', [eventCategory], [eventAction], [eventLabel], [eventValue], [fieldsObject]);

}

shuffle(list);

function loadElements() {
  delay = 0;
  var j = 0;
  document.getElementsByClassName('items')[0].innerHTML = "";
  var current = "";
  if (showEmployed !== 0) {
    for (i = 0; i <= list.length - 1; i++) {
      current = list[i];
      createNewDiv(current.img, current.name, current.website, current.companies, current.current, current.contact);
      $('.numberofdesigners').html(i + 1);
    }
  } else {
    for (i = 0; i <= list.length - 1; i++) {
      current = list[i];
      if (current.current !== "employed") {
        createNewDiv(current.img, current.name, current.website, current.companies, current.current, current.contact);
        j++;
        $('.numberofdesigners').html(j);
      }
    }
  }

  $('.showemail').on("click", function(e){
    e.preventDefault();
    ga('send', 'event', 'Email Request', 'Email Requested', '3');
    var email = $(this).next();
    $(this).fadeOut(function(){
      email.fadeIn();
    });
  });

}

loadElements();

// $('.item').click()
<commit_msg>fixed avichais name<commit_after>/*jshint esversion: 6 */

function changeFavicon(img) {
    var favicon = document.querySelector('link[rel="shortcut icon"]');

    if (!favicon) {
        favicon = document.createElement('link');
        favicon.setAttribute('rel', 'shortcut icon');
        var head = document.querySelector('head');
        head.appendChild(favicon);
    }

    favicon.setAttribute('type', 'image/png');
    favicon.setAttribute('href', img);
}

var hexas = [
  '7200FF',
  '0062FF',
  'FF3992',
  'DEA300',
  '00FFE4',
  '8A1E6E',
  'E43162',
  '1E2F8A'
];

var oks = [
  '👌', '👌🏻', '👌🏼', '👌🏽', '👌🏾', '👌🏿'
];
var currentRandom = Math.floor(Math.random() * oks.length);
var newOk = oks[currentRandom];
var ok = $('.side-headline').find('span');
ok.text(newOk);
changeFavicon('img/favicon'+currentRandom+'.png');

var currentHexa = 0;
var showEmployed = 1;

var delay = 0;

var isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);

function shuffle(a) {
  var j,
    x,
    i;
  for (i = a.length; i; i--) {
    j = Math.floor(Math.random() * i);
    x = a[i - 1];
    a[i - 1] = a[j];
    a[j] = x;
  }
}

var item = `

Website:

Worked with:

Currently:

Contact:

Show email

`;

function moveInsideLeft() {
  $('.inside').removeClass('insideright').addClass('insideleft');
}

function moveInsideRight() {
  $('.inside').removeClass('insideleft').addClass('insideright');
}

function getRandomGradient() {
  return 'linear-gradient(224deg, ' + getnextHexa() + ' 0%, rgba(9,17,28,0.00) 100%)';
}

function getnextHexa() {
  if (currentHexa === hexas.length - 1) {
    currentHexa = 0;
    return '#' + hexas[hexas.length - 1];
  } else {
    currentHexa++;
    return '#' + hexas[currentHexa - 1];
  }
}

function generateGradient() {
  return "#7200FF";
}

function createNewDiv(image, name, website, companies, status, email) {
  var div = document.createElement('div');
  var a = document.createElement('a');

  div.className = "item";
  div.innerHTML = item;

  var newName = div.getElementsByClassName("name")[0];
  var newWebsite = div.getElementsByClassName("website")[0].getElementsByTagName("a")[0];
  var newCompanies = div.getElementsByClassName("companies")[0].getElementsByTagName("p")[0];
  var newStatus = div.getElementsByClassName("status")[0].getElementsByTagName("p")[0];
  var newContact = div.getElementsByClassName("contact")[0].getElementsByClassName("email")[0];
  var newOverlay = div.getElementsByClassName("overlay")[0];

  //Name
  newName.append(name);

  //Website
  a.href = "http://" +website;
  $(a).addClass("linkOfItem");
  a.target = "_blank";
  newWebsite.append(website);
  // newWebsite.href = newWebsite;

  //Companies
  newCompanies.append(companies);

  //Current Status
  if (status === 'employed') {
    newStatus.append("Happily Employed 💼");
  } else if (status === 'freelance') {
    newStatus.append("Freelance 🎨");
  }
  else if (status === 'busyfreelance') {
    newStatus.append("Freelance, but busy 🏋");
  }
  else {
    newStatus.append("Looking for Opportunities 🖼");
  }

  //Email
  newContact.append(email);
  newContact.href = "mailto:" + email;

  //Done
  $(a).append(div);
  $('.items').append(a);

  //Background image + gradient

  if (isMobile){
    newBackground = '#2F3236';
  }
  else{
    newBackground = getRandomGradient() + ", url('img/designers/" + image + "') no-repeat center";
  }
  $(newOverlay).css("background", newBackground);
}

// createNewDiv("0002.gif", "Ariel Verber", "www.arielverber.com", "YO", "employed", "hi@arielverber.com");
// createNewDiv("0001.gif", "Artiom Dashinsky", "www.arielverber.com", "YO", "freelance", "hi@arielverber.com");

var list = [
  {
    img: "tevihirshhorn.jpg",
    name: "Tevi Hirschhorn",
    website: "www.tevster.com",
    companies: "-",
    current: "employed",
    contact: "tevi@tevster.com"
  }, {
    img: "liatabadi.jpg",
    name: "Liat Abadi",
    website: "abadiguard.com",
    companies: "Better Place, JPEGmini, Unomy",
    current: "employed",
    contact: "liat@abadiguard.com"
  }, {
    img: "hilapeleg.gif",
    name: "Hila Peleg",
    website: "www.behance.net/hilapeleg",
    companies: "Inkod Hypera ltd, Slidely, Playbuzz",
    current: "employed",
    contact: "hilapeleg1984@gmail.com "
  }, {
    img: "eyalzuri.png",
    name: "Eyal Zuri",
    website: "dribbble.com/eyalz",
    companies: "Muzli, InVision, Netcraft",
    current: "employed",
    contact: "zurieyal@gmail.com"
  }, {
    img: "robinkane.gif",
    name: "Robin Kane",
    website: "www.robinkane-art-house.com/",
    companies: "WIX, Horizon 2020, European Union",
    current: "employed",
    contact: "robinkane.art@gmail.com"
  }, {
    img: "edenvidal.png",
    name: "Eden Vidal",
    website: "edenvidal.com",
    companies: "YOTPO, Alooma, Zebra Medical",
    current: "freelance",
    contact: "edenvidal@gmail.com"
  }, {
    img: "lironcohen.png",
    name: "Liron Cohen",
    website: "lironic-design.com",
    companies: "BlueVine. Armis. Culture trip",
    current: "freelance",
    contact: "lironicdesign@gmail.com"
  }, {
    img: "adammorad.png",
    name: "Adam Morad",
    website: "dribbble.com/Adammorad",
    companies: "Netvertise, Yotpo, Wisdo",
    current: "employed",
    contact: "Adammorad7@gmail.com"
  }, {
    img: "roihiman.png",
    name: "Roi Himan",
    website: "roi-himan.com",
    companies: "Wix",
    current: "employed",
    contact: "roihim@gmail.com"
  }, {
    img: "izikrachman.png",
    name: "Izik Rachman",
    website: "www.izikrachman.com",
    companies: "Fiverr, Intel",
    current: "freelance",
    contact: "izik.rachman@gmail.com"
  }, {
    img: "nathanfreifeld.png",
    name: "Nathan Freifeld",
    website: "www.nathanfreifeld.com/",
    companies: "Mobilize, Spot.IM, Saatchi&Saatchi",
    current: "employed",
    contact: "nfreifeld@gmail.com"
  }, {
    img: "limorpeleg.png",
    name: "Limor Peleg",
    website: "www.behance.net/peleglimor0aeb",
    companies: "Pionet Group, JFrog, Kaminario",
    current: "freelance",
    contact: "peleg.limor@gmail.com"
  }, {
    img: "alextrue.jpg",
    name: "Alex True",
    website: "alex-true.com",
    companies: "Publicis Digital, Moburst and Freelance",
    current: "unemployed",
    contact: "trublenkov@gmail.com"
  }, {
    img: "omerfrank.png",
    name: "Omer Frank",
    website: "mrfrank.me",
    companies: "Waves, Polyverse",
    current: "freelance",
    contact: "omer@frankscreative.com"
  }, {
    img: "eladnaider.png",
    name: "Elad Naider",
    website: "dribbble.com/eladnaider",
    companies: "HP, SaferVPN",
    current: "employed",
    contact: "eladnaider@gmail.com"
  }, {
    img: "omerassa.gif",
    name: "Omer Assa",
    website: "dribbble.com/Omerico",
    companies: "Mreshet, Zeek, torii",
    current: "employed",
    contact: "omer@jointorii.co"
  }, {
    img: "artiomdashinsky.gif",
    name: "Artiom Dashinsky",
    website: "www.dashinsky.com",
    companies: "WeWork, InVision, SodaStream",
    current: "freelance",
    contact: "hvoostik@gmail.com"
  }, {
    img: "galshir.gif",
    name: "Gal Shir",
    website: "www.galshir.com",
    companies: "Lemonade",
    current: "employed",
    contact: "hello@galshir.com"
  },

  // {
  //   img: "arielverber.png",
  //   name: "Ariel Verber",
  //   website: "www.arielverber.com",
  //   companies: "Waze, Mobilize",
  //   current: "freelance",
  //   contact: "hi@arielverber.com"
  // },

  {
    img: "valburtakov.gif",
    name: "Val Burtakov",
    website: "daskulll.tumblr.com",
    companies: "YOTPO, eBay, Purple",
    current: "employed",
    contact: "valentinburtakov@gmail.com"
  },

  {
    img: "marinasokolov.png",
    name: "Marina Sokolov",
    website: "www.marinzart.com",
    companies: "Juno, eToro, Spot.IM",
    current: "employed",
    contact: "marinz.box@gmail.com"
  },
  {
    img: "urizur.jpg",
    name: "Uri Zur",
    website: "www.urizur.com",
    companies: "Sears, Waze, Zlango",
    current: "employed",
    contact: "uriandzur@gmail.com"
  },
  {
    img: "ransegall.png",
    name: "Ran Segall",
    website: "www.ransegall.com",
    companies: "Prospero, any.do, Nexar",
    current: "busyfreelance",
    contact: "ransegall@gmail.com"
  },
  {
    img: "sagischreiber.png",
    name: "Sagi Shrieber",
    website: "www.instagram.com/sagishrieber",
    companies: "Fiverr, SimilarWeb, Ebay",
    current: "freelance",
    contact: "sagishrieber@gmail.com"
  },
  {
    img: "avichaybaras.png",
    name: "Avichai Baras",
    website: "baras.co",
    companies: "Wix, Netcraft, Rollout",
    current: "unemployed",
    contact: "avichai@baras.co"
  },
  {
    img: "naorhazan.png",
    name: "Naor Hazan",
    website: "www.linkedin.com/in/naor-hazan-434069105/",
    companies: "Woo, glide, flok",
    current: "employed",
    contact: "Naorhazan22@gmail.com"
  }
];

function toggleEmployed() {
  if (showEmployed === 0) {
    showEmployed = 1;
    $('.border').removeClass("faded");
    $('.toggle').removeClass("faded");
    moveInsideRight();
    ga('send', 'event', 'Toggle', 'Toggle Off', '1');
  } else {
    showEmployed = 0;
    $('.border').addClass("faded");
    $('.toggle').addClass("faded");
    moveInsideLeft();
    ga('send', 'event', 'Toggle', 'Toggle On', '2');
  }
  loadElements();

  //ga('send', 'event', [eventCategory], [eventAction], [eventLabel], [eventValue], [fieldsObject]);

}

shuffle(list);

function loadElements() {
  delay = 0;
  var j = 0;
  document.getElementsByClassName('items')[0].innerHTML = "";
  var current = "";
  if (showEmployed !== 0) {
    for (i = 0; i <= list.length - 1; i++) {
      current = list[i];
      createNewDiv(current.img, current.name, current.website, current.companies, current.current, current.contact);
      $('.numberofdesigners').html(i + 1);
    }
  } else {
    for (i = 0; i <= list.length - 1; i++) {
      current = list[i];
      if (current.current !== "employed") {
        createNewDiv(current.img, current.name, current.website, current.companies, current.current, current.contact);
        j++;
        $('.numberofdesigners').html(j);
      }
    }
  }

  $('.showemail').on("click", function(e){
    e.preventDefault();
    ga('send', 'event', 'Email Request', 'Email Requested', '3');
    var email = $(this).next();
    $(this).fadeOut(function(){
      email.fadeIn();
    });
  });

}

loadElements();

// $('.item').click()
<|endoftext|>


<commit_before>let helpers = require(`${__dirname}/../helpers.js`);
let db = require(`${__dirname}/../schemas.js`);
let Recipe = db.Recipe;
let Follow = db.Follow;
let User = db.User;
let Notification = db.Notification;
let _ = require('underscore');
let Promise = require('bluebird')

module.exports = {
  // description: Returns a list of all followed users
  // params: {
  //   username: username of the person following the users
  // }
  getFollowedUsers: (req ,res) => {
    return Follow.findOne({
      username: req.params.username
    }).then(follows => {
      res.status(200).send(follows.users);
    }).catch(error => {
      console.log('ERROR: ', error);
      res.status(200).send([]);
    });
  },

  // description: Returns a list of all notifications for a user
  // params: {
  //   username: username of owner of the notifications,
  // }
  getNotifications: (req, res) => {
    return Notification.find({
      username: req.params.username
    }).then(notifications => {
      res.status(200).send(notifications);
    }).catch(error => {
      console.log('error: ', error);
      res.status(500).send();
    });
  },

  // description: Follows a user
  // params: {
  //   username: username of person following another
  //   user: username of the person being followed
  // }
  followUser: (req, res) => {
    let findFollow = Follow.findOne({
      username: req.params.username
    });
    let findUser = User.findOne({
      username: req.params.user
    });
    let notifyUser = helpers.createNotification({
      notificationOwner: req.params.user,
      username: req.params.username,
      text: `${req.params.username} started following you.`
    });

    return Promise.all([findFollow, findUser, notifyUser])
    .spread((follow, user, notification) => {
      let followers = user.followers;
      followers++;
      let updateFollowers = User.update({
        username: req.params.user
      }, {
        followers: followers
      });

      if (!follow) {
        return new Follow({
          username: req.params.username,
          users: [req.params.user]
        }).save()
        .then(() => {
          return updateFollowers;
        }).then(() => {
          res.status(201).send();
        });

      } else {
        let users = follow.users;
        users.push(req.params.user);

        return Follow.update({
          username: req.params.username
        }, {
          users: users
        }).then(() => {
          return updateFollowers;
        }).then(() => {
          res.status(200).send();
        });
      }

    }).catch(error => {
      console.log('error: ', error);
      res.status(500).send(error);
    });
  },

  // description: Follows a recipe
  // params: {
  //   username: username of the person following a recipe
  //   user: username of the person the recipe belongs to
  //   recipe: root recipe id of the recipe being followed
  // }
  followRecipe: (req, res) => {
    return Recipe.findOne({
      _id: req.params.recipe,
      username: req.params.user
    }).then(recipe => {
      let findFollow = Follow.findOne({
        username: req.params.username
      });
      let notifyUser = helpers.createNotification({
        notificationOwner: req.params.user,
        username: req.params.username,
        recipeId: req.params.recipe,
        text: `${req.params.username} followed your recipe ${recipe.name.value}.`
      });

      return Promise.all([findFollow, helpers.updateRecipeFollowers(req.params.user, req.params.recipe, 1)], notifyUser);
    }).spread((follow, followerCountUpdate, notification) => {

      if (!follow) {
        return new Follow({
          username: req.params.username,
          recipes: [{
            username: req.params.user,
            recipeId: req.params.recipe
          }]
        }).save().then(() => {
          res.status(201).send();
        });

      } else {
        let recipes = follow.recipes;
        recipes.push({
          username: req.params.user,
          recipeId: req.params.recipe
        });

        return Follow.update({
          username: req.params.username
        }, {
          recipes: recipes
        }).then(() => {
          res.status(200).send();
        });
      }
    }).catch(error => {
      console.log('error: ', error);
      res.status(500).send(error);
    });
  },

  // description: Unfollows a user
  // params: {
  //   username: username of person following another
  //   user: username of the person being followed
  // }
  unfollowUser: (req, res) => {
    let findFollow = Follow.findOne({
      username: req.params.username
    })
    let findUser = User.findOne({
      username: req.params.user
    });

    return Promise.all([findFollow, findUser])
    .spread((follow, user) => {
      let followers = user.followers;
      if (followers > 0) {
        followers--;
      }
      let updateFollowerCount = User.update({
        username: req.params.user
      }, {
        followers: followers
      });

      let userIndex = _.findIndex(follow.users, user => {
        return user === req.params.user;
      });
      let users = follow.users;
      users.splice(userIndex, 1);

      let updateFollowers = Follow.update({
        username: req.params.username
      }, {
        users: users
      });

      return Promise.all([updateFollowers, updateFollowerCount]);
    }).spread(() => {
      res.status(200).send();
    }).catch(error => {
      console.log('error: ', error);
      res.status(500).send();
    });
  },

  // description: Unfollows a Recipe
  // params: {
  //   username: username of the person following a recipe
  //   user: username of the person the recipe belongs to
  //   recipe: root recipe id of the recipe being followed
  // }
  unfollowRecipe: (req, res) => {
    let findFollow = Follow.findOne({
      username: req.params.username
    });

    Promise.all([findFollow, helpers.updateRecipeFollowers(req.params.user, req.params.recipe, -1)])
    .spread((follow, followerCountUpdate) => {
      let recipeIndex = _.findIndex(follow.recipes, recipe => {
        return recipe.username === req.params.user && recipe.recipeId.equals(req.params.recipe);
      });
      let recipes = follow.recipes;
      recipes.splice(recipeIndex, 1);

      return Follow.update({
        username: req.params.username
      }, {
        recipes: recipes
      });
    }).then(() => {
      res.status(200).send();
    }).catch(error => {
      console.log('error: ', error);
      res.status(500).send();
    });
  }
};<commit_msg>Fix getNotifications being reversed<commit_after>let helpers = require(`${__dirname}/../helpers.js`);
let db = require(`${__dirname}/../schemas.js`);
let Recipe = db.Recipe;
let Follow = db.Follow;
let User = db.User;
let Notification = db.Notification;
let _ = require('underscore');
let Promise = require('bluebird')

module.exports = {
  // description: Returns a list of all followed users
  // params: {
  //   username: username of the person following the users
  // }
  getFollowedUsers: (req ,res) => {
    return Follow.findOne({
      username: req.params.username
    }).then(follows => {
      res.status(200).send(follows.users);
    }).catch(error => {
      console.log('ERROR: ', error);
      res.status(200).send([]);
    });
  },

  // description: Returns a list of all notifications for a user
  // params: {
  //   username: username of owner of the notifications,
  // }
  getNotifications: (req, res) => {
    return Notification.find({
      notificationOwner: req.params.username
    }).then(notifications => {
      res.status(200).send(notifications);
    }).catch(error => {
      console.log('error: ', error);
      res.status(500).send();
    });
  },

  // description: Follows a user
  // params: {
  //   username: username of person following another
  //   user: username of the person being followed
  // }
  followUser: (req, res) => {
    let findFollow = Follow.findOne({
      username: req.params.username
    });
    let findUser = User.findOne({
      username: req.params.user
    });
    let notifyUser = helpers.createNotification({
      notificationOwner: req.params.user,
      username: req.params.username,
      text: `${req.params.username} started following you.`
    });

    return Promise.all([findFollow, findUser, notifyUser])
    .spread((follow, user, notification) => {
      let followers = user.followers;
      followers++;
      let updateFollowers = User.update({
        username: req.params.user
      }, {
        followers: followers
      });

      if (!follow) {
        return new Follow({
          username: req.params.username,
          users: [req.params.user]
        }).save()
        .then(() => {
          return updateFollowers;
        }).then(() => {
          res.status(201).send();
        });

      } else {
        let users = follow.users;
        users.push(req.params.user);

        return Follow.update({
          username: req.params.username
        }, {
          users: users
        }).then(() => {
          return updateFollowers;
        }).then(() => {
          res.status(200).send();
        });
      }

    }).catch(error => {
      console.log('error: ', error);
      res.status(500).send(error);
    });
  },

  // description: Follows a recipe
  // params: {
  //   username: username of the person following a recipe
  //   user: username of the person the recipe belongs to
  //   recipe: root recipe id of the recipe being followed
  // }
  followRecipe: (req, res) => {
    return Recipe.findOne({
      _id: req.params.recipe,
      username: req.params.user
    }).then(recipe => {
      let findFollow = Follow.findOne({
        username: req.params.username
      });
      let notifyUser = helpers.createNotification({
        notificationOwner: req.params.user,
        username: req.params.username,
        recipeId: req.params.recipe,
        text: `${req.params.username} followed your recipe ${recipe.name.value}.`
      });

      return Promise.all([findFollow, helpers.updateRecipeFollowers(req.params.user, req.params.recipe, 1)], notifyUser);
    }).spread((follow, followerCountUpdate, notification) => {

      if (!follow) {
        return new Follow({
          username: req.params.username,
          recipes: [{
            username: req.params.user,
            recipeId: req.params.recipe
          }]
        }).save().then(() => {
          res.status(201).send();
        });

      } else {
        let recipes = follow.recipes;
        recipes.push({
          username: req.params.user,
          recipeId: req.params.recipe
        });

        return Follow.update({
          username: req.params.username
        }, {
          recipes: recipes
        }).then(() => {
          res.status(200).send();
        });
      }
    }).catch(error => {
      console.log('error: ', error);
      res.status(500).send(error);
    });
  },

  // description: Unfollows a user
  // params: {
  //   username: username of person following another
  //   user: username of the person being followed
  // }
  unfollowUser: (req, res) => {
    let findFollow = Follow.findOne({
      username: req.params.username
    })
    let findUser = User.findOne({
      username: req.params.user
    });

    return Promise.all([findFollow, findUser])
    .spread((follow, user) => {
      let followers = user.followers;
      if (followers > 0) {
        followers--;
      }
      let updateFollowerCount = User.update({
        username: req.params.user
      }, {
        followers: followers
      });

      let userIndex = _.findIndex(follow.users, user => {
        return user === req.params.user;
      });
      let users = follow.users;
      users.splice(userIndex, 1);

      let updateFollowers = Follow.update({
        username: req.params.username
      }, {
        users: users
      });

      return Promise.all([updateFollowers, updateFollowerCount]);
    }).spread(() => {
      res.status(200).send();
    }).catch(error => {
      console.log('error: ', error);
      res.status(500).send();
    });
  },

  // description: Unfollows a Recipe
  // params: {
  //   username: username of the person following a recipe
  //   user: username of the person the recipe belongs to
  //   recipe: root recipe id of the recipe being followed
  // }
  unfollowRecipe: (req, res) => {
    let findFollow = Follow.findOne({
      username: req.params.username
    });

    Promise.all([findFollow, helpers.updateRecipeFollowers(req.params.user, req.params.recipe, -1)])
    .spread((follow, followerCountUpdate) => {
      let recipeIndex = _.findIndex(follow.recipes, recipe => {
        return recipe.username === req.params.user && recipe.recipeId.equals(req.params.recipe);
      });
      let recipes = follow.recipes;
      recipes.splice(recipeIndex, 1);

      return Follow.update({
        username: req.params.username
      }, {
        recipes: recipes
      });
    }).then(() => {
      res.status(200).send();
    }).catch(error => {
      console.log('error: ', error);
      res.status(500).send();
    });
  }
};<|endoftext|>


<commit_before><commit_msg>Update app-61895dfb7e.js<commit_after><|endoftext|>