<commit_before>function Cell(nodeId, owner, position, mass, gameServer) {
    this.nodeId = nodeId;
    this.owner = owner; // playerTracker that owns this cell
    this.color = {
        r: 0,
        g: 255,
        b: 0
    };
    this.position = position;
    this.mass = mass; // Starting mass of the cell
    this.cellType = -1; // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass
    this.spiked = 0; // If 1, then this cell has spikes around it

    this.killedBy; // Cell that ate this cell
    this.gameServer = gameServer;

    this.moveEngineTicks = 0; // Amount of times to loop the movement function
    this.moveEngineSpeed = 0;
    this.moveDecay = 0.85;
    this.angle = 0; // Angle of movement
    this.collisionRestoreTicks = 0; // Ticks left before cell starts checking for collision with client's cells
}

module.exports = Cell;

// Fields not defined by the constructor are considered private and need a getter/setter to access from a different class

Cell.prototype.getName = function() {
    if (this.owner) {
        return this.owner.name;
    } else {
        return "";
    }
};

Cell.prototype.setColor = function(color) {
    this.color.r = color.r;
    this.color.g = color.g;
    this.color.b = color.b;
};

Cell.prototype.getColor = function() {
    return this.color;
};

Cell.prototype.getType = function() {
    return this.cellType;
};

Cell.prototype.getSize = function() {
    // Calculates radius based on cell mass
    return Math.ceil(Math.sqrt(100 * this.mass));
};

Cell.prototype.getSquareSize = function() {
    // R * R
    return (100 * this.mass) >> 0;
};

Cell.prototype.addMass = function(n) {
    // Check if the cell needs to autosplit before adding mass
    if (this.mass > this.gameServer.config.playerMaxMass && this.owner.cells.length < this.gameServer.config.playerMaxCells) {
        var splitMass = this.mass / 2;
        var randomAngle = Math.random() * 6.28; // Get random angle
        this.gameServer.createPlayerCell(this.owner, this, randomAngle, splitMass);
    } else {
        this.mass = Math.min(this.mass, this.gameServer.config.playerMaxMass);
    }
    this.mass += n;
};

Cell.prototype.getSpeed = function() {
    // Based on 50ms ticks. If updateMoveEngine interval changes, change 50 to new value
    // (should possibly have a config value for this?)

    // Old formulas:
    // return 5 + (20 * (1 - (this.mass/(70+this.mass))));
    // return this.gameServer.config.playerSpeed * Math.pow(this.mass, -0.22) * 50 / 40;
    var t = Math.PI * Math.PI;
    return this.gameServer.config.playerSpeed * Math.pow(this.mass, -Math.PI / t / 1.5);
};

Cell.prototype.setAngle = function(radians) {
    this.angle = radians;
};

Cell.prototype.getAngle = function() {
    return this.angle;
};

Cell.prototype.setMoveEngineData = function(speed, ticks, decay) {
    this.moveEngineSpeed = speed;
    this.moveEngineTicks = ticks;
    this.moveDecay = isNaN(decay) ? 0.75 : decay;
};

Cell.prototype.getEatingRange = function() {
    return 0; // 0 for ejected cells
};

Cell.prototype.getKiller = function() {
    return this.killedBy;
};

Cell.prototype.setKiller = function(cell) {
    this.killedBy = cell;
};

// Functions

Cell.prototype.collisionCheck = function(bottomY, topY, rightX, leftX) {
    // Collision checking
    if (this.position.y > bottomY) {
        return false;
    }

    if (this.position.y < topY) {
        return false;
    }

    if (this.position.x > rightX) {
        return false;
    }

    if (this.position.x < leftX) {
        return false;
    }

    return true;
};

// This collision checking function is based on CIRCLE shape
Cell.prototype.collisionCheck2 = function(objectSquareSize, objectPosition) {
    // IF (O1O2 + r <= R) THEN collided. (O1O2: distance b/w 2 centers of cells)
    // (O1O2 + r)^2 <= R^2
    // approximately, remove 2*O1O2*r because it requires sqrt(): O1O2^2 + r^2 <= R^2

    var dx = this.position.x - objectPosition.x;
    var dy = this.position.y - objectPosition.y;

    return (dx * dx + dy * dy + this.getSquareSize() <= objectSquareSize);
};

Cell.prototype.visibleCheck = function(box, centerPos, cells) {
    // Checks if this cell is visible to the player
    if (this.collisionCheck(box.bottomY, box.topY, box.rightX, box.leftX)) {
        // It is
        // To save perfomance, check if any client's cell collides with this cell
        for (var i = 0; i < cells.length; i++) {
            var cell = cells[i];
            if (!cell) continue;

            var dist = this.getDist(this.position.x, this.position.y, cell.position.x, cell.position.y);
            var collideDist = cell.getSize() + this.getSize();

            if (dist < collideDist) {
                return 2;
            }// Colliding with one
        }
        return 1; // Not colliding with any
    }
    else return 0;
};

Cell.prototype.calcMovePhys = function(config) {
    // Move, twice as slower
    var X = this.position.x + ((this.moveEngineSpeed / 2) * Math.sin(this.angle) >> 0);
    var Y = this.position.y + ((this.moveEngineSpeed / 2) * Math.cos(this.angle) >> 0);

    // Movement engine
    if (this.moveEngineSpeed <= this.moveDecay * 3 && this.cellType == 0) this.moveEngineSpeed = 0;
    var speedDecrease = this.moveEngineSpeed - this.moveEngineSpeed * this.moveDecay;
    this.moveEngineSpeed -= speedDecrease / 2; // Decaying speed twice as slower
    if (this.moveEngineTicks >= 0.5) this.moveEngineTicks -= 0.5; // Ticks passing twice as slower

    // Ejected cell collision
    if (this.cellType == 3) {
        for (var i = 0; i < this.gameServer.nodesEjected.length; i++) {
            var check = this.gameServer.nodesEjected[i];

            if (check.nodeId == this.nodeId) continue; // Don't check for yourself

            var dist = this.getDist(this.position.x, this.position.y, check.position.x, check.position.y);
            var allowDist = this.getSize() + check.getSize(); // Allow cells to get in themselves a bit

            if (dist < allowDist) {
                // Two ejected cells collided
                var deltaX = this.position.x - check.position.x;
                var deltaY = this.position.y - check.position.y;
                var angle = Math.atan2(deltaX, deltaY);

                this.gameServer.setAsMovingNode(check);
                check.moveEngineTicks += 1;
                this.moveEngineTicks += 1;

                var move = allowDist - dist;

                X += Math.sin(angle) * move / 2;
                Y += Math.cos(angle) * move / 2;
            }
        }
    }

    // Border check - Bouncy physics
    var radius = 40;
    if ((this.position.x - radius) < -config.borderLeft) {
        // Flip angle horizontally - Left side
        this.angle = 6.28 - this.angle;
        X = -config.borderLeft + radius;
    }
    if ((this.position.x + radius) > config.borderRight) {
        // Flip angle horizontally - Right side
        this.angle = 6.28 - this.angle;
        X = config.borderRight - radius;
    }
    if ((this.position.y - radius) < -config.borderTop) {
        // Flip angle vertically - Top side
        this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle;
        Y = -config.borderTop + radius;
    }
    if ((this.position.y + radius) > config.borderBottom) {
        // Flip angle vertically - Bottom side
        this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle;
        Y = config.borderBottom - radius;
    }

    // Set position
    this.position.x = X >> 0;
    this.position.y = Y >> 0;
};

// Override these

Cell.prototype.sendUpdate = function() {
    // Whether or not to include this cell in the update packet
    return true;
};

Cell.prototype.onConsume = function(consumer, gameServer) {
    // Called when the cell is consumed
};

Cell.prototype.onAdd = function(gameServer) {
    // Called when this cell is added to the world
};

Cell.prototype.onRemove = function(gameServer) {
    // Called when this cell is removed
};

Cell.prototype.onAutoMove = function(gameServer) {
    // Called on each auto move engine tick
};

Cell.prototype.moveDone = function(gameServer) {
    // Called when this cell finished moving with the auto move engine
};

// Lib

Cell.prototype.abs = function(x) {
    return x < 0 ? -x : x;
};

Cell.prototype.getDist = function(x1, y1, x2, y2) {
    var xs = x2 - x1;
    xs = xs * xs;

    var ys = y2 - y1;
    ys = ys * ys;

    return Math.sqrt(xs + ys);
};
<commit_msg>Fix bug where large cell isn't seen by small one<commit_after>function Cell(nodeId, owner, position, mass, gameServer) {
    this.nodeId = nodeId;
    this.owner = owner; // playerTracker that owns this cell
    this.color = {
        r: 0,
        g: 255,
        b: 0
    };
    this.position = position;
    this.mass = mass; // Starting mass of the cell
    this.cellType = -1; // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass
    this.spiked = 0; // If 1, then this cell has spikes around it

    this.killedBy; // Cell that ate this cell
    this.gameServer = gameServer;

    this.moveEngineTicks = 0; // Amount of times to loop the movement function
    this.moveEngineSpeed = 0;
    this.moveDecay = 0.85;
    this.angle = 0; // Angle of movement
    this.collisionRestoreTicks = 0; // Ticks left before cell starts checking for collision with client's cells
}

module.exports = Cell;

// Fields not defined by the constructor are considered private and need a getter/setter to access from a different class

Cell.prototype.getName = function() {
    if (this.owner) {
        return this.owner.name;
    } else {
        return "";
    }
};

Cell.prototype.setColor = function(color) {
    this.color.r = color.r;
    this.color.g = color.g;
    this.color.b = color.b;
};

Cell.prototype.getColor = function() {
    return this.color;
};

Cell.prototype.getType = function() {
    return this.cellType;
};

Cell.prototype.getSize = function() {
    // Calculates radius based on cell mass
    return Math.ceil(Math.sqrt(100 * this.mass));
};

Cell.prototype.getSquareSize = function() {
    // R * R
    return (100 * this.mass) >> 0;
};

Cell.prototype.addMass = function(n) {
    // Check if the cell needs to autosplit before adding mass
    if (this.mass > this.gameServer.config.playerMaxMass && this.owner.cells.length < this.gameServer.config.playerMaxCells) {
        var splitMass = this.mass / 2;
        var randomAngle = Math.random() * 6.28; // Get random angle
        this.gameServer.createPlayerCell(this.owner, this, randomAngle, splitMass);
    } else {
        this.mass = Math.min(this.mass, this.gameServer.config.playerMaxMass);
    }
    this.mass += n;
};

Cell.prototype.getSpeed = function() {
    // Based on 50ms ticks. If updateMoveEngine interval changes, change 50 to new value
    // (should possibly have a config value for this?)

    // Old formulas:
    // return 5 + (20 * (1 - (this.mass/(70+this.mass))));
    // return this.gameServer.config.playerSpeed * Math.pow(this.mass, -0.22) * 50 / 40;
    var t = Math.PI * Math.PI;
    return this.gameServer.config.playerSpeed * Math.pow(this.mass, -Math.PI / t / 1.5);
};

Cell.prototype.setAngle = function(radians) {
    this.angle = radians;
};

Cell.prototype.getAngle = function() {
    return this.angle;
};

Cell.prototype.setMoveEngineData = function(speed, ticks, decay) {
    this.moveEngineSpeed = speed;
    this.moveEngineTicks = ticks;
    this.moveDecay = isNaN(decay) ? 0.75 : decay;
};

Cell.prototype.getEatingRange = function() {
    return 0; // 0 for ejected cells
};

Cell.prototype.getKiller = function() {
    return this.killedBy;
};

Cell.prototype.setKiller = function(cell) {
    this.killedBy = cell;
};

// Functions

Cell.prototype.collisionCheck = function(bottomY, topY, rightX, leftX) {
    // Collision checking
    if (this.position.y > bottomY) {
        return false;
    }

    if (this.position.y < topY) {
        return false;
    }

    if (this.position.x > rightX) {
        return false;
    }

    if (this.position.x < leftX) {
        return false;
    }

    return true;
};

// This collision checking function is based on CIRCLE shape
Cell.prototype.collisionCheck2 = function(objectSquareSize, objectPosition) {
    // IF (O1O2 + r <= R) THEN collided. (O1O2: distance b/w 2 centers of cells)
    // (O1O2 + r)^2 <= R^2
    // approximately, remove 2*O1O2*r because it requires sqrt(): O1O2^2 + r^2 <= R^2

    var dx = this.position.x - objectPosition.x;
    var dy = this.position.y - objectPosition.y;

    return (dx * dx + dy * dy + this.getSquareSize() <= objectSquareSize);
};

Cell.prototype.visibleCheck = function(box, centerPos, cells) {
    // Checks if this cell is visible to the player
    var isThere = false;
    if (this.mass < 100) isThere = this.collisionCheck(box.bottomY, box.topY, box.rightX, box.leftX);
    else {
        var cellSize = this.getSize();
        var lenX = cellSize + box.width >> 0; // Width of cell + width of the box (Int)
        var lenY = cellSize + box.height >> 0; // Height of cell + height of the box (Int)

        isThere = (this.abs(this.position.x - centerPos.x) < lenX) && (this.abs(this.position.y - centerPos.y) < lenY);
    }
    if (isThere) {
        // It is
        // To save perfomance, check if any client's cell collides with this cell
        for (var i = 0; i < cells.length; i++) {
            var cell = cells[i];
            if (!cell) continue;

            var dist = this.getDist(this.position.x, this.position.y, cell.position.x, cell.position.y);
            var collideDist = cell.getSize() + this.getSize();

            if (dist < collideDist) {
                return 2;
            }// Colliding with one
        }
        return 1; // Not colliding with any
    }
    else return 0;
};

Cell.prototype.calcMovePhys = function(config) {
    // Move, twice as slower
    var X = this.position.x + ((this.moveEngineSpeed / 2) * Math.sin(this.angle) >> 0);
    var Y = this.position.y + ((this.moveEngineSpeed / 2) * Math.cos(this.angle) >> 0);

    // Movement engine
    if (this.moveEngineSpeed <= this.moveDecay * 3 && this.cellType == 0) this.moveEngineSpeed = 0;
    var speedDecrease = this.moveEngineSpeed - this.moveEngineSpeed * this.moveDecay;
    this.moveEngineSpeed -= speedDecrease / 2; // Decaying speed twice as slower
    if (this.moveEngineTicks >= 0.5) this.moveEngineTicks -= 0.5; // Ticks passing twice as slower

    // Ejected cell collision
    if (this.cellType == 3) {
        for (var i = 0; i < this.gameServer.nodesEjected.length; i++) {
            var check = this.gameServer.nodesEjected[i];

            if (check.nodeId == this.nodeId) continue; // Don't check for yourself

            var dist = this.getDist(this.position.x, this.position.y, check.position.x, check.position.y);
            var allowDist = this.getSize() + check.getSize(); // Allow cells to get in themselves a bit

            if (dist < allowDist) {
                // Two ejected cells collided
                var deltaX = this.position.x - check.position.x;
                var deltaY = this.position.y - check.position.y;
                var angle = Math.atan2(deltaX, deltaY);

                this.gameServer.setAsMovingNode(check);
                check.moveEngineTicks += 1;
                this.moveEngineTicks += 1;

                var move = allowDist - dist;

                X += Math.sin(angle) * move / 2;
                Y += Math.cos(angle) * move / 2;
            }
        }
    }

    // Border check - Bouncy physics
    var radius = 40;
    if ((this.position.x - radius) < -config.borderLeft) {
        // Flip angle horizontally - Left side
        this.angle = 6.28 - this.angle;
        X = -config.borderLeft + radius;
    }
    if ((this.position.x + radius) > config.borderRight) {
        // Flip angle horizontally - Right side
        this.angle = 6.28 - this.angle;
        X = config.borderRight - radius;
    }
    if ((this.position.y - radius) < -config.borderTop) {
        // Flip angle vertically - Top side
        this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle;
        Y = -config.borderTop + radius;
    }
    if ((this.position.y + radius) > config.borderBottom) {
        // Flip angle vertically - Bottom side
        this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle;
        Y = config.borderBottom - radius;
    }

    // Set position
    this.position.x = X >> 0;
    this.position.y = Y >> 0;
};

// Override these

Cell.prototype.sendUpdate = function() {
    // Whether or not to include this cell in the update packet
    return true;
};

Cell.prototype.onConsume = function(consumer, gameServer) {
    // Called when the cell is consumed
};

Cell.prototype.onAdd = function(gameServer) {
    // Called when this cell is added to the world
};

Cell.prototype.onRemove = function(gameServer) {
    // Called when this cell is removed
};

Cell.prototype.onAutoMove = function(gameServer) {
    // Called on each auto move engine tick
};

Cell.prototype.moveDone = function(gameServer) {
    // Called when this cell finished moving with the auto move engine
};

// Lib

Cell.prototype.abs = function(x) {
    return x < 0 ? -x : x;
};

Cell.prototype.getDist = function(x1, y1, x2, y2) {
    var xs = x2 - x1;
    xs = xs * xs;

    var ys = y2 - y1;
    ys = ys * ys;

    return Math.sqrt(xs + ys);
};
<|endoftext|>


<commit_before>var assert = require("assert"),
    Syntax = require("./syntax"),
    Patcher = require("./patcher").Patcher,
    Visitor = require("./visitor").Visitor,
    normalizeOptions = require("./options").normalize;

function Parser(source, options) {
    assert.ok(this instanceof Parser);

    options = normalizeOptions(options);

    var self = this;

    var lines = require("./lines").fromString(
        source, options.tabWidth);

    var pure = require("esprima").parse(lines.toString({
        tabWidth: options.tabWidth,
        reuseWhitespace: false,
        useTabs: false
    }), {
        loc: true,
        comment: true
    });

    new LocationFixer(lines).visit(pure);

    require("./comments").add(pure);

    // In order to ensure we reprint leading and trailing program
    // comments, wrap the original Program node with a File node.
    assert.strictEqual(pure.type, Syntax.Program);
    pure = {
        type: Syntax.File,
        program: pure,
        loc: { start: lines.firstPos(),
               end: lines.lastPos() }
    };

    self.getAst = function() {
        // Return a copy of the original AST so that any changes made may
        // be compared to the original.
        return copyAst(pure);
    };

    function getIndent(node) {
        // TODO Improve this.
        return lines.getIndentAt(node.loc.start.line);
    }

    self.getReprinter = function(node) {
        var orig = node.original,
            reprints = [];

        if (!orig ||
            !traverse(orig, node, reprints, true))
            return;

        return function(print) {
            var patcher = new Patcher(lines);

            reprints.forEach(function(reprint) {
                var old = reprint.oldNode;
                patcher.replace(
                    old.loc,
                    print(reprint.newNode).indentTail(
                        getIndent(old)));
            });

            return patcher.get(orig.loc).indentTail(-getIndent(orig));
        };
    };
}
exports.Parser = Parser;

var LocationFixer = Visitor.extend({
    init: function(lines) {
        this.lines = lines;
    },

    genericVisit: function(node) {
        this._super(node);

        var loc = node && node.loc,
            end = loc && loc.end;

        if (end) {
            var lines = this.lines, pos = {
                line: end.line,
                column: end.column
            };

            // Negative columns might indicate an Esprima bug?
            // For now, treat them as reverse indices, a la Python.
            if (pos.column < 0)
                pos.column += lines.getLineLength(pos.line);

            while (lines.prevPos(pos)) {
                if (/\S/.test(lines.charAt(pos))) {
                    assert.ok(lines.nextPos(pos));

                    end.line = pos.line;
                    end.column = pos.column;

                    break;
                }
            }
        }
    }
});

function copyAst(node) {
    if (typeof node === "object" &&
        node !== null)
    {
        if (node instanceof Array)
            return node.map(copyAst);

        var copy = {},
            key,
            val;

        for (key in node) {
            if (node.hasOwnProperty(key)) {
                val = copyAst(node[key]);
                if (typeof val !== "function")
                    copy[key] = val;
            }
        }

        // Provide a link from the copy to the original.
        Object.defineProperty(copy, "original", {
            value: node,
            configurable: false,
            enumerable: false,
            writable: true,
        });

        return copy;
    }

    return node;
}

function traverse(a, b, reprints, ignoreFirst) {
    if (a === b)
        return true;

    if (a instanceof Array) {
        assert.ok(b instanceof Array);

        if (a.length !== b.length)
            return false;

        for (var i = 0, len = a.length; i < len; ++i)
            if (!traverse(a[i], b[i], reprints))
                return false;

    } else if (typeof a === "object") {
        assert.ok(typeof b === "object");

        if (a === null ||
            b === null)
            return false;

        if (a.type in Syntax) {
            assert.ok(b.type in Syntax);

            // TODO Decompose this check: if (!printTheSame(a, b))
            if (a.type !== b.type)
                return false;

            if (!ignoreFirst) {
                reprints.push({
                    oldNode: a,
                    newNode: b
                });

                return true;
            }
        }

        for (var k in getUnionOfKeys(a, b)) {
            if (k === "loc")
                continue;

            if (!traverse(a[k], b[k], reprints))
                return false;
        }

    } else {
        return false;
    }

    return true;
}

function getUnionOfKeys(obj) {
    for (var i = 0, key,
             result = {},
             objs = arguments,
             argc = objs.length;
         i < argc;
         i += 1)
    {
        obj = objs[i];
        for (key in obj)
            if (obj.hasOwnProperty(key))
                result[key] = true;
    }
    return result;
}
<commit_msg>Ensure that line numbers are never less than 1.<commit_after>var assert = require("assert"),
    Syntax = require("./syntax"),
    Patcher = require("./patcher").Patcher,
    Visitor = require("./visitor").Visitor,
    normalizeOptions = require("./options").normalize;

function Parser(source, options) {
    assert.ok(this instanceof Parser);

    options = normalizeOptions(options);

    var self = this;

    var lines = require("./lines").fromString(
        source, options.tabWidth);

    var pure = require("esprima").parse(lines.toString({
        tabWidth: options.tabWidth,
        reuseWhitespace: false,
        useTabs: false
    }), {
        loc: true,
        comment: true
    });

    new LocationFixer(lines).visit(pure);

    require("./comments").add(pure);

    // In order to ensure we reprint leading and trailing program
    // comments, wrap the original Program node with a File node.
    assert.strictEqual(pure.type, Syntax.Program);
    pure = {
        type: Syntax.File,
        program: pure,
        loc: { start: lines.firstPos(),
               end: lines.lastPos() }
    };

    self.getAst = function() {
        // Return a copy of the original AST so that any changes made may
        // be compared to the original.
        return copyAst(pure);
    };

    function getIndent(node) {
        // TODO Improve this.
        return lines.getIndentAt(node.loc.start.line);
    }

    self.getReprinter = function(node) {
        var orig = node.original,
            reprints = [];

        if (!orig ||
            !traverse(orig, node, reprints, true))
            return;

        return function(print) {
            var patcher = new Patcher(lines);

            reprints.forEach(function(reprint) {
                var old = reprint.oldNode;
                patcher.replace(
                    old.loc,
                    print(reprint.newNode).indentTail(
                        getIndent(old)));
            });

            return patcher.get(orig.loc).indentTail(-getIndent(orig));
        };
    };
}
exports.Parser = Parser;

var LocationFixer = Visitor.extend({
    init: function(lines) {
        this.lines = lines;
    },

    genericVisit: function(node) {
        this._super(node);

        var loc = node && node.loc,
            start = loc && loc.start,
            end = loc && loc.end;

        if (start) {
            start.line = Math.max(start.line, 1);
        }

        if (end) {
            end.line = Math.max(end.line, 1);

            var lines = this.lines, pos = {
                line: end.line,
                column: end.column
            };

            // Negative columns might indicate an Esprima bug?
            // For now, treat them as reverse indices, a la Python.
            if (pos.column < 0)
                pos.column += lines.getLineLength(pos.line);

            while (lines.prevPos(pos)) {
                if (/\S/.test(lines.charAt(pos))) {
                    assert.ok(lines.nextPos(pos));

                    end.line = pos.line;
                    end.column = pos.column;

                    break;
                }
            }
        }
    }
});

function copyAst(node) {
    if (typeof node === "object" &&
        node !== null)
    {
        if (node instanceof Array)
            return node.map(copyAst);

        var copy = {},
            key,
            val;

        for (key in node) {
            if (node.hasOwnProperty(key)) {
                val = copyAst(node[key]);
                if (typeof val !== "function")
                    copy[key] = val;
            }
        }

        // Provide a link from the copy to the original.
        Object.defineProperty(copy, "original", {
            value: node,
            configurable: false,
            enumerable: false,
            writable: true,
        });

        return copy;
    }

    return node;
}

function traverse(a, b, reprints, ignoreFirst) {
    if (a === b)
        return true;

    if (a instanceof Array) {
        assert.ok(b instanceof Array);

        if (a.length !== b.length)
            return false;

        for (var i = 0, len = a.length; i < len; ++i)
            if (!traverse(a[i], b[i], reprints))
                return false;

    } else if (typeof a === "object") {
        assert.ok(typeof b === "object");

        if (a === null ||
            b === null)
            return false;

        if (a.type in Syntax) {
            assert.ok(b.type in Syntax);

            // TODO Decompose this check: if (!printTheSame(a, b))
            if (a.type !== b.type)
                return false;

            if (!ignoreFirst) {
                reprints.push({
                    oldNode: a,
                    newNode: b
                });

                return true;
            }
        }

        for (var k in getUnionOfKeys(a, b)) {
            if (k === "loc")
                continue;

            if (!traverse(a[k], b[k], reprints))
                return false;
        }

    } else {
        return false;
    }

    return true;
}

function getUnionOfKeys(obj) {
    for (var i = 0, key,
             result = {},
             objs = arguments,
             argc = objs.length;
         i < argc;
         i += 1)
    {
        obj = objs[i];
        for (key in obj)
            if (obj.hasOwnProperty(key))
                result[key] = true;
    }
    return result;
}
<|endoftext|>


<commit_before>/**
 * @author daniel.joppi
 * @since 2/12/14.
 */
var compression = require('compression');
var express  = require("express");
var app      = express();
var bodyParser = require('body-parser');

// compress all request
app.use(compression({
    threshold: 1024 // ignore 1Kb
    filter: function(req, res) {
        console.log(res);
    }
}));
// for parsing application/json
app.use(bodyParser.json());
// for parsing application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
// for parsing multipart/form-data
//app.use(multer());

// Start MondgoDB connect
var db = require('./config/database')();

var port = process.env.PORT || 80; 		// set our port
// ROUTES FOR OUR API
// =============================================================================
var router = express.Router(); 		// get an instance of the express Router

var routes = require('./routes/route-config')(router, db);

// REGISTER OUR ROUTES -------------------------------
// all of our routes will be prefixed with /api
app.use('/api', router);

// START THE SERVER
// =============================================================================
app.listen(port);
console.log('start server on port ' + port);<commit_msg>gzip compress content<commit_after>/**
 * @author daniel.joppi
 * @since 2/12/14.
 */
var compression = require('compression');
var express  = require("express");
var app      = express();
var bodyParser = require('body-parser');

// compress all request
app.use(compression({
    threshold: 1024, // ignore 1Kb
    filter: function(req, res) {
        console.log(res);
    }
}));
// for parsing application/json
app.use(bodyParser.json());
// for parsing application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
// for parsing multipart/form-data
//app.use(multer());

// Start MondgoDB connect
var db = require('./config/database')();

var port = process.env.PORT || 80; 		// set our port
// ROUTES FOR OUR API
// =============================================================================
var router = express.Router(); 		// get an instance of the express Router

var routes = require('./routes/route-config')(router, db);

// REGISTER OUR ROUTES -------------------------------
// all of our routes will be prefixed with /api
app.use('/api', router);

// START THE SERVER
// =============================================================================
app.listen(port);
console.log('start server on port ' + port);<|endoftext|>


<commit_before>global.$ = global.jQuery = require('jquery');
require('foundation-sites');

function equalize_picture() {
    const section = $('#personal_details');
    const picture = $('img', section);
    const picture_margin = picture.outerHeight() - picture.innerHeight();
    const details_height = $('ul', section).height() - picture_margin;
    picture.removeAttr('style');
    if (details_height > picture.height()) {
        picture.height(details_height).width(details_height);
    }
}

function equalize_columns() {
    const elements = $('.left, .right');
    const max_height = Math.max.apply(
        null,
        elements.map((index, elt) => $(elt).height())
    );
    elements.each((index, elt) => {
        const last_child = $('section', elt).last()[0];

        $(elt).add(last_child).removeAttr('style');
        $(elt).height(max_height);

        const parent_bbox = elt.getBoundingClientRect();
        const last_child_bbox = last_child.getBoundingClientRect();
        const d = parent_bbox.bottom - last_child_bbox.bottom;

        $(last_child).height(last_child_bbox.height + d);
    });
}

function equalize() {
    equalize_picture();
    equalize_columns();
}

function before_print() {
    $('.left, .right').each((index, elt) => {
        $(elt).add($('section', elt).last()[0]).removeAttr('style');
    });
    equalize_picture();
}

$(window)
    .load(() => {
        $(document).foundation();
        equalize();
    })
    .on('resize', equalize);

if (window.matchMedia) {
    window.matchMedia('print').addListener((mql) => {
        if (mql.matches) {
            before_print();
        } else {
            equalize();
        }
    });
}
<commit_msg>Use foundation module.<commit_after>const $ = require('jquery');
const foundation = require('foundation');

function equalize_picture() {
    const section = $('#personal_details');
    const picture = $('img', section);
    const picture_margin = picture.outerHeight() - picture.innerHeight();
    const details_height = $('ul', section).height() - picture_margin;
    picture.removeAttr('style');
    if (details_height > picture.height()) {
        picture.height(details_height).width(details_height);
    }
}

function equalize_columns() {
    const elements = $('.left, .right');
    const max_height = Math.max.apply(
        null,
        elements.map((index, elt) => $(elt).height())
    );
    elements.each((index, elt) => {
        const last_child = $('section', elt).last()[0];

        $(elt).add(last_child).removeAttr('style');
        $(elt).height(max_height);

        const parent_bbox = elt.getBoundingClientRect();
        const last_child_bbox = last_child.getBoundingClientRect();
        const d = parent_bbox.bottom - last_child_bbox.bottom;

        $(last_child).height(last_child_bbox.height + d);
    });
}

function equalize() {
    equalize_picture();
    equalize_columns();
}

function before_print() {
    $('.left, .right').each((index, elt) => {
        $(elt).add($('section', elt).last()[0]).removeAttr('style');
    });
    equalize_picture();
}

$(window)
    .load(() => {
        foundation();
        equalize();
    })
    .on('resize', equalize);

if (window.matchMedia) {
    window.matchMedia('print').addListener((mql) => {
        if (mql.matches) {
            before_print();
        } else {
            equalize();
        }
    });
}
<|endoftext|>


<commit_before>function Cell(gameServer, owner, position, size) {
    this.gameServer = gameServer;
    this.owner = owner;     // playerTracker that owns this cell

    this.tickOfBirth = 0;
    this.color = { r: 0, g: 0, b: 0 };
    this.position = { x: 0, y: 0 };
    this._sizeSquared = 0;
    this._size = 0;
    this._mass = 0;
    this.cellType = -1;     // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass
    this.isSpiked = false;  // If true, then this cell has spikes around it
    this.isAgitated = false;// If true, then this cell has waves on it's outline
    this.killedBy = null;   // Cell that ate this cell
    this.isMoving = false;  // Indicate that cell is in boosted mode

    this.boostDistance = 0;
    this.boostDirection = { x: 1, y: 0, angle: Math.PI / 2 };
    this.ejector = null;

    if (this.gameServer) {
        this.tickOfBirth = this.gameServer.tickCounter;
        this.nodeId = this.getNextNodeId();
        if (size) this.setSize(size);
        if (position) this.position = position;
    }
}

module.exports = Cell;

// Fields not defined by the constructor are considered private and need a getter/setter to access from a different class

Cell.prototype.getNextNodeId = function () {
    if (this.gameServer.lastNodeId > 2147483647) {
        this.gameServer.lastNodeId = 1;
    }
    return this.gameServer.lastNodeId++ >> 0;
};

Cell.prototype.setColor = function (color) {
    this.color.r = color.r;
    this.color.g = color.g;
    this.color.b = color.b;
};

Cell.prototype.setSize = function (size) {
    this._size = size;
    this._sizeSquared = size * size;
    this._mass = this._sizeSquared / 100;
    if (this.owner)
        this.owner.isMassChanged = true;
};

// Returns cell age in ticks for specified game tick
Cell.prototype.getAge = function (tick) {
    if (this.tickOfBirth == null) return 0;
    return Math.max(0, tick - this.tickOfBirth);
};

// by default cell cannot eat anyone
Cell.prototype.canEat = function (cell) {
    return false;
};

// Called to eat prey cell
Cell.prototype.onEat = function (prey) {
    // Cant grow from cells under 17 mass (vanilla)
    if (this.gameServer.config.playerBotGrow == 0) {
        if (this._mass >= 625 && prey._mass <= 17 && prey.cellType != 3)
            prey._sizeSquared = 0;
    }
    this.setSize(Math.sqrt(this._sizeSquared + prey._sizeSquared));
};

Cell.prototype.setBoost = function (distance, angle) {
    this.boostDistance = distance;
    this.boostDirection = {
        x: Math.sin(angle),
        y: Math.cos(angle),
        angle: angle
    };
    this.isMoving = true;
    if (!this.owner) {
        var index = this.gameServer.movingNodes.indexOf(this);
        if (index < 0)
            this.gameServer.movingNodes.push(this);
    }
};

Cell.prototype.checkBorder = function (border) {
    this.position.x = Math.max(this.position.x, border.minx + this._size / 2);
    this.position.y = Math.max(this.position.y, border.miny + this._size / 2);
    this.position.x = Math.min(this.position.x, border.maxx - this._size / 2);
    this.position.y = Math.min(this.position.y, border.maxy - this._size / 2);
};

Cell.prototype.onEaten = function (hunter) { };
Cell.prototype.onAdd = function (gameServer) { };
Cell.prototype.onRemove = function (gameServer) { };
<commit_msg>Update Cell.js<commit_after>function Cell(gameServer, owner, position, size) {
    this.gameServer = gameServer;
    this.owner = owner;     // playerTracker that owns this cell

    this.tickOfBirth = 0;
    this.color = { r: 0, g: 0, b: 0 };
    this.position = { x: 0, y: 0 };
    this._sizeSquared = 0;
    this._size = 0;
    this._mass = 0;
    this.cellType = -1;     // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass
    this.isSpiked = false;  // If true, then this cell has spikes around it
    this.isAgitated = false;// If true, then this cell has waves on it's outline
    this.killedBy = null;   // Cell that ate this cell
    this.isMoving = false;  // Indicate that cell is in boosted mode
    this.boostDistance = 0;
    this.boostDirection = { x: 1, y: 0, angle: Math.PI / 2 };

    if (this.gameServer) {
        this.tickOfBirth = this.gameServer.tickCounter;
        this.nodeId = this.getNextNodeId();
        if (size) this.setSize(size);
        if (position) this.position = position;
    }
}

module.exports = Cell;

// Fields not defined by the constructor are considered private and need a getter/setter to access from a different class

Cell.prototype.getNextNodeId = function () {
    if (this.gameServer.lastNodeId > 2147483647) {
        this.gameServer.lastNodeId = 1;
    }
    return this.gameServer.lastNodeId++ >> 0;
};

Cell.prototype.setColor = function (color) {
    this.color.r = color.r;
    this.color.g = color.g;
    this.color.b = color.b;
};

Cell.prototype.setSize = function (size) {
    this._size = size;
    this._sizeSquared = size * size;
    this._mass = this._sizeSquared / 100;
    if (this.owner)
        this.owner.isMassChanged = true;
};

// Returns cell age in ticks for specified game tick
Cell.prototype.getAge = function (tick) {
    if (this.tickOfBirth == null) return 0;
    return Math.max(0, tick - this.tickOfBirth);
};

// by default cell cannot eat anyone
Cell.prototype.canEat = function (cell) {
    return false;
};

// Called to eat prey cell
Cell.prototype.onEat = function (prey) {
    if (!this.gameServer.config.playerBotGrow) {
        if (this._mass >= 625 && prey._mass <= 17 && prey.cellType != 3)
            prey._sizeSquared = 0; // Can't grow from cells under 17 mass
    }
    this.setSize(Math.sqrt(this._sizeSquared + prey._sizeSquared));
};

Cell.prototype.setBoost = function (distance, angle) {
    this.boostDistance = distance;
    this.boostDirection = {
        x: Math.sin(angle),
        y: Math.cos(angle),
        angle: angle
    };
    this.isMoving = true;
    if (!this.owner) {
        var index = this.gameServer.movingNodes.indexOf(this);
        if (index < 0)
            this.gameServer.movingNodes.push(this);
    }
};

Cell.prototype.checkBorder = function (border) {
    this.position.x = Math.max(this.position.x, border.minx + this._size / 2);
    this.position.y = Math.max(this.position.y, border.miny + this._size / 2);
    this.position.x = Math.min(this.position.x, border.maxx - this._size / 2);
    this.position.y = Math.min(this.position.y, border.maxy - this._size / 2);
};

Cell.prototype.onEaten = function (hunter) { };
Cell.prototype.onAdd = function (gameServer) { };
Cell.prototype.onRemove = function (gameServer) { };
<|endoftext|>


<commit_before>'use strict';

/**
 * Helper module to manipulate item fields and strings.
 * @module parser
 */

var conf = require("./conf"),
  _ = require("lodash"),
  KEYWORD_PREFIX = conf.get("keywordPrefix"),
  FIELD_SEPARATOR = conf.get("fieldSeparator"),
  EMPTY_FIELD_VALUE = conf.get("emptyFieldValue"),
  LINE_ENDING_REGEX = /\r\n|\n\r|\n|\r/g;

/**
 * Replace all line endings with Linux line endings (\n).
 * @param {string} text
 * @return {string} Normalized string.
 * @static
 */
function normalizeLineEndings(text) {
  return text.replace(LINE_ENDING_REGEX, "\n");
}

/**
 * Extract fields from a string. Fields are any line that begins with
 * a specific prefix. If the same field is found twice, the last one wins.
 *
 * @param {string} text - Input text to filter, will not be modified.
 * @param {Object.<string, string>} fields - A pre-constructed Object to hold
 *    the fields extracted. Can be empty or already contain fields, which will
 *    be overridden if found within the text.
 * @return {string} the text without any line containing a field.
 *
 * @static
 */
function extractFields(text, fields) {
  var lines,
    filteredLines;

  fields.should.be.type("object");

  lines = normalizeLineEndings(text).split("\n");

  // Filter out lines containing a field, keeps others.
  filteredLines = lines.filter(function (line) {
    var separatorIndex,
      fieldName,
      fieldValue;

    line = line.trim();

    if (line.charAt(0) !== KEYWORD_PREFIX) {
      return true;
    }

    // Minimum pattern is KEYWORD_PREFIX+a-character,
    // which could denote a 1-character field with an empty value
    if (line.length < 2) {
      return true;
    }

    separatorIndex = line.indexOf(FIELD_SEPARATOR);
    // If prefix is immediately followed by separator we have no field
    if (separatorIndex === 1) {
      return true;
    }

    // Assume we have a field with an empty value
    if (separatorIndex === -1) {
      fields[line.substring(1)] = EMPTY_FIELD_VALUE;
      return false;
    }

    // Got a field and value
    fields[line.substring(1, separatorIndex)] = line.substring(separatorIndex + 1);
    return false;
  });

  // Return the filtered text with normalized line endings
  return filteredLines.join("\n");
}

/**
 * Serialize fields to a string.
 *
 * @param {Object.<string, string>} fields
 * @return {string} the serialized string with one field per line. Each line
 *    starts with a line break).
 *
 * @static
 */
function serializeFields(fields) {
  var result = "";

  _.forOwn(fields, function (value, key) {
    result += "\n" + KEYWORD_PREFIX + key + FIELD_SEPARATOR + value;
  });

  return result;
}

module.exports = {
  normalizeLineEndings : normalizeLineEndings,
  extractFields : extractFields,
  serializeFields : serializeFields
};<commit_msg>Comment tweak<commit_after>'use strict';

/**
 * Helper module to manipulate item fields and strings.
 * @module parser
 */

var conf = require("./conf"),
  _ = require("lodash"),
  KEYWORD_PREFIX = conf.get("keywordPrefix"),
  FIELD_SEPARATOR = conf.get("fieldSeparator"),
  EMPTY_FIELD_VALUE = conf.get("emptyFieldValue"),
  LINE_ENDING_REGEX = /\r\n|\n\r|\n|\r/g;

/**
 * Replace all line endings with Linux line endings (\n).
 * @param {string} text
 * @return {string} Normalized string.
 * @static
 */
function normalizeLineEndings(text) {
  return text.replace(LINE_ENDING_REGEX, "\n");
}

/**
 * Extract fields from a string. Fields are any line that begins with
 * a specific prefix. If the same field is found twice, the last one wins.
 *
 * @param {string} text - Input text to filter, will not be modified.
 * @param {Object.<string, string>} fields - A pre-constructed Object to hold
 *    the fields extracted. Can be empty or already contain fields, which will
 *    be overridden if found within the text.
 * @return {string} the text without any line containing a field.
 *
 * @static
 */
function extractFields(text, fields) {
  var lines,
    filteredLines;

  fields.should.be.type("object");

  lines = normalizeLineEndings(text).split("\n");

  // Filter out lines containing a field, keeps others.
  filteredLines = lines.filter(function (line) {
    var separatorIndex,
      fieldName,
      fieldValue;

    line = line.trim();

    if (line.charAt(0) !== KEYWORD_PREFIX) {
      return true;
    }

    // Minimum pattern is KEYWORD_PREFIX+a-character,
    // which could denote a 1-character field with an empty value
    if (line.length < 2) {
      return true;
    }

    separatorIndex = line.indexOf(FIELD_SEPARATOR);
    // If prefix is immediately followed by separator we have no field
    if (separatorIndex === 1) {
      return true;
    }

    // Assume we have a field with an empty value
    if (separatorIndex === -1) {
      fields[line.substring(1)] = EMPTY_FIELD_VALUE;
      return false;
    }

    // Got a field and value
    fields[line.substring(1, separatorIndex)] = line.substring(separatorIndex + 1);
    return false;
  });

  // Return the filtered text with normalized line endings
  return filteredLines.join("\n");
}

/**
 * Serialize fields to a string.
 *
 * @param {Object.<string, string>} fields
 * @return {string} the serialized string with one field per line. Each line
 *    starts with a line break). Returns an empty string if there are no fields.
 *
 * @static
 */
function serializeFields(fields) {
  var result = "";

  _.forOwn(fields, function (value, key) {
    result += "\n" + KEYWORD_PREFIX + key + FIELD_SEPARATOR + value;
  });

  return result;
}

module.exports = {
  normalizeLineEndings : normalizeLineEndings,
  extractFields : extractFields,
  serializeFields : serializeFields
};<|endoftext|>


<commit_before>function drawBlock(parent, width, depth) {
    var newWidth = Math.sqrt(2 * Math.pow(width / 2, 2));
    var color = 'rgba(' + [0, Math.floor(depth * 25), 100, (1 - depth / 40)].join(',') + ');';
    console.log(color);
    var style = 'width: ' + newWidth + 'px;height: ' + newWidth + 'px;background-color: ' + color;
    var left = $('

');
    var right = $('

');

    left.hide();
    right.hide();

    parent.append(left);
    parent.append(right);

    left.fadeIn(1000);
    right.fadeIn(1000);

    if (depth > 0) {
        setTimeout(function() {
            drawBlock(left, newWidth, depth - 1);
            drawBlock(right, newWidth, depth - 1);
        }, 500);
    }

}

// TODO - Append to DOM once it's completely built out
drawBlock($('#root'), 100, 7);<commit_msg>Add timer, only calculate values once for each depth level, and remove delayed 'growth' of tree<commit_after>console.time('tree');

// TODO - Return the entire HTML for that depth.
var map = {};
function getData(depth, width) {

    var color,
        left,
        nextWidth,
        right,
        style;

    if (!map[depth]) {

        color = 'rgba(' + [0, Math.floor(depth * 25), 100, (1 - depth / 40)].join(',') + ');';
        nextWidth = Math.sqrt(2 * Math.pow(width / 2, 2));
        style = 'width: ' + nextWidth + 'px;height: ' + nextWidth + 'px;background-color: ' + color;
        left = $('

');
        right = $('

');

        map[depth] = {
            left: left,
            nextWidth: nextWidth,
            right: right
        };
    }

    return map[depth];
}

function drawBlock(parent, width, depth) {
    var data  = getData(depth, width),
        left  = data.left.clone(),
        right = data.right.clone();

    parent.append(left);
    parent.append(right);

    if (depth > 0) {
        drawBlock(left, data.nextWidth, depth - 1);
        drawBlock(right, data.nextWidth, depth - 1);
    }

}

// TODO - Append to DOM once it's completely built out
drawBlock($('#root'), 100, 10);
console.timeEnd('tree');
<|endoftext|>


<commit_before>var ipAddress = process.env.PORT ? '0.0.0.0' : 'localhost';
var port = process.env.PORT || 8020;
var isProd = (ipAddress !== 'localhost');
var wsPort = (ipAddress === 'localhost') ? 16433 : 8080;
var baseUrl = (isProd) ? 'http://www.artywidget.com' : 'http://' + ipAddress;
baseUrl = 'http://artywidget.us-3.evennode.com';

if (!isProd && port !== 80) {
  baseUrl += ':' + port;
}

var baseSecureUrl = baseUrl.replace(/^http/, 'https');

module.exports = {
  ipAddress: ipAddress,
  port: port,
  isProd: isProd,
  wsPort: wsPort,
  baseUrl: baseUrl,
  baseSecureUrl: baseSecureUrl
};
<commit_msg>Testing<commit_after>var ipAddress = process.env.PORT ? '0.0.0.0' : 'localhost';
var port = process.env.PORT || 8020;
var isProd = (ipAddress !== 'localhost');
var wsPort = (ipAddress === 'localhost') ? 16433 : 8080;
var baseUrl = (isProd) ? 'http://www.artywidget.com' : 'http://' + ipAddress;
baseUrl = 'http://artywidget.us-3.evennode.com';
console.log('!!!', baseUrl);

if (!isProd && port !== 80) {
  baseUrl += ':' + port;
}

var baseSecureUrl = baseUrl.replace(/^http/, 'https');

module.exports = {
  ipAddress: ipAddress,
  port: port,
  isProd: isProd,
  wsPort: wsPort,
  baseUrl: baseUrl,
  baseSecureUrl: baseSecureUrl
};
<|endoftext|>


<commit_before>import fs from 'fs';
import fetch from 'node-fetch';
import formData from 'form-data';
import config from '../../server/config';

class live {

    constructor(options){

    }

    post(url, options, callback){
        callback = callback || function(){};
        let form = [];
        for(name in options){
          form.push(`${name}=${options[name]}`);
        }
        fetch(url, { method: 'POST', body: form.join("&") })
        .then(function(res) {
            return res.json();
        }).then(function(data) {
            callback(data)
        });
    }

    sendMessage(options, callback){
        callback = callback || function(){};
        let form = new formData();
        for(name in options){
            if(typeof options[name] == 'object'){
                for (var i = 0; i < options[name].length; i++) {
                    form.append(name+"[]", options[name][i]);
                };
            }else{
                form.append(name, options[name]);
            }
        }
        fetch(config.MANAGER_HOST, { method: 'POST', body: form })
        .then(function(res) {
            return res.text();
        }).then(function(body) {
            //console.info(options)
            //console.info(body)
            try{
                var data = JSON.parse(body);
                callback(null, data.message, data.result)
            }catch(e){
                callback(true, e, null)
            }
        });
    }

}

module.exports = new live()
<commit_msg>add redpacket +3<commit_after>import fs from 'fs';
import fetch from 'node-fetch';
import formData from 'form-data';
import config from '../../server/config';

class live {

    constructor(options){

    }

    post(url, options, callback){
        callback = callback || function(){};
        let form = [];
        for(name in options){
          form.push(`${name}=${options[name]}`);
        }
        fetch(url, { method: 'POST', body: form.join("&") })
        .then(function(res) {
            return res.json();
        }).then(function(data) {
            callback(data)
        });
    }

    sendMessage(options, callback){
        callback = callback || function(){};
        let form = new formData();
        for(name in options){
            if(options[name] instanceof Array){
                for (var i = 0; i < options[name].length; i++) {
                    form.append(name+"[]", options[name][i]);
                };
            }else{
                form.append(name, options[name]);
            }
        }
        fetch(config.MANAGER_HOST, { method: 'POST', body: form })
        .then(function(res) {
            return res.text();
        }).then(function(body) {
            //console.info(options)
            //console.info(body)
            try{
                var data = JSON.parse(body);
                callback(null, data.message, data.result)
            }catch(e){
                callback(true, e, null)
            }
        });
    }

}

module.exports = new live()
<|endoftext|>


<commit_before>/**
 * Resolves queries from a map to arrays of maps to field values.
 *
 * @param queries A map to arrays of maps to field values.
 * @param name Query name.
 * @param rank Query result rank.
 * @param field Query result field name.
 * @return Resolved value, or '' if there was an error.
 */
function queriesObjectReader (queries, name, rank, field) {
  const items = queries[name]
  if (rank - 1 >= items.length) {
    return ''
  }
  const item = queries[name][rank - 1]
  if (!Object.prototype.hasOwnProperty.call(item, field)) {
    return ''
  }
  return item[field]
}

/**
 * Resolves a query in an environment.
 *
 * @param env The environment to evaluate the query in.
 * @return A function that resolves a query given its name, rank, and field.
 */
function resolveQuery (env) {
  return (name, rank, field) => {
    if (!env || !env.queries || !Object.prototype.hasOwnProperty.call(env.queries, name)) {
      return ''
    }
    const reader = typeof env.queries === 'function'
      ? env.queries
      : queriesObjectReader.bind(null, env.queries)

    const value = reader(name, rank, field)
    if (['number', 'string'].indexOf(typeof value) === -1) {
      return ''
    }
    return value
  }
}

module.exports = {
  query: resolveQuery
}
<commit_msg>Fix bug when env.queries is a function<commit_after>/**
 * Resolves queries from a map to arrays of maps to field values.
 *
 * @param queries A map to arrays of maps to field values.
 * @param name Query name.
 * @param rank Query result rank.
 * @param field Query result field name.
 * @return Resolved value, or '' if there was an error.
 */
function queriesObjectReader (queries, name, rank, field) {
  if (!Object.prototype.hasOwnProperty.call(queries, name)) {
    return ''
  }
  const items = queries[name]
  if (rank - 1 >= items.length) {
    return ''
  }
  const item = queries[name][rank - 1]
  if (!Object.prototype.hasOwnProperty.call(item, field)) {
    return ''
  }
  return item[field]
}

/**
 * Resolves a query in an environment.
 *
 * @param env The environment to evaluate the query in.
 * @return A function that resolves a query given its name, rank, and field.
 */
function resolveQuery (env) {
  return (name, rank, field) => {
    if (!env || !env.queries) {
      return ''
    }
    const reader = typeof env.queries === 'function'
      ? env.queries
      : queriesObjectReader.bind(null, env.queries)

    const value = reader(name, rank, field)
    if (['number', 'string'].indexOf(typeof value) === -1) {
      return ''
    }
    return value
  }
}

module.exports = {
  query: resolveQuery
}
<|endoftext|>


<commit_before>function controllerDocumentPreview($scope, $uibModalInstance, attachment, $filter,$http, UsersService,AttachmentsService) {

    $scope.path = "" ;

    $scope.getPath = function () {
        return $scope.path ;
    }

    $scope.getAttachment = function () {
                return attachment ;
    }
    $scope.attachment = attachment ;

    $scope.attName =  attachment.name ;
    $scope.attCreated =  attachment.created ;
    $scope.attSize =  attachment.size ;
    $scope.attCreator = null ;
    UsersService.getById(attachment.creator)
        .then(user => $scope.attCreator = user.name);

    $scope.attPath =  attachment.path ;
    $scope.attPrint =  attachment.size ;
    $scope.attType =  attachment.attachmentType;

    $scope.previewWindow = function(document) {
        AttachmentsService.previewWindow(document);
    }

    $scope.previewAttachment = function () {

        attachment.documentType = attachment.attachmentType ? attachment.attachmentType : attachment.documentType ;
         var resPath = myTest(attachment) ;
         return resPath ;
    }

    $scope.previewAttachment() ;

    function myTest(document1) {
        // Check if need to view as pdf
        if ((document1.documentType == "docx") ||
            (document1.documentType == "doc") ||
            (document1.documentType == "xlsx") ||
            (document1.documentType == "xls") ||
            (document1.documentType == "ppt") ||
            (document1.documentType == "pptx")) {
            var arr = document1.path.split("." + document1.documentType);
            var ToHref = arr[0] + ".pdf";
            // Check if convert file exists allready
            $http({
                url: ToHref.replace('/files/', '/api/files/'),
                method: 'HEAD'
            }).success(function() {
                // There is allready the convert file
                $scope.path = ToHref + '?view=true' ;
            }).error(function() {
                // Send to server
                $http.post('/officeDocsAppend.js', document1)
                    .then(res => {
                        // The convert is OK and now we open the pdf to the client in new window
                        $scope.path = ToHref + '?view=true' ;
                    }).catch(err => {
                        console.error(err);
                    });
            });
        }
        // Format is NOT needed to view as pdf
        else {
            $scope.path = document1.path + '?view=true' ;
        }
    }

    $scope.ok = function (sendingForm) {
        $uibModalInstance.dismiss('cancel');
    };

    $scope.cancel = function () {
        $uibModalInstance.dismiss('cancel');
    };

}
<commit_msg> fixed preview with ftp<commit_after>function controllerDocumentPreview($scope, $uibModalInstance, attachment, $filter,$http, UsersService, AttachmentsService, DocumentsService) {

    $scope.path = "" ;

    $scope.getPath = function () {
        return $scope.path ;
    }

    $scope.getAttachment = function () {
                return attachment ;
    }
    $scope.attachment = attachment ;

    $scope.attName =  attachment.name ;
    $scope.attCreated =  attachment.created ;
    $scope.attSize =  attachment.size ;
    $scope.attCreator = null ;
    UsersService.getById(attachment.creator)
        .then(user => $scope.attCreator = user.name);

    $scope.attPath =  attachment.path ;
    $scope.attForFTPPath = $scope.attPath.substring($scope.attPath.indexOf("/files"),$scope.attPath.length);
    $scope.attForFTPPath  = $scope.attForFTPPath.replace(/\//g, '%2f');
    $scope.attPrint =  attachment.size ;
    $scope.attType =  attachment.attachmentType;

    $scope.previewWindow = function (document1) {
        if (document1.documentType == "docx" || document1.documentType == "doc" || document1.documentType == "xlsx" || document1.documentType == "xls" || document1.documentType == "ppt" || document1.documentType == "pptx") {
         var pdfPath = document1.path.replace("/files","/preview");

         pdfPath = pdfPath.substring(pdfPath.indexOf('preview'),pdfPath.lastIndexOf('.'));
         pdfPath = pdfPath+".pdf";
         pdfPath = pdfPath.replace(/\//g,"%2f");
         console.log(pdfPath);
         DocumentsService.viewFileLink(pdfPath).then(function(result){
             window.open(result + '?view=true');
             console.dir($scope.path);
         });

     }
     else{
         var picPath = document1.path;

         picPath = picPath.substring(picPath.indexOf('files'),picPath.length);
         picPath = picPath.replace(/\//g,"%2f");
         DocumentsService.viewFileLink(picPath).then(function(result){
             window.open(result + '?view=true');
         });

     }
 };

    $scope.previewAttachment = function () {

        attachment.documentType = attachment.attachmentType ? attachment.attachmentType : attachment.documentType ;
         var resPath = myTest(attachment) ;
         return resPath ;
    }

    $scope.previewAttachment() ;

    function myTest(document1) {

        if (document1.documentType == "docx" || document1.documentType == "doc" || document1.documentType == "xlsx" || document1.documentType == "xls" || document1.documentType == "ppt" || document1.documentType == "pptx" || document1.documentType == "tif") {
            var pdfPath = document1.path.replace("/files","/preview");

            pdfPath = pdfPath.substring(pdfPath.indexOf('preview'),pdfPath.lastIndexOf('.'));
            pdfPath = pdfPath+".pdf";
            pdfPath=pdfPath.replace(/\//g,"%2f");
            console.log(pdfPath);
            DocumentsService.viewFileLink(pdfPath,document1.documentType).then(function(result){
                $scope.path =  result + '?view=true';
            });

        }
        else{
            var pdfPath = document1.path;

            pdfPath = pdfPath.substring(pdfPath.indexOf('files'),pdfPath.length);
            pdfPath = pdfPath.replace(/\//g,"%2f");
            DocumentsService.viewFileLink(pdfPath).then(function(result){
                $scope.path =  result + '?view=true';
            });

        }
    }

/**
    function myTest(document1) {
        // Check if need to view as pdf
        if ((document1.documentType == "docx") ||
            (document1.documentType == "doc") ||
            (document1.documentType == "xlsx") ||
            (document1.documentType == "xls") ||
            (document1.documentType == "ppt") ||
            (document1.documentType == "pptx")) {
            var arr = document1.path.split("." + document1.documentType);
            var ToHref = arr[0] + ".pdf";
            // Check if convert file exists allready
            $http({
                url: ToHref.replace('/files/', '/api/files/'),
                method: 'HEAD'
            }).success(function() {
                // There is allready the convert file
                $scope.path = ToHref + '?view=true' ;
            }).error(function() {
                // Send to server
                $http.post('/officeDocsAppend.js', document1)
                    .then(res => {
                        // The convert is OK and now we open the pdf to the client in new window
                        $scope.path = ToHref + '?view=true' ;
                    }).catch(err => {
                        console.error(err);
                    });
            });
        }
        // Format is NOT needed to view as pdf
        else {
            $scope.path = document1.path + '?view=true' ;
        }
    }
 */
    $scope.ok = function (sendingForm) {
        $uibModalInstance.dismiss('cancel');
    };

    $scope.cancel = function () {
        $uibModalInstance.dismiss('cancel');
    };

}
<|endoftext|>


<commit_before>var settings = require('./settings');
var express = require('express');
var routes = require('./routes');
var http = require('http');
var path = require('path');
var partials = require('express-partials');
var session = require('express-session');
var redisStore = require('connect-redis')(session);
var app = express();
var server = http.Server(app);
var sessionStore = new redisStore();
var Contest = require('./models/contest.js');
var socket_opt = {};

//connect mongodb
require('./models/connect');

if (app.get('env') == 'production') {
  console.log('production env.');
  socket_opt  = {
    'browser client minification': true,
    'browser client etag': true,
    'browser client gzip': true,
    'origins': 'acdream.info:80 115.28.76.232:80'
  };
  app.enable('trust proxy');
  app.enable('view cache');
} else {
  console.log('development env.');
}

//服务器配置
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');

app.use(partials());

app.use(require('body-parser').urlencoded({
  extended: false
}));

//文件上传
app.use(require('multer')({
  dest: './uploads/',
  rename: function(fieldname, filename) {
    return filename;
  }
}));

//gzip压缩传输
app.use(require('compression')());

//express-session
app.use(session({
  secret: settings.cookie_secret,
  store: sessionStore,
  resave: false,
  saveUninitialized: false
}));

//使用静态资源服务以及设置缓存(三天)
app.use(express.static(__dirname + '/public', {
  maxAge: 259200000
}));

//favicon.ico缓存(30天)
app.use(require('serve-favicon')(__dirname + '/public/favicon.ico', {
  maxAge: 2592000000
}));

//控制台日志
app.use(require('morgan')('dev'));

app.use(function(req, res, next){
  req.session.reload(function(){
    res.locals.user = req.session.user;
    res.locals.time = (new Date()).getTime();
    res.locals.msg = req.session.msg;
    if (res.locals.msg) {
      req.session.msg = null;
    }
    next();
  });
});

//#####server response
//主页
app.get('/', routes.index);
//user页面
app.get('/user/:name', routes.user);
//上传头像页面
app.get('/avatar', routes.avatar);
//addproblem页面
app.get('/addproblem', routes.addproblem);
app.post('/addproblem', routes.doAddproblem);
//addcontest页面
app.get('/addcontest', routes.addcontest);
app.post('/addcontest', routes.doAddcontest);
//addtopic页面
app.get('/addtopic', routes.addtopic);
app.post('/addtopic', routes.doAddtopic);
//登出
app.post('/logout', routes.logout);
//problemset面
app.get('/problemset', routes.problemset);
//problem页面
app.get('/problem', routes.problem);
//题目代码文件上传功能
app.post('/problem', routes.upload);
//onecontest页面
app.get('/onecontest/:cid', routes.onecontest);
//status页面
app.get('/status', routes.status);
//ranklist页面
app.get('/ranklist', routes.ranklist);
//standings页面
app.get('/standings', routes.standings);
//contest页面
app.get('/contest/:type', routes.contest);
//topic页面
app.get('/topic', routes.topic);
//onetopic页面
app.get('/topic/:id', routes.onetopic);
//submit页面及submit动作
app.get('/submit', routes.submit);
app.post('/submit', routes.doSubmit);
//sourcecode页面
app.get('/sourcecode/:runid', routes.sourcecode);
//statistic页面
app.get('/statistic/:pid', routes.statistic);
app.get('*', function(req, res){
  res.render('404', {
    layout: null
  });
});

//#####jquery ajax
//注册
app.post('/doReg', routes.doReg);
//创建验证码
app.post('/createVerifycode', routes.createVerifycode);
//用户登录
app.post('/doLogin', routes.doLogin);
//登录私有比赛
app.post('/loginContest', routes.loginContest);
//1.获取题目, addcontest.js引用;
//2.获取题目全部信息, onecontest.js引用
app.post('/getProblem', routes.getProblem);
//删除一个比赛或考试
app.post('/contestDelete', routes.contestDelete);
//显示比赛的题目到problemset
app.post('/toggleHide', routes.toggleHide);
//公有VIPContest的注册
app.post('/contestReg', routes.contestReg);
//get user's AC or not AC records
app.post('/getOverview', routes.getOverview);
//updateStatus
app.post('/updateStatus', routes.updateStatus);
//get a page of contest status
app.post('/getStatus', routes.getStatus);
//get a page of contest ranklist
app.post('/getRanklist', routes.getRanklist);
//get a page of contest discuss
app.post('/getTopic', routes.getTopic);
//add a discuss to contest Discuss
app.post('/addDiscuss', routes.addDiscuss);
//user页面的修改他人加题权限功能(for admin)
app.post('/changeAddprob', routes.changeAddprob);
//user页面的修改信息功能(setting)
app.post('/changeInfo', routes.changeInfo);
//user页面将他人密码恢复默认, 即123456(for admin)
app.post('/restorePsw', routes.restorePsw);
//编辑题目分类标签
app.post('/editTag', routes.editTag);
//单题重判
app.post('/rejudge', routes.rejudge);
//单个提交重判
app.post('/singleRejudge', routes.singleRejudge);
//VIP Contest添加参赛者(for admin)
app.post('/addContestant', routes.addContestant);
//将指定用户从比赛中移除(for admin)
app.post('/regContestRemove', routes.regContestRemove);
//上传图片
app.post('/imgUpload', routes.imgUpload);
//上传头像
app.post('/avatarUpload', routes.avatarUpload);
//上传数据
app.post('/dataUpload', routes.dataUpload);
//删除题目数据
app.post('/delData', routes.delData);
//删除题目图片
app.post('/delImg', routes.delImg);
//获取指定runID的CE信息
app.post('/getCE', routes.getCE);
//重新统计所有用户提交数和AC数
app.post('/recal', routes.recal);
//统计比赛rating
app.post('/calRating', routes.calRating);
//撤销某场比赛的rating
app.post('/resetRating', routes.resetRating);
//切换指定用户打星状态
app.post('/toggleStar', routes.toggleStar);
//切换话题置顶状态
app.post('/toggleTop', routes.toggleTop);
//添加回复
app.post('/review', routes.review);
//删除回复
app.post('/delComment', routes.delComment);
//编辑回复
app.post('/editComment', routes.editComment);
//设置指定题目的管理员(for admin)
app.post('/setProblemManager', routes.setProblemManager);

//running server
server.listen(app.get('port'), function(){
  console.log("Server running at http://localhost:3000");
});

//socket
require('./socket')(require('socket.io')(server, socket_opt), sessionStore);
<commit_msg>change express-session's setting<commit_after>var settings = require('./settings');
var express = require('express');
var routes = require('./routes');
var http = require('http');
var path = require('path');
var partials = require('express-partials');
var session = require('express-session');
var redisStore = require('connect-redis')(session);
var app = express();
var server = http.Server(app);
var sessionStore = new redisStore();
var Contest = require('./models/contest.js');
var socket_opt = {};

//connect mongodb
require('./models/connect');

if (app.get('env') == 'production') {
  console.log('production env.');
  socket_opt  = {
    'browser client minification': true,
    'browser client etag': true,
    'browser client gzip': true,
    'origins': 'acdream.info:80 115.28.76.232:80'
  };
  app.enable('trust proxy');
  app.enable('view cache');
} else {
  console.log('development env.');
}

//服务器配置
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');

app.use(partials());

app.use(require('body-parser').urlencoded({
  extended: false
}));

//文件上传
app.use(require('multer')({
  dest: './uploads/',
  rename: function(fieldname, filename) {
    return filename;
  }
}));

//gzip压缩传输
app.use(require('compression')());

//express-session
app.use(session({
  secret: settings.cookie_secret,
  store: sessionStore,
  resave: false,
  saveUninitialized: true
}));

//使用静态资源服务以及设置缓存(三天)
app.use(express.static(__dirname + '/public', {
  maxAge: 259200000
}));

//favicon.ico缓存(30天)
app.use(require('serve-favicon')(__dirname + '/public/favicon.ico', {
  maxAge: 2592000000
}));

//控制台日志
app.use(require('morgan')('dev'));

app.use(function(req, res, next){
  req.session.reload(function(){
    res.locals.user = req.session.user;
    res.locals.time = (new Date()).getTime();
    res.locals.msg = req.session.msg;
    if (res.locals.msg) {
      req.session.msg = null;
    }
    next();
  });
});

//#####server response
//主页
app.get('/', routes.index);
//user页面
app.get('/user/:name', routes.user);
//上传头像页面
app.get('/avatar', routes.avatar);
//addproblem页面
app.get('/addproblem', routes.addproblem);
app.post('/addproblem', routes.doAddproblem);
//addcontest页面
app.get('/addcontest', routes.addcontest);
app.post('/addcontest', routes.doAddcontest);
//addtopic页面
app.get('/addtopic', routes.addtopic);
app.post('/addtopic', routes.doAddtopic);
//登出
app.post('/logout', routes.logout);
//problemset面
app.get('/problemset', routes.problemset);
//problem页面
app.get('/problem', routes.problem);
//题目代码文件上传功能
app.post('/problem', routes.upload);
//onecontest页面
app.get('/onecontest/:cid', routes.onecontest);
//status页面
app.get('/status', routes.status);
//ranklist页面
app.get('/ranklist', routes.ranklist);
//standings页面
app.get('/standings', routes.standings);
//contest页面
app.get('/contest/:type', routes.contest);
//topic页面
app.get('/topic', routes.topic);
//onetopic页面
app.get('/topic/:id', routes.onetopic);
//submit页面及submit动作
app.get('/submit', routes.submit);
app.post('/submit', routes.doSubmit);
//sourcecode页面
app.get('/sourcecode/:runid', routes.sourcecode);
//statistic页面
app.get('/statistic/:pid', routes.statistic);
app.get('*', function(req, res){
  res.render('404', {
    layout: null
  });
});

//#####jquery ajax
//注册
app.post('/doReg', routes.doReg);
//创建验证码
app.post('/createVerifycode', routes.createVerifycode);
//用户登录
app.post('/doLogin', routes.doLogin);
//登录私有比赛
app.post('/loginContest', routes.loginContest);
//1.获取题目, addcontest.js引用;
//2.获取题目全部信息, onecontest.js引用
app.post('/getProblem', routes.getProblem);
//删除一个比赛或考试
app.post('/contestDelete', routes.contestDelete);
//显示比赛的题目到problemset
app.post('/toggleHide', routes.toggleHide);
//公有VIPContest的注册
app.post('/contestReg', routes.contestReg);
//get user's AC or not AC records
app.post('/getOverview', routes.getOverview);
//updateStatus
app.post('/updateStatus', routes.updateStatus);
//get a page of contest status
app.post('/getStatus', routes.getStatus);
//get a page of contest ranklist
app.post('/getRanklist', routes.getRanklist);
//get a page of contest discuss
app.post('/getTopic', routes.getTopic);
//add a discuss to contest Discuss
app.post('/addDiscuss', routes.addDiscuss);
//user页面的修改他人加题权限功能(for admin)
app.post('/changeAddprob', routes.changeAddprob);
//user页面的修改信息功能(setting)
app.post('/changeInfo', routes.changeInfo);
//user页面将他人密码恢复默认, 即123456(for admin)
app.post('/restorePsw', routes.restorePsw);
//编辑题目分类标签
app.post('/editTag', routes.editTag);
//单题重判
app.post('/rejudge', routes.rejudge);
//单个提交重判
app.post('/singleRejudge', routes.singleRejudge);
//VIP Contest添加参赛者(for admin)
app.post('/addContestant', routes.addContestant);
//将指定用户从比赛中移除(for admin)
app.post('/regContestRemove', routes.regContestRemove);
//上传图片
app.post('/imgUpload', routes.imgUpload);
//上传头像
app.post('/avatarUpload', routes.avatarUpload);
//上传数据
app.post('/dataUpload', routes.dataUpload);
//删除题目数据
app.post('/delData', routes.delData);
//删除题目图片
app.post('/delImg', routes.delImg);
//获取指定runID的CE信息
app.post('/getCE', routes.getCE);
//重新统计所有用户提交数和AC数
app.post('/recal', routes.recal);
//统计比赛rating
app.post('/calRating', routes.calRating);
//撤销某场比赛的rating
app.post('/resetRating', routes.resetRating);
//切换指定用户打星状态
app.post('/toggleStar', routes.toggleStar);
//切换话题置顶状态
app.post('/toggleTop', routes.toggleTop);
//添加回复
app.post('/review', routes.review);
//删除回复
app.post('/delComment', routes.delComment);
//编辑回复
app.post('/editComment', routes.editComment);
//设置指定题目的管理员(for admin)
app.post('/setProblemManager', routes.setProblemManager);

//running server
server.listen(app.get('port'), function(){
  console.log("Server running at http://localhost:3000");
});

//socket
require('./socket')(require('socket.io')(server, socket_opt), sessionStore);
<|endoftext|>


<commit_before>import _ from 'lodash'

const getBaseUrl = function() {
	const { protocol } = _.get(window, 'location', { protocol: '', location: '', port: '' })
	// const { protocol, host }
	// if (window && host.indexOf('test.ru') !== -1) {
	// 	return `${protocol}//localhost:3000/api/`
	// }
	return `${protocol}//localhost:3000/api/`
}

const axiosDefaults = {
	baseURL: getBaseUrl(),
	timeout: 60000,
	withCredentials: false
}

const core = {
	locale: 'ru',
	localStorage: {
		name: 'cart'
	}
}

export default core
export { core, axiosDefaults }
<commit_msg>Change port<commit_after>import _ from 'lodash'

const getBaseUrl = function() {
	const { protocol, host } = _.get(window, 'location', { protocol: '', location: '', port: '' })
	if (window && host.indexOf('herokuapp') !== -1) {
		return `${protocol}//safe-atoll-35554.herokuapp.com/api/`
	}
	return `${protocol}//localhost:5000/api/`
}

const axiosDefaults = {
	baseURL: getBaseUrl(),
	timeout: 60000,
	withCredentials: false
}

const core = {
	locale: 'ru',
	localStorage: {
		name: 'cart'
	}
}

export default core
export { core, axiosDefaults }
<|endoftext|>


<commit_before>// Description:
//   [description]
//
// Dependencies:
//   hubot-slack
//
// Configuartion:
//   HUBOT_SLACKSCRUM_QA
//   HUBOT_SLACKSCRUM_QB
//   HUBOT_SLACKSCRUM_QC
//
// Commands:
//   hubot scrum start
//   next
//   next user <reason>
//
// Author:
//   @eseceve
var env = process.env;

var QUESTIONS = [
  env.HUBOT_SLACKSCRUM_QA ||
    'What did you do yesterday that helped the development team meet the' +
    ' sprint goal?',
  env.HUBOT_SLACKSCRUM_QB ||
    'What will you do today to help the development team meet the sprint goal?',
  env.HUBOT_SLACKSCRUM_QC ||
    'Do you see any impediment that prevents me or the development team from' +
    ' meeting the sprint goal?'
];

module.exports = function scrum(robot) {
  var slackAdapterClient = robot.adapter.client;

  robot.respond(/scrum start/i, start);
  robot.hear(/next/i, next);
  robot.hear(/next user(.*)/i, nextUser);

  function start(res) {
    var channel = _getChannel(res.message.room);
    var scrum;

    if (_scrumExists(channel)) return;
    scrum = _getScrum(channel);
    res.send("Hi <!channel>, let's start a new Scrum");

    _doQuestion(scrum);
  }

  function next(res) {
    var channel = _getChannel(res.message.room);
    var scrum;

    if (!_scrumExists(channel)) return;
    if (res.message.text.toLowerCase().trim() !== 'next') return;

    scrum = _getScrum(channel);

    if (scrum.question === QUESTIONS.length) return nextUser(res, true);
    _doQuestion(scrum);
  }

  function nextUser(res, force) {
    var channel = _getChannel(res.message.room);
    var text = res.message.text.toLowerCase();
    var reason;
    var scrum;
    var user;

    if (!_scrumExists(channel)) return;

    scrum = _getScrum(channel);
    reason = res.match[1];

    if (!force) {
      if (text.indexOf('next user') !== 0 || !scrum) return;
      if (!reason) return res.send(
        'Command `next user <reason>` require a <reason>');
    }

    if (reason) {
      user = scrum.members[scrum.user];
      scrum.reasons[user.id] = reason;
    } else {
      _saveAnswer(scrum);
    }

    scrum.user++;
    scrum.question = 0;
    if (scrum.user >= scrum.members.length) return _finish(scrum);
    next(res);
  }

  function _createScrum(channel) {
    var scrum = {
      answers: {},
      channel: channel,
      question: 0,
      reasons: {},
      user: 0
    };

    scrum.members = channel.members.map(function getUserObject(userID) {
      var user = slackAdapterClient.getUserByID(userID);
      return user;
    }).filter(function filterBots(user) {
      return !user.is_bot;
    });

    return scrum;
  }

  function _doQuestion(scrum) {
    var user = scrum.members[scrum.user];
    var message = '<@' + user.id + '> ' + QUESTIONS[scrum.question];

    _saveAnswer(scrum);
    scrum.channel.send(message);
    scrum.question++;
  }

  function _finish(scrum) {
    _saveAnswer(scrum);
    res.send("Thanks <!channel> for participating =)");
    // TODO: send email summary
    robot.brain.set(_getScrumID(scrum.channel), false);
  }

  function _getChannel(roomName) {
    var channel = slackAdapterClient.getChannelByName(roomName);

    if (!channel) channel = slackAdapterClient.getGroupByName(roomName);
    if (!channel) throw new Error('Room must be a channel or group');

    return channel;
  }

  function _getScrum(channel) {
    var scrum;

    if (_scrumExists(channel)) return robot.brain.get(_getScrumID(channel));

    scrum = _createScrum(channel);
    robot.brain.get(_getScrumID(channel), scrum);

    return scrum;
  }

  function _getScrumID(channel) {
    return 'HSS-'+channel.id;
  }

  function _saveAnswer(scrum) {
    // TODO: only save current answer
    // TODO: prevent save `undefined` question
    var history = scrum.channel.getHistory();
    var noMore = false;
    var user = scrum.members[scrum.user];

    if (!user || !scrum.user && !scrum.question) return;

    scrum.answers[user.id][scrum.question-1] = Object.keys(history)
      .reverse()
      .filter(function checkMessage(messageTS) {
        var message = history[messageTS];

        if (noMore) return false;
        if (!message) return false;
        if (message.user !== user.id) return false;

        if (message.text.indexOf('next') === 0) {
          noMore = true;
          return false;
        }

        return true;
      })
      .map(function getText(messageTS) {
        return history[messageTS].text;
      })
      .reverse();
  }

  function _scrumExists(channel) {
    return !!robot.brain.get(_getScrumID(channel));
  }
};
<commit_msg>fix(nextuser): do the first question to the next user<commit_after>// Description:
//   [description]
//
// Dependencies:
//   hubot-slack
//
// Configuartion:
//   HUBOT_SLACKSCRUM_QA
//   HUBOT_SLACKSCRUM_QB
//   HUBOT_SLACKSCRUM_QC
//
// Commands:
//   hubot scrum start
//   next
//   next user <reason>
//
// Author:
//   @eseceve
var env = process.env;

var QUESTIONS = [
  env.HUBOT_SLACKSCRUM_QA ||
    'What did you do yesterday that helped the development team meet the' +
    ' sprint goal?',
  env.HUBOT_SLACKSCRUM_QB ||
    'What will you do today to help the development team meet the sprint goal?',
  env.HUBOT_SLACKSCRUM_QC ||
    'Do you see any impediment that prevents me or the development team from' +
    ' meeting the sprint goal?'
];

module.exports = function scrum(robot) {
  var slackAdapterClient = robot.adapter.client;

  robot.respond(/scrum start/i, start);
  robot.hear(/next/i, next);
  robot.hear(/next user(.*)/i, nextUser);

  function start(res) {
    var channel = _getChannel(res.message.room);
    var scrum;

    if (_scrumExists(channel)) return;
    scrum = _getScrum(channel);
    res.send("Hi <!channel>, let's start a new Scrum");

    _doQuestion(scrum);
  }

  function next(res, force) {
    var channel = _getChannel(res.message.room);
    var scrum;

    if (!_scrumExists(channel)) return;
    if (!force && res.message.text.toLowerCase().trim() !== 'next') return;

    scrum = _getScrum(channel);

    if (scrum.question === QUESTIONS.length) return nextUser(res, true);
    _doQuestion(scrum);
  }

  function nextUser(res, force) {
    var channel = _getChannel(res.message.room);
    var text = res.message.text.toLowerCase();
    var reason;
    var scrum;
    var user;

    if (!_scrumExists(channel)) return;

    scrum = _getScrum(channel);
    reason = res.match[1];

    if (!force) {
      if (text.indexOf('next user') !== 0 || !scrum) return;
      if (!reason) return res.send(
        'Command `next user <reason>` require a <reason>');
    }

    if (reason) {
      user = scrum.members[scrum.user];
      scrum.reasons[user.id] = reason;
    } else {
      _saveAnswer(scrum);
    }

    scrum.user++;
    scrum.question = 0;
    if (scrum.user >= scrum.members.length) return _finish(scrum);
    next(res, true);
  }

  function _createScrum(channel) {
    var scrum = {
      answers: {},
      channel: channel,
      question: 0,
      reasons: {},
      user: 0
    };

    scrum.members = channel.members.map(function getUserObject(userID) {
      var user = slackAdapterClient.getUserByID(userID);
      return user;
    }).filter(function filterBots(user) {
      return !user.is_bot;
    });

    return scrum;
  }

  function _doQuestion(scrum) {
    var user = scrum.members[scrum.user];
    var message = '<@' + user.id + '> ' + QUESTIONS[scrum.question];

    _saveAnswer(scrum);
    scrum.channel.send(message);
    scrum.question++;
  }

  function _finish(scrum) {
    _saveAnswer(scrum);
    scrum.channel.send("Thanks <!channel> for participating =)");
    // TODO: send email summary
    robot.brain.set(_getScrumID(scrum.channel), false);
  }

  function _getChannel(roomName) {
    var channel = slackAdapterClient.getChannelByName(roomName);

    if (!channel) channel = slackAdapterClient.getGroupByName(roomName);
    if (!channel) throw new Error('Room must be a channel or group');

    return channel;
  }

  function _getScrum(channel) {
    var scrum;

    if (_scrumExists(channel)) return robot.brain.get(_getScrumID(channel));

    scrum = _createScrum(channel);
    robot.brain.set(_getScrumID(channel), scrum);

    return scrum;
  }

  function _getScrumID(channel) {
    return 'HSS-'+channel.id;
  }

  function _saveAnswer(scrum) {
    // TODO: only save current answer
    // TODO: prevent save `undefined` question
    var history = scrum.channel.getHistory();
    var noMore = false;
    var user = scrum.members[scrum.user];

    if (!user || !scrum.answers[user.id] || !scrum.user && !scrum.question) {
      return;
    }

    scrum.answers[user.id][scrum.question-1] = Object.keys(history)
      .reverse()
      .filter(function checkMessage(messageTS) {
        var message = history[messageTS];

        if (noMore) return false;
        if (!message) return false;
        if (message.user !== user.id) return false;

        if (message.text.indexOf('next') === 0) {
          noMore = true;
          return false;
        }

        return true;
      })
      .map(function getText(messageTS) {
        return history[messageTS].text;
      })
      .reverse();

    //TODO: robot.brain.set(_getScrumID(scrum.channel), scrum);
  }

  function _scrumExists(channel) {
    return !!robot.brain.get(_getScrumID(channel));
  }
};
<|endoftext|>