<commit_before>'use strict';
const _ = require('lodash');
const EventEmitter2 = require('eventemitter2');
const config = require('../modules/config');
const Game = require('./game');
const Player = require('./player');
const Map = require('./map');
const NormalItem = require('./normal_item');
const PowerItem = require('./power_item');
const EnemyAI1 = require('./enemy_ai1');

class GameMaster extends EventEmitter2 {
    constructor (pixel = 64) {
        super({});
        this.game = null;

        this.pixel = pixel;
        this.invincibleTimeoutId = null;

        this.timeoutIds = [];
        this.intervalIds = [];

        this.decideSheepCount = {};
    }

    createGame () {
        this.game = new Game();
        return this.game;
    }

    reCreateGame () {
        this.timeoutIds.forEach(id => clearTimeout(id));
        this.timeoutIds = [];
        this.intervalIds.forEach(id => clearInterval(id));
        this.intervalIds = [];
        this.createGame();
    }

    addPlayer (user) {
        if (this.game.state !== Game.STATES.idle) {
            throw new Error('cannot add game player after init game');
        }

        if (this.game.players.size >= config.playerSize) {
            throw new Error(`cannot add over ${config.playerSize} players`);
        }

        const player = new Player({user, isAI: false});
        this.game.players.push(player);
    }

    removePlayer (user) {
        if (this.game.state === Game.STATES.running) {
            const player = this.game.players.find(x => x.user === user);
            if (player) {
                player.isAI = true;
            }
        } else {
            _.remove(this.game.players, player => player.user === user);
        }
    }

    decideSheep (players) {
        let maxNum = -1;

        return _.chain(players)
            .filter(x => x.user)
            .map(x => {
                const id = x.id + '__' + x.user.username;
                const num = this.decideSheepCount[id] || 0;
                this.decideSheepCount[id] = num;
                maxNum = Math.max(maxNum, num);
                return {id, num, player: x};
            })
            .filter(x => x.num === maxNum)
            .sample()
            .value()
            .player;
    }

    initGame () {
        if (this.game.state !== Game.STATES.idle) {
            throw new Error('cannot add game player after start game');
        }
        this.game.state = Game.STATES.init;

        this.game.timeLimit = config.gameTimeLimit;
        this.game.remainingTime = config.gameTimeLimit;

        const users = this.game.players.filter(x => !x.isAI);
        if (!users.length) {
            throw new Error('cannot start game joined no user');
        }

        // decide sheep
        const sheep = this.decideSheep(this.game.players);
        this.game.players.forEach(player => {
            player.isEnemy = player !== sheep;
        });

        // add AI
        while (this.game.players.length < config.playerSize) {
            const player = new Player({isEnemy: true, isAI: true});
            this.game.players.push(player);
        }

        // decide map
        this.game.map = new Map();

        // init coordinate of each players
        _.sampleSize(this.game.map.respawnPoints, config.playerSize).forEach(({x, y}, idx) => {
            this.game.players[idx].coordinate.x = x;
            this.game.players[idx].coordinate.y = y;
        });

        // init power item
        this.game.powerItems = [];
        _.times(config.defaultPowerItemSize, () => {
            const item = new PowerItem({x: 0, y: 0});
            do {
                item.coordinate.x = _.random(this.game.map.width - 1);
                item.coordinate.y = _.random(this.game.map.height - 1);
            } while (this.game.map.tiles[item.coordinate.y][item.coordinate.x] ||
                this._isNear(item, this.game.powerItems) ||
                this.game.map.respawnPoints.some(a => a.x === item.coordinate.x && a.y === item.coordinate.y));
            this.game.powerItems.push(item);
            ++this.game.remainingItemSize;
        });

        // init types of each power item
        this.game.powerItems.forEach(item => item.type = PowerItem.TYPES.invincible);
        _.zip(_.sampleSize(this.game.powerItems, 2), [PowerItem.TYPES.bomb, PowerItem.TYPES.slow]).forEach(([item, type]) => {
            item.type = type;
        });

        // init normal items
        this.game.normalItems = [];
        _.times(this.game.map.height, y => {
            _.times(this.game.map.width, x => {
                if (!this.game.map.tiles[y][x] &&
                    !this.game.powerItems.some(a => a.coordinate.x === x && a.coordinate.y === y) &&
                    !_.isEqual({x, y}, this.game.players.find(a => !a.isEnemy).coordinate)) {
                    const normalItem = new NormalItem({x, y});
                    this.game.normalItems.push(normalItem);
                    ++this.game.remainingItemSize;
                }
            });
        });

        // update coordinate with pixel
        _.concat(this.game.players, this.game.normalItems, this.game.powerItems).forEach(a => {
            a.coordinate.x = a.coordinate.x * this.pixel + config.gameOffSetX;
            a.coordinate.y = a.coordinate.y * this.pixel + config.gameOffSetY;
        });
    }

    startGame () {
        if (this.game.state !== Game.STATES.init) {
            throw new Error(`cannot start game on ${this.game.state}`);
        }

        this.game.state = Game.STATES.running;
        this.game.startedAt = new Date();

        const tid = setTimeout(() => {
            if (this.game.state === Game.STATES.running) {
                this.game.remainingTime = 0;
                this.endGame({isTimeLimit: true});
            }
        }, config.gameTimeLimit);
        this.timeoutIds.push(tid);

        const scoreIntervalId = setInterval(() => {
            if (this.game.state !== Game.STATES.running) {
                clearInterval(scoreIntervalId);
                return;
            }

            this.game.remainingTime = Math.max(0, this.game.remainingTime - 100);
            this.game.updateScore();

            this.emit('updateScore', {
                score: this.game.score,
                remainingTime: this.game.remainingTime,
                killCount: this.game.killCount,
                takeNormalItemCount: this.game.takeNormalItemCount,
                takePowerItemCount: this.game.takePowerItemCount,
                takeInvincibleItemCount: this.game.takeInvincibleItemCount,
                takeBombItemCount: this.game.takeBombItemCount,
                takeSlowItemCount: this.game.takeSlowItemCount
            });
        }, 100);
        this.intervalIds.push(scoreIntervalId);

        this.game.updateScore();

        this.game.players.forEach(player => {
            if (player.isAI) {
                const ai = new EnemyAI1(player.id, this.game.map);
                this.aiLoopStart(player.id, ai);
            }
        });
    }

    endGame ({isTimeLimit = false} = {}) {
        if (this.game.state !== Game.STATES.running) {
            throw new Error(`cannot end game on ${this.game.state}`);
        }

        this.game.state = Game.STATES.ended;
        this.game.updateScore();

        this.game.logging();

        this.emit('endGame', {game: this.game, isTimeLimit});
    }

    aiLoopStart (aiId, ai, duration = config.defaultAIDuration) {
        const that = this;

        const aiLoopIntervalId = setInterval(() => {
            if (that.game.state !== Game.STATES.running) {
                clearInterval(aiLoopIntervalId);
                return;
            }

            try {
                const aiPlayer = that.game.players.find(x => x.id === aiId);
                const sheep = that.game.players.find(x => !x.isEnemy);
                if (aiPlayer.isAlive && sheep.isAlive) {
                    const players = that.game.players.map(player => ({
                        id: player.id,
                        isEnemy: player.isEnemy,
                        x: Math.floor((player.coordinate.x - config.gameOffSetX) / that.pixel),
                        y: Math.floor((player.coordinate.y - config.gameOffSetY) / that.pixel)
                    }));

                    const {x, y} = ai.nextMove(players);
                    aiPlayer.coordinate.x = x * that.pixel + config.gameOffSetX;
                    aiPlayer.coordinate.y = y * that.pixel + config.gameOffSetY;

                    that.emit('moveAI', {aiPlayer});
                }
            } catch (e) {
                console.error(e);
            }
        }, duration);
        this.intervalIds.push(aiLoopIntervalId);
    }

    movePlayer (user, {x, y}) {
        if (this.game.state !== Game.STATES.running) { return; }

        const player = this.game.players.find(x => x.user === user);
        if (!player) {
            throw new Error(`user ${user && user.username} was not found`);
        }

        if (player.isAlive) {
            player.coordinate.x = x;
            player.coordinate.y = y;
        }

        return player;
    }

    killSheep () {
        if (this.game.state !== Game.STATES.running) { return; }

        const sheep = this.game.players.find(x => !x.isEnemy);

        if (sheep.isAlive) {
            sheep.isAlive = false;
        }

        this.endGame();

        return sheep;
    }

    killWolf (wolfId) {
        if (this.game.state !== Game.STATES.running) { return; }

        const wolf = this.game.players.find(x => x.id === wolfId);
        if (!wolf) {
            throw new Error(`user ${wolfId} was not found`);
        }

        if (wolf.isAlive) {
            wolf.isAlive = false;

            ++this.game.killCount;

            const tid = setTimeout(() => {
                this.respawnWolf(wolfId);
            }, config.respawnDuration + Math.random() * config.respawnDurationAlpha);
            this.timeoutIds.push(tid);
        }

        return wolf;
    }

    respawnWolf (wolfId) {
        if (this.game.state !== Game.STATES.running) { return; }

        const wolf = this.game.players.find(x => x.id === wolfId);
        if (!wolf) {
            throw new Error(`user ${wolfId} was not found`);
        }

        wolf.coordinate = this._decideRespawnCoordinate();
        wolf.isAlive = true;

        this.emit('respawnWolf', {wolf});
    }

    _decideRespawnCoordinate () {
        let {x, y} = this.game.map.respawnPoints[0];
        let dist = this._calcDistanceFromAllAlive(this.game.map.respawnPoints[0]);

        this.game.map.respawnPoints.forEach(point => {
            const ndist = this._calcDistanceFromAllAlive(point);
            if (ndist > dist) {
                ([dist, x, y] = [ndist, point.x, point.y]);
            }
        });

        return {
            x: x * this.pixel + config.gameOffSetX,
            y: y * this.pixel + config.gameOffSetY
        };
    }

    _calcDistanceFromAllAlive ({x, y}) {
        let distSum = 0;

        this.game.players.forEach(a => {
            if (!a.isAlive) { return; }

            const ax = Math.floor((a.coordinate.x - config.gameOffSetX) / this.pixel);
            const ay = Math.floor((a.coordinate.y - config.gameOffSetY) / this.pixel);

            const k = a.isEnemy ? 1 : 2;

            distSum += (Math.abs(ax - x) + Math.abs(ay - y)) * k;
        });

        return distSum;
    }

    takeNormalItem (itemId) {
        if (this.game.state !== Game.STATES.running) { return; }

        const item = this.game.normalItems.find(x => x.id === itemId);
        if (!itemId) {
            throw new Error(`item of ${itemId} was not found`);
        }

        if (item.enabled) {
            item.enabled = false;
            ++this.game.takeNormalItemCount;

            --this.game.remainingItemSize;
            if (!this.game.remainingItemSize) {
                this.endGame();
            }
        }

        return item;
    }

    takePowerItem (itemId) {
        if (this.game.state !== Game.STATES.running) { return; }

        const item = this.game.powerItems.find(x => x.id === itemId);
        if (!itemId) {
            throw new Error(`item of ${itemId} was not found`);
        }

        if (item.enabled) {
            item.enabled = false;

            if (item.type === PowerItem.TYPES.invincible) {
                this._takeInvincibleItem();
            } else if (item.type === PowerItem.TYPES.bomb) {
                this._takeBombItem();
            } else if (item.type === PowerItem.TYPES.slow) {
                this._takeSlowItem();
            }

            --this.game.remainingItemSize;
            if (!this.game.remainingItemSize) {
                this.endGame();
            }
        }

        return item;
    }

    _takeInvincibleItem () {
    	++this.game.takeInvincibleItemCount;
        if (this.invincibleTimeoutId) {
            clearTimeout(this.invincibleTimeoutId);
        }

        this.emit('startInvincible', {});

        this.invincibleTimeoutId = setTimeout(() => {
            if (this.game.state === Game.STATES.running) {
                this.emit('endInvincible', {});
            }
        }, config.powerDuration);
        this.timeoutIds.push(this.invincibleTimeoutId);
    }

    _takeBombItem () {
    	++this.game.takeBombItemCount;
        this.emit('bomb', {});
        this.game.players
            .filter(player => player.isEnemy)
            .forEach(player => this.killWolf(player.id));
    }

    _takeSlowItem () {
    	++this.game.takeSlowItemCount;
        this.emit('startSlow', {});

        const slowTimeoutId = setTimeout(() => {
            if (this.game.state === Game.STATES.running) {
                this.emit('endSlow', {});
            }
        }, config.slowDuration);
        this.timeoutIds.push(slowTimeoutId)
    }

    _isNear (target, others) {
        return others.some(a => {
            const xx = Math.abs(a.coordinate.x - target.coordinate.x);
            const yy = Math.abs(a.coordinate.y - target.coordinate.y);
            const dist = xx + yy;
            return dist <= config.nearPowerItemDistance;
        });
    }
}

module.exports = GameMaster;
<commit_msg>fix decide sheep<commit_after>'use strict';
const _ = require('lodash');
const EventEmitter2 = require('eventemitter2');
const config = require('../modules/config');
const Game = require('./game');
const Player = require('./player');
const Map = require('./map');
const NormalItem = require('./normal_item');
const PowerItem = require('./power_item');
const EnemyAI1 = require('./enemy_ai1');

class GameMaster extends EventEmitter2 {
    constructor (pixel = 64) {
        super({});
        this.game = null;

        this.pixel = pixel;
        this.invincibleTimeoutId = null;

        this.timeoutIds = [];
        this.intervalIds = [];

        this.decideSheepCount = {};
    }

    createGame () {
        this.game = new Game();
        return this.game;
    }

    reCreateGame () {
        this.timeoutIds.forEach(id => clearTimeout(id));
        this.timeoutIds = [];
        this.intervalIds.forEach(id => clearInterval(id));
        this.intervalIds = [];
        this.createGame();
    }

    addPlayer (user) {
        if (this.game.state !== Game.STATES.idle) {
            throw new Error('cannot add game player after init game');
        }

        if (this.game.players.size >= config.playerSize) {
            throw new Error(`cannot add over ${config.playerSize} players`);
        }

        const player = new Player({user, isAI: false});
        this.game.players.push(player);
    }

    removePlayer (user) {
        if (this.game.state === Game.STATES.running) {
            const player = this.game.players.find(x => x.user === user);
            if (player) {
                player.isAI = true;
            }
        } else {
            _.remove(this.game.players, player => player.user === user);
        }
    }

    decideSheep (players) {
        let maxNum = -1;

        const sheep = _.chain(players)
            .filter(x => x.user)
            .map(x => {
                const id = x.id + '__' + x.user.username;
                const num = this.decideSheepCount[id] || 0;
                this.decideSheepCount[id] = num;
                maxNum = Math.max(maxNum, num);
                return {id, num, player: x};
            })
            .filter(x => x.num === maxNum)
            .sample()
            .value();

        this.decideSheepCount[sheep.id] = sheep.num + 1;

        return sheep.player;
    }

    initGame () {
        if (this.game.state !== Game.STATES.idle) {
            throw new Error('cannot add game player after start game');
        }
        this.game.state = Game.STATES.init;

        this.game.timeLimit = config.gameTimeLimit;
        this.game.remainingTime = config.gameTimeLimit;

        const users = this.game.players.filter(x => !x.isAI);
        if (!users.length) {
            throw new Error('cannot start game joined no user');
        }

        // decide sheep
        const sheep = this.decideSheep(this.game.players);
        this.game.players.forEach(player => {
            player.isEnemy = player !== sheep;
        });

        // add AI
        while (this.game.players.length < config.playerSize) {
            const player = new Player({isEnemy: true, isAI: true});
            this.game.players.push(player);
        }

        // decide map
        this.game.map = new Map();

        // init coordinate of each players
        _.sampleSize(this.game.map.respawnPoints, config.playerSize).forEach(({x, y}, idx) => {
            this.game.players[idx].coordinate.x = x;
            this.game.players[idx].coordinate.y = y;
        });

        // init power item
        this.game.powerItems = [];
        _.times(config.defaultPowerItemSize, () => {
            const item = new PowerItem({x: 0, y: 0});
            do {
                item.coordinate.x = _.random(this.game.map.width - 1);
                item.coordinate.y = _.random(this.game.map.height - 1);
            } while (this.game.map.tiles[item.coordinate.y][item.coordinate.x] ||
                this._isNear(item, this.game.powerItems) ||
                this.game.map.respawnPoints.some(a => a.x === item.coordinate.x && a.y === item.coordinate.y));
            this.game.powerItems.push(item);
            ++this.game.remainingItemSize;
        });

        // init types of each power item
        this.game.powerItems.forEach(item => item.type = PowerItem.TYPES.invincible);
        _.zip(_.sampleSize(this.game.powerItems, 2), [PowerItem.TYPES.bomb, PowerItem.TYPES.slow]).forEach(([item, type]) => {
            item.type = type;
        });

        // init normal items
        this.game.normalItems = [];
        _.times(this.game.map.height, y => {
            _.times(this.game.map.width, x => {
                if (!this.game.map.tiles[y][x] &&
                    !this.game.powerItems.some(a => a.coordinate.x === x && a.coordinate.y === y) &&
                    !_.isEqual({x, y}, this.game.players.find(a => !a.isEnemy).coordinate)) {
                    const normalItem = new NormalItem({x, y});
                    this.game.normalItems.push(normalItem);
                    ++this.game.remainingItemSize;
                }
            });
        });

        // update coordinate with pixel
        _.concat(this.game.players, this.game.normalItems, this.game.powerItems).forEach(a => {
            a.coordinate.x = a.coordinate.x * this.pixel + config.gameOffSetX;
            a.coordinate.y = a.coordinate.y * this.pixel + config.gameOffSetY;
        });
    }

    startGame () {
        if (this.game.state !== Game.STATES.init) {
            throw new Error(`cannot start game on ${this.game.state}`);
        }

        this.game.state = Game.STATES.running;
        this.game.startedAt = new Date();

        const tid = setTimeout(() => {
            if (this.game.state === Game.STATES.running) {
                this.game.remainingTime = 0;
                this.endGame({isTimeLimit: true});
            }
        }, config.gameTimeLimit);
        this.timeoutIds.push(tid);

        const scoreIntervalId = setInterval(() => {
            if (this.game.state !== Game.STATES.running) {
                clearInterval(scoreIntervalId);
                return;
            }

            this.game.remainingTime = Math.max(0, this.game.remainingTime - 100);
            this.game.updateScore();

            this.emit('updateScore', {
                score: this.game.score,
                remainingTime: this.game.remainingTime,
                killCount: this.game.killCount,
                takeNormalItemCount: this.game.takeNormalItemCount,
                takePowerItemCount: this.game.takePowerItemCount,
                takeInvincibleItemCount: this.game.takeInvincibleItemCount,
                takeBombItemCount: this.game.takeBombItemCount,
                takeSlowItemCount: this.game.takeSlowItemCount
            });
        }, 100);
        this.intervalIds.push(scoreIntervalId);

        this.game.updateScore();

        this.game.players.forEach(player => {
            if (player.isAI) {
                const ai = new EnemyAI1(player.id, this.game.map);
                this.aiLoopStart(player.id, ai);
            }
        });
    }

    endGame ({isTimeLimit = false} = {}) {
        if (this.game.state !== Game.STATES.running) {
            throw new Error(`cannot end game on ${this.game.state}`);
        }

        this.game.state = Game.STATES.ended;
        this.game.updateScore();

        this.game.logging();

        this.emit('endGame', {game: this.game, isTimeLimit});
    }

    aiLoopStart (aiId, ai, duration = config.defaultAIDuration) {
        const that = this;

        const aiLoopIntervalId = setInterval(() => {
            if (that.game.state !== Game.STATES.running) {
                clearInterval(aiLoopIntervalId);
                return;
            }

            try {
                const aiPlayer = that.game.players.find(x => x.id === aiId);
                const sheep = that.game.players.find(x => !x.isEnemy);
                if (aiPlayer.isAlive && sheep.isAlive) {
                    const players = that.game.players.map(player => ({
                        id: player.id,
                        isEnemy: player.isEnemy,
                        x: Math.floor((player.coordinate.x - config.gameOffSetX) / that.pixel),
                        y: Math.floor((player.coordinate.y - config.gameOffSetY) / that.pixel)
                    }));

                    const {x, y} = ai.nextMove(players);
                    aiPlayer.coordinate.x = x * that.pixel + config.gameOffSetX;
                    aiPlayer.coordinate.y = y * that.pixel + config.gameOffSetY;

                    that.emit('moveAI', {aiPlayer});
                }
            } catch (e) {
                console.error(e);
            }
        }, duration);
        this.intervalIds.push(aiLoopIntervalId);
    }

    movePlayer (user, {x, y}) {
        if (this.game.state !== Game.STATES.running) { return; }

        const player = this.game.players.find(x => x.user === user);
        if (!player) {
            throw new Error(`user ${user && user.username} was not found`);
        }

        if (player.isAlive) {
            player.coordinate.x = x;
            player.coordinate.y = y;
        }

        return player;
    }

    killSheep () {
        if (this.game.state !== Game.STATES.running) { return; }

        const sheep = this.game.players.find(x => !x.isEnemy);

        if (sheep.isAlive) {
            sheep.isAlive = false;
        }

        this.endGame();

        return sheep;
    }

    killWolf (wolfId) {
        if (this.game.state !== Game.STATES.running) { return; }

        const wolf = this.game.players.find(x => x.id === wolfId);
        if (!wolf) {
            throw new Error(`user ${wolfId} was not found`);
        }

        if (wolf.isAlive) {
            wolf.isAlive = false;

            ++this.game.killCount;

            const tid = setTimeout(() => {
                this.respawnWolf(wolfId);
            }, config.respawnDuration + Math.random() * config.respawnDurationAlpha);
            this.timeoutIds.push(tid);
        }

        return wolf;
    }

    respawnWolf (wolfId) {
        if (this.game.state !== Game.STATES.running) { return; }

        const wolf = this.game.players.find(x => x.id === wolfId);
        if (!wolf) {
            throw new Error(`user ${wolfId} was not found`);
        }

        wolf.coordinate = this._decideRespawnCoordinate();
        wolf.isAlive = true;

        this.emit('respawnWolf', {wolf});
    }

    _decideRespawnCoordinate () {
        let {x, y} = this.game.map.respawnPoints[0];
        let dist = this._calcDistanceFromAllAlive(this.game.map.respawnPoints[0]);

        this.game.map.respawnPoints.forEach(point => {
            const ndist = this._calcDistanceFromAllAlive(point);
            if (ndist > dist) {
                ([dist, x, y] = [ndist, point.x, point.y]);
            }
        });

        return {
            x: x * this.pixel + config.gameOffSetX,
            y: y * this.pixel + config.gameOffSetY
        };
    }

    _calcDistanceFromAllAlive ({x, y}) {
        let distSum = 0;

        this.game.players.forEach(a => {
            if (!a.isAlive) { return; }

            const ax = Math.floor((a.coordinate.x - config.gameOffSetX) / this.pixel);
            const ay = Math.floor((a.coordinate.y - config.gameOffSetY) / this.pixel);

            const k = a.isEnemy ? 1 : 2;

            distSum += (Math.abs(ax - x) + Math.abs(ay - y)) * k;
        });

        return distSum;
    }

    takeNormalItem (itemId) {
        if (this.game.state !== Game.STATES.running) { return; }

        const item = this.game.normalItems.find(x => x.id === itemId);
        if (!itemId) {
            throw new Error(`item of ${itemId} was not found`);
        }

        if (item.enabled) {
            item.enabled = false;
            ++this.game.takeNormalItemCount;

            --this.game.remainingItemSize;
            if (!this.game.remainingItemSize) {
                this.endGame();
            }
        }

        return item;
    }

    takePowerItem (itemId) {
        if (this.game.state !== Game.STATES.running) { return; }

        const item = this.game.powerItems.find(x => x.id === itemId);
        if (!itemId) {
            throw new Error(`item of ${itemId} was not found`);
        }

        if (item.enabled) {
            item.enabled = false;

            if (item.type === PowerItem.TYPES.invincible) {
                this._takeInvincibleItem();
            } else if (item.type === PowerItem.TYPES.bomb) {
                this._takeBombItem();
            } else if (item.type === PowerItem.TYPES.slow) {
                this._takeSlowItem();
            }

            --this.game.remainingItemSize;
            if (!this.game.remainingItemSize) {
                this.endGame();
            }
        }

        return item;
    }

    _takeInvincibleItem () {
    	++this.game.takeInvincibleItemCount;
        if (this.invincibleTimeoutId) {
            clearTimeout(this.invincibleTimeoutId);
        }

        this.emit('startInvincible', {});

        this.invincibleTimeoutId = setTimeout(() => {
            if (this.game.state === Game.STATES.running) {
                this.emit('endInvincible', {});
            }
        }, config.powerDuration);
        this.timeoutIds.push(this.invincibleTimeoutId);
    }

    _takeBombItem () {
    	++this.game.takeBombItemCount;
        this.emit('bomb', {});
        this.game.players
            .filter(player => player.isEnemy)
            .forEach(player => this.killWolf(player.id));
    }

    _takeSlowItem () {
    	++this.game.takeSlowItemCount;
        this.emit('startSlow', {});

        const slowTimeoutId = setTimeout(() => {
            if (this.game.state === Game.STATES.running) {
                this.emit('endSlow', {});
            }
        }, config.slowDuration);
        this.timeoutIds.push(slowTimeoutId)
    }

    _isNear (target, others) {
        return others.some(a => {
            const xx = Math.abs(a.coordinate.x - target.coordinate.x);
            const yy = Math.abs(a.coordinate.y - target.coordinate.y);
            const dist = xx + yy;
            return dist <= config.nearPowerItemDistance;
        });
    }
}

module.exports = GameMaster;
<|endoftext|>


<commit_before>import http from 'http';
import fs from 'fs';
import http2 from 'http2';
import Koa from 'koa';
import path from 'path';
import serve from 'koa-serve';
import {RenderUtil} from '../../helper';
import render from 'koa-ejs';
import dispatcher from './middleware/dispatcher';
import routeMap from './middleware/routemap';
import {util} from '../../helper';
import {Server as WebSocketServer} from 'ws';
import bodyParser from 'koa-bodyparser';

class Server {
    constructor(config) {
        this.middleware = [];
        this.ifAppendHtmls = [];
        this.app = Koa();

        Object.assign(this, config);

        if (!this.syncDataMatch) {
            this.syncDataMatch = (url) => path.join(this.syncData, url);
        }

        if (!this.asyncDataMatch) {
            this.asyncDataMatch = (url) => path.join(this.asyncData, url);
        }

        if (undefined == this.divideMethod) {
            this.divideMethod = false;
        }

        this.setRender();
        this.setStaticHandler();
    }

    delayInit() {
        const app = this.app;
        if (!this.ifProxy) {
            app.use(bodyParser());
        }
        app.use(routeMap(this));
        this.middleware.forEach((g) => {
            app.use(g);
        });
        app.use(dispatcher(this));

        this.htmlAppender();
    }

    use(middleware) {
        this.middleware.push(middleware(this));
    }

    setRender() {
        if (this.tpl) {
            Object.assign(this, this.tpl);
        }
        let Render = this.RenderUtil || RenderUtil;
        this.extension = this.extension || 'ftl';

        this.tplRender = new Render({viewRoot: this.viewRoot});

        render(this.app, {
            root: path.resolve(__dirname, '../../../views'),
            layout: 'template',
            viewExt: 'html',
            cache: process.env.NODE_ENV !== 'development',
            debug: true
        });
    }

    setStaticHandler() {
        let rootdir;
        let dir;
        if (this.static && !Array.isArray(this.static)) this.static = [this.static];

        this.static.forEach((item) => {
            dir = /[^(\\\/)]*$/.exec(item);
            if (!dir || !dir[0]) return;
            rootdir = item.replace(/[^(\\\/)]*$/, '');
            this.app.use(serve(dir[0], rootdir));
        });
        this.app.use(serve('foxman_client', path.resolve(__dirname, '../../../')));
    }

    appendHtml(condition) {
        this.ifAppendHtmls.push(condition);
    }

    htmlAppender() {
        const ifAppendHtmls = this.ifAppendHtmls;
        let html;

        this.app.use(function*(next) {
            if (/text\/html/ig.test(this.type)) {
                html = ifAppendHtmls.map((item) => {
                    return item.condition(this.request) ? item.html : '';
                }).join('');
                this.body = this.body + html;
            }
            yield next;
        });
    }

    createServer() {
        const port = this.port || 3000;
        this.delayInit();
        const root = path.resolve(__dirname, '..', '..', '..');
        const httpOptions = {
            key: fs.readFileSync(path.resolve(root, 'config', 'crt', 'localhost.key')),
            cert: fs.readFileSync(path.resolve(root, 'config', 'crt', 'localhost.crt')),
        };
        const callback = this.app.callback();
        this.serverApp = (this.https ? http2.createServer(httpOptions, callback) : http.createServer(callback)).listen(port);
        this.wss = this.buildWebSocket(this.serverApp);
        util.log(`Server running on ${this.https ? 'https' : 'http'}://127.0.0.1:${port}/`);
    }

    buildWebSocket(serverApp) {
        var wss = new WebSocketServer({
            server: serverApp
        });

        wss.on('connection', (ws) => {
            ws.on('message', (message) => {
                console.log('received: %s', message);
            });
        });

        wss.broadcast = (data) => {
            wss.clients.forEach(function each(client) {
                client.send(data, function (error) {
                    if (error) {
                        console.log(error);
                    }
                });
            });
        };
        return wss;
    }
}

export default Server;
<commit_msg>Update server.es6<commit_after>import http from 'http';
import fs from 'fs';
import http2 from 'http2';
import Koa from 'koa';
import path from 'path';
import serve from 'koa-serve';
import {RenderUtil} from '../../helper';
import render from 'koa-ejs';
import dispatcher from './middleware/dispatcher';
import routeMap from './middleware/routemap';
import {util} from '../../helper';
import {Server as WebSocketServer} from 'ws';
import bodyParser from 'koa-bodyparser';

class Server {
    constructor(config) {
        this.middleware = [];
        this.ifAppendHtmls = [];
        this.app = Koa();

        Object.assign(this, config);

        if (!this.syncDataMatch) {
            this.syncDataMatch = (url) => path.join(this.syncData, url);
        }

        if (!this.asyncDataMatch) {
            this.asyncDataMatch = (url) => path.join(this.asyncData, url);
        }

        if (undefined == this.divideMethod) {
            this.divideMethod = false;
        }

        this.setRender();
        this.setStaticHandler();
    }

    delayInit() {
        const app = this.app;
        if (!this.ifProxy) {
            app.use(bodyParser());
        }
        app.use(routeMap(this));
        this.middleware.forEach((g) => {
            app.use(g);
        });
        app.use(dispatcher(this));

        this.htmlAppender();
    }

    use(middleware) {
        this.middleware.push(middleware(this));
    }

    setRender() {
        if (this.tpl) {
            Object.assign(this, this.tpl);
        }
        let Render = this.RenderUtil || RenderUtil;
        this.extension = this.extension || 'ftl';

        this.tplRender = new Render({viewRoot: this.viewRoot});

        render(this.app, {
            root: path.resolve(__dirname, '../../../views'),
            layout: 'template',
            viewExt: 'html',
            cache: process.env.NODE_ENV !== 'development',
            debug: true
        });
    }

    setStaticHandler() {
        let rootdir;
        let dir;
        if (this.static && !Array.isArray(this.static)) this.static = [this.static];

        this.static.forEach((item) => {
            dir = /[^(\\\/)]*$/.exec(item);
            if (!dir || !dir[0]) return;
            rootdir = item.replace(/[^(\\\/)]*$/, '');
            this.app.use(serve(dir[0], rootdir));
        });
        this.app.use(serve('foxman_client', path.resolve(__dirname, '../../../')));
    }

    appendHtml(condition) {
        this.ifAppendHtmls.push(condition);
    }

    htmlAppender() {
        const ifAppendHtmls = this.ifAppendHtmls;
        let html;

        this.app.use(function*(next) {
            if (/text\/html/ig.test(this.type)) {
                html = ifAppendHtmls.map((item) => {
                    return item.condition(this.request) ? item.html : '';
                }).join('');
                this.body = this.body + html;
            }
            yield next;
        });
    }

    createServer() {
        const port = this.port || 3000;
        this.delayInit();
        const root = path.resolve(__dirname, '..', '..', '..');
        const httpOptions = {
            key: fs.readFileSync(path.resolve(root, 'config', 'crt', 'localhost.key')),
            cert: fs.readFileSync(path.resolve(root, 'config', 'crt', 'localhost.crt')),
        };
        const callback = this.app.callback();
        this.serverApp = (this.https ? http2.createServer(httpOptions, callback) : http.createServer(callback)).listen(port);
        this.wss = this.buildWebSocket(this.serverApp);
        util.log(`Server running on ${this.https ? 'https' : 'http'}://127.0.0.1:${port}/`);
    }

    buildWebSocket(serverApp) {
        var wss = new WebSocketServer({
            server: serverApp
        });

        wss.on('connection', ws => {
            ws.on('message', message => {
                console.log('received: %s', message);
            });
        });

        wss.broadcast = data => {
            wss.clients.forEach( client => {
                client.send(data, error => {
                    if (error) {
                        util.debugLog(error);
                    }
                });
            });
        };
        return wss;
    }
}

export default Server;
<|endoftext|>


<commit_before>var util = require('./util')
  , isFunc = util.isFunc
  , path = require("path")
  , fs = require("fs")
  , all = {}
  , AbstractResource = require("./abstract_resource")

function File(p) {
  AbstractResource.call(this)
  this._path = p
}

File.new = function(p, fs) {
  p = File.normalizePath(p)
  return p in all ? all[p] : all[p] = new File(p)
}

File.normalizePath = function(p) {
  p = File.absolutePath.test(p)
    ? p
    : path.join(process.cwd(), p)
  p = path.normalize(p)
  return p
}

File.absolutePath = /^\//

util.inherits(File, AbstractResource)

util.def(File.prototype, {
  watchStart: function() {
    if (this._watching) return
    this._watching = true
    this._watchModule.watchFile(this._path, function(current, prev) {
      this.emit("change", this)
    }.bind(this))
  }

, watchStop: function() {
    this._watchModule.unwatchFile(this._path)
    this._watching = false
  }

, toString: function() {
    return this._path
  }

, get path() {
    return this._path
  }

, _watchModule: fs
})

module.exports = File
<commit_msg>Remove clutter<commit_after>var util = require('./util')
  , path = require("path")
  , fs = require("fs")
  , all = {}
  , AbstractResource = require("./abstract_resource")

function File(p) {
  AbstractResource.call(this)
  this._path = p
}

File.new = function(p, fs) {
  p = File.normalizePath(p)
  return p in all ? all[p] : all[p] = new File(p)
}

File.normalizePath = function(p) {
  p = File.absolutePath.test(p)
    ? p
    : path.join(process.cwd(), p)
  p = path.normalize(p)
  return p
}

File.absolutePath = /^\//

util.inherits(File, AbstractResource)

util.def(File.prototype, {
  watchStart: function() {
    if (this._watching) return
    this._watching = true
    this._watchModule.watchFile(this._path, function(current, prev) {
      this.emit("change", this)
    }.bind(this))
  }

, watchStop: function() {
    this._watchModule.unwatchFile(this._path)
    this._watching = false
  }

, toString: function() {
    return this._path
  }

, get path() {
    return this._path
  }

, _watchModule: fs
})

module.exports = File
<|endoftext|>


<commit_before>var jsonDefault = JSON.stringify({
	"WhatsUp": "E",
	"Survived": 100000000,
	"History": [
		"Giant Lizards",
		"Meteor",
		"Lizards Dead",
		"Katydids Alive"
	],
	"FeatureRequests": [
		{
			"Name": "Dart",
			"Properties": ["Poison", "Projectile"],
			"Anatomy": "Abdomen"
		},
		{
			"Name": "Fire Breath",
			"Properties": ["Fire", "Vapor"],
			"Anatomy": "Mouth"
		}
	],
	"DragonsExist": false,
	"MonkeysSmart": true,
	"Family": {
		"Class": "Insecta",
		"Order": {
			"Superorder": {
				"Subclass":	"Pterygota",
				"Infraclass": "Polyneoptera"
			},
			"Order": "Orthoptera"
		},
		"Suborder":	"Ensifera",
		"Family": "Tettigoniidae"
	}
}, "", 4);

var defaultKatydid = `(
	.WhatsUp == "E" &
	.Survived >= int(1000000) /*years*/ &
	.DragonsExist != true &
	.MonkeysSmart :: $bool &
	.FeatureRequests._ [
		Name *= "art",
		*,
		Anatomy $= "omen",
	]
)
`;<commit_msg>better array example in default textbox<commit_after>var jsonDefault = JSON.stringify({
	"WhatsUp": "E",
	"Survived": 100000000,
	"History": [
		"Giant Lizards",
		"Meteor",
		"Lizards Dead",
		"Katydids Alive"
	],
	"FeatureRequests": [
		{
			"Name": "Dart",
			"Properties": ["Poison", "Projectile"],
			"Anatomy": "Abdomen"
		},
		{
			"Name": "Fire Breath",
			"Properties": ["Fire", "Vapor"],
			"Anatomy": "Mouth"
		}
	],
	"DragonsExist": false,
	"MonkeysSmart": true,
	"Family": {
		"Class": "Insecta",
		"Order": {
			"Superorder": {
				"Subclass":	"Pterygota",
				"Infraclass": "Polyneoptera"
			},
			"Order": "Orthoptera"
		},
		"Suborder":	"Ensifera",
		"Family": "Tettigoniidae"
	}
}, "", 4);

var defaultKatydid = `(
	.WhatsUp == "E" &
	.Survived >= int(1000000) /*years*/ &
	.DragonsExist != true &
	.MonkeysSmart :: $bool &
	.History [
		*,
		_ == "Katydids Alive"
	] &
	.FeatureRequests._ [
		Name *= "art",
		*,
		Anatomy $= "omen",
	]
)
`;<|endoftext|>


<commit_before>

"use strict";
var Main = module.parent.exports;

Main.ActionHSET = function(Request){
  if(Request.length < 3){
    throw new Error(Main.MSG_ARGS_LESS);
  }
  Main.ValidateArguments(Main.ARGS_ODD, Request.length);
  let Key = Request.shift();
  let Value = this.Database.get(Key);
  try {
    Main.Validate(Main.VAL_HASH, 'HSET', Value);
  } catch(err){
    Value = new Map();
    this.Database.set(Key, Value);
  }
  for(let Number = 0; Number < Request.length; Number += 2){
    let HValue = Main.NormalizeType(Request[Number + 1]);
    Value.set(Request[Number], HValue);
  }
  return {Type: 'OK'};
};

Main.ActionHGET = function(Request){
  let Key = Request.shift();
  let Value = this.Database.get(Key);

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HGET', Value);

  if(Request.length === 1){
    return Value && Value.get(Request[0]) || '';
  } else {
    return Request.map(function(Key){
      return Value && Value.get(Key) || '';
    });
  }
};

Main.ActionHDEL = function(Request){
  if(Request.length < 2){
    throw new Error(Main.MSG_ARGS_LESS);
  }
  let Length = 0;
  let Key = Request.shift();
  let Value = this.Database.get(Key);

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HDEL', Value);
  else
    return Length;

  Request.forEach(function(Name){
    Value.delete(Name);
    Length ++;
  }.bind(this));
  return Length;
};

Main.ActionHEXPIRE = function(Request){
  Main.ValidateArguments(Main.ARGS_ODD, Request.length);
  let Key = Request.shift();
  let Value = this.Database.get(Key);
  let ToReturn = {Type: 'OK'};

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXPIRE', Value);

  for(let Number = 0; Number < Request.length; Number += 2){
    let HKey = Request[Number];
    let HTimeout = Main.NormalizeType(Request[Number + 1]);
    if(typeof HTimeout !== 'number')
      throw new Error("EXPIRE Expects even parameters to be numeric");
    let TimeoutKey = Symbol.for(Key + "\n" + HKey);
    clearTimeout(this.Timeouts[TimeoutKey]);
    this.Timeouts[TimeoutKey] = setTimeout(function(HKey){
      Main.ActionHDEL.call(this, [Key, HKey]);
    }.bind(this, HKey), HTimeout * 1000);
  }

  return ToReturn;
};

Main.ActionHEXISTS = function(Request){
  if(Request.length < 2){
    throw new Error(Main.MSG_ARGS_LESS);
  }

  let Key = Request.shift();
  let Value = this.Database.get(Key);

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);

  if(Request.length === 1){
    return (Value && Value.has(Request[0])) === true ? 1 : 0;
  } else {
    return Request.map(function(HKey){
      return (Value && Value.has(HKey)) === true ? 1 : 0;
    });
  }
};

Main.ActionHGETALL = function(Request){
  Main.ValidateArguments(1, Request.length);

  let Value = this.Database.get(Request[0]);
  let ToReturn = [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);
  else
    return ToReturn;
  Value.forEach(function(Value, Key){
    ToReturn.push(Key);
    ToReturn.push(Value);
  });
  return ToReturn;
};

Main.ActionHKEYS = function(Request){
  Main.ValidateArguments(1, Request.length);

  let Value = this.Database.get(Request[0]);
  let ToReturn = [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);
  else
    return ToReturn;
  let Iterator = Value.keys();
  let IteratorVal = null;
  while(!(IteratorVal = Iterator.next()).done){
    ToReturn.push(IteratorVal.value);
  }
  return ToReturn;
};

Main.ActionHLEN = function(Request){
  Main.ValidateArguments(1, Request.length);

  let Value = this.Database.get(Request[0]);

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);
  else
    return 0;
  return Value.size
};

Main.ActionHVALS = function(Request){
  Main.ValidateArguments(1, Request.length);

  let Value = this.Database.get(Request[0]);
  let ToReturn = [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);
  else
    return ToReturn;
  let Iterator = Value.values();
  let IteratorVal = null;
  while(!(IteratorVal = Iterator.next()).done){
    ToReturn.push(IteratorVal.value);
  }
  return ToReturn;
};

Main.ActionHMGET = Main.ActionHGET;
Main.ActionHMSET = Main.ActionHSET;<commit_msg>:tada: Implement HHSET<commit_after>

"use strict";
var Main = module.parent.exports;

Main.ActionHSET = function(Request){
  if(Request.length < 3){
    throw new Error(Main.MSG_ARGS_LESS);
  }
  Main.ValidateArguments(Main.ARGS_ODD, Request.length);
  let Key = Request.shift();
  let Value = this.Database.get(Key);
  try {
    Main.Validate(Main.VAL_HASH, 'HSET', Value);
  } catch(err){
    Value = new Map();
    this.Database.set(Key, Value);
  }
  for(let Number = 0; Number < Request.length; Number += 2){
    let HValue = Main.NormalizeType(Request[Number + 1]);
    Value.set(Request[Number], HValue);
  }
  return {Type: 'OK'};
};

Main.ActionHGET = function(Request){
  let Key = Request.shift();
  let Value = this.Database.get(Key);

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HGET', Value);

  if(Request.length === 1){
    return Value && Value.get(Request[0]) || '';
  } else {
    return Request.map(function(Key){
      return Value && Value.get(Key) || '';
    });
  }
};

Main.ActionHDEL = function(Request){
  if(Request.length < 2){
    throw new Error(Main.MSG_ARGS_LESS);
  }
  let Length = 0;
  let Key = Request.shift();
  let Value = this.Database.get(Key);

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HDEL', Value);
  else
    return Length;

  Request.forEach(function(Name){
    Value.delete(Name);
    Length ++;
  }.bind(this));
  return Length;
};

Main.ActionHEXPIRE = function(Request){
  Main.ValidateArguments(Main.ARGS_ODD, Request.length);
  let Key = Request.shift();
  let Value = this.Database.get(Key);
  let ToReturn = {Type: 'OK'};

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXPIRE', Value);

  for(let Number = 0; Number < Request.length; Number += 2){
    let HKey = Request[Number];
    let HTimeout = Main.NormalizeType(Request[Number + 1]);
    if(typeof HTimeout !== 'number')
      throw new Error("EXPIRE Expects even parameters to be numeric");
    let TimeoutKey = Symbol.for(Key + "\n" + HKey);
    clearTimeout(this.Timeouts[TimeoutKey]);
    this.Timeouts[TimeoutKey] = setTimeout(function(HKey){
      Main.ActionHDEL.call(this, [Key, HKey]);
    }.bind(this, HKey), HTimeout * 1000);
  }

  return ToReturn;
};

Main.ActionHEXISTS = function(Request){
  if(Request.length < 2){
    throw new Error(Main.MSG_ARGS_LESS);
  }

  let Key = Request.shift();
  let Value = this.Database.get(Key);

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);

  if(Request.length === 1){
    return (Value && Value.has(Request[0])) === true ? 1 : 0;
  } else {
    return Request.map(function(HKey){
      return (Value && Value.has(HKey)) === true ? 1 : 0;
    });
  }
};

Main.ActionHGETALL = function(Request){
  Main.ValidateArguments(1, Request.length);

  let Value = this.Database.get(Request[0]);
  let ToReturn = [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);
  else
    return ToReturn;
  Value.forEach(function(Value, Key){
    ToReturn.push(Key);
    ToReturn.push(Value);
  });
  return ToReturn;
};

Main.ActionHKEYS = function(Request){
  Main.ValidateArguments(1, Request.length);

  let Value = this.Database.get(Request[0]);
  let ToReturn = [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);
  else
    return ToReturn;
  let Iterator = Value.keys();
  let IteratorVal = null;
  while(!(IteratorVal = Iterator.next()).done){
    ToReturn.push(IteratorVal.value);
  }
  return ToReturn;
};

Main.ActionHLEN = function(Request){
  Main.ValidateArguments(1, Request.length);

  let Value = this.Database.get(Request[0]);

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);
  else
    return 0;
  return Value.size
};

Main.ActionHVALS = function(Request){
  Main.ValidateArguments(1, Request.length);

  let Value = this.Database.get(Request[0]);
  let ToReturn = [];

  if(typeof Value !== 'undefined')
    Main.Validate(Main.VAL_HASH, 'HEXISTS', Value);
  else
    return ToReturn;
  let Iterator = Value.values();
  let IteratorVal = null;
  while(!(IteratorVal = Iterator.next()).done){
    ToReturn.push(IteratorVal.value);
  }
  return ToReturn;
};

Main.ActionHMGET = Main.ActionHGET;
Main.ActionHMSET = Main.ActionHSET;

// Custom Functions
/**
 * I personally needed the features below to get this kind-of behaviour
 * =======================
 * QP:USERS {
 *  USER:1 {
 *   NAME: "Steel"
 *   USERNAME: "SteelBrain"
 *  }
 * }
 */

/**
 * HHSET KEY HKEY HHKEY HHVALUE HHKEY2 HHVALUE2
 */
Main.ActionHHSET = function(Request){
  Main.ValidateArguments(Main.ARGS_EVEN, Request.length);
  if(Request.length < 4){
    throw new Error(Main.MSG_ARGS_LESS);
  }
  let Key = Request.shift();
  let Value = this.Database.get(Key);
  try {
    Main.Validate(Main.VAL_HASH, 'HHSET', Value);
  } catch(err){
    Value = new Map();
    this.Database.set(Key, Value);
  }
  let HKey = Request.shift();
  let HValue = Value.get(HKey);
  try {
    Main.Validate(Main.VAL_HASH, 'HHSET', Value);
  } catch(err){
    HValue = new Map();
    Value.set(HKey, HValue);
  }
  for(let Number = 0; Number < Request.length; Number += 2){
    HValue.set(Request[Number], Main.NormalizeType(Request[Number + 1]));
  }
  return {Type: 'OK'};
};<|endoftext|>


<commit_before>define(function(require) {

    'use strict';

    var _                = require('underscore')
      , Base             = require('app/views/Base')
      , autosize         = require('autosize')
      , Thankyou         = require('app/views/Report/Thankyou')
      , File             = require('app/models/File')
      , user             = require('app/store/User')
      , log              = require('bows.min')('Views:Report:Description')

    return Base.extend({

      template: _.template(require('text!tpl/Report/Description.html')),

      requiresLogin: true,

      title: 'Add Description',

      className: 'report screen',

      events: {
        'submit form': 'send',
        'click .js-back': 'back'
      },

      initialize: function(options) {
        this.options = options
        this.router = options.router
        if (localStorage.uploads && window.File && window.FileReader && window.FileList && window.Blob) {
          this.model.set('canUploadFiles', true)
          this.file = new File({ jid: user.get('channelJid') })
        } else {
          log('The File APIs are not fully supported in this browser.')
          this.model.set('canUploadFiles', false)
        }
      },

      afterRender: function() {
        this.input = this.$('.js-description')
        autosize(this.input)
      },

      send: function(event) {
        event.preventDefault()
        this.showSpinner('Sending Report')
        if (!this.file) {
          return this.sendReport()
        }
        this.file.on('error:file', function(message) {
          this.error(message)
        }, this)
        this.file.on('change:id', function(url) {
          log('success', arguments)
          this.sendReport()
        }, this)
        this.file.upload(event)

      },

      sendReport: function(error) {
        this.model.set('description', this.$('.js-description').val())

        if ('home' === this.model.get('category')) {
          this.model.set(
            'idNumber',
            this.$('.js-municipal-account-number').val()
          )
        }

        // also upload photo(s) from this.$('.js-photos').val()

        log('Attempting to create a ticket', this.model)
        this.model.once('ticket:success', _.bind(this.success, this))
        this.model.once('ticket:error', _.bind(this.error, this))
        this.model.save()

      },

      error: function(error) {
        this.closeSpinner()
        this.showError(
          'Unfortunately we couldn\'t post your report. ' +
          'Please try again later.'
        )
      },

      success: function() {
        console.log(arguments)
        this.closeSpinner()
        log('Ticket created successfully')
        var thankyouView = new Thankyou(this.options)
        this.router.showView(thankyouView)
      },

      back: function(event) {
        /* overwrite the back buttons default behaviour
         * in order to hand over the report state
         */
        event.stopPropagation()
        this.router.showReportLocation(this.model)
      },

      onDestroy: function() {
        this.triggerAutosizeEvent('autosize.destroy')
      },

      triggerAutosizeEvent: function(event) {
        var evt = document.createEvent('Event');
        evt.initEvent(event, true, false);
        this.input.get(0).dispatchEvent(evt);
      },

    })

})
<commit_msg>Attach image details to report<commit_after>define(function(require) {

    'use strict';

    var _                = require('underscore')
      , Base             = require('app/views/Base')
      , autosize         = require('autosize')
      , Thankyou         = require('app/views/Report/Thankyou')
      , File             = require('app/models/File')
      , user             = require('app/store/User')
      , log              = require('bows.min')('Views:Report:Description')

    return Base.extend({

      template: _.template(require('text!tpl/Report/Description.html')),

      requiresLogin: true,

      title: 'Add Description',

      className: 'report screen',

      events: {
        'submit form': 'send',
        'click .js-back': 'back'
      },

      initialize: function(options) {
        this.options = options
        this.router = options.router
        if (localStorage.uploads && window.File && window.FileReader && window.FileList && window.Blob) {
          this.model.set('canUploadFiles', true)
          this.file = new File({ jid: user.get('channelJid') })
        } else {
          log('The File APIs are not fully supported in this browser.')
          this.model.set('canUploadFiles', false)
        }
      },

      afterRender: function() {
        this.input = this.$('.js-description')
        autosize(this.input)
      },

      send: function(event) {
        event.preventDefault()
        this.showSpinner('Sending Report')
        if (!this.file) {
          return this.sendReport()
        }
        this.file.on('error:file', function(message) {
          this.error(message)
        }, this)
        this.file.on('change:id', function(details) {
          this.sendReport(details.url + '/' + details.id)
        }, this)
        this.file.upload(event)

      },

      sendReport: function(imageUrl) {
        this.model.set('description', this.$('.js-description').val())

        if ('home' === this.model.get('category')) {
          this.model.set(
            'idNumber',
            this.$('.js-municipal-account-number').val()
          )
        }
        if (imageUrl) {
          this.model.set(
            'description',
            this.model.get('description')
              + '\n\nThe following image was attached: '
              + imageUrl
            )
        }

        // also upload photo(s) from this.$('.js-photos').val()

        log('Attempting to create a ticket', this.model)
        this.model.once('ticket:success', _.bind(this.success, this))
        this.model.once('ticket:error', _.bind(this.error, this))
        this.model.save()

      },

      error: function(error) {
        this.closeSpinner()
        this.showError(
          'Unfortunately we couldn\'t post your report. ' +
          'Please try again later.'
        )
      },

      success: function() {
        this.closeSpinner()
        log('Ticket created successfully')
        var thankyouView = new Thankyou(this.options)
        this.router.showView(thankyouView)
      },

      back: function(event) {
        /* overwrite the back buttons default behaviour
         * in order to hand over the report state
         */
        event.stopPropagation()
        this.router.showReportLocation(this.model)
      },

      onDestroy: function() {
        this.triggerAutosizeEvent('autosize.destroy')
      },

      triggerAutosizeEvent: function(event) {
        var evt = document.createEvent('Event');
        evt.initEvent(event, true, false);
        this.input.get(0).dispatchEvent(evt);
      },

    })

})
<|endoftext|>


<commit_before>const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const itemSchema = new Schema({
    name: {
        type: String,
        required: true
    },
    attributes: [{ type: String }],
    purchased: [{
        type: Schema.Types.ObjectId,
        ref: 'Purchase'
    }],
    stores: [{
        type: Schema.Types.ObjectId,
        ref: 'Store'
    }]
});

//method for best price

module.exports = mongoose.model('Item', itemSchema);<commit_msg>removed ref to deleted purchase schema<commit_after>const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const itemSchema = new Schema({
    name: {
        type: String,
        required: true
    },
    attributes: [{ type: String }],
    stores: [{
        type: Schema.Types.ObjectId,
        ref: 'Store'
    }]
});

//method for best price

module.exports = mongoose.model('Item', itemSchema);<|endoftext|>


<commit_before>/**
 * Module dependencies
 */

var _ = require('lodash');
var async = require('async');
var getRelations = require('../../../utils/getRelations');

/**
 * Try and synchronize the underlying physical-layer schema
 * to work with our app's collections. (i.e. models)
 *
 * @param  {Function} cb
 */
module.exports = function(cb) {
  var self = this;

  // Refuse to run this migration strategy in production.
  if (process.env.NODE_ENV === 'production') {
    return cb(new Error('`migrate: "alter"` strategy is not supported in production, please change to `migrate: "safe"`.'));
  }

  // Find any junctionTables that reference this collection
  var relations = getRelations({
    schema: self.query.waterline.schema,
    parentCollection: self.collection
  });

  var backupData;

  // Check that collection exists--
  self.describe(function afterDescribe(err, attrs) {

    if (err) return cb(err);

    // if it doesn't go ahead and add it and get out
    if (!attrs) return self.define(cb);

    var collectionName = _.find(self.query.waterline.schema, {tableName: self.collection}).identity;

    // Create a mapping of column names -> attribute names
    var columnNamesMap = _.reduce(self.query.waterline.schema[collectionName].definition, function(memo, val, key) {
      // If the attribute has a custom column name, use it as the key for the mapping
      if (val.columnName) {
        memo[val.columnName] = key;
      // Otherwise just use the attribute name
      } else {
        memo[key] = key;
      }
      return memo;
    }, {});

    // Transform column names into attribute names using the columnNamesMap,
    // removing attributes that no longer exist (they will be dropped)
    attrs = _.compact(_.keys(attrs).map(function(key) {
      return columnNamesMap[key];
    }));

    //
    // TODO:
    // Take a look and see if anything important has changed.
    // If it has (at all), we still have to follow the naive strategy below,
    // but it will at least save time in the general case.
    // (because it really sucks to have to wait for all of this to happen
    //  every time you initialize Waterline.)
    //

    //
    // OK so we have to fix up the schema and migrate the data...
    //
    // ... we'll let Waterline do it for us.
    //
    // Load all data from this collection into memory.
    // If this doesn't work, crash to avoid corrupting any data.
    // (see `waterline/lib/adapter/ddl/README.md` for more info about this)
    //
    // Make sure we only select the existing keys for the schema.
    // The default "find all" will select each attribute in the schema, which
    // now includes attributes that haven't been added to the table yet, so
    // on SQL databases the query will fail with "unknown field" error.
    //
    var hasSchema = self.query.hasSchema;

    // If we have a schema, make sure we only select the existing keys for the schema.
    // The default "find all" will select each attribute in the schema, which
    // now includes attributes that haven't been added to the table yet, so
    // on SQL databases the query will fail with "unknown field" error.
    //
    // If we don't have a schema then we need to select all the values to make
    // sure we don't lose data in the process.
    var queryCriteria;

    if (hasSchema) {
      queryCriteria = {select: attrs};
    } else {
      queryCriteria = {};
    }

    self.find(queryCriteria, function(err, existingData) {

      if (err) {
        //
        // TODO:
        // If this was a memory error, log a more useful error
        // explaining what happened.
        //
        return cb(err);
      }

      //
      // From this point forward, we must be very careful.
      //
      backupData = _.cloneDeep(existingData, function dealWithBuffers(val) {
        if (val instanceof Buffer) {
          return val.slice();
        }
      });

      // Check to see if there is anything obviously troublesome
      // that will cause the drop and redefinition of our schemaful
      // collections to fail.
      // (i.e. violation of uniqueness constraints)
      var attrs = self.query.waterline.collections[self.identity]._attributes;
      var pk = self.query.waterline.collections[self.identity].primaryKey;
      var attrsAsArray = _.reduce(_.cloneDeep(attrs), function(memo, attrDef, attrName) {
        attrDef.name = attrName;
        memo.push(attrDef);
        return memo;
      }, []);
      var uniqueAttrs = _.where(attrsAsArray, {unique: true});
      async.each(uniqueAttrs, function(uniqueAttr, each_cb) {
        var uniqueData = _.uniq(_.pluck(existingData, uniqueAttr.name));

        // Remove any unique values who have their values set to undefined or null
        var cleansedExistingData = _.filter(existingData, function(val) {
          return [undefined, null].indexOf(val[uniqueAttr.name]) < 0;
        });

        // Remove any undefined or null values from the unique data
        var cleansedUniqueData = _.filter(uniqueData, function(val) {
          return [undefined, null].indexOf(val) < 0;
        });

        if (cleansedUniqueData.length < cleansedExistingData.length) {
          // Some existing data violates a new uniqueness constraint
          var prompt = require('prompt');
          prompt.start();
          console.log(
            'One or more existing records in your database violate ' +
            'a new uniqueness constraint\n' +
            'on `' + uniqueAttr.name + '` ' +
            'in your `' + self.identity + '` model.');
          console.log();
          console.log('Should we automatically remove duplicates?');
          console.log();
          console.log('** WARNING: DO NOT TYPE "y" IF YOU ARE WORKING WITH PRODUCTION DATA **');
          // var laptimer = setInterval(function beepbeepbeepbeep(){
          //   process.stdout.write('\u0007');
          // }, 1500);
          prompt.get(['y/n'], function(err, results) {
            // clearInterval(laptimer);
            if (err) return each_cb(err);
            var wasConfirmedByUser = _.isString(results['y/n']) && results['y/n'].match(/y/);
            if (wasConfirmedByUser) {

              // Wipe out duplicate records in `backupData` and continue
              // to perform the automigration
              var diff = _.difference(existingData, _.uniq(existingData, false, uniqueAttr.name));

              var destroyCriteria = {};
              destroyCriteria[pk] = _.pluck(diff, pk);
              // console.log(diff, '\n', destroyCriteria);
              backupData = _.remove(backupData, function(datum) {
                return !_.contains(destroyCriteria[pk], datum[pk]);
              });
              return each_cb();
              // console.log(backupData);
              // throw new Error();
              // self.query.waterline.collections[self.collection].destroy(destroyCriteria).exec(each_cb);
            } else return each_cb(new Error('Auto-migration aborted. Please migrate your data manually and then try this again.'));
          });
        } else return each_cb();
      }, function afterAsyncEach(err) {
        if (err) return cb(err);

        // Now we'll drop the collection.
        self.drop(relations, function(err) {
          if (err) return uhoh(err, backupData, cb);

          // Now we'll redefine the collection.
          self.define(function(err) {
            if (err) return uhoh(err, backupData, cb);

            // Now we'll create the `backupData` again,
            // being careful not to run any lifecycle callbacks
            // and disable automatic updating of `createdAt` and
            // `updatedAt` attributes:
            //
            // ((((TODO: actually be careful about said things))))
            //
            self.createEach(backupData, function(err) {
              if (err) return uhoh(err, backupData, cb);

              // Done.
              return cb();
            });

          }); // </define>
        }); // </drop>
      }); // </find>
    });

    //
    // The old way-- (doesn't always work, and is way more
    // complex than we should spend time on for now)
    //
    //   ||      ||      ||      ||      ||      ||
    //   \/      \/      \/      \/      \/      \/
    //
    // Otherwise, if it *DOES* exist, we'll try and guess what changes need to be made
    // self.alter(function(err) {
    //   if (err) return cb(err);
    //   cb();
    // });

  });
};

/**
 * uh oh.
 *
 * If we can't persist the data again, we'll log an error message, then
 * stream the data to stdout as JSON to make sure that it gets persisted
 * SOMEWHERE at least.
 *
 * (this is another reason this automigration strategy cannot be used in
 * production currently..)
 *
 * @param  {[type]}   err        [description]
 * @param  {[type]}   backupData [description]
 * @param  {Function} cb         [description]
 * @return {[type]}              [description]
 */

function uhoh(err, backupData, cb) {

  console.error('Waterline encountered a fatal error when trying to perform the `alter` auto-migration strategy.');
  console.error('In a couple of seconds, the data (cached in memory) will be logged to stdout.');
  console.error('(a failsafe put in place to preserve development data)');
  console.error();
  console.error('In the mean time, here\'s the error:');
  console.error();
  console.error(err);
  console.error();
  console.error();

  setTimeout(function() {
    console.error('================================');
    console.error('Data backup:');
    console.error('================================');
    console.error('');
    console.log(backupData);
    return cb(err);
  }, 1200);

}
<commit_msg>run the find through the actual query function so that all transformers get run<commit_after>/**
 * Module dependencies
 */

var _ = require('lodash');
var async = require('async');
var getRelations = require('../../../utils/getRelations');

/**
 * Try and synchronize the underlying physical-layer schema
 * to work with our app's collections. (i.e. models)
 *
 * @param  {Function} cb
 */
module.exports = function(cb) {
  var self = this;

  // Refuse to run this migration strategy in production.
  if (process.env.NODE_ENV === 'production') {
    return cb(new Error('`migrate: "alter"` strategy is not supported in production, please change to `migrate: "safe"`.'));
  }

  // Find any junctionTables that reference this collection
  var relations = getRelations({
    schema: self.query.waterline.schema,
    parentCollection: self.collection
  });

  var backupData;

  // Check that collection exists--
  self.describe(function afterDescribe(err, attrs) {

    if (err) return cb(err);

    // if it doesn't go ahead and add it and get out
    if (!attrs) return self.define(cb);

    var collectionName = _.find(self.query.waterline.schema, {tableName: self.collection}).identity;

    // Create a mapping of column names -> attribute names
    var columnNamesMap = _.reduce(self.query.waterline.schema[collectionName].definition, function(memo, val, key) {
      // If the attribute has a custom column name, use it as the key for the mapping
      if (val.columnName) {
        memo[val.columnName] = key;
      // Otherwise just use the attribute name
      } else {
        memo[key] = key;
      }
      return memo;
    }, {});

    // Transform column names into attribute names using the columnNamesMap,
    // removing attributes that no longer exist (they will be dropped)
    attrs = _.compact(_.keys(attrs).map(function(key) {
      return columnNamesMap[key];
    }));

    //
    // TODO:
    // Take a look and see if anything important has changed.
    // If it has (at all), we still have to follow the naive strategy below,
    // but it will at least save time in the general case.
    // (because it really sucks to have to wait for all of this to happen
    //  every time you initialize Waterline.)
    //

    //
    // OK so we have to fix up the schema and migrate the data...
    //
    // ... we'll let Waterline do it for us.
    //
    // Load all data from this collection into memory.
    // If this doesn't work, crash to avoid corrupting any data.
    // (see `waterline/lib/adapter/ddl/README.md` for more info about this)
    //
    // Make sure we only select the existing keys for the schema.
    // The default "find all" will select each attribute in the schema, which
    // now includes attributes that haven't been added to the table yet, so
    // on SQL databases the query will fail with "unknown field" error.
    //
    var hasSchema = self.query.hasSchema;

    // If we have a schema, make sure we only select the existing keys for the schema.
    // The default "find all" will select each attribute in the schema, which
    // now includes attributes that haven't been added to the table yet, so
    // on SQL databases the query will fail with "unknown field" error.
    //
    // If we don't have a schema then we need to select all the values to make
    // sure we don't lose data in the process.
    var queryCriteria;

    if (hasSchema) {
      queryCriteria = {select: attrs};
    } else {
      queryCriteria = {};
    }

    self.query.find(queryCriteria, function(err, existingData) {

      if (err) {
        //
        // TODO:
        // If this was a memory error, log a more useful error
        // explaining what happened.
        //
        return cb(err);
      }

      //
      // From this point forward, we must be very careful.
      //
      backupData = _.cloneDeep(existingData, function dealWithBuffers(val) {
        if (val instanceof Buffer) {
          return val.slice();
        }
      });

      // Check to see if there is anything obviously troublesome
      // that will cause the drop and redefinition of our schemaful
      // collections to fail.
      // (i.e. violation of uniqueness constraints)
      var attrs = self.query.waterline.collections[self.identity]._attributes;
      var pk = self.query.waterline.collections[self.identity].primaryKey;
      var attrsAsArray = _.reduce(_.cloneDeep(attrs), function(memo, attrDef, attrName) {
        attrDef.name = attrName;
        memo.push(attrDef);
        return memo;
      }, []);
      var uniqueAttrs = _.where(attrsAsArray, {unique: true});
      async.each(uniqueAttrs, function(uniqueAttr, each_cb) {
        var uniqueData = _.uniq(_.pluck(existingData, uniqueAttr.name));

        // Remove any unique values who have their values set to undefined or null
        var cleansedExistingData = _.filter(existingData, function(val) {
          return [undefined, null].indexOf(val[uniqueAttr.name]) < 0;
        });

        // Remove any undefined or null values from the unique data
        var cleansedUniqueData = _.filter(uniqueData, function(val) {
          return [undefined, null].indexOf(val) < 0;
        });

        if (cleansedUniqueData.length < cleansedExistingData.length) {
          // Some existing data violates a new uniqueness constraint
          var prompt = require('prompt');
          prompt.start();
          console.log(
            'One or more existing records in your database violate ' +
            'a new uniqueness constraint\n' +
            'on `' + uniqueAttr.name + '` ' +
            'in your `' + self.identity + '` model.');
          console.log();
          console.log('Should we automatically remove duplicates?');
          console.log();
          console.log('** WARNING: DO NOT TYPE "y" IF YOU ARE WORKING WITH PRODUCTION DATA **');
          // var laptimer = setInterval(function beepbeepbeepbeep(){
          //   process.stdout.write('\u0007');
          // }, 1500);
          prompt.get(['y/n'], function(err, results) {
            // clearInterval(laptimer);
            if (err) return each_cb(err);
            var wasConfirmedByUser = _.isString(results['y/n']) && results['y/n'].match(/y/);
            if (wasConfirmedByUser) {

              // Wipe out duplicate records in `backupData` and continue
              // to perform the automigration
              var diff = _.difference(existingData, _.uniq(existingData, false, uniqueAttr.name));

              var destroyCriteria = {};
              destroyCriteria[pk] = _.pluck(diff, pk);
              // console.log(diff, '\n', destroyCriteria);
              backupData = _.remove(backupData, function(datum) {
                return !_.contains(destroyCriteria[pk], datum[pk]);
              });
              return each_cb();
              // console.log(backupData);
              // throw new Error();
              // self.query.waterline.collections[self.collection].destroy(destroyCriteria).exec(each_cb);
            } else return each_cb(new Error('Auto-migration aborted. Please migrate your data manually and then try this again.'));
          });
        } else return each_cb();
      }, function afterAsyncEach(err) {
        if (err) return cb(err);

        // Now we'll drop the collection.
        self.drop(relations, function(err) {
          if (err) return uhoh(err, backupData, cb);

          // Now we'll redefine the collection.
          self.define(function(err) {
            if (err) return uhoh(err, backupData, cb);

            // Now we'll create the `backupData` again,
            // being careful not to run any lifecycle callbacks
            // and disable automatic updating of `createdAt` and
            // `updatedAt` attributes:
            //
            // ((((TODO: actually be careful about said things))))
            //
            self.createEach(backupData, function(err) {
              if (err) return uhoh(err, backupData, cb);

              // Done.
              return cb();
            });

          }); // </define>
        }); // </drop>
      }); // </find>
    });

    //
    // The old way-- (doesn't always work, and is way more
    // complex than we should spend time on for now)
    //
    //   ||      ||      ||      ||      ||      ||
    //   \/      \/      \/      \/      \/      \/
    //
    // Otherwise, if it *DOES* exist, we'll try and guess what changes need to be made
    // self.alter(function(err) {
    //   if (err) return cb(err);
    //   cb();
    // });

  });
};

/**
 * uh oh.
 *
 * If we can't persist the data again, we'll log an error message, then
 * stream the data to stdout as JSON to make sure that it gets persisted
 * SOMEWHERE at least.
 *
 * (this is another reason this automigration strategy cannot be used in
 * production currently..)
 *
 * @param  {[type]}   err        [description]
 * @param  {[type]}   backupData [description]
 * @param  {Function} cb         [description]
 * @return {[type]}              [description]
 */

function uhoh(err, backupData, cb) {

  console.error('Waterline encountered a fatal error when trying to perform the `alter` auto-migration strategy.');
  console.error('In a couple of seconds, the data (cached in memory) will be logged to stdout.');
  console.error('(a failsafe put in place to preserve development data)');
  console.error();
  console.error('In the mean time, here\'s the error:');
  console.error();
  console.error(err);
  console.error();
  console.error();

  setTimeout(function() {
    console.error('================================');
    console.error('Data backup:');
    console.error('================================');
    console.error('');
    console.log(backupData);
    return cb(err);
  }, 1200);

}
<|endoftext|>


<commit_before>require('./support/helper');

var path = require("path")
  , expect = require("expect.js")
  , fs = require("fs")
  , Backbone = require("backbone")
  , EndDash = require("../lib/end-dash")
  , viewStore = EndDash.viewStore
  , Model = Backbone.Model
  , Collection = Backbone.Collection
  , generateTemplate = require("./support/generate_template")
  , views = {}

describe("When I initialize a template with a view bound to it", function() {
  beforeEach(function() {
    viewStore.setCustomGetView(function(name) {
      return views[name];
    })
  });

  afterEach(function() { viewStore.setCustomGetView(null); });

  it("it should initialize the view correctly", function(done) {

    function MockView(opts) {
      var that = this
      expect(this).to.be.a(MockView)
      expect(opts.model).to.be(model)
      //next tick because we have to allow the template constructor to return
      //in order to check that it passed itself in
      process.nextTick(function() {
        expect($(".ohHi-").data("view")).to.be(that)
        expect(opts.el.is($(".ohHi-"))).to.be(true)
        done()
      })
    }
    views.testView = MockView

    var model = new Model({ ohHi: "Hello There" })
      , template = generateTemplate(model, '

')
  })

   it("should setup the view heirarchy correctly", function(done) {
    function Parent() {}
    function MockView(opts) {
      var that = this
      expect(this).to.be.a(MockView)
      expect(opts.model).to.be(model)

      //next tick because we have to allow the template constructor to return
      //in order to check that it passed itself in
      process.nextTick(function() {
        expect($(".ohHi-").data("view")).to.be(that)
        expect(opts.el.is($(".ohHi-"))).to.be(true)
        done()
      })
    }

    views.embeddedTestView = MockView
    views.testView = Parent

    var model = new Model({ ohHi: "Hello There" })
      , template = generateTemplate(model, '

')
  })
  it("should setup a view for collections", function(done) {

    function Parent() {}
    function MockView(opts) {
      var that = this
      expect(this).to.be.a(MockView)
      expect(opts.collection).to.be(model.herp.things)
      //next tick because we have to allow the template constructor to return
      //in order to check that it passed itself in
      process.nextTick(function() {
        expect($(".things-").data("view")).to.be(that)
        expect(opts.el.is($(".things-"))).to.be(true)
        done()
      })
    }

    views.testCollectionView = MockView
    views.testView = Parent

    var model = { herp: { things: new Collection([]) } }
      , template = generateTemplate(model, '

')
  })
  it("should bind a view to submodels", function(done) {
    function Parent() {}

    function MockView(opts) {
      var that = this
      expect(this).to.be.a(MockView)
      expect(opts.model.attributes).to.eql(model.thing)
      //next tick because we have to allow the template constructor to return
      //in order to check that it passed itself in
      process.nextTick(function() {
        expect(opts.el.is(".thing-")).to.be(true)
        done()
      })
    }
    views.testCollectionView = MockView
    views.testView = Parent

    var model = { thing: { value: "derp" } }
      , template = generateTemplate(model, '

')
  })

  it("should setup the view with the correct scope, once", function(done) {
    var model = { currentUser: new Model() }
      , template

    views["navigation/menu_view"] = function(opts) {
      expect(opts.model).to.be(model.currentUser)
      done()
    }

    template = generateTemplate(model, '

')
  })
})
<commit_msg>view-construction test semi-coloniized<commit_after>require('./support/helper');

var path = require("path")
  , expect = require("expect.js")
  , fs = require("fs")
  , Backbone = require("backbone")
  , EndDash = require("../lib/end-dash")
  , viewStore = EndDash.viewStore
  , Model = Backbone.Model
  , Collection = Backbone.Collection
  , generateTemplate = require("./support/generate_template")
  , views = {}

describe("When I initialize a template with a view bound to it", function() {
  beforeEach(function() {
    viewStore.setCustomGetView(function(name) {
      return views[name];
    })
  });

  afterEach(function() { viewStore.setCustomGetView(null); });

  it("it should initialize the view correctly", function(done) {

    function MockView(opts) {
      var that = this;
      expect(this).to.be.a(MockView);
      expect(opts.model).to.be(model);
      //next tick because we have to allow the template constructor to return
      //in order to check that it passed itself in
      process.nextTick(function() {
        expect($(".ohHi-").data("view")).to.be(that);
        expect(opts.el.is($(".ohHi-"))).to.be(true);
        done();
      });
    };
    views.testView = MockView;

    var model = new Model({ ohHi: "Hello There" })
      , template = generateTemplate(model, '

');
  })

   it("should setup the view heirarchy correctly", function(done) {
    function Parent() {}
    function MockView(opts) {
      var that = this;
      expect(this).to.be.a(MockView);
      expect(opts.model).to.be(model);

      //next tick because we have to allow the template constructor to return
      //in order to check that it passed itself in
      process.nextTick(function() {
        expect($(".ohHi-").data("view")).to.be(that);
        expect(opts.el.is($(".ohHi-"))).to.be(true);
        done();
      });
    };

    views.embeddedTestView = MockView
    views.testView = Parent

    var model = new Model({ ohHi: "Hello There" })
      , template = generateTemplate(model, '

');
  })
  it("should setup a view for collections", function(done) {

    function Parent() {};
    function MockView(opts) {
      var that = this;
      expect(this).to.be.a(MockView);
      expect(opts.collection).to.be(model.herp.things);
      //next tick because we have to allow the template constructor to return
      //in order to check that it passed itself in
      process.nextTick(function() {
        expect($(".things-").data("view")).to.be(that);
        expect(opts.el.is($(".things-"))).to.be(true);
        done()
      });
    };

    views.testCollectionView = MockView
    views.testView = Parent

    var model = { herp: { things: new Collection([]) } }
      , template = generateTemplate(model, '

')
  })
  it("should bind a view to submodels", function(done) {
    function Parent() {};

    function MockView(opts) {
      var that = this;
      expect(this).to.be.a(MockView);
      expect(opts.model.attributes).to.eql(model.thing);
      //next tick because we have to allow the template constructor to return
      //in order to check that it passed itself in
      process.nextTick(function() {
        expect(opts.el.is(".thing-")).to.be(true);
        done();
      })
    }
    views.testCollectionView = MockView;
    views.testView = Parent;

    var model = { thing: { value: "derp" } }
      , template = generateTemplate(model
      , '

');
  });

  it("should setup the view with the correct scope, once", function(done) {
    var model = { currentUser: new Model() }
      , template;

    views["navigation/menu_view"] = function(opts) {
      expect(opts.model).to.be(model.currentUser);
      done();
    }

    template = generateTemplate(model, '

');
  });
});
<|endoftext|>


<commit_before>// import this polyfill in order to make webapp compatible with IE 11
import 'es6-math'

import {ClassicLegIcon, ClassicModeIcon} from '@opentripplanner/icons'
import { createHashHistory } from 'history'
import { connectRouter, routerMiddleware } from 'connected-react-router'
import React, { Component } from 'react'
import { render } from 'react-dom'
import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
import { Provider } from 'react-redux'
import thunk from 'redux-thunk'
import createLogger from 'redux-logger'

// import Bootstrap Grid components for layout
import { Grid, Row, Col } from 'react-bootstrap'

// import OTP-RR components
import {
  CallTakerControls,
  CallTakerPanel,
  CallTakerWindows,
  DefaultMainPanel,
  DesktopNav,
  Map,
  MobileMain,
  ResponsiveWebapp,
  createCallTakerReducer,
  createOtpReducer,
  createUserReducer
} from './lib'
// load the OTP configuration
import otpConfig from './config.yml'

// Set useCustomIcons to true to override classic icons with the exports from
// custom-icons.js
const useCustomIcons = false

// Define icon sets for modes.
let MyLegIcon = ClassicLegIcon
let MyModeIcon = ClassicModeIcon

if (useCustomIcons) {
  const CustomIcons = require('./custom-icons')
  MyLegIcon = CustomIcons.CustomLegIcon
  MyModeIcon = CustomIcons.CustomModeIcon
}

// create an initial query for demo/testing purposes
// FIXME: Remove. This is just for testing.
const initialQuery = otpConfig.datastoreUrl
  ? {}
  : {
    from: {
      lat: 28.45119,
      lon: -81.36818,
      name: 'P&R'
    },
    to: {
      lat: 28.54834,
      lon: -81.37745,
      name: 'Downtownish'
    },
    numItineraries: 1,
    maxWalkDistance: 1609.34 * 6, // 2 miles
    type: 'ITINERARY'
  }
const history = createHashHistory()
const middleware = [
  thunk,
  routerMiddleware(history) // for dispatching history actions
]

// check if app is being run in development mode. If so, enable redux-logger
if (process.env.NODE_ENV === 'development') {
  middleware.push(createLogger())
}

// set up the Redux store
const store = createStore(
  combineReducers({
    callTaker: createCallTakerReducer(),
    otp: createOtpReducer(otpConfig, initialQuery),
    user: createUserReducer(),
    router: connectRouter(history)
  }),
  compose(applyMiddleware(...middleware))
)
// define a simple responsive UI using Bootstrap and OTP-RR
class OtpRRExample extends Component {
  render () {
    /** desktop view **/
    const desktopView = (

        <DesktopNav />
        <Grid>
          <Row className='main-row'>
            <Col sm={6} md={4} className='sidebar'>
              {/* TODO: handle this switch better. This is just for testing. */}
              {otpConfig.datastoreUrl || otpConfig.routingTypes.find(t => t.key === 'BATCH')
                ? <CallTakerPanel LegIcon={MyLegIcon} ModeIcon={MyModeIcon} />
                : <DefaultMainPanel LegIcon={MyLegIcon} ModeIcon={MyModeIcon} />
              }
            </Col>
            {otpConfig.datastoreUrl ? <CallTakerControls /> : null}
            <Col sm={6} md={8} className='map-container'>
              {otpConfig.datastoreUrl ? <CallTakerWindows /> : null}
              <Map />
            </Col>
          </Row>
        </Grid>

    )

    /** mobile view **/
    const mobileView = (
      <MobileMain
        LegIcon={MyLegIcon}
        ModeIcon={MyModeIcon}
        map={<Map />}
        title={
OpenTripPlanner
}
      />
    )

    /** the main webapp **/
    return (
      <ResponsiveWebapp
        desktopView={desktopView}
        mobileView={mobileView}
        LegIcon={MyLegIcon}
      />
    )
  }
}

// render the app
render(
  (
    <Provider store={store}>
      { /**
     * If not using router history, simply include OtpRRExample here:
     * e.g.
     * <OtpRRExample />
     */
      }
      <OtpRRExample />
    </Provider>
  )
  ,

  document.getElementById('root')
)
<commit_msg>refactor(example): get initial query from config<commit_after>// import this polyfill in order to make webapp compatible with IE 11
import 'es6-math'

import {ClassicLegIcon, ClassicModeIcon} from '@opentripplanner/icons'
import { createHashHistory } from 'history'
import { connectRouter, routerMiddleware } from 'connected-react-router'
import React, { Component } from 'react'
import { render } from 'react-dom'
import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
import { Provider } from 'react-redux'
import thunk from 'redux-thunk'
import createLogger from 'redux-logger'

// import Bootstrap Grid components for layout
import { Grid, Row, Col } from 'react-bootstrap'

// import OTP-RR components
import {
  CallTakerControls,
  CallTakerPanel,
  CallTakerWindows,
  DefaultMainPanel,
  DesktopNav,
  Map,
  MobileMain,
  ResponsiveWebapp,
  createCallTakerReducer,
  createOtpReducer,
  createUserReducer
} from './lib'
// load the OTP configuration
import otpConfig from './config.yml'

// Set useCustomIcons to true to override classic icons with the exports from
// custom-icons.js
const useCustomIcons = false

// Define icon sets for modes.
let MyLegIcon = ClassicLegIcon
let MyModeIcon = ClassicModeIcon

if (useCustomIcons) {
  const CustomIcons = require('./custom-icons')
  MyLegIcon = CustomIcons.CustomLegIcon
  MyModeIcon = CustomIcons.CustomModeIcon
}

// Get the initial query from config (for demo/testing purposes).
const {initialQuery} = otpConfig
const history = createHashHistory()
const middleware = [
  thunk,
  routerMiddleware(history) // for dispatching history actions
]

// check if app is being run in development mode. If so, enable redux-logger
if (process.env.NODE_ENV === 'development') {
  middleware.push(createLogger())
}

// set up the Redux store
const store = createStore(
  combineReducers({
    callTaker: createCallTakerReducer(),
    otp: createOtpReducer(otpConfig, initialQuery),
    user: createUserReducer(),
    router: connectRouter(history)
  }),
  compose(applyMiddleware(...middleware))
)
// define a simple responsive UI using Bootstrap and OTP-RR
class OtpRRExample extends Component {
  render () {
    /** desktop view **/
    const desktopView = (

        <DesktopNav />
        <Grid>
          <Row className='main-row'>
            <Col sm={6} md={4} className='sidebar'>
              {/* TODO: handle this switch better. This is just for testing. */}
              {otpConfig.datastoreUrl || otpConfig.routingTypes.find(t => t.key === 'BATCH')
                ? <CallTakerPanel LegIcon={MyLegIcon} ModeIcon={MyModeIcon} />
                : <DefaultMainPanel LegIcon={MyLegIcon} ModeIcon={MyModeIcon} />
              }
            </Col>
            {otpConfig.datastoreUrl ? <CallTakerControls /> : null}
            <Col sm={6} md={8} className='map-container'>
              {otpConfig.datastoreUrl ? <CallTakerWindows /> : null}
              <Map />
            </Col>
          </Row>
        </Grid>

    )

    /** mobile view **/
    const mobileView = (
      <MobileMain
        LegIcon={MyLegIcon}
        ModeIcon={MyModeIcon}
        map={<Map />}
        title={
OpenTripPlanner
}
      />
    )

    /** the main webapp **/
    return (
      <ResponsiveWebapp
        desktopView={desktopView}
        mobileView={mobileView}
        LegIcon={MyLegIcon}
      />
    )
  }
}

// render the app
render(
  (
    <Provider store={store}>
      { /**
     * If not using router history, simply include OtpRRExample here:
     * e.g.
     * <OtpRRExample />
     */
      }
      <OtpRRExample />
    </Provider>
  )
  ,

  document.getElementById('root')
)
<|endoftext|>


<commit_before>const get = require('lodash.get')

const KNOWN_ERRORS = [
  {
    key: 'email_check',
    field: 'email',
    message: 'Please format your email like `a@b.c`.',
  },
  {
    key: 'pass_check',
    field: 'password',
    message: 'I saw an unfamiliar error.',
  },
  {
    key: 'user_email_key',
    field: 'email',
    message: "I've seen this email address before.",
  },
  { key: 'user_pkey', message: 'I saw an unfamiliar error.' },
  {
    key: 'user_name_key',
    field: 'name',
    message: "I've seen this name before.",
  },
  {
    key: 'create_user',
    message: 'I am unable to send you an email to create your account.',
  },
  { key: 'user_user_id_fkey', message: 'I saw an unfamiliar error.' },
  {
    key: '355CAC69',
    field: 'password',
    message: 'I need at least 8 characters for passwords.',
  },
  {
    key: '51EA51A9',
    field: 'password',
    message: "Your password didn't match.",
  },
  {
    key: '3883C744',
    field: 'email',
    message: "I didn't find an account with that email.",
  },
  {
    key: '4F811CFE',
    field: 'name',
    message: "I couldn't find a matching account.",
  },
  {
    key: '58483A61',
    message: "I couldn't update your email.",
  },
  {
    key: 'EBC6E992',
    message: "I couldn't update your password.",
  },
]

const DEFAULT_FIELD = 'all'

module.exports = function getGqlErrors(e) {
  console.error(e)
  return get(e, 'response.errors', [])
    .map(({ message }) => {
      const found = KNOWN_ERRORS.find(({ key }) => message.indexOf(key) > -1)
      return {
        message: get(found, 'message', message),
        field: get(found, 'field', undefined),
      }
    })
    .reduce((sum, { message, field }) => {
      const xfield = field || DEFAULT_FIELD
      sum[xfield] = sum[xfield] || [] // eslint-disable-line
      sum[xfield].push(message) // eslint-disable-line
      return sum
    }, {})
}
<commit_msg>Update gql-errors.js<commit_after>const get = require('lodash.get')
const toPairs = require('lodash.topairs')

const FIELD_REGEXP = /^<(.*?)>/i

const KNOWN_ERRORS = toPairs({
  email_check: '<email> Please format your email like `a@b.c`.',
  pass_check: '<password> I saw an unfamiliar error.',
  user_email_key: "<email> I've seen this email address before.",
  user_pkey: ' I saw an unfamiliar error.',
  user_name_key: "<name> I've seen this name before.",
  create_user: ' I am unable to send you an email to create your account.',
  user_user_id_fkey: ' I saw an unfamiliar error.',
  '355CAC69': '<password> I need at least 8 characters for passwords.',
  '51EA51A9': "<password> Your password didn't match.",
  '3883C744': "<email> I didn't find an account with that email.",
  '4F811CFE': "<name> I couldn't find a matching account.",
  '58483A61': " I couldn't update your email.",
  EBC6E992: " I couldn't update your password.",
  EE05C989: 'No card found.',
  '1306BF1C': 'You may only respond to a scored card.',
  '681942FD': 'You must submit an available response `id`.',
  '5E310F2E': '<name> Subject name in use.',
  '76177573': 'A reply must belong to the same topic.',
  '8DF72C56': 'A vote may only reply to a proposal.',
  E47E0411: 'A user cannot vote on their own proposal.',
  '47C88D24': 'No user found.',
  CF018471: 'No previous version found.',
  B7615F09: 'No previous version found.',
}).map(([key, message]) => ({
  key,
  field: get(message.match(FIELD_REGEXP), 1, undefined),
  message: message.replace(FIELD_REGEXP, ''),
}))

const DEFAULT_FIELD = 'all'

module.exports = function getGqlErrors(e) {
  if (process.env.NODE_ENV !== 'production') console.error(e) // eslint-disable-line
  return get(e, 'response.errors', [])
    .map(({ message }) =>
      get(KNOWN_ERRORS.filter(({ key }) => message.indexOf(key) > -1), 0, {
        message,
      })
    )
    .reduce((sum, { message, field }) => {
      const xfield = field || DEFAULT_FIELD
      sum[xfield] = sum[xfield] || [] // eslint-disable-line
      sum[xfield].push(message) // eslint-disable-line
      return sum
    }, {})
}
<|endoftext|>


<commit_before>function postItem({input, state, output}) {
    throw new Error('Unimplemented cerebral action postItem');
}
postItem.outputs = ['success', 'error'];

export default postItem;
<commit_msg>added post item<commit_after>function postItem({state, output, services}) {
  const items = state.get('app.items')

  services.http.post('/items', items[items.length - 1])
    .then(output.success)
    .catch(output.error)

}

postItem.async = true
postItem.outputs = ['success', 'error'];

export default postItem;
<|endoftext|>