<commit_before>function Hero(game, x, y) {
    Phaser.Sprite.call(this, game, x, y, 'hero');
    this.anchor.set(0.5, 0.5);

    this.game.physics.enable(this);
    this.body.collideWorldBounds = true;

    this.animations.add('stop', [0]);
    this.animations.add('run', [1, 2], 8, true);
    this.animations.add('jump', [3]);
    this.animations.add('fall', [4]);
}

Hero.prototype = Object.create(Phaser.Sprite.prototype);
Hero.prototype.constructor = Hero;

Hero.prototype.move = function (direction) {
    const SPEED = 200;
    this.body.velocity.x = direction * SPEED;

    if (this.body.velocity.x < 0) {
        this.scale.x = -1;
    }
    else if (this.body.velocity.x > 0) {
        this.scale.x = 1;
    }
};

Hero.prototype.jump = function () {
    const JUMP_SPEED = 600;
    let canJump = this.body.touching.down;

    if (canJump) {
        this.body.velocity.y = -JUMP_SPEED;
    }

    return canJump;
};

Hero.prototype.bounce = function () {
    const BOUNCE_SPEED = 200;
    this.body.velocity.y = -BOUNCE_SPEED;
};

Hero.prototype.update = function () {
    let animationName = this._getAnimationName();
    if (this.animations.name !== animationName) {
        this.animations.play(animationName);
    }
};

Hero.prototype._getAnimationName = function () {
    let name = 'stop';

    if (this.body.velocity.y < 0) {
        name = 'jump';
    }

    else if (this.body.velocity.y >= 0 && !this.body.touching.down) {
        name = 'fall';
    }
    else if (this.body.velocity.x !== 0 && this.body.touching.down) {
        name = 'run';
    }

    return name;
};

function Spider(game, x, y) {
    Phaser.Sprite.call(this, game, x, y, 'spider');

    this.anchor.set(0.5);

    this.animations.add('crawl', [0, 1, 2], 8, true);
    this.animations.add('die', [0, 4, 0, 4, 0, 4, 3, 3, 3, 3, 3, 3], 12);
    this.animations.play('crawl');

    this.game.physics.enable(this);
    this.body.collideWorldBounds = true;
    this.body.velocity.x = Spider.SPEED;
}

Spider.SPEED = 100;

Spider.prototype = Object.create(Phaser.Sprite.prototype);
Spider.prototype.constructor = Spider;

Spider.prototype.update = function () {
    if (this.body.touching.right || this.body.blocked.right) {
        this.body.velocity.x = -Spider.SPEED;
    }
    else if (this.body.touching.left || this.body.blocked.left) {
        this.body.velocity.x = Spider.SPEED;
    }
};

Spider.prototype.die = function () {
    this.body.enable = false;

    this.animations.play('die').onComplete.addOnce(function () {
        this.kill();
    }, this);
};

PlayState = {};

PlayState.init = function () {
    this.game.renderer.renderSession.roundPixels = true;

    this.keys = this.game.input.keyboard.addKeys({
        left: Phaser.KeyCode.LEFT,
        right: Phaser.KeyCode.RIGHT,
        up: Phaser.KeyCode.UP
    });

    this.keys.up.onDown.add(function () {
        let didJump = this.hero.jump();
        if (didJump) {
            this.sfx.jump.play();
        }
    }, this);

    this.coinPickupCount = 0;
    this.hasKey = false;
};

PlayState.preload = function () {
    this.game.load.json('level0', 'data/level00.j')
    this.game.load.json('level:1', 'data/level01.json');

    this.game.load.image('font:numbers', 'images/numbers.png');

    this.game.load.image('background', 'images/background1.png');
    this.game.load.image('ground', 'images/ground1.png');
    this.game.load.image('grass:8x1', 'images/sand_8x1.png');
    this.game.load.image('grass:6x1', 'images/sand_6x1.png');
    this.game.load.image('grass:4x1', 'images/sand_4x1.png');
    this.game.load.image('grass:2x1', 'images/sand_2x1.png');
    this.game.load.image('grass:1x1', 'images/sand_1x1.png');
    this.game.load.image('invisible-wall', 'images/invisible_wall.png');
    this.game.load.image('icon:coin', 'images/coin_icon.png');
    this.game.load.image('key', 'images/key.png')

    this.game.load.spritesheet('coin', 'images/coin_animated.png', 22, 22);
    this.game.load.spritesheet('spider', 'images/spider.png', 42, 32);
    this.game.load.spritesheet('hero', 'images/hero1.png', 36, 42);
    this.game.load.spritesheet('door', 'images/door1.png', 42, 66);
    this.game.load.spritesheet('icon:key', 'images/key_icon.png', 34, 30);

    this.game.load.audio('sfx:jump', 'audio/jump.wav');
    this.game.load.audio('sfx:coin', 'audio/coin.wav');
    this.game.load.audio('sfx:stomp', 'audio/stomp.wav');
    this.game.load.audio('sfx:key', 'audio/key.wav');
    this.game.load.audio('sfx:door', 'audio/door.wav');
};

PlayState.create = function () {
    this.sfx = {
        jump: this.game.add.audio('sfx:jump'),
        coin: this.game.add.audio('sfx:coin'),
        stomp: this.game.add.audio('sfx:stomp'),
        key: this.game.add.audio('sfx:key'),
        door: this.game.add.audio('sfx:door')
    };

    this.game.add.image(0, 0, 'background');
    this._loadLevel(this.game.cache.getJSON('level:1'));

    this._createHud();
};

PlayState.update = function () {
    this._handleCollisions();
    this._handleInput();

    this.coinFont.text = `x${this.coinPickupCount}`;
    this.keyIcon.frame = this.hasKey ? 1 : 0;
};

PlayState._handleCollisions = function () {
    this.game.physics.arcade.collide(this.spiders, this.platforms);
    this.game.physics.arcade.collide(this.spiders, this.enemyWalls);
    this.game.physics.arcade.collide(this.hero, this.platforms);

    this.game.physics.arcade.overlap(this.hero, this.coins, this._onHeroVsCoin,
        null, this);
    this.game.physics.arcade.overlap(this.hero, this.spiders,
        this._onHeroVsEnemy, null, this);
    this.game.physics.arcade.overlap(this.hero, this.key, this._onHeroVsKey,
        null, this)
    this.game.physics.arcade.overlap(this.hero, this.door, this._onHeroVsDoor,

        function (hero, door) {
            return this.hasKey && hero.body.touching.down;
        }, this);
};

PlayState._handleInput = function () {
    if (this.keys.left.isDown) {
        this.hero.move(-1);
    }
    else if (this.keys.right.isDown) {
        this.hero.move(1);
    }
    else {
        this.hero.move(0);
    }
};

PlayState._loadLevel = function (data) {
    this.bgDecoration = this.game.add.group();
    this.platforms = this.game.add.group();
    this.coins = this.game.add.group();
    this.spiders = this.game.add.group();
    this.enemyWalls = this.game.add.group();
    this.enemyWalls.visible = false;

    data.platforms.forEach(this._spawnPlatform, this);

    this._spawnCharacters({hero: data.hero, spiders: data.spiders});

    data.coins.forEach(this._spawnCoin, this);
    this._spawnDoor(data.door.x, data.door.y);
    this._spawnKey(data.key.x, data.key.y);

    const GRAVITY = 1200;
    this.game.physics.arcade.gravity.y = GRAVITY;
};

PlayState._spawnPlatform = function (platform) {
    let sprite = this.platforms.create(
        platform.x, platform.y, platform.image);

    this.game.physics.enable(sprite);
    sprite.body.allowGravity = false;
    sprite.body.immovable = true;

    this._spawnEnemyWall(platform.x, platform.y, 'left');
    this._spawnEnemyWall(platform.x + sprite.width, platform.y, 'right');
};

PlayState._spawnEnemyWall = function (x, y, side) {
    let sprite = this.enemyWalls.create(x, y, 'invisible-wall');

    sprite.anchor.set(side === 'left' ? 1 : 0, 1);

    this.game.physics.enable(sprite);
    sprite.body.immovable = true;
    sprite.body.allowGravity = false;
};

PlayState._spawnCharacters = function (data) {
    data.spiders.forEach(function (spider) {
        let sprite = new Spider(this.game, spider.x, spider.y);
        this.spiders.add(sprite);
    }, this);

    this.hero = new Hero(this.game, data.hero.x, data.hero.y);
    this.game.add.existing(this.hero);
};

PlayState._spawnCoin = function (coin) {
    let sprite = this.coins.create(coin.x, coin.y, 'coin');
    sprite.anchor.set(0.5, 0.5);

    this.game.physics.enable(sprite);
    sprite.body.allowGravity = false;

    sprite.animations.add('rotate', [0, 1, 2, 1], 6, true);
    sprite.animations.play('rotate');
};

PlayState._spawnDoor = function (x, y) {
    this.door = this.bgDecoration.create(x, y, 'door');
    this.door.anchor.setTo(0.5, 1);
    this.game.physics.enable(this.door);
    this.door.body.allowGravity = false;
};

PlayState._spawnKey = function (x, y) {
    this.key = this.bgDecoration.create(x, y, 'key');
    this.key.anchor.set(0.5, 0.5);
    this.game.physics.enable(this.key);
    this.key.body.allowGravity = false;
    this.key.y -= 3;
    this.game.add.tween(this.key)
        .to({y: this.key.y + 6}, 800, Phaser.Easing.Sinusoidal.InOut)
        .yoyo(true)
        .loop()
        .start();
};

PlayState._onHeroVsCoin = function (hero, coin) {
    this.sfx.coin.play();
    coin.kill();
    this.coinPickupCount++;
};

PlayState._onHeroVsEnemy = function (hero, enemy) {
    if (hero.body.velocity.y > 0) {
        hero.bounce();
        enemy.die();
        this.sfx.stomp.play();
    }
    else {
        this.sfx.stomp.play();
        this.game.state.restart();
    }
};

PlayState._onHeroVsKey = function (hero, key) {
    this.sfx.key.play();
    key.kill();
    this.hasKey = true;
};

PlayState._onHeroVsDoor = function (hero, door) {
    this.sfx.door.play();
    this.game.state.restart();
};

PlayState._createHud = function () {
    const NUMBERS_STR = '0123456789X ';
    this.coinFont = this.game.add.retroFont('font:numbers', 20, 26,
        NUMBERS_STR);

    this.keyIcon = this.game.make.image(0, 19, 'icon:key');
    this.keyIcon.anchor.set(0, 0.5);

    let coinIcon = this.game.make.image(this.keyIcon.width + 7, 0, 'icon:coin');
    let coinScoreImg = this.game.make.image(coinIcon.x + coinIcon.width,
        coinIcon.height / 2, this.coinFont);
    coinScoreImg.anchor.set(0, 0.5);

    this.hud = this.game.add.group();
    this.hud.add(coinIcon);
    this.hud.add(coinScoreImg);
    this.hud.add(this.keyIcon);
    this.hud.position.set(10, 10);
};

window.onload = function () {
    let game = new Phaser.Game(960, 600, Phaser.AUTO, 'game');
    game.state.add('play', PlayState);
    game.state.start('play');
};<commit_msg>finish game<commit_after>function Hero(game, x, y) {
    Phaser.Sprite.call(this, game, x, y, 'hero');
    this.anchor.set(0.5, 0.5);

    this.game.physics.enable(this);
    this.body.collideWorldBounds = true;

    this.animations.add('stop', [0]);
    this.animations.add('run', [1, 2], 8, true);
    this.animations.add('jump', [3]);
    this.animations.add('fall', [4]);
}

Hero.prototype = Object.create(Phaser.Sprite.prototype);
Hero.prototype.constructor = Hero;

Hero.prototype.move = function (direction) {
    const SPEED = 200;
    this.body.velocity.x = direction * SPEED;

    if (this.body.velocity.x < 0) {
        this.scale.x = -1;
    }
    else if (this.body.velocity.x > 0) {
        this.scale.x = 1;
    }
};

Hero.prototype.jump = function () {
    const JUMP_SPEED = 600;
    let canJump = this.body.touching.down;

    if (canJump) {
        this.body.velocity.y = -JUMP_SPEED;
    }

    return canJump;
};

Hero.prototype.bounce = function () {
    const BOUNCE_SPEED = 200;
    this.body.velocity.y = -BOUNCE_SPEED;
};

Hero.prototype.update = function () {
    let animationName = this._getAnimationName();
    if (this.animations.name !== animationName) {
        this.animations.play(animationName);
    }
};

Hero.prototype._getAnimationName = function () {
    let name = 'stop';

    if (this.body.velocity.y < 0) {
        name = 'jump';
    }

    else if (this.body.velocity.y >= 0 && !this.body.touching.down) {
        name = 'fall';
    }
    else if (this.body.velocity.x !== 0 && this.body.touching.down) {
        name = 'run';
    }

    return name;
};

function Spider(game, x, y) {
    Phaser.Sprite.call(this, game, x, y, 'spider');

    this.anchor.set(0.5);

    this.animations.add('crawl', [0, 1, 2], 8, true);
    this.animations.add('die', [0, 4, 0, 4, 0, 4, 3, 3, 3, 3, 3, 3], 12);
    this.animations.play('crawl');

    this.game.physics.enable(this);
    this.body.collideWorldBounds = true;
    this.body.velocity.x = Spider.SPEED;
}

Spider.SPEED = 100;

Spider.prototype = Object.create(Phaser.Sprite.prototype);
Spider.prototype.constructor = Spider;

Spider.prototype.update = function () {
    if (this.body.touching.right || this.body.blocked.right) {
        this.body.velocity.x = -Spider.SPEED;
    }
    else if (this.body.touching.left || this.body.blocked.left) {
        this.body.velocity.x = Spider.SPEED;
    }
};

Spider.prototype.die = function () {
    this.body.enable = false;

    this.animations.play('die').onComplete.addOnce(function () {
        this.kill();
    }, this);
};

PlayState = {};

const LEVEL_COUNT = 2;

PlayState.init = function (data) {
    this.game.renderer.renderSession.roundPixels = true;

    this.keys = this.game.input.keyboard.addKeys({
        left: Phaser.KeyCode.LEFT,
        right: Phaser.KeyCode.RIGHT,
        up: Phaser.KeyCode.UP
    });

    this.keys.up.onDown.add(function () {
        let didJump = this.hero.jump();
        if (didJump) {
            this.sfx.jump.play();
        }
    }, this);

    this.coinPickupCount = 0;
    this.hasKey = false;
    this.level = (data.level || 0) % LEVEL_COUNT;
};

PlayState.preload = function () {
    this.game.load.json('level:0', 'data/level00.json')
    this.game.load.json('level:1', 'data/level01.json');

    this.game.load.image('font:numbers', 'images/numbers.png');

    this.game.load.image('background', 'images/background1.png');
    this.game.load.image('ground', 'images/ground1.png');
    this.game.load.image('grass:8x1', 'images/sand_8x1.png');
    this.game.load.image('grass:6x1', 'images/sand_6x1.png');
    this.game.load.image('grass:4x1', 'images/sand_4x1.png');
    this.game.load.image('grass:2x1', 'images/sand_2x1.png');
    this.game.load.image('grass:1x1', 'images/sand_1x1.png');
    this.game.load.image('invisible-wall', 'images/invisible_wall.png');
    this.game.load.image('icon:coin', 'images/coin_icon.png');
    this.game.load.image('key', 'images/key.png')

    this.game.load.spritesheet('coin', 'images/coin_animated.png', 22, 22);
    this.game.load.spritesheet('spider', 'images/spider.png', 42, 32);
    this.game.load.spritesheet('hero', 'images/hero1.png', 36, 42);
    this.game.load.spritesheet('door', 'images/door1.png', 42, 66);
    this.game.load.spritesheet('icon:key', 'images/key_icon.png', 34, 30);

    this.game.load.audio('sfx:jump', 'audio/jump.wav');
    this.game.load.audio('sfx:coin', 'audio/coin.wav');
    this.game.load.audio('sfx:stomp', 'audio/stomp.wav');
    this.game.load.audio('sfx:key', 'audio/key.wav');
    this.game.load.audio('sfx:door', 'audio/door.wav');
};

PlayState.create = function () {
    this.sfx = {
        jump: this.game.add.audio('sfx:jump'),
        coin: this.game.add.audio('sfx:coin'),
        stomp: this.game.add.audio('sfx:stomp'),
        key: this.game.add.audio('sfx:key'),
        door: this.game.add.audio('sfx:door')
    };

    this.game.add.image(0, 0, 'background');
    this._loadLevel(this.game.cache.getJSON(`level:${this.level}`));

    this._createHud();
};

PlayState.update = function () {
    this._handleCollisions();
    this._handleInput();

    this.coinFont.text = `x${this.coinPickupCount}`;
    this.keyIcon.frame = this.hasKey ? 1 : 0;
};

PlayState._handleCollisions = function () {
    this.game.physics.arcade.collide(this.spiders, this.platforms);
    this.game.physics.arcade.collide(this.spiders, this.enemyWalls);
    this.game.physics.arcade.collide(this.hero, this.platforms);

    this.game.physics.arcade.overlap(this.hero, this.coins, this._onHeroVsCoin,
        null, this);
    this.game.physics.arcade.overlap(this.hero, this.spiders,
        this._onHeroVsEnemy, null, this);
    this.game.physics.arcade.overlap(this.hero, this.key, this._onHeroVsKey,
        null, this)
    this.game.physics.arcade.overlap(this.hero, this.door, this._onHeroVsDoor,

        function (hero, door) {
            return this.hasKey && hero.body.touching.down;
        }, this);
};

PlayState._handleInput = function () {
    if (this.keys.left.isDown) {
        this.hero.move(-1);
    }
    else if (this.keys.right.isDown) {
        this.hero.move(1);
    }
    else {
        this.hero.move(0);
    }
};

PlayState._loadLevel = function (data) {
    this.bgDecoration = this.game.add.group();
    this.platforms = this.game.add.group();
    this.coins = this.game.add.group();
    this.spiders = this.game.add.group();
    this.enemyWalls = this.game.add.group();
    this.enemyWalls.visible = false;

    data.platforms.forEach(this._spawnPlatform, this);

    this._spawnCharacters({hero: data.hero, spiders: data.spiders});

    data.coins.forEach(this._spawnCoin, this);
    this._spawnDoor(data.door.x, data.door.y);
    this._spawnKey(data.key.x, data.key.y);

    const GRAVITY = 1200;
    this.game.physics.arcade.gravity.y = GRAVITY;
};

PlayState._spawnPlatform = function (platform) {
    let sprite = this.platforms.create(
        platform.x, platform.y, platform.image);

    this.game.physics.enable(sprite);
    sprite.body.allowGravity = false;
    sprite.body.immovable = true;

    this._spawnEnemyWall(platform.x, platform.y, 'left');
    this._spawnEnemyWall(platform.x + sprite.width, platform.y, 'right');
};

PlayState._spawnEnemyWall = function (x, y, side) {
    let sprite = this.enemyWalls.create(x, y, 'invisible-wall');

    sprite.anchor.set(side === 'left' ? 1 : 0, 1);

    this.game.physics.enable(sprite);
    sprite.body.immovable = true;
    sprite.body.allowGravity = false;
};

PlayState._spawnCharacters = function (data) {
    data.spiders.forEach(function (spider) {
        let sprite = new Spider(this.game, spider.x, spider.y);
        this.spiders.add(sprite);
    }, this);

    this.hero = new Hero(this.game, data.hero.x, data.hero.y);
    this.game.add.existing(this.hero);
};

PlayState._spawnCoin = function (coin) {
    let sprite = this.coins.create(coin.x, coin.y, 'coin');
    sprite.anchor.set(0.5, 0.5);

    this.game.physics.enable(sprite);
    sprite.body.allowGravity = false;

    sprite.animations.add('rotate', [0, 1, 2, 1], 6, true);
    sprite.animations.play('rotate');
};

PlayState._spawnDoor = function (x, y) {
    this.door = this.bgDecoration.create(x, y, 'door');
    this.door.anchor.setTo(0.5, 1);
    this.game.physics.enable(this.door);
    this.door.body.allowGravity = false;
};

PlayState._spawnKey = function (x, y) {
    this.key = this.bgDecoration.create(x, y, 'key');
    this.key.anchor.set(0.5, 0.5);
    this.game.physics.enable(this.key);
    this.key.body.allowGravity = false;
    this.key.y -= 3;
    this.game.add.tween(this.key)
        .to({y: this.key.y + 6}, 800, Phaser.Easing.Sinusoidal.InOut)
        .yoyo(true)
        .loop()
        .start();
};

PlayState._onHeroVsCoin = function (hero, coin) {
    this.sfx.coin.play();
    coin.kill();
    this.coinPickupCount++;
};

PlayState._onHeroVsEnemy = function (hero, enemy) {
    if (hero.body.velocity.y > 0) {
        hero.bounce();
        enemy.die();
        this.sfx.stomp.play();
    }
    else {
        this.sfx.stomp.play();
        this.game.state.restart();
    }
};

PlayState._onHeroVsKey = function (hero, key) {
    this.sfx.key.play();
    key.kill();
    this.hasKey = true;
};

PlayState._onHeroVsDoor = function (hero, door) {
    this.sfx.door.play();
    this.game.state.restart(true, false, { level: this.level + 1});
};

PlayState._createHud = function () {
    const NUMBERS_STR = '0123456789X ';
    this.coinFont = this.game.add.retroFont('font:numbers', 20, 26,
        NUMBERS_STR);

    this.keyIcon = this.game.make.image(0, 19, 'icon:key');
    this.keyIcon.anchor.set(0, 0.5);

    let coinIcon = this.game.make.image(this.keyIcon.width + 7, 0, 'icon:coin');
    let coinScoreImg = this.game.make.image(coinIcon.x + coinIcon.width,
        coinIcon.height / 2, this.coinFont);
    coinScoreImg.anchor.set(0, 0.5);

    this.hud = this.game.add.group();
    this.hud.add(coinIcon);
    this.hud.add(coinScoreImg);
    this.hud.add(this.keyIcon);
    this.hud.position.set(10, 10);
};

window.onload = function () {
    let game = new Phaser.Game(960, 600, Phaser.AUTO, 'game');
    game.state.add('play', PlayState);
    game.state.start('play', true, false, {level: 0});
};<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const {Cc, Ci} = require("chrome");

let {Windows} = require("./util/windows");
let {Overlays} = require("./core/overlay");
let {Shortcut} = require("./core/shortcut");
let {Prefs} = require("sdk/preferences/service");

require("./prefs/prefpane");
require("./patch/bug-645371");
require("./patch/bug-78414");

if (require("./util/os.js").checkOSID() == "Darwin") // Add "Next/Previous Tab" to the Keyset. We'll be adding the menu items along with the one for our extension.
require("./patch/bug-406199");

let onKeyDown = function(event) {
    let shortcut = Shortcut.fromEvent(event);

    // check if this is a custom shortcut
    let overlay = Overlays.findByCustomShortcut(shortcut);

    if (overlay) overlay.key.executeCommand();
    else // check if this is a shortcut that is overriden by a custom one
    overlay = Overlays.findByOverriddenShortcut(shortcut);

    if (overlay) {
        event.preventDefault();
        event.stopPropagation();
    }
};

exports.main = function(options, callbacks) {
    Windows.addEventListener("keydown", onKeyDown);
};

exports.onUnload = function(reason) {
    Windows.removeEventListener("keydown", onKeyDown);
};
<commit_msg>Last instance of a superfluous ".js"<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const {Cc, Ci} = require("chrome");

let {Windows} = require("./util/windows");
let {Overlays} = require("./core/overlay");
let {Shortcut} = require("./core/shortcut");
let {Prefs} = require("sdk/preferences/service");

require("./prefs/prefpane");
require("./patch/bug-645371");
require("./patch/bug-78414");

if (require("./util/os").checkOSID() == "Darwin") // Add "Next/Previous Tab" to the Keyset. We'll be adding the menu items along with the one for our extension.
require("./patch/bug-406199");

let onKeyDown = function(event) {
    let shortcut = Shortcut.fromEvent(event);

    // check if this is a custom shortcut
    let overlay = Overlays.findByCustomShortcut(shortcut);

    if (overlay) overlay.key.executeCommand();
    else // check if this is a shortcut that is overriden by a custom one
    overlay = Overlays.findByOverriddenShortcut(shortcut);

    if (overlay) {
        event.preventDefault();
        event.stopPropagation();
    }
};

exports.main = function(options, callbacks) {
    Windows.addEventListener("keydown", onKeyDown);
};

exports.onUnload = function(reason) {
    Windows.removeEventListener("keydown", onKeyDown);
};
<|endoftext|>


<commit_before>"use strict";

var _ = require('underscore');
var THREE = require('three');
var $ = require('jquery');

var atomic = require('../../model/atomic');

const MOVE_THROTTLE = 50;

/**
 * Create a closure for handling mouse events in viewport.
 * Holds state usable by all event handlers and should be bound to the
 * Viewport view instance.
 */
function MouseHandler () {

    // Helpers
    // ------------------------------------------------------------------------

    /**
     * Find the 4 landmarks closest to a location (THREE vector)
     * from a LandmarkGroup
     *
     * @param  {LandmarkGroup} lmGroup
     * @param  {THREE.Vector} loc
     *
     * @return {Landmark[]}
     */
    var findClosestLandmarks = (lmGroup, loc, locked=false) => {
        var dist, i, j,
            lm, lmLoc,
            minDist, minLm,
            dists = new Array(4), lms= new Array(4);

        for (i = lmGroup.landmarks.length - 1; i >= 0; i--) {
            lm = lmGroup.landmarks[i];
            lmLoc = lm.point();

            if (lmLoc === null || (locked && lm === beingEditedLandmark)) {
                continue;
            }

            dist = loc.distanceTo(lmLoc);

            // Compare to stored lm in order, 0 being the closest
            for (j = 0; j < 3; j++) {
                minDist = dists[j];
                if (!minDist) {
                    [dists[j], lms[j]] = [dist, lm];
                    break;
                } else if (dist <= minDist) { // leq to ensure we always have 4
                    dists.splice(j, 0, dist);
                    lms.splice(j, 0, lm);
                    break;
                }
            }
        }

        return lms;
    }

    // Setup handler state variables
    // ------------------------------------------------------------------------
    var downEvent,
        lmPressed, lmPressedWasSelected,
        isPressed, hasGroupSelection,
        beingEditedLandmark;

    // x, y position of mouse on click states
    var onMouseDownPosition = new THREE.Vector2(),
        onMouseUpPosition = new THREE.Vector2();

    // current screen position when in drag state
    var positionLmDrag = new THREE.Vector2();
    // vector difference in one time step
    var deltaLmDrag = new THREE.Vector2();

    var intersectsWithLms, intersectsWithMesh;

    // Press handling
    // ------------------------------------------------------------------------

    var meshPressed = () => {
        console.log('mesh pressed!');
        if (hasGroupSelection) {
            hasGroupSelection = false;
            nothingPressed();
        } else if (event.button === 0 && event.shiftKey) {
            shiftPressed();  // LMB + SHIFT
        } else {
            $(document).one('mouseup.viewportMesh', meshOnMouseUp);
        }
    }

    var landmarkPressed = () => {
        var ctrl = (downEvent.ctrlKey || downEvent.metaKey);
        console.log('Viewport: landmark pressed');
        // before anything else, disable the camera
        this.cameraController.disable();
        // the clicked on landmark
        var landmarkSymbol = intersectsWithLms[0].object;
        // hunt through the landmarkViews for the right symbol
        for (var i = 0; i < this.landmarkViews.length; i++) {
            if (this.landmarkViews[i].symbol === landmarkSymbol) {
                lmPressed = this.landmarkViews[i].model;
            }
        }
        console.log('Viewport: finding the selected points');
        lmPressedWasSelected = lmPressed.isSelected();

        if (!lmPressedWasSelected && !ctrl) {
            // this lm wasn't pressed before and we aren't holding
            // mutliselection down - deselect rest and select this
            console.log("normal click on a unselected lm - deselecting rest and selecting me");
            lmPressed.selectAndDeselectRest();
        }

        if (ctrl && !lmPressedWasSelected) {
            lmPressed.select();
        }

        // record the position of where the drag started.
        positionLmDrag.copy(this.localToScreen(lmPressed.point()));
        // start listening for dragging landmarks
        $(document).on('mousemove.landmarkDrag', landmarkOnDrag);
        $(document).one(
            'mouseup.viewportLandmark', landmarkOnMouseUp);
    }

    var nothingPressed = () => {
        console.log('nothing pressed!');
        hasGroupSelection = false;
        $(document).one(
            'mouseup.viewportNothing', nothingOnMouseUp);
    }

    var shiftPressed = () => {
        console.log('shift pressed!');
        // before anything else, disable the camera
        hasGroupSelection = false;
        this.cameraController.disable();
        $(document).on('mousemove.shiftDrag', shiftOnDrag);
        $(document).one('mouseup.viewportShift', shiftOnMouseUp);
    }

    // Catch all clicks and delegate to other handlers once user's intent
    // has been figured out
    var onMouseDown = (event) => {
        event.preventDefault();
        this.$el.focus();

        isPressed = true;

        downEvent = event;
        onMouseDownPosition.set(event.clientX, event.clientY);

        // All interactions require intersections to distinguish
        intersectsWithLms = this.getIntersectsFromEvent(
            event, this.s_lms);
        // note that we explicitly ask for intersects with the mesh
        // object as we know get intersects will use an octree if
        // present.
        intersectsWithMesh = this.getIntersectsFromEvent(event, this.mesh);

        // Click type, we use MouseEvent.button which is the vanilla JS way
        // jQuery also exposes event.which which has different bindings
        if (event.button === 0) {  // left mouse button
            if (intersectsWithLms.length > 0 &&
                intersectsWithMesh.length > 0) {
                // degenerate case - which is closer?
                if (intersectsWithLms[0].distance <
                    intersectsWithMesh[0].distance) {
                    landmarkPressed(event);
                } else {
                    // the mesh was pressed. Check for shift first.
                    if (event.shiftKey) {
                        shiftPressed();
                    } else {
                        meshPressed();
                    }
                }
            } else if (intersectsWithLms.length > 0) {
                landmarkPressed(event);
            } else if (event.shiftKey) {
                // shift trumps all!
                shiftPressed();
            } else if (intersectsWithMesh.length > 0) {
                meshPressed();
            } else {
                nothingPressed();
            }
        } else if (event.button === 2) { // Right click
            if (intersectsWithLms.length <= 0 &&
                intersectsWithMesh.length > 0 &&
                this.model.isEditingOn()
            ) {
                this.model.landmarks().deselectAll();
                this.model.landmarks().resetNextAvailable();
                beingEditedLandmark = undefined;
                meshPressed();
            } else {
                nothingPressed();
            }
        }
    };

    // Drag Handlers
    // ------------------------------------------------------------------------

    var landmarkOnDrag = atomic.atomicOperation((event) => {
        console.log("drag");
        // note that positionLmDrag is set to where we started.
        // update where we are now and where we were
        var newPositionLmDrag = new THREE.Vector2(
            event.clientX, event.clientY);
        var prevPositionLmDrag = positionLmDrag.clone();
        // change in this step in screen space
        deltaLmDrag.subVectors(newPositionLmDrag, prevPositionLmDrag);
        // update the position
        positionLmDrag.copy(newPositionLmDrag);
        var selectedLandmarks = this.model.landmarks().selected();
        var lm, vScreen;
        for (var i = 0; i < selectedLandmarks.length; i++) {
            lm = selectedLandmarks[i];
            // convert to screen coordinates
            vScreen = this.localToScreen(lm.point());

            // budge the screen coordinate
            vScreen.add(deltaLmDrag);

            // use the standard machinery to find intersections
            // note that we intersect the mesh to use the octree
            intersectsWithMesh = this.getIntersects(vScreen.x,
                vScreen.y, this.mesh);
            if (intersectsWithMesh.length > 0) {
                // good, we're still on the mesh.
                lm.setPoint(this.worldToLocal(intersectsWithMesh[0].point));
            } else {
                // don't update point - it would fall off the surface.
                console.log("fallen off mesh");
            }
        }
    });

    var shiftOnDrag = (event) => {
        console.log("shift:drag");
        // note - we use client as we don't want to jump back to zero
        // if user drags into sidebar!
        var newX = event.clientX;
        var newY = event.clientY;
        // clear the canvas and draw a selection rect.
        this.clearCanvas();
        var x = onMouseDownPosition.x;
        var y = onMouseDownPosition.y;
        var dx = newX - x;
        var dy = newY - y;
        this.ctx.strokeRect(x, y, dx, dy);
    };

    // Up handlers
    // ------------------------------------------------------------------------

    var shiftOnMouseUp = atomic.atomicOperation((event) => {
        this.cameraController.enable();
        console.log("shift:up");
        $(document).off('mousemove.shiftDrag', shiftOnDrag);
        var x1 = onMouseDownPosition.x;
        var y1 = onMouseDownPosition.y;
        var x2 = event.clientX;
        var y2 = event.clientY;
        var min_x, max_x, min_y, max_y;
        if (x1 < x2) {
            min_x = x1;
            max_x = x2;
        } else {
            min_x = x2;
            max_x = x1;
        }
        if (y1 < y2) {
            min_y = y1;
            max_y = y2;
        } else {
            min_y = y2;
            max_y = y1;
        }
        // First, let's just find all the landmarks in screen space that
        // are within our selection.
        var lms = this.lmViewsInSelectionBox(min_x, min_y,
                                             max_x, max_y);

        // Of these, filter out the ones which are visible (not
        // obscured) and select the rest
        _.each(lms, (lm) => {
            if (this.lmViewVisible(lm)) {
                lm.model.select();
            }
        });

        this.clearCanvas();
        isPressed = false;
        hasGroupSelection = true;
    });

    var meshOnMouseUp = (event) => {
        console.log("meshPress:up");
        var p;
        onMouseUpPosition.set(event.clientX, event.clientY);
        if (onMouseDownPosition.distanceTo(onMouseUpPosition) < 2) {
            //  a click on the mesh
            p = intersectsWithMesh[0].point.clone();
            // Convert the point back into the mesh space
            this.worldToLocal(p, true);

            if (this.model.isEditingOn() && beingEditedLandmark) {
                this.model.landmarks().setLmAt(beingEditedLandmark, p);
            } else {
                this.model.landmarks().insertNew(p);
            }
        }

        this.clearCanvas();
        isPressed = false;
        hasGroupSelection = false;
    };

    var nothingOnMouseUp = (event) => {
        console.log("nothingPress:up");
        onMouseUpPosition.set(event.clientX, event.clientY);
        if (onMouseDownPosition.distanceTo(onMouseUpPosition) < 2) {
            // a click on nothing - deselect all
            this.model.landmarks().deselectAll();
        }

        this.clearCanvas();
        isPressed = false;
        hasGroupSelection = false;
    };

    var landmarkOnMouseUp = atomic.atomicOperation((event) => {
        var ctrl = downEvent.ctrlKey || downEvent.metaKey;
        this.cameraController.enable();
        console.log("landmarkPress:up");
        $(document).off('mousemove.landmarkDrag');
        onMouseUpPosition.set(event.clientX, event.clientY);
        if (onMouseDownPosition.distanceTo(onMouseUpPosition) === 0) {
            // landmark was pressed
            if (lmPressedWasSelected && ctrl) {
                lmPressed.deselect();
            } else if (!ctrl) {
                lmPressed.selectAndDeselectRest();
            } else {
                hasGroupSelection = true;
            }
        }

        this.clearCanvas();
        isPressed = false;
    });

    // Move handlers
    // ------------------------------------------------------------------------

    var onMouseMove = (evt) => {
        this.clearCanvas();

        if (isPressed || !this.model.isEditingOn()) {
            return null;
        }

        var intersectsWithMesh =
            this.getIntersectsFromEvent(evt, this.mesh);

        var lmGroup = this.model.landmarks();

        var shouldUpdate = (intersectsWithMesh.length > 0 &&
                            lmGroup &&
                            lmGroup.landmarks);

        if (!shouldUpdate) {
            return null;
        }

        var mouseLoc = this.worldToLocal(intersectsWithMesh[0].point);

        var lms = findClosestLandmarks(lmGroup, mouseLoc,
                                       evt.ctrlKey || evt.metaKey);

        if (lms[0] && !evt.ctrlKey) {
            beingEditedLandmark = lms[0];
            lms = lms.slice(1, 4);
        } else if (lms[0]) {
            lms = lms.slice(0, 3);
        }

        if (beingEditedLandmark && !hasGroupSelection) {

            beingEditedLandmark.selectAndDeselectRest();

            this.drawTargetingLine(
                {x: evt.clientX, y: evt.clientY},
                this.localToScreen(beingEditedLandmark.point()));

            lms.forEach((lm) => {
                this.drawTargetingLine(
                    {x: evt.clientX, y: evt.clientY},
                    this.localToScreen(lm.point()), true);
            });
        }
    };

    return {
        // State management
        setGroupSelected: function (val=true) { hasGroupSelection = val; },

        // Exposed handlers
        onMouseDown: atomic.atomicOperation(onMouseDown),
        onMouseMove: _.throttle(atomic.atomicOperation(onMouseMove),
                                MOVE_THROTTLE)
    }

}

module.exports = MouseHandler;
<commit_msg>Be able to nudge selected landmark<commit_after>"use strict";

var _ = require('underscore');
var THREE = require('three');
var $ = require('jquery');

var atomic = require('../../model/atomic');

const MOVE_THROTTLE = 50;

/**
 * Create a closure for handling mouse events in viewport.
 * Holds state usable by all event handlers and should be bound to the
 * Viewport view instance.
 */
function MouseHandler () {

    // Helpers
    // ------------------------------------------------------------------------

    /**
     * Find the 4 landmarks closest to a location (THREE vector)
     * from a LandmarkGroup
     *
     * @param  {LandmarkGroup} lmGroup
     * @param  {THREE.Vector} loc
     *
     * @return {Landmark[]}
     */
    var findClosestLandmarks = (lmGroup, loc, locked=false) => {
        var dist, i, j,
            lm, lmLoc,
            minDist, minLm,
            dists = new Array(4), lms= new Array(4);

        for (i = lmGroup.landmarks.length - 1; i >= 0; i--) {
            lm = lmGroup.landmarks[i];
            lmLoc = lm.point();

            if (lmLoc === null || (locked && lm === beingEditedLandmark)) {
                continue;
            }

            dist = loc.distanceTo(lmLoc);

            // Compare to stored lm in order, 0 being the closest
            for (j = 0; j < 3; j++) {
                minDist = dists[j];
                if (!minDist) {
                    [dists[j], lms[j]] = [dist, lm];
                    break;
                } else if (dist <= minDist) { // leq to ensure we always have 4
                    dists.splice(j, 0, dist);
                    lms.splice(j, 0, lm);
                    break;
                }
            }
        }

        return lms;
    }

    // Setup handler state variables
    // ------------------------------------------------------------------------
    var downEvent,
        lmPressed, lmPressedWasSelected,
        isPressed, hasGroupSelection,
        beingEditedLandmark;

    // x, y position of mouse on click states
    var onMouseDownPosition = new THREE.Vector2(),
        onMouseUpPosition = new THREE.Vector2();

    // current screen position when in drag state
    var positionLmDrag = new THREE.Vector2();
    // vector difference in one time step
    var deltaLmDrag = new THREE.Vector2();

    var intersectsWithLms, intersectsWithMesh;

    // Press handling
    // ------------------------------------------------------------------------

    var meshPressed = () => {
        console.log('mesh pressed!');
        if (hasGroupSelection) {
            hasGroupSelection = false;
            nothingPressed();
        } else if (event.button === 0 && event.shiftKey) {
            shiftPressed();  // LMB + SHIFT
        } else {
            $(document).one('mouseup.viewportMesh', meshOnMouseUp);
        }
    }

    var landmarkPressed = () => {
        var ctrl = (downEvent.ctrlKey || downEvent.metaKey);
        console.log('Viewport: landmark pressed');
        // before anything else, disable the camera
        this.cameraController.disable();
        // the clicked on landmark
        var landmarkSymbol = intersectsWithLms[0].object;
        // hunt through the landmarkViews for the right symbol
        for (var i = 0; i < this.landmarkViews.length; i++) {
            if (this.landmarkViews[i].symbol === landmarkSymbol) {
                lmPressed = this.landmarkViews[i].model;
            }
        }
        console.log('Viewport: finding the selected points');
        lmPressedWasSelected = lmPressed.isSelected();

        if (!lmPressedWasSelected && !ctrl) {
            // this lm wasn't pressed before and we aren't holding
            // mutliselection down - deselect rest and select this
            console.log("normal click on a unselected lm - deselecting rest and selecting me");
            lmPressed.selectAndDeselectRest();
        } else if (ctrl && !lmPressedWasSelected) {
            lmPressed.select();
        }

        // record the position of where the drag started.
        positionLmDrag.copy(this.localToScreen(lmPressed.point()));
        // start listening for dragging landmarks
        $(document).on('mousemove.landmarkDrag', landmarkOnDrag);
        $(document).one(
            'mouseup.viewportLandmark', landmarkOnMouseUp);
    }

    var nothingPressed = () => {
        console.log('nothing pressed!');
        hasGroupSelection = false;
        $(document).one(
            'mouseup.viewportNothing', nothingOnMouseUp);
    }

    var shiftPressed = () => {
        console.log('shift pressed!');
        // before anything else, disable the camera
        hasGroupSelection = false;
        this.cameraController.disable();
        $(document).on('mousemove.shiftDrag', shiftOnDrag);
        $(document).one('mouseup.viewportShift', shiftOnMouseUp);
    }

    // Catch all clicks and delegate to other handlers once user's intent
    // has been figured out
    var onMouseDown = (event) => {
        event.preventDefault();
        this.$el.focus();

        isPressed = true;

        downEvent = event;
        onMouseDownPosition.set(event.clientX, event.clientY);

        // All interactions require intersections to distinguish
        intersectsWithLms = this.getIntersectsFromEvent(
            event, this.s_lms);
        // note that we explicitly ask for intersects with the mesh
        // object as we know get intersects will use an octree if
        // present.
        intersectsWithMesh = this.getIntersectsFromEvent(event, this.mesh);

        // Click type, we use MouseEvent.button which is the vanilla JS way
        // jQuery also exposes event.which which has different bindings
        if (event.button === 0) {  // left mouse button
            if (intersectsWithLms.length > 0 &&
                intersectsWithMesh.length > 0) {
                // degenerate case - which is closer?
                if (intersectsWithLms[0].distance <
                    intersectsWithMesh[0].distance) {
                    landmarkPressed(event);
                } else {
                    // the mesh was pressed. Check for shift first.
                    if (event.shiftKey) {
                        shiftPressed();
                    } else {
                        meshPressed();
                    }
                }
            } else if (intersectsWithLms.length > 0) {
                landmarkPressed(event);
            } else if (event.shiftKey) {
                // shift trumps all!
                shiftPressed();
            } else if (intersectsWithMesh.length > 0) {
                meshPressed();
            } else {
                nothingPressed();
            }
        } else if (event.button === 2) { // Right click
            if (intersectsWithLms.length <= 0 &&
                intersectsWithMesh.length > 0 &&
                this.model.isEditingOn()
            ) {
                this.model.landmarks().deselectAll();
                this.model.landmarks().resetNextAvailable();
                beingEditedLandmark = undefined;
                meshPressed();
            } else {
                nothingPressed();
            }
        }
    };

    // Drag Handlers
    // ------------------------------------------------------------------------

    var landmarkOnDrag = atomic.atomicOperation((event) => {
        console.log("drag");
        // note that positionLmDrag is set to where we started.
        // update where we are now and where we were
        var newPositionLmDrag = new THREE.Vector2(
            event.clientX, event.clientY);
        var prevPositionLmDrag = positionLmDrag.clone();
        // change in this step in screen space
        deltaLmDrag.subVectors(newPositionLmDrag, prevPositionLmDrag);
        // update the position
        positionLmDrag.copy(newPositionLmDrag);
        var selectedLandmarks = this.model.landmarks().selected();
        var lm, vScreen;
        for (var i = 0; i < selectedLandmarks.length; i++) {
            lm = selectedLandmarks[i];
            // convert to screen coordinates
            vScreen = this.localToScreen(lm.point());

            // budge the screen coordinate
            vScreen.add(deltaLmDrag);

            // use the standard machinery to find intersections
            // note that we intersect the mesh to use the octree
            intersectsWithMesh = this.getIntersects(vScreen.x,
                vScreen.y, this.mesh);
            if (intersectsWithMesh.length > 0) {
                // good, we're still on the mesh.
                lm.setPoint(this.worldToLocal(intersectsWithMesh[0].point));
            } else {
                // don't update point - it would fall off the surface.
                console.log("fallen off mesh");
            }
        }
    });

    var shiftOnDrag = (event) => {
        console.log("shift:drag");
        // note - we use client as we don't want to jump back to zero
        // if user drags into sidebar!
        var newX = event.clientX;
        var newY = event.clientY;
        // clear the canvas and draw a selection rect.
        this.clearCanvas();
        var x = onMouseDownPosition.x;
        var y = onMouseDownPosition.y;
        var dx = newX - x;
        var dy = newY - y;
        this.ctx.strokeRect(x, y, dx, dy);
    };

    // Up handlers
    // ------------------------------------------------------------------------

    var shiftOnMouseUp = atomic.atomicOperation((event) => {
        this.cameraController.enable();
        console.log("shift:up");
        $(document).off('mousemove.shiftDrag', shiftOnDrag);
        var x1 = onMouseDownPosition.x;
        var y1 = onMouseDownPosition.y;
        var x2 = event.clientX;
        var y2 = event.clientY;
        var min_x, max_x, min_y, max_y;
        if (x1 < x2) {
            min_x = x1;
            max_x = x2;
        } else {
            min_x = x2;
            max_x = x1;
        }
        if (y1 < y2) {
            min_y = y1;
            max_y = y2;
        } else {
            min_y = y2;
            max_y = y1;
        }
        // First, let's just find all the landmarks in screen space that
        // are within our selection.
        var lms = this.lmViewsInSelectionBox(min_x, min_y,
                                             max_x, max_y);

        // Of these, filter out the ones which are visible (not
        // obscured) and select the rest
        _.each(lms, (lm) => {
            if (this.lmViewVisible(lm)) {
                lm.model.select();
            }
        });

        this.clearCanvas();
        isPressed = false;
        hasGroupSelection = true;
    });

    var meshOnMouseUp = (event) => {
        console.log("meshPress:up");
        var p;
        onMouseUpPosition.set(event.clientX, event.clientY);
        if (onMouseDownPosition.distanceTo(onMouseUpPosition) < 2) {
            //  a click on the mesh
            p = intersectsWithMesh[0].point.clone();
            // Convert the point back into the mesh space
            this.worldToLocal(p, true);

            if (this.model.isEditingOn() && beingEditedLandmark) {
                this.model.landmarks().setLmAt(beingEditedLandmark, p);
            } else {
                this.model.landmarks().insertNew(p);
            }
        }

        this.clearCanvas();
        isPressed = false;
        hasGroupSelection = false;
    };

    var nothingOnMouseUp = (event) => {
        console.log("nothingPress:up");
        onMouseUpPosition.set(event.clientX, event.clientY);
        if (onMouseDownPosition.distanceTo(onMouseUpPosition) < 2) {
            // a click on nothing - deselect all
            this.model.landmarks().deselectAll();
        }

        this.clearCanvas();
        isPressed = false;
        hasGroupSelection = false;
    };

    var landmarkOnMouseUp = atomic.atomicOperation((event) => {
        var ctrl = downEvent.ctrlKey || downEvent.metaKey;
        this.cameraController.enable();
        console.log("landmarkPress:up");
        $(document).off('mousemove.landmarkDrag');
        onMouseUpPosition.set(event.clientX, event.clientY);
        if (onMouseDownPosition.distanceTo(onMouseUpPosition) === 0) {
            // landmark was pressed
            if (lmPressedWasSelected && ctrl) {
                lmPressed.deselect();
            } else if (!ctrl && !lmPressedWasSelected) {
                lmPressed.selectAndDeselectRest();
            } else if (lmPressedWasSelected) {
                var p = intersectsWithMesh[0].point.clone();
                this.worldToLocal(p, true);
                this.model.landmarks().setLmAt(lmPressed, p);
            } else if (ctrl) {
                hasGroupSelection = true;
            }
        }

        this.clearCanvas();
        isPressed = false;
    });

    // Move handlers
    // ------------------------------------------------------------------------

    var onMouseMove = (evt) => {
        this.clearCanvas();

        if (isPressed || !this.model.isEditingOn()) {
            return null;
        }

        var intersectsWithMesh =
            this.getIntersectsFromEvent(evt, this.mesh);

        var lmGroup = this.model.landmarks();

        var shouldUpdate = (intersectsWithMesh.length > 0 &&
                            lmGroup &&
                            lmGroup.landmarks);

        if (!shouldUpdate) {
            return null;
        }

        var mouseLoc = this.worldToLocal(intersectsWithMesh[0].point);

        var lms = findClosestLandmarks(lmGroup, mouseLoc,
                                       evt.ctrlKey || evt.metaKey);

        if (lms[0] && !evt.ctrlKey) {
            beingEditedLandmark = lms[0];
            lms = lms.slice(1, 4);
        } else if (lms[0]) {
            lms = lms.slice(0, 3);
        }

        if (beingEditedLandmark && !hasGroupSelection) {

            beingEditedLandmark.selectAndDeselectRest();

            this.drawTargetingLine(
                {x: evt.clientX, y: evt.clientY},
                this.localToScreen(beingEditedLandmark.point()));

            lms.forEach((lm) => {
                this.drawTargetingLine(
                    {x: evt.clientX, y: evt.clientY},
                    this.localToScreen(lm.point()), true);
            });
        }
    };

    return {
        // State management
        setGroupSelected: function (val=true) { hasGroupSelection = val; },

        // Exposed handlers
        onMouseDown: atomic.atomicOperation(onMouseDown),
        onMouseMove: _.throttle(atomic.atomicOperation(onMouseMove),
                                MOVE_THROTTLE)
    }

}

module.exports = MouseHandler;
<|endoftext|>


<commit_before>const fs = require('fs');
const path = require('path');
const minimist = require('minimist');
const { parseYAML } = require('../format');

const help = (ui, code = 0) => {
    const { bold } = ui.colors;

    ui.print(`
${bold('NAME')}
        comfy setall - Replace the configuration for a given environment

${bold('SYNOPSIS')}
        ${bold('comfy')} setall <environment> <path> [<options>]

${bold('OPTIONS')}
        <environment>     Name of the environment (must already exist in project)
        <path>            Path to a configuration file (accepts json and yml formats)
        -t, --tag=<tag>   Set a tag for this config version (default: stable)
        -h, --help        Show this very help message

${bold('EXAMPLES')}
        comfy setall development /config/comfy.json
        comfy setall production /config/api.yml -t next
`);
    ui.exit(code);
};

module.exports = (ui, modules) => function* (rawOptions) {
    const { red, bold } = ui.colors;
    const options = minimist(rawOptions);
    const env = options._[0];
    const configPath = options._[1];
    const tag = options.tag || options.t || 'stable';

    if (options.help || options.h || options._.includes('help')) {
        help(ui);
        return ui.exit(0);
    }

    if (!env) {
        ui.error(red('No environment specified.'));
        help(ui, 1);
    }

    if (!configPath) {
        ui.error(red('No config file specified.'));
        help(ui, 1);
    }

    const filename = path.normalize(`${process.cwd()}${path.sep}${configPath}`);

    if (!fs.existsSync(filename)) {
        ui.error(`The file ${red(options.f)} doesn't exist.`);
        ui.exit(1);
    }

    const stats = fs.statSync(filename);
    if (!stats.isFile()) {
        ui.error(`The object located at ${red(options.f)} is not a file.`);
        ui.exit(1);
    }

    const file = fs.readFileSync(filename, 'utf-8');

    let parsedContent;

    try {
        parsedContent = parseYAML(file);
    } catch (err) {
        ui.error(red(`Failed to parse ${options.f}`));
    }

    const project = yield modules.project.retrieveFromConfig();
    yield modules.config.add(project, env, parsedContent, {
        tag,
        configName: 'default',
    });

    ui.print(`${bold('Great!')} Your configuration was successfully saved.`);
    ui.exit();
};
<commit_msg>Show that path can be relative<commit_after>const fs = require('fs');
const path = require('path');
const minimist = require('minimist');
const { parseYAML } = require('../format');

const help = (ui, code = 0) => {
    const { bold } = ui.colors;

    ui.print(`
${bold('NAME')}
        comfy setall - Replace the configuration for a given environment

${bold('SYNOPSIS')}
        ${bold('comfy')} setall <environment> <path> [<options>]

${bold('OPTIONS')}
        <environment>     Name of the environment (must already exist in project)
        <path>            Path to a configuration file (accepts json and yml formats)
        -t, --tag=<tag>   Set a tag for this config version (default: stable)
        -h, --help        Show this very help message

${bold('EXAMPLES')}
        comfy setall development config/comfy.json
        comfy setall production config/api.yml -t next
`);
    ui.exit(code);
};

module.exports = (ui, modules) => function* (rawOptions) {
    const { red, green, bold } = ui.colors;
    const options = minimist(rawOptions);
    const env = options._[0];
    const configPath = options._[1];
    const tag = options.tag || options.t || 'stable';

    if (options.help || options.h || options._.includes('help')) {
        help(ui);
        return ui.exit(0);
    }

    if (!env) {
        ui.error(red('No environment specified.'));
        help(ui, 1);
    }

    if (!configPath) {
        ui.error(red('No config file specified.'));
        help(ui, 1);
    }

    const filename = path.normalize(`${process.cwd()}${path.sep}${configPath}`);

    if (!fs.existsSync(filename)) {
        ui.error(`The file ${red(options.f)} doesn't exist.`);
        ui.exit(1);
    }

    const stats = fs.statSync(filename);
    if (!stats.isFile()) {
        ui.error(`The object located at ${red(options.f)} is not a file.`);
        ui.exit(1);
    }

    const file = fs.readFileSync(filename, 'utf-8');

    let parsedContent;

    try {
        parsedContent = parseYAML(file);
    } catch (err) {
        ui.error(red(`Failed to parse ${options.f}`));
    }

    const project = yield modules.project.retrieveFromConfig();
    yield modules.config.add(project, env, parsedContent, {
        tag,
        configName: 'default',
    });

    ui.print(`${bold(green('comfy configuration successfully saved'))}`);
    ui.exit();
};
<|endoftext|>


<commit_before>/**
 * Service for sending network requests.
 */

var xhr = require('./lib/xhr');
var jsonp = require('./lib/jsonp');
var Promise = require('./lib/promise');
var Transforms = require('./lib/transforms');

module.exports = function (_) {

    var originUrl = _.url.parse(location.href);
    var jsonType = {'Content-Type': 'application/json;charset=utf-8'};

    function Http(url, options) {
        var promise;

        if (_.isPlainObject(url)) {
            options = url;
            url = '';
        }

        options = _.extend({url: url}, options);
        options = _.extend(true, {},
            Http.options, this.options, options
        );

        if (options.crossOrigin === null) {
            options.crossOrigin = crossOrigin(options.url);
        }

        options.method = options.method.toUpperCase();
        options.headers = _.extend({}, Http.headers.common,
            !options.crossOrigin ? Http.headers.custom : {},
            Http.headers[options.method.toLowerCase()],
            options.headers
        );

        if (_.isPlainObject(options.data) && /^(GET|JSONP)$/i.test(options.method)) {
            _.extend(options.params, options.data);
            delete options.data;
        }

        if (options.emulateHTTP && !options.crossOrigin && /^(PUT|PATCH|DELETE)$/i.test(options.method)) {
            options.headers['X-HTTP-Method-Override'] = options.method;
            options.method = 'POST';
        }

        if (options.emulateJSON && _.isPlainObject(options.data)) {
            options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
            options.data = _.url.params(options.data);
        }

        options = transform(Http.transforms.request, options.transformRequest, options, this.vm);
        promise = (options.method == 'JSONP' ? jsonp : xhr).call(this.vm, _, options);
        promise = extendPromise(promise.then(function (response) {

            transform(Http.transforms.response, options.transformResponse, response, this);
            return response.reject ? Promise.reject(response) : response;

        }.bind(this.vm)), this.vm);

        if (options.success) {
            promise = promise.success(options.success);
        }

        if (options.error) {
            promise = promise.error(options.error);
        }

        return promise;
    }

    function extendPromise(promise, vm) {

        promise.success = function (fn) {

            return extendPromise(promise.then(function (response) {
                return fn.call(vm, response.data, response.status, response) || response;
            }), vm);

        };

        promise.error = function (fn) {

            return extendPromise(promise.then(undefined, function (response) {
                return fn.call(vm, response.data, response.status, response) || response;
            }), vm);

        };

        promise.always = function (fn) {

            var cb = function (response) {
                return fn.call(vm, response.data, response.status, response) || response;
            };

            return extendPromise(promise.then(cb, cb), vm);
        };

        return promise;
    }

    function transform(transforms, custom, arg, vm) {

        if (_.isArray(custom)) {
            transforms = custom;
        } else if (custom) {
            transforms = transforms.concat([custom]);
        }

        return transforms.reduce(function (arg, transform) {
            return transform.call(vm, arg);
        }, arg);
    }

    function crossOrigin(url) {

        var requestUrl = _.url.parse(url);

        return (requestUrl.protocol !== originUrl.protocol || requestUrl.host !== originUrl.host);
    }

    Http.transforms = Transforms.call(this, _);

    Http.options = {
        method: 'get',
        params: {},
        data: '',
        xhr: null,
        jsonp: 'callback',
        beforeSend: null,
        crossOrigin: null,
        emulateHTTP: false,
        emulateJSON: false,
        timeout: 0,
        transformRequest: null,
        transformResponse: null
    };

    Http.headers = {
        put: jsonType,
        post: jsonType,
        patch: jsonType,
        delete: jsonType,
        common: {'Accept': 'application/json, text/plain, */*'},
        custom: {'X-Requested-With': 'XMLHttpRequest'}
    };

    ['get', 'put', 'post', 'patch', 'delete', 'jsonp'].forEach(function (method) {

        Http[method] = function (url, data, success, options) {

            if (_.isFunction(data)) {
                options = success;
                success = data;
                data = undefined;
            }

            return this(url, _.extend({method: method, data: data, success: success}, options));
        };
    });

    return _.http = Http;
};
<commit_msg>transforms no longer async<commit_after>/**
 * Service for sending network requests.
 */

var xhr = require('./lib/xhr');
var jsonp = require('./lib/jsonp');
var Promise = require('./lib/promise');
var Transforms = require('./lib/transforms');

module.exports = function (_) {

    var originUrl = _.url.parse(location.href);
    var jsonType = {'Content-Type': 'application/json;charset=utf-8'};

    function Http(url, options) {
        var promise;

        if (_.isPlainObject(url)) {
            options = url;
            url = '';
        }

        options = _.extend({url: url}, options);
        options = _.extend(true, {},
            Http.options, this.options, options
        );

        if (options.crossOrigin === null) {
            options.crossOrigin = crossOrigin(options.url);
        }

        options.method = options.method.toUpperCase();
        options.headers = _.extend({}, Http.headers.common,
            !options.crossOrigin ? Http.headers.custom : {},
            Http.headers[options.method.toLowerCase()],
            options.headers
        );

        if (_.isPlainObject(options.data) && /^(GET|JSONP)$/i.test(options.method)) {
            _.extend(options.params, options.data);
            delete options.data;
        }

        if (options.emulateHTTP && !options.crossOrigin && /^(PUT|PATCH|DELETE)$/i.test(options.method)) {
            options.headers['X-HTTP-Method-Override'] = options.method;
            options.method = 'POST';
        }

        if (options.emulateJSON && _.isPlainObject(options.data)) {
            options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
            options.data = _.url.params(options.data);
        }

        options = transform(Http.transforms.request, options.transformRequest, options, this.vm);
        promise = (options.method == 'JSONP' ? jsonp : xhr).call(this.vm, _, options);
        promise = extendPromise(promise.then(function (response) {

            response = transform(Http.transforms.response, options.transformResponse, response, this);
            return response.reject ? Promise.reject(response) : response;

        }.bind(this.vm)), this.vm);

        if (options.success) {
            promise = promise.success(options.success);
        }

        if (options.error) {
            promise = promise.error(options.error);
        }

        return promise;
    }

    function extendPromise(promise, vm) {

        promise.success = function (fn) {

            return extendPromise(promise.then(function (response) {
                return fn.call(vm, response.data, response.status, response) || response;
            }), vm);

        };

        promise.error = function (fn) {

            return extendPromise(promise.then(undefined, function (response) {
                return fn.call(vm, response.data, response.status, response) || response;
            }), vm);

        };

        promise.always = function (fn) {

            var cb = function (response) {
                return fn.call(vm, response.data, response.status, response) || response;
            };

            return extendPromise(promise.then(cb, cb), vm);
        };

        return promise;
    }

    function transform(transforms, custom, arg, vm) {

        if (_.isArray(custom)) {
            transforms = custom;
        } else if (custom) {
            transforms = transforms.concat([custom]);
        }

        return transforms.reduce(function (arg, transform) {
            return transform.call(vm, arg);
        }, arg);
    }

    function crossOrigin(url) {

        var requestUrl = _.url.parse(url);

        return (requestUrl.protocol !== originUrl.protocol || requestUrl.host !== originUrl.host);
    }

    Http.transforms = Transforms.call(this, _);

    Http.options = {
        method: 'get',
        params: {},
        data: '',
        xhr: null,
        jsonp: 'callback',
        beforeSend: null,
        crossOrigin: null,
        emulateHTTP: false,
        emulateJSON: false,
        timeout: 0,
        transformRequest: null,
        transformResponse: null
    };

    Http.headers = {
        put: jsonType,
        post: jsonType,
        patch: jsonType,
        delete: jsonType,
        common: {'Accept': 'application/json, text/plain, */*'},
        custom: {'X-Requested-With': 'XMLHttpRequest'}
    };

    ['get', 'put', 'post', 'patch', 'delete', 'jsonp'].forEach(function (method) {

        Http[method] = function (url, data, success, options) {

            if (_.isFunction(data)) {
                options = success;
                success = data;
                data = undefined;
            }

            return this(url, _.extend({method: method, data: data, success: success}, options));
        };
    });

    return _.http = Http;
};
<|endoftext|>