<commit_before>import config from '../../setup/config';
export default class NetworkService {

    constructor(game, $container) {
        this.game = game;
        this.$container = $container;
        this.objects = $container.ObjectsService;
        this.playerFactory = $container.PlayerFactory;
        this.teamManager = $container.TeamManager;
        this.authService = $container.AuthService;
        this.socket = io();

        this.registerEvents();
    }

    registerEvents() {
        this.registerEvent('RoomJoinEvent', this.onRoomJoin);
        this.registerEvent('PlayerPositionEvent', this.onPlayerPosition);
        this.registerEvent('PlayerShootEvent', this.onPlayerShoot);
        this.registerEvent('TreasureChestStatusEvent', this.onTreasureChestStatus);
        this.registerEvent('PlayerUpdateWeaponEvent', this.onPlayerUpdateWeapon);
        this.registerEvent('PlayerAlienEvent', this.onPlayerAlien);
        this.registerEvent('RemoveFlagFromStatusBar', this.onRemoveFlagFromStatusBar);
        this.registerEvent('PlayerDamageEvent', this.onPlayerDamage);
        this.registerEvent('AskForExp', this.answerWithExp);
        this.registerEvent('AnswerWithExp', this.onAnswerWithExp);
        this.registerEvent('FlagCollected', this.onFlagCollected);

        eventSystem().on('bullet.shoot', (payload) => {
            this.objects.get('bulletGroup').add(payload.bullet);
        });

        eventSystem().on('treasureChest.newStatus', (payload) => {
            if (payload.source == "network") {
                return;
            }
            this.sendTreasureChestStatus(payload);
        });

        eventSystem().on('player.updateWeapon', (payload) => {
            if (payload.source == "network") {
                return;
            }
            this.sendPlayerUpdateWeapon(payload);
        });

        eventSystem().on('player.alien', (payload) => {
            this.sendPlayerAlien(payload);
        });

        eventSystem().on('player_dead', (payload) => {
            this.objects.get('points.' + payload.team).setText(this.teamManager.teams[payload.team].points + '/' + this.teamManager.maxPoints);
        });

        eventSystem().on('player.change_direction:after', (payload) => {
            console.log(' ' + payload.source);
            if (payload.source == "network") {
                return;
            }

            this.sendPosition(payload.player);
        });

        eventSystem().on('player.stop_moving:after', (payload) => {
            if (payload.source == "network") {
                return;
            }

            this.sendPosition(payload.player);
        });

        eventSystem().on('flag.getscollected', (payload) => {
            if (payload.source == "network") {
                return;
            }
            this.sendFlagCollected(payload);
        });

        eventSystem().on('player.exp', payload => {
            const player = payload.player;
            const hero = this.objects.get('hero');

            if (player !== hero) {
                return;
            }

            if (!this.authService.isLoggedIn()) {
                return;
            }

            this.sendExp(hero);
            this.answerWithExp();
        });

        eventSystem().on('stat.entry', payload => {
            const player = payload.player;
            const hero = this.objects.get('hero');

            if (player !== hero) {
                return;
            }

            this.statEntry(payload.key, payload.team, payload.options);
        });

        eventSystem().on('login', () => {
            this.getExp();
        });
    }

    sendFlagCollected(flag) {
        const payload = {
            flag: flag
        };

        this.broadcast('FlagCollected', payload);
    }

    connect() {
        const data = {};
        const targetRoomId = this.targetRoomId();

        if (targetRoomId) {
            data.targetRoomId = targetRoomId;

            console.log('Trying to join target room with id ' + targetRoomId);
        }

        this.emit('PlayerConnectEvent', data);
    }

    /* Convenience Functions */
    emit(event, data) {
        this.socket.emit(event, data);
    }

    broadcast(event, data) {
        const payload = {
            event: event,
            data: data
        };

        this.socket.emit('broadcast', payload);
    }

    registerEvent(event, callback) {
        this.socket.on(event, callback.bind(this));
    }

    /* Convenience Functions */

    /* Send Functions */
    sendPosition(player) {
        const payload = {
            player: player.number,
            x: player.x,
            y: player.y,
            direction: player.direction,
            moving: player.isMoving()
        };

        this.broadcast('PlayerPositionEvent', payload);
    }

    sendShoot(player) {
        const payload = {
            player: player.number,
            direction: player.direction
        };

        console.log('sendShoot', payload);
        this.broadcast('PlayerShootEvent', payload);
    }

    sendremoveFlagFromStatusBar(payload) {
        this.broadcast('RemoveFlagFromStatusBar', payload);
    }

    sendTreasureChestStatus(payload) {
        this.broadcast('TreasureChestStatusEvent', payload);
    }

    sendPlayerUpdateWeapon(payload) {
        this.broadcast('PlayerUpdateWeaponEvent', payload);
    }

    sendPlayerAlien(payload) {
        this.broadcast('PlayerAlienEvent', payload);
    }

    sendDamage(player, damage) {
        const payload = {
            player: player.number,
            health: player.health,
            damage: damage
        };

        console.log('sendDamage', payload);
        this.broadcast('PlayerDamageEvent', payload);
    }

    sendExp(player) {
        const token = this.authService.token();
        const payload = {
            token: token,
            exp: player.exp
        };

        console.log('sendExp', payload);
        this.emit('exp:set', payload);
    }

    getExp() {
        if (!this.authService.isLoggedIn()) {
            return;
        }

        const token = this.authService.token();

        this.socket.emit('exp:get', {token: token}, exp => {
            const hero = this.objects.get('hero');

            hero.setExp(exp);
            this.answerWithExp();
        });
    }

    askForExp() {
        this.broadcast('AskForExp');
    }

    answerWithExp() {
        const hero = this.objects.get('hero');

        this.broadcast('AnswerWithExp', {
            player: hero.number,
            exp: hero.exp
        });
    }

    statEntry(key, team, options) {
        this.socket.emit('stat-entry', {
            key: key,
            team: team,
            options: options
        });
    }

    /* Send Functions */

    /* Receive Functions */
    onRoomJoin(event) {
        const networkPlayer = {
            id: event.playerId,
            slot: event.slot
        };

        eventSystem().emit('network.handshake:before', networkPlayer);

        const player = this.teamManager.allPlayers()[networkPlayer.slot];
        this.teamManager.hero = player;

        eventSystem().emit('network.handshake:after', {
            hero: player
        });

        const roomId = event.roomId;
        const targetRoomId = this.targetRoomId();

        console.log('Joined room ' + roomId);

        if (roomId !== targetRoomId) {
            console.log('Given room id ' + roomId + ' does not match ' + targetRoomId + ', target room seems to be full or closed.');
            toastr.warning('You have joined a different room', 'Target room was full or has been closed');
        }

        const newUrl = window.location.origin + window.location.pathname + '?room=' + roomId;

        window.history.pushState({path: newUrl}, '', newUrl);
        this.objects.get('points.red').setText(event.redTickets + '/300');
        this.objects.get('points.blue').setText(event.blueTickets + '/300');
        this.getExp();
        this.askForExp();
    }

    onFlagCollected(event) {
        var flagObject = this.objects.get('flags.' + event.flag);
        flagObject.collected = true;
        flagObject.visible = false;

        const player = this.teamManager.findPlayer(event.player);
        player.getFlag(this);
    }

    onPlayerPosition(event) {
        console.log('onPlayerPosition', event);

        const player = this.teamManager.findPlayer(event.player);

        if (!player) {
            console.warn('Player number ' + event.player + ' not found yet!');
            return;
        }

        if (player.number == this.teamManager.hero.number) {
            console.error('Reposition the hero. ', player.number, this.teamManager.hero.number);
            return;
        }

        player.x = event.x;
        player.y = event.y;

        if (event.moving) {
            player.moveToDirection(event.direction, "network");
        }
        else
            player.stopMoving(event.direction, "network");

    }

    onPlayerShoot(event) {
        const player = this.teamManager.findPlayer(event.player);

        if (!player) {
            console.warn('Player number ' + event.player + ' not found yet!');
            return;
        }

        player.shoot(event.direction);
    }

    onTreasureChestStatus(payload) {
        this.objects.get('treasureChest.' + payload.id).setStatus(payload.newStatus);
    }

    onPlayerUpdateWeapon(payload) {
        const player = this.teamManager.findPlayer(payload.player);
        player.weapon.updateWeapon(payload.newWeapon);
    }

    onPlayerAlien(payload) {
        const player = this.teamManager.findPlayer(payload.player);
        player.getAlien();
    }

    onRemoveFlagFromStatusBar(payload) {
        console.log("onRemoveFlagFromStatusBar", payload);
        this.objects.get(payload.flag).visible = false;
        if (payload.gameOver) {
            this.game.input.enabled = false;
            this.game.physics.arcade.isPaused = true;
            this.objects.get('gameOverText').setText('Game Over!\n ' + payload.color + ' wins the game!');
            this.objects.get('gameOverText').visible = true;
            this.game.time.events.add(Phaser.Timer.SECOND * config.game.reloadPage.wait, this.reloadPage, this);
        }
    }

    reloadPage() {
        window.location.reload();
    }

    onPlayerDamage(event) {
        const player = this.teamManager.findPlayer(event.player);

        player.setHealth(event.health);
    }

    onAnswerWithExp(event) {
        const player = this.teamManager.findPlayer(event.player);

        player.setExp(event.exp);
    }

    targetRoomId() {
        const search = window.location.search;
        const searchSplit = search.split('=');

        if (searchSplit.length !== 2) {
            return null;
        }

        return searchSplit[1];
    }

}
<commit_msg>Only show target room toast if target room id was specified<commit_after>import config from '../../setup/config';
export default class NetworkService {

    constructor(game, $container) {
        this.game = game;
        this.$container = $container;
        this.objects = $container.ObjectsService;
        this.playerFactory = $container.PlayerFactory;
        this.teamManager = $container.TeamManager;
        this.authService = $container.AuthService;
        this.socket = io();

        this.registerEvents();
    }

    registerEvents() {
        this.registerEvent('RoomJoinEvent', this.onRoomJoin);
        this.registerEvent('PlayerPositionEvent', this.onPlayerPosition);
        this.registerEvent('PlayerShootEvent', this.onPlayerShoot);
        this.registerEvent('TreasureChestStatusEvent', this.onTreasureChestStatus);
        this.registerEvent('PlayerUpdateWeaponEvent', this.onPlayerUpdateWeapon);
        this.registerEvent('PlayerAlienEvent', this.onPlayerAlien);
        this.registerEvent('RemoveFlagFromStatusBar', this.onRemoveFlagFromStatusBar);
        this.registerEvent('PlayerDamageEvent', this.onPlayerDamage);
        this.registerEvent('AskForExp', this.answerWithExp);
        this.registerEvent('AnswerWithExp', this.onAnswerWithExp);
        this.registerEvent('FlagCollected', this.onFlagCollected);

        eventSystem().on('bullet.shoot', (payload) => {
            this.objects.get('bulletGroup').add(payload.bullet);
        });

        eventSystem().on('treasureChest.newStatus', (payload) => {
            if (payload.source == "network") {
                return;
            }
            this.sendTreasureChestStatus(payload);
        });

        eventSystem().on('player.updateWeapon', (payload) => {
            if (payload.source == "network") {
                return;
            }
            this.sendPlayerUpdateWeapon(payload);
        });

        eventSystem().on('player.alien', (payload) => {
            this.sendPlayerAlien(payload);
        });

        eventSystem().on('player_dead', (payload) => {
            this.objects.get('points.' + payload.team).setText(this.teamManager.teams[payload.team].points + '/' + this.teamManager.maxPoints);
        });

        eventSystem().on('player.change_direction:after', (payload) => {
            console.log(' ' + payload.source);
            if (payload.source == "network") {
                return;
            }

            this.sendPosition(payload.player);
        });

        eventSystem().on('player.stop_moving:after', (payload) => {
            if (payload.source == "network") {
                return;
            }

            this.sendPosition(payload.player);
        });

        eventSystem().on('flag.getscollected', (payload) => {
            if (payload.source == "network") {
                return;
            }
            this.sendFlagCollected(payload);
        });

        eventSystem().on('player.exp', payload => {
            const player = payload.player;
            const hero = this.objects.get('hero');

            if (player !== hero) {
                return;
            }

            if (!this.authService.isLoggedIn()) {
                return;
            }

            this.sendExp(hero);
            this.answerWithExp();
        });

        eventSystem().on('stat.entry', payload => {
            const player = payload.player;
            const hero = this.objects.get('hero');

            if (player !== hero) {
                return;
            }

            this.statEntry(payload.key, payload.team, payload.options);
        });

        eventSystem().on('login', () => {
            this.getExp();
        });
    }

    sendFlagCollected(flag) {
        const payload = {
            flag: flag
        };

        this.broadcast('FlagCollected', payload);
    }

    connect() {
        const data = {};
        const targetRoomId = this.targetRoomId();

        if (targetRoomId) {
            data.targetRoomId = targetRoomId;

            console.log('Trying to join target room with id ' + targetRoomId);
        }

        this.emit('PlayerConnectEvent', data);
    }

    /* Convenience Functions */
    emit(event, data) {
        this.socket.emit(event, data);
    }

    broadcast(event, data) {
        const payload = {
            event: event,
            data: data
        };

        this.socket.emit('broadcast', payload);
    }

    registerEvent(event, callback) {
        this.socket.on(event, callback.bind(this));
    }

    /* Convenience Functions */

    /* Send Functions */
    sendPosition(player) {
        const payload = {
            player: player.number,
            x: player.x,
            y: player.y,
            direction: player.direction,
            moving: player.isMoving()
        };

        this.broadcast('PlayerPositionEvent', payload);
    }

    sendShoot(player) {
        const payload = {
            player: player.number,
            direction: player.direction
        };

        console.log('sendShoot', payload);
        this.broadcast('PlayerShootEvent', payload);
    }

    sendremoveFlagFromStatusBar(payload) {
        this.broadcast('RemoveFlagFromStatusBar', payload);
    }

    sendTreasureChestStatus(payload) {
        this.broadcast('TreasureChestStatusEvent', payload);
    }

    sendPlayerUpdateWeapon(payload) {
        this.broadcast('PlayerUpdateWeaponEvent', payload);
    }

    sendPlayerAlien(payload) {
        this.broadcast('PlayerAlienEvent', payload);
    }

    sendDamage(player, damage) {
        const payload = {
            player: player.number,
            health: player.health,
            damage: damage
        };

        console.log('sendDamage', payload);
        this.broadcast('PlayerDamageEvent', payload);
    }

    sendExp(player) {
        const token = this.authService.token();
        const payload = {
            token: token,
            exp: player.exp
        };

        console.log('sendExp', payload);
        this.emit('exp:set', payload);
    }

    getExp() {
        if (!this.authService.isLoggedIn()) {
            return;
        }

        const token = this.authService.token();

        this.socket.emit('exp:get', {token: token}, exp => {
            const hero = this.objects.get('hero');

            hero.setExp(exp);
            this.answerWithExp();
        });
    }

    askForExp() {
        this.broadcast('AskForExp');
    }

    answerWithExp() {
        const hero = this.objects.get('hero');

        this.broadcast('AnswerWithExp', {
            player: hero.number,
            exp: hero.exp
        });
    }

    statEntry(key, team, options) {
        this.socket.emit('stat-entry', {
            key: key,
            team: team,
            options: options
        });
    }

    /* Send Functions */

    /* Receive Functions */
    onRoomJoin(event) {
        const networkPlayer = {
            id: event.playerId,
            slot: event.slot
        };

        eventSystem().emit('network.handshake:before', networkPlayer);

        const player = this.teamManager.allPlayers()[networkPlayer.slot];
        this.teamManager.hero = player;

        eventSystem().emit('network.handshake:after', {
            hero: player
        });

        const roomId = event.roomId;
        const targetRoomId = this.targetRoomId();

        console.log('Joined room ' + roomId);

        if (targetRoomId && roomId !== targetRoomId) {
            console.log('Given room id ' + roomId + ' does not match ' + targetRoomId + ', target room seems to be full or closed.');
            toastr.warning('You have joined a different room', 'Target room was full or has been closed');
        }

        const newUrl = window.location.origin + window.location.pathname + '?room=' + roomId;

        window.history.pushState({path: newUrl}, '', newUrl);
        this.objects.get('points.red').setText(event.redTickets + '/300');
        this.objects.get('points.blue').setText(event.blueTickets + '/300');
        this.getExp();
        this.askForExp();
    }

    onFlagCollected(event) {
        var flagObject = this.objects.get('flags.' + event.flag);
        flagObject.collected = true;
        flagObject.visible = false;

        const player = this.teamManager.findPlayer(event.player);
        player.getFlag(this);
    }

    onPlayerPosition(event) {
        console.log('onPlayerPosition', event);

        const player = this.teamManager.findPlayer(event.player);

        if (!player) {
            console.warn('Player number ' + event.player + ' not found yet!');
            return;
        }

        if (player.number == this.teamManager.hero.number) {
            console.error('Reposition the hero. ', player.number, this.teamManager.hero.number);
            return;
        }

        player.x = event.x;
        player.y = event.y;

        if (event.moving) {
            player.moveToDirection(event.direction, "network");
        }
        else
            player.stopMoving(event.direction, "network");

    }

    onPlayerShoot(event) {
        const player = this.teamManager.findPlayer(event.player);

        if (!player) {
            console.warn('Player number ' + event.player + ' not found yet!');
            return;
        }

        player.shoot(event.direction);
    }

    onTreasureChestStatus(payload) {
        this.objects.get('treasureChest.' + payload.id).setStatus(payload.newStatus);
    }

    onPlayerUpdateWeapon(payload) {
        const player = this.teamManager.findPlayer(payload.player);
        player.weapon.updateWeapon(payload.newWeapon);
    }

    onPlayerAlien(payload) {
        const player = this.teamManager.findPlayer(payload.player);
        player.getAlien();
    }

    onRemoveFlagFromStatusBar(payload) {
        console.log("onRemoveFlagFromStatusBar", payload);
        this.objects.get(payload.flag).visible = false;
        if (payload.gameOver) {
            this.game.input.enabled = false;
            this.game.physics.arcade.isPaused = true;
            this.objects.get('gameOverText').setText('Game Over!\n ' + payload.color + ' wins the game!');
            this.objects.get('gameOverText').visible = true;
            this.game.time.events.add(Phaser.Timer.SECOND * config.game.reloadPage.wait, this.reloadPage, this);
        }
    }

    reloadPage() {
        window.location.reload();
    }

    onPlayerDamage(event) {
        const player = this.teamManager.findPlayer(event.player);

        player.setHealth(event.health);
    }

    onAnswerWithExp(event) {
        const player = this.teamManager.findPlayer(event.player);

        player.setExp(event.exp);
    }

    targetRoomId() {
        const search = window.location.search;
        const searchSplit = search.split('=');

        if (searchSplit.length !== 2) {
            return null;
        }

        return searchSplit[1];
    }

}
<|endoftext|>


<commit_before>$(function(){

  // Leave some room for the header and footer
  $('#map').css("height", ($(window).height() - 200));
  $('#overlay').css("height", ($(window).height() - 264));

  var map = L.mapbox.map('map', 'codeforamerica.map-hhckoiuj',
    {
      scrollWheelZoom:false
    }
    ).setView([35, -100], 3);

  map.zoomControl.setPosition('bottomright');

  cfapi = "http://civic-tech-movement.codeforamerica.org/api/organizations"
  // cfapi = "http://localhost:5000/api/organizations"

  // Custom marker type with space for Brigade data
  var BrigadeMarker = L.Marker.extend({
    options: {
      brigade : {}
    }
  });

  $.getJSON(cfapi, function(data){
    var brigades = data.objects;

    $.each(brigades, function(i, brigade){

      if (brigade.type == "Brigade"){
        if (brigade.latitude && brigade.longitude){
          var lat = brigade.latitude, lng = brigade.longitude;

          var marker = new BrigadeMarker(new L.LatLng(lat,lng), {
            icon: L.mapbox.marker.icon({'marker-symbol': 'town-hall'}),
            title: brigade.name,
            brigade: brigade // Add Brigade data to marker
          });

          map.addLayer(marker);

          // On click, show the Brigade data on the overlay
          marker.on('click',function(e) {
            updateOverlay(e.target.options.brigade);
          });

        }
      }
    });
  }).done( function() {
    // This uses the HTML5 geolocation API, which is available on
    // most mobile browsers and modern browsers, but not in Internet Explorer
    //
    // See this chart of compatibility for details:
    // http://caniuse.com/#feat=geolocation
    if (!navigator.geolocation) {
        console.log('geolocation is not available');
    } else {
      map.locate({setView:true, maxZoom:4});
    }
  });

  function updateOverlay(brigade){
    $("#overlay-reset").show();
    $("#brigade-name").text(brigade.name);
    $("#brigade-url").text(brigade.website).attr("href",brigade.website).show();
    $("#program-info").hide();
    $("#join-form").hide();

    // Show two stories
    if (brigade.stories.length != 0) {
      $("#stories ul").empty();
      for (var i = 0; i < 2; i++) {
        story = brigade.stories[i];
        html = "
"+story.title+"
"
        $("#stories ul").append(html);
      }
      $("#stories").show();
    } else {
      $("#stories").hide();
    }

    // Show two events
    if (brigade.events){
      if (brigade.events.length != 0) {
        $("#events ul").empty();
        for (var i = 0; i < 2; i++) {
          event = brigade.events[i];
          html = "
"+event.name+"
"
          $("#events ul").append(html);
        }
        $("#events").show();
      } else {
        $("#events").hide();
      }
    }

    // Show two projects
    if (brigade.projects.length != 0) {
      $("#projects ul").empty();
      for (var i = 0; i < 3; i++) {
        project = brigade.projects[i];
        html = "
"+project.name+"";
        html += "
"+project.description+"

";
        $("#projects ul").append(html);
      };
      $("#projects").show();
    } else {
      $("#projects").hide();
    }

  }

  // Reset overlay
  map.on('click', function(e){
    resetOverlay();
  })

  function resetOverlay(){
    $("#overlay-reset").hide();
    $("#brigade-name").text("The Code for America Brigade");
    $("#brigade-url").hide();
    $("#program-info").show();
    $("#stories").hide();
    $("#projects").hide();
    $("#events").hide();
    $("#join-form").show();
  }

});<commit_msg>Add icons to stories<commit_after>$(function(){

  // Leave some room for the header and footer
  $('#map').css("height", ($(window).height() - 200));
  $('#overlay').css("height", ($(window).height() - 264));

  var map = L.mapbox.map('map', 'codeforamerica.map-hhckoiuj',
    {
      scrollWheelZoom:false
    }
    ).setView([35, -100], 3);

  map.zoomControl.setPosition('bottomright');

  cfapi = "http://civic-tech-movement.codeforamerica.org/api/organizations"
  // cfapi = "http://localhost:5000/api/organizations"

  // Custom marker type with space for Brigade data
  var BrigadeMarker = L.Marker.extend({
    options: {
      brigade : {}
    }
  });

  $.getJSON(cfapi, function(data){
    var brigades = data.objects;

    $.each(brigades, function(i, brigade){

      if (brigade.type == "Brigade"){
        if (brigade.latitude && brigade.longitude){
          var lat = brigade.latitude, lng = brigade.longitude;

          var marker = new BrigadeMarker(new L.LatLng(lat,lng), {
            icon: L.mapbox.marker.icon({'marker-symbol': 'town-hall'}),
            title: brigade.name,
            brigade: brigade // Add Brigade data to marker
          });

          map.addLayer(marker);

          // On click, show the Brigade data on the overlay
          marker.on('click',function(e) {
            updateOverlay(e.target.options.brigade);
          });

        }
      }
    });
  }).done( function() {
    // This uses the HTML5 geolocation API, which is available on
    // most mobile browsers and modern browsers, but not in Internet Explorer
    //
    // See this chart of compatibility for details:
    // http://caniuse.com/#feat=geolocation
    if (!navigator.geolocation) {
        console.log('geolocation is not available');
    } else {
      map.locate({setView:true, maxZoom:4});
    }
  });

  function updateOverlay(brigade){
    $("#overlay-reset").show();
    $("#brigade-name").text(brigade.name);
    $("#brigade-url").text(brigade.website).attr("href",brigade.website).show();
    $("#program-info").hide();
    $("#join-form").hide();

    // Show two stories
    if (brigade.stories.length != 0) {
      $("#stories ul").empty();
      for (var i = 0; i < 2; i++) {
        story = brigade.stories[i];
        html = "
"+story.title+"
"
        $("#stories ul").append(html);
      }
      $("#stories").show();
    } else {
      $("#stories").hide();
    }

    // Show two events
    if (brigade.events){
      if (brigade.events.length != 0) {
        $("#events ul").empty();
        for (var i = 0; i < 2; i++) {
          event = brigade.events[i];
          html = "
"+event.name+"
"
          $("#events ul").append(html);
        }
        $("#events").show();
      } else {
        $("#events").hide();
      }
    }

    // Show two projects
    if (brigade.projects.length != 0) {
      $("#projects ul").empty();
      for (var i = 0; i < 3; i++) {
        project = brigade.projects[i];
        html = "
"+project.name+"";
        html += "
"+project.description+"

";
        $("#projects ul").append(html);
      };
      $("#projects").show();
    } else {
      $("#projects").hide();
    }

  }

  // Reset overlay
  map.on('click', function(e){
    resetOverlay();
  })

  function resetOverlay(){
    $("#overlay-reset").hide();
    $("#brigade-name").text("The Code for America Brigade");
    $("#brigade-url").hide();
    $("#program-info").show();
    $("#stories").hide();
    $("#projects").hide();
    $("#events").hide();
    $("#join-form").show();
  }

});<|endoftext|>


<commit_before>// Link to Elm
var app = Elm.embed(Elm.App, document.getElementById('elm-div'), {
    responsePort: { actionType: "NoOp", data: null }
});

var Config = {
    validEmailExtension: "@gmail.com",
    invalidEmailMessage: "Email is not valid.
Expecting an email from gmail.com"
};

// Google login/logout
function onSignIn(googleUser) {
    // Useful data for your client-side scripts:
    var profile = googleUser.getBasicProfile();
    console.log("ID: " + profile.getId()); // Don't send this directly to your server!
    console.log("Name: " + profile.getName());
    console.log("Image URL: " + profile.getImageUrl());
    console.log("Email: " + profile.getEmail());

    // The ID token you need to pass to your backend:
    var id_token = googleUser.getAuthResponse().id_token;
    console.log("ID Token: " + id_token);

    var email = profile.getEmail()

    var isValid = email && email.endsWith(Config.validEmailExtension)

    if (isValid) {
        console.log(email + " is a valid email.")
        app.ports.responsePort.send({ actionType: "LogIn", data: null });

    } else {
        signOut();
        app.ports.responsePort.send({ actionType: "LogOut", data: null });
        console.log(Config.invalidEmailMessage)

        if(toastr) toastr.error(Config.invalidEmailMessage)
    }
};

function signOut() {
    var auth2 = gapi.auth2.getAuthInstance();
    auth2.signOut().then(function () {
      console.log('User signed out.');
    });
};

// Elm setup
(function (Elm, app) {
    // Set up port
    app.ports.requestPort.subscribe(appPortRequestHandler);

    function appPortRequestHandler(appPortRequest) {
        switch (appPortRequest.actionType) {
            case "LogOut":
                signOut();
                // Let Elm know that log out succeeded
                app.ports.responsePort.send(appPortRequest);
                break;
            case "Error":
                toastr.error(appPortRequest.data);
                break;
            case "Notify":
                toastr.success(appPortRequest.data);
                break;
            case "RequestConsoleLog":
                console.log(appPortRequest.data);
                break;
            default:
                console.log("Ignoring Request: " + appPortRequest)
        }
    };

})(Elm, app);
<commit_msg>Adding setup.js modification, needed for previous commit to work.<commit_after>// Link to Elm
var app = Elm.embed(Elm.App, document.getElementById('elm-div'), {
    responsePort: { actionType: "NoOp", data: null }
});

var Config = {
    validEmailExtension: "@gmail.com",
    invalidEmailMessage: "Email is not valid.
Expecting an email from gmail.com"
};

// Google login/logout
function onSignIn(googleUser) {
    // Useful data for your client-side scripts:
    var profile = googleUser.getBasicProfile();
    console.log("ID: " + profile.getId()); // Don't send this directly to your server!
    console.log("Name: " + profile.getName());
    console.log("Image URL: " + profile.getImageUrl());
    console.log("Email: " + profile.getEmail());

    // The ID token you need to pass to your backend:
    var id_token = googleUser.getAuthResponse().id_token;
    console.log("ID Token: " + id_token);

    var email = profile.getEmail()

    var isValid = email && email.endsWith(Config.validEmailExtension)

    if (isValid) {
        console.log(email + " is a valid email.")
        app.ports.responsePort.send({ actionType: "LogIn", data: email });

    } else {
        signOut();
        app.ports.responsePort.send({ actionType: "LogOut", data: null });
        console.log(Config.invalidEmailMessage)

        if(toastr) toastr.error(Config.invalidEmailMessage)
    }
};

function signOut() {
    var auth2 = gapi.auth2.getAuthInstance();
    auth2.signOut().then(function () {
      console.log('User signed out.');
    });
};

// Elm setup
(function (Elm, app) {
    // Set up port
    app.ports.requestPort.subscribe(appPortRequestHandler);

    function appPortRequestHandler(appPortRequest) {
        switch (appPortRequest.actionType) {
            case "LogOut":
                signOut();
                // Let Elm know that log out succeeded
                app.ports.responsePort.send(appPortRequest);
                break;
            case "Error":
                toastr.error(appPortRequest.data);
                break;
            case "Notify":
                toastr.success(appPortRequest.data);
                break;
            case "RequestConsoleLog":
                console.log(appPortRequest.data);
                break;
            default:
                console.log("Ignoring Request: " + appPortRequest)
        }
    };

})(Elm, app);
<|endoftext|>


<commit_before>var fs = require('fs'),
    path = require('path'),
    compile = require('./compile'),
    cmd = require('./cmd');
var existsSync = fs.existsSync || path.existsSync;

var prefix = '';

var recursive = cmd.recursive !== 'no';
var dir = cmd.dir;
if (cmd.help) {
    console.log('node watch.js --dir=... [--out=...]\n--dir directory where .xml files are\n--out out directory for compiled files if omited --out = --dir');
    return;
}
if (!dir){
    console.log('You need to pint directory "node watch.js dir=..."');
    return;
}
if (dir[0] !== '/'){
    dir = process.env.PWD + '/' + dir;
}
dir = dir.replace(/\/$/, '') + '/';
if (!existsSync(dir)){
    console.log('directory "' + dir + '" does not exists');
    return;
}
var out_dir = cmd.out || dir;
out_dir = out_dir.replace(/\/$/, '') + '/';
if (out_dir[0] !== '/'){
    out_dir = process.env.PWD + '/' + out_dir;
}
var fest = (cmd.fest === 'no' ? false : true);
var watched_files = {};

function do_compile(file, dir, out_dir, prefix){
    console.log('compile: ' + dir + file);
    var source = compile(dir + file, '', '');
    var file_name = (out_dir + file).replace(/\.xml/, '.js');
    if (!existsSync(out_dir)){
        fs.mkdirSync(out_dir);
    }
    if (fest){
        fs.writeFileSync(file_name,  ';(function(x){if(!x.fest)x.fest={};x.fest["' + prefix + file.replace(/\.xml$/, '') + '"]=' + source + '})(this);', 'utf8');
    } else {
        fs.writeFileSync(file_name, source, 'utf8');
    }
}
function findfiles(dir, out_dir, prefix){
    fs.readdir(dir, function(err, files){
        files.filter(function(file){return file.indexOf('.xml') > 0;})
             .forEach(function(file){
                 console.log('watch: ' + file);
                 do_compile(file, dir, out_dir, prefix);
             });
        if (recursive){
            files.filter(function(file){ return fs.statSync(dir + file).isDirectory() })
                 .forEach(function(file){
                     console.log('watch directory: ' + dir + file);
                     findfiles(dir + file + '/', out_dir + file + '/', prefix + file + '/');
                 });
        }
    });
}

findfiles(dir, out_dir, prefix);
<commit_msg>not anonymous template function<commit_after>var fs = require('fs'),
    path = require('path'),
    compile = require('./compile'),
    cmd = require('./cmd');
var existsSync = fs.existsSync || path.existsSync;

var prefix = '';

var recursive = cmd.recursive !== 'no';
var dir = cmd.dir;
if (cmd.help) {
    console.log('node watch.js --dir=... [--out=...]\n--dir directory where .xml files are\n--out out directory for compiled files if omited --out = --dir');
    return;
}
if (!dir){
    console.log('You need to pint directory "node watch.js dir=..."');
    return;
}
if (dir[0] !== '/'){
    dir = process.env.PWD + '/' + dir;
}
dir = dir.replace(/\/$/, '') + '/';
if (!existsSync(dir)){
    console.log('directory "' + dir + '" does not exists');
    return;
}
var out_dir = cmd.out || dir;
out_dir = out_dir.replace(/\/$/, '') + '/';
if (out_dir[0] !== '/'){
    out_dir = process.env.PWD + '/' + out_dir;
}
var fest = (cmd.fest === 'no' ? false : true);
var notAnonymous = cmd.anonymous === 'no';
if (notAnonymous) fest = false;
var watched_files = {};

function do_compile(file, dir, out_dir, prefix){
    console.log('compile: ' + dir + file);
    var source = compile(dir + file, '', notAnonymous ? file.replace('.xml', '') : '');
    var file_name = (out_dir + file).replace(/\.xml/, '.js');
    if (!existsSync(out_dir)){
        fs.mkdirSync(out_dir);
    }
    if (fest){
        fs.writeFileSync(file_name,  ';(function(x){if(!x.fest)x.fest={};x.fest["' + prefix + file.replace(/\.xml$/, '') + '"]=' + source + '})(this);', 'utf8');
    } else {
        fs.writeFileSync(file_name, source, 'utf8');
    }
}
function findfiles(dir, out_dir, prefix){
    fs.readdir(dir, function(err, files){
        files.filter(function(file){return file.indexOf('.xml') > 0;})
             .forEach(function(file){
                 console.log('watch: ' + file);
                 do_compile(file, dir, out_dir, prefix);
             });
        if (recursive){
            files.filter(function(file){ return fs.statSync(dir + file).isDirectory() })
                 .forEach(function(file){
                     console.log('watch directory: ' + dir + file);
                     findfiles(dir + file + '/', out_dir + file + '/', prefix + file + '/');
                 });
        }
    });
}

findfiles(dir, out_dir, prefix);
<|endoftext|>


<commit_before>'use strict';

var chai = require('chai');
chai.use(require('chai-shallow-deep-equal'));
var sdEqual = chai.assert.shallowDeepEqual;

var parse = require('../index').parse;

describe('#parse', function() {
    it('should parse a single expression with one arg', function() {
        sdEqual(parse('(a b)'), ['a', 'b']);
    });

    it('should parse a single expression with two args', function() {
        sdEqual(parse('(a b c)'), ['a', 'b', 'c']);
    });

    it('should parse a nested expression', function() {
        sdEqual(parse('(a (b c) d)'), ['a', ['b', 'c'], 'd']);
    });
});
<commit_msg>Better require in tests<commit_after>'use strict';

var chai = require('chai');
chai.use(require('chai-shallow-deep-equal'));
var sdEqual = chai.assert.shallowDeepEqual;

var clubExpr = require('../index');

var parse = clubExpr.parse;

describe('#parse', function() {
    it('should parse a single expression with one arg', function() {
        sdEqual(parse('(a b)'), ['a', 'b']);
    });

    it('should parse a single expression with two args', function() {
        sdEqual(parse('(a b c)'), ['a', 'b', 'c']);
    });

    it('should parse a nested expression', function() {
        sdEqual(parse('(a (b c) d)'), ['a', ['b', 'c'], 'd']);
    });
});
<|endoftext|>


<commit_before>$(function() {
	var $window = $(window),
		$doc = $(document),
		$html = $('html'),
		$body = $('body'),
		$container = $('#container'),
		$music = new Audio();

	$doc.on('keydown', function(event) {
		if (event.which == 32) {
			$window.trigger('advance');
		};
	});

	$doc.on('touchstart', function(event) {
		$window.trigger('advance');
	});

	$window.on('advance', function() {
		var $frame = $container.find('.frame'),
			$current = $container.find('.current'),
			$next = $frame.eq($current.index() + 1);

		if ($current.hasClass('final'))
			return;

		if ($next.hasClass('start-music')) {
			if ($music.canPlayType('audio/mpeg;')) {
				$music.type= 'audio/mpeg';
				$music.src= '/music.mp3';
			} else {
				$music.type= 'audio/ogg';
				$music.src= '/music.ogg';
			}
			$music.volume = 0;
			$music.play();
			$($music).animate({volume: 1}, 2000);
		}

		if (Modernizr.csstransitions) {
			$current.removeClass('current').addClass('spent');
			$next.addClass('current');
		} else {
			if ($current.hasClass('slow-hack')) {
				$current.css({ opacity: 1 }).removeClass('current').animate({ opacity: 0 }, 1500);
			} else {
				$current.css({ opacity: 1 }).removeClass('current').animate({ opacity: 0 }, 500);
			}
			$next.css({ opacity: 0 }).addClass('current').animate({ opacity: 1 }, 1000);
		}
	});
});
<commit_msg>Made the audio preload<commit_after>$(function() {
	var $window = $(window),
		$doc = $(document),
		$html = $('html'),
		$body = $('body'),
		$container = $('#container'),
		$music = new Audio();

	if ($music.canPlayType('audio/mpeg;')) {
		$music.type= 'audio/mpeg';
		$music.src= '/music.mp3';
	} else {
		$music.type= 'audio/ogg';
		$music.src= '/music.ogg';
	}
	$music.volume = 0;

	$doc.on('keydown', function(event) {
		if (event.which == 32) {
			$window.trigger('advance');
		};
	});

	$doc.on('touchstart', function(event) {
		$window.trigger('advance');
	});

	$window.on('advance', function() {
		var $frame = $container.find('.frame'),
			$current = $container.find('.current'),
			$next = $frame.eq($current.index() + 1);

		if ($current.hasClass('final'))
			return;

		if ($next.hasClass('start-music')) {
			$music.play();
			$($music).animate({volume: 1}, 2000);
		}

		if (Modernizr.csstransitions) {
			$current.removeClass('current').addClass('spent');
			$next.addClass('current');
		} else {
			if ($current.hasClass('slow-hack')) {
				$current.css({ opacity: 1 }).removeClass('current').animate({ opacity: 0 }, 1500);
			} else {
				$current.css({ opacity: 1 }).removeClass('current').animate({ opacity: 0 }, 500);
			}
			$next.css({ opacity: 0 }).addClass('current').animate({ opacity: 1 }, 1000);
		}
	});
});
<|endoftext|>


<commit_before>/**
 * etcd watch apis
 * @license Apache License 2
 */

var EventEmitter = require('events');
var util = require('util');
var _ = require('lodash');
var debug = require('debug')('etcdv3:watcher');
var pb = require('./pb/rpc_pb');

module.exports = Watch;

/**
 * @param services.WatchClient conn
 */
function Watch(conn){
    this.conn = conn;
    debug('new watch instance');
}

/**
 * do request
 */
function _watch(agent){
    debug('do watch');
    return agent.conn.watch();
}

/**
 * watch request
 * @return DuplexStream
 */
Watch.prototype.watch = function(){
    return _watch(this,'watch');
};

//create a Watcher instance
function createWatcher(agent,key,end){
    var create_request = new pb.WatchCreateRequest();
    var request = new pb.WatchRequest();
    var stream = null;
    create_request.setKey(key);
    if(end){
        create_request.setRangeEnd(end);
    }
    request.setCreateRequest(create_request);
    stream = agent.watch();
    return new Watcher(stream,request);
}

/**
 * create a simple watch request
 * @param string key
 * @param string end range end
 * @return Watcher instance
 */
Watch.prototype.create = function (key,end) {
    return createWatcher(this,key,end);
};

/**
 * @return new pb.CancelWatchRequest
 */
function createCancelRequest(id){
    var cancel_request = new pb.WatchCancelRequest();
    var request = new pb.WatchRequest();
    cancel_request.setWatchId(id);
    request.setCancelRequest(cancel_request);
    return request;
}

/**
 * close
 * call by etcdv3.Client
 */
Watch.prototype.close = function(){

};

/**
 * watch stream
 * all events
 * created  - after create request
 * canceled - after cancel request
 * events   - watch response
 * close    - stream close
 * error    - stream errors
 * @param DuplexStream stream
 * @param pb.WatchRequest request
 */
function Watcher(stream,request){
    EventEmitter.call(this);
    var self = this;
    this.id = null;
    this.request = request;
    this.cleanFns = [];
    this.stream = stream;
    function onData(response){
        self.id = response.getWatchId();
        debug('watcher %s ondata',self.id);
        if(response.getCreated()){
            self.emit('create',self.id);
            return;
        }
        if(response.getCanceled()){
            self.emit('cancel',self.id);
            return;
        }
        //events
        self.emit('events', _.reduce(response.getEventsList(),function(arr,event){
            var kv = event.getKv();
            arr.push({type:event.getType(),key:kv.getKey(),value:kv.getValue()});
            return arr;
        },[]));
    }
    function onEnd(){
        debug('watcher %s end',self.id);
        console.log(11111);
        self.emit('end');
    }
    function onError(){
        console.log(err);
        self.emit('error',err);
    }
    this.stream.on('data',onData);
    this.stream.once('end',onEnd);
    this.stream.on('error',onError);
    this.cleanFns.push(function(){
        debug('watcher %s cleanup',self.id);
        self.stream.removeListener('data',onData);
        self.stream.removeListener('error',onError);
    });
}

util.inherits(Watcher,EventEmitter);

/**
 * start watching
 */
Watcher.prototype.start = function(){
    _write(this,this.request);
}

function _write(w,data){
    function write(){
        return w.stream.write(data);
    }
    if(!write()){
        debug('watcher %s drain',w.id);
        w.stream.once('drain',write);
    }
}

/**
 * cancel watching
 */
Watcher.prototype.cancel = function(){
    if(!this.stream.writable){
        this.emit('error',new Error('this stream not writeable'));
        return;
    }
    var self = this;
    if(this.id ===null){
        this.emit('error',new Error('this watcher not started'));
        return;
    }
    _write(this,createCancelRequest(this.id));
};

/**
 * close
 */
Watcher.prototype.close = function(){
    this.cleanFns.forEach(function(fn) {
        fn();
    });
    this.stream.cancel();
    this.request = null;
    this.stream = null;
};

<commit_msg>delete test code<commit_after>/**
 * etcd watch apis
 * @license Apache License 2
 */

var EventEmitter = require('events');
var util = require('util');
var _ = require('lodash');
var debug = require('debug')('etcdv3:watcher');
var pb = require('./pb/rpc_pb');

module.exports = Watch;

/**
 * @param services.WatchClient conn
 */
function Watch(conn){
    this.conn = conn;
    debug('new watch instance');
}

/**
 * do request
 */
function _watch(agent){
    debug('do watch');
    return agent.conn.watch();
}

/**
 * watch request
 * @return DuplexStream
 */
Watch.prototype.watch = function(){
    return _watch(this,'watch');
};

//create a Watcher instance
function createWatcher(agent,key,end){
    var create_request = new pb.WatchCreateRequest();
    var request = new pb.WatchRequest();
    var stream = null;
    create_request.setKey(key);
    if(end){
        create_request.setRangeEnd(end);
    }
    request.setCreateRequest(create_request);
    stream = agent.watch();
    return new Watcher(stream,request);
}

/**
 * create a simple watch request
 * @param string key
 * @param string end range end
 * @return Watcher instance
 */
Watch.prototype.create = function (key,end) {
    return createWatcher(this,key,end);
};

/**
 * @return new pb.CancelWatchRequest
 */
function createCancelRequest(id){
    var cancel_request = new pb.WatchCancelRequest();
    var request = new pb.WatchRequest();
    cancel_request.setWatchId(id);
    request.setCancelRequest(cancel_request);
    return request;
}

/**
 * close
 * call by etcdv3.Client
 */
Watch.prototype.close = function(){

};

/**
 * watch stream
 * all events
 * created  - after create request
 * canceled - after cancel request
 * events   - watch response
 * close    - stream close
 * error    - stream errors
 * @param DuplexStream stream
 * @param pb.WatchRequest request
 */
function Watcher(stream,request){
    EventEmitter.call(this);
    var self = this;
    this.id = null;
    this.request = request;
    this.cleanFns = [];
    this.stream = stream;
    function onData(response){
        self.id = response.getWatchId();
        debug('watcher %s ondata',self.id);
        if(response.getCreated()){
            self.emit('create',self.id);
            return;
        }
        if(response.getCanceled()){
            self.emit('cancel',self.id);
            return;
        }
        //events
        self.emit('events', _.reduce(response.getEventsList(),function(arr,event){
            var kv = event.getKv();
            arr.push({type:event.getType(),key:kv.getKey(),value:kv.getValue()});
            return arr;
        },[]));
    }
    function onEnd(){
        debug('watcher %s end',self.id);
        self.emit('end');
    }
    function onError(err){
        self.emit('error',err);
    }
    this.stream.on('data',onData);
    this.stream.once('end',onEnd);
    this.stream.on('error',onError);
    this.cleanFns.push(function(){
        debug('watcher %s cleanup',self.id);
        self.stream.removeListener('data',onData);
        self.stream.removeListener('error',onError);
    });
}

util.inherits(Watcher,EventEmitter);

/**
 * start watching
 */
Watcher.prototype.start = function(){
    _write(this,this.request);
}

function _write(w,data){
    function write(){
        return w.stream.write(data);
    }
    if(!write()){
        debug('watcher %s drain',w.id);
        w.stream.once('drain',write);
    }
}

/**
 * cancel watching
 */
Watcher.prototype.cancel = function(){
    if(!this.stream.writable){
        this.emit('error',new Error('this stream not writeable'));
        return;
    }
    var self = this;
    if(this.id ===null){
        this.emit('error',new Error('this watcher not started'));
        return;
    }
    _write(this,createCancelRequest(this.id));
};

/**
 * close
 */
Watcher.prototype.close = function(){
    this.stream.cancel();
    this.cleanFns.forEach(function(fn) {
        fn();
    });
    this.request = null;
    this.stream = null;
};

<|endoftext|>


<commit_before>import styled from 'styled-components';
import { lineHeight } from 'styled-system';

import { preset } from 'react/styles/functions';

import mixin from 'react/components/UI/Inputs/mixin';

export default styled.textarea`
  ${mixin}
  ${preset(lineHeight, { lineHeight: 1 })}
`;
<commit_msg>Defaults textareas to be vertically-resizable<commit_after>import styled from 'styled-components';
import { lineHeight } from 'styled-system';

import { preset } from 'react/styles/functions';

import mixin from 'react/components/UI/Inputs/mixin';

const Textarea = styled.textarea`
  ${mixin}
  ${preset(lineHeight, { lineHeight: 1 })}
  resize: ${props => props.resize};
`;

Textarea.defaultProps = {
  resize: 'vertical',
};

export default Textarea;
<|endoftext|>


<commit_before>define([
    "urlutils",
    "utils",
    '../../components/vue/dist/vue'
], function (urlutils, utils, Vue) {
    "use strict";

    var ApplicationView = Vue.extend({
        template:
            '' +
            '
' +
            '  ' +
            '
' +
            '
' +
            '
' +
            '
' +
            '
{{ current_app.app_data.image | app_name }}
' +
            '

' +
            '
' +
            '
' +
            '
Policy
' +

            '
' +
            '            ' +
            '
' +
            '                Workspace accessible' +
            '
' +
            '
' +
            '                Workspace not accessible' +
            '
' +

            '            ' +
            '
' +
            '              Volume mounted: {{ app_policy.volume_source }} → {{ app_policy.volume_target }} ({{ app_policy.volume_mode }})' +
            '
' +
            '
' +
            '              No volumes mounted' +
            '
' +
            '
' +

            '
Configuration
' +
            '          <form class="configuration">' +
            '            <fieldset v-if="current_app.configurables.length === 0">No configurable options for this image</fieldset>' +
            '            <fieldset v-else :disabled="current_app.is_starting()">' +
            '              <component v-for="configurable in current_app.configurables"' +
            '                         :key="configurable.tag"' +
            '                         :is="configurable.tag + \'-component\'"' +
            '                         :config_dict="configurable.config_dict"' +
            '                         @update:config_dict="config_dict => configurable.config_dict = config_dict"></component>' +
            '            </fieldset>' +
            '          </form>' +
            '
' +

            '        ' +
            '
' +
            '          <button class="btn btn-primary pull-right start-button"' +
            '                  @click="start_application()"' +
            '                  :disabled="current_app.is_starting()">' +
            '            Start' +
            '          </button>' +
            '
' +
            '
' +
            '
' +
            '
' +

            '  ' +
            '  ' +
            '  ' +
            '
',

        computed: {
            current_app: function() {
                return this.model.app_list[this.model.selected_index] || null;
            },
            app_policy: function() {
                return this.current_app.app_data.image.policy;
            },
            app_source: function() {
                var url = urlutils.path_join(
                    window.apidata.base_url,
                    'containers',
                    this.current_app.app_data.container.url_id
                );
                var output = this.current_app.delayed ? url : url + '/';

                this.current_app.delayed = false;

                return output;
            }
        },

        methods: {
            start_application: function() {
                this.$emit('start_application', this.current_app);
                this.model.start_application();
            },
            get_iframe_size: function() {
                return utils.max_iframe_size();
            },
            focus_iframe: function() {
                var iframe = this.$el.querySelector('iframe');
                if(iframe !== null) {
                    iframe.focus();
                }
            }
        },

        updated: function() { this.focus_iframe(); }
    });

    return {
        ApplicationView : ApplicationView
    };
});
<commit_msg>Use .sync modifier<commit_after>define([
    "urlutils",
    "utils",
    '../../components/vue/dist/vue'
], function (urlutils, utils, Vue) {
    "use strict";

    var ApplicationView = Vue.extend({
        template:
            '' +
            '
' +
            '  ' +
            '
' +
            '
' +
            '
' +
            '
' +
            '
{{ current_app.app_data.image | app_name }}
' +
            '

' +
            '
' +
            '
' +
            '
Policy
' +

            '
' +
            '            ' +
            '
' +
            '                Workspace accessible' +
            '
' +
            '
' +
            '                Workspace not accessible' +
            '
' +

            '            ' +
            '
' +
            '              Volume mounted: {{ app_policy.volume_source }} → {{ app_policy.volume_target }} ({{ app_policy.volume_mode }})' +
            '
' +
            '
' +
            '              No volumes mounted' +
            '
' +
            '
' +

            '
Configuration
' +
            '          <form class="configuration">' +
            '            <fieldset v-if="current_app.configurables.length === 0">No configurable options for this image</fieldset>' +
            '            <fieldset v-else :disabled="current_app.is_starting()">' +
            '              <component v-for="configurable in current_app.configurables"' +
            '                         :key="configurable.tag"' +
            '                         :is="configurable.tag + \'-component\'"' +
            '                         :config_dict.sync="configurable.config_dict"></component>' +
            '            </fieldset>' +
            '          </form>' +
            '
' +

            '        ' +
            '
' +
            '          <button class="btn btn-primary pull-right start-button"' +
            '                  @click="start_application()"' +
            '                  :disabled="current_app.is_starting()">' +
            '            Start' +
            '          </button>' +
            '
' +
            '
' +
            '
' +
            '
' +

            '  ' +
            '  ' +
            '  ' +
            '
',

        computed: {
            current_app: function() {
                return this.model.app_list[this.model.selected_index] || null;
            },
            app_policy: function() {
                return this.current_app.app_data.image.policy;
            },
            app_source: function() {
                var url = urlutils.path_join(
                    window.apidata.base_url,
                    'containers',
                    this.current_app.app_data.container.url_id
                );
                var output = this.current_app.delayed ? url : url + '/';

                this.current_app.delayed = false;

                return output;
            }
        },

        methods: {
            start_application: function() {
                this.$emit('start_application', this.current_app);
                this.model.start_application();
            },
            get_iframe_size: function() {
                return utils.max_iframe_size();
            },
            focus_iframe: function() {
                var iframe = this.$el.querySelector('iframe');
                if(iframe !== null) {
                    iframe.focus();
                }
            }
        },

        updated: function() { this.focus_iframe(); }
    });

    return {
        ApplicationView : ApplicationView
    };
});
<|endoftext|>


<commit_before>/**
 * Common JS that talks XHR back to the server and runs the code and receives
 * the results.
 */

/**
 * A polyfill for HTML Templates.
 *
 * This just adds in the content attribute, it doesn't stop scripts
 * from running nor does it stop other side-effects.
 */
(function polyfillTemplates() {
  if('content' in document.createElement('template')) {
    return false;
  }

  var templates = document.getElementsByTagName('template');
  for (var i=0; i<templates.length; i++) {
    var content = document.createDocumentFragment();
    while (templates[i].firstChild) {
     content.appendChild(templates[i].firstChild);
    }
    templates[i].content = content;
  }
})();

/**
 * All the functionality is wrapped up in this anonymous closure, but we need
 * to be told if we are on the workspace page or a normal try page, so the
 * workspaceName is passed into the closure, it must be set in the global
 * namespace. If workspaceName is the empty string then we know we aren't
 * running on a workspace page.
 *
 * If we are on a workspace page we also look for a 'history'
 * variable in the global namespace which contains the list of tries
 * that are included in this workspace. That variable is used to
 * populate the history list.
 */
(function(workspaceName) {
    var run = document.getElementById('run');
    var permalink = document.getElementById('permalink');
    var embed = document.getElementById('embed');
    var embedButton = document.getElementById('embedButton');
    var code = document.getElementById('code');
    var output = document.getElementById('output');
    var stdout = document.getElementById('stdout');
    var img = document.getElementById('img');
    var tryHistory = document.getElementById('tryHistory');
    var parser = new DOMParser();
    var tryTemplate = document.getElementById('tryTemplate');

    function beginWait() {
      document.body.classList.add('waiting');
      run.disabled = true;
    }

    function endWait() {
      document.body.classList.remove('waiting');
      run.disabled = false;
    }

    /**
     * Callback when there's an XHR error.
     * @param e The callback event.
     */
    function xhrError(e) {
      endWait();
      alert('Something bad happened: ' + e);
    }

    function clearOutput() {
      output.innerText = "";
      if (stdout) {
        stdout.innerText = "";
      }
      embed.style.display='none';
    }

    /**
     * Called when an image in the workspace history is clicked.
     */
    function historyClick() {
      beginWait();
      clearOutput();
      var req = new XMLHttpRequest();
      req.addEventListener('load', historyComplete);
      req.addEventListener('error', xhrError);
      req.overrideMimeType('application/json');
      req.open('GET', this.getAttribute('data-try'), true);
      req.send();
    }

    /**
     * Callback for when the XHR kicked off in historyClick() returns.
     */
    function historyComplete(e) {
      // The response is JSON of the form:
      // {
      //   "hash": "unique id for a try",
      //   "code": "source code for try"
      // }
      endWait();
      body = JSON.parse(e.target.response);
      code.value = body.code;
      img.src = '/i/'+body.hash+'.png';
      if (permalink) {
        permalink.href = '/c/' + body.hash;
      }
    }

    /**
     * Add the given try image to the history of a workspace.
     */
    function addToHistory(hash, imgUrl) {
      var clone = tryTemplate.content.cloneNode(true);
      clone.querySelector('img').src = imgUrl;
      clone.querySelector('.tries').setAttribute('data-try', '/json/' + hash);
      tryHistory.insertBefore(clone, tryHistory.firstChild);
      tryHistory.querySelector('.tries').addEventListener('click', historyClick, true);
    }

    /**
     * Callback for when the XHR returns after attempting to run the code.
     * @param e The callback event.
     */
    function codeComplete(e) {
      // The response is JSON of the form:
      // {
      //   "message": "you had an error...",
      //   "img": "<base64 encoded image but only on success>"
      // }
      //
      // The img is optional and only appears if there is a valid
      // image to display.
      endWait();
      console.log(e.target.response);
      body = JSON.parse(e.target.response);
      output.innerText = body.message;
      if (stdout) {
        stdout.innerText = body.stdout;
      }
      if (body.hasOwnProperty('img')) {
        img.src = 'data:image/png;base64,' + body.img;
      } else {
        img.src = '';
      }
      // Add the image to the history if we are on a workspace page.
      if (tryHistory) {
        addToHistory(body.hash, 'data:image/png;base64,' + body.img);
      } else {
        window.history.pushState(null, null, './' + body.hash);
      }
      if (permalink) {
        permalink.href = '/c/' + body.hash;
      }
      if (embed) {
        var url = document.URL;
        url = url.replace('/c/', '/iframe/');
        embed.value = '<iframe src="' + url + '" width="740" height="550" style="border: solid #00a 5px; border-radius: 5px;"/>'
      }
      if (embedButton && embedButton.hasAttribute('disabled')) {
        embedButton.removeAttribute('disabled');
      }
    }

    function onSubmitCode() {
      beginWait();
      clearOutput();
      var req = new XMLHttpRequest();
      req.addEventListener('load', codeComplete);
      req.addEventListener('error', xhrError);
      req.overrideMimeType('application/json');
      req.open('POST', '/', true);
      req.setRequestHeader('content-type', 'application/json');
      req.send(JSON.stringify({'code': code.value, 'name': workspaceName}));
    }
    run.addEventListener('click', onSubmitCode);

    function onEmbedClick() {
      embed.style.display='inline';
    }

    if (embedButton) {
      embedButton.addEventListener('click', onEmbedClick);
    }

    // Add the images to the history if we are on a workspace page.
    if (tryHistory && history) {
      for (var i=0; i<history.length; i++) {
        addToHistory(history[i].hash, '/i/'+history[i].hash+'.png');
      }
    }

})(workspaceName);
<commit_msg>Use textContent instead of innerText.<commit_after>/**
 * Common JS that talks XHR back to the server and runs the code and receives
 * the results.
 */

/**
 * A polyfill for HTML Templates.
 *
 * This just adds in the content attribute, it doesn't stop scripts
 * from running nor does it stop other side-effects.
 */
(function polyfillTemplates() {
  if('content' in document.createElement('template')) {
    return false;
  }

  var templates = document.getElementsByTagName('template');
  for (var i=0; i<templates.length; i++) {
    var content = document.createDocumentFragment();
    while (templates[i].firstChild) {
     content.appendChild(templates[i].firstChild);
    }
    templates[i].content = content;
  }
})();

/**
 * All the functionality is wrapped up in this anonymous closure, but we need
 * to be told if we are on the workspace page or a normal try page, so the
 * workspaceName is passed into the closure, it must be set in the global
 * namespace. If workspaceName is the empty string then we know we aren't
 * running on a workspace page.
 *
 * If we are on a workspace page we also look for a 'history'
 * variable in the global namespace which contains the list of tries
 * that are included in this workspace. That variable is used to
 * populate the history list.
 */
(function(workspaceName) {
    var run = document.getElementById('run');
    var permalink = document.getElementById('permalink');
    var embed = document.getElementById('embed');
    var embedButton = document.getElementById('embedButton');
    var code = document.getElementById('code');
    var output = document.getElementById('output');
    var stdout = document.getElementById('stdout');
    var img = document.getElementById('img');
    var tryHistory = document.getElementById('tryHistory');
    var parser = new DOMParser();
    var tryTemplate = document.getElementById('tryTemplate');

    function beginWait() {
      document.body.classList.add('waiting');
      run.disabled = true;
    }

    function endWait() {
      document.body.classList.remove('waiting');
      run.disabled = false;
    }

    /**
     * Callback when there's an XHR error.
     * @param e The callback event.
     */
    function xhrError(e) {
      endWait();
      alert('Something bad happened: ' + e);
    }

    function clearOutput() {
      output.textContent = "";
      if (stdout) {
        stdout.textContent = "";
      }
      embed.style.display='none';
    }

    /**
     * Called when an image in the workspace history is clicked.
     */
    function historyClick() {
      beginWait();
      clearOutput();
      var req = new XMLHttpRequest();
      req.addEventListener('load', historyComplete);
      req.addEventListener('error', xhrError);
      req.overrideMimeType('application/json');
      req.open('GET', this.getAttribute('data-try'), true);
      req.send();
    }

    /**
     * Callback for when the XHR kicked off in historyClick() returns.
     */
    function historyComplete(e) {
      // The response is JSON of the form:
      // {
      //   "hash": "unique id for a try",
      //   "code": "source code for try"
      // }
      endWait();
      body = JSON.parse(e.target.response);
      code.value = body.code;
      img.src = '/i/'+body.hash+'.png';
      if (permalink) {
        permalink.href = '/c/' + body.hash;
      }
    }

    /**
     * Add the given try image to the history of a workspace.
     */
    function addToHistory(hash, imgUrl) {
      var clone = tryTemplate.content.cloneNode(true);
      clone.querySelector('img').src = imgUrl;
      clone.querySelector('.tries').setAttribute('data-try', '/json/' + hash);
      tryHistory.insertBefore(clone, tryHistory.firstChild);
      tryHistory.querySelector('.tries').addEventListener('click', historyClick, true);
    }

    /**
     * Callback for when the XHR returns after attempting to run the code.
     * @param e The callback event.
     */
    function codeComplete(e) {
      // The response is JSON of the form:
      // {
      //   "message": "you had an error...",
      //   "img": "<base64 encoded image but only on success>"
      // }
      //
      // The img is optional and only appears if there is a valid
      // image to display.
      endWait();
      console.log(e.target.response);
      body = JSON.parse(e.target.response);
      output.textContent = body.message;
      if (stdout) {
        stdout.textContent = body.stdout;
      }
      if (body.hasOwnProperty('img')) {
        img.src = 'data:image/png;base64,' + body.img;
      } else {
        img.src = '';
      }
      // Add the image to the history if we are on a workspace page.
      if (tryHistory) {
        addToHistory(body.hash, 'data:image/png;base64,' + body.img);
      } else {
        window.history.pushState(null, null, './' + body.hash);
      }
      if (permalink) {
        permalink.href = '/c/' + body.hash;
      }
      if (embed) {
        var url = document.URL;
        url = url.replace('/c/', '/iframe/');
        embed.value = '<iframe src="' + url + '" width="740" height="550" style="border: solid #00a 5px; border-radius: 5px;"/>'
      }
      if (embedButton && embedButton.hasAttribute('disabled')) {
        embedButton.removeAttribute('disabled');
      }
    }

    function onSubmitCode() {
      beginWait();
      clearOutput();
      var req = new XMLHttpRequest();
      req.addEventListener('load', codeComplete);
      req.addEventListener('error', xhrError);
      req.overrideMimeType('application/json');
      req.open('POST', '/', true);
      req.setRequestHeader('content-type', 'application/json');
      req.send(JSON.stringify({'code': code.value, 'name': workspaceName}));
    }
    run.addEventListener('click', onSubmitCode);

    function onEmbedClick() {
      embed.style.display='inline';
    }

    if (embedButton) {
      embedButton.addEventListener('click', onEmbedClick);
    }

    // Add the images to the history if we are on a workspace page.
    if (tryHistory && history) {
      for (var i=0; i<history.length; i++) {
        addToHistory(history[i].hash, '/i/'+history[i].hash+'.png');
      }
    }

})(workspaceName);
<|endoftext|>


<commit_before>/*
 * This implements link hinting. Typing "F" will enter link-hinting mode, where all clickable items on
 * the page have a hint marker displayed containing a sequence of letters. Typing those letters will select
 * a link.
 *
 * The characters we use to show link hints are a user-configurable option. By default they're the home row.
 * The CSS which is used on the link hints is also a configurable option.
 */

var hintMarkers = [];
var hintMarkerContainingDiv = null;
// The characters that were typed in while in "link hints" mode.
var hintKeystrokeQueue = [];
var linkHintsModeActivated = false;
var shouldOpenLinkHintInNewTab = false;
var shouldOpenLinkHintWithQueue = false;
// Whether link hint's "open in current/new tab" setting is currently toggled
var openLinkModeToggle = false;
// Whether we have added to the page the CSS needed to display link hints.
var linkHintsCssAdded = false;

/*
 * Generate an XPath describing what a clickable element is.
 * The final expression will be something like "//button | //xhtml:button | ..."
 */
var clickableElementsXPath = (function() {
  var clickableElements = ["a", "textarea", "button", "select", "input[not(@type='hidden')]"];
  var xpath = [];
  for (var i in clickableElements)
    xpath.push("//" + clickableElements[i], "//xhtml:" + clickableElements[i]);
  xpath.push("//*[@onclick]");
  return xpath.join(" | ")
})();

// We need this as a top-level function because our command system doesn't yet support arguments.
function activateLinkHintsModeToOpenInNewTab() { activateLinkHintsMode(true, false); }

function activeteLinkHintsModeWithQueue() { activateLinkHintsMode(true, true); }

function activateLinkHintsMode(openInNewTab, withQueue) {
  if (!linkHintsCssAdded)
    addCssToPage(linkHintCss); // linkHintCss is declared by vimiumFrontend.js
  linkHintCssAdded = true;
  linkHintsModeActivated = true;
  setOpenLinkMode(openInNewTab, withQueue);
  buildLinkHints();
  document.addEventListener("keydown", onKeyDownInLinkHintsMode, true);
  document.addEventListener("keyup", onKeyUpInLinkHintsMode, true);
}

function setOpenLinkMode(openInNewTab, withQueue) {
  shouldOpenLinkHintInNewTab = openInNewTab;
  shouldOpenLinkHintWithQueue = withQueue
  if (shouldOpenLinkHintWithQueue) {
    HUD.show("Open multiple links in a new tab");
  } else {
    if (shouldOpenLinkHintInNewTab)
      HUD.show("Open link in new tab");
    else
      HUD.show("Open link in current tab");
  }
}

/*
 * Builds and displays link hints for every visible clickable item on the page.
 */
function buildLinkHints() {
  var visibleElements = getVisibleClickableElements();

  // Initialize the number used to generate the character hints to be as many digits as we need to
  // highlight all the links on the page; we don't want some link hints to have more chars than others.
  var digitsNeeded = Math.ceil(logXOfBase(visibleElements.length, settings.linkHintCharacters.length));
  var linkHintNumber = 0;
  for (var i = 0; i < visibleElements.length; i++) {
    hintMarkers.push(createMarkerFor(visibleElements[i], linkHintNumber, digitsNeeded));
    linkHintNumber++;
  }
  // Note(philc): Append these markers as top level children instead of as child nodes to the link itself,
  // because some clickable elements cannot contain children, e.g. submit buttons. This has the caveat
  // that if you scroll the page and the link has position=fixed, the marker will not stay fixed.
  // Also note that adding these nodes to document.body all at once is significantly faster than one-by-one.
  hintMarkerContainingDiv = document.createElement("div");
  hintMarkerContainingDiv.className = "internalVimiumHintMarker";
  for (var i = 0; i < hintMarkers.length; i++)
    hintMarkerContainingDiv.appendChild(hintMarkers[i]);
  document.body.appendChild(hintMarkerContainingDiv);
}

function logXOfBase(x, base) { return Math.log(x) / Math.log(base); }

/*
 * Returns all clickable elements that are not hidden and are in the current viewport.
 * We prune invisible elements partly for performance reasons, but moreso it's to decrease the number
 * of digits needed to enumerate all of the links on screen.
 */
function getVisibleClickableElements() {
  var resultSet = document.evaluate(clickableElementsXPath, document.body,
    function (namespace) {
      return namespace == "xhtml" ? "http://www.w3.org/1999/xhtml" : null;
    },
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

  var visibleElements = [];

  // Find all visible clickable elements.
  for (var i = 0; i < resultSet.snapshotLength; i++) {
    var element = resultSet.snapshotItem(i);
    var clientRect = element.getClientRects()[0];

    if (isVisible(element, clientRect))
      visibleElements.push({element: element, rect: clientRect});

    // If the link has zero dimensions, it may be wrapping visible
    // but floated elements. Check for this.
    if (clientRect && (clientRect.width == 0 || clientRect.height == 0)) {
      for (var j = 0; j < element.children.length; j++) {
        if (window.getComputedStyle(element.children[j], null).getPropertyValue('float') != 'none') {
          var childClientRect = element.children[j].getClientRects()[0];
          if (isVisible(element.children[j], childClientRect)) {
            visibleElements.push({element: element.children[j], rect: childClientRect});
            break;
          }
        }
      }
    }
  }
  return visibleElements;
}

/*
 * Returns true if element is visible.
 */
function isVisible(element, clientRect) {
  // Exclude links which have just a few pixels on screen, because the link hints won't show for them anyway.
  var zoomFactor = currentZoomLevel / 100.0;
  if (!clientRect || clientRect.top < 0 || clientRect.top * zoomFactor >= window.innerHeight - 4 ||
      clientRect.left < 0 || clientRect.left * zoomFactor >= window.innerWidth - 4)
    return false;

  if (clientRect.width < 3 || clientRect.height < 3)
    return false;

  // eliminate invisible elements (see test_harnesses/visibility_test.html)
  var computedStyle = window.getComputedStyle(element, null);
  if (computedStyle.getPropertyValue('visibility') != 'visible' ||
      computedStyle.getPropertyValue('display') == 'none')
    return false;

  return true;
}

function onKeyDownInLinkHintsMode(event) {
  console.log("Key Down");
  if (event.keyCode == keyCodes.shiftKey && !openLinkModeToggle) {
    // Toggle whether to open link in a new or current tab.
    setOpenLinkMode(!shouldOpenLinkHintInNewTab, shouldOpenLinkHintWithQueue);
    openLinkModeToggle = true;
  }

  var keyChar = getKeyChar(event);
  if (!keyChar)
    return;

  // TODO(philc): Ignore keys that have modifiers.
  if (isEscape(event)) {
    deactivateLinkHintsMode();
  } else if (event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey) {
    if (hintKeystrokeQueue.length == 0) {
      deactivateLinkHintsMode();
    } else {
      hintKeystrokeQueue.pop();
      updateLinkHints();
    }
  } else if (settings.linkHintCharacters.indexOf(keyChar) >= 0) {
    hintKeystrokeQueue.push(keyChar);
    updateLinkHints();
  } else {
    return;
  }

  event.stopPropagation();
  event.preventDefault();
}

function onKeyUpInLinkHintsMode(event) {
  if (event.keyCode == keyCodes.shiftKey && openLinkModeToggle) {
    // Revert toggle on whether to open link in new or current tab.
    setOpenLinkMode(!shouldOpenLinkHintInNewTab, shouldOpenLinkHintWithQueue);
    openLinkModeToggle = false;
  }
  event.stopPropagation();
  event.preventDefault();
}

/*
 * Updates the visibility of link hints on screen based on the keystrokes typed thus far. If only one
 * link hint remains, click on that link and exit link hints mode.
 */
function updateLinkHints() {
  var matchString = hintKeystrokeQueue.join("");
  var linksMatched = highlightLinkMatches(matchString);
  if (linksMatched.length == 0)
    deactivateLinkHintsMode();
  else if (linksMatched.length == 1) {
    var matchedLink = linksMatched[0];
    if (isSelectable(matchedLink)) {
      matchedLink.focus();
      // When focusing a textbox, put the selection caret at the end of the textbox's contents.
      matchedLink.setSelectionRange(matchedLink.value.length, matchedLink.value.length);
    } else {
      // When we're opening the link in the current tab, don't navigate to the selected link immediately;
      // we want to give the user some feedback depicting which link they've selected by focusing it.
      if (shouldOpenLinkHintWithQueue) {
        simulateClick(matchedLink);
        resetLinkHintsMode();
      } else if (shouldOpenLinkHintInNewTab) {
        simulateClick(matchedLink);
        matchedLink.focus();
        deactivateLinkHintsMode();
      } else {
        setTimeout(function() { simulateClick(matchedLink); }, 400);
        matchedLink.focus();
        deactivateLinkHintsMode();
      }
      /*if (!shouldOpenLinkHintWithQueue) {
        matchedLink.focus();
        deactivateLinkHintsMode();
      } else {
        console.log("Reseting Hint Link Mode");
        resetLinkHintsMode();
      }*/
    }
  }
}

/*
 * Selectable means the element has a text caret; this is not the same as "focusable".
 */
function isSelectable(element) {
  var selectableTypes = ["search", "text", "password"];
  return (element.tagName == "INPUT" && selectableTypes.indexOf(element.type) >= 0) ||
      element.tagName == "TEXTAREA";
}

/*
 * Hides link hints which do not match the given search string. To allow the backspace key to work, this
 * will also show link hints which do match but were previously hidden.
 */
function highlightLinkMatches(searchString) {
  var linksMatched = [];
  for (var i = 0; i < hintMarkers.length; i++) {
    var linkMarker = hintMarkers[i];
    if (linkMarker.getAttribute("hintString").indexOf(searchString) == 0) {
      if (linkMarker.style.display == "none")
        linkMarker.style.display = "";
      for (var j = 0; j < linkMarker.childNodes.length; j++)
        linkMarker.childNodes[j].className = (j >= searchString.length) ? "" : "matchingCharacter";
      linksMatched.push(linkMarker.clickableItem);
    } else {
      linkMarker.style.display = "none";
    }
  }
  return linksMatched;
}

/*
 * Converts a number like "8" into a hint string like "JK". This is used to sequentially generate all of
 * the hint text. The hint string will be "padded with zeroes" to ensure its length is equal to numHintDigits.
 */
function numberToHintString(number, numHintDigits) {
  var base = settings.linkHintCharacters.length;
  var hintString = [];
  var remainder = 0;
  do {
    remainder = number % base;
    hintString.unshift(settings.linkHintCharacters[remainder]);
    number -= remainder;
    number /= Math.floor(base);
  } while (number > 0);

  // Pad the hint string we're returning so that it matches numHintDigits.
  var hintStringLength = hintString.length;
  for (var i = 0; i < numHintDigits - hintStringLength; i++)
    hintString.unshift(settings.linkHintCharacters[0]);
  return hintString.join("");
}

function simulateClick(link) {
  var event = document.createEvent("MouseEvents");
  // When "clicking" on a link, dispatch the event with the appropriate meta key (CMD on Mac, CTRL on windows)
  // to open it in a new tab if necessary.
  var metaKey = (platform == "Mac" && shouldOpenLinkHintInNewTab);
  var ctrlKey = (platform != "Mac" && shouldOpenLinkHintInNewTab);
  event.initMouseEvent("click", true, true, window, 1, 0, 0, 0, 0, ctrlKey, false, false, metaKey, 0, null);

  // Debugging note: Firefox will not execute the link's default action if we dispatch this click event,
  // but Webkit will. Dispatching a click on an input box does not seem to focus it; we do that separately
  link.dispatchEvent(event);
}

function deactivateLinkHintsMode() {
  if (hintMarkerContainingDiv)
    hintMarkerContainingDiv.parentNode.removeChild(hintMarkerContainingDiv);
  hintMarkerContainingDiv = null;
  hintMarkers = [];
  hintKeystrokeQueue = [];
  document.removeEventListener("keydown", onKeyDownInLinkHintsMode, true);
  document.removeEventListener("keyup", onKeyUpInLinkHintsMode, true);
  linkHintsModeActivated = false;
  HUD.hide();
}

function resetLinkHintsMode() {
  deactivateLinkHintsMode();
  activeteLinkHintsModeWithQueue();
}

/*
 * Creates a link marker for the given link.
 */
function createMarkerFor(link, linkHintNumber, linkHintDigits) {
  var hintString = numberToHintString(linkHintNumber, linkHintDigits);
  var marker = document.createElement("div");
  marker.className = "internalVimiumHintMarker vimiumHintMarker";
  var innerHTML = [];
  // Make each hint character a span, so that we can highlight the typed characters as you type them.
  for (var i = 0; i < hintString.length; i++)
    innerHTML.push("<span>" + hintString[i].toUpperCase() + "</span>");
  marker.innerHTML = innerHTML.join("");
  marker.setAttribute("hintString", hintString);

  // Note: this call will be expensive if we modify the DOM in between calls.
  var clientRect = link.rect;
  // The coordinates given by the window do not have the zoom factor included since the zoom is set only on
  // the document node.
  var zoomFactor = currentZoomLevel / 100.0;
  marker.style.left = clientRect.left + window.scrollX / zoomFactor + "px";
  marker.style.top = clientRect.top  + window.scrollY / zoomFactor + "px";

  marker.clickableItem = link.element;
  return marker;
}
<commit_msg>Clean up code<commit_after>/*
 * This implements link hinting. Typing "F" will enter link-hinting mode, where all clickable items on
 * the page have a hint marker displayed containing a sequence of letters. Typing those letters will select
 * a link.
 *
 * The characters we use to show link hints are a user-configurable option. By default they're the home row.
 * The CSS which is used on the link hints is also a configurable option.
 */

var hintMarkers = [];
var hintMarkerContainingDiv = null;
// The characters that were typed in while in "link hints" mode.
var hintKeystrokeQueue = [];
var linkHintsModeActivated = false;
var shouldOpenLinkHintInNewTab = false;
var shouldOpenLinkHintWithQueue = false;
// Whether link hint's "open in current/new tab" setting is currently toggled
var openLinkModeToggle = false;
// Whether we have added to the page the CSS needed to display link hints.
var linkHintsCssAdded = false;

/*
 * Generate an XPath describing what a clickable element is.
 * The final expression will be something like "//button | //xhtml:button | ..."
 */
var clickableElementsXPath = (function() {
  var clickableElements = ["a", "textarea", "button", "select", "input[not(@type='hidden')]"];
  var xpath = [];
  for (var i in clickableElements)
    xpath.push("//" + clickableElements[i], "//xhtml:" + clickableElements[i]);
  xpath.push("//*[@onclick]");
  return xpath.join(" | ")
})();

// We need this as a top-level function because our command system doesn't yet support arguments.
function activateLinkHintsModeToOpenInNewTab() { activateLinkHintsMode(true, false); }

function activeteLinkHintsModeWithQueue() { activateLinkHintsMode(true, true); }

function activateLinkHintsMode(openInNewTab, withQueue) {
  if (!linkHintsCssAdded)
    addCssToPage(linkHintCss); // linkHintCss is declared by vimiumFrontend.js
  linkHintCssAdded = true;
  linkHintsModeActivated = true;
  setOpenLinkMode(openInNewTab, withQueue);
  buildLinkHints();
  document.addEventListener("keydown", onKeyDownInLinkHintsMode, true);
  document.addEventListener("keyup", onKeyUpInLinkHintsMode, true);
}

function setOpenLinkMode(openInNewTab, withQueue) {
  shouldOpenLinkHintInNewTab = openInNewTab;
  shouldOpenLinkHintWithQueue = withQueue
  if (shouldOpenLinkHintWithQueue) {
    HUD.show("Open multiple links in a new tab");
  } else {
    if (shouldOpenLinkHintInNewTab)
      HUD.show("Open link in new tab");
    else
      HUD.show("Open link in current tab");
  }
}

/*
 * Builds and displays link hints for every visible clickable item on the page.
 */
function buildLinkHints() {
  var visibleElements = getVisibleClickableElements();

  // Initialize the number used to generate the character hints to be as many digits as we need to
  // highlight all the links on the page; we don't want some link hints to have more chars than others.
  var digitsNeeded = Math.ceil(logXOfBase(visibleElements.length, settings.linkHintCharacters.length));
  var linkHintNumber = 0;
  for (var i = 0; i < visibleElements.length; i++) {
    hintMarkers.push(createMarkerFor(visibleElements[i], linkHintNumber, digitsNeeded));
    linkHintNumber++;
  }
  // Note(philc): Append these markers as top level children instead of as child nodes to the link itself,
  // because some clickable elements cannot contain children, e.g. submit buttons. This has the caveat
  // that if you scroll the page and the link has position=fixed, the marker will not stay fixed.
  // Also note that adding these nodes to document.body all at once is significantly faster than one-by-one.
  hintMarkerContainingDiv = document.createElement("div");
  hintMarkerContainingDiv.className = "internalVimiumHintMarker";
  for (var i = 0; i < hintMarkers.length; i++)
    hintMarkerContainingDiv.appendChild(hintMarkers[i]);
  document.body.appendChild(hintMarkerContainingDiv);
}

function logXOfBase(x, base) { return Math.log(x) / Math.log(base); }

/*
 * Returns all clickable elements that are not hidden and are in the current viewport.
 * We prune invisible elements partly for performance reasons, but moreso it's to decrease the number
 * of digits needed to enumerate all of the links on screen.
 */
function getVisibleClickableElements() {
  var resultSet = document.evaluate(clickableElementsXPath, document.body,
    function (namespace) {
      return namespace == "xhtml" ? "http://www.w3.org/1999/xhtml" : null;
    },
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

  var visibleElements = [];

  // Find all visible clickable elements.
  for (var i = 0; i < resultSet.snapshotLength; i++) {
    var element = resultSet.snapshotItem(i);
    var clientRect = element.getClientRects()[0];

    if (isVisible(element, clientRect))
      visibleElements.push({element: element, rect: clientRect});

    // If the link has zero dimensions, it may be wrapping visible
    // but floated elements. Check for this.
    if (clientRect && (clientRect.width == 0 || clientRect.height == 0)) {
      for (var j = 0; j < element.children.length; j++) {
        if (window.getComputedStyle(element.children[j], null).getPropertyValue('float') != 'none') {
          var childClientRect = element.children[j].getClientRects()[0];
          if (isVisible(element.children[j], childClientRect)) {
            visibleElements.push({element: element.children[j], rect: childClientRect});
            break;
          }
        }
      }
    }
  }
  return visibleElements;
}

/*
 * Returns true if element is visible.
 */
function isVisible(element, clientRect) {
  // Exclude links which have just a few pixels on screen, because the link hints won't show for them anyway.
  var zoomFactor = currentZoomLevel / 100.0;
  if (!clientRect || clientRect.top < 0 || clientRect.top * zoomFactor >= window.innerHeight - 4 ||
      clientRect.left < 0 || clientRect.left * zoomFactor >= window.innerWidth - 4)
    return false;

  if (clientRect.width < 3 || clientRect.height < 3)
    return false;

  // eliminate invisible elements (see test_harnesses/visibility_test.html)
  var computedStyle = window.getComputedStyle(element, null);
  if (computedStyle.getPropertyValue('visibility') != 'visible' ||
      computedStyle.getPropertyValue('display') == 'none')
    return false;

  return true;
}

function onKeyDownInLinkHintsMode(event) {
  console.log("Key Down");
  if (event.keyCode == keyCodes.shiftKey && !openLinkModeToggle) {
    // Toggle whether to open link in a new or current tab.
    setOpenLinkMode(!shouldOpenLinkHintInNewTab, shouldOpenLinkHintWithQueue);
    openLinkModeToggle = true;
  }

  var keyChar = getKeyChar(event);
  if (!keyChar)
    return;

  // TODO(philc): Ignore keys that have modifiers.
  if (isEscape(event)) {
    deactivateLinkHintsMode();
  } else if (event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey) {
    if (hintKeystrokeQueue.length == 0) {
      deactivateLinkHintsMode();
    } else {
      hintKeystrokeQueue.pop();
      updateLinkHints();
    }
  } else if (settings.linkHintCharacters.indexOf(keyChar) >= 0) {
    hintKeystrokeQueue.push(keyChar);
    updateLinkHints();
  } else {
    return;
  }

  event.stopPropagation();
  event.preventDefault();
}

function onKeyUpInLinkHintsMode(event) {
  if (event.keyCode == keyCodes.shiftKey && openLinkModeToggle) {
    // Revert toggle on whether to open link in new or current tab.
    setOpenLinkMode(!shouldOpenLinkHintInNewTab, shouldOpenLinkHintWithQueue);
    openLinkModeToggle = false;
  }
  event.stopPropagation();
  event.preventDefault();
}

/*
 * Updates the visibility of link hints on screen based on the keystrokes typed thus far. If only one
 * link hint remains, click on that link and exit link hints mode.
 */
function updateLinkHints() {
  var matchString = hintKeystrokeQueue.join("");
  var linksMatched = highlightLinkMatches(matchString);
  if (linksMatched.length == 0)
    deactivateLinkHintsMode();
  else if (linksMatched.length == 1) {
    var matchedLink = linksMatched[0];
    if (isSelectable(matchedLink)) {
      matchedLink.focus();
      // When focusing a textbox, put the selection caret at the end of the textbox's contents.
      matchedLink.setSelectionRange(matchedLink.value.length, matchedLink.value.length);
    } else {
      // When we're opening the link in the current tab, don't navigate to the selected link immediately;
      // we want to give the user some feedback depicting which link they've selected by focusing it.
      if (shouldOpenLinkHintWithQueue) {
        simulateClick(matchedLink);
        resetLinkHintsMode();
      } else if (shouldOpenLinkHintInNewTab) {
        simulateClick(matchedLink);
        matchedLink.focus();
        deactivateLinkHintsMode();
      } else {
        setTimeout(function() { simulateClick(matchedLink); }, 400);
        matchedLink.focus();
        deactivateLinkHintsMode();
      }
    }
  }
}

/*
 * Selectable means the element has a text caret; this is not the same as "focusable".
 */
function isSelectable(element) {
  var selectableTypes = ["search", "text", "password"];
  return (element.tagName == "INPUT" && selectableTypes.indexOf(element.type) >= 0) ||
      element.tagName == "TEXTAREA";
}

/*
 * Hides link hints which do not match the given search string. To allow the backspace key to work, this
 * will also show link hints which do match but were previously hidden.
 */
function highlightLinkMatches(searchString) {
  var linksMatched = [];
  for (var i = 0; i < hintMarkers.length; i++) {
    var linkMarker = hintMarkers[i];
    if (linkMarker.getAttribute("hintString").indexOf(searchString) == 0) {
      if (linkMarker.style.display == "none")
        linkMarker.style.display = "";
      for (var j = 0; j < linkMarker.childNodes.length; j++)
        linkMarker.childNodes[j].className = (j >= searchString.length) ? "" : "matchingCharacter";
      linksMatched.push(linkMarker.clickableItem);
    } else {
      linkMarker.style.display = "none";
    }
  }
  return linksMatched;
}

/*
 * Converts a number like "8" into a hint string like "JK". This is used to sequentially generate all of
 * the hint text. The hint string will be "padded with zeroes" to ensure its length is equal to numHintDigits.
 */
function numberToHintString(number, numHintDigits) {
  var base = settings.linkHintCharacters.length;
  var hintString = [];
  var remainder = 0;
  do {
    remainder = number % base;
    hintString.unshift(settings.linkHintCharacters[remainder]);
    number -= remainder;
    number /= Math.floor(base);
  } while (number > 0);

  // Pad the hint string we're returning so that it matches numHintDigits.
  var hintStringLength = hintString.length;
  for (var i = 0; i < numHintDigits - hintStringLength; i++)
    hintString.unshift(settings.linkHintCharacters[0]);
  return hintString.join("");
}

function simulateClick(link) {
  var event = document.createEvent("MouseEvents");
  // When "clicking" on a link, dispatch the event with the appropriate meta key (CMD on Mac, CTRL on windows)
  // to open it in a new tab if necessary.
  var metaKey = (platform == "Mac" && shouldOpenLinkHintInNewTab);
  var ctrlKey = (platform != "Mac" && shouldOpenLinkHintInNewTab);
  event.initMouseEvent("click", true, true, window, 1, 0, 0, 0, 0, ctrlKey, false, false, metaKey, 0, null);

  // Debugging note: Firefox will not execute the link's default action if we dispatch this click event,
  // but Webkit will. Dispatching a click on an input box does not seem to focus it; we do that separately
  link.dispatchEvent(event);
}

function deactivateLinkHintsMode() {
  if (hintMarkerContainingDiv)
    hintMarkerContainingDiv.parentNode.removeChild(hintMarkerContainingDiv);
  hintMarkerContainingDiv = null;
  hintMarkers = [];
  hintKeystrokeQueue = [];
  document.removeEventListener("keydown", onKeyDownInLinkHintsMode, true);
  document.removeEventListener("keyup", onKeyUpInLinkHintsMode, true);
  linkHintsModeActivated = false;
  HUD.hide();
}

function resetLinkHintsMode() {
  deactivateLinkHintsMode();
  activeteLinkHintsModeWithQueue();
}

/*
 * Creates a link marker for the given link.
 */
function createMarkerFor(link, linkHintNumber, linkHintDigits) {
  var hintString = numberToHintString(linkHintNumber, linkHintDigits);
  var marker = document.createElement("div");
  marker.className = "internalVimiumHintMarker vimiumHintMarker";
  var innerHTML = [];
  // Make each hint character a span, so that we can highlight the typed characters as you type them.
  for (var i = 0; i < hintString.length; i++)
    innerHTML.push("<span>" + hintString[i].toUpperCase() + "</span>");
  marker.innerHTML = innerHTML.join("");
  marker.setAttribute("hintString", hintString);

  // Note: this call will be expensive if we modify the DOM in between calls.
  var clientRect = link.rect;
  // The coordinates given by the window do not have the zoom factor included since the zoom is set only on
  // the document node.
  var zoomFactor = currentZoomLevel / 100.0;
  marker.style.left = clientRect.left + window.scrollX / zoomFactor + "px";
  marker.style.top = clientRect.top  + window.scrollY / zoomFactor + "px";

  marker.clickableItem = link.element;
  return marker;
}
<|endoftext|>


<commit_before>const http = require('http');
const twilio = require('twilio');

http.createServer((req, res) => {
  // Create TwiML response
  const twiml = new twilio.TwimlResponse();
  twiml.say('Hello! You will get an SMS message soon.');
  twiml.sms('This is the ship that made the Kessel Run in fourteen parsecs?');

  res.writeHead(200, {'Content-Type': 'text/xml'});
  res.end(twiml.toString());
}).listen(1337, '127.0.0.1');

console.log('TwiML servin\' server running at http://127.0.0.1:1337/');
<commit_msg>Update a comment /rest/messages/generate-twiml-sms-voice<commit_after>const http = require('http');
const twilio = require('twilio');

http.createServer((req, res) => {
  // Create TwiML response
  const twiml = new twilio.TwimlResponse();
  twiml.say('Hello! You will get an SMS message soon.');
  twiml.sms('This is the ship that made the Kessel Run in fourteen parsecs?');

  res.writeHead(200, {'Content-Type': 'text/xml'});
  res.end(twiml.toString());
}).listen(1337, '127.0.0.1');

console.log('TwiML server running at http://127.0.0.1:1337/');
<|endoftext|>


<commit_before>// test/test.js
var assert = require("assert");

describe('Array', function(){
    describe('#indexOf()', function(){
        it('should return -1 when the value is not present', function(){
            assert.equal(-1, [1,2,3].indexOf(0));
            assert.equal(3, [1,2,3].indexOf(5));
        });
    });
});
<commit_msg>Fix error<commit_after>// test/test.js
var assert = require("assert");

describe('Array', function(){
    describe('#indexOf()', function(){
        it('should return -1 when the value is not present', function(){
            assert.equal(-1, [1,2,3].indexOf(0));
            assert.equal(-1, [1,2,3].indexOf(5));
        });
    });
});
<|endoftext|>