Revert "Testing out removing pagination from Committees"

This reverts commit 6548533d20516b8cf9705db08cd7bb50bff5cf59.

--- models/committee.js
+++ models/committee.js
@@ -1,5 +1,6 @@
 import sequelize from '../config/sequelize';
 import DataTypes from 'sequelize';
+import paginate from '../helpers/paginate';
 import Officer from './officer';

 export default sequelize.define('committees', {
@@ -24,5 +25,6 @@
         }],
       };
     },
+    paginate,
   },
 });

<|original_code|>
import sequelize from '../config/sequelize';
import DataTypes from 'sequelize';
import Officer from './officer';

export default sequelize.define('committees', {
  name: {
    type: DataTypes.STRING,
    allowNull: false,
    primaryKey: true,
  },
  description: {
    type: DataTypes.TEXT,
    allowNull: true,
  },
}, {
  scopes: {
    name(name) {
      return { where: { name } };
    },
    active(date) {
      return {
        include: [{
          model: Officer.scope({ method: ['active', date] }),
        }],
      };
    },
  },
});

<|edits_diff|>
--- models/committee.js
+++ models/committee.js
@@ -1,5 +1,6 @@
 import sequelize from '../config/sequelize';
 import DataTypes from 'sequelize';
+import paginate from '../helpers/paginate';
 import Officer from './officer';

 export default sequelize.define('committees', {
<|current_version|>
import sequelize from '../config/sequelize';
import DataTypes from 'sequelize';
import paginate from '../helpers/paginate';
import Officer from './officer';

export default sequelize.define('committees', {
  name: {
    type: DataTypes.STRING,
    allowNull: false,
    primaryKey: true,
  },
  description: {
    type: DataTypes.TEXT,
    allowNull: true,
  },
}, {
  scopes: {
    name(name) {
      return { where: { name } };
    },
    active(date) {
      return {
        include: [{
          model: Officer.scope({ method: ['active', date] }),
        }],
      };
    },
  },
});

<|next_version|>
import sequelize from '../config/sequelize';
import DataTypes from 'sequelize';
import paginate from '../helpers/paginate';
import Officer from './officer';

export default sequelize.define('committees', {
  name: {
    type: DataTypes.STRING,
    allowNull: false,
    primaryKey: true,
  },
  description: {
    type: DataTypes.TEXT,
    allowNull: true,
  },
}, {
  scopes: {
    name(name) {
      return { where: { name } };
    },
    active(date) {
      return {
        include: [{
          model: Officer.scope({ method: ['active', date] }),
        }],
      };
    },
    paginate,
  },
});


Check arguments in Events constructor

Check arguments in Events constructor

--- lib/events/index.js
+++ lib/events/index.js
@@ -1,4 +1,6 @@
 'use strict';
+
+const _ = require('lodash');

 /**
  * Events provides abstract event messaging.
@@ -7,6 +9,9 @@
  * @param {Object} provider [description]
  */
 function Events(provider) {
+  if (_.isNil(provider)) {
+    throw new Error('provider does not exist');
+  }
   this.provider = provider;
 }

<|original_code|>
'use strict';

/**
 * Events provides abstract event messaging.
 *
 * @constructor
 * @param {Object} provider [description]
 */
function Events(provider) {
  this.provider = provider;
}

/**
 * Returns a topic.
 *
 * @param  {string} name Topic name
 * @return {Topic}      Topic
 */
Events.prototype.topic = function* topic(name) {
  return yield this.provider.topic(name);
};

module.exports.Events = Events;

<|edits_diff|>
--- lib/events/index.js
+++ lib/events/index.js
@@ -1,4 +1,6 @@
 'use strict';
+
+const _ = require('lodash');

 /**
  * Events provides abstract event messaging.
<|current_version|>
'use strict';

const _ = require('lodash');

/**
 * Events provides abstract event messaging.
 *
 * @constructor
 * @param {Object} provider [description]
 */
function Events(provider) {
  this.provider = provider;
}

/**
 * Returns a topic.
 *
 * @param  {string} name Topic name
 * @return {Topic}      Topic
 */
Events.prototype.topic = function* topic(name) {
  return yield this.provider.topic(name);
};

module.exports.Events = Events;

<|next_version|>
'use strict';

const _ = require('lodash');

/**
 * Events provides abstract event messaging.
 *
 * @constructor
 * @param {Object} provider [description]
 */
function Events(provider) {
  if (_.isNil(provider)) {
    throw new Error('provider does not exist');
  }
  this.provider = provider;
}

/**
 * Returns a topic.
 *
 * @param  {string} name Topic name
 * @return {Topic}      Topic
 */
Events.prototype.topic = function* topic(name) {
  return yield this.provider.topic(name);
};

module.exports.Events = Events;


Make autoFocus work again in dialogs

Make autoFocus work again in dialogs

--- src/components/Form/TextField.js
+++ src/components/Form/TextField.js
@@ -3,6 +3,7 @@
 import PropTypes from 'prop-types';

 const {
+  useEffect,
   useImperativeHandle,
   useRef,
 } = React;
@@ -11,12 +12,18 @@
   className,
   type = 'text',
   icon,
+  autoFocus,
   ...props
 }, ref) => {
   const refInput = useRef(null);
   useImperativeHandle(ref, () => ({
     get value() { return refInput.current.value; },
   }));
+  useEffect(() => {
+    if (autoFocus) {
+      refInput.current?.focus();
+    }
+  }, []); // eslint-disable-line react-hooks/exhaustive-deps

   return (
     <div className={cx('TextField', className)}>
@@ -37,6 +44,7 @@
   className: PropTypes.string,
   type: PropTypes.string,
   icon: PropTypes.node,
+  autoFocus: PropTypes.bool,
 };

 export default TextField;

<|original_code|>
import cx from 'clsx';
import React from 'react';
import PropTypes from 'prop-types';

const {
  useImperativeHandle,
  useRef,
} = React;

const TextField = React.forwardRef(({
  className,
  type = 'text',
  icon,
  ...props
}, ref) => {
  const refInput = useRef(null);
  useImperativeHandle(ref, () => ({
    get value() { return refInput.current.value; },
  }));

  return (

      <input
        ref={refInput}
        className="TextField-input"
        type={type}
        {...props}
      />
      {icon ? (

{icon}

      ) : null}

  );
});

TextField.propTypes = {
  className: PropTypes.string,
  type: PropTypes.string,
  icon: PropTypes.node,
};

export default TextField;

<|edits_diff|>
--- src/components/Form/TextField.js
+++ src/components/Form/TextField.js
@@ -3,6 +3,7 @@
 import PropTypes from 'prop-types';

 const {
+  useEffect,
   useImperativeHandle,
   useRef,
 } = React;
@@ -11,12 +12,18 @@
   className,
   type = 'text',
   icon,
+  autoFocus,
   ...props
 }, ref) => {
   const refInput = useRef(null);
   useImperativeHandle(ref, () => ({
     get value() { return refInput.current.value; },
   }));
+  useEffect(() => {
+    if (autoFocus) {
+      refInput.current?.focus();
+    }
+  }, []); // eslint-disable-line react-hooks/exhaustive-deps

   return (

<|current_version|>
import cx from 'clsx';
import React from 'react';
import PropTypes from 'prop-types';

const {
  useEffect,
  useImperativeHandle,
  useRef,
} = React;

const TextField = React.forwardRef(({
  className,
  type = 'text',
  icon,
  autoFocus,
  ...props
}, ref) => {
  const refInput = useRef(null);
  useImperativeHandle(ref, () => ({
    get value() { return refInput.current.value; },
  }));
  useEffect(() => {
    if (autoFocus) {
      refInput.current?.focus();
    }
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  return (

      <input
        ref={refInput}
        className="TextField-input"
        type={type}
        {...props}
      />
      {icon ? (

{icon}

      ) : null}

  );
});

TextField.propTypes = {
  className: PropTypes.string,
  type: PropTypes.string,
  icon: PropTypes.node,
};

export default TextField;

<|next_version|>
import cx from 'clsx';
import React from 'react';
import PropTypes from 'prop-types';

const {
  useEffect,
  useImperativeHandle,
  useRef,
} = React;

const TextField = React.forwardRef(({
  className,
  type = 'text',
  icon,
  autoFocus,
  ...props
}, ref) => {
  const refInput = useRef(null);
  useImperativeHandle(ref, () => ({
    get value() { return refInput.current.value; },
  }));
  useEffect(() => {
    if (autoFocus) {
      refInput.current?.focus();
    }
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  return (

      <input
        ref={refInput}
        className="TextField-input"
        type={type}
        {...props}
      />
      {icon ? (

{icon}

      ) : null}

  );
});

TextField.propTypes = {
  className: PropTypes.string,
  type: PropTypes.string,
  icon: PropTypes.node,
  autoFocus: PropTypes.bool,
};

export default TextField;


Add loading state to hll update lifecycle

--- app/pods/riak-object/hll/route.js
+++ app/pods/riak-object/hll/route.js
@@ -9,6 +9,7 @@
      */
     addElement: function(item) {
       let self = this;
+      let controller = this.get('controller');
       let hll = this.currentModel;
       let clusterName = hll.get('cluster').get('name');
       let bucketTypeName = hll.get('bucketType').get('name');
@@ -16,7 +17,11 @@
       let objectName = hll.get('name');

       this.explorer.updateCRDT(hll, { add: item }).then(function() {
+        controller.set('showLoadingSpinner', true);
+
         return self.explorer.getObject(clusterName, bucketTypeName, bucketName, objectName);
+      }).then(function() {
+        return controller.set('showLoadingSpinner', false);
       });
     }
   }

<|original_code|>
import RiakObjectRoute from '../route';

export default RiakObjectRoute.extend({
  actions: {
    /**
     * Adds an element to the HLL list.
     * @event addSetElement
     * @param {String} item
     */
    addElement: function(item) {
      let self = this;
      let hll = this.currentModel;
      let clusterName = hll.get('cluster').get('name');
      let bucketTypeName = hll.get('bucketType').get('name');
      let bucketName = hll.get('bucket').get('name');
      let objectName = hll.get('name');

      this.explorer.updateCRDT(hll, { add: item }).then(function() {
        return self.explorer.getObject(clusterName, bucketTypeName, bucketName, objectName);
      });
    }
  }
});

<|edits_diff|>
--- app/pods/riak-object/hll/route.js
+++ app/pods/riak-object/hll/route.js
@@ -9,6 +9,7 @@
      */
     addElement: function(item) {
       let self = this;
+      let controller = this.get('controller');
       let hll = this.currentModel;
       let clusterName = hll.get('cluster').get('name');
       let bucketTypeName = hll.get('bucketType').get('name');
@@ -16,6 +17,8 @@
       let objectName = hll.get('name');

       this.explorer.updateCRDT(hll, { add: item }).then(function() {
+        controller.set('showLoadingSpinner', true);
+
         return self.explorer.getObject(clusterName, bucketTypeName, bucketName, objectName);
       });
     }
<|current_version|>
import RiakObjectRoute from '../route';

export default RiakObjectRoute.extend({
  actions: {
    /**
     * Adds an element to the HLL list.
     * @event addSetElement
     * @param {String} item
     */
    addElement: function(item) {
      let self = this;
      let controller = this.get('controller');
      let hll = this.currentModel;
      let clusterName = hll.get('cluster').get('name');
      let bucketTypeName = hll.get('bucketType').get('name');
      let bucketName = hll.get('bucket').get('name');
      let objectName = hll.get('name');

      this.explorer.updateCRDT(hll, { add: item }).then(function() {
        controller.set('showLoadingSpinner', true);

        return self.explorer.getObject(clusterName, bucketTypeName, bucketName, objectName);
      });
    }
  }
});

<|next_version|>
import RiakObjectRoute from '../route';

export default RiakObjectRoute.extend({
  actions: {
    /**
     * Adds an element to the HLL list.
     * @event addSetElement
     * @param {String} item
     */
    addElement: function(item) {
      let self = this;
      let controller = this.get('controller');
      let hll = this.currentModel;
      let clusterName = hll.get('cluster').get('name');
      let bucketTypeName = hll.get('bucketType').get('name');
      let bucketName = hll.get('bucket').get('name');
      let objectName = hll.get('name');

      this.explorer.updateCRDT(hll, { add: item }).then(function() {
        controller.set('showLoadingSpinner', true);

        return self.explorer.getObject(clusterName, bucketTypeName, bucketName, objectName);
      }).then(function() {
        return controller.set('showLoadingSpinner', false);
      });
    }
  }
});


Check mergeImages returns a Promise

Check mergeImages returns a Promise

--- test/types.js
+++ test/types.js
@@ -1,6 +1,11 @@
 import test from 'ava';
+import Canvas from 'canvas';
 import mergeImages from '../';

 test('mergeImages is a function', t => {
 	t.is(typeof mergeImages, 'function');
 });
+
+test('mergeImages returns a Promise', t => {
+	t.true(mergeImages([], {Canvas}) instanceof Promise);
+});

<|original_code|>
import test from 'ava';
import mergeImages from '../';

test('mergeImages is a function', t => {
	t.is(typeof mergeImages, 'function');
});

<|edits_diff|>
--- test/types.js
+++ test/types.js
@@ -1,4 +1,5 @@
 import test from 'ava';
+import Canvas from 'canvas';
 import mergeImages from '../';

 test('mergeImages is a function', t => {
<|current_version|>
import test from 'ava';
import Canvas from 'canvas';
import mergeImages from '../';

test('mergeImages is a function', t => {
	t.is(typeof mergeImages, 'function');
});

<|next_version|>
import test from 'ava';
import Canvas from 'canvas';
import mergeImages from '../';

test('mergeImages is a function', t => {
	t.is(typeof mergeImages, 'function');
});

test('mergeImages returns a Promise', t => {
	t.true(mergeImages([], {Canvas}) instanceof Promise);
});


Add return requests reducer in root reducer

--- client/reducers/index.js
+++ client/reducers/index.js
@@ -7,6 +7,7 @@
 import favorites from './favorites';
 import profile from './profile';
 import borrowRequests from './borrowrequests';
+import returnRequests from './returnrequests';
 import { books, mostUpvotedBooks } from './books';

 const rootReducer = combineReducers({
@@ -18,6 +19,7 @@
   mostUpvotedBooks,
   favorites,
   borrowRequests,
+  returnRequests,
   profile,
   routing: routerReducer,
 });

<|original_code|>
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import { reducer as formReducer } from 'redux-form';
import auth from './userSignUp';
import login from './login';
import book from './book';
import favorites from './favorites';
import profile from './profile';
import borrowRequests from './borrowrequests';
import { books, mostUpvotedBooks } from './books';

const rootReducer = combineReducers({
  form: formReducer,
  auth,
  login,
  books,
  book,
  mostUpvotedBooks,
  favorites,
  borrowRequests,
  profile,
  routing: routerReducer,
});

export default rootReducer;

<|edits_diff|>
--- client/reducers/index.js
+++ client/reducers/index.js
@@ -7,6 +7,7 @@
 import favorites from './favorites';
 import profile from './profile';
 import borrowRequests from './borrowrequests';
+import returnRequests from './returnrequests';
 import { books, mostUpvotedBooks } from './books';

 const rootReducer = combineReducers({
<|current_version|>
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import { reducer as formReducer } from 'redux-form';
import auth from './userSignUp';
import login from './login';
import book from './book';
import favorites from './favorites';
import profile from './profile';
import borrowRequests from './borrowrequests';
import returnRequests from './returnrequests';
import { books, mostUpvotedBooks } from './books';

const rootReducer = combineReducers({
  form: formReducer,
  auth,
  login,
  books,
  book,
  mostUpvotedBooks,
  favorites,
  borrowRequests,
  profile,
  routing: routerReducer,
});

export default rootReducer;

<|next_version|>
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import { reducer as formReducer } from 'redux-form';
import auth from './userSignUp';
import login from './login';
import book from './book';
import favorites from './favorites';
import profile from './profile';
import borrowRequests from './borrowrequests';
import returnRequests from './returnrequests';
import { books, mostUpvotedBooks } from './books';

const rootReducer = combineReducers({
  form: formReducer,
  auth,
  login,
  books,
  book,
  mostUpvotedBooks,
  favorites,
  borrowRequests,
  returnRequests,
  profile,
  routing: routerReducer,
});

export default rootReducer;


Revert "Revert "Revert "Revert "Try child process""""

This reverts commit 59eec97713488c95c2e8869e8b73ce0d0eab09bb.

--- plugin.js
+++ plugin.js
@@ -1,5 +1,8 @@
+const { spawn } = require('child_process')
+
 module.exports = {
   onPreBuild({ utils: { status: { show } } }) {
+    spawn('sleep', ['60'], { detached: true })
     show({ summary: 'Test' })
   }
 }

<|original_code|>
module.exports = {
  onPreBuild({ utils: { status: { show } } }) {
    show({ summary: 'Test' })
  }
}

<|edits_diff|>
--- plugin.js
+++ plugin.js
@@ -1,3 +1,5 @@
+const { spawn } = require('child_process')
+
 module.exports = {
   onPreBuild({ utils: { status: { show } } }) {
     show({ summary: 'Test' })
<|current_version|>
const { spawn } = require('child_process')

module.exports = {
  onPreBuild({ utils: { status: { show } } }) {
    show({ summary: 'Test' })
  }
}

<|next_version|>
const { spawn } = require('child_process')

module.exports = {
  onPreBuild({ utils: { status: { show } } }) {
    spawn('sleep', ['60'], { detached: true })
    show({ summary: 'Test' })
  }
}


Extend mocha timeout to 10 sec

Extend mocha timeout to 10 sec

--- tests/test-helper.js
+++ tests/test-helper.js
@@ -1,4 +1,9 @@
+/* global mocha */
 import resolver from './helpers/resolver'
 import {setResolver} from 'ember-mocha'

+mocha.setup({
+  timeout: 10000
+})
+
 setResolver(resolver)

<|original_code|>
import resolver from './helpers/resolver'
import {setResolver} from 'ember-mocha'

setResolver(resolver)

<|edits_diff|>
--- tests/test-helper.js
+++ tests/test-helper.js
@@ -1,3 +1,4 @@
+/* global mocha */
 import resolver from './helpers/resolver'
 import {setResolver} from 'ember-mocha'

<|current_version|>
/* global mocha */
import resolver from './helpers/resolver'
import {setResolver} from 'ember-mocha'

setResolver(resolver)

<|next_version|>
/* global mocha */
import resolver from './helpers/resolver'
import {setResolver} from 'ember-mocha'

mocha.setup({
  timeout: 10000
})

setResolver(resolver)


Add Fetch API example to the non-React version too

Ref #4.

--- generators/app/templates/app/scripts/app.js
+++ generators/app/templates/app/scripts/app.js
@@ -1,4 +1,10 @@
+import fetch from './helpers/fetch';
+
 import 'svg4everybody';
 import './fonts';

 console.log('Hello World');
+
+fetch('/index.html').then((res) => {
+  console.log('Fetch API call successful!');
+});

<|original_code|>
import 'svg4everybody';
import './fonts';

console.log('Hello World');

<|edits_diff|>
--- generators/app/templates/app/scripts/app.js
+++ generators/app/templates/app/scripts/app.js
@@ -1,3 +1,5 @@
+import fetch from './helpers/fetch';
+
 import 'svg4everybody';
 import './fonts';

<|current_version|>
import fetch from './helpers/fetch';

import 'svg4everybody';
import './fonts';

console.log('Hello World');

<|next_version|>
import fetch from './helpers/fetch';

import 'svg4everybody';
import './fonts';

console.log('Hello World');

fetch('/index.html').then((res) => {
  console.log('Fetch API call successful!');
});


Add file format in aviary editor component options

--- addon/components/aviary-editor.js
+++ addon/components/aviary-editor.js
@@ -6,6 +6,7 @@
   classNameBindings: ['featherActive:active'],
   image: null,
   url: null,
+  fileFormat: null,
   imageSelector: null,
   closeOnSave: false,
   featherActive: false,
@@ -21,6 +22,7 @@
     var options = {
       image: this.get('imageToEdit'),
       url: this.get('url'),
+      fileFormat: this.get('fileFormat'),
       onSave: function(imageId, src) {
         self.send('onSave', imageId, src);
       },

<|original_code|>
import Ember from 'ember';

export default Ember.Component.extend({
  aviaryApiClient: null, // injected
  classNames: ['edit-icon'],
  classNameBindings: ['featherActive:active'],
  image: null,
  url: null,
  imageSelector: null,
  closeOnSave: false,
  featherActive: false,

  imageToEdit: function() {
    var imageNotSet = Ember.isEmpty(this.get('image'));
    var selector = this.get('imageSelector');
    return imageNotSet ? this.get('parentView').$(selector || 'img') : this.get('image');
  }.property('parentView', 'image', 'imageSelector'),

  click: function() {
    var self = this;
    var options = {
      image: this.get('imageToEdit'),
      url: this.get('url'),
      onSave: function(imageId, src) {
        self.send('onSave', imageId, src);
      },
      onClose: function() {
        self.send('onClose');
      }
    };

    this.aviaryApiClient.launch(options);
    this.set('featherActive', true);

    return false;
  },

  actions: {
    onSave: function(imageId, src) {
      this.sendAction('onSave', imageId, src);

      var closeOnSave = this.get('closeOnSave');
      var featherEditor = this.aviaryApiClient.get('featherEditor');
      if(closeOnSave) { featherEditor.close(); }
    },
    onClose: function() {
      this.set('featherActive', false);
    }
  }
});

<|edits_diff|>
--- addon/components/aviary-editor.js
+++ addon/components/aviary-editor.js
@@ -6,6 +6,7 @@
   classNameBindings: ['featherActive:active'],
   image: null,
   url: null,
+  fileFormat: null,
   imageSelector: null,
   closeOnSave: false,
   featherActive: false,
<|current_version|>
import Ember from 'ember';

export default Ember.Component.extend({
  aviaryApiClient: null, // injected
  classNames: ['edit-icon'],
  classNameBindings: ['featherActive:active'],
  image: null,
  url: null,
  fileFormat: null,
  imageSelector: null,
  closeOnSave: false,
  featherActive: false,

  imageToEdit: function() {
    var imageNotSet = Ember.isEmpty(this.get('image'));
    var selector = this.get('imageSelector');
    return imageNotSet ? this.get('parentView').$(selector || 'img') : this.get('image');
  }.property('parentView', 'image', 'imageSelector'),

  click: function() {
    var self = this;
    var options = {
      image: this.get('imageToEdit'),
      url: this.get('url'),
      onSave: function(imageId, src) {
        self.send('onSave', imageId, src);
      },
      onClose: function() {
        self.send('onClose');
      }
    };

    this.aviaryApiClient.launch(options);
    this.set('featherActive', true);

    return false;
  },

  actions: {
    onSave: function(imageId, src) {
      this.sendAction('onSave', imageId, src);

      var closeOnSave = this.get('closeOnSave');
      var featherEditor = this.aviaryApiClient.get('featherEditor');
      if(closeOnSave) { featherEditor.close(); }
    },
    onClose: function() {
      this.set('featherActive', false);
    }
  }
});

<|next_version|>
import Ember from 'ember';

export default Ember.Component.extend({
  aviaryApiClient: null, // injected
  classNames: ['edit-icon'],
  classNameBindings: ['featherActive:active'],
  image: null,
  url: null,
  fileFormat: null,
  imageSelector: null,
  closeOnSave: false,
  featherActive: false,

  imageToEdit: function() {
    var imageNotSet = Ember.isEmpty(this.get('image'));
    var selector = this.get('imageSelector');
    return imageNotSet ? this.get('parentView').$(selector || 'img') : this.get('image');
  }.property('parentView', 'image', 'imageSelector'),

  click: function() {
    var self = this;
    var options = {
      image: this.get('imageToEdit'),
      url: this.get('url'),
      fileFormat: this.get('fileFormat'),
      onSave: function(imageId, src) {
        self.send('onSave', imageId, src);
      },
      onClose: function() {
        self.send('onClose');
      }
    };

    this.aviaryApiClient.launch(options);
    this.set('featherActive', true);

    return false;
  },

  actions: {
    onSave: function(imageId, src) {
      this.sendAction('onSave', imageId, src);

      var closeOnSave = this.get('closeOnSave');
      var featherEditor = this.aviaryApiClient.get('featherEditor');
      if(closeOnSave) { featherEditor.close(); }
    },
    onClose: function() {
      this.set('featherActive', false);
    }
  }
});


Resolve path names relatives to cwd when using via CLI

--- src/cli.js
+++ src/cli.js
@@ -1,4 +1,5 @@
 #!/usr/bin/env node
+var path = require('path')
 var express = require('express')
 var app = express()
 var dafuq = require('./')
@@ -19,6 +20,10 @@
     .alias('help', 'h')
     .argv;

+// Resolve full path when executing via CLI
+if (require.main === module)
+    argv.commands = path.resolve(process.pwd(), argv.commands)
+
 app.use(dafuq({
 	path: argv.commands,
 	shebang: argv.shebang

<|original_code|>
#!/usr/bin/env node
var express = require('express')
var app = express()
var dafuq = require('./')

var argv = require('yargs')
	.usage('$0 Leverages command-based api')
    .describe('commands', 'the path to commands directory')
    .alias('commands', 'c')
    .alias('commands', 'path')
    .alias('commands', 'directory')
    .demand('commands')
    .describe('shebang', 'the interpreter to use when running the command files')
    .default('shebang', '')
    .describe('port', 'the port where to listen for api call')
    .alias('port', 'p')
    .default('port', 3000)
    .help('help')
    .alias('help', 'h')
    .argv;

app.use(dafuq({
	path: argv.commands,
	shebang: argv.shebang
}))
app.listen(argv.port)

<|edits_diff|>
--- src/cli.js
+++ src/cli.js
@@ -1,4 +1,5 @@
 #!/usr/bin/env node
+var path = require('path')
 var express = require('express')
 var app = express()
 var dafuq = require('./')
<|current_version|>
#!/usr/bin/env node
var path = require('path')
var express = require('express')
var app = express()
var dafuq = require('./')

var argv = require('yargs')
	.usage('$0 Leverages command-based api')
    .describe('commands', 'the path to commands directory')
    .alias('commands', 'c')
    .alias('commands', 'path')
    .alias('commands', 'directory')
    .demand('commands')
    .describe('shebang', 'the interpreter to use when running the command files')
    .default('shebang', '')
    .describe('port', 'the port where to listen for api call')
    .alias('port', 'p')
    .default('port', 3000)
    .help('help')
    .alias('help', 'h')
    .argv;

app.use(dafuq({
	path: argv.commands,
	shebang: argv.shebang
}))
app.listen(argv.port)

<|next_version|>
#!/usr/bin/env node
var path = require('path')
var express = require('express')
var app = express()
var dafuq = require('./')

var argv = require('yargs')
	.usage('$0 Leverages command-based api')
    .describe('commands', 'the path to commands directory')
    .alias('commands', 'c')
    .alias('commands', 'path')
    .alias('commands', 'directory')
    .demand('commands')
    .describe('shebang', 'the interpreter to use when running the command files')
    .default('shebang', '')
    .describe('port', 'the port where to listen for api call')
    .alias('port', 'p')
    .default('port', 3000)
    .help('help')
    .alias('help', 'h')
    .argv;

// Resolve full path when executing via CLI
if (require.main === module)
    argv.commands = path.resolve(process.pwd(), argv.commands)

app.use(dafuq({
	path: argv.commands,
	shebang: argv.shebang
}))
app.listen(argv.port)


Copy site into releases folder

site: Copy site into releases folder

--- tasks/build.js
+++ tasks/build.js
@@ -20,11 +20,18 @@
   harp.compile('./site', '../www', done)
 })

+gulp.task('copy:site', done => {
+  return gulp.src(['./www/**/*'])
+    .pipe(gulp.dest('./releases'))
+})
+
 gulp.task('build', done => {
   runSequence(
     ['clean', 'dist'],
     'webpack:prod',
     'harp:compile',
+    'copy:site',
+    'clean',
     done
   )
 })

<|original_code|>
const gulp = require('gulp')
const runSequence = require('run-sequence')
const harp = require('harp')
const webpackStream = require('webpack-stream')
const del = require('del')

const config = require('./config')

gulp.task('clean', () => {
  return del(['./www'])
})

gulp.task('webpack:prod', () => {
  return gulp.src('./site/public/_js/script.js')
    .pipe(webpackStream(config.webpack.prod))
    .pipe(gulp.dest('site/'))
})

gulp.task('harp:compile', done => {
  harp.compile('./site', '../www', done)
})

gulp.task('build', done => {
  runSequence(
    ['clean', 'dist'],
    'webpack:prod',
    'harp:compile',
    done
  )
})

<|edits_diff|>
--- tasks/build.js
+++ tasks/build.js
@@ -20,6 +20,11 @@
   harp.compile('./site', '../www', done)
 })

+gulp.task('copy:site', done => {
+  return gulp.src(['./www/**/*'])
+    .pipe(gulp.dest('./releases'))
+})
+
 gulp.task('build', done => {
   runSequence(
     ['clean', 'dist'],
<|current_version|>
const gulp = require('gulp')
const runSequence = require('run-sequence')
const harp = require('harp')
const webpackStream = require('webpack-stream')
const del = require('del')

const config = require('./config')

gulp.task('clean', () => {
  return del(['./www'])
})

gulp.task('webpack:prod', () => {
  return gulp.src('./site/public/_js/script.js')
    .pipe(webpackStream(config.webpack.prod))
    .pipe(gulp.dest('site/'))
})

gulp.task('harp:compile', done => {
  harp.compile('./site', '../www', done)
})

gulp.task('copy:site', done => {
  return gulp.src(['./www/**/*'])
    .pipe(gulp.dest('./releases'))
})

gulp.task('build', done => {
  runSequence(
    ['clean', 'dist'],
    'webpack:prod',
    'harp:compile',
    done
  )
})

<|next_version|>
const gulp = require('gulp')
const runSequence = require('run-sequence')
const harp = require('harp')
const webpackStream = require('webpack-stream')
const del = require('del')

const config = require('./config')

gulp.task('clean', () => {
  return del(['./www'])
})

gulp.task('webpack:prod', () => {
  return gulp.src('./site/public/_js/script.js')
    .pipe(webpackStream(config.webpack.prod))
    .pipe(gulp.dest('site/'))
})

gulp.task('harp:compile', done => {
  harp.compile('./site', '../www', done)
})

gulp.task('copy:site', done => {
  return gulp.src(['./www/**/*'])
    .pipe(gulp.dest('./releases'))
})

gulp.task('build', done => {
  runSequence(
    ['clean', 'dist'],
    'webpack:prod',
    'harp:compile',
    'copy:site',
    'clean',
    done
  )
})


Test for headers and stderr changes

Test for headers and stderr changes

--- test.js
+++ test.js
@@ -15,15 +15,24 @@
 execFile(process.execPath, ['.'], {}, assertNoTests);

 function assertBad(err, stdout, stderr) {
+  assertHeader(err, stdout, stderr);
   assert(err, 'bad file exits with an error');
+  assert.notEqual(stderr, '', 'tapsert does not clear stderr');
   assert(/Premature exit with code \d/.test(stdout), 'exits prematurely');
   assert(/No assertions run/.test(stdout),
          'notices that no asserions were run');
 }

 function assertNoTests(err, stdout, stderr) {
+  assertHeader(err, stdout, stderr);
   assert(err, 'no assertions run is considered a failure');
+  assert.equal(stderr, '', 'tapsert does not write to stderr');
   assert(!/Premature exit with code/.test(stdout),
          'does not exit prematurely');
   assert(/No assertions run/.test(stdout), 'says no assertions were run');
 }
+
+function assertHeader(err, stdout, stderr) {
+  assert(/^[\s]*TAP version 13/.test(stdout),
+         'TAP version header is the first non-whitespace output');
+}

<|original_code|>
var assert = require('./');
var execFile = require('child_process').execFile;

assert(assert, 'assert exists');
assert(assert.equal, 'assert.equal exists');
assert.equal(typeof assert.strictEqual, 'function',
             'assert.strictEqual is a function');
assert.doesNotThrow(function() {
  assert.throws(function() {
    throw Error('expected!');
  }, /expected/, 'supports assert.throws');
}, 'nested asserts are weird.');

execFile(process.execPath, ['test-bad-tests.js'], {}, assertBad);
execFile(process.execPath, ['.'], {}, assertNoTests);

function assertBad(err, stdout, stderr) {
  assert(err, 'bad file exits with an error');
  assert(/Premature exit with code \d/.test(stdout), 'exits prematurely');
  assert(/No assertions run/.test(stdout),
         'notices that no asserions were run');
}

function assertNoTests(err, stdout, stderr) {
  assert(err, 'no assertions run is considered a failure');
  assert(!/Premature exit with code/.test(stdout),
         'does not exit prematurely');
  assert(/No assertions run/.test(stdout), 'says no assertions were run');
}

<|edits_diff|>
--- test.js
+++ test.js
@@ -15,14 +15,18 @@
 execFile(process.execPath, ['.'], {}, assertNoTests);

 function assertBad(err, stdout, stderr) {
+  assertHeader(err, stdout, stderr);
   assert(err, 'bad file exits with an error');
+  assert.notEqual(stderr, '', 'tapsert does not clear stderr');
   assert(/Premature exit with code \d/.test(stdout), 'exits prematurely');
   assert(/No assertions run/.test(stdout),
          'notices that no asserions were run');
 }

 function assertNoTests(err, stdout, stderr) {
+  assertHeader(err, stdout, stderr);
   assert(err, 'no assertions run is considered a failure');
+  assert.equal(stderr, '', 'tapsert does not write to stderr');
   assert(!/Premature exit with code/.test(stdout),
          'does not exit prematurely');
   assert(/No assertions run/.test(stdout), 'says no assertions were run');
<|current_version|>
var assert = require('./');
var execFile = require('child_process').execFile;

assert(assert, 'assert exists');
assert(assert.equal, 'assert.equal exists');
assert.equal(typeof assert.strictEqual, 'function',
             'assert.strictEqual is a function');
assert.doesNotThrow(function() {
  assert.throws(function() {
    throw Error('expected!');
  }, /expected/, 'supports assert.throws');
}, 'nested asserts are weird.');

execFile(process.execPath, ['test-bad-tests.js'], {}, assertBad);
execFile(process.execPath, ['.'], {}, assertNoTests);

function assertBad(err, stdout, stderr) {
  assertHeader(err, stdout, stderr);
  assert(err, 'bad file exits with an error');
  assert.notEqual(stderr, '', 'tapsert does not clear stderr');
  assert(/Premature exit with code \d/.test(stdout), 'exits prematurely');
  assert(/No assertions run/.test(stdout),
         'notices that no asserions were run');
}

function assertNoTests(err, stdout, stderr) {
  assertHeader(err, stdout, stderr);
  assert(err, 'no assertions run is considered a failure');
  assert.equal(stderr, '', 'tapsert does not write to stderr');
  assert(!/Premature exit with code/.test(stdout),
         'does not exit prematurely');
  assert(/No assertions run/.test(stdout), 'says no assertions were run');
}

<|next_version|>
var assert = require('./');
var execFile = require('child_process').execFile;

assert(assert, 'assert exists');
assert(assert.equal, 'assert.equal exists');
assert.equal(typeof assert.strictEqual, 'function',
             'assert.strictEqual is a function');
assert.doesNotThrow(function() {
  assert.throws(function() {
    throw Error('expected!');
  }, /expected/, 'supports assert.throws');
}, 'nested asserts are weird.');

execFile(process.execPath, ['test-bad-tests.js'], {}, assertBad);
execFile(process.execPath, ['.'], {}, assertNoTests);

function assertBad(err, stdout, stderr) {
  assertHeader(err, stdout, stderr);
  assert(err, 'bad file exits with an error');
  assert.notEqual(stderr, '', 'tapsert does not clear stderr');
  assert(/Premature exit with code \d/.test(stdout), 'exits prematurely');
  assert(/No assertions run/.test(stdout),
         'notices that no asserions were run');
}

function assertNoTests(err, stdout, stderr) {
  assertHeader(err, stdout, stderr);
  assert(err, 'no assertions run is considered a failure');
  assert.equal(stderr, '', 'tapsert does not write to stderr');
  assert(!/Premature exit with code/.test(stdout),
         'does not exit prematurely');
  assert(/No assertions run/.test(stdout), 'says no assertions were run');
}

function assertHeader(err, stdout, stderr) {
  assert(/^[\s]*TAP version 13/.test(stdout),
         'TAP version header is the first non-whitespace output');
}


Save Interactive State in addition to URL

Since calling `setLearnerUrl` does not alter the app behavior, we also
call `interactiveState` and post the current URL in a JSON blob there.
This does get saved on their end, and allows us to devise a redirection
mechanism to this saved URL when the user comes back.

--- src/mmw/js/src/core/itsiEmbed.js
+++ src/mmw/js/src/core/itsiEmbed.js
@@ -10,15 +10,20 @@

     this.url = window.location.href + '?' + QUERY_SUFFIX;

+    this.interactiveState = { url: this.url };
+
     this.setLearnerUrl = function(url) {
         if (url) {
             this.url = url + (url.indexOf('?') > 0 ? '&' : '?') + QUERY_SUFFIX;
+            this.interactiveState = { url: this.url };
         }

         this.phone.post('setLearnerUrl', this.url);
+        this.phone.post('interactiveState', this.interactiveState);
     };

     this.phone.addListener('getLearnerUrl', _.bind(this.setLearnerUrl, this));
+    this.phone.addListener('getInteractiveState', _.bind(this.setLearnerUrl, this));
     this.phone.initialize();
 };

<|original_code|>
"use strict";

var _ = require('lodash'),
    iframePhone = require('iframe-phone'),
    EMBED_FLAG = 'itsi_embed',
    QUERY_SUFFIX = EMBED_FLAG + '=true';

var ItsiEmbed = function() {
    this.phone = new iframePhone.getIFrameEndpoint();

    this.url = window.location.href + '?' + QUERY_SUFFIX;

    this.setLearnerUrl = function(url) {
        if (url) {
            this.url = url + (url.indexOf('?') > 0 ? '&' : '?') + QUERY_SUFFIX;
        }

        this.phone.post('setLearnerUrl', this.url);
    };

    this.phone.addListener('getLearnerUrl', _.bind(this.setLearnerUrl, this));
    this.phone.initialize();
};

module.exports = {
    ItsiEmbed: ItsiEmbed
};

<|edits_diff|>
--- src/mmw/js/src/core/itsiEmbed.js
+++ src/mmw/js/src/core/itsiEmbed.js
@@ -10,12 +10,16 @@

     this.url = window.location.href + '?' + QUERY_SUFFIX;

+    this.interactiveState = { url: this.url };
+
     this.setLearnerUrl = function(url) {
         if (url) {
             this.url = url + (url.indexOf('?') > 0 ? '&' : '?') + QUERY_SUFFIX;
+            this.interactiveState = { url: this.url };
         }

         this.phone.post('setLearnerUrl', this.url);
+        this.phone.post('interactiveState', this.interactiveState);
     };

     this.phone.addListener('getLearnerUrl', _.bind(this.setLearnerUrl, this));
<|current_version|>
"use strict";

var _ = require('lodash'),
    iframePhone = require('iframe-phone'),
    EMBED_FLAG = 'itsi_embed',
    QUERY_SUFFIX = EMBED_FLAG + '=true';

var ItsiEmbed = function() {
    this.phone = new iframePhone.getIFrameEndpoint();

    this.url = window.location.href + '?' + QUERY_SUFFIX;

    this.interactiveState = { url: this.url };

    this.setLearnerUrl = function(url) {
        if (url) {
            this.url = url + (url.indexOf('?') > 0 ? '&' : '?') + QUERY_SUFFIX;
            this.interactiveState = { url: this.url };
        }

        this.phone.post('setLearnerUrl', this.url);
        this.phone.post('interactiveState', this.interactiveState);
    };

    this.phone.addListener('getLearnerUrl', _.bind(this.setLearnerUrl, this));
    this.phone.initialize();
};

module.exports = {
    ItsiEmbed: ItsiEmbed
};

<|next_version|>
"use strict";

var _ = require('lodash'),
    iframePhone = require('iframe-phone'),
    EMBED_FLAG = 'itsi_embed',
    QUERY_SUFFIX = EMBED_FLAG + '=true';

var ItsiEmbed = function() {
    this.phone = new iframePhone.getIFrameEndpoint();

    this.url = window.location.href + '?' + QUERY_SUFFIX;

    this.interactiveState = { url: this.url };

    this.setLearnerUrl = function(url) {
        if (url) {
            this.url = url + (url.indexOf('?') > 0 ? '&' : '?') + QUERY_SUFFIX;
            this.interactiveState = { url: this.url };
        }

        this.phone.post('setLearnerUrl', this.url);
        this.phone.post('interactiveState', this.interactiveState);
    };

    this.phone.addListener('getLearnerUrl', _.bind(this.setLearnerUrl, this));
    this.phone.addListener('getInteractiveState', _.bind(this.setLearnerUrl, this));
    this.phone.initialize();
};

module.exports = {
    ItsiEmbed: ItsiEmbed
};


Add isPrivate flag to user

Add isPrivate flag to user

--- packages/shared/lib/user/helpers.js
+++ packages/shared/lib/user/helpers.js
@@ -5,6 +5,7 @@
 export const hasPaidMail = ({ Subscribed }) => Subscribed & 1;
 export const hasPaidVpn = ({ Subscribed }) => Subscribed & 4;
 export const isPaid = ({ Subscribed }) => Subscribed;
+export const isPrivate = ({ Private }) => Private === 1;
 export const isFree = (user) => !isPaid(user);
 export const isAdmin = ({ Role }) => Role === ADMIN_ROLE;
 export const isMember = ({ Role }) => Role === MEMBER_ROLE;
@@ -17,6 +18,7 @@
         isMember: isMember(User),
         isFree: isFree(User),
         isPaid: isPaid(User),
+        isPrivate: isPrivate(User),
         isSubUser: isSubUser(User),
         isDelinquent: isDelinquent(User),
         hasPaidMail: hasPaidMail(User),

<|original_code|>
import { USER_ROLES } from '../constants';

const { ADMIN_ROLE, MEMBER_ROLE } = USER_ROLES;

export const hasPaidMail = ({ Subscribed }) => Subscribed & 1;
export const hasPaidVpn = ({ Subscribed }) => Subscribed & 4;
export const isPaid = ({ Subscribed }) => Subscribed;
export const isFree = (user) => !isPaid(user);
export const isAdmin = ({ Role }) => Role === ADMIN_ROLE;
export const isMember = ({ Role }) => Role === MEMBER_ROLE;
export const isSubUser = ({ OrganizationPrivateKey }) => typeof OrganizationPrivateKey !== 'undefined';
export const isDelinquent = ({ Delinquent }) => Delinquent;

export const getInfo = (User) => {
    return {
        isAdmin: isAdmin(User),
        isMember: isMember(User),
        isFree: isFree(User),
        isPaid: isPaid(User),
        isSubUser: isSubUser(User),
        isDelinquent: isDelinquent(User),
        hasPaidMail: hasPaidMail(User),
        hasPaidVpn: hasPaidVpn(User)
    };
};

<|edits_diff|>
--- packages/shared/lib/user/helpers.js
+++ packages/shared/lib/user/helpers.js
@@ -5,6 +5,7 @@
 export const hasPaidMail = ({ Subscribed }) => Subscribed & 1;
 export const hasPaidVpn = ({ Subscribed }) => Subscribed & 4;
 export const isPaid = ({ Subscribed }) => Subscribed;
+export const isPrivate = ({ Private }) => Private === 1;
 export const isFree = (user) => !isPaid(user);
 export const isAdmin = ({ Role }) => Role === ADMIN_ROLE;
 export const isMember = ({ Role }) => Role === MEMBER_ROLE;
<|current_version|>
import { USER_ROLES } from '../constants';

const { ADMIN_ROLE, MEMBER_ROLE } = USER_ROLES;

export const hasPaidMail = ({ Subscribed }) => Subscribed & 1;
export const hasPaidVpn = ({ Subscribed }) => Subscribed & 4;
export const isPaid = ({ Subscribed }) => Subscribed;
export const isPrivate = ({ Private }) => Private === 1;
export const isFree = (user) => !isPaid(user);
export const isAdmin = ({ Role }) => Role === ADMIN_ROLE;
export const isMember = ({ Role }) => Role === MEMBER_ROLE;
export const isSubUser = ({ OrganizationPrivateKey }) => typeof OrganizationPrivateKey !== 'undefined';
export const isDelinquent = ({ Delinquent }) => Delinquent;

export const getInfo = (User) => {
    return {
        isAdmin: isAdmin(User),
        isMember: isMember(User),
        isFree: isFree(User),
        isPaid: isPaid(User),
        isSubUser: isSubUser(User),
        isDelinquent: isDelinquent(User),
        hasPaidMail: hasPaidMail(User),
        hasPaidVpn: hasPaidVpn(User)
    };
};

<|next_version|>
import { USER_ROLES } from '../constants';

const { ADMIN_ROLE, MEMBER_ROLE } = USER_ROLES;

export const hasPaidMail = ({ Subscribed }) => Subscribed & 1;
export const hasPaidVpn = ({ Subscribed }) => Subscribed & 4;
export const isPaid = ({ Subscribed }) => Subscribed;
export const isPrivate = ({ Private }) => Private === 1;
export const isFree = (user) => !isPaid(user);
export const isAdmin = ({ Role }) => Role === ADMIN_ROLE;
export const isMember = ({ Role }) => Role === MEMBER_ROLE;
export const isSubUser = ({ OrganizationPrivateKey }) => typeof OrganizationPrivateKey !== 'undefined';
export const isDelinquent = ({ Delinquent }) => Delinquent;

export const getInfo = (User) => {
    return {
        isAdmin: isAdmin(User),
        isMember: isMember(User),
        isFree: isFree(User),
        isPaid: isPaid(User),
        isPrivate: isPrivate(User),
        isSubUser: isSubUser(User),
        isDelinquent: isDelinquent(User),
        hasPaidMail: hasPaidMail(User),
        hasPaidVpn: hasPaidVpn(User)
    };
};


Allow one field payload on method links.

--- resources/assets/js/method-link.js
+++ resources/assets/js/method-link.js
@@ -10,6 +10,9 @@
     var url = $(this).attr('href');
     var method = $(this).data('method');

+    var formItem = $(this).data('form-item');
+    var formValue = $(this).data('form-value');
+
     var csrfToken = $('meta[name=csrf-token]').attr('content');

     var $form = $('<form method="post" action="' + url + '"></form>');
@@ -19,6 +22,10 @@
       metadataInput += '<input name="_token" value="' + csrfToken + '" type="hidden" />';
     }

+    if (formItem !== undefined && formValue !== undefined) {
+      metadataInput += '<input name="' + formItem + '" value="' + formValue + '" type="hidden" />';
+    }
+
     $form.hide().append(metadataInput);
     $body.append($form);

<|original_code|>
var $ = require('jquery');

$(document).ready(function() {
  var $body = $('body');

  // Allow links to specify a HTTP method using `data-method`
  $('[data-method]').on('click', function(event) {
    event.preventDefault();

    var url = $(this).attr('href');
    var method = $(this).data('method');

    var csrfToken = $('meta[name=csrf-token]').attr('content');

    var $form = $('<form method="post" action="' + url + '"></form>');
    var metadataInput = '<input name="_method" value="' + method + '" type="hidden" />';

    if (csrfToken !== undefined) {
      metadataInput += '<input name="_token" value="' + csrfToken + '" type="hidden" />';
    }

    $form.hide().append(metadataInput);
    $body.append($form);

    $form.submit();
  });
});

<|edits_diff|>
--- resources/assets/js/method-link.js
+++ resources/assets/js/method-link.js
@@ -9,6 +9,9 @@

     var url = $(this).attr('href');
     var method = $(this).data('method');
+
+    var formItem = $(this).data('form-item');
+    var formValue = $(this).data('form-value');

     var csrfToken = $('meta[name=csrf-token]').attr('content');

<|current_version|>
var $ = require('jquery');

$(document).ready(function() {
  var $body = $('body');

  // Allow links to specify a HTTP method using `data-method`
  $('[data-method]').on('click', function(event) {
    event.preventDefault();

    var url = $(this).attr('href');
    var method = $(this).data('method');

    var formItem = $(this).data('form-item');
    var formValue = $(this).data('form-value');

    var csrfToken = $('meta[name=csrf-token]').attr('content');

    var $form = $('<form method="post" action="' + url + '"></form>');
    var metadataInput = '<input name="_method" value="' + method + '" type="hidden" />';

    if (csrfToken !== undefined) {
      metadataInput += '<input name="_token" value="' + csrfToken + '" type="hidden" />';
    }

    $form.hide().append(metadataInput);
    $body.append($form);

    $form.submit();
  });
});

<|next_version|>
var $ = require('jquery');

$(document).ready(function() {
  var $body = $('body');

  // Allow links to specify a HTTP method using `data-method`
  $('[data-method]').on('click', function(event) {
    event.preventDefault();

    var url = $(this).attr('href');
    var method = $(this).data('method');

    var formItem = $(this).data('form-item');
    var formValue = $(this).data('form-value');

    var csrfToken = $('meta[name=csrf-token]').attr('content');

    var $form = $('<form method="post" action="' + url + '"></form>');
    var metadataInput = '<input name="_method" value="' + method + '" type="hidden" />';

    if (csrfToken !== undefined) {
      metadataInput += '<input name="_token" value="' + csrfToken + '" type="hidden" />';
    }

    if (formItem !== undefined && formValue !== undefined) {
      metadataInput += '<input name="' + formItem + '" value="' + formValue + '" type="hidden" />';
    }

    $form.hide().append(metadataInput);
    $body.append($form);

    $form.submit();
  });
});


Add import of location reducer

Add import of location reducer

--- src/reducers/Entities/index.js
+++ src/reducers/Entities/index.js
@@ -2,8 +2,10 @@

 import { TemperatureBreachConfigReducer } from './TemperatureBreachConfigReducer';
 import { SensorReducer } from './SensorReducer';
+import { LocationReducer } from './LocationReducer';

 export const EntitiesReducer = combineReducers({
   temperatureBreachConfiguration: TemperatureBreachConfigReducer,
   sensor: SensorReducer,
+  location: LocationReducer,
 });

<|original_code|>
import { combineReducers } from 'redux';

import { TemperatureBreachConfigReducer } from './TemperatureBreachConfigReducer';
import { SensorReducer } from './SensorReducer';

export const EntitiesReducer = combineReducers({
  temperatureBreachConfiguration: TemperatureBreachConfigReducer,
  sensor: SensorReducer,
});

<|edits_diff|>
--- src/reducers/Entities/index.js
+++ src/reducers/Entities/index.js
@@ -2,6 +2,7 @@

 import { TemperatureBreachConfigReducer } from './TemperatureBreachConfigReducer';
 import { SensorReducer } from './SensorReducer';
+import { LocationReducer } from './LocationReducer';

 export const EntitiesReducer = combineReducers({
   temperatureBreachConfiguration: TemperatureBreachConfigReducer,
<|current_version|>
import { combineReducers } from 'redux';

import { TemperatureBreachConfigReducer } from './TemperatureBreachConfigReducer';
import { SensorReducer } from './SensorReducer';
import { LocationReducer } from './LocationReducer';

export const EntitiesReducer = combineReducers({
  temperatureBreachConfiguration: TemperatureBreachConfigReducer,
  sensor: SensorReducer,
});

<|next_version|>
import { combineReducers } from 'redux';

import { TemperatureBreachConfigReducer } from './TemperatureBreachConfigReducer';
import { SensorReducer } from './SensorReducer';
import { LocationReducer } from './LocationReducer';

export const EntitiesReducer = combineReducers({
  temperatureBreachConfiguration: TemperatureBreachConfigReducer,
  sensor: SensorReducer,
  location: LocationReducer,
});


Add workaround to display DB error log.

Add workaround to display DB error log.

--- api/src/hooks/errors.js
+++ api/src/hooks/errors.js
@@ -1,10 +1,15 @@
 import { UniqueViolationError } from 'objection-db-errors'
 import { FeathersError, Conflict, GeneralError } from '@feathersjs/errors'
+import _ from 'lodash'

 const errorHandler = (ctx) => {
   if (!ctx.error) {
     return
   }
+  // FIXME currently using console.log to display DB errors
+  // as app.error doesn't display it
+  // eslint-disable-next-line no-console
+  console.log(_.omit(ctx.error, ['hook']))
   if (ctx.error instanceof UniqueViolationError) {
     const error = new Conflict('Unique Violation Error')
     error.errors = ctx.error.columns

<|original_code|>
import { UniqueViolationError } from 'objection-db-errors'
import { FeathersError, Conflict, GeneralError } from '@feathersjs/errors'

const errorHandler = (ctx) => {
  if (!ctx.error) {
    return
  }
  if (ctx.error instanceof UniqueViolationError) {
    const error = new Conflict('Unique Violation Error')
    error.errors = ctx.error.columns
    throw error
  }
  if (ctx.error instanceof FeathersError) {
    throw ctx.error
  }
  throw new GeneralError('unknown error', ctx.error.data)
}

export default errorHandler

<|edits_diff|>
--- api/src/hooks/errors.js
+++ api/src/hooks/errors.js
@@ -1,5 +1,6 @@
 import { UniqueViolationError } from 'objection-db-errors'
 import { FeathersError, Conflict, GeneralError } from '@feathersjs/errors'
+import _ from 'lodash'

 const errorHandler = (ctx) => {
   if (!ctx.error) {
<|current_version|>
import { UniqueViolationError } from 'objection-db-errors'
import { FeathersError, Conflict, GeneralError } from '@feathersjs/errors'
import _ from 'lodash'

const errorHandler = (ctx) => {
  if (!ctx.error) {
    return
  }
  if (ctx.error instanceof UniqueViolationError) {
    const error = new Conflict('Unique Violation Error')
    error.errors = ctx.error.columns
    throw error
  }
  if (ctx.error instanceof FeathersError) {
    throw ctx.error
  }
  throw new GeneralError('unknown error', ctx.error.data)
}

export default errorHandler

<|next_version|>
import { UniqueViolationError } from 'objection-db-errors'
import { FeathersError, Conflict, GeneralError } from '@feathersjs/errors'
import _ from 'lodash'

const errorHandler = (ctx) => {
  if (!ctx.error) {
    return
  }
  // FIXME currently using console.log to display DB errors
  // as app.error doesn't display it
  // eslint-disable-next-line no-console
  console.log(_.omit(ctx.error, ['hook']))
  if (ctx.error instanceof UniqueViolationError) {
    const error = new Conflict('Unique Violation Error')
    error.errors = ctx.error.columns
    throw error
  }
  if (ctx.error instanceof FeathersError) {
    throw ctx.error
  }
  throw new GeneralError('unknown error', ctx.error.data)
}

export default errorHandler


Add comment with jsdoc style

Add comment with jsdoc style

--- email.js
+++ email.js
@@ -3,6 +3,11 @@
 var logger = log4js.getLogger('Logging');

 function sendAliveMail (connection,opt,callback){
+
+/**
+ *Receive the connection of mail server, mail option and callback,
+ *Then send the alive mail by the these value.
+ * */

     logger.debug('Sending Alive Mail......');

@@ -10,6 +15,11 @@
 }

 function sendWarningMail(connection,opt,callback){
+
+/**
+ *Receive the connection of mail server, mail option and callback,
+ *Then send the warning mail by the these value.
+ * */

     logger.debug('Sending Warning Mail......');

<|original_code|>
//Require Module
var log4js = require('log4js');
var logger = log4js.getLogger('Logging');

function sendAliveMail (connection,opt,callback){

    logger.debug('Sending Alive Mail......');

    connection.send(opt,callback);
}

function sendWarningMail(connection,opt,callback){

    logger.debug('Sending Warning Mail......');

    connection.send(opt,callback);
}

module.exports = {
    sendAliveMail:sendAliveMail,
    sendWarningMail:sendWarningMail
}

<|edits_diff|>
--- email.js
+++ email.js
@@ -3,6 +3,11 @@
 var logger = log4js.getLogger('Logging');

 function sendAliveMail (connection,opt,callback){
+
+/**
+ *Receive the connection of mail server, mail option and callback,
+ *Then send the alive mail by the these value.
+ * */

     logger.debug('Sending Alive Mail......');

<|current_version|>
//Require Module
var log4js = require('log4js');
var logger = log4js.getLogger('Logging');

function sendAliveMail (connection,opt,callback){

/**
 *Receive the connection of mail server, mail option and callback,
 *Then send the alive mail by the these value.
 * */

    logger.debug('Sending Alive Mail......');

    connection.send(opt,callback);
}

function sendWarningMail(connection,opt,callback){

    logger.debug('Sending Warning Mail......');

    connection.send(opt,callback);
}

module.exports = {
    sendAliveMail:sendAliveMail,
    sendWarningMail:sendWarningMail
}

<|next_version|>
//Require Module
var log4js = require('log4js');
var logger = log4js.getLogger('Logging');

function sendAliveMail (connection,opt,callback){

/**
 *Receive the connection of mail server, mail option and callback,
 *Then send the alive mail by the these value.
 * */

    logger.debug('Sending Alive Mail......');

    connection.send(opt,callback);
}

function sendWarningMail(connection,opt,callback){

/**
 *Receive the connection of mail server, mail option and callback,
 *Then send the warning mail by the these value.
 * */

    logger.debug('Sending Warning Mail......');

    connection.send(opt,callback);
}

module.exports = {
    sendAliveMail:sendAliveMail,
    sendWarningMail:sendWarningMail
}


fix: Replace Mongoose promise support with another Q's

--- index.js
+++ index.js
@@ -3,10 +3,15 @@
 const winston = require('winston');
 const bodyParser = require('body-parser');
 const env = require('./src/env');
+const q = require('q');
+var mongoose = require('mongoose');

 var server = express();
 var router = require('./src/router');
 var PORT = env.PORT || 8000;
+
+// Built-in Promise support is deprecated
+mongoose.Promise = q.Promise;

 server.use(bodyParser.json());
 server.use(helmet());

<|original_code|>
const express = require('express');
const helmet = require('helmet');
const winston = require('winston');
const bodyParser = require('body-parser');
const env = require('./src/env');

var server = express();
var router = require('./src/router');
var PORT = env.PORT || 8000;

server.use(bodyParser.json());
server.use(helmet());
server.use('/', router);

function _get() {
  return server;
}

function run(fn) {

  fn = fn || function _defaultStart() {
    winston.info('Listening at ' + PORT);
  };

  return server.listen(PORT, fn);
}

if (require.main === module) {
  run();
}

module.exports = {
  _get: _get,
  run: run
};

<|edits_diff|>
--- index.js
+++ index.js
@@ -3,6 +3,8 @@
 const winston = require('winston');
 const bodyParser = require('body-parser');
 const env = require('./src/env');
+const q = require('q');
+var mongoose = require('mongoose');

 var server = express();
 var router = require('./src/router');
<|current_version|>
const express = require('express');
const helmet = require('helmet');
const winston = require('winston');
const bodyParser = require('body-parser');
const env = require('./src/env');
const q = require('q');
var mongoose = require('mongoose');

var server = express();
var router = require('./src/router');
var PORT = env.PORT || 8000;

server.use(bodyParser.json());
server.use(helmet());
server.use('/', router);

function _get() {
  return server;
}

function run(fn) {

  fn = fn || function _defaultStart() {
    winston.info('Listening at ' + PORT);
  };

  return server.listen(PORT, fn);
}

if (require.main === module) {
  run();
}

module.exports = {
  _get: _get,
  run: run
};

<|next_version|>
const express = require('express');
const helmet = require('helmet');
const winston = require('winston');
const bodyParser = require('body-parser');
const env = require('./src/env');
const q = require('q');
var mongoose = require('mongoose');

var server = express();
var router = require('./src/router');
var PORT = env.PORT || 8000;

// Built-in Promise support is deprecated
mongoose.Promise = q.Promise;

server.use(bodyParser.json());
server.use(helmet());
server.use('/', router);

function _get() {
  return server;
}

function run(fn) {

  fn = fn || function _defaultStart() {
    winston.info('Listening at ' + PORT);
  };

  return server.listen(PORT, fn);
}

if (require.main === module) {
  run();
}

module.exports = {
  _get: _get,
  run: run
};


Add debug logs to preview release

Add debug logs to preview release

--- core/actions/slackPreviewRelease.js
+++ core/actions/slackPreviewRelease.js
@@ -14,6 +14,7 @@
   };

   function getConfigForEachRepo(repos, cb) {
+    logger.debug('getConfigForEachRepo', { repos });
     mapSeries(repos, (repo, nextRepo) => {
       getConfig(repo, (err, config) => {
         if (err) {
@@ -26,6 +27,7 @@
   }

   function notifyPreviewInSlack(reposInfo, verbose, cb) {
+    logger.debug('notifyPreviewInSlack', { reposInfo, verbose });
     notify(reposInfo, 'preview', verbose, cb);
   }
 };

<|original_code|>
'use strict';

const { waterfall, mapSeries } = require('async');
const logger = require('../../lib/logger');

module.exports = function createPreviewRelease(getConfig, getReleasePreview, notify, repos) {

  return ({ verbose = false }, cb) => {
    waterfall([
      (next)            => getConfigForEachRepo(repos, next),
      (reposInfo, next) => getReleasePreview(reposInfo, next),
      (reposInfo, next) => notifyPreviewInSlack(reposInfo, verbose, next)
    ], cb);
  };

  function getConfigForEachRepo(repos, cb) {
    mapSeries(repos, (repo, nextRepo) => {
      getConfig(repo, (err, config) => {
        if (err) {
          logger.error('Error getting repo config', repo, err);
          return nextRepo(null, { repo, failReason: 'INVALID_REPO_CONFIG' });
        }
        nextRepo(null, { repo, config });
      });
    }, cb);
  }

  function notifyPreviewInSlack(reposInfo, verbose, cb) {
    notify(reposInfo, 'preview', verbose, cb);
  }
};

<|edits_diff|>
--- core/actions/slackPreviewRelease.js
+++ core/actions/slackPreviewRelease.js
@@ -14,6 +14,7 @@
   };

   function getConfigForEachRepo(repos, cb) {
+    logger.debug('getConfigForEachRepo', { repos });
     mapSeries(repos, (repo, nextRepo) => {
       getConfig(repo, (err, config) => {
         if (err) {
<|current_version|>
'use strict';

const { waterfall, mapSeries } = require('async');
const logger = require('../../lib/logger');

module.exports = function createPreviewRelease(getConfig, getReleasePreview, notify, repos) {

  return ({ verbose = false }, cb) => {
    waterfall([
      (next)            => getConfigForEachRepo(repos, next),
      (reposInfo, next) => getReleasePreview(reposInfo, next),
      (reposInfo, next) => notifyPreviewInSlack(reposInfo, verbose, next)
    ], cb);
  };

  function getConfigForEachRepo(repos, cb) {
    logger.debug('getConfigForEachRepo', { repos });
    mapSeries(repos, (repo, nextRepo) => {
      getConfig(repo, (err, config) => {
        if (err) {
          logger.error('Error getting repo config', repo, err);
          return nextRepo(null, { repo, failReason: 'INVALID_REPO_CONFIG' });
        }
        nextRepo(null, { repo, config });
      });
    }, cb);
  }

  function notifyPreviewInSlack(reposInfo, verbose, cb) {
    notify(reposInfo, 'preview', verbose, cb);
  }
};

<|next_version|>
'use strict';

const { waterfall, mapSeries } = require('async');
const logger = require('../../lib/logger');

module.exports = function createPreviewRelease(getConfig, getReleasePreview, notify, repos) {

  return ({ verbose = false }, cb) => {
    waterfall([
      (next)            => getConfigForEachRepo(repos, next),
      (reposInfo, next) => getReleasePreview(reposInfo, next),
      (reposInfo, next) => notifyPreviewInSlack(reposInfo, verbose, next)
    ], cb);
  };

  function getConfigForEachRepo(repos, cb) {
    logger.debug('getConfigForEachRepo', { repos });
    mapSeries(repos, (repo, nextRepo) => {
      getConfig(repo, (err, config) => {
        if (err) {
          logger.error('Error getting repo config', repo, err);
          return nextRepo(null, { repo, failReason: 'INVALID_REPO_CONFIG' });
        }
        nextRepo(null, { repo, config });
      });
    }, cb);
  }

  function notifyPreviewInSlack(reposInfo, verbose, cb) {
    logger.debug('notifyPreviewInSlack', { reposInfo, verbose });
    notify(reposInfo, 'preview', verbose, cb);
  }
};


Add new REST API for getting the target location

- /arduino/location?lat=123&lon=456

--- routes/index.js
+++ routes/index.js
@@ -19,6 +19,13 @@
 			results: results
 		});
 	});
+});

+router.get('/arduino/location', function(req, res, next) {
+    var lat = req.param('lat');
+    var lon = req.param('lon');
+    console.info('[NFC] lat: ' + lat + " | lon: " + lon);
+    res.send(200);
 });
+
 module.exports = router;

<|original_code|>
var express = require('express');
var router = express.Router();
var arduino = require('../lib/arduino');

/* GET home page. */
router.get('/', function(req, res, next) {
    res.render('index', {
        title: 'Findmon - 2015 SBA IoT Hackathon'
    });
});

router.get('/arduino/display', function(req, res, next) {
	var message = req.param("message");

	arduino.sendMessage(message, function(err, results) {
		res.render('arduinoRes', {
			title: 'Arduino Response',
			error: err,
			results: results
		});
	});

});
module.exports = router;
<|edits_diff|>
--- routes/index.js
+++ routes/index.js
@@ -19,6 +19,12 @@
 			results: results
 		});
 	});
+});

+router.get('/arduino/location', function(req, res, next) {
+    var lat = req.param('lat');
+    var lon = req.param('lon');
+    console.info('[NFC] lat: ' + lat + " | lon: " + lon);
+    res.send(200);
 });
 module.exports = router;
<|current_version|>
var express = require('express');
var router = express.Router();
var arduino = require('../lib/arduino');

/* GET home page. */
router.get('/', function(req, res, next) {
    res.render('index', {
        title: 'Findmon - 2015 SBA IoT Hackathon'
    });
});

router.get('/arduino/display', function(req, res, next) {
	var message = req.param("message");

	arduino.sendMessage(message, function(err, results) {
		res.render('arduinoRes', {
			title: 'Arduino Response',
			error: err,
			results: results
		});
	});
});

router.get('/arduino/location', function(req, res, next) {
    var lat = req.param('lat');
    var lon = req.param('lon');
    console.info('[NFC] lat: ' + lat + " | lon: " + lon);
    res.send(200);
});
module.exports = router;
<|next_version|>
var express = require('express');
var router = express.Router();
var arduino = require('../lib/arduino');

/* GET home page. */
router.get('/', function(req, res, next) {
    res.render('index', {
        title: 'Findmon - 2015 SBA IoT Hackathon'
    });
});

router.get('/arduino/display', function(req, res, next) {
	var message = req.param("message");

	arduino.sendMessage(message, function(err, results) {
		res.render('arduinoRes', {
			title: 'Arduino Response',
			error: err,
			results: results
		});
	});
});

router.get('/arduino/location', function(req, res, next) {
    var lat = req.param('lat');
    var lon = req.param('lon');
    console.info('[NFC] lat: ' + lat + " | lon: " + lon);
    res.send(200);
});

module.exports = router;


Add isRunning method to Job

Add isRunning method to Job

--- src/job.js
+++ src/job.js
@@ -1,6 +1,7 @@
 var util = require('util');
 var _ = require('underscore');
 var EventEmitter = require('events').EventEmitter;
+var PulsarJob = require('../node_modules/pulsar-rest-api/lib/pulsar/job');

 /**
  * @param {String} app
@@ -29,6 +30,10 @@
   return _.extend(this.data, jobData);
 };

+Job.prototype.isRunning = function() {
+  return this.data.status == PulsarJob.STATUS.RUNNING;
+};
+
 Job.prototype.toString = function() {
   var result = util.format('%s "%s" to "%s"', this.task, this.app, this.env);
   if (this.data.id) {

<|original_code|>
var util = require('util');
var _ = require('underscore');
var EventEmitter = require('events').EventEmitter;

/**
 * @param {String} app
 * @param {String} env
 * @param {String} task
 * @param {Object.<String, String>} [taskVariables]
 * @constructor
 */
function Job(app, env, task, taskVariables) {
  this.app = app;
  this.env = env;
  this.task = task;
  this.taskVariables = taskVariables;
  this.data = {};

  EventEmitter.call(this);
}

util.inherits(Job, EventEmitter);

/**
 * @param {Object} jobData
 * @returns {Object} new merged Job's data
 */
Job.prototype.setData = function(jobData) {
  return _.extend(this.data, jobData);
};

Job.prototype.toString = function() {
  var result = util.format('%s "%s" to "%s"', this.task, this.app, this.env);
  if (this.data.id) {
    result += ' id: ' + this.data.id;
  }
  return result;
};

module.exports = Job;

<|edits_diff|>
--- src/job.js
+++ src/job.js
@@ -1,6 +1,7 @@
 var util = require('util');
 var _ = require('underscore');
 var EventEmitter = require('events').EventEmitter;
+var PulsarJob = require('../node_modules/pulsar-rest-api/lib/pulsar/job');

 /**
  * @param {String} app
<|current_version|>
var util = require('util');
var _ = require('underscore');
var EventEmitter = require('events').EventEmitter;
var PulsarJob = require('../node_modules/pulsar-rest-api/lib/pulsar/job');

/**
 * @param {String} app
 * @param {String} env
 * @param {String} task
 * @param {Object.<String, String>} [taskVariables]
 * @constructor
 */
function Job(app, env, task, taskVariables) {
  this.app = app;
  this.env = env;
  this.task = task;
  this.taskVariables = taskVariables;
  this.data = {};

  EventEmitter.call(this);
}

util.inherits(Job, EventEmitter);

/**
 * @param {Object} jobData
 * @returns {Object} new merged Job's data
 */
Job.prototype.setData = function(jobData) {
  return _.extend(this.data, jobData);
};

Job.prototype.toString = function() {
  var result = util.format('%s "%s" to "%s"', this.task, this.app, this.env);
  if (this.data.id) {
    result += ' id: ' + this.data.id;
  }
  return result;
};

module.exports = Job;

<|next_version|>
var util = require('util');
var _ = require('underscore');
var EventEmitter = require('events').EventEmitter;
var PulsarJob = require('../node_modules/pulsar-rest-api/lib/pulsar/job');

/**
 * @param {String} app
 * @param {String} env
 * @param {String} task
 * @param {Object.<String, String>} [taskVariables]
 * @constructor
 */
function Job(app, env, task, taskVariables) {
  this.app = app;
  this.env = env;
  this.task = task;
  this.taskVariables = taskVariables;
  this.data = {};

  EventEmitter.call(this);
}

util.inherits(Job, EventEmitter);

/**
 * @param {Object} jobData
 * @returns {Object} new merged Job's data
 */
Job.prototype.setData = function(jobData) {
  return _.extend(this.data, jobData);
};

Job.prototype.isRunning = function() {
  return this.data.status == PulsarJob.STATUS.RUNNING;
};

Job.prototype.toString = function() {
  var result = util.format('%s "%s" to "%s"', this.task, this.app, this.env);
  if (this.data.id) {
    result += ' id: ' + this.data.id;
  }
  return result;
};

module.exports = Job;


Add errorMessage notifcation from backend

--- app/utils/websockets.js
+++ app/utils/websockets.js
@@ -1,6 +1,7 @@
 import WebSocketClient from 'websocket.js';
 import config from '../config';
 import createQueryString from './createQueryString';
+import { addNotification } from 'app/actions/NotificationActions';
 import { User } from 'app/actions/ActionTypes';

 export default function createWebSocketMiddleware() {
@@ -16,6 +17,9 @@
       socket.onmessage = event => {
         const { type, payload, meta } = JSON.parse(event.data);
         store.dispatch({ type, payload, meta });
+        if (meta.errorMessage) {
+          store.dispatch(addNotification({ message: meta.errorMessage }));
+        }
       };

       socket.onopen = () => {

<|original_code|>
import WebSocketClient from 'websocket.js';
import config from '../config';
import createQueryString from './createQueryString';
import { User } from 'app/actions/ActionTypes';

export default function createWebSocketMiddleware() {
  let socket = null;

  return store => {
    const makeSocket = jwt => {
      if (socket || !jwt) return;

      const qs = createQueryString({ jwt });
      socket = new WebSocketClient(`${config.wsServerUrl}/${qs}`);

      socket.onmessage = event => {
        const { type, payload, meta } = JSON.parse(event.data);
        store.dispatch({ type, payload, meta });
      };

      socket.onopen = () => {
        store.dispatch({ type: 'WS_CONNECTED' });
      };

      socket.onclose = () => {
        store.dispatch({ type: 'WS_CLOSED' });
      };

      socket.onerror = () => {
        store.dispatch({ type: 'WS_ERROR' });
      };
    };

    return next =>
      action => {
        if (action.type === 'REHYDRATED') {
          makeSocket(store.getState().auth.token);
          return next(action);
        }

        if (action.type === User.LOGIN.SUCCESS) {
          makeSocket(action.payload.token);
          return next(action);
        }

        if (action.type === User.LOGOUT) {
          if (socket) {
            socket.close();
          }

          socket = null;

          return next(action);
        }

        return next(action);
      };
  };
}

<|edits_diff|>
--- app/utils/websockets.js
+++ app/utils/websockets.js
@@ -1,6 +1,7 @@
 import WebSocketClient from 'websocket.js';
 import config from '../config';
 import createQueryString from './createQueryString';
+import { addNotification } from 'app/actions/NotificationActions';
 import { User } from 'app/actions/ActionTypes';

 export default function createWebSocketMiddleware() {
<|current_version|>
import WebSocketClient from 'websocket.js';
import config from '../config';
import createQueryString from './createQueryString';
import { addNotification } from 'app/actions/NotificationActions';
import { User } from 'app/actions/ActionTypes';

export default function createWebSocketMiddleware() {
  let socket = null;

  return store => {
    const makeSocket = jwt => {
      if (socket || !jwt) return;

      const qs = createQueryString({ jwt });
      socket = new WebSocketClient(`${config.wsServerUrl}/${qs}`);

      socket.onmessage = event => {
        const { type, payload, meta } = JSON.parse(event.data);
        store.dispatch({ type, payload, meta });
      };

      socket.onopen = () => {
        store.dispatch({ type: 'WS_CONNECTED' });
      };

      socket.onclose = () => {
        store.dispatch({ type: 'WS_CLOSED' });
      };

      socket.onerror = () => {
        store.dispatch({ type: 'WS_ERROR' });
      };
    };

    return next =>
      action => {
        if (action.type === 'REHYDRATED') {
          makeSocket(store.getState().auth.token);
          return next(action);
        }

        if (action.type === User.LOGIN.SUCCESS) {
          makeSocket(action.payload.token);
          return next(action);
        }

        if (action.type === User.LOGOUT) {
          if (socket) {
            socket.close();
          }

          socket = null;

          return next(action);
        }

        return next(action);
      };
  };
}

<|next_version|>
import WebSocketClient from 'websocket.js';
import config from '../config';
import createQueryString from './createQueryString';
import { addNotification } from 'app/actions/NotificationActions';
import { User } from 'app/actions/ActionTypes';

export default function createWebSocketMiddleware() {
  let socket = null;

  return store => {
    const makeSocket = jwt => {
      if (socket || !jwt) return;

      const qs = createQueryString({ jwt });
      socket = new WebSocketClient(`${config.wsServerUrl}/${qs}`);

      socket.onmessage = event => {
        const { type, payload, meta } = JSON.parse(event.data);
        store.dispatch({ type, payload, meta });
        if (meta.errorMessage) {
          store.dispatch(addNotification({ message: meta.errorMessage }));
        }
      };

      socket.onopen = () => {
        store.dispatch({ type: 'WS_CONNECTED' });
      };

      socket.onclose = () => {
        store.dispatch({ type: 'WS_CLOSED' });
      };

      socket.onerror = () => {
        store.dispatch({ type: 'WS_ERROR' });
      };
    };

    return next =>
      action => {
        if (action.type === 'REHYDRATED') {
          makeSocket(store.getState().auth.token);
          return next(action);
        }

        if (action.type === User.LOGIN.SUCCESS) {
          makeSocket(action.payload.token);
          return next(action);
        }

        if (action.type === User.LOGOUT) {
          if (socket) {
            socket.close();
          }

          socket = null;

          return next(action);
        }

        return next(action);
      };
  };
}


Add handling to InvalidChannelNameError for generating reply messages for several other commands

--- lib/errors/invalid-channel-name.js
+++ lib/errors/invalid-channel-name.js
@@ -32,7 +32,9 @@
 		var command = this.getCommand();

 		switch (command) {
+			case Commands.JOIN:
 			case Commands.MODE:
+			case Commands.PART:
 				let message = createMessage(ReplyNumerics.ERR_NOSUCHCHANNEL);

 				return message.setChannelName(this.getValue());

<|original_code|>

var
	extend        = req('/lib/utilities/extend'),
	BaseError     = req('/lib/errors/base'),
	ErrorCodes    = req('/lib/constants/error-codes'),
	ErrorReasons  = req('/lib/constants/error-reasons'),
	createMessage = req('/lib/utilities/create-message'),
	ReplyNumerics = req('/lib/constants/reply-numerics'),
	Commands      = req('/lib/constants/commands');

class InvalidChannelNameError extends BaseError {

	getBody() {
		switch (this.reason) {
			case ErrorReasons.OMITTED:
				return 'You must specify a channel name';
			case ErrorReasons.INVALID_CHARACTERS:
				return 'Invalid channel name: ' + this.getChannelName();
			case ErrorReasons.NOT_FOUND:
				return 'Channel did not exist: ' + this.getChannelName();
			default:
				return 'Invalid channel name: ' + this.reason;
		}
	}

	getChannelName() {
		return this.value;
	}

	toMessage() {
		var command = this.getCommand();

		switch (command) {
			case Commands.MODE:
				let message = createMessage(ReplyNumerics.ERR_NOSUCHCHANNEL);

				return message.setChannelName(this.getValue());

			default:
				throw new Error('implement');
		}
	}

}

extend(InvalidChannelNameError.prototype, {
	code: ErrorCodes.INVALID_CHANNEL_NAME
});

module.exports = InvalidChannelNameError;

<|edits_diff|>
--- lib/errors/invalid-channel-name.js
+++ lib/errors/invalid-channel-name.js
@@ -32,6 +32,7 @@
 		var command = this.getCommand();

 		switch (command) {
+			case Commands.JOIN:
 			case Commands.MODE:
 				let message = createMessage(ReplyNumerics.ERR_NOSUCHCHANNEL);

<|current_version|>

var
	extend        = req('/lib/utilities/extend'),
	BaseError     = req('/lib/errors/base'),
	ErrorCodes    = req('/lib/constants/error-codes'),
	ErrorReasons  = req('/lib/constants/error-reasons'),
	createMessage = req('/lib/utilities/create-message'),
	ReplyNumerics = req('/lib/constants/reply-numerics'),
	Commands      = req('/lib/constants/commands');

class InvalidChannelNameError extends BaseError {

	getBody() {
		switch (this.reason) {
			case ErrorReasons.OMITTED:
				return 'You must specify a channel name';
			case ErrorReasons.INVALID_CHARACTERS:
				return 'Invalid channel name: ' + this.getChannelName();
			case ErrorReasons.NOT_FOUND:
				return 'Channel did not exist: ' + this.getChannelName();
			default:
				return 'Invalid channel name: ' + this.reason;
		}
	}

	getChannelName() {
		return this.value;
	}

	toMessage() {
		var command = this.getCommand();

		switch (command) {
			case Commands.JOIN:
			case Commands.MODE:
				let message = createMessage(ReplyNumerics.ERR_NOSUCHCHANNEL);

				return message.setChannelName(this.getValue());

			default:
				throw new Error('implement');
		}
	}

}

extend(InvalidChannelNameError.prototype, {
	code: ErrorCodes.INVALID_CHANNEL_NAME
});

module.exports = InvalidChannelNameError;

<|next_version|>

var
	extend        = req('/lib/utilities/extend'),
	BaseError     = req('/lib/errors/base'),
	ErrorCodes    = req('/lib/constants/error-codes'),
	ErrorReasons  = req('/lib/constants/error-reasons'),
	createMessage = req('/lib/utilities/create-message'),
	ReplyNumerics = req('/lib/constants/reply-numerics'),
	Commands      = req('/lib/constants/commands');

class InvalidChannelNameError extends BaseError {

	getBody() {
		switch (this.reason) {
			case ErrorReasons.OMITTED:
				return 'You must specify a channel name';
			case ErrorReasons.INVALID_CHARACTERS:
				return 'Invalid channel name: ' + this.getChannelName();
			case ErrorReasons.NOT_FOUND:
				return 'Channel did not exist: ' + this.getChannelName();
			default:
				return 'Invalid channel name: ' + this.reason;
		}
	}

	getChannelName() {
		return this.value;
	}

	toMessage() {
		var command = this.getCommand();

		switch (command) {
			case Commands.JOIN:
			case Commands.MODE:
			case Commands.PART:
				let message = createMessage(ReplyNumerics.ERR_NOSUCHCHANNEL);

				return message.setChannelName(this.getValue());

			default:
				throw new Error('implement');
		}
	}

}

extend(InvalidChannelNameError.prototype, {
	code: ErrorCodes.INVALID_CHANNEL_NAME
});

module.exports = InvalidChannelNameError;


Add `lodash.memoize` to benchmark suite

Add `lodash.memoize` to benchmark suite

--- benchmark/index.js
+++ benchmark/index.js
@@ -3,6 +3,7 @@
 let Benchmark = require('benchmark')
 let memoize1 = require('./1')
 let underscore = require('underscore').memoize
+let lodash = require('lodash').memoize

 //
 // Fibonacci suite
@@ -14,6 +15,7 @@

 let memoized1 = memoize1(fibonacci)
 let memoizedUnderscore = underscore(fibonacci)
+let memoizedLodash = lodash(fibonacci)

 let suiteFibonnaci = new Benchmark.Suite()

@@ -27,6 +29,9 @@
   .add('underscore', () => {
     memoizedUnderscore(15)
   })
+  .add('lodash', () => {
+    memoizedLodash(15)
+  })
   .on('cycle', (event) => {
     console.log(String(event.target))
   })

<|original_code|>
'use strict'

let Benchmark = require('benchmark')
let memoize1 = require('./1')
let underscore = require('underscore').memoize

//
// Fibonacci suite
//

let fibonacci = (n) => {
  return n < 2 ? n: fibonacci(n - 1) + fibonacci(n - 2)
}

let memoized1 = memoize1(fibonacci)
let memoizedUnderscore = underscore(fibonacci)

let suiteFibonnaci = new Benchmark.Suite()

suiteFibonnaci
  .add('vanilla', () => {
    fibonacci(15)
  })
  .add('algorithm1', () => {
    memoized1(15)
  })
  .add('underscore', () => {
    memoizedUnderscore(15)
  })
  .on('cycle', (event) => {
    console.log(String(event.target))
  })
  .on('complete', function() {
    console.log('Fastest is ' + this.filter('fastest').map('name'))
  })
  .run({'async': true})

<|edits_diff|>
--- benchmark/index.js
+++ benchmark/index.js
@@ -3,6 +3,7 @@
 let Benchmark = require('benchmark')
 let memoize1 = require('./1')
 let underscore = require('underscore').memoize
+let lodash = require('lodash').memoize

 //
 // Fibonacci suite
@@ -14,6 +15,7 @@

 let memoized1 = memoize1(fibonacci)
 let memoizedUnderscore = underscore(fibonacci)
+let memoizedLodash = lodash(fibonacci)

 let suiteFibonnaci = new Benchmark.Suite()

<|current_version|>
'use strict'

let Benchmark = require('benchmark')
let memoize1 = require('./1')
let underscore = require('underscore').memoize
let lodash = require('lodash').memoize

//
// Fibonacci suite
//

let fibonacci = (n) => {
  return n < 2 ? n: fibonacci(n - 1) + fibonacci(n - 2)
}

let memoized1 = memoize1(fibonacci)
let memoizedUnderscore = underscore(fibonacci)
let memoizedLodash = lodash(fibonacci)

let suiteFibonnaci = new Benchmark.Suite()

suiteFibonnaci
  .add('vanilla', () => {
    fibonacci(15)
  })
  .add('algorithm1', () => {
    memoized1(15)
  })
  .add('underscore', () => {
    memoizedUnderscore(15)
  })
  .on('cycle', (event) => {
    console.log(String(event.target))
  })
  .on('complete', function() {
    console.log('Fastest is ' + this.filter('fastest').map('name'))
  })
  .run({'async': true})

<|next_version|>
'use strict'

let Benchmark = require('benchmark')
let memoize1 = require('./1')
let underscore = require('underscore').memoize
let lodash = require('lodash').memoize

//
// Fibonacci suite
//

let fibonacci = (n) => {
  return n < 2 ? n: fibonacci(n - 1) + fibonacci(n - 2)
}

let memoized1 = memoize1(fibonacci)
let memoizedUnderscore = underscore(fibonacci)
let memoizedLodash = lodash(fibonacci)

let suiteFibonnaci = new Benchmark.Suite()

suiteFibonnaci
  .add('vanilla', () => {
    fibonacci(15)
  })
  .add('algorithm1', () => {
    memoized1(15)
  })
  .add('underscore', () => {
    memoizedUnderscore(15)
  })
  .add('lodash', () => {
    memoizedLodash(15)
  })
  .on('cycle', (event) => {
    console.log(String(event.target))
  })
  .on('complete', function() {
    console.log('Fastest is ' + this.filter('fastest').map('name'))
  })
  .run({'async': true})


Add 'active' class bindings for component that is added when Aviary Editor is launched and removed with Aviary's 'onClose' callback

--- addon/components/aviary-editor.js
+++ addon/components/aviary-editor.js
@@ -3,10 +3,12 @@
 export default Ember.Component.extend({
   aviaryApiClient: null, // injected
   classNames: ['edit-icon'],
+  classNameBindings: ['featherActive:active'],
   image: null,
   url: null,
   imageSelector: null,
   closeOnSave: false,
+  featherActive: false,

   imageToEdit: function() {
     var imageNotSet = Ember.isEmpty(this.get('image'));
@@ -21,10 +23,14 @@
       url: this.get('url'),
       onSave: function(imageId, src) {
         self.send('onSave', imageId, src);
+      },
+      onClose: function() {
+        self.send('onClose');
       }
     };

     this.aviaryApiClient.launch(options);
+    this.set('featherActive', true);

     return false;
   },
@@ -36,6 +42,9 @@
       var closeOnSave = this.get('closeOnSave');
       var featherEditor = this.aviaryApiClient.get('featherEditor');
       if(closeOnSave) { featherEditor.close(); }
+    },
+    onClose: function() {
+      this.set('featherActive', false);
     }
   }
 });

<|original_code|>
import Ember from 'ember';

export default Ember.Component.extend({
  aviaryApiClient: null, // injected
  classNames: ['edit-icon'],
  image: null,
  url: null,
  imageSelector: null,
  closeOnSave: false,

  imageToEdit: function() {
    var imageNotSet = Ember.isEmpty(this.get('image'));
    var selector = this.get('imageSelector');
    return imageNotSet ? this.get('parentView').$(selector || 'img') : this.get('image');
  }.property('parentView', 'image', 'imageSelector'),

  click: function() {
    var self = this;
    var options = {
      image: this.get('imageToEdit'),
      url: this.get('url'),
      onSave: function(imageId, src) {
        self.send('onSave', imageId, src);
      }
    };

    this.aviaryApiClient.launch(options);

    return false;
  },

  actions: {
    onSave: function(imageId, src) {
      this.sendAction('onSave', imageId, src);

      var closeOnSave = this.get('closeOnSave');
      var featherEditor = this.aviaryApiClient.get('featherEditor');
      if(closeOnSave) { featherEditor.close(); }
    }
  }
});

<|edits_diff|>
--- addon/components/aviary-editor.js
+++ addon/components/aviary-editor.js
@@ -3,10 +3,12 @@
 export default Ember.Component.extend({
   aviaryApiClient: null, // injected
   classNames: ['edit-icon'],
+  classNameBindings: ['featherActive:active'],
   image: null,
   url: null,
   imageSelector: null,
   closeOnSave: false,
+  featherActive: false,

   imageToEdit: function() {
     var imageNotSet = Ember.isEmpty(this.get('image'));
@@ -21,10 +23,14 @@
       url: this.get('url'),
       onSave: function(imageId, src) {
         self.send('onSave', imageId, src);
+      },
+      onClose: function() {
+        self.send('onClose');
       }
     };

     this.aviaryApiClient.launch(options);
+    this.set('featherActive', true);

     return false;
   },
<|current_version|>
import Ember from 'ember';

export default Ember.Component.extend({
  aviaryApiClient: null, // injected
  classNames: ['edit-icon'],
  classNameBindings: ['featherActive:active'],
  image: null,
  url: null,
  imageSelector: null,
  closeOnSave: false,
  featherActive: false,

  imageToEdit: function() {
    var imageNotSet = Ember.isEmpty(this.get('image'));
    var selector = this.get('imageSelector');
    return imageNotSet ? this.get('parentView').$(selector || 'img') : this.get('image');
  }.property('parentView', 'image', 'imageSelector'),

  click: function() {
    var self = this;
    var options = {
      image: this.get('imageToEdit'),
      url: this.get('url'),
      onSave: function(imageId, src) {
        self.send('onSave', imageId, src);
      },
      onClose: function() {
        self.send('onClose');
      }
    };

    this.aviaryApiClient.launch(options);
    this.set('featherActive', true);

    return false;
  },

  actions: {
    onSave: function(imageId, src) {
      this.sendAction('onSave', imageId, src);

      var closeOnSave = this.get('closeOnSave');
      var featherEditor = this.aviaryApiClient.get('featherEditor');
      if(closeOnSave) { featherEditor.close(); }
    }
  }
});

<|next_version|>
import Ember from 'ember';

export default Ember.Component.extend({
  aviaryApiClient: null, // injected
  classNames: ['edit-icon'],
  classNameBindings: ['featherActive:active'],
  image: null,
  url: null,
  imageSelector: null,
  closeOnSave: false,
  featherActive: false,

  imageToEdit: function() {
    var imageNotSet = Ember.isEmpty(this.get('image'));
    var selector = this.get('imageSelector');
    return imageNotSet ? this.get('parentView').$(selector || 'img') : this.get('image');
  }.property('parentView', 'image', 'imageSelector'),

  click: function() {
    var self = this;
    var options = {
      image: this.get('imageToEdit'),
      url: this.get('url'),
      onSave: function(imageId, src) {
        self.send('onSave', imageId, src);
      },
      onClose: function() {
        self.send('onClose');
      }
    };

    this.aviaryApiClient.launch(options);
    this.set('featherActive', true);

    return false;
  },

  actions: {
    onSave: function(imageId, src) {
      this.sendAction('onSave', imageId, src);

      var closeOnSave = this.get('closeOnSave');
      var featherEditor = this.aviaryApiClient.get('featherEditor');
      if(closeOnSave) { featherEditor.close(); }
    },
    onClose: function() {
      this.set('featherActive', false);
    }
  }
});


Add new test domains for our test google suite instances

--- rules/SAML-test-mozilla-com-google.js
+++ rules/SAML-test-mozilla-com-google.js
@@ -7,7 +7,12 @@
   if (context.clientID !== 'q0tFB9QyFIKqPOOKvkFnHMj2VwrLjX46') return callback(null, user, context); // Google (test.mozilla.com)

   // This rule simply remaps @mozilla.com e-mail addresses to @test.mozilla.com to be used with the test.mozilla.com GSuite domain.
+  // Be careful when adding replacements not to do "double-replacements" where a replace replaces another rule. If that happens,
+  // you probably want to improve this code instead
   user.myemail = user.email.replace("mozilla.com", "test.mozilla.com");
+  user.myemail = user.email.replace("mozillafoundation.org", "test.mozillafoundation.org");
+  user.myemail = user.email.replace("getpocket.com", "test-gsuite.getpocket.com");
+
   context.samlConfiguration = context.samlConfiguration || {};

   context.samlConfiguration.mappings = {

<|original_code|>
function (user, context, callback) {
  if (!user) {
    // If the user is not presented (i.e. a rule deleted it), just go on, since authenticate will always fail.
    return callback(null, null, context);
  }

  if (context.clientID !== 'q0tFB9QyFIKqPOOKvkFnHMj2VwrLjX46') return callback(null, user, context); // Google (test.mozilla.com)

  // This rule simply remaps @mozilla.com e-mail addresses to @test.mozilla.com to be used with the test.mozilla.com GSuite domain.
  user.myemail = user.email.replace("mozilla.com", "test.mozilla.com");
  context.samlConfiguration = context.samlConfiguration || {};

  context.samlConfiguration.mappings = {
     "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier":     "myemail",
     "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress":       "myemail",
     "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/email":       "myemail",
  };
  context.samlConfiguration.nameIdentifierFormat = "urn:oasis:names:tc:SAML:2.0:nameid-format:email";

  callback(null, user, context);
}

<|edits_diff|>
--- rules/SAML-test-mozilla-com-google.js
+++ rules/SAML-test-mozilla-com-google.js
@@ -7,6 +7,8 @@
   if (context.clientID !== 'q0tFB9QyFIKqPOOKvkFnHMj2VwrLjX46') return callback(null, user, context); // Google (test.mozilla.com)

   // This rule simply remaps @mozilla.com e-mail addresses to @test.mozilla.com to be used with the test.mozilla.com GSuite domain.
+  // Be careful when adding replacements not to do "double-replacements" where a replace replaces another rule. If that happens,
+  // you probably want to improve this code instead
   user.myemail = user.email.replace("mozilla.com", "test.mozilla.com");
   context.samlConfiguration = context.samlConfiguration || {};

<|current_version|>
function (user, context, callback) {
  if (!user) {
    // If the user is not presented (i.e. a rule deleted it), just go on, since authenticate will always fail.
    return callback(null, null, context);
  }

  if (context.clientID !== 'q0tFB9QyFIKqPOOKvkFnHMj2VwrLjX46') return callback(null, user, context); // Google (test.mozilla.com)

  // This rule simply remaps @mozilla.com e-mail addresses to @test.mozilla.com to be used with the test.mozilla.com GSuite domain.
  // Be careful when adding replacements not to do "double-replacements" where a replace replaces another rule. If that happens,
  // you probably want to improve this code instead
  user.myemail = user.email.replace("mozilla.com", "test.mozilla.com");
  context.samlConfiguration = context.samlConfiguration || {};

  context.samlConfiguration.mappings = {
     "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier":     "myemail",
     "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress":       "myemail",
     "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/email":       "myemail",
  };
  context.samlConfiguration.nameIdentifierFormat = "urn:oasis:names:tc:SAML:2.0:nameid-format:email";

  callback(null, user, context);
}

<|next_version|>
function (user, context, callback) {
  if (!user) {
    // If the user is not presented (i.e. a rule deleted it), just go on, since authenticate will always fail.
    return callback(null, null, context);
  }

  if (context.clientID !== 'q0tFB9QyFIKqPOOKvkFnHMj2VwrLjX46') return callback(null, user, context); // Google (test.mozilla.com)

  // This rule simply remaps @mozilla.com e-mail addresses to @test.mozilla.com to be used with the test.mozilla.com GSuite domain.
  // Be careful when adding replacements not to do "double-replacements" where a replace replaces another rule. If that happens,
  // you probably want to improve this code instead
  user.myemail = user.email.replace("mozilla.com", "test.mozilla.com");
  user.myemail = user.email.replace("mozillafoundation.org", "test.mozillafoundation.org");
  user.myemail = user.email.replace("getpocket.com", "test-gsuite.getpocket.com");

  context.samlConfiguration = context.samlConfiguration || {};

  context.samlConfiguration.mappings = {
     "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier":     "myemail",
     "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress":       "myemail",
     "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/email":       "myemail",
  };
  context.samlConfiguration.nameIdentifierFormat = "urn:oasis:names:tc:SAML:2.0:nameid-format:email";

  callback(null, user, context);
}


Add the UCLA icon back

Add the UCLA icon back

--- client/src/components/footer/index.js
+++ client/src/components/footer/index.js
@@ -4,6 +4,7 @@
 import React, { Component } from 'react';

 import './index.css'
+import uclaLogo from './ucla.png';
 import kitwareLogo from './Kitware_Full_Logo.png';
 import strobeLogo from './strobe.png';
 import berkeleyLogo from './berkeley.jpg';
@@ -17,6 +18,11 @@
   render = () => {
     return (
       <BottomNavigation style={style}>
+        <BottomNavigationAction
+          icon={}
+          onClick={() => this.select(0)} href="http://www.ucla.edu/"
+          target="_blank"
+        />
         <BottomNavigationAction
           icon={}
           onClick={() => this.select(0)} href="http://www.kitware.com"

<|original_code|>
import BottomNavigation from '@material-ui/core/BottomNavigation';
import BottomNavigationAction from '@material-ui/core/BottomNavigationAction';

import React, { Component } from 'react';

import './index.css'
import kitwareLogo from './Kitware_Full_Logo.png';
import strobeLogo from './strobe.png';
import berkeleyLogo from './berkeley.jpg';

const style = {
  height: '5rem'
}

export default class Footer extends Component {
  render = () => {
    return (
      <BottomNavigation style={style}>
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://www.kitware.com"
          target="_blank"
        />
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://strobe.colorado.edu/"
          target="_blank"
        />
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://www.lbl.gov/"
          target="_blank"
        />
      </BottomNavigation>
    );
  }
}

<|edits_diff|>
--- client/src/components/footer/index.js
+++ client/src/components/footer/index.js
@@ -4,6 +4,7 @@
 import React, { Component } from 'react';

 import './index.css'
+import uclaLogo from './ucla.png';
 import kitwareLogo from './Kitware_Full_Logo.png';
 import strobeLogo from './strobe.png';
 import berkeleyLogo from './berkeley.jpg';
<|current_version|>
import BottomNavigation from '@material-ui/core/BottomNavigation';
import BottomNavigationAction from '@material-ui/core/BottomNavigationAction';

import React, { Component } from 'react';

import './index.css'
import uclaLogo from './ucla.png';
import kitwareLogo from './Kitware_Full_Logo.png';
import strobeLogo from './strobe.png';
import berkeleyLogo from './berkeley.jpg';

const style = {
  height: '5rem'
}

export default class Footer extends Component {
  render = () => {
    return (
      <BottomNavigation style={style}>
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://www.kitware.com"
          target="_blank"
        />
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://strobe.colorado.edu/"
          target="_blank"
        />
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://www.lbl.gov/"
          target="_blank"
        />
      </BottomNavigation>
    );
  }
}

<|next_version|>
import BottomNavigation from '@material-ui/core/BottomNavigation';
import BottomNavigationAction from '@material-ui/core/BottomNavigationAction';

import React, { Component } from 'react';

import './index.css'
import uclaLogo from './ucla.png';
import kitwareLogo from './Kitware_Full_Logo.png';
import strobeLogo from './strobe.png';
import berkeleyLogo from './berkeley.jpg';

const style = {
  height: '5rem'
}

export default class Footer extends Component {
  render = () => {
    return (
      <BottomNavigation style={style}>
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://www.ucla.edu/"
          target="_blank"
        />
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://www.kitware.com"
          target="_blank"
        />
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://strobe.colorado.edu/"
          target="_blank"
        />
        <BottomNavigationAction
          icon={}
          onClick={() => this.select(0)} href="http://www.lbl.gov/"
          target="_blank"
        />
      </BottomNavigation>
    );
  }
}