<commit_before>/* global Player, Projectile, Zombie, Explosion, ObjectPoolMaker */

(function () {
  'use strict';

  var Game = {
    // Current game difficulty
    difficulty: 0.01,

    // Max difficulty that Game can reach at any given time
    // It represents the chance percentage of spawning a new Zombie
    maxDifficulty: 0.2,

    // How much difficulty increases after its cooldown elapsed
    difficultyIncrement: 0.005,

    // How long it takes (in seconds) to difficulty to increase
    difficultyCooldown: 3,

    // Last time difficulty was increased
    lastDifficultyIncrease: 0,

    // Time elapsed since Game began
    gameTime: 0,

    // Maps which keys as currently pressed by e.keyCode
    keysDown: {},

    // Last time a projectile was shot by the player
    lastProjectileTime: 0,

    // How long it takes (in ms) for another projectile to be fired
    projectileCooldown: 100,

    // How many pixels Player and Zombies are constrained by vertically/horizontally
    // (this is interesting according to the background image used)
    verticalBoundary: 35,
    horizontalBoundary: 5,

    isGameOver: false,

    init: function () {
      Game.canvas = document.getElementById('game-canvas');
      Game.context = Game.canvas.getContext('2d');

      Game.loadBackground();

      Game.player = new Player(
        Game.canvas.width / 2 - Player.width / 2,
        Game.canvas.height / 2 - Player.height / 2
      );

      Game.zombiePool = new ObjectPoolMaker(Zombie, 100);
      Game.projectilePool = new ObjectPoolMaker(Projectile, 100);
      Game.explosions = [];

      Game.bind();
    },

    bind: function () {
      document.addEventListener('keydown', function (e) {
        Game.keysDown[e.keyCode] = true;
      }, false);

      document.addEventListener('keyup', function (e) {
        delete Game.keysDown[e.keyCode];
      }, false);
    },

    loadBackground: function () {
      Game.backgroundReady = false;

      Game.backgroundImg = new Image();
      Game.backgroundImg.src = 'public/images/background.png';

      Game.backgroundImg.onload = function () {
        Game.backgroundReady = true;
      };
    },

    update: function (modifier) {
      Game.gameTime += modifier;

      Game.handleInput(modifier);
      Game.handleDifficulty();
      Game.spawnZombies();
      Game.updateEntities(modifier);
    },

    handleInput: function (modifier) {
      if (Game.isGameOver) {
        return;
      }

      var now = Date.now();

      // UP
      if (Game.keysDown[38] && Game.player.y > Game.verticalBoundary) {
        Game.player.setY(Game.player.y - (Game.player.speed * modifier));
      }

      // DOWN
      if (Game.keysDown[40] && Game.player.y < Game.canvas.height - Game.verticalBoundary - Player.height) {
        Game.player.setY(Game.player.y + (Game.player.speed * modifier));
      }

      // RIGHT
      if (Game.keysDown[39] && Game.player.x < Game.canvas.width - Game.horizontalBoundary - Player.width) {
        Game.player.setX(Game.player.x + (Game.player.speed * modifier));
      }

      // LEFT
      if (Game.keysDown[37] && Game.player.x > Game.horizontalBoundary) {
        Game.player.setX(Game.player.x - (Game.player.speed * modifier));
      }

      // SPACE - shoot!
      if (Game.keysDown[32]) {
        // prevent spamming projectiles
        if (now - Game.lastProjectileTime > Game.projectileCooldown) {
          Game.projectilePool.create(
            Game.player.x,
            Game.player.y + (Player.height / 2) - (Projectile.height / 2)
          );
          Game.lastProjectileTime = now;
        }
      }
    },

    handleDifficulty: function () {
      // It gets harder as time goes by..
      if (Game.difficulty < Game.maxDifficulty &&
          Game.gameTime - Game.lastDifficultyIncrease > Game.difficultyCooldown) {
        Game.difficulty += Game.difficultyIncrement;
        Game.lastDifficultyIncrease = Game.gameTime;
      }
    },

    spawnZombies: function () {
      // Create some enemies
      if (Math.random() < Game.difficulty) {
        // resets x coordinate
        // randomize new y coordinate (constrained in available map space)
        Game.zombiePool.create(
          Game.canvas.width,
          Math.random() * (Game.canvas.height - Zombie.height - (2 * Game.verticalBoundary)) + Game.verticalBoundary
        );
      }
    },

    updateEntities: function (modifier) {
      Game.updateProjectilesAndKillZombies(modifier);
      Game.updateZombies(modifier);
      Game.updateExplosions();
      Game.updatePlayer();
    },

    updateProjectilesAndKillZombies: function (modifier) {
      var i,
          j,
          zombie,
          projectile;

      // update all projectiles
      for (i = 0; i < Game.projectilePool.size(); i++) {
        projectile = Game.projectilePool.objectPool()[i];

        projectile.setX(projectile.x + (projectile.speed * modifier));

        // delete projectile if out of the scene
        if (projectile.x > Game.canvas.width) {
          Game.projectilePool.destroy(projectile);
          i--;
        }
        else {
          // kill zombies!
          for (j = 0; j < Game.zombiePool.size(); j++) {
            zombie = Game.zombiePool.objectPool()[j];
            if (projectile.isCollided(zombie)) {
              Game.explosions.push(new Explosion(zombie.x, zombie.y));

              Game.projectilePool.destroy(projectile);
              i--;
              Game.zombiePool.destroy(zombie);
              j--;

              break;
            }
          }
        }
      }
    },

    updateZombies: function (modifier) {
      var zombie,
          i,
          now = Date.now();

      // update all enemies
      for (i = 0; i < Game.zombiePool.size(); i++) {
        zombie = Game.zombiePool.objectPool()[i];
        zombie.update(now);
        zombie.setX(zombie.x - (zombie.speed * modifier));

        // delete zombie if out of the scene
        if (zombie.x + Zombie.width < 0) {
          Game.zombiePool.destroy(zombie);
          i--;
        }
      }
    },

    updateExplosions: function () {
      var i;

      for (i = 0; i < Game.explosions.length; ++i) {
        Game.explosions[i].update();
        if (Game.explosions[i].currentFrame >= Explosion.framesPosition.length - 1) {
          Game.explosions.splice(i--, 1);
        }
      }
    },

    updatePlayer: function () {
      var zombie,
          i;

      if (!Game.isGameOver) {
        for (i = 0; i < Game.zombiePool.size(); ++i) {
          zombie = Game.zombiePool.objectPool()[i];
          if (zombie.isCollided(Game.player)) {
            Game.gameOver();
            break;
          }
        }
      }
    },

    gameOver: function () {
      document.getElementById('game-over-overlay').style.display = 'block';
      document.getElementById('game-over').style.display = 'block';
      Game.isGameOver = true;
    },

    render: function () {
      var i;

      // Render Background
      if (Game.backgroundReady) {
        Game.context.drawImage(Game.backgroundImg, 0, 0);
      }

      // Render Projectiles
      for (i = 0; i < Game.projectilePool.size(); ++i) {
        Game.projectilePool.objectPool()[i].render(Game.context);
      }

      // Render Explosions
      for (i = 0; i < Game.explosions.length; ++i) {
        Game.explosions[i].render(Game.context);
      }

      // Render Zombies
      for (i = 0; i < Game.zombiePool.size(); ++i) {
        Game.zombiePool.objectPool()[i].render(Game.context);
      }

      // Render Player
      if (!Game.isGameOver) {
        Game.player.render(Game.context);
      }
    }
  };

  // expose Game globally
  window.Game = Game;
}());
<commit_msg>Allow game to be played again!<commit_after>/* global Player, Projectile, Zombie, Explosion, ObjectPoolMaker */

(function () {
  'use strict';

  var Game = {
    init: function () {
      Game.canvas = document.getElementById('game-canvas');
      Game.context = Game.canvas.getContext('2d');

      Game.loadBackground();
      Game.bind();
      Game.reset();
    },

    bind: function () {
      document.addEventListener('keydown', function (e) {
        Game.keysDown[e.keyCode] = true;
      }, false);

      document.addEventListener('keyup', function (e) {
        delete Game.keysDown[e.keyCode];
      }, false);

      document.getElementById('play-again').addEventListener('click', function (e) {
        Game.reset();
      });
    },

    reset: function () {
      // Current game difficulty
      Game.difficulty = 0.01;

      // Max difficulty that Game can reach at any given time
      // It represents the chance percentage of spawning a new Zombie
      Game.maxDifficulty = 0.2;

      // How much difficulty increases after its cooldown elapsed
      Game.difficultyIncrement = 0.005;

      // How long it takes (in seconds) to difficulty to increase
      Game.difficultyCooldown = 3;

      // Last time difficulty was increased
      Game.lastDifficultyIncrease = 0;

      // Time elapsed since Game began
      Game.gameTime = 0;

      // Maps which keys as currently pressed by e.keyCode
      Game.keysDown = {};

      // Last time a projectile was shot by the player
      Game.lastProjectileTime = 0;

      // How long it takes (in ms) for another projectile to be fired
      Game.projectileCooldown = 100;

      // How many pixels Player and Zombies are constrained by vertically/horizontally
      // (this is interesting according to the background image used)
      Game.verticalBoundary = 35;
      Game.horizontalBoundary = 5;

      Game.isGameOver = false;

      Game.player = new Player(
        Game.canvas.width / 2 - Player.width / 2,
        Game.canvas.height / 2 - Player.height / 2
      );

      // Store pool of objects to be reused for each Entity
      Game.zombiePool = new ObjectPoolMaker(Zombie, 100);
      Game.projectilePool = new ObjectPoolMaker(Projectile, 100);

      // Got lazy here and just used an array.. soon to be updated :p
      Game.explosions = [];

      document.getElementById('game-over-overlay').style.display = 'none';
      document.getElementById('game-over').style.display = 'none';
    },

    loadBackground: function () {
      Game.backgroundReady = false;

      Game.backgroundImg = new Image();
      Game.backgroundImg.src = 'public/images/background.png';

      Game.backgroundImg.onload = function () {
        Game.backgroundReady = true;
      };
    },

    update: function (modifier) {
      Game.gameTime += modifier;

      Game.handleInput(modifier);
      Game.handleDifficulty();
      Game.spawnZombies();
      Game.updateEntities(modifier);
    },

    handleInput: function (modifier) {
      if (Game.isGameOver) {
        return;
      }

      var now = Date.now();

      // UP
      if (Game.keysDown[38] && Game.player.y > Game.verticalBoundary) {
        Game.player.setY(Game.player.y - (Game.player.speed * modifier));
      }

      // DOWN
      if (Game.keysDown[40] && Game.player.y < Game.canvas.height - Game.verticalBoundary - Player.height) {
        Game.player.setY(Game.player.y + (Game.player.speed * modifier));
      }

      // RIGHT
      if (Game.keysDown[39] && Game.player.x < Game.canvas.width - Game.horizontalBoundary - Player.width) {
        Game.player.setX(Game.player.x + (Game.player.speed * modifier));
      }

      // LEFT
      if (Game.keysDown[37] && Game.player.x > Game.horizontalBoundary) {
        Game.player.setX(Game.player.x - (Game.player.speed * modifier));
      }

      // SPACE - shoot!
      if (Game.keysDown[32]) {
        // prevent spamming projectiles
        if (now - Game.lastProjectileTime > Game.projectileCooldown) {
          Game.projectilePool.create(
            Game.player.x,
            Game.player.y + (Player.height / 2) - (Projectile.height / 2)
          );
          Game.lastProjectileTime = now;
        }
      }
    },

    handleDifficulty: function () {
      // It gets harder as time goes by..
      if (Game.difficulty < Game.maxDifficulty &&
          Game.gameTime - Game.lastDifficultyIncrease > Game.difficultyCooldown) {
        Game.difficulty += Game.difficultyIncrement;
        Game.lastDifficultyIncrease = Game.gameTime;
      }
    },

    spawnZombies: function () {
      // Create some enemies
      if (Math.random() < Game.difficulty) {
        // resets x coordinate
        // randomize new y coordinate (constrained in available map space)
        Game.zombiePool.create(
          Game.canvas.width,
          Math.random() * (Game.canvas.height - Zombie.height - (2 * Game.verticalBoundary)) + Game.verticalBoundary
        );
      }
    },

    updateEntities: function (modifier) {
      Game.updateProjectilesAndKillZombies(modifier);
      Game.updateZombies(modifier);
      Game.updateExplosions();
      Game.updatePlayer();
    },

    updateProjectilesAndKillZombies: function (modifier) {
      var i,
          j,
          zombie,
          projectile;

      // update all projectiles
      for (i = 0; i < Game.projectilePool.size(); i++) {
        projectile = Game.projectilePool.objectPool()[i];

        projectile.setX(projectile.x + (projectile.speed * modifier));

        // delete projectile if out of the scene
        if (projectile.x > Game.canvas.width) {
          Game.projectilePool.destroy(projectile);
          i--;
        }
        else {
          // kill zombies!
          for (j = 0; j < Game.zombiePool.size(); j++) {
            zombie = Game.zombiePool.objectPool()[j];
            if (projectile.isCollided(zombie)) {
              Game.explosions.push(new Explosion(zombie.x, zombie.y));

              Game.projectilePool.destroy(projectile);
              i--;
              Game.zombiePool.destroy(zombie);
              j--;

              break;
            }
          }
        }
      }
    },

    updateZombies: function (modifier) {
      var zombie,
          i,
          now = Date.now();

      // update all enemies
      for (i = 0; i < Game.zombiePool.size(); i++) {
        zombie = Game.zombiePool.objectPool()[i];
        zombie.update(now);
        zombie.setX(zombie.x - (zombie.speed * modifier));

        // delete zombie if out of the scene
        if (zombie.x + Zombie.width < 0) {
          Game.zombiePool.destroy(zombie);
          i--;
        }
      }
    },

    updateExplosions: function () {
      var i;

      for (i = 0; i < Game.explosions.length; ++i) {
        Game.explosions[i].update();
        if (Game.explosions[i].currentFrame >= Explosion.framesPosition.length - 1) {
          Game.explosions.splice(i--, 1);
        }
      }
    },

    updatePlayer: function () {
      var zombie,
          i;

      if (!Game.isGameOver) {
        for (i = 0; i < Game.zombiePool.size(); ++i) {
          zombie = Game.zombiePool.objectPool()[i];
          if (zombie.isCollided(Game.player)) {
            Game.gameOver();
            break;
          }
        }
      }
    },

    gameOver: function () {
      document.getElementById('game-over-overlay').style.display = 'block';
      document.getElementById('game-over').style.display = 'block';
      Game.isGameOver = true;
    },

    render: function () {
      var i;

      // Render Background
      if (Game.backgroundReady) {
        Game.context.drawImage(Game.backgroundImg, 0, 0);
      }

      // Render Projectiles
      for (i = 0; i < Game.projectilePool.size(); ++i) {
        Game.projectilePool.objectPool()[i].render(Game.context);
      }

      // Render Explosions
      for (i = 0; i < Game.explosions.length; ++i) {
        Game.explosions[i].render(Game.context);
      }

      // Render Zombies
      for (i = 0; i < Game.zombiePool.size(); ++i) {
        Game.zombiePool.objectPool()[i].render(Game.context);
      }

      // Render Player
      if (!Game.isGameOver) {
        Game.player.render(Game.context);
      }
    }
  };

  // expose Game globally
  window.Game = Game;
}());
<|endoftext|>


<commit_before>// This is a bi-directional infinite scroller.
// As the beginning & end are reached, the dates are recalculated and the current
// index adjusted to match the previous visible date.
// RecyclerListView helps to efficiently recycle instances, but the data that
// it's fed is finite. Hence the data must be shifted at the ends to appear as
// an infinite scroller.

import React, { Component } from "react";
import { View } from "react-native";
import PropTypes from "prop-types";
import { RecyclerListView, DataProvider, LayoutProvider } from "recyclerlistview";
import moment from "moment";

export default class CalendarScroller extends Component {
  static propTypes = {
    data: PropTypes.array.isRequired,
    initialRenderIndex: PropTypes.number,
    renderDay: PropTypes.func,
    renderDayParams: PropTypes.object.isRequired,
    minDate: PropTypes.any,
    maxDate: PropTypes.any,
    maxSimultaneousDays: PropTypes.number,
    updateMonthYear: PropTypes.func,
    onWeekChanged: PropTypes.func,
  }

  static defaultProps = {
    data: [],
    renderDayParams: {},
  };

  constructor(props) {
    super(props);

    this.updateLayout = renderDayParams => {
      const itemHeight = renderDayParams.size;
      const itemWidth = itemHeight + renderDayParams.marginHorizontal * 2;

      const layoutProvider = new LayoutProvider(
        index => 0, // only 1 view type
        (type, dim) => {
          // Square, plus horizontal margin
          dim.width = itemWidth;
          dim.height = itemHeight;
        }
      );

      return { layoutProvider, itemHeight, itemWidth };
    }

    this.dataProvider = new DataProvider((r1, r2) => {
      return r1 !== r2;
    });

    this.updateDaysData = data => {
      return {
        data,
        numDays: data.length,
        dataProvider: this.dataProvider.cloneWithRows(data),
      }
    }

    this.state = {
      ...this.updateLayout(props.renderDayParams),
      ...this.updateDaysData(props.data),
      numVisibleItems: 1, // updated in onLayout
    };
  }

  componentDidUpdate(prevProps, prevState) {
    let newState = {};
    let updateState = false;

    if (this.props.renderDayParams.size !== prevProps.renderDayParams.size) {
      updateState = true;
      newState = this.updateLayout(this.props.renderDayParams);
    }

    if (this.props.data !== prevProps.data) {
      updateState = true;
      newState = {...newState, ...this.updateDaysData(this.props.data)};
    }

    if (updateState) {
      this.setState(newState);
    }
  }

  // Scroll left, guarding against start index.
  scrollLeft = () => {
    if (this.state.visibleStartIndex === 0) {
      return;
    }
    const newIndex = Math.max(this.state.visibleStartIndex - this.state.numVisibleItems, 0);
    this.rlv.scrollToIndex(newIndex, true);
  }

  // Scroll right, guarding against end index.
  scrollRight = () => {
    const endIndex = this.state.visibleStartIndex + this.state.numVisibleItems;
    if (endIndex === (this.state.numDays - 1)) {
      return;
    }
    const newIndex = Math.min(endIndex, this.state.numDays - 1 - this.state.numVisibleItems);
    this.rlv.scrollToIndex(newIndex, true);
  }

  // Shift dates when end of list is reached.
  shiftDaysForward = (visibleStartDate = this.state.visibleStartDate) => {
    const prevVisStart = visibleStartDate.clone();
    const newStartDate = prevVisStart.clone().subtract(Math.floor(this.state.numDays / 3), "days");
    this.updateDays(prevVisStart, newStartDate);
  }

  // Shift dates when beginning of list is reached.
  shiftDaysBackward = (visibleStartDate) => {
    const prevVisStart = visibleStartDate.clone();
    const newStartDate = prevVisStart.clone().subtract(Math.floor(this.state.numDays * 2/3), "days");
    this.updateDays(prevVisStart, newStartDate);
  }

  updateDays = (prevVisStart, newStartDate) => {
    if (this.shifting) {
      return;
    }
    const {
      minDate,
      maxDate,
    } = this.props;
    const data = [];
    let _newStartDate = newStartDate;
    if (minDate && newStartDate.isBefore(minDate, "day")) {
      _newStartDate = moment(minDate);
    }
    for (let i = 0; i < this.state.numDays; i++) {
      let date = _newStartDate.clone().add(i, "days");
      if (maxDate && date.isAfter(maxDate, "day")) {
        break;
      }
      data.push({date});
    }
    // Prevent reducing range when the minDate - maxDate range is small.
    if (data.length < this.props.maxSimultaneousDays) {
      return;
    }

    // Scroll to previous date
    for (let i = 0; i < data.length; i++) {
      if (data[i].date.isSame(prevVisStart, "day")) {
        this.shifting = true;
        this.rlv.scrollToIndex(i, false);
        // RecyclerListView sometimes returns position to old index after
        // moving to the new one. Set position again after delay.
        setTimeout(() => {
          this.rlv.scrollToIndex(i, false);
          this.shifting = false; // debounce
        }, 800);
        break;
      }
    }
    this.setState({
      data,
      dataProvider: this.dataProvider.cloneWithRows(data),
    });
  }

  // Track which dates are visible.
  onVisibleIndicesChanged = (all, now, notNow) => {
    const {
      data,
      numDays,
      numVisibleItems,
      visibleStartDate: _visStartDate,
      visibleEndDate: _visEndDate,
    } = this.state;
    const visibleStartIndex = all[0];
    const visibleStartDate = data[visibleStartIndex].date;
    const visibleEndIndex = Math.min(visibleStartIndex + numVisibleItems - 1, data.length - 1);
    const visibleEndDate = data[visibleEndIndex].date;

    const {
      updateMonthYear,
      onWeekChanged,
    } = this.props;

    // Fire month/year update on both week and month changes.  This is
    // necessary for the header and onWeekChanged updates.
    if (!_visStartDate || !_visEndDate ||
        !visibleStartDate.isSame(_visStartDate, "week") ||
        !visibleEndDate.isSame(_visEndDate, "week") ||
        !visibleStartDate.isSame(_visStartDate, "month") ||
        !visibleEndDate.isSame(_visEndDate, "month") )
    {
      const visStart = visibleStartDate && visibleStartDate.clone();
      const visEnd = visibleEndDate && visibleEndDate.clone();
      updateMonthYear && updateMonthYear(visStart, visEnd);
      onWeekChanged && onWeekChanged(visStart, visEnd);
    }

    if (visibleStartIndex === 0) {
      this.shiftDaysBackward(visibleStartDate);
    } else {
      const minEndOffset = numDays - numVisibleItems;
      if (minEndOffset > numVisibleItems) {
        for (let a of all) {
          if (a > minEndOffset) {
            this.shiftDaysForward(visibleStartDate);
            break;
          }
        }
      }
    }
    this.setState({
      visibleStartDate,
      visibleEndDate,
      visibleStartIndex,
    });
  }

  onLayout = event => {
    let width = event.nativeEvent.layout.width;
    this.setState({
      numVisibleItems: Math.floor(width / this.state.itemWidth),
    });
  }

  rowRenderer = (type, data, i, extState) => {
    return this.props.renderDay && this.props.renderDay({...data, ...extState});
  }

  render() {
    if (!this.state.data || this.state.numDays === 0 || !this.state.itemHeight) {
      return null;
    }
    return (
      <View
        style={{ height: this.state.itemHeight, flex: 1 }}
        onLayout={this.onLayout}
      >
        <RecyclerListView
          ref={rlv => this.rlv = rlv}
          layoutProvider={this.state.layoutProvider}
          dataProvider={this.state.dataProvider}
          rowRenderer={this.rowRenderer}
          extendedState={this.props.renderDayParams}
          initialRenderIndex={this.props.initialRenderIndex}
          onVisibleIndicesChanged={this.onVisibleIndicesChanged}
          isHorizontal
          scrollViewProps={{
            showsHorizontalScrollIndicator: false,
            contentContainerStyle: {paddingRight: this.state.itemWidth / 2},
          }}
        />
      </View>
    );
  }
}
<commit_msg>Fix scrollable nav arrows at min/maxDate edges (#202).<commit_after>// This is a bi-directional infinite scroller.
// As the beginning & end are reached, the dates are recalculated and the current
// index adjusted to match the previous visible date.
// RecyclerListView helps to efficiently recycle instances, but the data that
// it's fed is finite. Hence the data must be shifted at the ends to appear as
// an infinite scroller.

import React, { Component } from "react";
import { View } from "react-native";
import PropTypes from "prop-types";
import { RecyclerListView, DataProvider, LayoutProvider } from "recyclerlistview";
import moment from "moment";

export default class CalendarScroller extends Component {
  static propTypes = {
    data: PropTypes.array.isRequired,
    initialRenderIndex: PropTypes.number,
    renderDay: PropTypes.func,
    renderDayParams: PropTypes.object.isRequired,
    minDate: PropTypes.any,
    maxDate: PropTypes.any,
    maxSimultaneousDays: PropTypes.number,
    updateMonthYear: PropTypes.func,
    onWeekChanged: PropTypes.func,
  }

  static defaultProps = {
    data: [],
    renderDayParams: {},
  };

  constructor(props) {
    super(props);

    this.updateLayout = renderDayParams => {
      const itemHeight = renderDayParams.size;
      const itemWidth = itemHeight + renderDayParams.marginHorizontal * 2;

      const layoutProvider = new LayoutProvider(
        index => 0, // only 1 view type
        (type, dim) => {
          // Square, plus horizontal margin
          dim.width = itemWidth;
          dim.height = itemHeight;
        }
      );

      return { layoutProvider, itemHeight, itemWidth };
    }

    this.dataProvider = new DataProvider((r1, r2) => {
      return r1 !== r2;
    });

    this.updateDaysData = data => {
      return {
        data,
        numDays: data.length,
        dataProvider: this.dataProvider.cloneWithRows(data),
      }
    }

    this.state = {
      ...this.updateLayout(props.renderDayParams),
      ...this.updateDaysData(props.data),
      numVisibleItems: 1, // updated in onLayout
    };
  }

  componentDidUpdate(prevProps, prevState) {
    let newState = {};
    let updateState = false;

    if (this.props.renderDayParams.size !== prevProps.renderDayParams.size) {
      updateState = true;
      newState = this.updateLayout(this.props.renderDayParams);
    }

    if (this.props.data !== prevProps.data) {
      updateState = true;
      newState = {...newState, ...this.updateDaysData(this.props.data)};
    }

    if (updateState) {
      this.setState(newState);
    }
  }

  // Scroll left, guarding against start index.
  scrollLeft = () => {
    if (this.state.visibleStartIndex === 0) {
      return;
    }
    const newIndex = Math.max(this.state.visibleStartIndex - this.state.numVisibleItems, 0);
    this.rlv.scrollToIndex(newIndex, true);
  }

  // Scroll right, guarding against end index.
  scrollRight = () => {
    const newIndex = this.state.visibleStartIndex + this.state.numVisibleItems;
    if (newIndex >= (this.state.numDays - 1)) {
      this.rlv.scrollToEnd(true); // scroll to the very end, including padding
      return;
    }
    this.rlv.scrollToIndex(newIndex, true);
  }

  // Shift dates when end of list is reached.
  shiftDaysForward = (visibleStartDate = this.state.visibleStartDate) => {
    const prevVisStart = visibleStartDate.clone();
    const newStartDate = prevVisStart.clone().subtract(Math.floor(this.state.numDays / 3), "days");
    this.updateDays(prevVisStart, newStartDate);
  }

  // Shift dates when beginning of list is reached.
  shiftDaysBackward = (visibleStartDate) => {
    const prevVisStart = visibleStartDate.clone();
    const newStartDate = prevVisStart.clone().subtract(Math.floor(this.state.numDays * 2/3), "days");
    this.updateDays(prevVisStart, newStartDate);
  }

  updateDays = (prevVisStart, newStartDate) => {
    if (this.shifting) {
      return;
    }
    const {
      minDate,
      maxDate,
    } = this.props;
    const data = [];
    let _newStartDate = newStartDate;
    if (minDate && newStartDate.isBefore(minDate, "day")) {
      _newStartDate = moment(minDate);
    }
    for (let i = 0; i < this.state.numDays; i++) {
      let date = _newStartDate.clone().add(i, "days");
      if (maxDate && date.isAfter(maxDate, "day")) {
        break;
      }
      data.push({date});
    }
    // Prevent reducing range when the minDate - maxDate range is small.
    if (data.length < this.props.maxSimultaneousDays) {
      return;
    }

    // Scroll to previous date
    for (let i = 0; i < data.length; i++) {
      if (data[i].date.isSame(prevVisStart, "day")) {
        this.shifting = true;
        this.rlv.scrollToIndex(i, false);
        // RecyclerListView sometimes returns position to old index after
        // moving to the new one. Set position again after delay.
        setTimeout(() => {
          this.rlv.scrollToIndex(i, false);
          this.shifting = false; // debounce
        }, 800);
        break;
      }
    }
    this.setState({
      data,
      dataProvider: this.dataProvider.cloneWithRows(data),
    });
  }

  // Track which dates are visible.
  onVisibleIndicesChanged = (all, now, notNow) => {
    const {
      data,
      numDays,
      numVisibleItems,
      visibleStartDate: _visStartDate,
      visibleEndDate: _visEndDate,
    } = this.state;
    const visibleStartIndex = all[0];
    const visibleStartDate = data[visibleStartIndex].date;
    const visibleEndIndex = Math.min(visibleStartIndex + numVisibleItems - 1, data.length - 1);
    const visibleEndDate = data[visibleEndIndex].date;

    const {
      updateMonthYear,
      onWeekChanged,
    } = this.props;

    // Fire month/year update on both week and month changes.  This is
    // necessary for the header and onWeekChanged updates.
    if (!_visStartDate || !_visEndDate ||
        !visibleStartDate.isSame(_visStartDate, "week") ||
        !visibleEndDate.isSame(_visEndDate, "week") ||
        !visibleStartDate.isSame(_visStartDate, "month") ||
        !visibleEndDate.isSame(_visEndDate, "month") )
    {
      const visStart = visibleStartDate && visibleStartDate.clone();
      const visEnd = visibleEndDate && visibleEndDate.clone();
      onWeekChanged && onWeekChanged(visStart, visEnd);
    }

    // Always update weekstart/end for WeekSelectors.
    updateMonthYear && updateMonthYear(visibleStartDate, visibleEndDate);

    if (visibleStartIndex === 0) {
      this.shiftDaysBackward(visibleStartDate);
    } else {
      const minEndOffset = numDays - numVisibleItems;
      if (minEndOffset > numVisibleItems) {
        for (let a of all) {
          if (a > minEndOffset) {
            this.shiftDaysForward(visibleStartDate);
            break;
          }
        }
      }
    }
    this.setState({
      visibleStartDate,
      visibleEndDate,
      visibleStartIndex,
    });
  }

  onLayout = event => {
    let width = event.nativeEvent.layout.width;
    this.setState({
      numVisibleItems: Math.floor(width / this.state.itemWidth),
    });
  }

  rowRenderer = (type, data, i, extState) => {
    return this.props.renderDay && this.props.renderDay({...data, ...extState});
  }

  render() {
    if (!this.state.data || this.state.numDays === 0 || !this.state.itemHeight) {
      return null;
    }
    return (
      <View
        style={{ height: this.state.itemHeight, flex: 1 }}
        onLayout={this.onLayout}
      >
        <RecyclerListView
          ref={rlv => this.rlv = rlv}
          layoutProvider={this.state.layoutProvider}
          dataProvider={this.state.dataProvider}
          rowRenderer={this.rowRenderer}
          extendedState={this.props.renderDayParams}
          initialRenderIndex={this.props.initialRenderIndex}
          onVisibleIndicesChanged={this.onVisibleIndicesChanged}
          isHorizontal
          scrollViewProps={{
            showsHorizontalScrollIndicator: false,
            contentContainerStyle: {paddingRight: this.state.itemWidth / 2},
          }}
        />
      </View>
    );
  }
}
<|endoftext|>


<commit_before>window.hashString = function(str, callback) {
        cordova.exec(callback, function(err) {
            callback('Nothing to hashString.');
        }, "Hash", "hashString", [str]);
    };

window.hashFile = function(str, callback) {
        cordova.exec(callback, function(err) {
            callback('Nothing to hashFile.');
        }, "Hash", "hashFile", [str]);
    };
<commit_msg>Delete Hash.js<commit_after><|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import Truncate from 'react-truncate';

class ShowMore extends Component {
    static defaultProps = {
        lines: 3,
        more: 'Show more',
        less: 'Show less',
        anchorClass: ''
    }

    static propTypes = {
        children: PropTypes.node.isRequired,
        lines: PropTypes.number,
        more: PropTypes.node,
        less: PropTypes.node,
        anchorClass: PropTypes.string
    }

    state = {
        expanded: false,
        truncated: false
    }

    handleTruncate = truncated => {
        if (truncated !== this.state.truncated) {
            this.setState({
                truncated
            });
        }
    }

    toggleLines = event => {
        event.preventDefault();

        this.setState({
            expanded: !this.state.expanded
        });
    }

    render() {
        const {
            children,
            more,
            less,
            lines,
            anchorClass
        } = this.props;

        const {
            expanded,
            truncated
        } = this.state;

        return (

                <Truncate
                    lines={!expanded && lines}
                    ellipsis={(
                        ... {more}
                    )}
                    onTruncate={this.handleTruncate}
                >
                    {children}
                </Truncate>
                {!truncated && expanded && (
                     {less}
                )}

        );
    }
}

export default ShowMore;
<commit_msg>Make "children" an optional prop<commit_after>import React, { Component, PropTypes } from 'react';
import Truncate from 'react-truncate';

class ShowMore extends Component {
    static defaultProps = {
        lines: 3,
        more: 'Show more',
        less: 'Show less',
        anchorClass: ''
    }

    static propTypes = {
        children: PropTypes.node,
        lines: PropTypes.number,
        more: PropTypes.node,
        less: PropTypes.node,
        anchorClass: PropTypes.string
    }

    state = {
        expanded: false,
        truncated: false
    }

    handleTruncate = truncated => {
        if (truncated !== this.state.truncated) {
            this.setState({
                truncated
            });
        }
    }

    toggleLines = event => {
        event.preventDefault();

        this.setState({
            expanded: !this.state.expanded
        });
    }

    render() {
        const {
            children,
            more,
            less,
            lines,
            anchorClass
        } = this.props;

        const {
            expanded,
            truncated
        } = this.state;

        return (

                <Truncate
                    lines={!expanded && lines}
                    ellipsis={(
                        ... {more}
                    )}
                    onTruncate={this.handleTruncate}
                >
                    {children}
                </Truncate>
                {!truncated && expanded && (
                     {less}
                )}

        );
    }
}

export default ShowMore;
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');

var ArrayStore = function(datasetId, data) {
  this.data = data;
  this.datasetId = datasetId;
}

ArrayStore.prototype.listen = function(mediator) {
  var self = this;
  var topicCreate = 'sync:' + self.datasetId + ':create';
  console.log('Subscribing to mediator topic:', topicCreate);
  mediator.subscribe(topicCreate, function(object) {
    setTimeout(function() {
      object.id = self.data.length;
      self.data.push(object);
      console.log('Created object:', object);
      mediator.publish('done:' + topicCreate + ':' + object.createdTs, object);
    }, 0);
  });

  var topicLoad = 'sync:' + self.datasetId + ':load';
  console.log('Subscribing to mediator topic:', topicLoad);
  mediator.subscribe(topicLoad, function(id) {
    setTimeout(function() {
      var object = _.find(self.data, function(_object) {
        return _object.id == id;
      });
      mediator.publish('done:' + topicLoad + ':' + id, object);
    }, 0);
  });

  var topicSave = 'sync:' + self.datasetId + ':save';
  console.log('Subscribing to mediator topic:', topicSave);
  mediator.subscribe(topicSave, function(object) {
    setTimeout(function() {
      var index = _.findIndex(self.data, function(_object) {
        return _object.id == object.id;
      });
      self.data[index] = object;
      console.log('Saved object:', object);
      mediator.publish('done:' + topicSave + ':' + object.id, object);
    }, 0);
  });

  var topicList = 'sync:' + self.datasetId + ':list:load';
  console.log('Subscribing to mediator topic:', topicList);
  mediator.subscribe(topicList, function() {
    setTimeout(function() {
      mediator.publish('done:' + topicList, self.data);
    }, 0);
  });
}

module.exports = ArrayStore;
<commit_msg>Narrowed the workorders managed by the sync framework to those assigned to the logged-in user<commit_after>'use strict';

var _ = require('lodash');

var ArrayStore = function(datasetId, data) {
  this.data = data;
  this.datasetId = datasetId;
}

ArrayStore.prototype.listen = function(mediator) {
  var self = this;
  var topicCreate = 'sync:' + self.datasetId + ':create';
  console.log('Subscribing to mediator topic:', topicCreate);
  mediator.subscribe(topicCreate, function(object) {
    setTimeout(function() {
      object.id = self.data.length;
      self.data.push(object);
      console.log('Created object:', object);
      mediator.publish('done:' + topicCreate + ':' + object.createdTs, object);
    }, 0);
  });

  var topicLoad = 'sync:' + self.datasetId + ':load';
  console.log('Subscribing to mediator topic:', topicLoad);
  mediator.subscribe(topicLoad, function(id) {
    setTimeout(function() {
      var object = _.find(self.data, function(_object) {
        return _object.id == id;
      });
      mediator.publish('done:' + topicLoad + ':' + id, object);
    }, 0);
  });

  var topicSave = 'sync:' + self.datasetId + ':save';
  console.log('Subscribing to mediator topic:', topicSave);
  mediator.subscribe(topicSave, function(object) {
    setTimeout(function() {
      var index = _.findIndex(self.data, function(_object) {
        return _object.id == object.id;
      });
      self.data[index] = object;
      console.log('Saved object:', object);
      mediator.publish('done:' + topicSave + ':' + object.id, object);
    }, 0);
  });

  var topicList = 'sync:' + self.datasetId + ':list:load';
  console.log('Subscribing to mediator topic:', topicList);
  mediator.subscribe(topicList, function(queryParams) {
    var filterResults;
    if (queryParams && queryParams.filter) {
      console.log('Applying list filter:', queryParams.filter, 'to dataset', self.datasetId);
      filterResults = self.data.filter(function(object) {
        return object[queryParams.filter.key] == queryParams.filter.value;
      });
    } else {
      filterResults = self.data;
    };
    setTimeout(function() {
      mediator.publish('done:' + topicList, filterResults);
    }, 0);
  });
}

module.exports = ArrayStore;
<|endoftext|>


<commit_before>/*
    This file contains all unique details about API routes exposed by Backbeats
    API server.
*/

/**
 * The metrics route model.
 * @param {Object} redisKeys - The Redis keys used for Backbeat metrics
 * @param {Array} allSites - The list of replication site names
 * @return {Array} The array of route objects
 */
function routes(redisKeys, allSites) {
    return [
        {
            httpMethod: 'GET',
            category: 'healthcheck',
            type: 'basic',
            method: 'getHealthcheck',
            extensions: {},
        },
        {
            httpMethod: 'GET',
            category: 'metrics',
            type: 'backlog',
            extensions: { crr: [...allSites, 'all'] },
            method: 'getBacklog',
            dataPoints: [redisKeys.ops, redisKeys.opsDone, redisKeys.bytes,
                redisKeys.bytesDone],
        },
        {
            httpMethod: 'GET',
            category: 'metrics',
            type: 'completions',
            extensions: { crr: [...allSites, 'all'] },
            method: 'getCompletions',
            dataPoints: [redisKeys.opsDone, redisKeys.bytesDone],
        },
        {
            httpMethod: 'GET',
            category: 'metrics',
            type: 'throughput',
            extensions: { crr: [...allSites, 'all'] },
            method: 'getThroughput',
            dataPoints: [redisKeys.opsDone, redisKeys.bytesDone],
        },
        {
            httpMethod: 'GET',
            category: 'metrics',
            type: 'all',
            extensions: { crr: [...allSites, 'all'] },
            method: 'getAllMetrics',
            dataPoints: [redisKeys.ops, redisKeys.opsDone, redisKeys.bytes,
                redisKeys.bytesDone],
        },
        {
            httpMethod: 'GET',
            type: 'all',
            extensions: { crr: ['failed'] },
            method: 'getAllFailedCRR',
        },
        {
            httpMethod: 'GET',
            type: 'specific',
            extensions: { crr: ['failed'] },
            method: 'getFailedCRR',
        },
        {
            httpMethod: 'POST',
            type: 'all',
            extensions: { crr: ['failed'] },
            method: 'retryFailedCRR',
        },
        {
            httpMethod: 'GET',
            category: 'monitoring',
            type: 'monitoring',
            extensions: {},
            method: 'monitoringHandler',
        },
    ];
}

module.exports = routes;
<commit_msg>rf: edit monitoring route details<commit_after>/*
    This file contains all unique details about API routes exposed by Backbeats
    API server.
*/

/**
 * The metrics route model.
 * @param {Object} redisKeys - The Redis keys used for Backbeat metrics
 * @param {Array} allSites - The list of replication site names
 * @return {Array} The array of route objects
 */
function routes(redisKeys, allSites) {
    return [
        {
            httpMethod: 'GET',
            category: 'healthcheck',
            type: 'basic',
            method: 'getHealthcheck',
            extensions: {},
        },
        {
            httpMethod: 'GET',
            category: 'metrics',
            type: 'backlog',
            extensions: { crr: [...allSites, 'all'] },
            method: 'getBacklog',
            dataPoints: [redisKeys.ops, redisKeys.opsDone, redisKeys.bytes,
                redisKeys.bytesDone],
        },
        {
            httpMethod: 'GET',
            category: 'metrics',
            type: 'completions',
            extensions: { crr: [...allSites, 'all'] },
            method: 'getCompletions',
            dataPoints: [redisKeys.opsDone, redisKeys.bytesDone],
        },
        {
            httpMethod: 'GET',
            category: 'metrics',
            type: 'throughput',
            extensions: { crr: [...allSites, 'all'] },
            method: 'getThroughput',
            dataPoints: [redisKeys.opsDone, redisKeys.bytesDone],
        },
        {
            httpMethod: 'GET',
            category: 'metrics',
            type: 'all',
            extensions: { crr: [...allSites, 'all'] },
            method: 'getAllMetrics',
            dataPoints: [redisKeys.ops, redisKeys.opsDone, redisKeys.bytes,
                redisKeys.bytesDone],
        },
        {
            httpMethod: 'GET',
            type: 'all',
            extensions: { crr: ['failed'] },
            method: 'getAllFailedCRR',
        },
        {
            httpMethod: 'GET',
            type: 'specific',
            extensions: { crr: ['failed'] },
            method: 'getFailedCRR',
        },
        {
            httpMethod: 'POST',
            type: 'all',
            extensions: { crr: ['failed'] },
            method: 'retryFailedCRR',
        },
        {
            httpMethod: 'GET',
            category: 'monitoring',
            type: 'metrics',
            extensions: {},
            method: 'monitoringHandler',
        },
    ];
}

module.exports = routes;
<|endoftext|>


<commit_before>"use strict";

var mongoose = require("mongoose");

var connectDB = function(path, logger) {
  logger = logger || console;
  if(mongoose.connection._readyState!==0) {
    logger.warn("There is already a connection...");
    //console.log(mongoose.connection);
    return mongoose;
  }
  mongoose.connect(path);
  mongoose.connection.once("open", function(){
    logger.info("Connection to db '"+path+"' established...");
  });
  return mongoose;
};

module.exports = function(cms){
  if(!cms) throw new Error("You have to specify the cms object");

//# Mandantory Setup:
  var ext = cms.createExtension({package: require("./package.json")});

  ext.on("install", function(event){ // set options, but don't run or make available in cms
    //# Seting extension-config:
    ext.config.href = ext.config.href || "mongodb://localhost/nce-cms";
    ext.config.logger = ext.config.logger || {};

    //# Declarations and settings:
    ext.logger = cms.getExtension("winston").createLogger(ext.name, ext.config.logger);
  });

  ext.on("uninstall", function(event){ // undo installation
    //# Undeclare:
    cms.getExtension("winston").removeLogger(ext.name);
  });

  ext.on("activate", function(event){ // don't set options, just run, make available in cms or register.
	  store = connectDB(ext.config.href, ext.logger);
  });

  ext.on("deactivate", function(event){ // undo activation

  });

//# Private declarations:
  var store;

//# Public declarations and exports:
  ext.createSchema = function(obj){
    if(!store) throw new Error("The store isn't activated");
    var schema = new store.Schema(obj);
    // TODO: this causes an error at the moment: schema.plugin(eventify);
    ext.emit("newSchema", schema);
    return schema;
  };
  ext.getModel = function(name){
    if(!store) throw new Error("The store isn't activated");
    return store.models[name];
  };
  ext.createModel = function(name, schema){
    if(!store) throw new Error("The store isn't activated");
    var model = store.model(name, schema);
    ext.emit("newModel", model);
    // register events...
    return model;
  };
  ext.getStore = function(){ return store; };

  return ext;
}<commit_msg>#4: Close mongodb connectio on deactivation<commit_after>"use strict";

var mongoose = require("mongoose");

var connectDB = function(path, logger) {
  logger = logger || console;
  if(mongoose.connection._readyState!==0) {
    logger.warn("There is already a connection...");
    //console.log(mongoose.connection);
    return mongoose;
  }
  mongoose.connect(path, function(err){
    if(err) {
      logger.error(err.message, err);
    }
  });
  mongoose.connection.once("open", function(){
    logger.info("Connection to db '"+path+"' established...");
  });
  return mongoose;
};

module.exports = function(cms){
  if(!cms) throw new Error("You have to specify the cms object");

//# Mandantory Setup:
  var ext = cms.createExtension({package: require("./package.json")});

  ext.on("install", function(event){ // set options, but don't run or make available in cms
    //# Seting extension-config:
    ext.config.href = ext.config.href || "mongodb://localhost/nce-cms";
    ext.config.logger = ext.config.logger || {};

    //# Declarations and settings:
    ext.logger = cms.getExtension("winston").createLogger(ext.name, ext.config.logger);
  });

  ext.on("uninstall", function(event){ // undo installation
    //# Undeclare:
    cms.getExtension("winston").removeLogger(ext.name);
  });

  ext.on("activate", function(event){ // don't set options, just run, make available in cms or register.
	  store = connectDB(ext.config.href, ext.logger);
  });

  ext.on("deactivate", function(event){ // undo activation
	  mongoose.connection.close();
  });

//# Private declarations:
  var store;

//# Public declarations and exports:
  ext.createSchema = function(obj){
    if(!store) throw new Error("The store isn't activated");
    var schema = new store.Schema(obj);
    // TODO: this causes an error at the moment: schema.plugin(eventify);
    ext.emit("newSchema", schema);
    return schema;
  };
  ext.getModel = function(name){
    if(!store) throw new Error("The store isn't activated");
    return store.models[name];
  };
  ext.createModel = function(name, schema){
    if(!store) throw new Error("The store isn't activated");
    var model = store.model(name, schema);
    ext.emit("newModel", model);
    // register events...
    return model;
  };
  ext.getStore = function(){ return store; };

  return ext;
}<|endoftext|>


<commit_before>var argv = require('yargs').argv;
var WebSocket = require('ws');
var eve = require('evejs');
var MonitorAgent = require('./MonitorAgent');

var RECONNECT_DELAY = 10000; // ms
var CONDUCTOR_AGENT_URL = argv.conductor || 'ws://localhost:8082/ws/conductor';
var MONITOR_AGENT_URL = 'monitor';

console.log('conductor agent url:', CONDUCTOR_AGENT_URL);
console.log('monitor agent url:  ', MONITOR_AGENT_URL);

var monitorAgent = new MonitorAgent(MONITOR_AGENT_URL, {port: 3000});
var ws = null;

// create a sort of a proxy agent for the conductorAgent
// TODO: replace this with a websocket transport as soon as this is implemented in evejs
var conductorProxyAgent = new eve.Agent(CONDUCTOR_AGENT_URL);
conductorProxyAgent.extend('rpc', []);
conductorProxyAgent.connect(eve.system.transports.getAll());

function connect() {
// open a websocket pass incoming messages via the conductorAgent to the monitorAgent
  ws = new WebSocket(CONDUCTOR_AGENT_URL + '?id=' + MONITOR_AGENT_URL);
  ws.on('open', function () {
    console.log('Connected to the conductor agent');
  });
  ws.on('message', function (data, flags) {
    var rpc = JSON.parse(data);
    console.log('received', data);
    conductorProxyAgent.request('monitor', rpc)
        .then(function (result) {
          ws.send(JSON.stringify({id: rpc.id, result: result, error: null}));
        })
        .catch(function (err) {
          ws.send(JSON.stringify({id: rpc.id, result: null, error: err.toString()}));
        });
  });
  ws.on('close', function (err) {
    reconnect();
  });
  ws.on('error', function (err) {
    console.log('Error: Failed to connect to the conductor agent or connection is broken');
    reconnect();
  });
}

function reconnect() {
  console.log('Reconnecting in ' + (RECONNECT_DELAY / 1000) + ' seconds...');
  setTimeout(connect, RECONNECT_DELAY);
};

connect();

// generate ran
if (argv.random) {
  var NOTES = {
    C4: 0,
    D4: 1,
    E4: 2,
    F4: 3,
    G4: 4,
    A4: 5,
    B4: 6,
    C5: 7,
    D5: 8
  };

  // emit a random note once a second
  // TODO: replace this with connecting via the websocket to the conductor agent
  //       to listen for real events
  var nr = 0;
  var arr = Object.keys(NOTES);
  setInterval(function () {
    var now = new Date().toISOString();
    var duration = Math.pow(2, Math.round(Math.random() * 4)) * 100;
    //var note = arr[Math.floor(Math.random() * arr.length)];
    var note = arr[nr];
    nr = (nr + 1) % 9;

    monitorAgent.onNote({'note': note, duration: duration, start: now});
  }, 1000);
}
<commit_msg>Some minor tweaks<commit_after>var argv = require('yargs').argv;
var WebSocket = require('ws');
var eve = require('evejs');
var MonitorAgent = require('./MonitorAgent');

var RECONNECT_DELAY = 10000; // ms
var CONDUCTOR_AGENT_URL = argv.conductor || 'ws://localhost:8082/ws/conductor';
var MONITOR_AGENT_URL = 'monitor';

console.log('conductor agent url:', CONDUCTOR_AGENT_URL);
console.log('monitor agent url:  ', MONITOR_AGENT_URL);

var monitorAgent = new MonitorAgent(MONITOR_AGENT_URL, {port: 3000});

// create a sort of a proxy agent for the conductorAgent
// TODO: replace this with a websocket transport as soon as this is implemented in evejs
var conductorProxyAgent = new eve.Agent(CONDUCTOR_AGENT_URL);
conductorProxyAgent.extend('rpc', []);
conductorProxyAgent.connect(eve.system.transports.getAll());

// open a web socket to the conductor agent now
connect();

function connect() {
// open a websocket pass incoming messages via the conductorAgent to the monitorAgent
  var ws = new WebSocket(CONDUCTOR_AGENT_URL + '?id=' + MONITOR_AGENT_URL);
  ws.on('open', function () {
    console.log('Connected to the conductor agent');
  });
  ws.on('message', function (data, flags) {
    var rpc = JSON.parse(data);
    console.log('received', data);
    conductorProxyAgent.request('monitor', rpc)
        .then(function (result) {
          ws.send(JSON.stringify({id: rpc.id, result: result, error: null}));
        })
        .catch(function (err) {
          ws.send(JSON.stringify({id: rpc.id, result: null, error: err.toString()}));
        });
  });
  ws.on('close', function (err) {
    console.log('Error: Connection to the conductor agent closed');
    reconnect();
  });
  ws.on('error', function (err) {
    console.log('Error: Failed to connect to the conductor agent');
    reconnect();
  });
}

function reconnect() {
  console.log('Reconnecting in ' + (RECONNECT_DELAY / 1000) + ' seconds...');
  setTimeout(connect, RECONNECT_DELAY);
}

if (argv.random) {
// emit a random note once a second
  var NOTES = {
    C4: 0,
    D4: 1,
    E4: 2,
    F4: 3,
    G4: 4,
    A4: 5,
    B4: 6,
    C5: 7,
    D5: 8
  };

  var nr = 0;
  var arr = Object.keys(NOTES);
  setInterval(function () {
    var now = new Date().toISOString();
    var duration = Math.pow(2, Math.round(Math.random() * 4)) * 100;
    //var note = arr[Math.floor(Math.random() * arr.length)];
    var note = arr[nr];
    nr = (nr + 1) % 9;

    monitorAgent.onNote({'note': note, duration: duration, start: now});
  }, 1000);
}
<|endoftext|>


<commit_before>import _ from 'lodash'
import chokidar from 'chokidar'
import fs from 'fs-extra'
import hash from 'hash-sum'
import pify from 'pify'
import webpack from 'webpack'
import serialize from 'serialize-javascript'
import { join, resolve, basename, dirname } from 'path'
import Tapable from 'tappable'
import MFS from 'memory-fs'
import webpackDevMiddleware from 'webpack-dev-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware'
import { r, wp, createRoutes, parallel } from 'utils'
import Debug from 'debug'
import Glob from 'glob'
import clientWebpackConfig from './webpack/client.config.js'
import serverWebpackConfig from './webpack/server.config.js'

const debug = Debug('nuxt:build')
debug.color = 2 // Force green color

const remove = pify(fs.remove)
const readFile = pify(fs.readFile)
const utimes = pify(fs.utimes)
const writeFile = pify(fs.writeFile)
const mkdirp = pify(fs.mkdirp)
const glob = pify(Glob)

export default class Builder extends Tapable {
  constructor (nuxt) {
    super()
    this.nuxt = nuxt
    this.options = nuxt.options

    this._buildStatus = STATUS.INITIAL

    // Fields that set on build
    this.compiler = null
    this.webpackDevMiddleware = null
    this.webpackHotMiddleware = null

    // Mute stats on dev
    this.webpackStats = this.options.dev ? false : {
      chunks: false,
      children: false,
      modules: false,
      colors: true
    }
  }

  async build () {
    // Avoid calling this method multiple times
    if (this._buildStatus === STATUS.BUILD_DONE) {
      return this
    }
    // If building
    if (this._buildStatus === STATUS.BUILDING) {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve(this.build())
        }, 1000)
      })
    }
    this._buildStatus = STATUS.BUILDING

    // Check if pages dir exists and warn if not
    this._nuxtPages = typeof this.options.build.createRoutes !== 'function'
    if (this._nuxtPages) {
      if (!fs.existsSync(join(this.options.srcDir, 'pages'))) {
        let dir = this.options.srcDir
        if (fs.existsSync(join(this.options.srcDir, '..', 'pages'))) {
          throw new Error(`No \`pages\` directory found in ${dir}. Did you mean to run \`nuxt\` in the parent (\`../\`) directory?`)
        } else {
          throw new Error(`Couldn't find a \`pages\` directory in ${dir}. Please create one under the project root`)
        }
      }
    }

    debug(`App root: ${this.options.srcDir}`)
    debug(`Generating ${this.options.buildDir} files...`)

    // Create .nuxt/, .nuxt/components and .nuxt/dist folders
    await remove(r(this.options.buildDir))
    await mkdirp(r(this.options.buildDir, 'components'))
    if (!this.options.dev) {
      await mkdirp(r(this.options.buildDir, 'dist'))
    }

    // Generate routes and interpret the template files
    await this.generateRoutesAndFiles()

    // Start webpack build
    await this.webpackBuild()

    // Flag to set that building is done
    this._buildStatus = STATUS.BUILD_DONE

    return this
  }

  async generateRoutesAndFiles () {
    debug('Generating files...')
    // -- Templates --
    let templatesFiles = [
      'App.vue',
      'client.js',
      'index.js',
      'middleware.js',
      'router.js',
      'server.js',
      'utils.js',
      'components/nuxt-error.vue',
      'components/nuxt-loading.vue',
      'components/nuxt-child.js',
      'components/nuxt-link.js',
      'components/nuxt.vue',
      'views/app.template.html',
      'views/error.html'
    ]
    const templateVars = {
      options: this.options,
      uniqBy: _.uniqBy,
      isDev: this.options.dev,
      router: this.options.router,
      env: this.options.env,
      head: this.options.head,
      middleware: fs.existsSync(join(this.options.srcDir, 'middleware')),
      store: this.options.store,
      css: this.options.css,
      plugins: this.options.plugins.map((p, i) => {
        if (typeof p === 'string') p = { src: p }
        p.src = r(this.options.srcDir, p.src)
        return { src: p.src, ssr: (p.ssr !== false), name: `plugin${i}` }
      }),
      appPath: './App.vue',
      layouts: Object.assign({}, this.options.layouts),
      loading: typeof this.options.loading === 'string' ? r(this.options.srcDir, this.options.loading) : this.options.loading,
      transition: this.options.transition,
      components: {
        ErrorPage: this.options.ErrorPage ? r(this.options.ErrorPage) : null
      }
    }

    // -- Layouts --
    if (fs.existsSync(resolve(this.options.srcDir, 'layouts'))) {
      const layoutsFiles = await glob('layouts/*.vue', { cwd: this.options.srcDir })
      layoutsFiles.forEach((file) => {
        let name = file.split('/').slice(-1)[0].replace('.vue', '')
        if (name === 'error') return
        templateVars.layouts[name] = r(this.options.srcDir, file)
      })
      if (layoutsFiles.includes('layouts/error.vue')) {
        templateVars.components.ErrorPage = r(this.options.srcDir, 'layouts/error.vue')
      }
    }
    // If no default layout, create its folder and add the default folder
    if (!templateVars.layouts.default) {
      await mkdirp(r(this.options.buildDir, 'layouts'))
      templatesFiles.push('layouts/default.vue')
      templateVars.layouts.default = r(this.options.nuxtAppDir, 'layouts', 'default.vue')
    }

    // -- Routes --
    debug('Generating routes...')
    // If user defined a custom method to create routes
    if (this._nuxtPages) {
      // Use nuxt.js createRoutes bases on pages/
      const files = await glob('pages/**/*.vue', { cwd: this.options.srcDir })
      templateVars.router.routes = createRoutes(files, this.options.srcDir)
    } else {
      templateVars.router.routes = this.options.build.createRoutes(this.options.srcDir)
    }
    // router.extendRoutes method
    if (typeof this.options.router.extendRoutes === 'function') {
      // let the user extend the routes
      this.options.router.extendRoutes(templateVars.router.routes, r)
    }

    // -- Store --
    // Add store if needed
    if (this.options.store) {
      templatesFiles.push('store.js')
    }

    // Resolve template files
    const customTemplateFiles = this.options.build.templates.map(t => t.dst || basename(t.src || t))

    templatesFiles = templatesFiles.map(file => {
      // Skip if custom file was already provided in build.templates[]
      if (customTemplateFiles.indexOf(file) !== -1) {
        return
      }
      // Allow override templates using a file with same name in ${srcDir}/app
      const customPath = r(this.options.srcDir, 'app', file)
      const customFileExists = fs.existsSync(customPath)

      return {
        src: customFileExists
          ? customPath
          : r(this.options.nuxtAppDir, file),
        dst: file,
        custom: customFileExists
      }
    }).filter(i => !!i)

    // -- Custom templates --
    // Add custom template files
    templatesFiles = templatesFiles.concat(this.options.build.templates.map(t => {
      return Object.assign({
        src: r(this.options.srcDir, t.src || t),
        dst: t.dst || basename(t.src || t),
        custom: true
      }, t)
    }))

    // Interpret and move template files to .nuxt/
    await Promise.all(templatesFiles.map(async ({ src, dst, options, custom }) => {
      // Add template to watchers
      this.options.build.watch.push(src)
      // Render template to dst
      const fileContent = await readFile(src, 'utf8')
      const template = _.template(fileContent, {
        imports: {
          serialize,
          hash,
          r,
          wp
        }
      })
      const content = template(Object.assign({}, templateVars, {
        options: options || {},
        custom,
        src,
        dst
      }))
      const path = r(this.options.buildDir, dst)
      // Ensure parent dir exits
      await mkdirp(dirname(path))
      // Write file
      await writeFile(path, content, 'utf8')
      // Fix webpack loop (https://github.com/webpack/watchpack/issues/25#issuecomment-287789288)
      const dateFS = Date.now() / 1000 - 1000
      return utimes(path, dateFS, dateFS)
    }))
  }

  webpackBuild () {
    debug('Building files...')
    let compilersOptions = []

    // Client
    let clientConfig = clientWebpackConfig.call(this)
    compilersOptions.push(clientConfig)

    // Server
    let serverConfig = serverWebpackConfig.call(this)
    compilersOptions.push(serverConfig)

    // Simulate webpack multi compiler interface
    // Separate compilers are simpler, safer and faster
    this.compiler = { cache: {}, compilers: [] }
    compilersOptions.forEach(compilersOption => {
      this.compiler.compilers.push(webpack(compilersOption))
    })
    this.compiler.plugin = (...args) => {
      this.compiler.compilers.forEach(compiler => {
        compiler.plugin(...args)
      })
    }

    // Access to compilers with name
    this.compiler.compilers.forEach(compiler => {
      if (compiler.name) {
        this.compiler[compiler.name] = compiler
      }
    })

    // Add dev Stuff
    if (this.options.dev) {
      this.webpackDev()
    }

    // Start Builds
    return parallel(this.compiler.compilers, compiler => new Promise((resolve, reject) => {
      if (this.options.dev) {
        // --- Dev Build ---
        if (compiler.options.name === 'client') {
          // Client watch is started by dev-middleware
          resolve()
        } else {
          // Build and watch for changes
          compiler.watch(this.options.watchers.webpack, (err) => {
            if (err) {
              return reject(err)
            }
            resolve()
          })
        }
      } else {
        // --- Production build ---
        compiler.run((err, stats) => {
          // Show build stats for production
          console.log(stats.toString(this.webpackStats)) // eslint-disable-line no-console
          if (stats.hasErrors()) {
            return reject(new Error('Webpack build exited with errors'))
          }
          resolve()
        })
      }
    }))
  }

  webpackDev () {
    // Use shared MFS + Cache for faster builds
    let mfs = new MFS()
    this.compiler.compilers.forEach(compiler => {
      compiler.outputFileSystem = mfs
      compiler.cache = this.compiler.cache
    })

    // Run after each compile
    this.compiler.plugin('done', stats => {
      // Don't reload failed builds
      if (stats.hasErrors() || stats.hasWarnings()) {
        return
      }
      // Reload renderer if available
      if (this.nuxt.renderer) {
        this.nuxt.renderer.loadResources(mfs)
      }
    })

    // Add dev Middleware
    debug('Adding webpack middleware...')

    // Create webpack dev middleware
    this.webpackDevMiddleware = pify(webpackDevMiddleware(this.compiler.client, {
      publicPath: this.options.build.publicPath,
      stats: this.webpackStats,
      noInfo: true,
      quiet: true,
      watchOptions: this.options.watchers.webpack
    }))

    this.webpackHotMiddleware = pify(webpackHotMiddleware(this.compiler.client, {
      log: false,
      heartbeat: 2500
    }))

    // Stop webpack middleware on nuxt.close()
    this.nuxt.plugin('close', () => new Promise(resolve => {
      this.webpackDevMiddleware.close(() => resolve())
    }))

    // Start watching files
    this.watchFiles()
  }

  watchFiles () {
    const patterns = [
      r(this.options.srcDir, 'layouts'),
      r(this.options.srcDir, 'store'),
      r(this.options.srcDir, 'middleware'),
      r(this.options.srcDir, 'layouts/*.vue'),
      r(this.options.srcDir, 'layouts/**/*.vue')
    ]
    if (this._nuxtPages) {
      patterns.push(r(this.options.srcDir, 'pages'))
      patterns.push(r(this.options.srcDir, 'pages/*.vue'))
      patterns.push(r(this.options.srcDir, 'pages/**/*.vue'))
    }
    const options = Object.assign({}, this.options.watchers.chokidar, {
      ignoreInitial: true
    })
    /* istanbul ignore next */
    const refreshFiles = _.debounce(this.generateRoutesAndFiles, 200)

    // Watch for src Files
    let filesWatcher = chokidar.watch(patterns, options)
      .on('add', refreshFiles)
      .on('unlink', refreshFiles)

    // Watch for custom provided files
    let customFilesWatcher = chokidar.watch(_.uniq(this.options.build.watch), options)
      .on('change', refreshFiles)

    // Stop watching on nuxt.close()
    this.nuxt.plugin('close', () => {
      filesWatcher.close()
      customFilesWatcher.close()
    })
  }
}

const STATUS = {
  INITIAL: 1,
  BUILD_DONE: 2,
  BUILDING: 3
}
<commit_msg>Handle error<commit_after>import _ from 'lodash'
import chokidar from 'chokidar'
import fs from 'fs-extra'
import hash from 'hash-sum'
import pify from 'pify'
import webpack from 'webpack'
import serialize from 'serialize-javascript'
import { join, resolve, basename, dirname } from 'path'
import Tapable from 'tappable'
import MFS from 'memory-fs'
import webpackDevMiddleware from 'webpack-dev-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware'
import { r, wp, createRoutes, parallel } from 'utils'
import Debug from 'debug'
import Glob from 'glob'
import clientWebpackConfig from './webpack/client.config.js'
import serverWebpackConfig from './webpack/server.config.js'

const debug = Debug('nuxt:build')
debug.color = 2 // Force green color

const remove = pify(fs.remove)
const readFile = pify(fs.readFile)
const utimes = pify(fs.utimes)
const writeFile = pify(fs.writeFile)
const mkdirp = pify(fs.mkdirp)
const glob = pify(Glob)

export default class Builder extends Tapable {
  constructor (nuxt) {
    super()
    this.nuxt = nuxt
    this.options = nuxt.options

    this._buildStatus = STATUS.INITIAL

    // Fields that set on build
    this.compiler = null
    this.webpackDevMiddleware = null
    this.webpackHotMiddleware = null

    // Mute stats on dev
    this.webpackStats = this.options.dev ? false : {
      chunks: false,
      children: false,
      modules: false,
      colors: true
    }
  }

  async build () {
    // Avoid calling this method multiple times
    if (this._buildStatus === STATUS.BUILD_DONE) {
      return this
    }
    // If building
    if (this._buildStatus === STATUS.BUILDING) {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve(this.build())
        }, 1000)
      })
    }
    this._buildStatus = STATUS.BUILDING

    // Check if pages dir exists and warn if not
    this._nuxtPages = typeof this.options.build.createRoutes !== 'function'
    if (this._nuxtPages) {
      if (!fs.existsSync(join(this.options.srcDir, 'pages'))) {
        let dir = this.options.srcDir
        if (fs.existsSync(join(this.options.srcDir, '..', 'pages'))) {
          throw new Error(`No \`pages\` directory found in ${dir}. Did you mean to run \`nuxt\` in the parent (\`../\`) directory?`)
        } else {
          throw new Error(`Couldn't find a \`pages\` directory in ${dir}. Please create one under the project root`)
        }
      }
    }

    debug(`App root: ${this.options.srcDir}`)
    debug(`Generating ${this.options.buildDir} files...`)

    // Create .nuxt/, .nuxt/components and .nuxt/dist folders
    await remove(r(this.options.buildDir))
    await mkdirp(r(this.options.buildDir, 'components'))
    if (!this.options.dev) {
      await mkdirp(r(this.options.buildDir, 'dist'))
    }

    // Generate routes and interpret the template files
    await this.generateRoutesAndFiles()

    // Start webpack build
    await this.webpackBuild()

    // Flag to set that building is done
    this._buildStatus = STATUS.BUILD_DONE

    return this
  }

  async generateRoutesAndFiles () {
    debug('Generating files...')
    // -- Templates --
    let templatesFiles = [
      'App.vue',
      'client.js',
      'index.js',
      'middleware.js',
      'router.js',
      'server.js',
      'utils.js',
      'components/nuxt-error.vue',
      'components/nuxt-loading.vue',
      'components/nuxt-child.js',
      'components/nuxt-link.js',
      'components/nuxt.vue',
      'views/app.template.html',
      'views/error.html'
    ]
    const templateVars = {
      options: this.options,
      uniqBy: _.uniqBy,
      isDev: this.options.dev,
      router: this.options.router,
      env: this.options.env,
      head: this.options.head,
      middleware: fs.existsSync(join(this.options.srcDir, 'middleware')),
      store: this.options.store,
      css: this.options.css,
      plugins: this.options.plugins.map((p, i) => {
        if (typeof p === 'string') p = { src: p }
        p.src = r(this.options.srcDir, p.src)
        return { src: p.src, ssr: (p.ssr !== false), name: `plugin${i}` }
      }),
      appPath: './App.vue',
      layouts: Object.assign({}, this.options.layouts),
      loading: typeof this.options.loading === 'string' ? r(this.options.srcDir, this.options.loading) : this.options.loading,
      transition: this.options.transition,
      components: {
        ErrorPage: this.options.ErrorPage ? r(this.options.ErrorPage) : null
      }
    }

    // -- Layouts --
    if (fs.existsSync(resolve(this.options.srcDir, 'layouts'))) {
      const layoutsFiles = await glob('layouts/*.vue', { cwd: this.options.srcDir })
      layoutsFiles.forEach((file) => {
        let name = file.split('/').slice(-1)[0].replace('.vue', '')
        if (name === 'error') return
        templateVars.layouts[name] = r(this.options.srcDir, file)
      })
      if (layoutsFiles.includes('layouts/error.vue')) {
        templateVars.components.ErrorPage = r(this.options.srcDir, 'layouts/error.vue')
      }
    }
    // If no default layout, create its folder and add the default folder
    if (!templateVars.layouts.default) {
      await mkdirp(r(this.options.buildDir, 'layouts'))
      templatesFiles.push('layouts/default.vue')
      templateVars.layouts.default = r(this.options.nuxtAppDir, 'layouts', 'default.vue')
    }

    // -- Routes --
    debug('Generating routes...')
    // If user defined a custom method to create routes
    if (this._nuxtPages) {
      // Use nuxt.js createRoutes bases on pages/
      const files = await glob('pages/**/*.vue', { cwd: this.options.srcDir })
      templateVars.router.routes = createRoutes(files, this.options.srcDir)
    } else {
      templateVars.router.routes = this.options.build.createRoutes(this.options.srcDir)
    }
    // router.extendRoutes method
    if (typeof this.options.router.extendRoutes === 'function') {
      // let the user extend the routes
      this.options.router.extendRoutes(templateVars.router.routes, r)
    }

    // -- Store --
    // Add store if needed
    if (this.options.store) {
      templatesFiles.push('store.js')
    }

    // Resolve template files
    const customTemplateFiles = this.options.build.templates.map(t => t.dst || basename(t.src || t))

    templatesFiles = templatesFiles.map(file => {
      // Skip if custom file was already provided in build.templates[]
      if (customTemplateFiles.indexOf(file) !== -1) {
        return
      }
      // Allow override templates using a file with same name in ${srcDir}/app
      const customPath = r(this.options.srcDir, 'app', file)
      const customFileExists = fs.existsSync(customPath)

      return {
        src: customFileExists
          ? customPath
          : r(this.options.nuxtAppDir, file),
        dst: file,
        custom: customFileExists
      }
    }).filter(i => !!i)

    // -- Custom templates --
    // Add custom template files
    templatesFiles = templatesFiles.concat(this.options.build.templates.map(t => {
      return Object.assign({
        src: r(this.options.srcDir, t.src || t),
        dst: t.dst || basename(t.src || t),
        custom: true
      }, t)
    }))

    // Interpret and move template files to .nuxt/
    await Promise.all(templatesFiles.map(async ({ src, dst, options, custom }) => {
      // Add template to watchers
      this.options.build.watch.push(src)
      // Render template to dst
      const fileContent = await readFile(src, 'utf8')
      const template = _.template(fileContent, {
        imports: {
          serialize,
          hash,
          r,
          wp
        }
      })
      const content = template(Object.assign({}, templateVars, {
        options: options || {},
        custom,
        src,
        dst
      }))
      const path = r(this.options.buildDir, dst)
      // Ensure parent dir exits
      await mkdirp(dirname(path))
      // Write file
      await writeFile(path, content, 'utf8')
      // Fix webpack loop (https://github.com/webpack/watchpack/issues/25#issuecomment-287789288)
      const dateFS = Date.now() / 1000 - 1000
      return utimes(path, dateFS, dateFS)
    }))
  }

  webpackBuild () {
    debug('Building files...')
    let compilersOptions = []

    // Client
    let clientConfig = clientWebpackConfig.call(this)
    compilersOptions.push(clientConfig)

    // Server
    let serverConfig = serverWebpackConfig.call(this)
    compilersOptions.push(serverConfig)

    // Simulate webpack multi compiler interface
    // Separate compilers are simpler, safer and faster
    this.compiler = { cache: {}, compilers: [] }
    compilersOptions.forEach(compilersOption => {
      this.compiler.compilers.push(webpack(compilersOption))
    })
    this.compiler.plugin = (...args) => {
      this.compiler.compilers.forEach(compiler => {
        compiler.plugin(...args)
      })
    }

    // Access to compilers with name
    this.compiler.compilers.forEach(compiler => {
      if (compiler.name) {
        this.compiler[compiler.name] = compiler
      }
    })

    // Add dev Stuff
    if (this.options.dev) {
      this.webpackDev()
    }

    // Start Builds
    return parallel(this.compiler.compilers, compiler => new Promise((resolve, reject) => {
      if (this.options.dev) {
        // --- Dev Build ---
        if (compiler.options.name === 'client') {
          // Client watch is started by dev-middleware
          resolve()
        } else {
          // Build and watch for changes
          compiler.watch(this.options.watchers.webpack, (err) => {
            if (err) {
              return reject(err)
            }
            resolve()
          })
        }
      } else {
        // --- Production build ---
        compiler.run((err, stats) => {
          if (err) return console.error(err) // eslint-disable-line no-console
          // Show build stats for production
          console.log(stats.toString(this.webpackStats)) // eslint-disable-line no-console
          if (stats.hasErrors()) {
            return reject(new Error('Webpack build exited with errors'))
          }
          resolve()
        })
      }
    }))
  }

  webpackDev () {
    // Use shared MFS + Cache for faster builds
    let mfs = new MFS()
    this.compiler.compilers.forEach(compiler => {
      compiler.outputFileSystem = mfs
      compiler.cache = this.compiler.cache
    })

    // Run after each compile
    this.compiler.plugin('done', stats => {
      // Don't reload failed builds
      if (stats.hasErrors() || stats.hasWarnings()) {
        return
      }
      // Reload renderer if available
      if (this.nuxt.renderer) {
        this.nuxt.renderer.loadResources(mfs)
      }
    })

    // Add dev Middleware
    debug('Adding webpack middleware...')

    // Create webpack dev middleware
    this.webpackDevMiddleware = pify(webpackDevMiddleware(this.compiler.client, {
      publicPath: this.options.build.publicPath,
      stats: this.webpackStats,
      noInfo: true,
      quiet: true,
      watchOptions: this.options.watchers.webpack
    }))

    this.webpackHotMiddleware = pify(webpackHotMiddleware(this.compiler.client, {
      log: false,
      heartbeat: 2500
    }))

    // Stop webpack middleware on nuxt.close()
    this.nuxt.plugin('close', () => new Promise(resolve => {
      this.webpackDevMiddleware.close(() => resolve())
    }))

    // Start watching files
    this.watchFiles()
  }

  watchFiles () {
    const patterns = [
      r(this.options.srcDir, 'layouts'),
      r(this.options.srcDir, 'store'),
      r(this.options.srcDir, 'middleware'),
      r(this.options.srcDir, 'layouts/*.vue'),
      r(this.options.srcDir, 'layouts/**/*.vue')
    ]
    if (this._nuxtPages) {
      patterns.push(r(this.options.srcDir, 'pages'))
      patterns.push(r(this.options.srcDir, 'pages/*.vue'))
      patterns.push(r(this.options.srcDir, 'pages/**/*.vue'))
    }
    const options = Object.assign({}, this.options.watchers.chokidar, {
      ignoreInitial: true
    })
    /* istanbul ignore next */
    const refreshFiles = _.debounce(this.generateRoutesAndFiles, 200)

    // Watch for src Files
    let filesWatcher = chokidar.watch(patterns, options)
      .on('add', refreshFiles)
      .on('unlink', refreshFiles)

    // Watch for custom provided files
    let customFilesWatcher = chokidar.watch(_.uniq(this.options.build.watch), options)
      .on('change', refreshFiles)

    // Stop watching on nuxt.close()
    this.nuxt.plugin('close', () => {
      filesWatcher.close()
      customFilesWatcher.close()
    })
  }
}

const STATUS = {
  INITIAL: 1,
  BUILD_DONE: 2,
  BUILDING: 3
}
<|endoftext|>