Add CORS to web api

Add CORS to web api

--- api/app.js
+++ api/app.js
@@ -1,5 +1,6 @@
 'use strict';

+var cors = require('cors')
 var express = require('express');
 var bodyParser = require('body-parser');

@@ -18,6 +19,7 @@
   this.app = express();

   // parse POST data
+  this.app.use(cors());
   this.app.use(bodyParser.json());
   this.app.use(bodyParser.urlencoded({ extended: false }));

<|original_code|>
'use strict';

var express = require('express');
var bodyParser = require('body-parser');

var config = require('./config');
var routes = require('./routes');

function API(backend, opts) {
  this.backend = backend;
  this.opts = opts;

  this._initApp();
}

API.prototype._initApp = function() {
  this.app = express();

  // parse POST data
  this.app.use(bodyParser.json());
  this.app.use(bodyParser.urlencoded({ extended: false }));

  // install routes
  this.app.use('/', routes);

  // catch 404 and forward to error handler
  this.app.use(function(req, res, next) {
      var err = new Error('Not Found');
      err.status = 404;
      next(err);
  });

  // production error handler
  this.app.use(function(err, req, res, next) {
      res.status(err.status || 500);
      res.send({
          message: err.message,
          error: {}
      });
  });
}

module.exports = API;

<|edits_diff|>
--- api/app.js
+++ api/app.js
@@ -1,5 +1,6 @@
 'use strict';

+var cors = require('cors')
 var express = require('express');
 var bodyParser = require('body-parser');

<|current_version|>
'use strict';

var cors = require('cors')
var express = require('express');
var bodyParser = require('body-parser');

var config = require('./config');
var routes = require('./routes');

function API(backend, opts) {
  this.backend = backend;
  this.opts = opts;

  this._initApp();
}

API.prototype._initApp = function() {
  this.app = express();

  // parse POST data
  this.app.use(bodyParser.json());
  this.app.use(bodyParser.urlencoded({ extended: false }));

  // install routes
  this.app.use('/', routes);

  // catch 404 and forward to error handler
  this.app.use(function(req, res, next) {
      var err = new Error('Not Found');
      err.status = 404;
      next(err);
  });

  // production error handler
  this.app.use(function(err, req, res, next) {
      res.status(err.status || 500);
      res.send({
          message: err.message,
          error: {}
      });
  });
}

module.exports = API;

<|next_version|>
'use strict';

var cors = require('cors')
var express = require('express');
var bodyParser = require('body-parser');

var config = require('./config');
var routes = require('./routes');

function API(backend, opts) {
  this.backend = backend;
  this.opts = opts;

  this._initApp();
}

API.prototype._initApp = function() {
  this.app = express();

  // parse POST data
  this.app.use(cors());
  this.app.use(bodyParser.json());
  this.app.use(bodyParser.urlencoded({ extended: false }));

  // install routes
  this.app.use('/', routes);

  // catch 404 and forward to error handler
  this.app.use(function(req, res, next) {
      var err = new Error('Not Found');
      err.status = 404;
      next(err);
  });

  // production error handler
  this.app.use(function(err, req, res, next) {
      res.status(err.status || 500);
      res.send({
          message: err.message,
          error: {}
      });
  });
}

module.exports = API;


Clear search text after action

Clear search text after action

--- src/app/modules/search/search_controller.js
+++ src/app/modules/search/search_controller.js
@@ -8,6 +8,7 @@
    */
   this.submit = function() {
     $state.go('packages', {search: this.searchText});
+    this.searchText = null;
   };

   /**
@@ -16,6 +17,7 @@
    */
   this.itemSelected = function(item) {
     $state.go('package', {name: item.name});
+    this.searchText = null;
   };

   /**

<|original_code|>
app.controller('SearchController', function($scope, $state, $q, KpmApi) {

  var self = this;

  /**
   * Callback when 'Return' key is pressed
   * Redirect to the package list page
   */
  this.submit = function() {
    $state.go('packages', {search: this.searchText});
  };

  /**
   * Callback when a suggested item is selected
   * Redirect to the package view page
   */
  this.itemSelected = function(item) {
    $state.go('package', {name: item.name});
  };

  /**
   * Autocomplete suggestion callback
   * @return promise
   */
  this.querySearch = function(search) {
    var deferred = $q.defer();
    KpmApi.get('packages', {
      named_like: search
    })
    .success(function(data) {
      deferred.resolve(data);
    });
    return deferred.promise;
  };
});

<|edits_diff|>
--- src/app/modules/search/search_controller.js
+++ src/app/modules/search/search_controller.js
@@ -8,6 +8,7 @@
    */
   this.submit = function() {
     $state.go('packages', {search: this.searchText});
+    this.searchText = null;
   };

   /**
<|current_version|>
app.controller('SearchController', function($scope, $state, $q, KpmApi) {

  var self = this;

  /**
   * Callback when 'Return' key is pressed
   * Redirect to the package list page
   */
  this.submit = function() {
    $state.go('packages', {search: this.searchText});
    this.searchText = null;
  };

  /**
   * Callback when a suggested item is selected
   * Redirect to the package view page
   */
  this.itemSelected = function(item) {
    $state.go('package', {name: item.name});
  };

  /**
   * Autocomplete suggestion callback
   * @return promise
   */
  this.querySearch = function(search) {
    var deferred = $q.defer();
    KpmApi.get('packages', {
      named_like: search
    })
    .success(function(data) {
      deferred.resolve(data);
    });
    return deferred.promise;
  };
});

<|next_version|>
app.controller('SearchController', function($scope, $state, $q, KpmApi) {

  var self = this;

  /**
   * Callback when 'Return' key is pressed
   * Redirect to the package list page
   */
  this.submit = function() {
    $state.go('packages', {search: this.searchText});
    this.searchText = null;
  };

  /**
   * Callback when a suggested item is selected
   * Redirect to the package view page
   */
  this.itemSelected = function(item) {
    $state.go('package', {name: item.name});
    this.searchText = null;
  };

  /**
   * Autocomplete suggestion callback
   * @return promise
   */
  this.querySearch = function(search) {
    var deferred = $q.defer();
    KpmApi.get('packages', {
      named_like: search
    })
    .success(function(data) {
      deferred.resolve(data);
    });
    return deferred.promise;
  };
});


Mark login as Index route

Mark login as Index route

--- src/front/app/routes.js
+++ src/front/app/routes.js
@@ -1,5 +1,6 @@
 import React from 'react';
 import { Route } from 'react-router';
+import { IndexRoute } from 'react-router';
 import Login from './features/login';
 import CategoryTileListContainer from './features/categories/components/CategoryTileListContainer';
 import ChallengeScreenContainer from './features/challenges/components/ChallengeScreenContainer';
@@ -10,6 +11,7 @@

 export default () => (
     <Route path="/" >
+        <IndexRoute component={Login} />
         <Route path="login" component={Login} />
         <Route path="challenges" component={ChallengeScreenContainer} />
         <Route path="awards" component={Awards} />

<|original_code|>
import React from 'react';
import { Route } from 'react-router';
import Login from './features/login';
import CategoryTileListContainer from './features/categories/components/CategoryTileListContainer';
import ChallengeScreenContainer from './features/challenges/components/ChallengeScreenContainer';
import TodoScreen from './features/todo/TodoScreen';
import Awards from './features/awards';
import Updates from './features/updates';
import Profile from './features/profile';

export default () => (
    <Route path="/" >
        <Route path="login" component={Login} />
        <Route path="challenges" component={ChallengeScreenContainer} />
        <Route path="awards" component={Awards} />
        <Route path="updates" component={Updates} />
        <Route path="profile" component={Profile} />
        <Route path="todo" component={TodoScreen} />
        <Route path="categories" component={CategoryTileListContainer} />
    </Route>
);

<|edits_diff|>
--- src/front/app/routes.js
+++ src/front/app/routes.js
@@ -1,5 +1,6 @@
 import React from 'react';
 import { Route } from 'react-router';
+import { IndexRoute } from 'react-router';
 import Login from './features/login';
 import CategoryTileListContainer from './features/categories/components/CategoryTileListContainer';
 import ChallengeScreenContainer from './features/challenges/components/ChallengeScreenContainer';
<|current_version|>
import React from 'react';
import { Route } from 'react-router';
import { IndexRoute } from 'react-router';
import Login from './features/login';
import CategoryTileListContainer from './features/categories/components/CategoryTileListContainer';
import ChallengeScreenContainer from './features/challenges/components/ChallengeScreenContainer';
import TodoScreen from './features/todo/TodoScreen';
import Awards from './features/awards';
import Updates from './features/updates';
import Profile from './features/profile';

export default () => (
    <Route path="/" >
        <Route path="login" component={Login} />
        <Route path="challenges" component={ChallengeScreenContainer} />
        <Route path="awards" component={Awards} />
        <Route path="updates" component={Updates} />
        <Route path="profile" component={Profile} />
        <Route path="todo" component={TodoScreen} />
        <Route path="categories" component={CategoryTileListContainer} />
    </Route>
);

<|next_version|>
import React from 'react';
import { Route } from 'react-router';
import { IndexRoute } from 'react-router';
import Login from './features/login';
import CategoryTileListContainer from './features/categories/components/CategoryTileListContainer';
import ChallengeScreenContainer from './features/challenges/components/ChallengeScreenContainer';
import TodoScreen from './features/todo/TodoScreen';
import Awards from './features/awards';
import Updates from './features/updates';
import Profile from './features/profile';

export default () => (
    <Route path="/" >
        <IndexRoute component={Login} />
        <Route path="login" component={Login} />
        <Route path="challenges" component={ChallengeScreenContainer} />
        <Route path="awards" component={Awards} />
        <Route path="updates" component={Updates} />
        <Route path="profile" component={Profile} />
        <Route path="todo" component={TodoScreen} />
        <Route path="categories" component={CategoryTileListContainer} />
    </Route>
);


Add subset of missing react-autosuggest props as optional properties

--- packages/bpk-component-autosuggest/src/BpkAutosuggest.js
+++ packages/bpk-component-autosuggest/src/BpkAutosuggest.js
@@ -25,8 +25,10 @@
     <Autosuggest
       suggestions={props.suggestions}
       onSuggestionsUpdateRequested={props.onSuggestionsUpdateRequested}
+      onSuggestionsClearRequested={props.onSuggestionsClearRequested || (() => {})}
       getSuggestionValue={props.getSuggestionValue}
       renderSuggestion={props.renderSuggestion}
+      onSuggestionSelected={props.onSuggestionSelected || (() => {})}
       inputProps={inputProps}
       theme={defaultTheme}
     />

<|original_code|>
import React from 'react'
import Autosuggest from 'react-autosuggest'

import style from './bpk-autosuggest.scss'

const defaultTheme = {
  container:            'bpk-autosuggest__container',
  containerOpen:        'bpk-autosuggest__container--open',
  input:                'bpk-autosuggest__input',
  suggestionsContainer: 'bpk-autosuggest__suggestions-container',
  suggestionsList:      'bpk-autosuggest__suggestions-list',
  suggestion:           'bpk-autosuggest__suggestion',
  suggestionFocused:    'bpk-autosuggest__suggestion--focused',
  sectionContainer:     'bpk-autosuggest__section-container',
  sectionTitle:         'bpk-autosuggest__section-title'
}

const BpkAutosuggest = (props) => {
  const inputProps = {
    value: props.value,
    onChange: props.onChange
  }

  return (
    <Autosuggest
      suggestions={props.suggestions}
      onSuggestionsUpdateRequested={props.onSuggestionsUpdateRequested}
      getSuggestionValue={props.getSuggestionValue}
      renderSuggestion={props.renderSuggestion}
      inputProps={inputProps}
      theme={defaultTheme}
    />
  )
}

export default BpkAutosuggest

<|edits_diff|>
--- packages/bpk-component-autosuggest/src/BpkAutosuggest.js
+++ packages/bpk-component-autosuggest/src/BpkAutosuggest.js
@@ -25,6 +25,7 @@
     <Autosuggest
       suggestions={props.suggestions}
       onSuggestionsUpdateRequested={props.onSuggestionsUpdateRequested}
+      onSuggestionsClearRequested={props.onSuggestionsClearRequested || (() => {})}
       getSuggestionValue={props.getSuggestionValue}
       renderSuggestion={props.renderSuggestion}
       inputProps={inputProps}
<|current_version|>
import React from 'react'
import Autosuggest from 'react-autosuggest'

import style from './bpk-autosuggest.scss'

const defaultTheme = {
  container:            'bpk-autosuggest__container',
  containerOpen:        'bpk-autosuggest__container--open',
  input:                'bpk-autosuggest__input',
  suggestionsContainer: 'bpk-autosuggest__suggestions-container',
  suggestionsList:      'bpk-autosuggest__suggestions-list',
  suggestion:           'bpk-autosuggest__suggestion',
  suggestionFocused:    'bpk-autosuggest__suggestion--focused',
  sectionContainer:     'bpk-autosuggest__section-container',
  sectionTitle:         'bpk-autosuggest__section-title'
}

const BpkAutosuggest = (props) => {
  const inputProps = {
    value: props.value,
    onChange: props.onChange
  }

  return (
    <Autosuggest
      suggestions={props.suggestions}
      onSuggestionsUpdateRequested={props.onSuggestionsUpdateRequested}
      onSuggestionsClearRequested={props.onSuggestionsClearRequested || (() => {})}
      getSuggestionValue={props.getSuggestionValue}
      renderSuggestion={props.renderSuggestion}
      inputProps={inputProps}
      theme={defaultTheme}
    />
  )
}

export default BpkAutosuggest

<|next_version|>
import React from 'react'
import Autosuggest from 'react-autosuggest'

import style from './bpk-autosuggest.scss'

const defaultTheme = {
  container:            'bpk-autosuggest__container',
  containerOpen:        'bpk-autosuggest__container--open',
  input:                'bpk-autosuggest__input',
  suggestionsContainer: 'bpk-autosuggest__suggestions-container',
  suggestionsList:      'bpk-autosuggest__suggestions-list',
  suggestion:           'bpk-autosuggest__suggestion',
  suggestionFocused:    'bpk-autosuggest__suggestion--focused',
  sectionContainer:     'bpk-autosuggest__section-container',
  sectionTitle:         'bpk-autosuggest__section-title'
}

const BpkAutosuggest = (props) => {
  const inputProps = {
    value: props.value,
    onChange: props.onChange
  }

  return (
    <Autosuggest
      suggestions={props.suggestions}
      onSuggestionsUpdateRequested={props.onSuggestionsUpdateRequested}
      onSuggestionsClearRequested={props.onSuggestionsClearRequested || (() => {})}
      getSuggestionValue={props.getSuggestionValue}
      renderSuggestion={props.renderSuggestion}
      onSuggestionSelected={props.onSuggestionSelected || (() => {})}
      inputProps={inputProps}
      theme={defaultTheme}
    />
  )
}

export default BpkAutosuggest


Add grunt-release and rename NPM module to grypher.

--- Gruntfile.js
+++ Gruntfile.js
@@ -38,7 +38,9 @@
     grunt.loadNpmTasks('grunt-contrib-jshint');
     grunt.loadNpmTasks('grunt-contrib-watch');
     grunt.loadNpmTasks('grunt-mocha-cli');
+    grunt.loadNpmTasks('grunt-release');
     grunt.registerTask('test', ['jshint', 'jison', 'mochacli', 'watch']);
     grunt.registerTask('ci', ['jshint', 'jison', 'mochacli']);
     grunt.registerTask('default', ['test']);
+    grunt.registerTask('publish', ['ci', 'release']);
 };

<|original_code|>
// Generated on 2014-07-07 using generator-nodejs 2.0.0
module.exports = function (grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jshint: {
            all: [
                'Gruntfile.js',
                'test/**/*.js'
            ],
            options: {
                jshintrc: '.jshintrc'
            }
        },
        jison: {
            all : {
                files: { 'lib/parser.js': 'data/grypher.jison' }
            }
        },
        mochacli: {
            all: ['test/**/*.js'],
            options: {
                reporter: 'spec',
                ui: 'tdd'
            }
        },
        watch: {
            js: {
                files: ['**/*.js', 'data/*.jison', '!node_modules/**/*.js'],
                tasks: ['default'],
                options: {
                    nospawn: true
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-jison');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-mocha-cli');
    grunt.registerTask('test', ['jshint', 'jison', 'mochacli', 'watch']);
    grunt.registerTask('ci', ['jshint', 'jison', 'mochacli']);
    grunt.registerTask('default', ['test']);
};

<|edits_diff|>
--- Gruntfile.js
+++ Gruntfile.js
@@ -38,6 +38,7 @@
     grunt.loadNpmTasks('grunt-contrib-jshint');
     grunt.loadNpmTasks('grunt-contrib-watch');
     grunt.loadNpmTasks('grunt-mocha-cli');
+    grunt.loadNpmTasks('grunt-release');
     grunt.registerTask('test', ['jshint', 'jison', 'mochacli', 'watch']);
     grunt.registerTask('ci', ['jshint', 'jison', 'mochacli']);
     grunt.registerTask('default', ['test']);
<|current_version|>
// Generated on 2014-07-07 using generator-nodejs 2.0.0
module.exports = function (grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jshint: {
            all: [
                'Gruntfile.js',
                'test/**/*.js'
            ],
            options: {
                jshintrc: '.jshintrc'
            }
        },
        jison: {
            all : {
                files: { 'lib/parser.js': 'data/grypher.jison' }
            }
        },
        mochacli: {
            all: ['test/**/*.js'],
            options: {
                reporter: 'spec',
                ui: 'tdd'
            }
        },
        watch: {
            js: {
                files: ['**/*.js', 'data/*.jison', '!node_modules/**/*.js'],
                tasks: ['default'],
                options: {
                    nospawn: true
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-jison');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-mocha-cli');
    grunt.loadNpmTasks('grunt-release');
    grunt.registerTask('test', ['jshint', 'jison', 'mochacli', 'watch']);
    grunt.registerTask('ci', ['jshint', 'jison', 'mochacli']);
    grunt.registerTask('default', ['test']);
};

<|next_version|>
// Generated on 2014-07-07 using generator-nodejs 2.0.0
module.exports = function (grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jshint: {
            all: [
                'Gruntfile.js',
                'test/**/*.js'
            ],
            options: {
                jshintrc: '.jshintrc'
            }
        },
        jison: {
            all : {
                files: { 'lib/parser.js': 'data/grypher.jison' }
            }
        },
        mochacli: {
            all: ['test/**/*.js'],
            options: {
                reporter: 'spec',
                ui: 'tdd'
            }
        },
        watch: {
            js: {
                files: ['**/*.js', 'data/*.jison', '!node_modules/**/*.js'],
                tasks: ['default'],
                options: {
                    nospawn: true
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-jison');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-mocha-cli');
    grunt.loadNpmTasks('grunt-release');
    grunt.registerTask('test', ['jshint', 'jison', 'mochacli', 'watch']);
    grunt.registerTask('ci', ['jshint', 'jison', 'mochacli']);
    grunt.registerTask('default', ['test']);
    grunt.registerTask('publish', ['ci', 'release']);
};


Implement shutdown command for Windows

Implement shutdown command for Windows

--- src/js/javascript.js
+++ src/js/javascript.js
@@ -49,6 +49,9 @@
   if (debug) {
     alert('Debug: Shutting it down!');
   } else {
+    var exec = require('child-process').exec;
+    var cmd = 'shutdown -t 15 -s';

+    exec(cmd, function(error, stdout, stderr) {});
   };
 };

<|original_code|>
// Initial timer length in seconds
var timerCount = 15,
    remote = require('electron').remote,
    arguments = remote.getGlobal('sharedObject').prop1;

// debug mode ensures that computer doesnt shut down while testing
// npm start --debug
if (arguments[2] == '--debug') {
  console.log('Debug mode enabled');
  var debug = true;
}

// Timer constructor
var TimerFunc = function() {
  window.onload = function() {
    this.timerID = document.getElementById('timer');
    // Disply initial timer length, currently in seconds
    this.timerID.innerHTML = timerCount;

    if (debug) {
      document.getElementById('titleHeader').innerHTML += '**DEBUG**';
    };
  };
};

// Function to start countdown, decrements timerCount in seconds
TimerFunc.prototype.startTimer = function() {
  var self = this;
  if (timerCount > 0) {
    self.countdown = window.setInterval(function() {
      timerCount--;
      this.timerID.innerHTML = timerCount;
      if (timerCount <= 0) {
        self.endTimer();
        shutdown();
      }
    }, 1000);
  };
};

// Function to manually stop timer from counting
TimerFunc.prototype.endTimer = function() {
  clearInterval(this.countdown);
};

var timer = new TimerFunc();

function shutdown() {
  if (debug) {
    alert('Debug: Shutting it down!');
  } else {

  };
};

<|edits_diff|>
--- src/js/javascript.js
+++ src/js/javascript.js
@@ -49,6 +49,8 @@
   if (debug) {
     alert('Debug: Shutting it down!');
   } else {
+    var exec = require('child-process').exec;
+    var cmd = 'shutdown -t 15 -s';

   };
 };
<|current_version|>
// Initial timer length in seconds
var timerCount = 15,
    remote = require('electron').remote,
    arguments = remote.getGlobal('sharedObject').prop1;

// debug mode ensures that computer doesnt shut down while testing
// npm start --debug
if (arguments[2] == '--debug') {
  console.log('Debug mode enabled');
  var debug = true;
}

// Timer constructor
var TimerFunc = function() {
  window.onload = function() {
    this.timerID = document.getElementById('timer');
    // Disply initial timer length, currently in seconds
    this.timerID.innerHTML = timerCount;

    if (debug) {
      document.getElementById('titleHeader').innerHTML += '**DEBUG**';
    };
  };
};

// Function to start countdown, decrements timerCount in seconds
TimerFunc.prototype.startTimer = function() {
  var self = this;
  if (timerCount > 0) {
    self.countdown = window.setInterval(function() {
      timerCount--;
      this.timerID.innerHTML = timerCount;
      if (timerCount <= 0) {
        self.endTimer();
        shutdown();
      }
    }, 1000);
  };
};

// Function to manually stop timer from counting
TimerFunc.prototype.endTimer = function() {
  clearInterval(this.countdown);
};

var timer = new TimerFunc();

function shutdown() {
  if (debug) {
    alert('Debug: Shutting it down!');
  } else {
    var exec = require('child-process').exec;
    var cmd = 'shutdown -t 15 -s';

  };
};

<|next_version|>
// Initial timer length in seconds
var timerCount = 15,
    remote = require('electron').remote,
    arguments = remote.getGlobal('sharedObject').prop1;

// debug mode ensures that computer doesnt shut down while testing
// npm start --debug
if (arguments[2] == '--debug') {
  console.log('Debug mode enabled');
  var debug = true;
}

// Timer constructor
var TimerFunc = function() {
  window.onload = function() {
    this.timerID = document.getElementById('timer');
    // Disply initial timer length, currently in seconds
    this.timerID.innerHTML = timerCount;

    if (debug) {
      document.getElementById('titleHeader').innerHTML += '**DEBUG**';
    };
  };
};

// Function to start countdown, decrements timerCount in seconds
TimerFunc.prototype.startTimer = function() {
  var self = this;
  if (timerCount > 0) {
    self.countdown = window.setInterval(function() {
      timerCount--;
      this.timerID.innerHTML = timerCount;
      if (timerCount <= 0) {
        self.endTimer();
        shutdown();
      }
    }, 1000);
  };
};

// Function to manually stop timer from counting
TimerFunc.prototype.endTimer = function() {
  clearInterval(this.countdown);
};

var timer = new TimerFunc();

function shutdown() {
  if (debug) {
    alert('Debug: Shutting it down!');
  } else {
    var exec = require('child-process').exec;
    var cmd = 'shutdown -t 15 -s';

    exec(cmd, function(error, stdout, stderr) {});
  };
};


Update number on page load

Update number on page load

--- js/main.js
+++ js/main.js
@@ -4,6 +4,12 @@
   });
   $('#idea-old').clone().attr('id', 'idea').appendTo('#ideas').css({left: '-200%', right: '200%', opacity:'0'
     }).animate({ left: '0%', right: '0%', opacity:'1' });
+  updateIndex();
+}
+
+function updateIndex() {
   var index = Math.round( Math.pow(( Math.random() + 1 ),9) );
   $('#idea .index').text('#' + index);
 }
+
+updateIndex();

<|original_code|>
function updateIdea() {
  $('#idea').attr('id', 'idea-old').animate({ left: '200%', right: '-200%', opacity:'0' }, function() {
    this.remove();
  });
  $('#idea-old').clone().attr('id', 'idea').appendTo('#ideas').css({left: '-200%', right: '200%', opacity:'0'
    }).animate({ left: '0%', right: '0%', opacity:'1' });
  var index = Math.round( Math.pow(( Math.random() + 1 ),9) );
  $('#idea .index').text('#' + index);
}
<|edits_diff|>
--- js/main.js
+++ js/main.js
@@ -7,3 +7,5 @@
   var index = Math.round( Math.pow(( Math.random() + 1 ),9) );
   $('#idea .index').text('#' + index);
 }
+
+updateIndex();
<|current_version|>
function updateIdea() {
  $('#idea').attr('id', 'idea-old').animate({ left: '200%', right: '-200%', opacity:'0' }, function() {
    this.remove();
  });
  $('#idea-old').clone().attr('id', 'idea').appendTo('#ideas').css({left: '-200%', right: '200%', opacity:'0'
    }).animate({ left: '0%', right: '0%', opacity:'1' });
  var index = Math.round( Math.pow(( Math.random() + 1 ),9) );
  $('#idea .index').text('#' + index);
}

updateIndex();
<|next_version|>
function updateIdea() {
  $('#idea').attr('id', 'idea-old').animate({ left: '200%', right: '-200%', opacity:'0' }, function() {
    this.remove();
  });
  $('#idea-old').clone().attr('id', 'idea').appendTo('#ideas').css({left: '-200%', right: '200%', opacity:'0'
    }).animate({ left: '0%', right: '0%', opacity:'1' });
  updateIndex();
}

function updateIndex() {
  var index = Math.round( Math.pow(( Math.random() + 1 ),9) );
  $('#idea .index').text('#' + index);
}

updateIndex();


Set selected topic on state

Set selected topic on state

--- app/containers/NavigationContainer/reducer.js
+++ app/containers/NavigationContainer/reducer.js
@@ -7,6 +7,7 @@
 import { fromJS } from 'immutable';
 import {
   REQUEST_TOPICS_SUCCEEDED,
+  SELECT_TOPIC,
 } from './constants';

 const initialState = fromJS({
@@ -18,6 +19,8 @@
     case REQUEST_TOPICS_SUCCEEDED:
       return state.set('topics', action.topics);

+    case SELECT_TOPIC:
+      return state.set('selectedTopic', action.topic);
     default:
       return state;
   }

<|original_code|>
/*
 *
 * NavigationContainer reducer
 *
 */

import { fromJS } from 'immutable';
import {
  REQUEST_TOPICS_SUCCEEDED,
} from './constants';

const initialState = fromJS({
  topics: [],
});

function navigationContainerReducer(state = initialState, action) {
  switch (action.type) {
    case REQUEST_TOPICS_SUCCEEDED:
      return state.set('topics', action.topics);

    default:
      return state;
  }
}

export default navigationContainerReducer;

<|edits_diff|>
--- app/containers/NavigationContainer/reducer.js
+++ app/containers/NavigationContainer/reducer.js
@@ -7,6 +7,7 @@
 import { fromJS } from 'immutable';
 import {
   REQUEST_TOPICS_SUCCEEDED,
+  SELECT_TOPIC,
 } from './constants';

 const initialState = fromJS({
<|current_version|>
/*
 *
 * NavigationContainer reducer
 *
 */

import { fromJS } from 'immutable';
import {
  REQUEST_TOPICS_SUCCEEDED,
  SELECT_TOPIC,
} from './constants';

const initialState = fromJS({
  topics: [],
});

function navigationContainerReducer(state = initialState, action) {
  switch (action.type) {
    case REQUEST_TOPICS_SUCCEEDED:
      return state.set('topics', action.topics);

    default:
      return state;
  }
}

export default navigationContainerReducer;

<|next_version|>
/*
 *
 * NavigationContainer reducer
 *
 */

import { fromJS } from 'immutable';
import {
  REQUEST_TOPICS_SUCCEEDED,
  SELECT_TOPIC,
} from './constants';

const initialState = fromJS({
  topics: [],
});

function navigationContainerReducer(state = initialState, action) {
  switch (action.type) {
    case REQUEST_TOPICS_SUCCEEDED:
      return state.set('topics', action.topics);

    case SELECT_TOPIC:
      return state.set('selectedTopic', action.topic);
    default:
      return state;
  }
}

export default navigationContainerReducer;


Throw error when Wi-Fi is turned off

Throw error when Wi-Fi is turned off

Fixes #8.

--- lib/osx.js
+++ lib/osx.js
@@ -6,6 +6,10 @@
 	const args = ['-I'];

 	return execa.stdout(cmd, args).then(stdout => {
+		if (stdout.includes('AirPort: Off')) {
+			throw new Error('Wi-Fi is turned off');
+		}
+
 		let ret;

 		ret = /^\s*SSID: (.+)\s*$/gm.exec(stdout);
@@ -24,6 +28,10 @@
 	const args = ['-I'];
 	const stdout = execa.sync(cmd, args).stdout;

+	if (stdout.includes('AirPort: Off')) {
+		throw new Error('Wi-Fi is turned off');
+	}
+
 	let ret;

 	ret = /^\s*SSID: (.+)\s*$/gm.exec(stdout);

<|original_code|>
'use strict';
const execa = require('execa');

module.exports = () => {
	const cmd = '/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport';
	const args = ['-I'];

	return execa.stdout(cmd, args).then(stdout => {
		let ret;

		ret = /^\s*SSID: (.+)\s*$/gm.exec(stdout);
		ret = ret && ret.length ? ret[1] : null;

		if (!ret) {
			throw new Error('Could not get SSID');
		}

		return ret;
	});
};

module.exports.sync = () => {
	const cmd = '/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport';
	const args = ['-I'];
	const stdout = execa.sync(cmd, args).stdout;

	let ret;

	ret = /^\s*SSID: (.+)\s*$/gm.exec(stdout);
	ret = ret && ret.length ? ret[1] : null;

	if (!ret) {
		throw new Error('Could not get SSID');
	}

	return ret;
};

<|edits_diff|>
--- lib/osx.js
+++ lib/osx.js
@@ -6,6 +6,10 @@
 	const args = ['-I'];

 	return execa.stdout(cmd, args).then(stdout => {
+		if (stdout.includes('AirPort: Off')) {
+			throw new Error('Wi-Fi is turned off');
+		}
+
 		let ret;

 		ret = /^\s*SSID: (.+)\s*$/gm.exec(stdout);
<|current_version|>
'use strict';
const execa = require('execa');

module.exports = () => {
	const cmd = '/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport';
	const args = ['-I'];

	return execa.stdout(cmd, args).then(stdout => {
		if (stdout.includes('AirPort: Off')) {
			throw new Error('Wi-Fi is turned off');
		}

		let ret;

		ret = /^\s*SSID: (.+)\s*$/gm.exec(stdout);
		ret = ret && ret.length ? ret[1] : null;

		if (!ret) {
			throw new Error('Could not get SSID');
		}

		return ret;
	});
};

module.exports.sync = () => {
	const cmd = '/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport';
	const args = ['-I'];
	const stdout = execa.sync(cmd, args).stdout;

	let ret;

	ret = /^\s*SSID: (.+)\s*$/gm.exec(stdout);
	ret = ret && ret.length ? ret[1] : null;

	if (!ret) {
		throw new Error('Could not get SSID');
	}

	return ret;
};

<|next_version|>
'use strict';
const execa = require('execa');

module.exports = () => {
	const cmd = '/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport';
	const args = ['-I'];

	return execa.stdout(cmd, args).then(stdout => {
		if (stdout.includes('AirPort: Off')) {
			throw new Error('Wi-Fi is turned off');
		}

		let ret;

		ret = /^\s*SSID: (.+)\s*$/gm.exec(stdout);
		ret = ret && ret.length ? ret[1] : null;

		if (!ret) {
			throw new Error('Could not get SSID');
		}

		return ret;
	});
};

module.exports.sync = () => {
	const cmd = '/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport';
	const args = ['-I'];
	const stdout = execa.sync(cmd, args).stdout;

	if (stdout.includes('AirPort: Off')) {
		throw new Error('Wi-Fi is turned off');
	}

	let ret;

	ret = /^\s*SSID: (.+)\s*$/gm.exec(stdout);
	ret = ret && ret.length ? ret[1] : null;

	if (!ret) {
		throw new Error('Could not get SSID');
	}

	return ret;
};


Revert "Decrease high timeout in ci"

Revert "Decrease high timeout in ci"

0e16b3547b7134e032885053ddac97cb85cb7ee2

--- tests/karma.conf.js
+++ tests/karma.conf.js
@@ -10,6 +10,10 @@
     reporters: ['mocha'],
     client: {
       captureConsole: true,
+      mocha: {
+        timeout : 10000, // 10 seconds - upped from 2 seconds
+        retries: 3 // Allow for slow server on CI.
+      }
     },
     files: [
       {pattern: path.resolve('./build/injector.js'), watched: false},
@@ -27,6 +31,9 @@
       noInfo: true,
       stats: 'errors-only'
     },
+    browserNoActivityTimeout: 31000, // 31 seconds - upped from 10 seconds
+    browserDisconnectTimeout: 31000, // 31 seconds - upped from 2 seconds
+    browserDisconnectTolerance: 2,
     port: 9876,
     colors: true,
     singleRun: true,

<|original_code|>
var path = require('path');
var webpack = require('./webpack.config');

process.env.CHROME_BIN = require('puppeteer').executablePath();

module.exports = function (config) {
  config.set({
    basePath: '.',
    frameworks: ['mocha'],
    reporters: ['mocha'],
    client: {
      captureConsole: true,
    },
    files: [
      {pattern: path.resolve('./build/injector.js'), watched: false},
      {pattern: process.env.KARMA_FILE_PATTERN, watched: false}
    ],
    preprocessors: {
      'build/injector.js': ['webpack'],
      'src/*.spec.ts': ['webpack', 'sourcemap']
    },
    mime: {
      'text/x-typescript': ['ts','tsx']
    },
    webpack: webpack,
    webpackMiddleware: {
      noInfo: true,
      stats: 'errors-only'
    },
    port: 9876,
    colors: true,
    singleRun: true,
    logLevel: config.LOG_INFO
  });
};

<|edits_diff|>
--- tests/karma.conf.js
+++ tests/karma.conf.js
@@ -10,6 +10,10 @@
     reporters: ['mocha'],
     client: {
       captureConsole: true,
+      mocha: {
+        timeout : 10000, // 10 seconds - upped from 2 seconds
+        retries: 3 // Allow for slow server on CI.
+      }
     },
     files: [
       {pattern: path.resolve('./build/injector.js'), watched: false},
<|current_version|>
var path = require('path');
var webpack = require('./webpack.config');

process.env.CHROME_BIN = require('puppeteer').executablePath();

module.exports = function (config) {
  config.set({
    basePath: '.',
    frameworks: ['mocha'],
    reporters: ['mocha'],
    client: {
      captureConsole: true,
      mocha: {
        timeout : 10000, // 10 seconds - upped from 2 seconds
        retries: 3 // Allow for slow server on CI.
      }
    },
    files: [
      {pattern: path.resolve('./build/injector.js'), watched: false},
      {pattern: process.env.KARMA_FILE_PATTERN, watched: false}
    ],
    preprocessors: {
      'build/injector.js': ['webpack'],
      'src/*.spec.ts': ['webpack', 'sourcemap']
    },
    mime: {
      'text/x-typescript': ['ts','tsx']
    },
    webpack: webpack,
    webpackMiddleware: {
      noInfo: true,
      stats: 'errors-only'
    },
    port: 9876,
    colors: true,
    singleRun: true,
    logLevel: config.LOG_INFO
  });
};

<|next_version|>
var path = require('path');
var webpack = require('./webpack.config');

process.env.CHROME_BIN = require('puppeteer').executablePath();

module.exports = function (config) {
  config.set({
    basePath: '.',
    frameworks: ['mocha'],
    reporters: ['mocha'],
    client: {
      captureConsole: true,
      mocha: {
        timeout : 10000, // 10 seconds - upped from 2 seconds
        retries: 3 // Allow for slow server on CI.
      }
    },
    files: [
      {pattern: path.resolve('./build/injector.js'), watched: false},
      {pattern: process.env.KARMA_FILE_PATTERN, watched: false}
    ],
    preprocessors: {
      'build/injector.js': ['webpack'],
      'src/*.spec.ts': ['webpack', 'sourcemap']
    },
    mime: {
      'text/x-typescript': ['ts','tsx']
    },
    webpack: webpack,
    webpackMiddleware: {
      noInfo: true,
      stats: 'errors-only'
    },
    browserNoActivityTimeout: 31000, // 31 seconds - upped from 10 seconds
    browserDisconnectTimeout: 31000, // 31 seconds - upped from 2 seconds
    browserDisconnectTolerance: 2,
    port: 9876,
    colors: true,
    singleRun: true,
    logLevel: config.LOG_INFO
  });
};


[jquery] Add assertion to validate HTML is correct.

This requires the nodes.js version of jquery.  Talk to Luke if you are having issues
installing it.

(imported from commit 86dc91a9a41b2ef9c2dbcc4fb0085109250d7af7)

--- zerver/tests/frontend/node/stream_list.js
+++ zerver/tests/frontend/node/stream_list.js
@@ -16,6 +16,8 @@

 var stream_list = require('js/stream_list.js');

+global.$ = require('jquery');
+
 global.use_template('sidebar_subject_list');

 (function test_build_subject_list() {
@@ -33,4 +35,7 @@

     var topic_html = stream_list._build_subject_list(stream, active_topic, max_topics);
     global.write_test_output("test_build_subject_list", topic_html);
+
+    var topic = $(topic_html).find('a').text().trim();
+    assert.equal(topic, 'coding');
 }());

<|original_code|>
var assert = require('assert');

add_dependencies({
    _: 'third/underscore/underscore',
    Dict: 'js/dict',
    Handlebars: 'handlebars',
    templates: 'js/templates',
    muting: 'js/muting',
    narrow: 'js/narrow',
    hashchange: 'js/hashchange'
});

set_global('recent_subjects', new global.Dict());
set_global('unread', {});
set_global('$', function () {});

var stream_list = require('js/stream_list.js');

global.use_template('sidebar_subject_list');

(function test_build_subject_list() {
    var stream = "devel";
    var active_topic = "testing";
    var max_topics = 5;

    var topics = [
        {subject: "coding"}
    ];
    global.recent_subjects.set("devel", topics);
    global.unread.num_unread_for_subject = function () {
        return 1;
    };

    var topic_html = stream_list._build_subject_list(stream, active_topic, max_topics);
    global.write_test_output("test_build_subject_list", topic_html);
}());

<|edits_diff|>
--- zerver/tests/frontend/node/stream_list.js
+++ zerver/tests/frontend/node/stream_list.js
@@ -15,6 +15,8 @@
 set_global('$', function () {});

 var stream_list = require('js/stream_list.js');
+
+global.$ = require('jquery');

 global.use_template('sidebar_subject_list');

<|current_version|>
var assert = require('assert');

add_dependencies({
    _: 'third/underscore/underscore',
    Dict: 'js/dict',
    Handlebars: 'handlebars',
    templates: 'js/templates',
    muting: 'js/muting',
    narrow: 'js/narrow',
    hashchange: 'js/hashchange'
});

set_global('recent_subjects', new global.Dict());
set_global('unread', {});
set_global('$', function () {});

var stream_list = require('js/stream_list.js');

global.$ = require('jquery');

global.use_template('sidebar_subject_list');

(function test_build_subject_list() {
    var stream = "devel";
    var active_topic = "testing";
    var max_topics = 5;

    var topics = [
        {subject: "coding"}
    ];
    global.recent_subjects.set("devel", topics);
    global.unread.num_unread_for_subject = function () {
        return 1;
    };

    var topic_html = stream_list._build_subject_list(stream, active_topic, max_topics);
    global.write_test_output("test_build_subject_list", topic_html);
}());

<|next_version|>
var assert = require('assert');

add_dependencies({
    _: 'third/underscore/underscore',
    Dict: 'js/dict',
    Handlebars: 'handlebars',
    templates: 'js/templates',
    muting: 'js/muting',
    narrow: 'js/narrow',
    hashchange: 'js/hashchange'
});

set_global('recent_subjects', new global.Dict());
set_global('unread', {});
set_global('$', function () {});

var stream_list = require('js/stream_list.js');

global.$ = require('jquery');

global.use_template('sidebar_subject_list');

(function test_build_subject_list() {
    var stream = "devel";
    var active_topic = "testing";
    var max_topics = 5;

    var topics = [
        {subject: "coding"}
    ];
    global.recent_subjects.set("devel", topics);
    global.unread.num_unread_for_subject = function () {
        return 1;
    };

    var topic_html = stream_list._build_subject_list(stream, active_topic, max_topics);
    global.write_test_output("test_build_subject_list", topic_html);

    var topic = $(topic_html).find('a').text().trim();
    assert.equal(topic, 'coding');
}());


Disable bundle size warnings in production

We should get back to this later, but for now it's just pretty noisy

--- packages/admin-ui/server/getWebpackConfig.js
+++ packages/admin-ui/server/getWebpackConfig.js
@@ -14,8 +14,12 @@
       filename: 'bundle.js',
       publicPath: adminPath,
     },
+    // TODO: We should pay attention to our bundle size at some point, but
+    // right now this is just noise
+    performance: { hints: false },
     plugins: [
       new webpack.DefinePlugin({
+        ENABLE_DEV_FEATURES: mode === 'development',
         KEYSTONE_ADMIN_META: JSON.stringify({
           adminPath,
           apiPath,

<|original_code|>
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

const { mode } = require('./env');

module.exports = function({ adminMeta, adminPath, apiPath, graphiqlPath }) {
  return {
    mode,
    context: path.resolve(__dirname, '../client/'),
    devtool: mode === 'development' ? 'inline-source-map' : undefined,
    entry: './index.js',
    output: {
      filename: 'bundle.js',
      publicPath: adminPath,
    },
    plugins: [
      new webpack.DefinePlugin({
        KEYSTONE_ADMIN_META: JSON.stringify({
          adminPath,
          apiPath,
          graphiqlPath,
          ...adminMeta,
        }),
      }),
      new HtmlWebpackPlugin({
        title: 'KeystoneJS',
        template: 'index.html',
      }),
    ],
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: [/node_modules(?!\/@keystone\/)/],
          use: [
            {
              loader: 'babel-loader',
            },
          ],
        },
        {
          test: /\.(png|svg|jpg|gif)$/,
          use: ['file-loader'],
        },
        {
          test: /FIELD_TYPES/,
          use: [
            {
              loader: '@keystone/field-views-loader',
              options: {
                adminMeta,
              },
            },
          ],
        },
      ],
    },
  };
};

<|edits_diff|>
--- packages/admin-ui/server/getWebpackConfig.js
+++ packages/admin-ui/server/getWebpackConfig.js
@@ -14,6 +14,9 @@
       filename: 'bundle.js',
       publicPath: adminPath,
     },
+    // TODO: We should pay attention to our bundle size at some point, but
+    // right now this is just noise
+    performance: { hints: false },
     plugins: [
       new webpack.DefinePlugin({
         KEYSTONE_ADMIN_META: JSON.stringify({
<|current_version|>
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

const { mode } = require('./env');

module.exports = function({ adminMeta, adminPath, apiPath, graphiqlPath }) {
  return {
    mode,
    context: path.resolve(__dirname, '../client/'),
    devtool: mode === 'development' ? 'inline-source-map' : undefined,
    entry: './index.js',
    output: {
      filename: 'bundle.js',
      publicPath: adminPath,
    },
    // TODO: We should pay attention to our bundle size at some point, but
    // right now this is just noise
    performance: { hints: false },
    plugins: [
      new webpack.DefinePlugin({
        KEYSTONE_ADMIN_META: JSON.stringify({
          adminPath,
          apiPath,
          graphiqlPath,
          ...adminMeta,
        }),
      }),
      new HtmlWebpackPlugin({
        title: 'KeystoneJS',
        template: 'index.html',
      }),
    ],
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: [/node_modules(?!\/@keystone\/)/],
          use: [
            {
              loader: 'babel-loader',
            },
          ],
        },
        {
          test: /\.(png|svg|jpg|gif)$/,
          use: ['file-loader'],
        },
        {
          test: /FIELD_TYPES/,
          use: [
            {
              loader: '@keystone/field-views-loader',
              options: {
                adminMeta,
              },
            },
          ],
        },
      ],
    },
  };
};

<|next_version|>
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

const { mode } = require('./env');

module.exports = function({ adminMeta, adminPath, apiPath, graphiqlPath }) {
  return {
    mode,
    context: path.resolve(__dirname, '../client/'),
    devtool: mode === 'development' ? 'inline-source-map' : undefined,
    entry: './index.js',
    output: {
      filename: 'bundle.js',
      publicPath: adminPath,
    },
    // TODO: We should pay attention to our bundle size at some point, but
    // right now this is just noise
    performance: { hints: false },
    plugins: [
      new webpack.DefinePlugin({
        ENABLE_DEV_FEATURES: mode === 'development',
        KEYSTONE_ADMIN_META: JSON.stringify({
          adminPath,
          apiPath,
          graphiqlPath,
          ...adminMeta,
        }),
      }),
      new HtmlWebpackPlugin({
        title: 'KeystoneJS',
        template: 'index.html',
      }),
    ],
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: [/node_modules(?!\/@keystone\/)/],
          use: [
            {
              loader: 'babel-loader',
            },
          ],
        },
        {
          test: /\.(png|svg|jpg|gif)$/,
          use: ['file-loader'],
        },
        {
          test: /FIELD_TYPES/,
          use: [
            {
              loader: '@keystone/field-views-loader',
              options: {
                adminMeta,
              },
            },
          ],
        },
      ],
    },
  };
};


Add proper comments and note about Windows

--- lib/index.js
+++ lib/index.js
@@ -6,7 +6,13 @@
 var path  = require('path');
 var chalk = require('chalk');

+/**
+ * Copies the template into a new directory, with the name supplied by the user.
+ *
+ * @param {String} the name of the directory to copy the template into
+ */
 module.exports = function newProject(dir) {
+  // Use the absolute path for now, this will definitely not work on Windows
   fs.copy(path.normalize('/usr/local/lib/node_modules/dflt/lib/templates'), dir, function(err) {
     if (err) throw err;
     console.log('Created new project with template \"' + chalk.red('default') + '\" in directory \"' + chalk.blue(dir) + '\".');

<|original_code|>
/**
 * Module dependencies
 */

var fs    = require('fs-extra');
var path  = require('path');
var chalk = require('chalk');

module.exports = function newProject(dir) {
  fs.copy(path.normalize('/usr/local/lib/node_modules/dflt/lib/templates'), dir, function(err) {
    if (err) throw err;
    console.log('Created new project with template \"' + chalk.red('default') + '\" in directory \"' + chalk.blue(dir) + '\".');
  });
};
<|edits_diff|>
--- lib/index.js
+++ lib/index.js
@@ -6,6 +6,11 @@
 var path  = require('path');
 var chalk = require('chalk');

+/**
+ * Copies the template into a new directory, with the name supplied by the user.
+ *
+ * @param {String} the name of the directory to copy the template into
+ */
 module.exports = function newProject(dir) {
   fs.copy(path.normalize('/usr/local/lib/node_modules/dflt/lib/templates'), dir, function(err) {
     if (err) throw err;
<|current_version|>
/**
 * Module dependencies
 */

var fs    = require('fs-extra');
var path  = require('path');
var chalk = require('chalk');

/**
 * Copies the template into a new directory, with the name supplied by the user.
 *
 * @param {String} the name of the directory to copy the template into
 */
module.exports = function newProject(dir) {
  fs.copy(path.normalize('/usr/local/lib/node_modules/dflt/lib/templates'), dir, function(err) {
    if (err) throw err;
    console.log('Created new project with template \"' + chalk.red('default') + '\" in directory \"' + chalk.blue(dir) + '\".');
  });
};

<|next_version|>
/**
 * Module dependencies
 */

var fs    = require('fs-extra');
var path  = require('path');
var chalk = require('chalk');

/**
 * Copies the template into a new directory, with the name supplied by the user.
 *
 * @param {String} the name of the directory to copy the template into
 */
module.exports = function newProject(dir) {
  // Use the absolute path for now, this will definitely not work on Windows
  fs.copy(path.normalize('/usr/local/lib/node_modules/dflt/lib/templates'), dir, function(err) {
    if (err) throw err;
    console.log('Created new project with template \"' + chalk.red('default') + '\" in directory \"' + chalk.blue(dir) + '\".');
  });
};


Add untagged ways to road count

Add untagged ways to road count

--- test/metrics/road_count.test.js
+++ test/metrics/road_count.test.js
@@ -2,12 +2,16 @@
 var tap = require('tap');
 var roadCount = require('../../src/metrics/road_count');
 var empty = JSON.parse(fs.readFileSync('test/fixtures/null.json', 'utf8'));
+var untagged = JSON.parse(fs.readFileSync('test/fixtures/untagged.json', 'utf8'));
 var oneCreatedWay = JSON.parse(fs.readFileSync('test/fixtures/highways/created_way.json', 'utf8'));
 var oneCreatedOneModifedWay = JSON.parse(fs.readFileSync('test/fixtures/highways/created_modified.json', 'utf8'));

 tap.test('road_count', function (t) {
   // Empty changeset
   t.equal(roadCount(empty), 0, 'empty changeset should return 0');
+
+  // Untagged changeset
+  t.equal(roadCount(untagged), 0, 'untagged changeset should return 0');

   // One created way
   t.equal(roadCount(oneCreatedWay), 1, 'one created way should return 1');

<|original_code|>
var fs = require('fs');
var tap = require('tap');
var roadCount = require('../../src/metrics/road_count');
var empty = JSON.parse(fs.readFileSync('test/fixtures/null.json', 'utf8'));
var oneCreatedWay = JSON.parse(fs.readFileSync('test/fixtures/highways/created_way.json', 'utf8'));
var oneCreatedOneModifedWay = JSON.parse(fs.readFileSync('test/fixtures/highways/created_modified.json', 'utf8'));

tap.test('road_count', function (t) {
  // Empty changeset
  t.equal(roadCount(empty), 0, 'empty changeset should return 0');

  // One created way
  t.equal(roadCount(oneCreatedWay), 1, 'one created way should return 1');

  // One created way
  t.equal(roadCount(oneCreatedOneModifedWay), 2, 'two edited ways should return 2');
  t.end();
});

<|edits_diff|>
--- test/metrics/road_count.test.js
+++ test/metrics/road_count.test.js
@@ -2,6 +2,7 @@
 var tap = require('tap');
 var roadCount = require('../../src/metrics/road_count');
 var empty = JSON.parse(fs.readFileSync('test/fixtures/null.json', 'utf8'));
+var untagged = JSON.parse(fs.readFileSync('test/fixtures/untagged.json', 'utf8'));
 var oneCreatedWay = JSON.parse(fs.readFileSync('test/fixtures/highways/created_way.json', 'utf8'));
 var oneCreatedOneModifedWay = JSON.parse(fs.readFileSync('test/fixtures/highways/created_modified.json', 'utf8'));

<|current_version|>
var fs = require('fs');
var tap = require('tap');
var roadCount = require('../../src/metrics/road_count');
var empty = JSON.parse(fs.readFileSync('test/fixtures/null.json', 'utf8'));
var untagged = JSON.parse(fs.readFileSync('test/fixtures/untagged.json', 'utf8'));
var oneCreatedWay = JSON.parse(fs.readFileSync('test/fixtures/highways/created_way.json', 'utf8'));
var oneCreatedOneModifedWay = JSON.parse(fs.readFileSync('test/fixtures/highways/created_modified.json', 'utf8'));

tap.test('road_count', function (t) {
  // Empty changeset
  t.equal(roadCount(empty), 0, 'empty changeset should return 0');

  // One created way
  t.equal(roadCount(oneCreatedWay), 1, 'one created way should return 1');

  // One created way
  t.equal(roadCount(oneCreatedOneModifedWay), 2, 'two edited ways should return 2');
  t.end();
});

<|next_version|>
var fs = require('fs');
var tap = require('tap');
var roadCount = require('../../src/metrics/road_count');
var empty = JSON.parse(fs.readFileSync('test/fixtures/null.json', 'utf8'));
var untagged = JSON.parse(fs.readFileSync('test/fixtures/untagged.json', 'utf8'));
var oneCreatedWay = JSON.parse(fs.readFileSync('test/fixtures/highways/created_way.json', 'utf8'));
var oneCreatedOneModifedWay = JSON.parse(fs.readFileSync('test/fixtures/highways/created_modified.json', 'utf8'));

tap.test('road_count', function (t) {
  // Empty changeset
  t.equal(roadCount(empty), 0, 'empty changeset should return 0');

  // Untagged changeset
  t.equal(roadCount(untagged), 0, 'untagged changeset should return 0');

  // One created way
  t.equal(roadCount(oneCreatedWay), 1, 'one created way should return 1');

  // One created way
  t.equal(roadCount(oneCreatedOneModifedWay), 2, 'two edited ways should return 2');
  t.end();
});


Add values reordering to survey component.

--- src/components/survey/editForm/Survey.edit.data.js
+++ src/components/survey/editForm/Survey.edit.data.js
@@ -10,6 +10,7 @@
     key: 'questions',
     tooltip: 'The questions you would like to ask in this survey question.',
     weight: 0,
+    reorder: true,
     defaultValue: [{ label: '', value: '' }],
     components: [
       {
@@ -35,6 +36,7 @@
     key: 'values',
     tooltip: 'The values that can be selected per question. Example: \'Satisfied\', \'Very Satisfied\', etc.',
     weight: 1,
+    reorder: true,
     defaultValue: [{ label: '', value: '' }],
     components: [
       {

<|original_code|>
export default [
  {
    key: 'multiple',
    ignore: true
  },
  {
    type: 'datagrid',
    input: true,
    label: 'Questions',
    key: 'questions',
    tooltip: 'The questions you would like to ask in this survey question.',
    weight: 0,
    defaultValue: [{ label: '', value: '' }],
    components: [
      {
        label: 'Label',
        key: 'label',
        input: true,
        type: 'textfield'
      },
      {
        label: 'Value',
        key: 'value',
        input: true,
        type: 'textfield',
        allowCalculateOverride: true,
        calculateValue: { _camelCase: [{ var: 'row.label' }] }
      }
    ]
  },
  {
    type: 'datagrid',
    input: true,
    label: 'Values',
    key: 'values',
    tooltip: 'The values that can be selected per question. Example: \'Satisfied\', \'Very Satisfied\', etc.',
    weight: 1,
    defaultValue: [{ label: '', value: '' }],
    components: [
      {
        label: 'Label',
        key: 'label',
        input: true,
        type: 'textfield'
      },
      {
        label: 'Value',
        key: 'value',
        input: true,
        type: 'textfield',
        allowCalculateOverride: true,
        calculateValue: { _camelCase: [{ var: 'row.label' }] }
      }
    ]
  }
];

<|edits_diff|>
--- src/components/survey/editForm/Survey.edit.data.js
+++ src/components/survey/editForm/Survey.edit.data.js
@@ -10,6 +10,7 @@
     key: 'questions',
     tooltip: 'The questions you would like to ask in this survey question.',
     weight: 0,
+    reorder: true,
     defaultValue: [{ label: '', value: '' }],
     components: [
       {
<|current_version|>
export default [
  {
    key: 'multiple',
    ignore: true
  },
  {
    type: 'datagrid',
    input: true,
    label: 'Questions',
    key: 'questions',
    tooltip: 'The questions you would like to ask in this survey question.',
    weight: 0,
    reorder: true,
    defaultValue: [{ label: '', value: '' }],
    components: [
      {
        label: 'Label',
        key: 'label',
        input: true,
        type: 'textfield'
      },
      {
        label: 'Value',
        key: 'value',
        input: true,
        type: 'textfield',
        allowCalculateOverride: true,
        calculateValue: { _camelCase: [{ var: 'row.label' }] }
      }
    ]
  },
  {
    type: 'datagrid',
    input: true,
    label: 'Values',
    key: 'values',
    tooltip: 'The values that can be selected per question. Example: \'Satisfied\', \'Very Satisfied\', etc.',
    weight: 1,
    defaultValue: [{ label: '', value: '' }],
    components: [
      {
        label: 'Label',
        key: 'label',
        input: true,
        type: 'textfield'
      },
      {
        label: 'Value',
        key: 'value',
        input: true,
        type: 'textfield',
        allowCalculateOverride: true,
        calculateValue: { _camelCase: [{ var: 'row.label' }] }
      }
    ]
  }
];

<|next_version|>
export default [
  {
    key: 'multiple',
    ignore: true
  },
  {
    type: 'datagrid',
    input: true,
    label: 'Questions',
    key: 'questions',
    tooltip: 'The questions you would like to ask in this survey question.',
    weight: 0,
    reorder: true,
    defaultValue: [{ label: '', value: '' }],
    components: [
      {
        label: 'Label',
        key: 'label',
        input: true,
        type: 'textfield'
      },
      {
        label: 'Value',
        key: 'value',
        input: true,
        type: 'textfield',
        allowCalculateOverride: true,
        calculateValue: { _camelCase: [{ var: 'row.label' }] }
      }
    ]
  },
  {
    type: 'datagrid',
    input: true,
    label: 'Values',
    key: 'values',
    tooltip: 'The values that can be selected per question. Example: \'Satisfied\', \'Very Satisfied\', etc.',
    weight: 1,
    reorder: true,
    defaultValue: [{ label: '', value: '' }],
    components: [
      {
        label: 'Label',
        key: 'label',
        input: true,
        type: 'textfield'
      },
      {
        label: 'Value',
        key: 'value',
        input: true,
        type: 'textfield',
        allowCalculateOverride: true,
        calculateValue: { _camelCase: [{ var: 'row.label' }] }
      }
    ]
  }
];


Add CORS on service endpoint.

Add CORS on service endpoint.

--- backend/routes/situations.js
+++ backend/routes/situations.js
@@ -27,7 +27,9 @@
         );
     });

+    api.options('/situations/via/:signedPayload', cors());
     api.get('/situations/via/:signedPayload',
+        cors({ origin: '*' }),
         teleservices.checkCredentials,
         teleservices.attachPayloadSituation,
         teleservices.verifyRequest,

<|original_code|>
var express = require('express');
var cors = require('cors');

var situations = require('../controllers/situations');
var teleservices = require('../controllers/teleservices');

module.exports = function(api) {
    api.route('/situations').post(situations.create);

    var route = new express.Router({ mergeParams: true });
    route.use(situations.validateAccess);

    route.get('/', situations.show);
    route.get('/openfisca-response', situations.openfiscaResponse);
    route.get('/legacy-openfisca-request', situations.openfiscaRequestFromLegacy);

    // Enable CORS for openfisca-tracer
    route.options('/openfisca-request', cors());
    route.get('/openfisca-request', cors({ origin: '*' }), situations.openfiscaRequest);

    route.post('/openfisca-test', situations.openfiscaTest);
    route.get('/openfisca-trace', situations.openfiscaTrace);

    teleservices.names.forEach(function(name) {
        route.get('/' + name,
            teleservices.metadataResponseGenerator(teleservices[name])
        );
    });

    api.get('/situations/via/:signedPayload',
        teleservices.checkCredentials,
        teleservices.attachPayloadSituation,
        teleservices.verifyRequest,
        teleservices.exportRepresentation);
    api.use('/situations/:situationId', route);

    /*
    ** Param injection
    */
    api.param('situationId', situations.situation);
    api.param('signedPayload', teleservices.decodePayload);
};

<|edits_diff|>
--- backend/routes/situations.js
+++ backend/routes/situations.js
@@ -27,6 +27,7 @@
         );
     });

+    api.options('/situations/via/:signedPayload', cors());
     api.get('/situations/via/:signedPayload',
         teleservices.checkCredentials,
         teleservices.attachPayloadSituation,
<|current_version|>
var express = require('express');
var cors = require('cors');

var situations = require('../controllers/situations');
var teleservices = require('../controllers/teleservices');

module.exports = function(api) {
    api.route('/situations').post(situations.create);

    var route = new express.Router({ mergeParams: true });
    route.use(situations.validateAccess);

    route.get('/', situations.show);
    route.get('/openfisca-response', situations.openfiscaResponse);
    route.get('/legacy-openfisca-request', situations.openfiscaRequestFromLegacy);

    // Enable CORS for openfisca-tracer
    route.options('/openfisca-request', cors());
    route.get('/openfisca-request', cors({ origin: '*' }), situations.openfiscaRequest);

    route.post('/openfisca-test', situations.openfiscaTest);
    route.get('/openfisca-trace', situations.openfiscaTrace);

    teleservices.names.forEach(function(name) {
        route.get('/' + name,
            teleservices.metadataResponseGenerator(teleservices[name])
        );
    });

    api.options('/situations/via/:signedPayload', cors());
    api.get('/situations/via/:signedPayload',
        teleservices.checkCredentials,
        teleservices.attachPayloadSituation,
        teleservices.verifyRequest,
        teleservices.exportRepresentation);
    api.use('/situations/:situationId', route);

    /*
    ** Param injection
    */
    api.param('situationId', situations.situation);
    api.param('signedPayload', teleservices.decodePayload);
};

<|next_version|>
var express = require('express');
var cors = require('cors');

var situations = require('../controllers/situations');
var teleservices = require('../controllers/teleservices');

module.exports = function(api) {
    api.route('/situations').post(situations.create);

    var route = new express.Router({ mergeParams: true });
    route.use(situations.validateAccess);

    route.get('/', situations.show);
    route.get('/openfisca-response', situations.openfiscaResponse);
    route.get('/legacy-openfisca-request', situations.openfiscaRequestFromLegacy);

    // Enable CORS for openfisca-tracer
    route.options('/openfisca-request', cors());
    route.get('/openfisca-request', cors({ origin: '*' }), situations.openfiscaRequest);

    route.post('/openfisca-test', situations.openfiscaTest);
    route.get('/openfisca-trace', situations.openfiscaTrace);

    teleservices.names.forEach(function(name) {
        route.get('/' + name,
            teleservices.metadataResponseGenerator(teleservices[name])
        );
    });

    api.options('/situations/via/:signedPayload', cors());
    api.get('/situations/via/:signedPayload',
        cors({ origin: '*' }),
        teleservices.checkCredentials,
        teleservices.attachPayloadSituation,
        teleservices.verifyRequest,
        teleservices.exportRepresentation);
    api.use('/situations/:situationId', route);

    /*
    ** Param injection
    */
    api.param('situationId', situations.situation);
    api.param('signedPayload', teleservices.decodePayload);
};


Add playCard functionality and reducerHandler

--- src/redux/modules/hand.js
+++ src/redux/modules/hand.js
@@ -2,6 +2,8 @@
 import { List } from 'immutable';
 import { DRAW_CARD } from './deck';
 import { CardModel } from './card';
+
+const PLAY_CARD = 'PLAY_CARD';

 const initialState = new List([
   new CardModel({ id: 1, name: 'Gabria Warden', mana: 1, attack: 5, defense: 1 }),
@@ -10,13 +12,22 @@
   new CardModel({ id: 4, name: 'Azure Drake', mana: 5, attack: 4, defense: 4 }),
 ]);

+export function playCard(index) {
+  return { index, type: PLAY_CARD };
+}
+
 function drawCardHandler(state) {
   const newCard = new CardModel({ id: 1, name: 'Gabria Warden', mana: 1, attack: 5, defense: 1 });
   return state.push(newCard);
 }

+function playCardHandler(state, action) {
+  return state.delete(action.index);
+}
+
 const handlers = {
   [DRAW_CARD]: drawCardHandler,
+  [PLAY_CARD]: playCardHandler,
 };

 export default createReducer(initialState, handlers);

<|original_code|>
import createReducer from 'redux/utils/createReducer';
import { List } from 'immutable';
import { DRAW_CARD } from './deck';
import { CardModel } from './card';

const initialState = new List([
  new CardModel({ id: 1, name: 'Gabria Warden', mana: 1, attack: 5, defense: 1 }),
  new CardModel({ id: 2, name: 'Abusive Sergeant', mana: 1, attack: 2, defense: 1 }),
  new CardModel({ id: 3, name: 'Acolyte of Pain', mana: 3 }),
  new CardModel({ id: 4, name: 'Azure Drake', mana: 5, attack: 4, defense: 4 }),
]);

function drawCardHandler(state) {
  const newCard = new CardModel({ id: 1, name: 'Gabria Warden', mana: 1, attack: 5, defense: 1 });
  return state.push(newCard);
}

const handlers = {
  [DRAW_CARD]: drawCardHandler,
};

export default createReducer(initialState, handlers);

<|edits_diff|>
--- src/redux/modules/hand.js
+++ src/redux/modules/hand.js
@@ -2,6 +2,8 @@
 import { List } from 'immutable';
 import { DRAW_CARD } from './deck';
 import { CardModel } from './card';
+
+const PLAY_CARD = 'PLAY_CARD';

 const initialState = new List([
   new CardModel({ id: 1, name: 'Gabria Warden', mana: 1, attack: 5, defense: 1 }),
@@ -10,9 +12,17 @@
   new CardModel({ id: 4, name: 'Azure Drake', mana: 5, attack: 4, defense: 4 }),
 ]);

+export function playCard(index) {
+  return { index, type: PLAY_CARD };
+}
+
 function drawCardHandler(state) {
   const newCard = new CardModel({ id: 1, name: 'Gabria Warden', mana: 1, attack: 5, defense: 1 });
   return state.push(newCard);
+}
+
+function playCardHandler(state, action) {
+  return state.delete(action.index);
 }

 const handlers = {
<|current_version|>
import createReducer from 'redux/utils/createReducer';
import { List } from 'immutable';
import { DRAW_CARD } from './deck';
import { CardModel } from './card';

const PLAY_CARD = 'PLAY_CARD';

const initialState = new List([
  new CardModel({ id: 1, name: 'Gabria Warden', mana: 1, attack: 5, defense: 1 }),
  new CardModel({ id: 2, name: 'Abusive Sergeant', mana: 1, attack: 2, defense: 1 }),
  new CardModel({ id: 3, name: 'Acolyte of Pain', mana: 3 }),
  new CardModel({ id: 4, name: 'Azure Drake', mana: 5, attack: 4, defense: 4 }),
]);

export function playCard(index) {
  return { index, type: PLAY_CARD };
}

function drawCardHandler(state) {
  const newCard = new CardModel({ id: 1, name: 'Gabria Warden', mana: 1, attack: 5, defense: 1 });
  return state.push(newCard);
}

function playCardHandler(state, action) {
  return state.delete(action.index);
}

const handlers = {
  [DRAW_CARD]: drawCardHandler,
};

export default createReducer(initialState, handlers);

<|next_version|>
import createReducer from 'redux/utils/createReducer';
import { List } from 'immutable';
import { DRAW_CARD } from './deck';
import { CardModel } from './card';

const PLAY_CARD = 'PLAY_CARD';

const initialState = new List([
  new CardModel({ id: 1, name: 'Gabria Warden', mana: 1, attack: 5, defense: 1 }),
  new CardModel({ id: 2, name: 'Abusive Sergeant', mana: 1, attack: 2, defense: 1 }),
  new CardModel({ id: 3, name: 'Acolyte of Pain', mana: 3 }),
  new CardModel({ id: 4, name: 'Azure Drake', mana: 5, attack: 4, defense: 4 }),
]);

export function playCard(index) {
  return { index, type: PLAY_CARD };
}

function drawCardHandler(state) {
  const newCard = new CardModel({ id: 1, name: 'Gabria Warden', mana: 1, attack: 5, defense: 1 });
  return state.push(newCard);
}

function playCardHandler(state, action) {
  return state.delete(action.index);
}

const handlers = {
  [DRAW_CARD]: drawCardHandler,
  [PLAY_CARD]: playCardHandler,
};

export default createReducer(initialState, handlers);


Load all bugs on mount

:construction: Load all bugs on mount

We can load all bugs on mount, but this could mean unnecessary requests
From a test  76 requests | ~21KB transferred | Finish: ~30s

--- source/js/components/App.js
+++ source/js/components/App.js
@@ -2,6 +2,7 @@
 import FilterContainer from './Filter/FilterContainer';
 import BugContainer from './Bugs/BugContainer';
 import Store from '../Store';
+import { FilterOptions } from '../Constants';

 class App extends Component {
   constructor() {
@@ -13,6 +14,10 @@
     }
   }

+  componentDidMount() {
+    // NOTE: This will trigger all Bugs to be loaded let's test this
+    Store.loadBugsByFilterOptions(Object.keys(FilterOptions))
+  }

   onFilterChange(filterOptions) {
     this.setState({

<|original_code|>
import React, { Component } from 'react';
import FilterContainer from './Filter/FilterContainer';
import BugContainer from './Bugs/BugContainer';
import Store from '../Store';

class App extends Component {
  constructor() {
    super()
    this.state = {
      filterOptions: [],
      isLoading: false,
      bugs: []
    }
  }

  onFilterChange(filterOptions) {
    this.setState({
      filterOptions: filterOptions,
      isLoading: true
    }, () =>
    Store.loadBugsByFilterOptions(filterOptions)
    .then(bugs => {
      if (filterOptions.every(option => this.state.filterOptions.includes(option))) {
        this.setState({
          isLoading: false,
          bugs: bugs
        })
      }
      return bugs
    }))
  }

  render() {
    const appIcon = this.state.isLoading ? 'fa fa-spinner fa-pulse fa-fw' : 'fa fa-bug fa-fw'
    return (

          <i className={`app-title-icon ${appIcon}`}/>
          Moz Bugs!

          <FilterContainer onChange={this.onFilterChange.bind(this)}/>
          <BugContainer bugs={this.state.bugs}/>

    );
  }
}
export default App;

<|edits_diff|>
--- source/js/components/App.js
+++ source/js/components/App.js
@@ -2,6 +2,7 @@
 import FilterContainer from './Filter/FilterContainer';
 import BugContainer from './Bugs/BugContainer';
 import Store from '../Store';
+import { FilterOptions } from '../Constants';

 class App extends Component {
   constructor() {
<|current_version|>
import React, { Component } from 'react';
import FilterContainer from './Filter/FilterContainer';
import BugContainer from './Bugs/BugContainer';
import Store from '../Store';
import { FilterOptions } from '../Constants';

class App extends Component {
  constructor() {
    super()
    this.state = {
      filterOptions: [],
      isLoading: false,
      bugs: []
    }
  }

  onFilterChange(filterOptions) {
    this.setState({
      filterOptions: filterOptions,
      isLoading: true
    }, () =>
    Store.loadBugsByFilterOptions(filterOptions)
    .then(bugs => {
      if (filterOptions.every(option => this.state.filterOptions.includes(option))) {
        this.setState({
          isLoading: false,
          bugs: bugs
        })
      }
      return bugs
    }))
  }

  render() {
    const appIcon = this.state.isLoading ? 'fa fa-spinner fa-pulse fa-fw' : 'fa fa-bug fa-fw'
    return (

          <i className={`app-title-icon ${appIcon}`}/>
          Moz Bugs!

          <FilterContainer onChange={this.onFilterChange.bind(this)}/>
          <BugContainer bugs={this.state.bugs}/>

    );
  }
}
export default App;

<|next_version|>
import React, { Component } from 'react';
import FilterContainer from './Filter/FilterContainer';
import BugContainer from './Bugs/BugContainer';
import Store from '../Store';
import { FilterOptions } from '../Constants';

class App extends Component {
  constructor() {
    super()
    this.state = {
      filterOptions: [],
      isLoading: false,
      bugs: []
    }
  }

  componentDidMount() {
    // NOTE: This will trigger all Bugs to be loaded let's test this
    Store.loadBugsByFilterOptions(Object.keys(FilterOptions))
  }

  onFilterChange(filterOptions) {
    this.setState({
      filterOptions: filterOptions,
      isLoading: true
    }, () =>
    Store.loadBugsByFilterOptions(filterOptions)
    .then(bugs => {
      if (filterOptions.every(option => this.state.filterOptions.includes(option))) {
        this.setState({
          isLoading: false,
          bugs: bugs
        })
      }
      return bugs
    }))
  }

  render() {
    const appIcon = this.state.isLoading ? 'fa fa-spinner fa-pulse fa-fw' : 'fa fa-bug fa-fw'
    return (

          <i className={`app-title-icon ${appIcon}`}/>
          Moz Bugs!

          <FilterContainer onChange={this.onFilterChange.bind(this)}/>
          <BugContainer bugs={this.state.bugs}/>

    );
  }
}
export default App;


Add offerings to the courses when fetching

--- client/helpers/getCourses.js
+++ client/helpers/getCourses.js
@@ -1,5 +1,7 @@
 var _ = require('lodash')
 var Promise = require("bluebird")
+
+var time = require('../helpers/time')

 window.courseCache = {}

@@ -12,6 +14,7 @@
 			window.server.courses.get(clbid)
 				.then(function(course) {
 					if (course) {
+						course = time.convertTimeStringsToOfferings(course)
 						console.log('course retrieved:', course)
 						window.courseCache[clbid] = course
 						resolve(course)

<|original_code|>
var _ = require('lodash')
var Promise = require("bluebird")

window.courseCache = {}

function getCourse(clbid) {
	return new Promise(function(resolve, reject) {
		if (_.contains(courseCache, clbid)) {
			console.log('course cached:', clbid)
			resolve(window.courseCache[clbid])
		} else {
			window.server.courses.get(clbid)
				.then(function(course) {
					if (course) {
						console.log('course retrieved:', course)
						window.courseCache[clbid] = course
						resolve(course)
					} else {
						console.error('course retrieval failed for: ' + clbid)
						reject(new Error('course retrieval failed for: ' + clbid))
					}
				})
		}
	})
}

function getCourses(clbids) {
	// Takes a list of clbids, and returns a list of the course objects for
	// those clbids.

	console.log('called getCourses with', clbids)
	return Promise.all(_.map(clbids, getCourse))
}

module.exports.getCourses = getCourses
module.exports.getCourse = getCourse

<|edits_diff|>
--- client/helpers/getCourses.js
+++ client/helpers/getCourses.js
@@ -1,5 +1,7 @@
 var _ = require('lodash')
 var Promise = require("bluebird")
+
+var time = require('../helpers/time')

 window.courseCache = {}

<|current_version|>
var _ = require('lodash')
var Promise = require("bluebird")

var time = require('../helpers/time')

window.courseCache = {}

function getCourse(clbid) {
	return new Promise(function(resolve, reject) {
		if (_.contains(courseCache, clbid)) {
			console.log('course cached:', clbid)
			resolve(window.courseCache[clbid])
		} else {
			window.server.courses.get(clbid)
				.then(function(course) {
					if (course) {
						console.log('course retrieved:', course)
						window.courseCache[clbid] = course
						resolve(course)
					} else {
						console.error('course retrieval failed for: ' + clbid)
						reject(new Error('course retrieval failed for: ' + clbid))
					}
				})
		}
	})
}

function getCourses(clbids) {
	// Takes a list of clbids, and returns a list of the course objects for
	// those clbids.

	console.log('called getCourses with', clbids)
	return Promise.all(_.map(clbids, getCourse))
}

module.exports.getCourses = getCourses
module.exports.getCourse = getCourse

<|next_version|>
var _ = require('lodash')
var Promise = require("bluebird")

var time = require('../helpers/time')

window.courseCache = {}

function getCourse(clbid) {
	return new Promise(function(resolve, reject) {
		if (_.contains(courseCache, clbid)) {
			console.log('course cached:', clbid)
			resolve(window.courseCache[clbid])
		} else {
			window.server.courses.get(clbid)
				.then(function(course) {
					if (course) {
						course = time.convertTimeStringsToOfferings(course)
						console.log('course retrieved:', course)
						window.courseCache[clbid] = course
						resolve(course)
					} else {
						console.error('course retrieval failed for: ' + clbid)
						reject(new Error('course retrieval failed for: ' + clbid))
					}
				})
		}
	})
}

function getCourses(clbids) {
	// Takes a list of clbids, and returns a list of the course objects for
	// those clbids.

	console.log('called getCourses with', clbids)
	return Promise.all(_.map(clbids, getCourse))
}

module.exports.getCourses = getCourses
module.exports.getCourse = getCourse


Add flight to GeoJSON view

Add flight to GeoJSON view

--- index.js
+++ index.js
@@ -1,8 +1,14 @@
+const turf = require('@turf/turf');
+
 const readTask = require('./src/read-task');
 const taskToGeoJSON = require('./src/task-to-geojson');
+const readFlight = require('./src/read-flight');
 const viewGeoJSON = require('./src/view-geojson');

 let task = readTask(`${__dirname}/fixtures/2017-07-15-lev/task.tsk`);
+let flight = readFlight(`${__dirname}/fixtures/2017-07-15-lev/SW_77flqgg1.igc`);
+
 let json = taskToGeoJSON(task);
+json.features.push(turf.lineString(flight, { color: 'red', opacity: 0.85 }));

 viewGeoJSON(json);

<|original_code|>
const readTask = require('./src/read-task');
const taskToGeoJSON = require('./src/task-to-geojson');
const viewGeoJSON = require('./src/view-geojson');

let task = readTask(`${__dirname}/fixtures/2017-07-15-lev/task.tsk`);
let json = taskToGeoJSON(task);

viewGeoJSON(json);

<|edits_diff|>
--- index.js
+++ index.js
@@ -1,8 +1,13 @@
+const turf = require('@turf/turf');
+
 const readTask = require('./src/read-task');
 const taskToGeoJSON = require('./src/task-to-geojson');
+const readFlight = require('./src/read-flight');
 const viewGeoJSON = require('./src/view-geojson');

 let task = readTask(`${__dirname}/fixtures/2017-07-15-lev/task.tsk`);
+let flight = readFlight(`${__dirname}/fixtures/2017-07-15-lev/SW_77flqgg1.igc`);
+
 let json = taskToGeoJSON(task);

 viewGeoJSON(json);
<|current_version|>
const turf = require('@turf/turf');

const readTask = require('./src/read-task');
const taskToGeoJSON = require('./src/task-to-geojson');
const readFlight = require('./src/read-flight');
const viewGeoJSON = require('./src/view-geojson');

let task = readTask(`${__dirname}/fixtures/2017-07-15-lev/task.tsk`);
let flight = readFlight(`${__dirname}/fixtures/2017-07-15-lev/SW_77flqgg1.igc`);

let json = taskToGeoJSON(task);

viewGeoJSON(json);

<|next_version|>
const turf = require('@turf/turf');

const readTask = require('./src/read-task');
const taskToGeoJSON = require('./src/task-to-geojson');
const readFlight = require('./src/read-flight');
const viewGeoJSON = require('./src/view-geojson');

let task = readTask(`${__dirname}/fixtures/2017-07-15-lev/task.tsk`);
let flight = readFlight(`${__dirname}/fixtures/2017-07-15-lev/SW_77flqgg1.igc`);

let json = taskToGeoJSON(task);
json.features.push(turf.lineString(flight, { color: 'red', opacity: 0.85 }));

viewGeoJSON(json);


import React, { Component } from 'react';
import { Link } from 'react-router';
import map from 'lodash/map';
import classNames from 'classnames';

const ColorPicker = ({ colors, current, urlPrefix, onClick }) => {
  return (
    <div className="colorPicker">
      {map(colors, color => {
        const className = classNames('color', color.id, {
          active: current === color.id
        });

        return (
          <Link key={color.id}
            to={`${urlPrefix}${color.id}`}
            className={className} activeClassName="active"
            onClick={() => onClick(color.id)}
          >
          </Link>
        );
      })}
    </div>
  );
}

export default ColorPicker;

Hide color picker if there is only one available

--- src/components/slider/colorpicker.js
+++ src/components/slider/colorpicker.js
@@ -5,6 +5,8 @@

 const ColorPicker = ({ colors, current, urlPrefix, onClick }) => {
   return (
+    Object.keys(colors).length > 1
+    ?

       {map(colors, color => {
         const className = classNames('color', color.id, {
@@ -21,6 +23,8 @@
         );
       })}

+    :
+

   );
 }

import React, { Component } from 'react';
import { Link } from 'react-router';
import map from 'lodash/map';
import classNames from 'classnames';

const ColorPicker = ({ colors, current, urlPrefix, onClick }) => {
  return (
    Object.keys(colors).length > 1
    ?
    <div className="colorPicker">
      {map(colors, color => {
        const className = classNames('color', color.id, {
          active: current === color.id
        });

        return (
          <Link key={color.id}
            to={`${urlPrefix}${color.id}`}
            className={className} activeClassName="active"
            onClick={() => onClick(color.id)}
          >
          </Link>
        );
      })}
    </div>
  );
}

export default ColorPicker;

<|original_code|>
import React, { Component } from 'react';
import { Link } from 'react-router';
import map from 'lodash/map';
import classNames from 'classnames';

const ColorPicker = ({ colors, current, urlPrefix, onClick }) => {
  return (

      {map(colors, color => {
        const className = classNames('color', color.id, {
          active: current === color.id
        });

        return (
          <Link key={color.id}
            to={`${urlPrefix}${color.id}`}
            className={className} activeClassName="active"
            onClick={() => onClick(color.id)}
          >
          </Link>
        );
      })}

  );
}

export default ColorPicker;

<|edits_diff|>
--- src/components/slider/colorpicker.js
+++ src/components/slider/colorpicker.js
@@ -5,6 +5,8 @@

 const ColorPicker = ({ colors, current, urlPrefix, onClick }) => {
   return (
+    Object.keys(colors).length > 1
+    ?

       {map(colors, color => {
         const className = classNames('color', color.id, {
<|current_version|>
import React, { Component } from 'react';
import { Link } from 'react-router';
import map from 'lodash/map';
import classNames from 'classnames';

const ColorPicker = ({ colors, current, urlPrefix, onClick }) => {
  return (
    Object.keys(colors).length > 1
    ?

      {map(colors, color => {
        const className = classNames('color', color.id, {
          active: current === color.id
        });

        return (
          <Link key={color.id}
            to={`${urlPrefix}${color.id}`}
            className={className} activeClassName="active"
            onClick={() => onClick(color.id)}
          >
          </Link>
        );
      })}

  );
}

export default ColorPicker;

<|next_version|>
import React, { Component } from 'react';
import { Link } from 'react-router';
import map from 'lodash/map';
import classNames from 'classnames';

const ColorPicker = ({ colors, current, urlPrefix, onClick }) => {
  return (
    Object.keys(colors).length > 1
    ?

      {map(colors, color => {
        const className = classNames('color', color.id, {
          active: current === color.id
        });

        return (
          <Link key={color.id}
            to={`${urlPrefix}${color.id}`}
            className={className} activeClassName="active"
            onClick={() => onClick(color.id)}
          >
          </Link>
        );
      })}

    :

  );
}

export default ColorPicker;


Add css class for mobile modal canvas.

Add css class for mobile modal canvas.

--- ui/ModalCanvas.js
+++ ui/ModalCanvas.js
@@ -10,7 +10,9 @@

   render () {
     const { renderModal } = this.state
+    const { isMobile } = this.props
     const el = $$('div', { class: 'sc-modal-canvas' })
+    if (isMobile) el.addClass('sm-modal-mobile')
     if (renderModal) {
       el.append(
         renderModal().ref('renderedModal')

<|original_code|>
import { Component, $$, domHelpers } from '../dom'

export default class ModalCanvas extends Component {
  getActionHandlers () {
    return {
      cancel: this._cancel,
      confirm: this._confirm
    }
  }

  render () {
    const { renderModal } = this.state
    const el = $$('div', { class: 'sc-modal-canvas' })
    if (renderModal) {
      el.append(
        renderModal().ref('renderedModal')
      )
    }
    // do not let the global context menu handler handle this
    el.on('contextmenu', domHelpers.stopAndPrevent)
    return el
  }

  openModal (renderModal) {
    // if (this._resolve) throw new Error('Previous modal has not been closed.')
    this.setState({ renderModal })
    return new Promise((resolve, reject) => {
      this._resolve = resolve
    })
  }

  close () {
    this._cancel()
  }

  _cancel () {
    this._resolve(null)
    this._resolve = null
    this.setState({})
    this.send('closePopover')
  }

  _confirm () {
    this._resolve(this.refs.renderedModal)
    this._resolve = null
    this.setState({})
    this.send('closePopover')
  }
}

<|edits_diff|>
--- ui/ModalCanvas.js
+++ ui/ModalCanvas.js
@@ -10,6 +10,7 @@

   render () {
     const { renderModal } = this.state
+    const { isMobile } = this.props
     const el = $$('div', { class: 'sc-modal-canvas' })
     if (renderModal) {
       el.append(
<|current_version|>
import { Component, $$, domHelpers } from '../dom'

export default class ModalCanvas extends Component {
  getActionHandlers () {
    return {
      cancel: this._cancel,
      confirm: this._confirm
    }
  }

  render () {
    const { renderModal } = this.state
    const { isMobile } = this.props
    const el = $$('div', { class: 'sc-modal-canvas' })
    if (renderModal) {
      el.append(
        renderModal().ref('renderedModal')
      )
    }
    // do not let the global context menu handler handle this
    el.on('contextmenu', domHelpers.stopAndPrevent)
    return el
  }

  openModal (renderModal) {
    // if (this._resolve) throw new Error('Previous modal has not been closed.')
    this.setState({ renderModal })
    return new Promise((resolve, reject) => {
      this._resolve = resolve
    })
  }

  close () {
    this._cancel()
  }

  _cancel () {
    this._resolve(null)
    this._resolve = null
    this.setState({})
    this.send('closePopover')
  }

  _confirm () {
    this._resolve(this.refs.renderedModal)
    this._resolve = null
    this.setState({})
    this.send('closePopover')
  }
}

<|next_version|>
import { Component, $$, domHelpers } from '../dom'

export default class ModalCanvas extends Component {
  getActionHandlers () {
    return {
      cancel: this._cancel,
      confirm: this._confirm
    }
  }

  render () {
    const { renderModal } = this.state
    const { isMobile } = this.props
    const el = $$('div', { class: 'sc-modal-canvas' })
    if (isMobile) el.addClass('sm-modal-mobile')
    if (renderModal) {
      el.append(
        renderModal().ref('renderedModal')
      )
    }
    // do not let the global context menu handler handle this
    el.on('contextmenu', domHelpers.stopAndPrevent)
    return el
  }

  openModal (renderModal) {
    // if (this._resolve) throw new Error('Previous modal has not been closed.')
    this.setState({ renderModal })
    return new Promise((resolve, reject) => {
      this._resolve = resolve
    })
  }

  close () {
    this._cancel()
  }

  _cancel () {
    this._resolve(null)
    this._resolve = null
    this.setState({})
    this.send('closePopover')
  }

  _confirm () {
    this._resolve(this.refs.renderedModal)
    this._resolve = null
    this.setState({})
    this.send('closePopover')
  }
}


Check for dropped players list

test: Check for dropped players list

--- test/makeTournament.js
+++ test/makeTournament.js
@@ -9,6 +9,7 @@
   it('should allow players with the same name', () => {
     const t = makeTournament(['a', 'a'])
     expect(t.players.length).to.equal(2)
+    expect(t.playersHasDropped.length).to.equal(2)
     expect(t.results.length).to.equal(2)
     expect(t.results[0].length).to.equal(2)
     expect(t.results[1].length).to.equal(2)
@@ -17,6 +18,7 @@
   it('should allow a tournament with no players', () => {
     const t = makeTournament([])
     expect(t.players.length).to.equal(0)
+    expect(t.playersHasDropped.length).to.equal(0)
     expect(t.results.length).to.equal(0)
   })

<|original_code|>

'use strict'

const expect = require('chai').expect
const makeTournament = require('..').makeTournament

describe('makeTournament', () => {

  it('should allow players with the same name', () => {
    const t = makeTournament(['a', 'a'])
    expect(t.players.length).to.equal(2)
    expect(t.results.length).to.equal(2)
    expect(t.results[0].length).to.equal(2)
    expect(t.results[1].length).to.equal(2)
  })

  it('should allow a tournament with no players', () => {
    const t = makeTournament([])
    expect(t.players.length).to.equal(0)
    expect(t.results.length).to.equal(0)
  })

})

<|edits_diff|>
--- test/makeTournament.js
+++ test/makeTournament.js
@@ -9,6 +9,7 @@
   it('should allow players with the same name', () => {
     const t = makeTournament(['a', 'a'])
     expect(t.players.length).to.equal(2)
+    expect(t.playersHasDropped.length).to.equal(2)
     expect(t.results.length).to.equal(2)
     expect(t.results[0].length).to.equal(2)
     expect(t.results[1].length).to.equal(2)
<|current_version|>

'use strict'

const expect = require('chai').expect
const makeTournament = require('..').makeTournament

describe('makeTournament', () => {

  it('should allow players with the same name', () => {
    const t = makeTournament(['a', 'a'])
    expect(t.players.length).to.equal(2)
    expect(t.playersHasDropped.length).to.equal(2)
    expect(t.results.length).to.equal(2)
    expect(t.results[0].length).to.equal(2)
    expect(t.results[1].length).to.equal(2)
  })

  it('should allow a tournament with no players', () => {
    const t = makeTournament([])
    expect(t.players.length).to.equal(0)
    expect(t.results.length).to.equal(0)
  })

})

<|next_version|>

'use strict'

const expect = require('chai').expect
const makeTournament = require('..').makeTournament

describe('makeTournament', () => {

  it('should allow players with the same name', () => {
    const t = makeTournament(['a', 'a'])
    expect(t.players.length).to.equal(2)
    expect(t.playersHasDropped.length).to.equal(2)
    expect(t.results.length).to.equal(2)
    expect(t.results[0].length).to.equal(2)
    expect(t.results[1].length).to.equal(2)
  })

  it('should allow a tournament with no players', () => {
    const t = makeTournament([])
    expect(t.players.length).to.equal(0)
    expect(t.playersHasDropped.length).to.equal(0)
    expect(t.results.length).to.equal(0)
  })

})


Add ice rinks to used services

Add ice rinks to used services

--- src/modules/service/constants.js
+++ src/modules/service/constants.js
@@ -4,6 +4,7 @@
 export const UnitServices = {
   MECHANICALLY_FROZEN_ICE: 33417,
   ICE_SKATING_FIELD: 33418,
+  ICE_RINK: 33419,
   SPEED_SKATING_TRACK: 33420,
   SKI_TRACK: 33483,
   DOG_SKI_TRACK: 33492
@@ -12,6 +13,7 @@
 export const IceSkatingServices = [
   UnitServices.MECHANICALLY_FROZEN_ICE,
   UnitServices.ICE_SKATING_FIELD,
+  UnitServices.ICE_RINK,
   UnitServices.SPEED_SKATING_TRACK
 ];

<|original_code|>
import {Schema} from 'normalizr';
import {normalizeActionName} from '../common/helpers';

export const UnitServices = {
  MECHANICALLY_FROZEN_ICE: 33417,
  ICE_SKATING_FIELD: 33418,
  SPEED_SKATING_TRACK: 33420,
  SKI_TRACK: 33483,
  DOG_SKI_TRACK: 33492
};

export const IceSkatingServices = [
  UnitServices.MECHANICALLY_FROZEN_ICE,
  UnitServices.ICE_SKATING_FIELD,
  UnitServices.SPEED_SKATING_TRACK
];

export const SkiingServices = [
  UnitServices.SKI_TRACK,
  UnitServices.DOG_SKI_TRACK
];

export const SwimmingServices = [];

export const ServiceActions = {
  FETCH: normalizeActionName('service/FETCH'),
  RECEIVE: normalizeActionName('service/RECEIVE'),
  FETCH_ERROR: normalizeActionName('service/FETCH_ERROR')
};

export type UnitState = {
  isFetching: boolean,
  byId: Object,
  // Filtered arrays of ids
  all: Array<string>,
  skating: Array<string>,
  skiing: Array<string>,
  searchResults: Array<string>
};

export const serviceSchema = new Schema('service'/*, {}*/);

<|edits_diff|>
--- src/modules/service/constants.js
+++ src/modules/service/constants.js
@@ -4,6 +4,7 @@
 export const UnitServices = {
   MECHANICALLY_FROZEN_ICE: 33417,
   ICE_SKATING_FIELD: 33418,
+  ICE_RINK: 33419,
   SPEED_SKATING_TRACK: 33420,
   SKI_TRACK: 33483,
   DOG_SKI_TRACK: 33492
<|current_version|>
import {Schema} from 'normalizr';
import {normalizeActionName} from '../common/helpers';

export const UnitServices = {
  MECHANICALLY_FROZEN_ICE: 33417,
  ICE_SKATING_FIELD: 33418,
  ICE_RINK: 33419,
  SPEED_SKATING_TRACK: 33420,
  SKI_TRACK: 33483,
  DOG_SKI_TRACK: 33492
};

export const IceSkatingServices = [
  UnitServices.MECHANICALLY_FROZEN_ICE,
  UnitServices.ICE_SKATING_FIELD,
  UnitServices.SPEED_SKATING_TRACK
];

export const SkiingServices = [
  UnitServices.SKI_TRACK,
  UnitServices.DOG_SKI_TRACK
];

export const SwimmingServices = [];

export const ServiceActions = {
  FETCH: normalizeActionName('service/FETCH'),
  RECEIVE: normalizeActionName('service/RECEIVE'),
  FETCH_ERROR: normalizeActionName('service/FETCH_ERROR')
};

export type UnitState = {
  isFetching: boolean,
  byId: Object,
  // Filtered arrays of ids
  all: Array<string>,
  skating: Array<string>,
  skiing: Array<string>,
  searchResults: Array<string>
};

export const serviceSchema = new Schema('service'/*, {}*/);

<|next_version|>
import {Schema} from 'normalizr';
import {normalizeActionName} from '../common/helpers';

export const UnitServices = {
  MECHANICALLY_FROZEN_ICE: 33417,
  ICE_SKATING_FIELD: 33418,
  ICE_RINK: 33419,
  SPEED_SKATING_TRACK: 33420,
  SKI_TRACK: 33483,
  DOG_SKI_TRACK: 33492
};

export const IceSkatingServices = [
  UnitServices.MECHANICALLY_FROZEN_ICE,
  UnitServices.ICE_SKATING_FIELD,
  UnitServices.ICE_RINK,
  UnitServices.SPEED_SKATING_TRACK
];

export const SkiingServices = [
  UnitServices.SKI_TRACK,
  UnitServices.DOG_SKI_TRACK
];

export const SwimmingServices = [];

export const ServiceActions = {
  FETCH: normalizeActionName('service/FETCH'),
  RECEIVE: normalizeActionName('service/RECEIVE'),
  FETCH_ERROR: normalizeActionName('service/FETCH_ERROR')
};

export type UnitState = {
  isFetching: boolean,
  byId: Object,
  // Filtered arrays of ids
  all: Array<string>,
  skating: Array<string>,
  skiing: Array<string>,
  searchResults: Array<string>
};

export const serviceSchema = new Schema('service'/*, {}*/);