<commit_before>// Game: Use cowboys to have a good time and play some music on a Piano, while brawling with enemy Coyboys.

var Class = require("classe");
var log = require(__basedir + "/gameplay/log");
var TwoPlayerGame = require(__basedir + "/gameplay/shared/twoPlayerGame");
var TurnBasedGame = require(__basedir + "/gameplay/shared/turnBasedGame");
var TiledGame = require(__basedir + "/gameplay/shared/tiledGame");

//<<-- Creer-Merge: requires -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

// any additional requires you want can be required here safely between Creer re-runs

//<<-- /Creer-Merge: requires -->>

// @class Game: Use cowboys to have a good time and play some music on a Piano, while brawling with enemy Coyboys.
var Game = Class(TwoPlayerGame, TurnBasedGame, TiledGame, {
    /**
     * Initializes Games.
     *
     * @param {Object} data - a simple mapping passsed in to the constructor with whatever you sent with it. GameSettings are in here by key/value as well.
     */
    init: function(data) {
        TiledGame.init.apply(this, arguments);
        TurnBasedGame.init.apply(this, arguments);
        TwoPlayerGame.init.apply(this, arguments);

        //<<-- Creer-Merge: init -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        // these settings are all per side
        this._minFurnishings = 0;
        this._maxFurnishings = 5;
        this._minPianos = 2;
        this._maxPianos = 5;
        this._minHazards = 0;
        this._maxHazards = 6;

        // map dimensions used for tile generation
        this.mapWidth = 22;
        this.mapHeight = 12;

        // game constants
        this.rowdynessToSiesta = 8;
        this.siestaLength = 8;
        this.maxCowboysPerJob = 2;

        this.jobs.push(
            "Sharpshooter",
            "Bartender",
            "Brawler"
        );

        // list of cowboys to add to their cowboy lists between turns (so we don't resize arrays during players turns)
        this.spawnedCowboys = [];

        //<<-- /Creer-Merge: init -->>
    },

    name: "Saloon",

    aliases: [
        //<<-- Creer-Merge: aliases -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.
        "MegaMinerAI-18-Saloon",
        //<<-- /Creer-Merge: aliases -->>
    ],

    /**
     * This is called when the game begins, once players are connected and ready to play, and game objects have been initialized. Anything in init() may not have the appropriate game objects created yet..
     */
    begin: function() {
        TiledGame.begin.apply(this, arguments);
        TurnBasedGame.begin.apply(this, arguments);
        TwoPlayerGame.begin.apply(this, arguments);

        //<<-- Creer-Merge: begin -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        TiledGame._initMap.call(this);

        // make top and bottom sides walls
        for(var x = 0; x < this.mapWidth; x++) {
            this.getTile(x, 0).isBalcony = true;
            this.getTile(x, this.mapHeight - 1).isBalcony = true;
        }

        // make left and right sides walls
        for(var y = 0; y < this.mapHeight; y++) {
            this.getTile(0, y).isBalcony = true;
            this.getTile(this.mapWidth - 1, y).isBalcony = true;
        }

        // spawn some random furnishings in quadrants
        var numFurnishings = Math.randomInt(this._maxFurnishings, this._minFurnishings)*2; // *2 for each side
        var numPianos = Math.randomInt(this._maxPianos, this._minPianos)*2;
        var numHazards = Math.randomInt(this._maxHazards, this._minHazards)*2;
        while(numFurnishings > 0 || numPianos > 0 || numHazards > 0) { // while there is stuff to spawn
            while(true) { // get a random tile on this side that is empty
                x = Math.randomInt(this.mapWidth/2 - 1, 1);
                y = Math.randomInt(this.mapHeight - 2, 1);

                if(!this.getTile(x, y).furnishing) {
                    break; // because we found a tile that does not have a furnishing to spawn one on, else we continue our random search
                }
            }

            for(var side = 0; side < 2; side++) { // for each side (left and right)
                if(side === 1) { // if the right side, invert the x, y coordinate
                    x = this.mapWidth - x - 1;
                    y = this.mapHeight - y - 1;
                }

                if(numHazards > 0) { // if there are hazards to spawn
                    numHazards--;
                    this.getTile(x, y).hasHazard = true; // "spawn" it by setting that tile's hasHazard to true
                }
                else { // need to spawn a furnishing
                    this.create("Furnishing", {
                        tile: this.getTile(x, y),
                        isPiano: numPianos > 0, // if there are pianos to spawn make it one, else false and thus it is not a piano
                    });

                    if(numPianos > 0) { // decrement whatever we spawned
                        numPianos--;
                    }
                    else {
                        numFurnishings--;
                    }
                }
            }
        }

        // create the players' Young Guns
        for(var i = 0; i < this.players.length; i++) {
            var player = this.players[i];

            x = 0;
            y = 0;
            var dy = 1;
            if(i > 0) { // then change x, y for the second player
                x = this.mapWidth - 1;
                y = this.mapHeight - 1;
                dy = -1;
            }

            player.youngGun = this.create("YoungGun", {
                owner: player,
                tile: this.getTile(x, y),
                canCallIn: true,
            });

            player.youngGun.previousTile = this.getTile(x, y + dy); // used for moving the young guns around the map, but not a property exposed to clients
        }

        //<<-- /Creer-Merge: begin -->>
    },

    /**
     * This is called when the game has started, after all the begin()s. This is a good spot to send orders.
     */
    _started: function() {
        TiledGame._started.apply(this, arguments);
        TurnBasedGame._started.apply(this, arguments);
        TwoPlayerGame._started.apply(this, arguments);

        //<<-- Creer-Merge: _started -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.
        // any logic for _started can be put here
        //<<-- /Creer-Merge: _started -->>
    },

    //<<-- Creer-Merge: added-functions -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

    /**
     * Invoked when the current player ends their turn. Perform in between game logic here
     *
     * @override
     * @returns {*} passes through the default return value
     */
    nextTurn: function() {
        // before we go to the next turn, reset variables and do end of turn logic

        this._updateSpawnedCowboys();

        this._updateCowboys();
        this._advanceBottles();
        this._damagePianos();
        this._applyHazardDamage();

        this._cleanupArray("cowboys");
        this._cleanupArray("furnishings");
        this._cleanupArray("bottles");

        this._doYoungGun(this.currentPlayer);

        if(this._checkForWinner()) {
            return;
        }
        // else continue to the next player (normal next turn logic)
        return TurnBasedGame.nextTurn.apply(this, arguments);
    },

    /**
     * Take all the cowboys spawned during the turn and put them in the appropriate arrays
     */
    _updateSpawnedCowboys: function() {
        while(this.spawnedCowboys.length > 0) {
            var cowboy = this.spawnedCowboys.pop();

            this.cowboys.push(cowboy);
            cowboy.owner.cowboys.push(cowboy);
        }
    },

    /**
     * Updates all Cowboys (for the current player) between turns, reset variables and such
     */
    _updateCowboys: function() {
        for(var i = 0; i < this.currentPlayer.cowboys.length; i++) {
            var cowboy = this.currentPlayer.cowboys[i];

            if(cowboy.isDead) {
                continue; // don't update dead dudes, they won't come back
            }

            if(cowboy.isDrunk) { // move him!
                var next = cowboy.tile.getNeighbor(cowboy.drunkDirection);
                if(!next || next.isBalcony || next.cowboy || next.furnishing) { // then something is in the way
                    if(next) {
                        if(next.cowboy) {
                            next.cowboy.focus = 0;
                        }

                        if(next.isBalcony || next.furnishing) {
                            cowboy.damage(1);
                            if(cowboy.isDead) { // RIP he died from that
                                continue; // don't update dead dudes, they won't come back
                            }
                        }
                    }
                }
                else { // the next tile is valid
                    cowboy.tile.cowboy = null;
                    cowboy.tile = next;
                    next.cowboy = cowboy;
                }

                cowboy.turnsBusy = Math.max(0, cowboy.turnsBusy - 1);
                cowboy.isDrunk = (cowboy.turnsBusy !== 0);
                cowboy.canMove = !cowboy.isDrunk;
            }
            else { // they are not drunk, so update them for use next turn
                if(cowboy.job === "Sharpshooter" && cowboy.canMove) { // then the sharpshooter didn't move, so increase his focus
                    cowboy.focus++;
                }

                cowboy.canMove = true;
            }

            cowboy.turnsBusy = Math.max(0, cowboy.turnsBusy - 1);

            if(cowboy.job === "Brawler") { // damage surroundings
                var neighbors = cowboy.tile.getNeighbors();
                for(var j = 0; j < neighbors.length; j++) { // for each neighbor
                    var neighbor = neighbors[j];
                    if(neighbor.cowboy) { // if there is a cowboy, damage them
                        neighbor.cowboy.damage(1);
                    }
                    if(neighbor.furnishing) { // if there is a furnishing, damage it
                        neighbor.furnishing.damage(1);
                    }
                }
            }
        }
    },

    /**
     * Moves all bottles currenly in the game
     */
    _advanceBottles: function() {
        for(var i = 0; i < this.bottles.length; i++) {
            var bottle = this.bottles[i];
            if(bottle.isDestroyed) {
                continue;
            }

            bottle.advance();
        }
    },

    /**
     * Damages all pianos 1 damage, accelerating the game
     */
    _damagePianos: function() {
        for(var i = 0; i < this.furnishings.length; i++) {
            var furnishing = this.furnishings[i];

            if(furnishing.isDestroyed || !furnishing.isPiano) {
                continue;
            }
            // else it's a non destroyed piano, so damage it
            furnishing.damage(1);
        }
    },

    /**
     * Damages all cowboys which are standing on a hazard
     */
    _applyHazardDamage: function() {
        for(var i = 0; i < this.cowboys.length; i++) {
            var cowboy = this.cowboys[i];

            if (cowboy.tile && cowboy.tile.hasHazard) {
                cowboy.damage(1);
            }
        }
    },

    /**
     * Cleans up an array of dead game objects, done between turns so AIs don't have arrays resizing too much during gameplay
     *
     * @param {string} arrayKey - key to the array in this game instance
     */
    _cleanupArray: function(arrayKey) {
        var list = this[arrayKey];
        var clone = list.clone();
        for(var i = 0; i < clone.length; i++) {
            var item = clone[i];
            if(item.isDestroyed || item.isDead) {
                list.removeElement(item);

                if(item.owner) {
                    item.owner[arrayKey].removeElement(item);
                }
            }
        }
    },

    /**
     * Does Young Gun related logic: moving them clockwise
     *
     * @param {Player} player - the player to apply Young Gun logic to
     */
    _doYoungGun: function(player) {
        var youngGun = player.youngGun; // shorthand
        youngGun.canCallIn = true; // they can call in a cowboy in their next turn

        // find the adjacent tile that they were not on last turn,
        //   this way all YoungGuns continue walking clockwise
        var tiles = youngGun.tile.getNeighbors();
        for(var i = 0; i < tiles.length; i++) {
            var tile = tiles[i];

            if(tile.isBalcony && youngGun.previousTile !== tile) { // then this is the tile the young gun needs to talk to
                youngGun.previousTile = youngGun.tile;
                youngGun.tile.youngGun = null;
                youngGun.tile = tile;
                tile.youngGun = youngGun;

                break;
            }
        }
    },

    /**
     * Checks if someone won, and if so declares a winner
     *
     * @returns {boolean} true if there was a winner and the game is over, false otherwise
     */
    _checkForWinner: function() {
        var numberOfPianos = 0;
        for(var i = 0; i < this.furnishings.length; i++) {
            if(this.furnishings[i].isPiano) {
                numberOfPianos++;
            }
        }

        if(numberOfPianos === 0) { // game over
            this._makeSomeoneWin("all pianos destroyed.");
            return true;
        }

        return false;
    },

    /**
     * Makes someone win the game, and the rest lose
     *
     * @param {string} reason - reason we are making someone win the game
     * @returns {boolean} true if victory by game conditions, false otherwise (random winner)
     */
    _makeSomeoneWin: function(reason) {
        var players = this.players.clone();
        if(players[0].score !== players[1].score) { // someone won with a higher score
            players.sortDescending("score");

            this.declareWinner(players.shift(), "Has highest score after " + reason);
            this.declareLosers(players, "Lower score than winner");
            return true;
        }

        if(players[0].kills > players[1].kills) { // someone won with a higher kill count
            players.sortDescending("kills");

            this.declareWinner(players.shift(), "Has most kills after " + reason);
            this.declareLosers(players, "Less kills than winner");
            return true;
        }

        return this._endGameViaCoinFlip(); // if we got here primary and secondary win conditions failed, so win via coin flip
    },

    /**
     * Invoked when the maximum number of turns is reached, and a winner must be declared
     *
     * @override
     * @returns {*} passes through the default return value
     */
    _maxTurnsReached: function() {
        // When max turns are reached invoke primary/secondary win conditions
        this._makeSomeoneWin("max turns reached ({})".format(this.maxTurns));

        return TurnBasedGame._maxTurnsReached.apply(this, arguments);
    },

    //<<-- /Creer-Merge: added-functions -->>

});

module.exports = Game;
<commit_msg>Match formatting<commit_after>// Game: Use cowboys to have a good time and play some music on a Piano, while brawling with enemy Coyboys.

var Class = require("classe");
var log = require(__basedir + "/gameplay/log");
var TwoPlayerGame = require(__basedir + "/gameplay/shared/twoPlayerGame");
var TurnBasedGame = require(__basedir + "/gameplay/shared/turnBasedGame");
var TiledGame = require(__basedir + "/gameplay/shared/tiledGame");

//<<-- Creer-Merge: requires -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

// any additional requires you want can be required here safely between Creer re-runs

//<<-- /Creer-Merge: requires -->>

// @class Game: Use cowboys to have a good time and play some music on a Piano, while brawling with enemy Coyboys.
var Game = Class(TwoPlayerGame, TurnBasedGame, TiledGame, {
    /**
     * Initializes Games.
     *
     * @param {Object} data - a simple mapping passsed in to the constructor with whatever you sent with it. GameSettings are in here by key/value as well.
     */
    init: function(data) {
        TiledGame.init.apply(this, arguments);
        TurnBasedGame.init.apply(this, arguments);
        TwoPlayerGame.init.apply(this, arguments);

        //<<-- Creer-Merge: init -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        // these settings are all per side
        this._minFurnishings = 0;
        this._maxFurnishings = 5;
        this._minPianos = 2;
        this._maxPianos = 5;
        this._minHazards = 0;
        this._maxHazards = 6;

        // map dimensions used for tile generation
        this.mapWidth = 22;
        this.mapHeight = 12;

        // game constants
        this.rowdynessToSiesta = 8;
        this.siestaLength = 8;
        this.maxCowboysPerJob = 2;

        this.jobs.push(
            "Sharpshooter",
            "Bartender",
            "Brawler"
        );

        // list of cowboys to add to their cowboy lists between turns (so we don't resize arrays during players turns)
        this.spawnedCowboys = [];

        //<<-- /Creer-Merge: init -->>
    },

    name: "Saloon",

    aliases: [
        //<<-- Creer-Merge: aliases -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.
        "MegaMinerAI-18-Saloon",
        //<<-- /Creer-Merge: aliases -->>
    ],

    /**
     * This is called when the game begins, once players are connected and ready to play, and game objects have been initialized. Anything in init() may not have the appropriate game objects created yet..
     */
    begin: function() {
        TiledGame.begin.apply(this, arguments);
        TurnBasedGame.begin.apply(this, arguments);
        TwoPlayerGame.begin.apply(this, arguments);

        //<<-- Creer-Merge: begin -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        TiledGame._initMap.call(this);

        // make top and bottom sides walls
        for(var x = 0; x < this.mapWidth; x++) {
            this.getTile(x, 0).isBalcony = true;
            this.getTile(x, this.mapHeight - 1).isBalcony = true;
        }

        // make left and right sides walls
        for(var y = 0; y < this.mapHeight; y++) {
            this.getTile(0, y).isBalcony = true;
            this.getTile(this.mapWidth - 1, y).isBalcony = true;
        }

        // spawn some random furnishings in quadrants
        var numFurnishings = Math.randomInt(this._maxFurnishings, this._minFurnishings)*2; // *2 for each side
        var numPianos = Math.randomInt(this._maxPianos, this._minPianos)*2;
        var numHazards = Math.randomInt(this._maxHazards, this._minHazards)*2;
        while(numFurnishings > 0 || numPianos > 0 || numHazards > 0) { // while there is stuff to spawn
            while(true) { // get a random tile on this side that is empty
                x = Math.randomInt(this.mapWidth/2 - 1, 1);
                y = Math.randomInt(this.mapHeight - 2, 1);

                if(!this.getTile(x, y).furnishing) {
                    break; // because we found a tile that does not have a furnishing to spawn one on, else we continue our random search
                }
            }

            for(var side = 0; side < 2; side++) { // for each side (left and right)
                if(side === 1) { // if the right side, invert the x, y coordinate
                    x = this.mapWidth - x - 1;
                    y = this.mapHeight - y - 1;
                }

                if(numHazards > 0) { // if there are hazards to spawn
                    numHazards--;
                    this.getTile(x, y).hasHazard = true; // "spawn" it by setting that tile's hasHazard to true
                }
                else { // need to spawn a furnishing
                    this.create("Furnishing", {
                        tile: this.getTile(x, y),
                        isPiano: numPianos > 0, // if there are pianos to spawn make it one, else false and thus it is not a piano
                    });

                    if(numPianos > 0) { // decrement whatever we spawned
                        numPianos--;
                    }
                    else {
                        numFurnishings--;
                    }
                }
            }
        }

        // create the players' Young Guns
        for(var i = 0; i < this.players.length; i++) {
            var player = this.players[i];

            x = 0;
            y = 0;
            var dy = 1;
            if(i > 0) { // then change x, y for the second player
                x = this.mapWidth - 1;
                y = this.mapHeight - 1;
                dy = -1;
            }

            player.youngGun = this.create("YoungGun", {
                owner: player,
                tile: this.getTile(x, y),
                canCallIn: true,
            });

            player.youngGun.previousTile = this.getTile(x, y + dy); // used for moving the young guns around the map, but not a property exposed to clients
        }

        //<<-- /Creer-Merge: begin -->>
    },

    /**
     * This is called when the game has started, after all the begin()s. This is a good spot to send orders.
     */
    _started: function() {
        TiledGame._started.apply(this, arguments);
        TurnBasedGame._started.apply(this, arguments);
        TwoPlayerGame._started.apply(this, arguments);

        //<<-- Creer-Merge: _started -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.
        // any logic for _started can be put here
        //<<-- /Creer-Merge: _started -->>
    },

    //<<-- Creer-Merge: added-functions -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

    /**
     * Invoked when the current player ends their turn. Perform in between game logic here
     *
     * @override
     * @returns {*} passes through the default return value
     */
    nextTurn: function() {
        // before we go to the next turn, reset variables and do end of turn logic

        this._updateSpawnedCowboys();

        this._updateCowboys();
        this._advanceBottles();
        this._damagePianos();
        this._applyHazardDamage();

        this._cleanupArray("cowboys");
        this._cleanupArray("furnishings");
        this._cleanupArray("bottles");

        this._doYoungGun(this.currentPlayer);

        if(this._checkForWinner()) {
            return;
        }
        // else continue to the next player (normal next turn logic)
        return TurnBasedGame.nextTurn.apply(this, arguments);
    },

    /**
     * Take all the cowboys spawned during the turn and put them in the appropriate arrays
     */
    _updateSpawnedCowboys: function() {
        while(this.spawnedCowboys.length > 0) {
            var cowboy = this.spawnedCowboys.pop();

            this.cowboys.push(cowboy);
            cowboy.owner.cowboys.push(cowboy);
        }
    },

    /**
     * Updates all Cowboys (for the current player) between turns, reset variables and such
     */
    _updateCowboys: function() {
        for(var i = 0; i < this.currentPlayer.cowboys.length; i++) {
            var cowboy = this.currentPlayer.cowboys[i];

            if(cowboy.isDead) {
                continue; // don't update dead dudes, they won't come back
            }

            if(cowboy.isDrunk) { // move him!
                var next = cowboy.tile.getNeighbor(cowboy.drunkDirection);
                if(!next || next.isBalcony || next.cowboy || next.furnishing) { // then something is in the way
                    if(next) {
                        if(next.cowboy) {
                            next.cowboy.focus = 0;
                        }

                        if(next.isBalcony || next.furnishing) {
                            cowboy.damage(1);
                            if(cowboy.isDead) { // RIP he died from that
                                continue; // don't update dead dudes, they won't come back
                            }
                        }
                    }
                }
                else { // the next tile is valid
                    cowboy.tile.cowboy = null;
                    cowboy.tile = next;
                    next.cowboy = cowboy;
                }

                cowboy.turnsBusy = Math.max(0, cowboy.turnsBusy - 1);
                cowboy.isDrunk = (cowboy.turnsBusy !== 0);
                cowboy.canMove = !cowboy.isDrunk;
            }
            else { // they are not drunk, so update them for use next turn
                if(cowboy.job === "Sharpshooter" && cowboy.canMove) { // then the sharpshooter didn't move, so increase his focus
                    cowboy.focus++;
                }

                cowboy.canMove = true;
            }

            cowboy.turnsBusy = Math.max(0, cowboy.turnsBusy - 1);

            if(cowboy.job === "Brawler") { // damage surroundings
                var neighbors = cowboy.tile.getNeighbors();
                for(var j = 0; j < neighbors.length; j++) { // for each neighbor
                    var neighbor = neighbors[j];
                    if(neighbor.cowboy) { // if there is a cowboy, damage them
                        neighbor.cowboy.damage(1);
                    }
                    if(neighbor.furnishing) { // if there is a furnishing, damage it
                        neighbor.furnishing.damage(1);
                    }
                }
            }
        }
    },

    /**
     * Moves all bottles currenly in the game
     */
    _advanceBottles: function() {
        for(var i = 0; i < this.bottles.length; i++) {
            var bottle = this.bottles[i];
            if(bottle.isDestroyed) {
                continue;
            }

            bottle.advance();
        }
    },

    /**
     * Damages all pianos 1 damage, accelerating the game
     */
    _damagePianos: function() {
        for(var i = 0; i < this.furnishings.length; i++) {
            var furnishing = this.furnishings[i];

            if(furnishing.isDestroyed || !furnishing.isPiano) {
                continue;
            }
            // else it's a non destroyed piano, so damage it
            furnishing.damage(1);
        }
    },

    /**
     * Damages all cowboys which are standing on a hazard
     */
    _applyHazardDamage: function() {
        for(var i = 0; i < this.cowboys.length; i++) {
            var cowboy = this.cowboys[i];

            if(cowboy.tile && cowboy.tile.hasHazard) {
                cowboy.damage(1);
            }
        }
    },

    /**
     * Cleans up an array of dead game objects, done between turns so AIs don't have arrays resizing too much during gameplay
     *
     * @param {string} arrayKey - key to the array in this game instance
     */
    _cleanupArray: function(arrayKey) {
        var list = this[arrayKey];
        var clone = list.clone();
        for(var i = 0; i < clone.length; i++) {
            var item = clone[i];
            if(item.isDestroyed || item.isDead) {
                list.removeElement(item);

                if(item.owner) {
                    item.owner[arrayKey].removeElement(item);
                }
            }
        }
    },

    /**
     * Does Young Gun related logic: moving them clockwise
     *
     * @param {Player} player - the player to apply Young Gun logic to
     */
    _doYoungGun: function(player) {
        var youngGun = player.youngGun; // shorthand
        youngGun.canCallIn = true; // they can call in a cowboy in their next turn

        // find the adjacent tile that they were not on last turn,
        //   this way all YoungGuns continue walking clockwise
        var tiles = youngGun.tile.getNeighbors();
        for(var i = 0; i < tiles.length; i++) {
            var tile = tiles[i];

            if(tile.isBalcony && youngGun.previousTile !== tile) { // then this is the tile the young gun needs to talk to
                youngGun.previousTile = youngGun.tile;
                youngGun.tile.youngGun = null;
                youngGun.tile = tile;
                tile.youngGun = youngGun;

                break;
            }
        }
    },

    /**
     * Checks if someone won, and if so declares a winner
     *
     * @returns {boolean} true if there was a winner and the game is over, false otherwise
     */
    _checkForWinner: function() {
        var numberOfPianos = 0;
        for(var i = 0; i < this.furnishings.length; i++) {
            if(this.furnishings[i].isPiano) {
                numberOfPianos++;
            }
        }

        if(numberOfPianos === 0) { // game over
            this._makeSomeoneWin("all pianos destroyed.");
            return true;
        }

        return false;
    },

    /**
     * Makes someone win the game, and the rest lose
     *
     * @param {string} reason - reason we are making someone win the game
     * @returns {boolean} true if victory by game conditions, false otherwise (random winner)
     */
    _makeSomeoneWin: function(reason) {
        var players = this.players.clone();
        if(players[0].score !== players[1].score) { // someone won with a higher score
            players.sortDescending("score");

            this.declareWinner(players.shift(), "Has highest score after " + reason);
            this.declareLosers(players, "Lower score than winner");
            return true;
        }

        if(players[0].kills > players[1].kills) { // someone won with a higher kill count
            players.sortDescending("kills");

            this.declareWinner(players.shift(), "Has most kills after " + reason);
            this.declareLosers(players, "Less kills than winner");
            return true;
        }

        return this._endGameViaCoinFlip(); // if we got here primary and secondary win conditions failed, so win via coin flip
    },

    /**
     * Invoked when the maximum number of turns is reached, and a winner must be declared
     *
     * @override
     * @returns {*} passes through the default return value
     */
    _maxTurnsReached: function() {
        // When max turns are reached invoke primary/secondary win conditions
        this._makeSomeoneWin("max turns reached ({})".format(this.maxTurns));

        return TurnBasedGame._maxTurnsReached.apply(this, arguments);
    },

    //<<-- /Creer-Merge: added-functions -->>

});

module.exports = Game;
<|endoftext|>


<commit_before>function make_image_source(useVideo, fileList) {

  var videoSourceId;
  var selectedSource;

  var imageData;

  function getVideoSources(cbFun) {
    // Populate list of video sources e.g. laptop camera and USB webcam
    MediaStreamTrack.getSources(
      function( srcInfo ) {
        videoSourceId = srcInfo
          .filter( function(s) { return s.kind == "video"; } );
        selectedSource = videoSourceId[0].id;
        if (typeof cbFun !== 'undefined') {
          cbFun(videoSourceId);
        }
      }
    );
  }

  function setVideoInput(cbFun) {
    navigator.webkitGetUserMedia(
      {video: { optional:[{sourceId : selectedSource}] }, audio:false},
      cbFun,
      function (err) {
        console.log("Unable to get media stream:" + err.Code );
      });
  }

  return {
    init : getVideoSources,
    getSourceIds : function(){ return videoSourceId; },
    setSource : function(srcId) { selectedSource = srcId;},
    getSource : function() { return selectedSource;},
    setVideoInput: setVideoInput
  }
}

function make_image_source_view(image_source) {
  // DOM nodes
  var videoSelect;
  var video;
  var display;
  var context;

  var src = image_source;
  var samplePeriod = 40; // ms
  var averageFramePeriod = samplePeriod;
  var ewmaSmooth = 0.3;

  var isRunning = false;
  var isReadyToReceive = false;

  function setVideoCb () {
    console.log("camera changed from " +
      src.getSource() + " to " + videoSelect.value);
    src.setSource(videoSelect.value);
    src.setVideoInput(drawCb);
  }

  function drawCb(stream) {
    video.src = window.URL.createObjectURL(stream);
    start();
  }

  function start() {
    isRunning = true;
    draw();
  }

  function draw() {
    if (!isRunning) return; // Early exit
    // Draw from video v to canvas context c
    context.drawImage(video, 0, 0);
    setTimeout( draw, samplePeriod, video, context);
  }

  function setVideoSelectList(videoSourceId) {
    videoSourceId.map( function(s, ind) {
            // Set up list of cameras
            var option = document.createElement( "option" );
            option.text = "camera " + ind;
            option.value= s.id;
            videoSelect.appendChild( option );
            return s.id; } );
    setVideoCb();
    videoSelect.onchange = setVideoCb;
    videoSelect.hidden = false;
  }

  function getImageData( id ) {
    // Get image data from specified canvas
    var ctx = display.getContext( "2d" );
    var height = display.height;
    var width = display.width;
    var nBytes = height * width * 4;
    var pixels = ctx.getImageData(0, 0, width, height);
    var imData = { width: width, height: height,
      data: pixels.data.buffer };
    return imData;
  }

  function initControls(videoId, canvasId, selectId) {
  // DOM Nodes for controls
    video = document.getElementById( videoId );
    display = document.getElementById( canvasId );
    videoSelect = document.getElementById( selectId );
    context = display.getContext("2d");
    src.init(setVideoSelectList);
  }

  return {
    init: initControls,
    stop: function() { isRunning = false; },
    start: start,
    setSamplePeriod: function (period) {
      samplePeriod = Math.max(period,0); },
    getSamplePeriod: function () { return samplePeriod; },
    isRunning: function() { return isRunning; }
  }

}
<commit_msg>Get image data from view canvas or any other canvas.<commit_after>function make_image_source(useVideo, fileList) {

  var videoSourceId;
  var selectedSource;

  var imageData;

  function getVideoSources(cbFun) {
    // Populate list of video sources e.g. laptop camera and USB webcam
    MediaStreamTrack.getSources(
      function( srcInfo ) {
        videoSourceId = srcInfo
          .filter( function(s) { return s.kind == "video"; } );
        selectedSource = videoSourceId[0].id;
        if (typeof cbFun !== 'undefined') {
          cbFun(videoSourceId);
        }
      }
    );
  }

  function setVideoInput(cbFun) {
    navigator.webkitGetUserMedia(
      {video: { optional:[{sourceId : selectedSource}] }, audio:false},
      cbFun,
      function (err) {
        console.log("Unable to get media stream:" + err.Code );
      });
  }

  return {
    init : getVideoSources,
    getSourceIds : function(){ return videoSourceId; },
    setSource : function(srcId) { selectedSource = srcId;},
    getSource : function() { return selectedSource;},
    setVideoInput: setVideoInput
  }
}

function make_image_source_view(image_source) {
  // DOM nodes
  var videoSelect;
  var video;
  var display;
  var context;

  var src = image_source;
  var samplePeriod = 40; // ms
  var averageFramePeriod = samplePeriod;
  var ewmaSmooth = 0.3;

  var isRunning = false;
  var isReadyToReceive = false;

  function setVideoCb () {
    console.log("camera changed from " +
      src.getSource() + " to " + videoSelect.value);
    src.setSource(videoSelect.value);
    src.setVideoInput(drawCb);
  }

  function drawCb(stream) {
    video.src = window.URL.createObjectURL(stream);
    start();
  }

  function start() {
    isRunning = true;
    draw();
  }

  function draw() {
    if (!isRunning) return; // Early exit
    // Draw from video v to canvas context c
    context.drawImage(video, 0, 0);
    setTimeout( draw, samplePeriod, video, context);
  }

  function setVideoSelectList(videoSourceId) {
    videoSourceId.map( function(s, ind) {
            // Set up list of cameras
            var option = document.createElement( "option" );
            option.text = "camera " + ind;
            option.value= s.id;
            videoSelect.appendChild( option );
            return s.id; } );
    setVideoCb();
    videoSelect.onchange = setVideoCb;
    videoSelect.hidden = false;
  }

  function getImageData( id ) {
    // Get image data from specified canvas
    var cv;
    if (typeof id === 'undefined') {
      cv = display;
    } else {
      cv = document.getElementById( id );
    }
    var ctx = cv.getContext( "2d" );
    var height = cv.height;
    var width = cv.width;
    var nBytes = height * width * 4;
    var pixels = ctx.getImageData(0, 0, width, height);
    var imData = { width: width, height: height,
      data: pixels.data.buffer };
    return imData;
  }

  function initControls(videoId, canvasId, selectId) {
  // DOM Nodes for controls
    video = document.getElementById( videoId );
    display = document.getElementById( canvasId );
    videoSelect = document.getElementById( selectId );
    context = display.getContext("2d");
    src.init(setVideoSelectList);
  }

  return {
    init: initControls,
    stop: function() { isRunning = false; },
    start: start,
    setSamplePeriod: function (period) {
      samplePeriod = Math.max(period,0); },
    getSamplePeriod: function () { return samplePeriod; },
    getImageData: getImageData,
    isRunning: function() { return isRunning; }
  }

}
<|endoftext|>


<commit_before>// @flow

/* eslint-disable camelcase */
import TextContent from './textcontent';
import Finder from './finder';
import TextNodeXpath from './textnodexpath';
import Range from './range';
/* eslint-enable camelcase */

export type XpathSubject = {|
  start: { xpath: string, offset: number },
  end: { xpath: string, offset: number },
|};

/**
 * Class responsible for locating text in a `TextContent` instance from an
 * XPath representation and start and end offsets.
 */
class XpathFinder extends Finder {
  /**
   * Class constructor
   *
   * @param {TextContent} content - Reference to `TextContent` instance
   * @param {XpathSubject} subject - Descriptor containing an XPath representation and
   * start and end offsets.
   */
  // FIXME: what type is `subject`?
  constructor(content: TextContent, subject: XpathSubject) {
    // Construct base class
    super(content);

    if (subject.start.offset < 0 || subject.end.offset < 0) {
      throw new Error('Invalid or no XPath object specified');
    }

    // Compute text node start and end elements that the XPath representation refers to.
    let end;
    let xpath = new TextNodeXpath(this.content.root);
    let start = xpath.elementAt(subject.start.xpath);

    // If an element could not be obtained from the XPath representation, abort now (messages will
    // have been output).
    if (start === null) {
      return;
    }

    end = xpath.elementAt(subject.end.xpath);
    if (end === null) {
      return;
    }

    // Retrieve global character offset of the text node.
    start = content.find(start);
    end = content.find(end);
    if (start < 0 || end < 0) {
      console.error(
        'Unable to derive global offsets: %d:%d [xpath=%s:%s to end=%s:%s]',
        start,
        end,
        subject.start.xpath,
        subject.start.offset,
        subject.end.xpath,
        subject.end.offset
      );
      return;
    }

    // Retrieve offset markers.
    start = content.at(start).offset + subject.start.offset;
    end = content.at(end).offset + subject.end.offset - 1;

    /*     console.log("DEBUG start = ", start, "end = ", end, subject); */

    if (start > end) {
      throw new Error('Invalid XPath representation: start > end');
    }

    // Save global character offset and relative start and end offsets in descriptor.
    this.results.push({ start: start, end: end });
  }

  /**
   * Return next available match
   *
   * @returns {Range | null} Returns a `Range` if a match is available, or `null` if no more
   * matches are available.
   */
  next(): Range | null {
    if (this.current >= this.results.length) {
      return null;
    }

    const subject: any = this.results[this.current];
    if (subject == null) {
      throw new Error('Subject not found');
    }
    ++this.current;

    // TODO: we don't necessarily need to invoke getAt_ for the end offset.  A check has to be made
    // to ascertain if the end offset falls within the start node.
    return new Range(this.content, this.getAt_(subject.start), this.getAt_(subject.end));
  }
}

export default XpathFinder;
<commit_msg>src: prefer short form<commit_after>// @flow

/* eslint-disable camelcase */
import TextContent from './textcontent';
import Finder from './finder';
import TextNodeXpath from './textnodexpath';
import Range from './range';
/* eslint-enable camelcase */

export type XpathSubject = {|
  start: { xpath: string, offset: number },
  end: { xpath: string, offset: number },
|};

/**
 * Class responsible for locating text in a `TextContent` instance from an
 * XPath representation and start and end offsets.
 */
class XpathFinder extends Finder {
  /**
   * Class constructor
   *
   * @param {TextContent} content - Reference to `TextContent` instance
   * @param {XpathSubject} subject - Descriptor containing an XPath representation and
   * start and end offsets.
   */
  // FIXME: what type is `subject`?
  constructor(content: TextContent, subject: XpathSubject) {
    // Construct base class
    super(content);

    if (subject.start.offset < 0 || subject.end.offset < 0) {
      throw new Error('Invalid or no XPath object specified');
    }

    // Compute text node start and end elements that the XPath representation refers to.
    let end;
    let xpath = new TextNodeXpath(this.content.root);
    let start = xpath.elementAt(subject.start.xpath);

    // If an element could not be obtained from the XPath representation, abort now (messages will
    // have been output).
    if (start === null) {
      return;
    }

    end = xpath.elementAt(subject.end.xpath);
    if (end === null) {
      return;
    }

    // Retrieve global character offset of the text node.
    start = content.find(start);
    end = content.find(end);
    if (start < 0 || end < 0) {
      console.error(
        'Unable to derive global offsets: %d:%d [xpath=%s:%s to end=%s:%s]',
        start,
        end,
        subject.start.xpath,
        subject.start.offset,
        subject.end.xpath,
        subject.end.offset
      );
      return;
    }

    // Retrieve offset markers.
    start = content.at(start).offset + subject.start.offset;
    end = content.at(end).offset + subject.end.offset - 1;

    /*     console.log("DEBUG start = ", start, "end = ", end, subject); */

    if (start > end) {
      throw new Error('Invalid XPath representation: start > end');
    }

    // Save global character offset and relative start and end offsets in descriptor.
    this.results.push({ start, end });
  }

  /**
   * Return next available match
   *
   * @returns {Range | null} Returns a `Range` if a match is available, or `null` if no more
   * matches are available.
   */
  next(): Range | null {
    if (this.current >= this.results.length) {
      return null;
    }

    const subject: any = this.results[this.current];
    if (subject == null) {
      throw new Error('Subject not found');
    }
    ++this.current;

    // TODO: we don't necessarily need to invoke getAt_ for the end offset.  A check has to be made
    // to ascertain if the end offset falls within the start node.
    return new Range(this.content, this.getAt_(subject.start), this.getAt_(subject.end));
  }
}

export default XpathFinder;
<|endoftext|>


<commit_before>import React from 'React';
import Ad_comment from './ad_comment';
import Ad_commentThread from './ad_commentThread';
import Ad_participates_item from './ad_participates_item';
import Ad_signeduser from './ad_signeduser'
import {getActivityDetail} from '../server';
import {adpostComment,sendJoinActivityRequest} from '../server';
import {likeActivity} from '../server';
import {unLikeActivity} from '../server';
import {Link} from 'react-router';
var moment = require('moment');
import {socket,getToken} from '../credentials';
import {hideElement} from '../util';
// var debug = require('react-debug');

export default class Ad_body extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      activity: {},
      ishost: "",
      joined: false,
      success:false
    };
  }

  didUserLike(user) {
    var likeCounter = this.state.activity.likeCounter;
    for (var i = 0; i < likeCounter.length; i++) {
      if (likeCounter[i]._id === user)
        return true;
    }
    return false;
  }

  handleLikeClick(e){
    e.preventDefault();

    if(e.button === 0){
      var cb = (likeCounter) => {
        var activity = this.state.activity;
        activity.likeCounter = likeCounter;
        this.setState(
          {activity:activity}
        );
      };

      if(!this.didUserLike(this.props.currentUser)){
        likeActivity(this.state.activity._id,this.props.currentUser,cb);
      }
      else{
        unLikeActivity(this.state.activity._id,this.props.currentUser,cb);
      }
    }
  }

  handlePostComment(comment){
    adpostComment(this.state.activity._id, this.props.currentUser ,comment, (newFeedItem)=>{
      this.setState({activity:newFeedItem});
    })
  }

  getData(){
    getActivityDetail(this.props.id,(activitydata)=>{
      this.setState({activity:activitydata},()=>{
        if(this.isHost()){
          this.setState({ishost:"disabled"});
        }
        if(this.checkJoined()){
          this.setState({ishost:"disabled"});
          this.setState({joined:true});
        }
      });
    });

  }

  isHost(){
    return this.props.currentUser === this.state.activity.author._id;
  }

  checkJoined(){
    if(this.state.activity.participants===undefined){
      return false;
    }
    return this.state.activity.participants.filter((user)=>{
      if(user._id===this.props.currentUser)
        return true;
      else return false;
    }).length>0;
  }

  handleRequestJoin(e){
    e.preventDefault();
    sendJoinActivityRequest(this.props.currentUser,this.state.activity.author._id,  this.state.activity._id,(success)=>{
      if(success){
        socket.emit('notification',{
          authorization:getToken(),
          sender: this.props.currentUser,
          target: this.state.activity.author._id
        });
        this.setState({
          success:true
        });

      }
    });
  }

  componentDidMount(){
    this.getData();
  }

  render(){
    var buttonText;
    if(this.state.ishost==="disabled"&&this.state.joined!=true){
      buttonText = "You are the host"
    }
    else if(this.state.ishost==="disabled"&&this.state.joined==true){
      buttonText = "You have joined"
    }
    else{
        buttonText = "Click to sign up"
    }
    var data = this.state.activity
    var contents;
    var text;
    var name;
    var authorid;
    switch(data.type){
      case "Event":
      contents = data.contents;
      name = this.state.activity.author.fullname;
      authorid = this.state.activity.author._id;
        text = contents.text.split("\n").map((line, i) => {
          return (

{line}

          )                       ;
        })
          break;
      case "Entertainment":
      contents = data.contents;
      name = this.state.activity.author.fullname;
      authorid = this.state.activity.author._id;
        text = contents.text.split("\n").map((line, i) => {
          return (

{line}

          )                       ;
        })
          break;
      case "Study":
        contents = data.contents;
        name = this.state.activity.author.fullname;
        authorid = this.state.activity.author._id;
          text = contents.text.split("\n").map((line, i) => {
            return (

{line}

            )                       ;
          })
        break;
      default:
        text = null;
        name = null;
    }

    return(

                <button type="button" className="close" data-dismiss="modal" aria-label="Close">
                  ×
                </button>

 Participating users

                  {this.state.activity.participants === undefined ? 0:
                    (this.state.activity.participants.length === 0 ? "No one has signed up yet!" :
                    this.state.activity.participants.map((p,i)=>{
                    return (
                      <Ad_participates_item key={i} data={p} currUser={this.props.currentUser} friends={this.props.friends} />
                    )
                  }))}

{this.state.activity.title}

                        {moment(this.state.activity.startTime).format('MMMM Do YYYY, h:mm:ss a')}

                      {this.state.activity.location}

                      <Link to={"profile/"+authorid}>
                          {name}
                        </Link>

                        {this.state.activity.participants === undefined ? 0:this.state.activity.participants.length} people signed up

                      View All

                      {this.state.activity.participants === undefined ? 0:this.state.activity.participants.map((p,i)=>{
                        return (<Ad_signeduser key={i} data={p} />)
                      })}

Request sent!

                    this.handleRequestJoin(e)} align="center">
                    {buttonText}

                    this.handleLikeClick(e)}>
                      {this.state.activity.likeCounter === undefined ? 0:this.state.activity.likeCounter.length}

                    {this.state.activity.comments === undefined ? 0:this.state.activity.comments.length}

Activity Details

                      {text}

    <Ad_commentThread count={this.state.activity.comments === undefined ? 0:this.state.activity.comments.length} user={this.props.currentUser} avatar={this.props.avatar} onPost={(comment)=>this.handlePostComment(comment)}>
      {this.state.activity.comments === undefined ? 0:this.state.activity.comments.map((comment,i)=>{
        return (
          <Ad_comment key={i} data={comment} />
        )
      })}
    </Ad_commentThread>

  )
}

}
<commit_msg>fix button disabled bug<commit_after>import React from 'React';
import Ad_comment from './ad_comment';
import Ad_commentThread from './ad_commentThread';
import Ad_participates_item from './ad_participates_item';
import Ad_signeduser from './ad_signeduser'
import {getActivityDetail} from '../server';
import {adpostComment,sendJoinActivityRequest} from '../server';
import {likeActivity} from '../server';
import {unLikeActivity} from '../server';
import {Link} from 'react-router';
var moment = require('moment');
import {socket,getToken} from '../credentials';
import {hideElement} from '../util';
var debug = require('react-debug');

export default class Ad_body extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      activity: {},
      ishost: "",
      joined: false,
      success:false,
    };
  }

  didUserLike(user) {
    var likeCounter = this.state.activity.likeCounter;
    for (var i = 0; i < likeCounter.length; i++) {
      if (likeCounter[i]._id === user)
        return true;
    }
    return false;
  }

  handleLikeClick(e){
    e.preventDefault();

    if(e.button === 0){
      var cb = (likeCounter) => {
        var activity = this.state.activity;
        activity.likeCounter = likeCounter;
        this.setState(
          {activity:activity}
        );
      };

      if(!this.didUserLike(this.props.currentUser)){
        likeActivity(this.state.activity._id,this.props.currentUser,cb);
      }
      else{
        unLikeActivity(this.state.activity._id,this.props.currentUser,cb);
      }
    }
  }

  handlePostComment(comment){
    adpostComment(this.state.activity._id, this.props.currentUser ,comment, (newFeedItem)=>{
      this.setState({activity:newFeedItem});
    })
  }

  getData(){
    getActivityDetail(this.props.id,(activitydata)=>{
      this.setState({activity:activitydata},()=>{
        if(this.isHost()){
          this.setState({ishost:"disabled"});
        }
        if(this.checkJoined()){
          this.setState({ishost:"disabled"});
          this.setState({joined:true});
        }
      });
    });

  }

  isHost(){
    return this.props.currentUser === this.state.activity.author._id;
  }

  checkJoined(){
    if(this.state.activity.participants===undefined){
      return false;
    }
    return this.state.activity.participants.filter((user)=>{
      if(user._id===this.props.currentUser)
        return true;
      else return false;
    }).length>0;
  }

  handleRequestJoin(e){
    e.preventDefault();
    sendJoinActivityRequest(this.props.currentUser,this.state.activity.author._id,  this.state.activity._id,(success)=>{
      if(success){
        socket.emit('notification',{
          authorization:getToken(),
          sender: this.props.currentUser,
          target: this.state.activity.author._id
        });
        this.setState({
          success:true
        });

      }
    });
  }

  componentDidMount(){
    this.getData();
  }

  render(){
    var buttonText;
    if(this.state.ishost==="disabled"&&this.state.joined!=true){
      buttonText = "You are the host"
    }
    else if(this.state.ishost==="disabled"&&this.state.joined==true){
      buttonText = "You have joined"
    }
    else{
        buttonText = "Click to sign up"
    }
    var data = this.state.activity
    var contents;
    var text;
    var name;
    var authorid;
    switch(data.type){
      case "Event":
      contents = data.contents;
      name = this.state.activity.author.fullname;
      authorid = this.state.activity.author._id;
        text = contents.text.split("\n").map((line, i) => {
          return (

{line}

          )                       ;
        })
          break;
      case "Entertainment":
      contents = data.contents;
      name = this.state.activity.author.fullname;
      authorid = this.state.activity.author._id;
        text = contents.text.split("\n").map((line, i) => {
          return (

{line}

          )                       ;
        })
          break;
      case "Study":
        contents = data.contents;
        name = this.state.activity.author.fullname;
        authorid = this.state.activity.author._id;
          text = contents.text.split("\n").map((line, i) => {
            return (

{line}

            )                       ;
          })
        break;
      default:
        text = null;
        name = null;
    }

    return(

                <button type="button" className="close" data-dismiss="modal" aria-label="Close">
                  ×
                </button>

 Participating users

                  {this.state.activity.participants === undefined ? 0:
                    (this.state.activity.participants.length === 0 ? "No one has signed up yet!" :
                    this.state.activity.participants.map((p,i)=>{
                    return (
                      <Ad_participates_item key={i} data={p} currUser={this.props.currentUser} friends={this.props.friends} />
                    )
                  }))}

{this.state.activity.title}

                        {moment(this.state.activity.startTime).format('MMMM Do YYYY, h:mm:ss a')}

                      {this.state.activity.location}

                      <Link to={"profile/"+authorid}>
                          {name}
                        </Link>

                        {this.state.activity.participants === undefined ? 0:this.state.activity.participants.length} people signed up

                      View All

                      {this.state.activity.participants === undefined ? 0:this.state.activity.participants.map((p,i)=>{
                        return (<Ad_signeduser key={i} data={p} />)
                      })}

Request sent!

                    {
                      if(this.state.ishost!=="disabled"){
                      this.handleRequestJoin(e)
                      }
                      else{
                        return
                      }
                    }
                  } align="center">
                    {buttonText}

                    this.handleLikeClick(e)}>
                      {this.state.activity.likeCounter === undefined ? 0:this.state.activity.likeCounter.length}

                    {this.state.activity.comments === undefined ? 0:this.state.activity.comments.length}

Activity Details

                      {text}

    <Ad_commentThread count={this.state.activity.comments === undefined ? 0:this.state.activity.comments.length} user={this.props.currentUser} avatar={this.props.avatar} onPost={(comment)=>this.handlePostComment(comment)}>
      {this.state.activity.comments === undefined ? 0:this.state.activity.comments.map((comment,i)=>{
        return (
          <Ad_comment key={i} data={comment} />
        )
      })}
    </Ad_commentThread>

  )
}

}
<|endoftext|>


<commit_before>/*
 * This file contains common variables, datatypes and functions used by all
 * cellular automata in this folder.
 *
 * Copyright (c) 2015 Johannes Maibaum <jmaibaum@gmail.com>
 */

var Settings = {
    boardDivision: 0,
    boardSideLenght: 0,
    canvas: undefined,
    context: undefined,
    cellsPerLine: 50,        // Devides the canvas into 'n' cellsPerLine.
    cellSize: 0,
    cellColor: ["#ffffff"],  // Needs to be extended by final game with colors.
    cellToDraw: 1,           // Default cell type for drawing operations.
    intervalID: 0,
    speed: 100
};

// Wait for DOM, then initialize.
document.addEventListener("DOMContentLoaded", initialize, false);

// Custom datatypes for generic Cells and a playing Field:
var Cell = function ()
{
    this.state = 0;
    this.neighbours = 0;
}

var Field = function (rows, columns)
{
    this.rows  = rows;
    this.columns = columns;
    this.generation = 0;
    this.generationHTML = undefined;  // Needs to be set by initialize().
    this.population = 0;
    this.oldPopulaton = 0;
    this.populationHTML = undefined;  // Needs to be set by initialize().
    this.data = [];

    for (var i = 0; i < this.rows + 2; i++) {
        this.data[i] = [];

        for (var j = 0; j < this.columns + 2; j++)
            this.data[i].push(new Cell());
    }
}

Field.prototype.updateNeighbours = function (row, column, increment)
{
    this.data[row - 1][column - 1].neighbours += increment;
    this.data[row - 1][column].neighbours     += increment;
    this.data[row - 1][column + 1].neighbours += increment;
    this.data[row][column - 1].neighbours     += increment;
    this.data[row][column + 1].neighbours     += increment;
    this.data[row + 1][column - 1].neighbours += increment;
    this.data[row + 1][column].neighbours     += increment;
    this.data[row + 1][column + 1].neighbours += increment;
}

Field.prototype.resetAllNeighbours = function()
{
    for (var row in this.data)
        for (var column in this.data[row])
            this.data[row][column].neighbours = 0;
}

Field.prototype.increaseGeneration = function()
{
    this.generation++;
    this.generationHTML.innerHTML = this.generation;
}

Field.prototype.updatePopulationHTML = function ()
{
    if (this.population != this.oldPopulation) {
        this.populationHTML.innerHTML = this.population;
        this.oldPopulation = this.population;
    }
}

Field.prototype.clear = function ()
{
    for (var row in this.data)
        for (var column in this.data[row]) {
            this.createCell(row, column, 0);
            drawCell(row, column, 0);
            this.updatePopulationHTML();
        }

    // Reset generation counter.
    this.generation = 0;
    this.generationHTML.innerHTML = this.generation;
}

// Initialize the canvas and add the event listener.
function initialize()
{
    // Prepare canvas:
    Settings.canvas = document.getElementById("board");
    Settings.context = Settings.canvas.getContext("2d");
    field.generationHTML = document.getElementById("Generation");
    field.populationHTML = document.getElementById("Population");

    Settings.boardSideLength = Settings.canvas.width;  // Assume as quadratic.
    Settings.boardDivision = Math.floor(Settings.boardSideLength /
                                        Settings.cellsPerLine);
    Settings.cellSize = Settings.boardDivision - 2;

    // Draw cell grid:
    Settings.context.strokeStyle = "#cccccc";
    Settings.context.lineWidth = 2;

    for (var i = 0; i <= Settings.boardSideLength;
         i = i + Settings.boardDivision) {
        Settings.context.moveTo(0, i);
        Settings.context.lineTo(Settings.boardSideLength, i);
        Settings.context.moveTo(i, 0);
        Settings.context.lineTo(i, Settings.boardSideLength);
        Settings.context.stroke();
    }

    // Add event listeners:
    Settings.canvas.addEventListener("mousedown", drawCellFromMouseClick,
                                     false);

    // Setup for dropping field files on the canvas:
    var dropZone = Settings.canvas;
    dropZone.addEventListener("dragenter", dragenter, false);
    dropZone.addEventListener("dragover", dragover, false);
    dropZone.addEventListener("drop", drop, false);
}

/*
 * Callback function to determine the mouse click coordinates on the canvas.
 * Adapted from:
 * http://miloq.blogspot.de/2011/05/coordinates-mouse-click-canvas.html
 */
function drawCellFromMouseClick(event)
{
    var x = event.x;
    var y = event.y;

    // Firefox workaround:
    if (x == undefined && y == undefined) {
        x = event.clientX + document.body.scrollLeft +
            document.documentElement.scrollLeft;
        y = event.clientY + document.body.scrollTop +
            document.documentElement.scrollTop;
    }

    x -= Settings.canvas.offsetLeft;
    y -= Settings.canvas.offsetTop;

    var coords = convertClickToFieldCoordinates(x, y);
    var row    = coords[0];
    var column = coords[1];

    if (field.createCell(row, column, Settings.cellToDraw, 1))
        drawCell(row, column, Settings.cellToDraw);
    else {
        field.createCell(row, column, 0, -1);
        drawCell(row, column, 0);
    }
}

function convertClickToFieldCoordinates(x, y) {
    var row = Math.floor(y / Settings.boardDivision) + 1;
    var column = Math.floor(x / Settings.boardDivision) + 1;

    // Ensure that we get valid coordinates:
    if (row > field.rows)
        row--;

    if (column > field.columns)
        column--;

    return [row, column];
}

// Draw a cell using field coordinates:
function drawCell(row, column, state)
{
    // Set Cell color:
    Settings.context.fillStyle = Settings.cellColor[state];

    var drawx = Math.floor(column * Settings.boardDivision)
        - Settings.boardDivision + 1;
    var drawy = Math.floor(row * Settings.boardDivision)
        - Settings.boardDivision + 1;

    Settings.context.fillRect(drawx, drawy, Settings.cellSize,
                              Settings.cellSize);
}

// Wrapper for field.clear():
function clearField()
{
    field.clear();
}

// Wrapper for next generation method:
function nextGeneration()
{
    if (!Settings.intervalID)
        field.nextGeneration();
    else
        stopAutomata();
}

function changeSpeed(selectItem)
{
    Settings.speed = selectItem.value;

    if (Settings.intervalID) {
        stopAutomata();
        runAutomata();
    }
}

function runAutomata()
{
    if (!Settings.intervalID)
        Settings.intervalID = window.setInterval(nextGenerationFromInterval,
                                                 Settings.speed);
}

function stopAutomata()
{
    window.clearInterval(Settings.intervalID);
    Settings.intervalID = 0;
}

function nextGenerationFromInterval()
{
    field.nextGeneration();
}

/*
 * Save field to and load field from files.
 * Adapted from:
 * https://thiscouldbebetter.wordpress.com/2012/12/18/loading-editing-and-
 * saving-a-text-file-in-html5-using-javascrip/
 */
function saveFile()
{
    var textToWrite = '';

    // Loop over field to construct text string:
    for (var row = 1; row <= field.rows; row++) {
        for (var column = 1; column <= field.columns; column++) {
            textToWrite += field.generateChar(row, column);
        }
        textToWrite += '\n';
    }

    var fileName = document.getElementById("FileName").value;
    var textFileAsBlob = new Blob([textToWrite], {type:'text/plain'});
    var downloadLink = document.createElement("a");
    downloadLink.download = fileName;
    downloadLink.innerHTML = "Download File";

    if (window.webkitURL != null) {
        // Chrome allows the link to be clicked without actually adding it to
        // the DOM.
        downloadLink.href = window.webkitURL.createObjectURL(textFileAsBlob);
    } else {
        // Firefox requires the link to be added to the DOM before it can be
        // clicked.
        downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
        downloadLink.onclick = function (e) {
            document.body.removeChild(e.target);
        };
        downloadLink.style.display = "none";
        document.body.appendChild(downloadLink);
    }

    downloadLink.click();
}

function loadFile(files)
{
    var file = files[0];
    var textType = /text.*/;

    if (file.type.match(textType)) {
        var reader = new FileReader();

        reader.onload = function (e) {
            var row = 1, column = 1;
            field.clear();

            for (var charNr in reader.result) {
                field.parseChar(row, column, reader.result[charNr]);

                column++;
                if (column > field.columns) {
                    column = 0;
                    row++;
                }
            }

            field.countAllNeighbours();
            field.updatePopulationHTML();
        };

        reader.readAsText(file);
    } else {
        console.log("File not supported.")
    }
}

// Functions for drag and dop onto the canvas:
function dragenter(e)
{
    e.stopPropagation();
    e.preventDefault();
}

function dragover(e)
{
    e.stopPropagation();
    e.preventDefault();
}

function drop(e)
{
    e.stopPropagation();
    e.preventDefault();

    loadFile(e.dataTransfer.files);
}

// Function for submitting with enter from a text field:
function handleKeyPress(event, textField)
{
    if (event.keyCode === 13) {  // Enter key was pressed.
        if (textField == "FileName")
            saveFile();
        else if (textField == "RandomCells")
            fillWithRandomCells();
    }
}
<commit_msg>Completed reordering of UI functions.<commit_after>/*
 * This file contains common variables, datatypes and functions used by all
 * cellular automata in this folder.
 *
 * Copyright (c) 2015 Johannes Maibaum <jmaibaum@gmail.com>
 */

var Settings = {
    boardDivision: 0,
    boardSideLenght: 0,
    canvas: undefined,
    context: undefined,
    cellsPerLine: 50,        // Devides the canvas into 'n' cellsPerLine.
    cellSize: 0,
    cellColor: ["#ffffff"],  // Needs to be extended by final game with colors.
    cellToDraw: 1,           // Default cell type for drawing operations.
    intervalID: 0,
    speed: 100
};

// Wait for DOM, then initialize.
document.addEventListener("DOMContentLoaded", initialize, false);

// Custom datatypes for generic Cells and a playing Field:
var Cell = function ()
{
    this.state = 0;
    this.neighbours = 0;
}

var Field = function (rows, columns)
{
    this.rows  = rows;
    this.columns = columns;
    this.generation = 0;
    this.generationHTML = undefined;  // Needs to be set by initialize().
    this.population = 0;
    this.oldPopulaton = 0;
    this.populationHTML = undefined;  // Needs to be set by initialize().
    this.data = [];

    for (var i = 0; i < this.rows + 2; i++) {
        this.data[i] = [];

        for (var j = 0; j < this.columns + 2; j++)
            this.data[i].push(new Cell());
    }
}

Field.prototype.updateNeighbours = function (row, column, increment)
{
    this.data[row - 1][column - 1].neighbours += increment;
    this.data[row - 1][column].neighbours     += increment;
    this.data[row - 1][column + 1].neighbours += increment;
    this.data[row][column - 1].neighbours     += increment;
    this.data[row][column + 1].neighbours     += increment;
    this.data[row + 1][column - 1].neighbours += increment;
    this.data[row + 1][column].neighbours     += increment;
    this.data[row + 1][column + 1].neighbours += increment;
}

Field.prototype.resetAllNeighbours = function()
{
    for (var row in this.data)
        for (var column in this.data[row])
            this.data[row][column].neighbours = 0;
}

Field.prototype.increaseGeneration = function()
{
    this.generation++;
    this.generationHTML.innerHTML = this.generation;
}

Field.prototype.updatePopulationHTML = function ()
{
    if (this.population != this.oldPopulation) {
        this.populationHTML.innerHTML = this.population;
        this.oldPopulation = this.population;
    }
}

Field.prototype.clear = function ()
{
    for (var row in this.data)
        for (var column in this.data[row]) {
            this.createCell(row, column, 0);
            drawCell(row, column, 0);
            this.updatePopulationHTML();
        }

    // Reset generation counter.
    this.generation = 0;
    this.generationHTML.innerHTML = this.generation;
}

// Initialize the canvas and add the event listener.
function initialize()
{
    // Prepare canvas:
    Settings.canvas = document.getElementById("board");
    Settings.context = Settings.canvas.getContext("2d");
    field.generationHTML = document.getElementById("Generation");
    field.populationHTML = document.getElementById("Population");

    Settings.boardSideLength = Settings.canvas.width;  // Assume as quadratic.
    Settings.boardDivision = Math.floor(Settings.boardSideLength /
                                        Settings.cellsPerLine);
    Settings.cellSize = Settings.boardDivision - 2;

    // Draw cell grid:
    Settings.context.strokeStyle = "#cccccc";
    Settings.context.lineWidth = 2;

    for (var i = 0; i <= Settings.boardSideLength;
         i = i + Settings.boardDivision) {
        Settings.context.moveTo(0, i);
        Settings.context.lineTo(Settings.boardSideLength, i);
        Settings.context.moveTo(i, 0);
        Settings.context.lineTo(i, Settings.boardSideLength);
        Settings.context.stroke();
    }

    // Add event listeners:
    Settings.canvas.addEventListener("mousedown", drawCellFromMouseClick,
                                     false);

    // Setup for dropping field files on the canvas:
    var dropZone = Settings.canvas;
    dropZone.addEventListener("dragenter", dragenter, false);
    dropZone.addEventListener("dragover", dragover, false);
    dropZone.addEventListener("drop", drop, false);
}

/*
 * Callback function to determine the mouse click coordinates on the canvas.
 * Adapted from:
 * http://miloq.blogspot.de/2011/05/coordinates-mouse-click-canvas.html
 */
function drawCellFromMouseClick(event)
{
    var x = event.x;
    var y = event.y;

    // Firefox workaround:
    if (x == undefined && y == undefined) {
        x = event.clientX + document.body.scrollLeft +
            document.documentElement.scrollLeft;
        y = event.clientY + document.body.scrollTop +
            document.documentElement.scrollTop;
    }

    x -= Settings.canvas.offsetLeft;
    y -= Settings.canvas.offsetTop;

    var coords = convertClickToFieldCoordinates(x, y);
    var row    = coords[0];
    var column = coords[1];

    if (field.createCell(row, column, Settings.cellToDraw, 1))
        drawCell(row, column, Settings.cellToDraw);
    else {
        field.createCell(row, column, 0, -1);
        drawCell(row, column, 0);
    }
}

function convertClickToFieldCoordinates(x, y) {
    var row = Math.floor(y / Settings.boardDivision) + 1;
    var column = Math.floor(x / Settings.boardDivision) + 1;

    // Ensure that we get valid coordinates:
    if (row > field.rows)
        row--;

    if (column > field.columns)
        column--;

    return [row, column];
}

// Draw a cell using field coordinates:
function drawCell(row, column, state)
{
    // Set Cell color:
    Settings.context.fillStyle = Settings.cellColor[state];

    var drawx = Math.floor(column * Settings.boardDivision)
        - Settings.boardDivision + 1;
    var drawy = Math.floor(row * Settings.boardDivision)
        - Settings.boardDivision + 1;

    Settings.context.fillRect(drawx, drawy, Settings.cellSize,
                              Settings.cellSize);
}

// Wrapper for field.clear():
function clearField()
{
    field.clear();
}

function runAutomata()
{
    if (!Settings.intervalID)
        Settings.intervalID = window.setInterval(nextGenerationFromInterval,
                                                 Settings.speed);
}

function stopAutomata()
{
    window.clearInterval(Settings.intervalID);
    Settings.intervalID = 0;
}

function changeSpeed(selectItem)
{
    Settings.speed = selectItem.value;

    if (Settings.intervalID) {
        stopAutomata();
        runAutomata();
    }
}

// Wrapper for next generation method:
function nextGeneration()
{
    if (!Settings.intervalID)
        field.nextGeneration();
    else
        stopAutomata();
}

function nextGenerationFromInterval()
{
    field.nextGeneration();
}

/*
 * Save field to and load field from files.
 * Adapted from:
 * https://thiscouldbebetter.wordpress.com/2012/12/18/loading-editing-and-
 * saving-a-text-file-in-html5-using-javascrip/
 */
function saveFile()
{
    var textToWrite = '';

    // Loop over field to construct text string:
    for (var row = 1; row <= field.rows; row++) {
        for (var column = 1; column <= field.columns; column++) {
            textToWrite += field.generateChar(row, column);
        }
        textToWrite += '\n';
    }

    var fileName = document.getElementById("FileName").value;
    var textFileAsBlob = new Blob([textToWrite], {type:'text/plain'});
    var downloadLink = document.createElement("a");
    downloadLink.download = fileName;
    downloadLink.innerHTML = "Download File";

    if (window.webkitURL != null) {
        // Chrome allows the link to be clicked without actually adding it to
        // the DOM.
        downloadLink.href = window.webkitURL.createObjectURL(textFileAsBlob);
    } else {
        // Firefox requires the link to be added to the DOM before it can be
        // clicked.
        downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
        downloadLink.onclick = function (e) {
            document.body.removeChild(e.target);
        };
        downloadLink.style.display = "none";
        document.body.appendChild(downloadLink);
    }

    downloadLink.click();
}

function loadFile(files)
{
    var file = files[0];
    var textType = /text.*/;

    if (file.type.match(textType)) {
        var reader = new FileReader();

        reader.onload = function (e) {
            var row = 1, column = 1;
            field.clear();

            for (var charNr in reader.result) {
                field.parseChar(row, column, reader.result[charNr]);

                column++;
                if (column > field.columns) {
                    column = 0;
                    row++;
                }
            }

            field.countAllNeighbours();
            field.updatePopulationHTML();
        };

        reader.readAsText(file);
    } else {
        console.log("File not supported.")
    }
}

// Functions for drag and dop onto the canvas:
function dragenter(e)
{
    e.stopPropagation();
    e.preventDefault();
}

function dragover(e)
{
    e.stopPropagation();
    e.preventDefault();
}

function drop(e)
{
    e.stopPropagation();
    e.preventDefault();

    loadFile(e.dataTransfer.files);
}

// Function for submitting with enter from a text field:
function handleKeyPress(event, textField)
{
    if (event.keyCode === 13) {  // Enter key was pressed.
        if (textField == "FileName")
            saveFile();
        else if (textField == "RandomCells")
            fillWithRandomCells();
    }
}
<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Controller.extend({

    args: '',

    transformArgs: function () {
        var args = this.get('args').split(',').without('');
        this.set('model.args', args);
    }.observes('args'),

    filechanged: function () {
        var args = this.get('model.file').match(/(\$\d \= \")\w+\"/g),
            key;

        if (args) {
            args = args.map(function (x) {
                key = x.replace(/\"/g, '').replace(/\$\d = /g, '');
                return '[?not' + key + ':' + key + ']'
            });

            this.set('model.args', 'build.sh,' + args.join(','));
        }

    }.observes('model.file'),

    actions: {
        createfile: function () {
            this.set('model.file', 'if [ $1 = "build" ]; then \n#Enter some commands here for building service \nfi\n\nif [ $2 = "run" ]; then \n#Enter some commands here for running service \nfi')
        },
        save: function () {
            Ember.$.ajax({
                type: 'POST',
                url: '/process',
                data: JSON.stringify(this.get('model')),
                success: function (response) {
                    this.notify.success('Process Saved!');
                    this.transitionToRoute('process.list');
                }.bind(this)
            });
        }
    }
});
<commit_msg>optimize<commit_after>import Ember from 'ember';

export default Ember.Controller.extend({

    transformArgs: function() {
        return this.get('model.args').split(',').without(' ');
    },

    filechanged: function() {
        var args = this.get('model.file').match(/(\$\d \= \")\w+\"/g),
            key;

        if (args) {
            args = args.map(function(x) {
                key = x.replace(/\"/g, '').replace(/\$\d = /g, '');
                return '[?not' + key + ':' + key + ']'
            });

            this.set('model.args', 'build.sh,' + args.join(','));
        }

    }.observes('model.file'),

    actions: {
        createfile: function() {
            this.set('model.file', 'if [ $1 = "build" ]; then \n#Enter some commands here for building service \nfi\n\nif [ $2 = "run" ]; then \n#Enter some commands here for running service \nfi')
        },
        save: function() {
            var args = this.get('model.args');
            args = args.match(/\,/gi) ? this.transformArgs() : args
            this.set('model.args', args);
            // console.log(this.get('model'))
            Ember.$.ajax({
                type: 'POST',
                url: '/process',
                data: JSON.stringify(this.get('model')),
                success: function(response) {
                    this.notify.success('Process Saved!');
                    this.transitionToRoute('process.list');
                }.bind(this)
            });
        }
    }
});
<|endoftext|>


<commit_before>import {
  defineContext,
  withContext,
  define,
  props,
  css,
  hasNativeShadowDomSupport,
} from '@bolt/core/utils';
import {
  render,
  withLitHtml,
  html,
} from '@bolt/core/renderers/renderer-lit-html';

import heightUtils from '@bolt/global/styles/07-utilities/_utilities-height.scss';
import styles from './accordion.scss';

import { Accordion } from './_accordion-handorgel';

// define which specific props to provide to children that subscribe
export const AccordionContext = defineContext({
  noSeparator: false,
  shadow: false,
  spacing: 'medium',
  iconValign: 'top',
  useShadow: hasNativeShadowDomSupport,
});

@define
class BoltAccordion extends withContext(withLitHtml()) {
  static is = 'bolt-accordion';

  // provide context info to children that subscribe
  // (context + subscriber idea originally from https://codepen.io/trusktr/project/editor/XbEOMk)
  static get provides() {
    return [AccordionContext];
  }

  static props = {
    multiple: props.boolean,
    noSeparator: props.boolean,
    shadow: props.boolean,
    spacing: props.string,
    iconValign: props.string,
  };

  constructor(self) {
    self = super(self);

    this.useShadow = hasNativeShadowDomSupport;

    // this.state = {
    //   open: this.props.autoOpen ? this.props.autoOpen : false,
    //   collapse: this.props.collapse ? this.props.collapse : false,
    // };

    // this.uuid = '12345';
    return self;
  }

  handleAccordionItemReady(item) {
    const root = item.renderRoot;
    const header = root.querySelector('.c-bolt-accordion-item__trigger');
    const content = root.querySelector('.c-bolt-accordion-item__content');

    if (header && content) {
      this.accordionItems = [...this.accordionItems, header, content];
    }

    const index = this.accordionItemElementsCopy.indexOf(item);

    if (index !== -1) {
      this.accordionItemElementsCopy.splice(index, 1);
    }

    if (this.accordionItemElementsCopy.length === 0) {
      if (this.accordion) {
        this.accordion.update();
      } else {
        this.initAccordion();
      }
    }
  }

  initAccordion() {
    this.accordion = new Accordion(this.accordionElement, {
      // array of node references, alternating header + content: [header, content, header, content]
      items: this.accordionItems,

      // whether multiple folds can be opened at once
      multiSelectable: this.props.multiple,
      // whether the folds are collapsible
      collapsible: true,

      // whether ARIA attributes are enabled
      ariaEnabled: true,
      // whether W3C keyboard shortcuts are enabled
      keyboardInteraction: true,
      // whether to loop header focus (sets focus back to first/last header when end/start reached)
      carouselFocus: true,

      // attribute for the header or content to open folds at initialization
      initialOpenAttribute: 'data-open',
      // whether to use transition at initial open
      initialOpenTransition: true,
      // delay used to show initial transition
      initialOpenTransitionDelay: 200,

      // header/content class if fold is open
      headerOpenClass: 'c-bolt-accordion-item__trigger--open',
      contentOpenClass: 'c-bolt-accordion-item__content--open',

      // header/content class if fold has been opened (transition finished)
      headerOpenedClass: 'c-bolt-accordion-item__trigger--opened',
      contentOpenedClass: 'c-bolt-accordion-item__content--opened',

      // header/content class if fold has been focused
      headerFocusClass: 'c-bolt-accordion-item__trigger--focus',
      contentFocusClass: 'c-bolt-accordion-item__content--focus',

      // header/content class if fold is disabled
      headerDisabledClass: 'c-bolt-accordion-item__trigger--disabled',
      contentDisabledClass: 'c-bolt-accordion-item__content--disabled',

      // header/content class if no transition should be active (applied on resize)
      headerNoTransitionClass: 'c-bolt-accordion-item__trigger--notransition',
      contentNoTransitionClass: 'c-bolt-accordion-item__content--notransition',
    });

    this.accordion.on('destroyed', fold => {
      delete this.accordion;
    });
  }

  setupAccordion() {
    // Accordion outer container, accordion plugin will be instantiated on this element
    this.accordionElement = this.renderRoot.querySelector('.c-bolt-accordion');

    // Root used to find child accordion items
    this.accordionRoot = this.useShadow ? this : this.accordionElement;

    // Reference to all the accordion items
    this.accordionItemElements = Array.from(this.accordionRoot.children).filter(
      item => item.tagName === 'BOLT-ACCORDION-ITEM',
    );

    // Copy of that reference that will be mutated as we process each accordion item
    this.accordionItemElementsCopy = [...this.accordionItemElements];

    // Array passed to the Accordion plugin, a series of header/content pairs
    this.accordionItems = [];

    this.accordionItemElements.forEach(item => {
      const onItemReady = () => {
        this.handleAccordionItemReady(item);
        item.removeEventListener('rendered', onItemReady);
      };

      if (item._wasInitiallyRendered || this._wasMutated) {
        this.handleAccordionItemReady(item);
        this._wasMutated = false;
      } else {
        item.addEventListener('rendered', onItemReady);
      }
    });
  }

  addMutationObserver() {
    const self = this;

    if (window.MutationObserver) {
      // Re-generate slots + re-render when mutations are observed
      const mutationCallback = function(mutationsList, observer) {
        for (let mutation of mutationsList) {
          if (mutation.type == 'childList') {
            // @todo: handle add/remove children
            // console.log('A child node has been added or removed.');
            // self.slots = self._checkSlots();
            // self.triggerUpdate();
          } else if (mutation.type == 'attributes') {
            if (mutation.target.tagName === 'BOLT-ACCORDION-ITEM') {
              const target = mutation.target;

              this._wasMutated = true;

              const resetAccordion = () => {
                self.accordion.destroy();
                self.setupAccordion();
                target.removeEventListener('rendered', resetAccordion);
              };

              target.addEventListener('rendered', resetAccordion);
            }
          }
        }
      };

      // Create an observer instance linked to the callback function
      self.observer = new MutationObserver(mutationCallback);

      // Start observing the target node for configured mutations
      self.observer.observe(this, {
        attributes: true,
        childList: true,
        subtree: true,
      });
    }
  }

  template() {
    const classes = css(
      'c-bolt-accordion',
      this.props.shadow ? 'c-bolt-accordion--shadow' : '',
    );

    return html`

        ${this.slots.default ? this.slot('default') : ''}

    `;
  }

  rendered() {
    super.rendered && super.rendered();

    this.setupAccordion();
    this.addMutationObserver();
  }

  disconnected() {
    super.disconnected && super.disconnected();

    // remove MutationObserver if supported + exists
    if (window.MutationObserver && this.observer) {
      this.observer.disconnect();
    }
  }

  render() {
    // @todo: replace with validated props
    this.contexts.get(AccordionContext).noSeparator =
      this.props.noSeparator || false;
    this.contexts.get(AccordionContext).shadow = this.props.shadow || false;
    this.contexts.get(AccordionContext).spacing =
      this.props.spacing || 'medium';
    this.contexts.get(AccordionContext).iconValign =
      this.props.iconValign || 'top';
    this.contexts.get(AccordionContext).useShadow = this.useShadow;

    return html`
      ${this.addStyles([styles, heightUtils])} ${this.template()}
    `;
  }
}

export { BoltAccordion };
<commit_msg>feat: update accordion options on re-render<commit_after>import {
  defineContext,
  withContext,
  define,
  props,
  css,
  hasNativeShadowDomSupport,
} from '@bolt/core/utils';
import {
  render,
  withLitHtml,
  html,
} from '@bolt/core/renderers/renderer-lit-html';

import heightUtils from '@bolt/global/styles/07-utilities/_utilities-height.scss';
import styles from './accordion.scss';

import { Accordion } from './_accordion-handorgel';

// define which specific props to provide to children that subscribe
export const AccordionContext = defineContext({
  noSeparator: false,
  shadow: false,
  spacing: 'medium',
  iconValign: 'top',
  useShadow: hasNativeShadowDomSupport,
});

@define
class BoltAccordion extends withContext(withLitHtml()) {
  static is = 'bolt-accordion';

  // provide context info to children that subscribe
  // (context + subscriber idea originally from https://codepen.io/trusktr/project/editor/XbEOMk)
  static get provides() {
    return [AccordionContext];
  }

  static props = {
    multiple: props.boolean,
    noSeparator: props.boolean,
    shadow: props.boolean,
    spacing: props.string,
    iconValign: props.string,
  };

  constructor(self) {
    self = super(self);

    this.useShadow = hasNativeShadowDomSupport;

    // this.state = {
    //   open: this.props.autoOpen ? this.props.autoOpen : false,
    //   collapse: this.props.collapse ? this.props.collapse : false,
    // };

    // this.uuid = '12345';
    return self;
  }

  get accordionOptions() {
    return {
      // array of node references, alternating header + content: [header, content, header, content]
      items: this.accordionItems,

      // whether multiple folds can be opened at once
      multiSelectable: this.props.multiple,
      // whether the folds are collapsible
      collapsible: true,

      // whether ARIA attributes are enabled
      ariaEnabled: true,
      // whether W3C keyboard shortcuts are enabled
      keyboardInteraction: true,
      // whether to loop header focus (sets focus back to first/last header when end/start reached)
      carouselFocus: true,

      // attribute for the header or content to open folds at initialization
      initialOpenAttribute: 'data-open',
      // whether to use transition at initial open
      initialOpenTransition: true,
      // delay used to show initial transition
      initialOpenTransitionDelay: 200,

      // header/content class if fold is open
      headerOpenClass: 'c-bolt-accordion-item__trigger--open',
      contentOpenClass: 'c-bolt-accordion-item__content--open',

      // header/content class if fold has been opened (transition finished)
      headerOpenedClass: 'c-bolt-accordion-item__trigger--opened',
      contentOpenedClass: 'c-bolt-accordion-item__content--opened',

      // header/content class if fold has been focused
      headerFocusClass: 'c-bolt-accordion-item__trigger--focus',
      contentFocusClass: 'c-bolt-accordion-item__content--focus',

      // header/content class if fold is disabled
      headerDisabledClass: 'c-bolt-accordion-item__trigger--disabled',
      contentDisabledClass: 'c-bolt-accordion-item__content--disabled',

      // header/content class if no transition should be active (applied on resize)
      headerNoTransitionClass: 'c-bolt-accordion-item__trigger--notransition',
      contentNoTransitionClass: 'c-bolt-accordion-item__content--notransition',
    };
  }

  handleAccordionItemReady(item) {
    const root = item.renderRoot;
    const header = root.querySelector('.c-bolt-accordion-item__trigger');
    const content = root.querySelector('.c-bolt-accordion-item__content');

    if (header && content) {
      this.accordionItems = [...this.accordionItems, header, content];
    }

    const index = this.accordionItemElementsCopy.indexOf(item);

    if (index !== -1) {
      this.accordionItemElementsCopy.splice(index, 1);
    }

    if (this.accordionItemElementsCopy.length === 0) {
      if (this.accordion) {
        this.accordion.update();
      } else {
        this.initAccordion();
      }
    }
  }

  initAccordion() {
    this.accordion = new Accordion(
      this.accordionElement,
      this.accordionOptions,
    );

    this.accordion.on('destroyed', fold => {
      delete this.accordion;
    });
  }

  setupAccordion() {
    // Accordion outer container, accordion plugin will be instantiated on this element
    this.accordionElement = this.renderRoot.querySelector('.c-bolt-accordion');

    // Root used to find child accordion items
    this.accordionRoot = this.useShadow ? this : this.accordionElement;

    // Reference to all the accordion items
    this.accordionItemElements = Array.from(this.accordionRoot.children).filter(
      item => item.tagName === 'BOLT-ACCORDION-ITEM',
    );

    // Copy of that reference that will be mutated as we process each accordion item
    this.accordionItemElementsCopy = [...this.accordionItemElements];

    // Array passed to the Accordion plugin, a series of header/content pairs
    this.accordionItems = [];

    this.accordionItemElements.forEach(item => {
      const onItemReady = () => {
        this.handleAccordionItemReady(item);
        item.removeEventListener('rendered', onItemReady);
      };

      if (item._wasInitiallyRendered || this._wasMutated) {
        this.handleAccordionItemReady(item);
        this._wasMutated = false;
      } else {
        item.addEventListener('rendered', onItemReady);
      }
    });
  }

  addMutationObserver() {
    const self = this;

    if (window.MutationObserver) {
      // Re-generate slots + re-render when mutations are observed
      const mutationCallback = function(mutationsList, observer) {
        for (let mutation of mutationsList) {
          if (mutation.type === 'childList') {
            // @todo: handle add/remove children
            // console.log('A child node has been added or removed.');
            // self.slots = self._checkSlots();
            // self.triggerUpdate();
          } else if (mutation.type === 'attributes') {
            if (mutation.target.tagName === 'BOLT-ACCORDION-ITEM') {
              const target = mutation.target;

              this._wasMutated = true;

              const resetAccordion = () => {
                self.accordion.destroy();
                self.setupAccordion();
                target.removeEventListener('rendered', resetAccordion);
              };

              target.addEventListener('rendered', resetAccordion);
            }
          }
        }
      };

      // Create an observer instance linked to the callback function
      self.observer = new MutationObserver(mutationCallback);

      // Start observing the target node for configured mutations
      self.observer.observe(this, {
        attributes: true,
        childList: true,
        subtree: true,
      });
    }
  }

  template() {
    const classes = css(
      'c-bolt-accordion',
      this.props.shadow ? 'c-bolt-accordion--shadow' : '',
    );

    return html`

        ${this.slots.default ? this.slot('default') : ''}

    `;
  }

  rendered() {
    super.rendered && super.rendered();

    if (this.accordion) {
      // If accordion already exists, update options
      this.accordion.options = this.accordionOptions;
    } else {
      this.setupAccordion();
    }
    if (!this.observer) {
      this.addMutationObserver();
    }
  }

  disconnected() {
    super.disconnected && super.disconnected();

    // remove MutationObserver if supported + exists
    if (window.MutationObserver && this.observer) {
      this.observer.disconnect();
    }
  }

  render() {
    // @todo: replace with validated props
    this.contexts.get(AccordionContext).noSeparator =
      this.props.noSeparator || false;
    this.contexts.get(AccordionContext).shadow = this.props.shadow || false;
    this.contexts.get(AccordionContext).spacing =
      this.props.spacing || 'medium';
    this.contexts.get(AccordionContext).iconValign =
      this.props.iconValign || 'top';
    this.contexts.get(AccordionContext).useShadow = this.useShadow;

    return html`
      ${this.addStyles([styles, heightUtils])} ${this.template()}
    `;
  }
}

export { BoltAccordion };
<|endoftext|>


<commit_before>"use strict";
var assert = require('assert');

var Suit = {
  Spades: 1,
  Diamonds: 2,
  Clubs: 3,
  Hearts: 4
};

var Stack = function() {

  Stack.prototype._cards = [];

  Stack.prototype.empty = function() {
    return true;
  };

  Stack.prototype.add = function(card) {
    this._cards.push(card);
  };

  Stack.prototype.top = function() {
    return this._cards[0];
  };

  return this;
};

var Card = function(suit,value) {
  this._suit = suit;
  this._value = value;

  Card.prototype.suit = function() {
    return this._suit;
  };

  Card.prototype.value = function() {
    return this._value;
  };

  return this;
};

describe('klondike', function() {
  describe('card', function() {
    it('has a suit and a value', function() {
      var card = new Card(Suit.Spades,1);
      assert.equal(Suit.Spades, card.suit());
      assert.equal(1, card.value());
    });
  });

  describe('stack', function() {
    it('is a concept', function() {
      assert(new Stack().empty());
    });

    it('can add card', function() {
      var stack = new Stack();
      stack.add(new Card(Suit.Spades, 1));

      assert.deepEqual(new Card(Suit.Spades, 1), stack.top());
    });
  });

});
<commit_msg>failing test for pushing multiple cards up<commit_after>"use strict";
var assert = require('assert');

var Suit = {
  Spades: 1,
  Diamonds: 2,
  Clubs: 3,
  Hearts: 4
};

var Stack = function() {

  Stack.prototype._cards = [];

  Stack.prototype.empty = function() {
    return true;
  };

  Stack.prototype.add = function(card) {
    this._cards.push(card);
  };

  Stack.prototype.top = function() {
    return this._cards[0];
  };

  return this;
};

var Card = function(suit,value) {
  this._suit = suit;
  this._value = value;

  Card.prototype.suit = function() {
    return this._suit;
  };

  Card.prototype.value = function() {
    return this._value;
  };

  return this;
};

describe('klondike', function() {
  describe('card', function() {
    it('has a suit and a value', function() {
      var card = new Card(Suit.Spades,1);
      assert.equal(Suit.Spades, card.suit());
      assert.equal(1, card.value());
    });
  });

  describe('stack', function() {
    it('is a concept', function() {
      assert(new Stack().empty());
    });

    it('can add card', function() {
      var stack = new Stack();
      stack.add(new Card(Suit.Spades, 1));

      assert.deepEqual(new Card(Suit.Spades, 1), stack.top());
    });

    it('can add multiple cards', function() {
      var stack = new Stack();
      stack.add(new Card(Suit.Spades, 1));
      stack.add(new Card(Suit.Hearts, 2));

      assert.deepEqual(new Card(Suit.Hearts, 2), stack.top());
    });
  });

});
<|endoftext|>