Hide the button after adding the page

Hide the button after adding the page

--- static/js/rango-ajax.js
+++ static/js/rango-ajax.js
@@ -20,7 +20,9 @@
   catid = $(this).attr('data-catid');
   title = $(this).attr('data-title');
   url = $(this).attr('data-url');
+  var $this = $(this);
   $.get('/rango/auto_add_page/', {category_id: catid, title: title, url: url}, function(data) {
     $('#pages').html(data);
+    $this.hide();
   });
 });

<|original_code|>
$('#likes').click(function() {
  var catid;
  catid = $(this).attr('data-catid');
  $.get('/rango/like_category/', {category_id: catid}, function(data) {
    $('#like_count').html(data);
    $('#likes').hide();
  });
});

$('#suggestion').keyup(function () {
  var query;
  query = $(this).val();
  $.get('/rango/suggest_category/', {suggestion: query}, function(data) {
    $('#cats').html(data);
  });
});

$('.rango-add').click(function() {
  var catid, title, url;
  catid = $(this).attr('data-catid');
  title = $(this).attr('data-title');
  url = $(this).attr('data-url');
  $.get('/rango/auto_add_page/', {category_id: catid, title: title, url: url}, function(data) {
    $('#pages').html(data);
  });
});
<|edits_diff|>
--- static/js/rango-ajax.js
+++ static/js/rango-ajax.js
@@ -20,6 +20,7 @@
   catid = $(this).attr('data-catid');
   title = $(this).attr('data-title');
   url = $(this).attr('data-url');
+  var $this = $(this);
   $.get('/rango/auto_add_page/', {category_id: catid, title: title, url: url}, function(data) {
     $('#pages').html(data);
   });
<|current_version|>
$('#likes').click(function() {
  var catid;
  catid = $(this).attr('data-catid');
  $.get('/rango/like_category/', {category_id: catid}, function(data) {
    $('#like_count').html(data);
    $('#likes').hide();
  });
});

$('#suggestion').keyup(function () {
  var query;
  query = $(this).val();
  $.get('/rango/suggest_category/', {suggestion: query}, function(data) {
    $('#cats').html(data);
  });
});

$('.rango-add').click(function() {
  var catid, title, url;
  catid = $(this).attr('data-catid');
  title = $(this).attr('data-title');
  url = $(this).attr('data-url');
  var $this = $(this);
  $.get('/rango/auto_add_page/', {category_id: catid, title: title, url: url}, function(data) {
    $('#pages').html(data);
  });
});
<|next_version|>
$('#likes').click(function() {
  var catid;
  catid = $(this).attr('data-catid');
  $.get('/rango/like_category/', {category_id: catid}, function(data) {
    $('#like_count').html(data);
    $('#likes').hide();
  });
});

$('#suggestion').keyup(function () {
  var query;
  query = $(this).val();
  $.get('/rango/suggest_category/', {suggestion: query}, function(data) {
    $('#cats').html(data);
  });
});

$('.rango-add').click(function() {
  var catid, title, url;
  catid = $(this).attr('data-catid');
  title = $(this).attr('data-title');
  url = $(this).attr('data-url');
  var $this = $(this);
  $.get('/rango/auto_add_page/', {category_id: catid, title: title, url: url}, function(data) {
    $('#pages').html(data);
    $this.hide();
  });
});


Read info.json files into master run list

--- generate_list.js
+++ generate_list.js
@@ -7,7 +7,13 @@
   job.run_id = run_id;
   job.tasks = fs.readdirSync('runs/'+run_id);
   var stat = fs.statSync('runs/'+run_id);
+  var info = {};
+  try {
+    var infoFile = fs.readFileSync('runs/'+run_id+'/info.json');
+    info = JSON.parse(infoFile);
+  } catch (e) {};
   job.date = stat.mtime;
+  job.info = info;
   jobs.push(job);
 });

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

var jobs = [];

fs.readdirSync('runs').forEach(function(run_id) {
  var job = {}
  job.run_id = run_id;
  job.tasks = fs.readdirSync('runs/'+run_id);
  var stat = fs.statSync('runs/'+run_id);
  job.date = stat.mtime;
  jobs.push(job);
});

fs.writeFile('list.json',JSON.stringify(jobs));

<|edits_diff|>
--- generate_list.js
+++ generate_list.js
@@ -7,6 +7,11 @@
   job.run_id = run_id;
   job.tasks = fs.readdirSync('runs/'+run_id);
   var stat = fs.statSync('runs/'+run_id);
+  var info = {};
+  try {
+    var infoFile = fs.readFileSync('runs/'+run_id+'/info.json');
+    info = JSON.parse(infoFile);
+  } catch (e) {};
   job.date = stat.mtime;
   jobs.push(job);
 });
<|current_version|>
var fs = require('fs');

var jobs = [];

fs.readdirSync('runs').forEach(function(run_id) {
  var job = {}
  job.run_id = run_id;
  job.tasks = fs.readdirSync('runs/'+run_id);
  var stat = fs.statSync('runs/'+run_id);
  var info = {};
  try {
    var infoFile = fs.readFileSync('runs/'+run_id+'/info.json');
    info = JSON.parse(infoFile);
  } catch (e) {};
  job.date = stat.mtime;
  jobs.push(job);
});

fs.writeFile('list.json',JSON.stringify(jobs));

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

var jobs = [];

fs.readdirSync('runs').forEach(function(run_id) {
  var job = {}
  job.run_id = run_id;
  job.tasks = fs.readdirSync('runs/'+run_id);
  var stat = fs.statSync('runs/'+run_id);
  var info = {};
  try {
    var infoFile = fs.readFileSync('runs/'+run_id+'/info.json');
    info = JSON.parse(infoFile);
  } catch (e) {};
  job.date = stat.mtime;
  job.info = info;
  jobs.push(job);
});

fs.writeFile('list.json',JSON.stringify(jobs));


Change the background, and default style

--- app/components/3dmol/3dmol.directive.js
+++ app/components/3dmol/3dmol.directive.js
@@ -12,11 +12,13 @@

                 // Create GLViewer within 'gldiv'
                 $scope.viewer = $3Dmol.createViewer($($element), config);
+                $scope.viewer.setBackgroundColor(0xeeeeee);
                 $scope.viewer.resize();

                 $http.get('/data/2POR.pdb')
                     .success(function(data) {
                         $scope.viewer.addModel(data, 'pdb');
+                        $scope.viewer.setStyle({}, {stick:{}});
                         $scope.viewer.zoomTo();
                         $scope.viewer.render();
                     })

<|original_code|>
require('script!3Dmol/release/3Dmol.js');
require('style/3dmol.styl');

angular.module('chemPhyWebApp')
    .directive('chemPhyWeb3dmol', ['$http', '$log', function($http, $log) {

        return {
            scope: {},
            link: function($scope, $element) {
                // Viewer config - properties 'defaultcolors' and 'callback'
                var config = {defaultcolors: $3Dmol.rasmolElementColors };

                // Create GLViewer within 'gldiv'
                $scope.viewer = $3Dmol.createViewer($($element), config);
                $scope.viewer.resize();

                $http.get('/data/2POR.pdb')
                    .success(function(data) {
                        $scope.viewer.addModel(data, 'pdb');
                        $scope.viewer.zoomTo();
                        $scope.viewer.render();
                    })
                    .error(function(data) {
                        $log.error(data);
                    });
            }
        };
    }]);
<|edits_diff|>
--- app/components/3dmol/3dmol.directive.js
+++ app/components/3dmol/3dmol.directive.js
@@ -12,6 +12,7 @@

                 // Create GLViewer within 'gldiv'
                 $scope.viewer = $3Dmol.createViewer($($element), config);
+                $scope.viewer.setBackgroundColor(0xeeeeee);
                 $scope.viewer.resize();

                 $http.get('/data/2POR.pdb')
<|current_version|>
require('script!3Dmol/release/3Dmol.js');
require('style/3dmol.styl');

angular.module('chemPhyWebApp')
    .directive('chemPhyWeb3dmol', ['$http', '$log', function($http, $log) {

        return {
            scope: {},
            link: function($scope, $element) {
                // Viewer config - properties 'defaultcolors' and 'callback'
                var config = {defaultcolors: $3Dmol.rasmolElementColors };

                // Create GLViewer within 'gldiv'
                $scope.viewer = $3Dmol.createViewer($($element), config);
                $scope.viewer.setBackgroundColor(0xeeeeee);
                $scope.viewer.resize();

                $http.get('/data/2POR.pdb')
                    .success(function(data) {
                        $scope.viewer.addModel(data, 'pdb');
                        $scope.viewer.zoomTo();
                        $scope.viewer.render();
                    })
                    .error(function(data) {
                        $log.error(data);
                    });
            }
        };
    }]);
<|next_version|>
require('script!3Dmol/release/3Dmol.js');
require('style/3dmol.styl');

angular.module('chemPhyWebApp')
    .directive('chemPhyWeb3dmol', ['$http', '$log', function($http, $log) {

        return {
            scope: {},
            link: function($scope, $element) {
                // Viewer config - properties 'defaultcolors' and 'callback'
                var config = {defaultcolors: $3Dmol.rasmolElementColors };

                // Create GLViewer within 'gldiv'
                $scope.viewer = $3Dmol.createViewer($($element), config);
                $scope.viewer.setBackgroundColor(0xeeeeee);
                $scope.viewer.resize();

                $http.get('/data/2POR.pdb')
                    .success(function(data) {
                        $scope.viewer.addModel(data, 'pdb');
                        $scope.viewer.setStyle({}, {stick:{}});
                        $scope.viewer.zoomTo();
                        $scope.viewer.render();
                    })
                    .error(function(data) {
                        $log.error(data);
                    });
            }
        };
    }]);


Add a height offset for macOS' dialogs

Add a height offset for macOS' dialogs

--- app/src/renderer/js/preferences.js
+++ app/src/renderer/js/preferences.js
@@ -10,7 +10,11 @@

   // Element definitions
   const chooseSaveDirectoryBtn = document.querySelector('.js-choose-save');
+  const header = document.querySelector('header');
+
   const electronWindow = getCurrentWindow();
+
+  electronWindow.setSheetOffset(header.offsetHeight);

   chooseSaveDirectoryBtn.onclick = function () {
     const location = dialog.showOpenDialog(electronWindow, {properties: ['openDirectory']});

<|original_code|>
import {remote} from 'electron';
import settings from 'electron-settings';

const {dialog, getCurrentWindow} = remote;

document.addEventListener('DOMContentLoaded', () => {
  settings.get('openOnStartup').then(val => {
    console.log(val);
  });

  // Element definitions
  const chooseSaveDirectoryBtn = document.querySelector('.js-choose-save');
  const electronWindow = getCurrentWindow();

  chooseSaveDirectoryBtn.onclick = function () {
    const location = dialog.showOpenDialog(electronWindow, {properties: ['openDirectory']});
    if (location) {
      settings.set('kapturesDir', location[0]);
    }
  };
});

<|edits_diff|>
--- app/src/renderer/js/preferences.js
+++ app/src/renderer/js/preferences.js
@@ -10,6 +10,8 @@

   // Element definitions
   const chooseSaveDirectoryBtn = document.querySelector('.js-choose-save');
+  const header = document.querySelector('header');
+
   const electronWindow = getCurrentWindow();

   chooseSaveDirectoryBtn.onclick = function () {
<|current_version|>
import {remote} from 'electron';
import settings from 'electron-settings';

const {dialog, getCurrentWindow} = remote;

document.addEventListener('DOMContentLoaded', () => {
  settings.get('openOnStartup').then(val => {
    console.log(val);
  });

  // Element definitions
  const chooseSaveDirectoryBtn = document.querySelector('.js-choose-save');
  const header = document.querySelector('header');

  const electronWindow = getCurrentWindow();

  chooseSaveDirectoryBtn.onclick = function () {
    const location = dialog.showOpenDialog(electronWindow, {properties: ['openDirectory']});
    if (location) {
      settings.set('kapturesDir', location[0]);
    }
  };
});

<|next_version|>
import {remote} from 'electron';
import settings from 'electron-settings';

const {dialog, getCurrentWindow} = remote;

document.addEventListener('DOMContentLoaded', () => {
  settings.get('openOnStartup').then(val => {
    console.log(val);
  });

  // Element definitions
  const chooseSaveDirectoryBtn = document.querySelector('.js-choose-save');
  const header = document.querySelector('header');

  const electronWindow = getCurrentWindow();

  electronWindow.setSheetOffset(header.offsetHeight);

  chooseSaveDirectoryBtn.onclick = function () {
    const location = dialog.showOpenDialog(electronWindow, {properties: ['openDirectory']});
    if (location) {
      settings.set('kapturesDir', location[0]);
    }
  };
});


Annotate build steps for clarity

Annotate build steps for clarity

--- gulpfile.js
+++ gulpfile.js
@@ -1,6 +1,7 @@
 const gulp = require('gulp');
 const babel = require('gulp-babel');

+// Copy all files except for *.js ones
 gulp.task('cp', function() {
   gulp.src([
       'app/**/!(*.js)',
@@ -10,11 +11,13 @@
     .pipe(gulp.dest('dist'));
 });

+// Copy the entire lib/ directory; it's vendor, 3rd party stuff
 gulp.task('cp-lib', function() {
   gulp.src('app/lib/**')
     .pipe(gulp.dest('dist/lib'));
 });

+// Compile app JS with babel
 gulp.task('js', function() {
   var presets = ['es2015', 'react'];

<|original_code|>
const gulp = require('gulp');
const babel = require('gulp-babel');

gulp.task('cp', function() {
  gulp.src([
      'app/**/!(*.js)',
      'MIT-LICENSE.txt',
      'README.md',
    ])
    .pipe(gulp.dest('dist'));
});

gulp.task('cp-lib', function() {
  gulp.src('app/lib/**')
    .pipe(gulp.dest('dist/lib'));
});

gulp.task('js', function() {
  var presets = ['es2015', 'react'];

  gulp.src('app/*.js')
    .pipe(babel({
      presets: presets,
    }))
    .pipe(gulp.dest('dist'));

  gulp.src('app/js/*.js')
    .pipe(babel({
      presets: presets,
    }))
    .pipe(gulp.dest('dist/js'));
});

gulp.task('default', ['cp', 'cp-lib', 'js']);

<|edits_diff|>
--- gulpfile.js
+++ gulpfile.js
@@ -1,6 +1,7 @@
 const gulp = require('gulp');
 const babel = require('gulp-babel');

+// Copy all files except for *.js ones
 gulp.task('cp', function() {
   gulp.src([
       'app/**/!(*.js)',
@@ -10,6 +11,7 @@
     .pipe(gulp.dest('dist'));
 });

+// Copy the entire lib/ directory; it's vendor, 3rd party stuff
 gulp.task('cp-lib', function() {
   gulp.src('app/lib/**')
     .pipe(gulp.dest('dist/lib'));
<|current_version|>
const gulp = require('gulp');
const babel = require('gulp-babel');

// Copy all files except for *.js ones
gulp.task('cp', function() {
  gulp.src([
      'app/**/!(*.js)',
      'MIT-LICENSE.txt',
      'README.md',
    ])
    .pipe(gulp.dest('dist'));
});

// Copy the entire lib/ directory; it's vendor, 3rd party stuff
gulp.task('cp-lib', function() {
  gulp.src('app/lib/**')
    .pipe(gulp.dest('dist/lib'));
});

gulp.task('js', function() {
  var presets = ['es2015', 'react'];

  gulp.src('app/*.js')
    .pipe(babel({
      presets: presets,
    }))
    .pipe(gulp.dest('dist'));

  gulp.src('app/js/*.js')
    .pipe(babel({
      presets: presets,
    }))
    .pipe(gulp.dest('dist/js'));
});

gulp.task('default', ['cp', 'cp-lib', 'js']);

<|next_version|>
const gulp = require('gulp');
const babel = require('gulp-babel');

// Copy all files except for *.js ones
gulp.task('cp', function() {
  gulp.src([
      'app/**/!(*.js)',
      'MIT-LICENSE.txt',
      'README.md',
    ])
    .pipe(gulp.dest('dist'));
});

// Copy the entire lib/ directory; it's vendor, 3rd party stuff
gulp.task('cp-lib', function() {
  gulp.src('app/lib/**')
    .pipe(gulp.dest('dist/lib'));
});

// Compile app JS with babel
gulp.task('js', function() {
  var presets = ['es2015', 'react'];

  gulp.src('app/*.js')
    .pipe(babel({
      presets: presets,
    }))
    .pipe(gulp.dest('dist'));

  gulp.src('app/js/*.js')
    .pipe(babel({
      presets: presets,
    }))
    .pipe(gulp.dest('dist/js'));
});

gulp.task('default', ['cp', 'cp-lib', 'js']);


Throw on non-int array lenghts

Throw on non-int array lenghts

--- src/stdlib/Array.js
+++ src/stdlib/Array.js
@@ -3,10 +3,15 @@
 const EasyObjectValue = require('../values/EasyObjectValue');
 const ObjectValue = require('../values/ObjectValue');
 const ArrayValue = require('../values/ArrayValue');
+const CompletionRecord = require('../CompletionRecord');

 class ArrayObject extends EasyObjectValue {
 	*call(thiz, args, s) {
 		if ( args.length === 1 && args[0].jsTypeName === 'number' ) {
+			let len = args[0].toNative();
+			if ( len != len>>0 ) {
+				return yield CompletionRecord.makeRangeError(this.realm, "Invalid array length");
+			}
 			let result = ArrayValue.make([], s.realm);
 			yield * result.set('length', args[0]);
 			return result;

<|original_code|>
'use strict';

const EasyObjectValue = require('../values/EasyObjectValue');
const ObjectValue = require('../values/ObjectValue');
const ArrayValue = require('../values/ArrayValue');

class ArrayObject extends EasyObjectValue {
	*call(thiz, args, s) {
		if ( args.length === 1 && args[0].jsTypeName === 'number' ) {
			let result = ArrayValue.make([], s.realm);
			yield * result.set('length', args[0]);
			return result;
		}
		return ArrayValue.make(args, s.realm);
	}

	callPrototype(realm) { return realm.ArrayPrototype; }
	constructorFor(realm) { return realm.ArrayPrototype; }
	//objPrototype(realm) { return realm.Function; }

	static *isArray(thiz, args) {
		if ( args.length < 1 ) return EasyObjectValue.false;
		return EasyObjectValue.fromNative(args[0] instanceof ArrayValue);
	}
}

module.exports = ArrayObject;

<|edits_diff|>
--- src/stdlib/Array.js
+++ src/stdlib/Array.js
@@ -3,6 +3,7 @@
 const EasyObjectValue = require('../values/EasyObjectValue');
 const ObjectValue = require('../values/ObjectValue');
 const ArrayValue = require('../values/ArrayValue');
+const CompletionRecord = require('../CompletionRecord');

 class ArrayObject extends EasyObjectValue {
 	*call(thiz, args, s) {
<|current_version|>
'use strict';

const EasyObjectValue = require('../values/EasyObjectValue');
const ObjectValue = require('../values/ObjectValue');
const ArrayValue = require('../values/ArrayValue');
const CompletionRecord = require('../CompletionRecord');

class ArrayObject extends EasyObjectValue {
	*call(thiz, args, s) {
		if ( args.length === 1 && args[0].jsTypeName === 'number' ) {
			let result = ArrayValue.make([], s.realm);
			yield * result.set('length', args[0]);
			return result;
		}
		return ArrayValue.make(args, s.realm);
	}

	callPrototype(realm) { return realm.ArrayPrototype; }
	constructorFor(realm) { return realm.ArrayPrototype; }
	//objPrototype(realm) { return realm.Function; }

	static *isArray(thiz, args) {
		if ( args.length < 1 ) return EasyObjectValue.false;
		return EasyObjectValue.fromNative(args[0] instanceof ArrayValue);
	}
}

module.exports = ArrayObject;

<|next_version|>
'use strict';

const EasyObjectValue = require('../values/EasyObjectValue');
const ObjectValue = require('../values/ObjectValue');
const ArrayValue = require('../values/ArrayValue');
const CompletionRecord = require('../CompletionRecord');

class ArrayObject extends EasyObjectValue {
	*call(thiz, args, s) {
		if ( args.length === 1 && args[0].jsTypeName === 'number' ) {
			let len = args[0].toNative();
			if ( len != len>>0 ) {
				return yield CompletionRecord.makeRangeError(this.realm, "Invalid array length");
			}
			let result = ArrayValue.make([], s.realm);
			yield * result.set('length', args[0]);
			return result;
		}
		return ArrayValue.make(args, s.realm);
	}

	callPrototype(realm) { return realm.ArrayPrototype; }
	constructorFor(realm) { return realm.ArrayPrototype; }
	//objPrototype(realm) { return realm.Function; }

	static *isArray(thiz, args) {
		if ( args.length < 1 ) return EasyObjectValue.false;
		return EasyObjectValue.fromNative(args[0] instanceof ArrayValue);
	}
}

module.exports = ArrayObject;


Make gulp-mocha comatible with watch

Make gulp-mocha comatible with watch

Since mocha uses `require` to load tests - we need either run mocha in
spawned process or clear requires cache.

--- index.js
+++ index.js
@@ -1,11 +1,13 @@
 'use strict';
 var es = require('event-stream');
 var Mocha = require('mocha');
+var path = require('path');

 module.exports = function (options) {
 	var mocha = new Mocha(options);

 	return es.through(function (file) {
+		delete require.cache[require.resolve(path.resolve(file.path))];
 		mocha.addFile(file.path);
 		this.emit('data', file);
 	}, function () {

<|original_code|>
'use strict';
var es = require('event-stream');
var Mocha = require('mocha');

module.exports = function (options) {
	var mocha = new Mocha(options);

	return es.through(function (file) {
		mocha.addFile(file.path);
		this.emit('data', file);
	}, function () {
		mocha.run(function (errCount) {
			if (errCount > 0) {
				return this.emit('error', new Error('gulp-mocha: ' + errCount + ' ' + (errCount === 1 ? 'test' : 'tests') + ' failed.'));
			}

			this.emit('end');
		}.bind(this));
	});
};

<|edits_diff|>
--- index.js
+++ index.js
@@ -1,6 +1,7 @@
 'use strict';
 var es = require('event-stream');
 var Mocha = require('mocha');
+var path = require('path');

 module.exports = function (options) {
 	var mocha = new Mocha(options);
<|current_version|>
'use strict';
var es = require('event-stream');
var Mocha = require('mocha');
var path = require('path');

module.exports = function (options) {
	var mocha = new Mocha(options);

	return es.through(function (file) {
		mocha.addFile(file.path);
		this.emit('data', file);
	}, function () {
		mocha.run(function (errCount) {
			if (errCount > 0) {
				return this.emit('error', new Error('gulp-mocha: ' + errCount + ' ' + (errCount === 1 ? 'test' : 'tests') + ' failed.'));
			}

			this.emit('end');
		}.bind(this));
	});
};

<|next_version|>
'use strict';
var es = require('event-stream');
var Mocha = require('mocha');
var path = require('path');

module.exports = function (options) {
	var mocha = new Mocha(options);

	return es.through(function (file) {
		delete require.cache[require.resolve(path.resolve(file.path))];
		mocha.addFile(file.path);
		this.emit('data', file);
	}, function () {
		mocha.run(function (errCount) {
			if (errCount > 0) {
				return this.emit('error', new Error('gulp-mocha: ' + errCount + ' ' + (errCount === 1 ? 'test' : 'tests') + ' failed.'));
			}

			this.emit('end');
		}.bind(this));
	});
};


Add comments to tutorial code

Add comments to tutorial code

--- tutorials/basicChart/basicChart.js
+++ tutorials/basicChart/basicChart.js
@@ -1,11 +1,19 @@
 window.onload = function() {
+  // These scales are Plottable wrappers for a d3.scale object.
+  // Like D3 scales, they manage a mapping from data to visual properties; pixel positions in this case
+  // Unlike D3 scales, they automatically set their domain and range, and have event handling to update dependent components on changes
   var xScale = new Plottable.LinearScale();
   var yScale = new Plottable.LinearScale();

+  // The Axes and LineRenderer are all Components, meaning they take up visual space and are placed by the layout engine
   var xAxis = new Plottable.XAxis(xScale, "bottom");
   var yAxis = new Plottable.YAxis(yScale, "left");
   var renderer = new Plottable.LineRenderer(xyData, xScale, yScale);

+
+  // Now we'll make a Table to organize the layout of the components. The first row will have a yAxis and renderer; the second will
+  // only have the xAxis, and it will be aligned to the column of the renderer.
+  // The yAxis is fixed-width and the xAxis is fixed-height, so the renderer will naturally expand to take up all free space
   var chart = new Plottable.Table([
                     [yAxis, renderer],
                     [null,  xAxis   ]

<|original_code|>
window.onload = function() {
  var xScale = new Plottable.LinearScale();
  var yScale = new Plottable.LinearScale();

  var xAxis = new Plottable.XAxis(xScale, "bottom");
  var yAxis = new Plottable.YAxis(yScale, "left");
  var renderer = new Plottable.LineRenderer(xyData, xScale, yScale);

  var chart = new Plottable.Table([
                    [yAxis, renderer],
                    [null,  xAxis   ]
                  ]);

  chart.renderTo("#chart");
}

<|edits_diff|>
--- tutorials/basicChart/basicChart.js
+++ tutorials/basicChart/basicChart.js
@@ -1,7 +1,11 @@
 window.onload = function() {
+  // These scales are Plottable wrappers for a d3.scale object.
+  // Like D3 scales, they manage a mapping from data to visual properties; pixel positions in this case
+  // Unlike D3 scales, they automatically set their domain and range, and have event handling to update dependent components on changes
   var xScale = new Plottable.LinearScale();
   var yScale = new Plottable.LinearScale();

+  // The Axes and LineRenderer are all Components, meaning they take up visual space and are placed by the layout engine
   var xAxis = new Plottable.XAxis(xScale, "bottom");
   var yAxis = new Plottable.YAxis(yScale, "left");
   var renderer = new Plottable.LineRenderer(xyData, xScale, yScale);
<|current_version|>
window.onload = function() {
  // These scales are Plottable wrappers for a d3.scale object.
  // Like D3 scales, they manage a mapping from data to visual properties; pixel positions in this case
  // Unlike D3 scales, they automatically set their domain and range, and have event handling to update dependent components on changes
  var xScale = new Plottable.LinearScale();
  var yScale = new Plottable.LinearScale();

  // The Axes and LineRenderer are all Components, meaning they take up visual space and are placed by the layout engine
  var xAxis = new Plottable.XAxis(xScale, "bottom");
  var yAxis = new Plottable.YAxis(yScale, "left");
  var renderer = new Plottable.LineRenderer(xyData, xScale, yScale);

  var chart = new Plottable.Table([
                    [yAxis, renderer],
                    [null,  xAxis   ]
                  ]);

  chart.renderTo("#chart");
}

<|next_version|>
window.onload = function() {
  // These scales are Plottable wrappers for a d3.scale object.
  // Like D3 scales, they manage a mapping from data to visual properties; pixel positions in this case
  // Unlike D3 scales, they automatically set their domain and range, and have event handling to update dependent components on changes
  var xScale = new Plottable.LinearScale();
  var yScale = new Plottable.LinearScale();

  // The Axes and LineRenderer are all Components, meaning they take up visual space and are placed by the layout engine
  var xAxis = new Plottable.XAxis(xScale, "bottom");
  var yAxis = new Plottable.YAxis(yScale, "left");
  var renderer = new Plottable.LineRenderer(xyData, xScale, yScale);

  // Now we'll make a Table to organize the layout of the components. The first row will have a yAxis and renderer; the second will
  // only have the xAxis, and it will be aligned to the column of the renderer.
  // The yAxis is fixed-width and the xAxis is fixed-height, so the renderer will naturally expand to take up all free space
  var chart = new Plottable.Table([
                    [yAxis, renderer],
                    [null,  xAxis   ]
                  ]);

  chart.renderTo("#chart");
}


Add notification toggle to admin page

Add notification toggle to admin page

--- client/src/components/Admin/Home.js
+++ client/src/components/Admin/Home.js
@@ -3,6 +3,7 @@
 import { IssueContainer } from './Issue';
 import Alternatives from './Alternatives';
 import { ConcludedIssueListContainer } from '../ConcludedIssueList';
+import NotificationToggle from '../NotificationToggle';
 import IssueStatus from '../IssueStatus';
 import css from '../../css/Home.css';

@@ -17,6 +18,7 @@
         </div>
         <IssueStatus />
       </div>
+      <NotificationToggle />
       <ConcludedIssueListContainer />
     </div>
   </DocumentTitle>

<|original_code|>
import React from 'react';
import DocumentTitle from 'react-document-title';
import { IssueContainer } from './Issue';
import Alternatives from './Alternatives';
import { ConcludedIssueListContainer } from '../ConcludedIssueList';
import IssueStatus from '../IssueStatus';
import css from '../../css/Home.css';

const AdminHome = () => (
  <DocumentTitle title="Generalforsamling adminpanel">

          <IssueContainer />
          <Alternatives disabled />

        <IssueStatus />

      <ConcludedIssueListContainer />

  </DocumentTitle>
);

export default AdminHome;

<|edits_diff|>
--- client/src/components/Admin/Home.js
+++ client/src/components/Admin/Home.js
@@ -3,6 +3,7 @@
 import { IssueContainer } from './Issue';
 import Alternatives from './Alternatives';
 import { ConcludedIssueListContainer } from '../ConcludedIssueList';
+import NotificationToggle from '../NotificationToggle';
 import IssueStatus from '../IssueStatus';
 import css from '../../css/Home.css';

<|current_version|>
import React from 'react';
import DocumentTitle from 'react-document-title';
import { IssueContainer } from './Issue';
import Alternatives from './Alternatives';
import { ConcludedIssueListContainer } from '../ConcludedIssueList';
import NotificationToggle from '../NotificationToggle';
import IssueStatus from '../IssueStatus';
import css from '../../css/Home.css';

const AdminHome = () => (
  <DocumentTitle title="Generalforsamling adminpanel">

          <IssueContainer />
          <Alternatives disabled />

        <IssueStatus />

      <ConcludedIssueListContainer />

  </DocumentTitle>
);

export default AdminHome;

<|next_version|>
import React from 'react';
import DocumentTitle from 'react-document-title';
import { IssueContainer } from './Issue';
import Alternatives from './Alternatives';
import { ConcludedIssueListContainer } from '../ConcludedIssueList';
import NotificationToggle from '../NotificationToggle';
import IssueStatus from '../IssueStatus';
import css from '../../css/Home.css';

const AdminHome = () => (
  <DocumentTitle title="Generalforsamling adminpanel">

          <IssueContainer />
          <Alternatives disabled />

        <IssueStatus />

      <NotificationToggle />
      <ConcludedIssueListContainer />

  </DocumentTitle>
);

export default AdminHome;


import React from 'react';
import { connect } from 'react-redux';

import { doControl } from '../../actions/app-actions';

class NodeDetailsControlButton extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    let className = `node-control-button fa ${this.props.control.icon}`;
    if (this.props.pending) {
      className += ' node-control-button-pending';
    }
    return (
      <span className={className} title={this.props.control.human} onClick={this.handleClick} />
    );
  }

  handleClick(ev) {
    ev.preventDefault();
    this.props.dispatch(doControl(this.props.nodeId, this.props.control));
  }
}

// Using this instead of PureComponent because of props.dispatch
export default connect()(NodeDetailsControlButton);

import React from 'react';
import { connect } from 'react-redux';

import { trackMixpanelEvent } from '../../utils/tracking-utils';
import { doControl } from '../../actions/app-actions';

class NodeDetailsControlButton extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    let className = `node-control-button fa ${this.props.control.icon}`;
    if (this.props.pending) {
      className += ' node-control-button-pending';
    }
    return (
      <span className={className} title={this.props.control.human} onClick={this.handleClick} />
    );
  }

  handleClick(ev) {
    ev.preventDefault();
    const { id, human } = this.props.control;
    trackMixpanelEvent('scope.node.control.click', { id, title: human });
    this.props.dispatch(doControl(this.props.nodeId, this.props.control));
  }
}

// Using this instead of PureComponent because of props.dispatch
export default connect()(NodeDetailsControlButton);

Add Mixpanel event on Control clicks

tracking: Add Mixpanel event on Control clicks

We'd like to gain some insight on how controls are used.

--- client/app/scripts/components/node-details/node-details-control-button.js
+++ client/app/scripts/components/node-details/node-details-control-button.js
@@ -1,6 +1,7 @@
 import React from 'react';
 import { connect } from 'react-redux';

+import { trackMixpanelEvent } from '../../utils/tracking-utils';
 import { doControl } from '../../actions/app-actions';

 class NodeDetailsControlButton extends React.Component {
@@ -21,6 +22,8 @@

   handleClick(ev) {
     ev.preventDefault();
+    const { id, human } = this.props.control;
+    trackMixpanelEvent('scope.node.control.click', { id, title: human });
     this.props.dispatch(doControl(this.props.nodeId, this.props.control));
   }
 }

import React from 'react';
import { connect } from 'react-redux';

import { trackMixpanelEvent } from '../../utils/tracking-utils';
import { doControl } from '../../actions/app-actions';

class NodeDetailsControlButton extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    let className = `node-control-button fa ${this.props.control.icon}`;
    if (this.props.pending) {
      className += ' node-control-button-pending';
    }
    return (
      <span className={className} title={this.props.control.human} onClick={this.handleClick} />
    );
  }

  handleClick(ev) {
    ev.preventDefault();
    this.props.dispatch(doControl(this.props.nodeId, this.props.control));
  }
}

// Using this instead of PureComponent because of props.dispatch
export default connect()(NodeDetailsControlButton);

<|original_code|>
import React from 'react';
import { connect } from 'react-redux';

import { doControl } from '../../actions/app-actions';

class NodeDetailsControlButton extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    let className = `node-control-button fa ${this.props.control.icon}`;
    if (this.props.pending) {
      className += ' node-control-button-pending';
    }
    return (

    );
  }

  handleClick(ev) {
    ev.preventDefault();
    this.props.dispatch(doControl(this.props.nodeId, this.props.control));
  }
}

// Using this instead of PureComponent because of props.dispatch
export default connect()(NodeDetailsControlButton);

<|edits_diff|>
--- client/app/scripts/components/node-details/node-details-control-button.js
+++ client/app/scripts/components/node-details/node-details-control-button.js
@@ -1,6 +1,7 @@
 import React from 'react';
 import { connect } from 'react-redux';

+import { trackMixpanelEvent } from '../../utils/tracking-utils';
 import { doControl } from '../../actions/app-actions';

 class NodeDetailsControlButton extends React.Component {
<|current_version|>
import React from 'react';
import { connect } from 'react-redux';

import { trackMixpanelEvent } from '../../utils/tracking-utils';
import { doControl } from '../../actions/app-actions';

class NodeDetailsControlButton extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    let className = `node-control-button fa ${this.props.control.icon}`;
    if (this.props.pending) {
      className += ' node-control-button-pending';
    }
    return (

    );
  }

  handleClick(ev) {
    ev.preventDefault();
    this.props.dispatch(doControl(this.props.nodeId, this.props.control));
  }
}

// Using this instead of PureComponent because of props.dispatch
export default connect()(NodeDetailsControlButton);

<|next_version|>
import React from 'react';
import { connect } from 'react-redux';

import { trackMixpanelEvent } from '../../utils/tracking-utils';
import { doControl } from '../../actions/app-actions';

class NodeDetailsControlButton extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    let className = `node-control-button fa ${this.props.control.icon}`;
    if (this.props.pending) {
      className += ' node-control-button-pending';
    }
    return (

    );
  }

  handleClick(ev) {
    ev.preventDefault();
    const { id, human } = this.props.control;
    trackMixpanelEvent('scope.node.control.click', { id, title: human });
    this.props.dispatch(doControl(this.props.nodeId, this.props.control));
  }
}

// Using this instead of PureComponent because of props.dispatch
export default connect()(NodeDetailsControlButton);


Add middleware for react-router-redux navigation

--- src/store/configureStore.dev.js
+++ src/store/configureStore.dev.js
@@ -6,6 +6,8 @@
 import reduxImmutableStateInvariant from 'redux-immutable-state-invariant';
 import thunkMiddleware from 'redux-thunk';
 import rootReducer from '../reducers';
+import {routerMiddleware} from 'react-router-redux';
+import {browserHistory} from 'react-router';

 export default function configureStore(initialState) {
   const middewares = [
@@ -17,6 +19,7 @@
     // thunk middleware can also accept an extra argument to be passed to each thunk action
     // https://github.com/gaearon/redux-thunk#injecting-a-custom-argument
     thunkMiddleware,
+    routerMiddleware(browserHistory)
   ];

   const store = createStore(rootReducer, initialState, compose(

<|original_code|>
// This file merely configures the store for hot reloading.
// This boilerplate file is likely to be the same for each project that uses Redux.
// With Redux, the actual stores are in /reducers.

import {createStore, compose, applyMiddleware} from 'redux';
import reduxImmutableStateInvariant from 'redux-immutable-state-invariant';
import thunkMiddleware from 'redux-thunk';
import rootReducer from '../reducers';

export default function configureStore(initialState) {
  const middewares = [
    // Add other middleware on this line...

    // Redux middleware that spits an error on you when you try to mutate your state either inside a dispatch or between dispatches.
    reduxImmutableStateInvariant(),

    // thunk middleware can also accept an extra argument to be passed to each thunk action
    // https://github.com/gaearon/redux-thunk#injecting-a-custom-argument
    thunkMiddleware,
  ];

  const store = createStore(rootReducer, initialState, compose(
    applyMiddleware(...middewares),
    window.devToolsExtension ? window.devToolsExtension() : f => f // add support for Redux dev tools
    )
  );

  if (module.hot) {
    // Enable Webpack hot module replacement for reducers
    module.hot.accept('../reducers', () => {
      const nextReducer = require('../reducers').default; // eslint-disable-line global-require
      store.replaceReducer(nextReducer);
    });
  }

  return store;
}

<|edits_diff|>
--- src/store/configureStore.dev.js
+++ src/store/configureStore.dev.js
@@ -6,6 +6,8 @@
 import reduxImmutableStateInvariant from 'redux-immutable-state-invariant';
 import thunkMiddleware from 'redux-thunk';
 import rootReducer from '../reducers';
+import {routerMiddleware} from 'react-router-redux';
+import {browserHistory} from 'react-router';

 export default function configureStore(initialState) {
   const middewares = [
<|current_version|>
// This file merely configures the store for hot reloading.
// This boilerplate file is likely to be the same for each project that uses Redux.
// With Redux, the actual stores are in /reducers.

import {createStore, compose, applyMiddleware} from 'redux';
import reduxImmutableStateInvariant from 'redux-immutable-state-invariant';
import thunkMiddleware from 'redux-thunk';
import rootReducer from '../reducers';
import {routerMiddleware} from 'react-router-redux';
import {browserHistory} from 'react-router';

export default function configureStore(initialState) {
  const middewares = [
    // Add other middleware on this line...

    // Redux middleware that spits an error on you when you try to mutate your state either inside a dispatch or between dispatches.
    reduxImmutableStateInvariant(),

    // thunk middleware can also accept an extra argument to be passed to each thunk action
    // https://github.com/gaearon/redux-thunk#injecting-a-custom-argument
    thunkMiddleware,
  ];

  const store = createStore(rootReducer, initialState, compose(
    applyMiddleware(...middewares),
    window.devToolsExtension ? window.devToolsExtension() : f => f // add support for Redux dev tools
    )
  );

  if (module.hot) {
    // Enable Webpack hot module replacement for reducers
    module.hot.accept('../reducers', () => {
      const nextReducer = require('../reducers').default; // eslint-disable-line global-require
      store.replaceReducer(nextReducer);
    });
  }

  return store;
}

<|next_version|>
// This file merely configures the store for hot reloading.
// This boilerplate file is likely to be the same for each project that uses Redux.
// With Redux, the actual stores are in /reducers.

import {createStore, compose, applyMiddleware} from 'redux';
import reduxImmutableStateInvariant from 'redux-immutable-state-invariant';
import thunkMiddleware from 'redux-thunk';
import rootReducer from '../reducers';
import {routerMiddleware} from 'react-router-redux';
import {browserHistory} from 'react-router';

export default function configureStore(initialState) {
  const middewares = [
    // Add other middleware on this line...

    // Redux middleware that spits an error on you when you try to mutate your state either inside a dispatch or between dispatches.
    reduxImmutableStateInvariant(),

    // thunk middleware can also accept an extra argument to be passed to each thunk action
    // https://github.com/gaearon/redux-thunk#injecting-a-custom-argument
    thunkMiddleware,
    routerMiddleware(browserHistory)
  ];

  const store = createStore(rootReducer, initialState, compose(
    applyMiddleware(...middewares),
    window.devToolsExtension ? window.devToolsExtension() : f => f // add support for Redux dev tools
    )
  );

  if (module.hot) {
    // Enable Webpack hot module replacement for reducers
    module.hot.accept('../reducers', () => {
      const nextReducer = require('../reducers').default; // eslint-disable-line global-require
      store.replaceReducer(nextReducer);
    });
  }

  return store;
}


Throw Error if subclass doesn't call super

--- src/Annotation/index.js
+++ src/Annotation/index.js
@@ -1,7 +1,12 @@
 class Annotation {
     constructor(objectOf) {
+        if (objectOf == undefined) {
+            throw new Error("Call to super(String annotationName) is required ");
+        }
+
         this.value = null;
         this.objectOf = objectOf;
+
     }

     value() { return this.value; }

<|original_code|>
class Annotation {
    constructor(objectOf) {
        this.value = null;
        this.objectOf = objectOf;
    }

    value() { return this.value; }
    objectOf() { return this.objectOf; }

    set(name, value) {
        if (name !== "value" && typeof this[name] !== "function") {
            let err = new Error("Invalid Argument "+name);

            throw err;
        } else {
            this[name] = value;
        }
    }
}

module.exports = Annotation;
<|edits_diff|>
--- src/Annotation/index.js
+++ src/Annotation/index.js
@@ -1,5 +1,9 @@
 class Annotation {
     constructor(objectOf) {
+        if (objectOf == undefined) {
+            throw new Error("Call to super(String annotationName) is required ");
+        }
+
         this.value = null;
         this.objectOf = objectOf;
     }
<|current_version|>
class Annotation {
    constructor(objectOf) {
        if (objectOf == undefined) {
            throw new Error("Call to super(String annotationName) is required ");
        }

        this.value = null;
        this.objectOf = objectOf;
    }

    value() { return this.value; }
    objectOf() { return this.objectOf; }

    set(name, value) {
        if (name !== "value" && typeof this[name] !== "function") {
            let err = new Error("Invalid Argument "+name);

            throw err;
        } else {
            this[name] = value;
        }
    }
}

module.exports = Annotation;
<|next_version|>
class Annotation {
    constructor(objectOf) {
        if (objectOf == undefined) {
            throw new Error("Call to super(String annotationName) is required ");
        }

        this.value = null;
        this.objectOf = objectOf;

    }

    value() { return this.value; }
    objectOf() { return this.objectOf; }

    set(name, value) {
        if (name !== "value" && typeof this[name] !== "function") {
            let err = new Error("Invalid Argument "+name);

            throw err;
        } else {
            this[name] = value;
        }
    }
}

module.exports = Annotation;


Add validation to user input

Add validation to user input

--- src/api.js
+++ src/api.js
@@ -11,6 +11,11 @@

 // CREATE
 export function addStory(storyBody) {
+  if (typeof storyBody !== 'string') {
+    throw new TypeError(
+      'story body must be a string',
+    );
+  }
   return db.stories.insert({
     body: storyBody,
     votes: 0,
@@ -34,6 +39,11 @@
   });
 };
 export function getStory(id) {
+  if (typeof id !== 'string') {
+    throw new TypeError(
+      'story id must be a string',
+    );
+  }
   if (id in cacheById) return cacheById[id];
   return cacheById[id] = db.stories.findOne({_id: new ObjectId(id)});
 };
@@ -43,6 +53,11 @@

 // UPDATE
 export function voteStory(id) {
+  if (typeof id !== 'string') {
+    throw new TypeError(
+      'story id must be a string',
+    );
+  }
   return db.stories.update(
     {_id: new ObjectId(id)},
     {$inc: {votes: 1}},

<|original_code|>
import mongo from 'then-mongo';

const db = mongo(
  process.env.DATABASE,
  ['stories'],
);
const ObjectId = db.ObjectId;

let storiesCache = _getStories();
let cacheById = {};

// CREATE
export function addStory(storyBody) {
  return db.stories.insert({
    body: storyBody,
    votes: 0,
  }).then(result => {
    storiesCache = _getStories();
    cacheById = {};
    return result;
  });
};

function _getStories() {
  return db.stories.find().then(stories => {
    return stories.sort((a, b) => b.votes - a.votes);
  });
}

// READ
export function getStoryIds() {
  return storiesCache.then(stories => {
    return stories.map(s => s._id);
  });
};
export function getStory(id) {
  if (id in cacheById) return cacheById[id];
  return cacheById[id] = db.stories.findOne({_id: new ObjectId(id)});
};
export function getStories() {
  return storiesCache;
}

// UPDATE
export function voteStory(id) {
  return db.stories.update(
    {_id: new ObjectId(id)},
    {$inc: {votes: 1}},
  ).then(result => {
    storiesCache = _getStories();
    cacheById = {};
    return result;
  });
};

<|edits_diff|>
--- src/api.js
+++ src/api.js
@@ -11,6 +11,11 @@

 // CREATE
 export function addStory(storyBody) {
+  if (typeof storyBody !== 'string') {
+    throw new TypeError(
+      'story body must be a string',
+    );
+  }
   return db.stories.insert({
     body: storyBody,
     votes: 0,
@@ -34,6 +39,11 @@
   });
 };
 export function getStory(id) {
+  if (typeof id !== 'string') {
+    throw new TypeError(
+      'story id must be a string',
+    );
+  }
   if (id in cacheById) return cacheById[id];
   return cacheById[id] = db.stories.findOne({_id: new ObjectId(id)});
 };
<|current_version|>
import mongo from 'then-mongo';

const db = mongo(
  process.env.DATABASE,
  ['stories'],
);
const ObjectId = db.ObjectId;

let storiesCache = _getStories();
let cacheById = {};

// CREATE
export function addStory(storyBody) {
  if (typeof storyBody !== 'string') {
    throw new TypeError(
      'story body must be a string',
    );
  }
  return db.stories.insert({
    body: storyBody,
    votes: 0,
  }).then(result => {
    storiesCache = _getStories();
    cacheById = {};
    return result;
  });
};

function _getStories() {
  return db.stories.find().then(stories => {
    return stories.sort((a, b) => b.votes - a.votes);
  });
}

// READ
export function getStoryIds() {
  return storiesCache.then(stories => {
    return stories.map(s => s._id);
  });
};
export function getStory(id) {
  if (typeof id !== 'string') {
    throw new TypeError(
      'story id must be a string',
    );
  }
  if (id in cacheById) return cacheById[id];
  return cacheById[id] = db.stories.findOne({_id: new ObjectId(id)});
};
export function getStories() {
  return storiesCache;
}

// UPDATE
export function voteStory(id) {
  return db.stories.update(
    {_id: new ObjectId(id)},
    {$inc: {votes: 1}},
  ).then(result => {
    storiesCache = _getStories();
    cacheById = {};
    return result;
  });
};

<|next_version|>
import mongo from 'then-mongo';

const db = mongo(
  process.env.DATABASE,
  ['stories'],
);
const ObjectId = db.ObjectId;

let storiesCache = _getStories();
let cacheById = {};

// CREATE
export function addStory(storyBody) {
  if (typeof storyBody !== 'string') {
    throw new TypeError(
      'story body must be a string',
    );
  }
  return db.stories.insert({
    body: storyBody,
    votes: 0,
  }).then(result => {
    storiesCache = _getStories();
    cacheById = {};
    return result;
  });
};

function _getStories() {
  return db.stories.find().then(stories => {
    return stories.sort((a, b) => b.votes - a.votes);
  });
}

// READ
export function getStoryIds() {
  return storiesCache.then(stories => {
    return stories.map(s => s._id);
  });
};
export function getStory(id) {
  if (typeof id !== 'string') {
    throw new TypeError(
      'story id must be a string',
    );
  }
  if (id in cacheById) return cacheById[id];
  return cacheById[id] = db.stories.findOne({_id: new ObjectId(id)});
};
export function getStories() {
  return storiesCache;
}

// UPDATE
export function voteStory(id) {
  if (typeof id !== 'string') {
    throw new TypeError(
      'story id must be a string',
    );
  }
  return db.stories.update(
    {_id: new ObjectId(id)},
    {$inc: {votes: 1}},
  ).then(result => {
    storiesCache = _getStories();
    cacheById = {};
    return result;
  });
};


Add comments to explain checking for userAgent

--- lib/index.js
+++ lib/index.js
@@ -5,8 +5,10 @@
 'use strict';

 if (typeof Realm != 'undefined') {
+    // The global Realm constructor should be available on device (using JavaScriptCore).
     module.exports = Realm;  // eslint-disable-line no-undef
 } else if (navigator.userAgent) {
+    // The userAgent will be defined when running in a browser (such as Chrome debugging mode).
     module.exports = require('./realm');
 } else {
     throw new Error('Missing Realm constructor - please ensure RealmReact framework is included!');

<|original_code|>
/* Copyright 2015 Realm Inc - All Rights Reserved
 * Proprietary and Confidential
 */

'use strict';

if (typeof Realm != 'undefined') {
    module.exports = Realm;  // eslint-disable-line no-undef
} else if (navigator.userAgent) {
    module.exports = require('./realm');
} else {
    throw new Error('Missing Realm constructor - please ensure RealmReact framework is included!');
}

<|edits_diff|>
--- lib/index.js
+++ lib/index.js
@@ -5,6 +5,7 @@
 'use strict';

 if (typeof Realm != 'undefined') {
+    // The global Realm constructor should be available on device (using JavaScriptCore).
     module.exports = Realm;  // eslint-disable-line no-undef
 } else if (navigator.userAgent) {
     module.exports = require('./realm');
<|current_version|>
/* Copyright 2015 Realm Inc - All Rights Reserved
 * Proprietary and Confidential
 */

'use strict';

if (typeof Realm != 'undefined') {
    // The global Realm constructor should be available on device (using JavaScriptCore).
    module.exports = Realm;  // eslint-disable-line no-undef
} else if (navigator.userAgent) {
    module.exports = require('./realm');
} else {
    throw new Error('Missing Realm constructor - please ensure RealmReact framework is included!');
}

<|next_version|>
/* Copyright 2015 Realm Inc - All Rights Reserved
 * Proprietary and Confidential
 */

'use strict';

if (typeof Realm != 'undefined') {
    // The global Realm constructor should be available on device (using JavaScriptCore).
    module.exports = Realm;  // eslint-disable-line no-undef
} else if (navigator.userAgent) {
    // The userAgent will be defined when running in a browser (such as Chrome debugging mode).
    module.exports = require('./realm');
} else {
    throw new Error('Missing Realm constructor - please ensure RealmReact framework is included!');
}


Add js to load and close modal window

Add js to load and close modal window

--- src/main/webapp/scripts/index.js
+++ src/main/webapp/scripts/index.js
@@ -1,4 +1,8 @@
+const MODAL_PROMISE = loadTemplate('/templates/modalWindow.html');
+
 $().ready(function() {
+  loadModalWindow();
+
   const location = getCookie('location');
   if (location != "") {
     $('#location-checkbox').prop('checked', true);
@@ -11,4 +15,13 @@
       eraseCookie('location');
     }
   });
+
+  $('body').on('click', '#pop-up > .close', function() {
+    $('#modal-window').addClass('hide');
+  });
 });
+
+async function loadModalWindow() {
+  const modalHTML = await MODAL_PROMISE;
+  $('#real-body').append(modalHTML);
+}

<|original_code|>
$().ready(function() {
  const location = getCookie('location');
  if (location != "") {
    $('#location-checkbox').prop('checked', true);
  }

  $('#location-checkbox').change(function() {
    if(this.checked) {
      setCookie('location', 'true', 1);
    } else {
      eraseCookie('location');
    }
  });
});

<|edits_diff|>
--- src/main/webapp/scripts/index.js
+++ src/main/webapp/scripts/index.js
@@ -1,4 +1,8 @@
+const MODAL_PROMISE = loadTemplate('/templates/modalWindow.html');
+
 $().ready(function() {
+  loadModalWindow();
+
   const location = getCookie('location');
   if (location != "") {
     $('#location-checkbox').prop('checked', true);
@@ -11,4 +15,8 @@
       eraseCookie('location');
     }
   });
+
+  $('body').on('click', '#pop-up > .close', function() {
+    $('#modal-window').addClass('hide');
+  });
 });
<|current_version|>
const MODAL_PROMISE = loadTemplate('/templates/modalWindow.html');

$().ready(function() {
  loadModalWindow();

  const location = getCookie('location');
  if (location != "") {
    $('#location-checkbox').prop('checked', true);
  }

  $('#location-checkbox').change(function() {
    if(this.checked) {
      setCookie('location', 'true', 1);
    } else {
      eraseCookie('location');
    }
  });

  $('body').on('click', '#pop-up > .close', function() {
    $('#modal-window').addClass('hide');
  });
});

<|next_version|>
const MODAL_PROMISE = loadTemplate('/templates/modalWindow.html');

$().ready(function() {
  loadModalWindow();

  const location = getCookie('location');
  if (location != "") {
    $('#location-checkbox').prop('checked', true);
  }

  $('#location-checkbox').change(function() {
    if(this.checked) {
      setCookie('location', 'true', 1);
    } else {
      eraseCookie('location');
    }
  });

  $('body').on('click', '#pop-up > .close', function() {
    $('#modal-window').addClass('hide');
  });
});

async function loadModalWindow() {
  const modalHTML = await MODAL_PROMISE;
  $('#real-body').append(modalHTML);
}


Add DocsContainer as story decorator

Official-storybook: Add DocsContainer as story decorator

--- examples/official-storybook/stories/addon-docs/mdx.stories.js
+++ examples/official-storybook/stories/addon-docs/mdx.stories.js
@@ -1,8 +1,10 @@
 import React from 'react';
+import { DocsContainer } from '@storybook/addon-docs/blocks';
 import markdown from './markdown.stories.mdx';

 export default {
   title: 'Addons|Docs/mdx-in-story',
+  decorators: [storyFn => <DocsContainer context={{}}>{storyFn()}</DocsContainer>],
 };

 // This renders the contents of the docs panel into story content

<|original_code|>
import React from 'react';
import markdown from './markdown.stories.mdx';

export default {
  title: 'Addons|Docs/mdx-in-story',
};

// This renders the contents of the docs panel into story content
export const typography = () => {
  const Docs = markdown.parameters.docs;
  return <Docs />;
};

<|edits_diff|>
--- examples/official-storybook/stories/addon-docs/mdx.stories.js
+++ examples/official-storybook/stories/addon-docs/mdx.stories.js
@@ -1,4 +1,5 @@
 import React from 'react';
+import { DocsContainer } from '@storybook/addon-docs/blocks';
 import markdown from './markdown.stories.mdx';

 export default {
<|current_version|>
import React from 'react';
import { DocsContainer } from '@storybook/addon-docs/blocks';
import markdown from './markdown.stories.mdx';

export default {
  title: 'Addons|Docs/mdx-in-story',
};

// This renders the contents of the docs panel into story content
export const typography = () => {
  const Docs = markdown.parameters.docs;
  return <Docs />;
};

<|next_version|>
import React from 'react';
import { DocsContainer } from '@storybook/addon-docs/blocks';
import markdown from './markdown.stories.mdx';

export default {
  title: 'Addons|Docs/mdx-in-story',
  decorators: [storyFn => <DocsContainer context={{}}>{storyFn()}</DocsContainer>],
};

// This renders the contents of the docs panel into story content
export const typography = () => {
  const Docs = markdown.parameters.docs;
  return <Docs />;
};


Add analytics for bottom toolbar events

Add analytics for bottom toolbar events

--- modules/UI/toolbars/BottomToolbar.js
+++ modules/UI/toolbars/BottomToolbar.js
@@ -1,14 +1,18 @@
 /* global $ */
 var PanelToggler = require("../side_pannels/SidePanelToggler");
+var AnalyticsAdapter = require("../../statistics/AnalyticsAdapter");

 var buttonHandlers = {
     "bottom_toolbar_contact_list": function () {
+        AnalyticsAdapter.sendEvent('bottomtoolbar.contacts.toggled');
         BottomToolbar.toggleContactList();
     },
     "bottom_toolbar_film_strip": function () {
+        AnalyticsAdapter.sendEvent('bottomtoolbar.filmstrip.toggled');
         BottomToolbar.toggleFilmStrip();
     },
     "bottom_toolbar_chat": function () {
+        AnalyticsAdapter.sendEvent('bottomtoolbar.chat.toggled');
         BottomToolbar.toggleChat();
     }
 };

<|original_code|>
/* global $ */
var PanelToggler = require("../side_pannels/SidePanelToggler");

var buttonHandlers = {
    "bottom_toolbar_contact_list": function () {
        BottomToolbar.toggleContactList();
    },
    "bottom_toolbar_film_strip": function () {
        BottomToolbar.toggleFilmStrip();
    },
    "bottom_toolbar_chat": function () {
        BottomToolbar.toggleChat();
    }
};

var BottomToolbar = (function (my) {
    my.init = function () {
        for(var k in buttonHandlers)
            $("#" + k).click(buttonHandlers[k]);
    };

    my.toggleChat = function() {
        PanelToggler.toggleChat();
    };

    my.toggleContactList = function() {
        PanelToggler.toggleContactList();
    };

    my.toggleFilmStrip = function() {
        var filmstrip = $("#remoteVideos");
        filmstrip.toggleClass("hidden");
    };

    $(document).bind("remotevideo.resized", function (event, width, height) {
        var bottom = (height - $('#bottomToolbar').outerHeight())/2 + 18;

        $('#bottomToolbar').css({bottom: bottom + 'px'});
    });

    return my;
}(BottomToolbar || {}));

module.exports = BottomToolbar;

<|edits_diff|>
--- modules/UI/toolbars/BottomToolbar.js
+++ modules/UI/toolbars/BottomToolbar.js
@@ -1,11 +1,14 @@
 /* global $ */
 var PanelToggler = require("../side_pannels/SidePanelToggler");
+var AnalyticsAdapter = require("../../statistics/AnalyticsAdapter");

 var buttonHandlers = {
     "bottom_toolbar_contact_list": function () {
+        AnalyticsAdapter.sendEvent('bottomtoolbar.contacts.toggled');
         BottomToolbar.toggleContactList();
     },
     "bottom_toolbar_film_strip": function () {
+        AnalyticsAdapter.sendEvent('bottomtoolbar.filmstrip.toggled');
         BottomToolbar.toggleFilmStrip();
     },
     "bottom_toolbar_chat": function () {
<|current_version|>
/* global $ */
var PanelToggler = require("../side_pannels/SidePanelToggler");
var AnalyticsAdapter = require("../../statistics/AnalyticsAdapter");

var buttonHandlers = {
    "bottom_toolbar_contact_list": function () {
        AnalyticsAdapter.sendEvent('bottomtoolbar.contacts.toggled');
        BottomToolbar.toggleContactList();
    },
    "bottom_toolbar_film_strip": function () {
        AnalyticsAdapter.sendEvent('bottomtoolbar.filmstrip.toggled');
        BottomToolbar.toggleFilmStrip();
    },
    "bottom_toolbar_chat": function () {
        BottomToolbar.toggleChat();
    }
};

var BottomToolbar = (function (my) {
    my.init = function () {
        for(var k in buttonHandlers)
            $("#" + k).click(buttonHandlers[k]);
    };

    my.toggleChat = function() {
        PanelToggler.toggleChat();
    };

    my.toggleContactList = function() {
        PanelToggler.toggleContactList();
    };

    my.toggleFilmStrip = function() {
        var filmstrip = $("#remoteVideos");
        filmstrip.toggleClass("hidden");
    };

    $(document).bind("remotevideo.resized", function (event, width, height) {
        var bottom = (height - $('#bottomToolbar').outerHeight())/2 + 18;

        $('#bottomToolbar').css({bottom: bottom + 'px'});
    });

    return my;
}(BottomToolbar || {}));

module.exports = BottomToolbar;

<|next_version|>
/* global $ */
var PanelToggler = require("../side_pannels/SidePanelToggler");
var AnalyticsAdapter = require("../../statistics/AnalyticsAdapter");

var buttonHandlers = {
    "bottom_toolbar_contact_list": function () {
        AnalyticsAdapter.sendEvent('bottomtoolbar.contacts.toggled');
        BottomToolbar.toggleContactList();
    },
    "bottom_toolbar_film_strip": function () {
        AnalyticsAdapter.sendEvent('bottomtoolbar.filmstrip.toggled');
        BottomToolbar.toggleFilmStrip();
    },
    "bottom_toolbar_chat": function () {
        AnalyticsAdapter.sendEvent('bottomtoolbar.chat.toggled');
        BottomToolbar.toggleChat();
    }
};

var BottomToolbar = (function (my) {
    my.init = function () {
        for(var k in buttonHandlers)
            $("#" + k).click(buttonHandlers[k]);
    };

    my.toggleChat = function() {
        PanelToggler.toggleChat();
    };

    my.toggleContactList = function() {
        PanelToggler.toggleContactList();
    };

    my.toggleFilmStrip = function() {
        var filmstrip = $("#remoteVideos");
        filmstrip.toggleClass("hidden");
    };

    $(document).bind("remotevideo.resized", function (event, width, height) {
        var bottom = (height - $('#bottomToolbar').outerHeight())/2 + 18;

        $('#bottomToolbar').css({bottom: bottom + 'px'});
    });

    return my;
}(BottomToolbar || {}));

module.exports = BottomToolbar;


Add fail conditions to initial ajax requests

--- app/assets/javascripts/getAuth.js
+++ app/assets/javascripts/getAuth.js
@@ -4,6 +4,8 @@
     type: "get",
   }).done(function(signin_response) {
     $(".modal-1-inner").append(signin_response);
+  }).fail(function(error) {
+    console.log(error);
   });

   $.ajax({
@@ -11,6 +13,8 @@
     type: "get"
   }).done(function(signup_response) {
     $(".modal-2-inner").append(signup_response);
+  }).fail(function(error) {
+    console.log(error);
   });

   $(".sign-in-link").click(function(event) {

<|original_code|>
function getAuth() {
  $.ajax({
    url: "/users/sign_in",
    type: "get",
  }).done(function(signin_response) {
    $(".modal-1-inner").append(signin_response);
  });

  $.ajax({
    url: "/users/sign_up",
    type: "get"
  }).done(function(signup_response) {
    $(".modal-2-inner").append(signup_response);
  });

  $(".sign-in-link").click(function(event) {
    event.preventDefault();
    $(".modal-1-trigger").click();
  });

  $(".sign-up-link").click(function(event) {
    event.preventDefault();
    $(".modal-2-trigger").click();
  });
}

<|edits_diff|>
--- app/assets/javascripts/getAuth.js
+++ app/assets/javascripts/getAuth.js
@@ -4,6 +4,8 @@
     type: "get",
   }).done(function(signin_response) {
     $(".modal-1-inner").append(signin_response);
+  }).fail(function(error) {
+    console.log(error);
   });

   $.ajax({
<|current_version|>
function getAuth() {
  $.ajax({
    url: "/users/sign_in",
    type: "get",
  }).done(function(signin_response) {
    $(".modal-1-inner").append(signin_response);
  }).fail(function(error) {
    console.log(error);
  });

  $.ajax({
    url: "/users/sign_up",
    type: "get"
  }).done(function(signup_response) {
    $(".modal-2-inner").append(signup_response);
  });

  $(".sign-in-link").click(function(event) {
    event.preventDefault();
    $(".modal-1-trigger").click();
  });

  $(".sign-up-link").click(function(event) {
    event.preventDefault();
    $(".modal-2-trigger").click();
  });
}

<|next_version|>
function getAuth() {
  $.ajax({
    url: "/users/sign_in",
    type: "get",
  }).done(function(signin_response) {
    $(".modal-1-inner").append(signin_response);
  }).fail(function(error) {
    console.log(error);
  });

  $.ajax({
    url: "/users/sign_up",
    type: "get"
  }).done(function(signup_response) {
    $(".modal-2-inner").append(signup_response);
  }).fail(function(error) {
    console.log(error);
  });

  $(".sign-in-link").click(function(event) {
    event.preventDefault();
    $(".modal-1-trigger").click();
  });

  $(".sign-up-link").click(function(event) {
    event.preventDefault();
    $(".modal-2-trigger").click();
  });
}


Improve tests on the post render.

Improve tests on the post render.

--- lib/helpers/post_rendering_helper.js
+++ lib/helpers/post_rendering_helper.js
@@ -10,7 +10,12 @@
   };
   //Options must have a selector property and a helperName one.
   var callHelper = function( config) {
+    //If there is nothing selected.
     if(config.selector === undefined || config.selector.size() === 0){
+      return;
+    }
+    //If the function  desn not exist on the selection.
+    if(config.selector[postRenderingHelpers[config.helperName].fn] === undefined){
       return;
     }
     config.selector[postRenderingHelpers[config.helperName].fn](postRenderingHelpers[config.helperName].options);

<|original_code|>
/*global window*/
(function(NS) {
  NS = NS || {};
  var isInBrowser = typeof module === 'undefined' && typeof window !== 'undefined';
  //Container for all the post renderings functions.
  var postRenderingHelpers = {};
  //Register a helper inside the application.
  var registerHelper = function registerHelper(helper) {
    postRenderingHelpers[helper.name] = {fn: helper.fn, options: helper.options};
  };
  //Options must have a selector property and a helperName one.
  var callHelper = function( config) {
    if(config.selector === undefined || config.selector.size() === 0){
      return;
    }
    config.selector[postRenderingHelpers[config.helperName].fn](postRenderingHelpers[config.helperName].options);
    //config.selector[config.helperName](options);
  };
  var mdl = {
    registerHelper: registerHelper,
    callHelper: callHelper
  };
    // Differenciating export for node or browser.
  if (isInBrowser) {
    NS.Helpers = NS.Helpers || {};
    NS.Helpers.postRenderingHelper = mdl;
  } else {
    module.exports = mdl;
  }

})(typeof module === 'undefined' && typeof window !== 'undefined' ? window.Fmk : module.exports);
<|edits_diff|>
--- lib/helpers/post_rendering_helper.js
+++ lib/helpers/post_rendering_helper.js
@@ -10,6 +10,7 @@
   };
   //Options must have a selector property and a helperName one.
   var callHelper = function( config) {
+    //If there is nothing selected.
     if(config.selector === undefined || config.selector.size() === 0){
       return;
     }
<|current_version|>
/*global window*/
(function(NS) {
  NS = NS || {};
  var isInBrowser = typeof module === 'undefined' && typeof window !== 'undefined';
  //Container for all the post renderings functions.
  var postRenderingHelpers = {};
  //Register a helper inside the application.
  var registerHelper = function registerHelper(helper) {
    postRenderingHelpers[helper.name] = {fn: helper.fn, options: helper.options};
  };
  //Options must have a selector property and a helperName one.
  var callHelper = function( config) {
    //If there is nothing selected.
    if(config.selector === undefined || config.selector.size() === 0){
      return;
    }
    config.selector[postRenderingHelpers[config.helperName].fn](postRenderingHelpers[config.helperName].options);
    //config.selector[config.helperName](options);
  };
  var mdl = {
    registerHelper: registerHelper,
    callHelper: callHelper
  };
    // Differenciating export for node or browser.
  if (isInBrowser) {
    NS.Helpers = NS.Helpers || {};
    NS.Helpers.postRenderingHelper = mdl;
  } else {
    module.exports = mdl;
  }

})(typeof module === 'undefined' && typeof window !== 'undefined' ? window.Fmk : module.exports);
<|next_version|>
/*global window*/
(function(NS) {
  NS = NS || {};
  var isInBrowser = typeof module === 'undefined' && typeof window !== 'undefined';
  //Container for all the post renderings functions.
  var postRenderingHelpers = {};
  //Register a helper inside the application.
  var registerHelper = function registerHelper(helper) {
    postRenderingHelpers[helper.name] = {fn: helper.fn, options: helper.options};
  };
  //Options must have a selector property and a helperName one.
  var callHelper = function( config) {
    //If there is nothing selected.
    if(config.selector === undefined || config.selector.size() === 0){
      return;
    }
    //If the function  desn not exist on the selection.
    if(config.selector[postRenderingHelpers[config.helperName].fn] === undefined){
      return;
    }
    config.selector[postRenderingHelpers[config.helperName].fn](postRenderingHelpers[config.helperName].options);
    //config.selector[config.helperName](options);
  };
  var mdl = {
    registerHelper: registerHelper,
    callHelper: callHelper
  };
    // Differenciating export for node or browser.
  if (isInBrowser) {
    NS.Helpers = NS.Helpers || {};
    NS.Helpers.postRenderingHelper = mdl;
  } else {
    module.exports = mdl;
  }

})(typeof module === 'undefined' && typeof window !== 'undefined' ? window.Fmk : module.exports);


Add function for calculating time range in months

--- src/js/utils.js
+++ src/js/utils.js
@@ -1,3 +1,5 @@
+
+var moment = require('moment');

 function daysInMonth(month,year) {
   return new Date(year, month + 1, 0).getDate();
@@ -7,5 +9,10 @@
   return kmh / 111;
 }

+function timeRangeInMonths(timeRange) {
+  return Math.round(moment(timeRange[1]*1000).diff(moment(timeRange[0]*1000), 'months', true));
+}
+
 module.exports.daysInMonth = daysInMonth;
 module.exports.kmhToDegsPerH = kmhToDegsPerH;
+module.exports.timeRangeInMonths = timeRangeInMonths;

<|original_code|>

function daysInMonth(month,year) {
  return new Date(year, month + 1, 0).getDate();
}

function kmhToDegsPerH(kmh) {
  return kmh / 111;
}

module.exports.daysInMonth = daysInMonth;
module.exports.kmhToDegsPerH = kmhToDegsPerH;

<|edits_diff|>
--- src/js/utils.js
+++ src/js/utils.js
@@ -1,3 +1,5 @@
+
+var moment = require('moment');

 function daysInMonth(month,year) {
   return new Date(year, month + 1, 0).getDate();
@@ -7,5 +9,9 @@
   return kmh / 111;
 }

+function timeRangeInMonths(timeRange) {
+  return Math.round(moment(timeRange[1]*1000).diff(moment(timeRange[0]*1000), 'months', true));
+}
+
 module.exports.daysInMonth = daysInMonth;
 module.exports.kmhToDegsPerH = kmhToDegsPerH;
<|current_version|>

var moment = require('moment');

function daysInMonth(month,year) {
  return new Date(year, month + 1, 0).getDate();
}

function kmhToDegsPerH(kmh) {
  return kmh / 111;
}

function timeRangeInMonths(timeRange) {
  return Math.round(moment(timeRange[1]*1000).diff(moment(timeRange[0]*1000), 'months', true));
}

module.exports.daysInMonth = daysInMonth;
module.exports.kmhToDegsPerH = kmhToDegsPerH;

<|next_version|>

var moment = require('moment');

function daysInMonth(month,year) {
  return new Date(year, month + 1, 0).getDate();
}

function kmhToDegsPerH(kmh) {
  return kmh / 111;
}

function timeRangeInMonths(timeRange) {
  return Math.round(moment(timeRange[1]*1000).diff(moment(timeRange[0]*1000), 'months', true));
}

module.exports.daysInMonth = daysInMonth;
module.exports.kmhToDegsPerH = kmhToDegsPerH;
module.exports.timeRangeInMonths = timeRangeInMonths;


Add heading structure to tracking script for clarity

--- assets/javascripts/event_tracking.js
+++ assets/javascripts/event_tracking.js
@@ -1,6 +1,7 @@
 $(document).ready(function() {
   // check if the Google Analytics function is available
   if (typeof ga == 'function') {
+    // Following
     function trackFollow() {
       ga('send', 'event', 'Following', 'click Follow button');
     };
@@ -15,6 +16,7 @@
     $('.unsubscribe__action').click(function(e) { trackUnsubscribe() });
     $('.unsubscribe-request-action').click(function(e) { trackUnsubscribe() });

+    // Request for personal information question
     $("#request_personal_switch_yes").click(function(e) {
       ga('send', 'event', 'Request for personal information question', 'click Yes');
     });

<|original_code|>
$(document).ready(function() {
  // check if the Google Analytics function is available
  if (typeof ga == 'function') {
    function trackFollow() {
      ga('send', 'event', 'Following', 'click Follow button');
    };

    function trackUnsubscribe() {
      ga('send', 'event', 'Following', 'click Unsubscribe button');
    };

    $('.track-request-action').click(function(e) { trackFollow(); });
    $('.track__action').click(function(e) { trackFollow(); });

    $('.unsubscribe__action').click(function(e) { trackUnsubscribe() });
    $('.unsubscribe-request-action').click(function(e) { trackUnsubscribe() });

    $("#request_personal_switch_yes").click(function(e) {
      ga('send', 'event', 'Request for personal information question', 'click Yes');
    });

    $("#request_personal_switch_no").click(function(e) {
      ga('send', 'event', 'Request for personal information question', 'click No');
    });

    $(".request_personal_message .request_email a").click(function(e) {
      ga('send', 'event', 'Request for personal information question', 'click Authority email address');
    });
  }
})

<|edits_diff|>
--- assets/javascripts/event_tracking.js
+++ assets/javascripts/event_tracking.js
@@ -1,6 +1,7 @@
 $(document).ready(function() {
   // check if the Google Analytics function is available
   if (typeof ga == 'function') {
+    // Following
     function trackFollow() {
       ga('send', 'event', 'Following', 'click Follow button');
     };
<|current_version|>
$(document).ready(function() {
  // check if the Google Analytics function is available
  if (typeof ga == 'function') {
    // Following
    function trackFollow() {
      ga('send', 'event', 'Following', 'click Follow button');
    };

    function trackUnsubscribe() {
      ga('send', 'event', 'Following', 'click Unsubscribe button');
    };

    $('.track-request-action').click(function(e) { trackFollow(); });
    $('.track__action').click(function(e) { trackFollow(); });

    $('.unsubscribe__action').click(function(e) { trackUnsubscribe() });
    $('.unsubscribe-request-action').click(function(e) { trackUnsubscribe() });

    $("#request_personal_switch_yes").click(function(e) {
      ga('send', 'event', 'Request for personal information question', 'click Yes');
    });

    $("#request_personal_switch_no").click(function(e) {
      ga('send', 'event', 'Request for personal information question', 'click No');
    });

    $(".request_personal_message .request_email a").click(function(e) {
      ga('send', 'event', 'Request for personal information question', 'click Authority email address');
    });
  }
})

<|next_version|>
$(document).ready(function() {
  // check if the Google Analytics function is available
  if (typeof ga == 'function') {
    // Following
    function trackFollow() {
      ga('send', 'event', 'Following', 'click Follow button');
    };

    function trackUnsubscribe() {
      ga('send', 'event', 'Following', 'click Unsubscribe button');
    };

    $('.track-request-action').click(function(e) { trackFollow(); });
    $('.track__action').click(function(e) { trackFollow(); });

    $('.unsubscribe__action').click(function(e) { trackUnsubscribe() });
    $('.unsubscribe-request-action').click(function(e) { trackUnsubscribe() });

    // Request for personal information question
    $("#request_personal_switch_yes").click(function(e) {
      ga('send', 'event', 'Request for personal information question', 'click Yes');
    });

    $("#request_personal_switch_no").click(function(e) {
      ga('send', 'event', 'Request for personal information question', 'click No');
    });

    $(".request_personal_message .request_email a").click(function(e) {
      ga('send', 'event', 'Request for personal information question', 'click Authority email address');
    });
  }
})


Allow user to remove sample people

Allow user to remove sample people

--- growsocial/common/methods/searchMethods.js
+++ growsocial/common/methods/searchMethods.js
@@ -33,6 +33,7 @@
     var addedList = [];
     for (var i = 0; i < 3; i++) {
       var person = {
+        searchSamplePerson: true,
         'member_key': Random.id(),
         firstname: Random.choice(first_names),
         lastname: Random.choice(last_names),
@@ -48,4 +49,8 @@
     // console.log('method, addedList:', addedList);
     return addedList; // passed through client callback
   },
+
+  removeSearchSamplePeople: function() {
+    People.remove({searchSamplePerson: true});
+  },
 });

<|original_code|>
Meteor.methods({

  addSearchSamplePeople: function() {

    var first_names = [
      "Ada",
      "Grace",
      "Marie",
      "Carl",
      "Nikola",
      "Claude",
      "Peter",
      "Stefan",
      "Stephen",
      "Lisa",
      "Christian",
      "Barack"
    ];
    var last_names = [
      "Lovelace",
      "Hopper",
      "Curie",
      "Tesla",
      "Shannon",
      "Muller",
      "Meier",
      "Miller",
      "Gaga",
      "Franklin"
    ];
    var locations = ["Davie", "Fort Lauderdale", "Tampa"];

    var addedList = [];
    for (var i = 0; i < 3; i++) {
      var person = {
        'member_key': Random.id(),
        firstname: Random.choice(first_names),
        lastname: Random.choice(last_names),
        about: "Random test user created for testing search.",
        location: Random.choice(locations),
      };
      person.email = person.member_key + "@test.t";
      person.fullname = person.firstname + ' ' + person.lastname;
      addedList.push(person.fullname);
      // console.log('for search, adding sample person:', person);
      People.insert(person);
    }
    // console.log('method, addedList:', addedList);
    return addedList; // passed through client callback
  },
});

<|edits_diff|>
--- growsocial/common/methods/searchMethods.js
+++ growsocial/common/methods/searchMethods.js
@@ -33,6 +33,7 @@
     var addedList = [];
     for (var i = 0; i < 3; i++) {
       var person = {
+        searchSamplePerson: true,
         'member_key': Random.id(),
         firstname: Random.choice(first_names),
         lastname: Random.choice(last_names),
<|current_version|>
Meteor.methods({

  addSearchSamplePeople: function() {

    var first_names = [
      "Ada",
      "Grace",
      "Marie",
      "Carl",
      "Nikola",
      "Claude",
      "Peter",
      "Stefan",
      "Stephen",
      "Lisa",
      "Christian",
      "Barack"
    ];
    var last_names = [
      "Lovelace",
      "Hopper",
      "Curie",
      "Tesla",
      "Shannon",
      "Muller",
      "Meier",
      "Miller",
      "Gaga",
      "Franklin"
    ];
    var locations = ["Davie", "Fort Lauderdale", "Tampa"];

    var addedList = [];
    for (var i = 0; i < 3; i++) {
      var person = {
        searchSamplePerson: true,
        'member_key': Random.id(),
        firstname: Random.choice(first_names),
        lastname: Random.choice(last_names),
        about: "Random test user created for testing search.",
        location: Random.choice(locations),
      };
      person.email = person.member_key + "@test.t";
      person.fullname = person.firstname + ' ' + person.lastname;
      addedList.push(person.fullname);
      // console.log('for search, adding sample person:', person);
      People.insert(person);
    }
    // console.log('method, addedList:', addedList);
    return addedList; // passed through client callback
  },
});

<|next_version|>
Meteor.methods({

  addSearchSamplePeople: function() {

    var first_names = [
      "Ada",
      "Grace",
      "Marie",
      "Carl",
      "Nikola",
      "Claude",
      "Peter",
      "Stefan",
      "Stephen",
      "Lisa",
      "Christian",
      "Barack"
    ];
    var last_names = [
      "Lovelace",
      "Hopper",
      "Curie",
      "Tesla",
      "Shannon",
      "Muller",
      "Meier",
      "Miller",
      "Gaga",
      "Franklin"
    ];
    var locations = ["Davie", "Fort Lauderdale", "Tampa"];

    var addedList = [];
    for (var i = 0; i < 3; i++) {
      var person = {
        searchSamplePerson: true,
        'member_key': Random.id(),
        firstname: Random.choice(first_names),
        lastname: Random.choice(last_names),
        about: "Random test user created for testing search.",
        location: Random.choice(locations),
      };
      person.email = person.member_key + "@test.t";
      person.fullname = person.firstname + ' ' + person.lastname;
      addedList.push(person.fullname);
      // console.log('for search, adding sample person:', person);
      People.insert(person);
    }
    // console.log('method, addedList:', addedList);
    return addedList; // passed through client callback
  },

  removeSearchSamplePeople: function() {
    People.remove({searchSamplePerson: true});
  },
});


Add edit icon for password fields

Add edit icon for password fields

--- src/Form/FieldPassword.js
+++ src/Form/FieldPassword.js
@@ -3,6 +3,7 @@
 /*eslint-enable no-unused-vars */

 import FieldInput from "./FieldInput";
+import IconEdit from "./icons/IconEdit";

 const DEFAULT_PASSWORD_TEXT = "••••••";

@@ -52,6 +53,7 @@
         className={this.props.readClass}
         onClick={attributes.onFocus}>
         {attributes.defaultPasswordValue || DEFAULT_PASSWORD_TEXT}
+        <IconEdit />
       </span>
     );
   }

<|original_code|>
/*eslint-disable no-unused-vars */
import React from "react";
/*eslint-enable no-unused-vars */

import FieldInput from "./FieldInput";

const DEFAULT_PASSWORD_TEXT = "••••••";

const METHODS_TO_BIND = ["handleOnFocus"];

export default class FieldPassword extends FieldInput {
  constructor() {
    super();

    METHODS_TO_BIND.forEach((method) => {
      this[method] = this[method].bind(this);
    });
  }

  handleOnFocus(event) {
    if (!this.focused) {
      this.focused = true;
      this.forceUpdate();
    }

    this.props.handleEvent("focus", this.props.name, event);
  }

  getInputElement(attributes) {
    attributes = this.bindEvents(attributes, this.props.handleEvent);
    attributes.onFocus = this.handleOnFocus;

    let startValue = DEFAULT_PASSWORD_TEXT;
    if (this.focused) {
      startValue = attributes.startValue;
    }

    if (this.isEditing() || this.props.writeType === "input") {
      return (
        <input
          ref="inputElement"
          className={this.props.inputClass}
          {...attributes}
          value={startValue} />
      );
    }

    return (
      <span
        ref="inputElement"
        {...attributes}
        className={this.props.readClass}
        onClick={attributes.onFocus}>
        {attributes.defaultPasswordValue || DEFAULT_PASSWORD_TEXT}
      </span>
    );
  }

}

<|edits_diff|>
--- src/Form/FieldPassword.js
+++ src/Form/FieldPassword.js
@@ -3,6 +3,7 @@
 /*eslint-enable no-unused-vars */

 import FieldInput from "./FieldInput";
+import IconEdit from "./icons/IconEdit";

 const DEFAULT_PASSWORD_TEXT = "••••••";

<|current_version|>
/*eslint-disable no-unused-vars */
import React from "react";
/*eslint-enable no-unused-vars */

import FieldInput from "./FieldInput";
import IconEdit from "./icons/IconEdit";

const DEFAULT_PASSWORD_TEXT = "••••••";

const METHODS_TO_BIND = ["handleOnFocus"];

export default class FieldPassword extends FieldInput {
  constructor() {
    super();

    METHODS_TO_BIND.forEach((method) => {
      this[method] = this[method].bind(this);
    });
  }

  handleOnFocus(event) {
    if (!this.focused) {
      this.focused = true;
      this.forceUpdate();
    }

    this.props.handleEvent("focus", this.props.name, event);
  }

  getInputElement(attributes) {
    attributes = this.bindEvents(attributes, this.props.handleEvent);
    attributes.onFocus = this.handleOnFocus;

    let startValue = DEFAULT_PASSWORD_TEXT;
    if (this.focused) {
      startValue = attributes.startValue;
    }

    if (this.isEditing() || this.props.writeType === "input") {
      return (
        <input
          ref="inputElement"
          className={this.props.inputClass}
          {...attributes}
          value={startValue} />
      );
    }

    return (
      <span
        ref="inputElement"
        {...attributes}
        className={this.props.readClass}
        onClick={attributes.onFocus}>
        {attributes.defaultPasswordValue || DEFAULT_PASSWORD_TEXT}
      </span>
    );
  }

}

<|next_version|>
/*eslint-disable no-unused-vars */
import React from "react";
/*eslint-enable no-unused-vars */

import FieldInput from "./FieldInput";
import IconEdit from "./icons/IconEdit";

const DEFAULT_PASSWORD_TEXT = "••••••";

const METHODS_TO_BIND = ["handleOnFocus"];

export default class FieldPassword extends FieldInput {
  constructor() {
    super();

    METHODS_TO_BIND.forEach((method) => {
      this[method] = this[method].bind(this);
    });
  }

  handleOnFocus(event) {
    if (!this.focused) {
      this.focused = true;
      this.forceUpdate();
    }

    this.props.handleEvent("focus", this.props.name, event);
  }

  getInputElement(attributes) {
    attributes = this.bindEvents(attributes, this.props.handleEvent);
    attributes.onFocus = this.handleOnFocus;

    let startValue = DEFAULT_PASSWORD_TEXT;
    if (this.focused) {
      startValue = attributes.startValue;
    }

    if (this.isEditing() || this.props.writeType === "input") {
      return (
        <input
          ref="inputElement"
          className={this.props.inputClass}
          {...attributes}
          value={startValue} />
      );
    }

    return (
      <span
        ref="inputElement"
        {...attributes}
        className={this.props.readClass}
        onClick={attributes.onFocus}>
        {attributes.defaultPasswordValue || DEFAULT_PASSWORD_TEXT}
        <IconEdit />
      </span>
    );
  }

}


Use helmet on graphql server

Use helmet on graphql server

--- server/graphql/index.js
+++ server/graphql/index.js
@@ -2,6 +2,7 @@
 import graphQLHTTP from 'express-graphql'
 import { maskErrors } from 'graphql-errors'
 import cors from 'cors'
+import helmet from 'helmet'
 import cookieParser from 'cookie-parser'
 import dotenv from 'dotenv'
 import debug from 'debug'
@@ -25,6 +26,7 @@
   optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
 }

+app.use(helmet())
 app.use(cors(corsOptions))
 app.use(cookieParser())
 app.use(intlMiddleware)

<|original_code|>
import express from 'express'
import graphQLHTTP from 'express-graphql'
import { maskErrors } from 'graphql-errors'
import cors from 'cors'
import cookieParser from 'cookie-parser'
import dotenv from 'dotenv'
import debug from 'debug'

import sessionMiddleware from './sessionMiddleware'
import Schema from './schema'
import intlMiddleware from './intlMiddleware'
import Database from './data/Database'

dotenv.config()
const log = debug('graphql')
const PORT = process.env.PORT_GRAPHQL

const app = express()

const corsOptions = {
  origin: process.env.APP_ENDPOINT,
  methods: ['POST'],
  allowedHeaders: ['X-Requested-With', 'content-type'],
  credentials: true,
  optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
}

app.use(cors(corsOptions))
app.use(cookieParser())
app.use(intlMiddleware)
app.use(sessionMiddleware)

maskErrors(Schema)
app.use('/', graphQLHTTP(({ session, tokenData }, res) => ({
  graphiql: true,
  pretty: true,
  schema: Schema,
  context: { db: new Database() },
  rootValue: { session, tokenData, res },
})))

app.listen(PORT, () =>
  log(`GraphQL Server is now running on http://localhost:${PORT}`),
)

<|edits_diff|>
--- server/graphql/index.js
+++ server/graphql/index.js
@@ -2,6 +2,7 @@
 import graphQLHTTP from 'express-graphql'
 import { maskErrors } from 'graphql-errors'
 import cors from 'cors'
+import helmet from 'helmet'
 import cookieParser from 'cookie-parser'
 import dotenv from 'dotenv'
 import debug from 'debug'
<|current_version|>
import express from 'express'
import graphQLHTTP from 'express-graphql'
import { maskErrors } from 'graphql-errors'
import cors from 'cors'
import helmet from 'helmet'
import cookieParser from 'cookie-parser'
import dotenv from 'dotenv'
import debug from 'debug'

import sessionMiddleware from './sessionMiddleware'
import Schema from './schema'
import intlMiddleware from './intlMiddleware'
import Database from './data/Database'

dotenv.config()
const log = debug('graphql')
const PORT = process.env.PORT_GRAPHQL

const app = express()

const corsOptions = {
  origin: process.env.APP_ENDPOINT,
  methods: ['POST'],
  allowedHeaders: ['X-Requested-With', 'content-type'],
  credentials: true,
  optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
}

app.use(cors(corsOptions))
app.use(cookieParser())
app.use(intlMiddleware)
app.use(sessionMiddleware)

maskErrors(Schema)
app.use('/', graphQLHTTP(({ session, tokenData }, res) => ({
  graphiql: true,
  pretty: true,
  schema: Schema,
  context: { db: new Database() },
  rootValue: { session, tokenData, res },
})))

app.listen(PORT, () =>
  log(`GraphQL Server is now running on http://localhost:${PORT}`),
)

<|next_version|>
import express from 'express'
import graphQLHTTP from 'express-graphql'
import { maskErrors } from 'graphql-errors'
import cors from 'cors'
import helmet from 'helmet'
import cookieParser from 'cookie-parser'
import dotenv from 'dotenv'
import debug from 'debug'

import sessionMiddleware from './sessionMiddleware'
import Schema from './schema'
import intlMiddleware from './intlMiddleware'
import Database from './data/Database'

dotenv.config()
const log = debug('graphql')
const PORT = process.env.PORT_GRAPHQL

const app = express()

const corsOptions = {
  origin: process.env.APP_ENDPOINT,
  methods: ['POST'],
  allowedHeaders: ['X-Requested-With', 'content-type'],
  credentials: true,
  optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
}

app.use(helmet())
app.use(cors(corsOptions))
app.use(cookieParser())
app.use(intlMiddleware)
app.use(sessionMiddleware)

maskErrors(Schema)
app.use('/', graphQLHTTP(({ session, tokenData }, res) => ({
  graphiql: true,
  pretty: true,
  schema: Schema,
  context: { db: new Database() },
  rootValue: { session, tokenData, res },
})))

app.listen(PORT, () =>
  log(`GraphQL Server is now running on http://localhost:${PORT}`),
)


Exit linking mode after creating a booking

--- indico/modules/rb_new/client/js/common/linking/reducers.js
+++ indico/modules/rb_new/client/js/common/linking/reducers.js
@@ -16,6 +16,7 @@
  */

 import * as linkingActions from './actions';
+import {actions as bookRoomActions} from '../../modules/bookRoom';

 const initialState = {
@@ -37,6 +38,7 @@
                 eventTitle: action.eventTitle,
             };
         case linkingActions.CLEAR_OBJECT:
+        case bookRoomActions.CREATE_BOOKING_SUCCESS:
             return initialState;
         default:
             return state;

<|original_code|>
/* This file is part of Indico.
 * Copyright (C) 2002 - 2019 European Organization for Nuclear Research (CERN).
 *
 * Indico is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Indico is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indico; if not, see <http://www.gnu.org/licenses/>.
 */

import * as linkingActions from './actions';

const initialState = {
    type: null,
    id: null,
    title: null,
    eventURL: null,
    eventTitle: null,
};

export default (state = initialState, action) => {
    switch (action.type) {
        case linkingActions.SET_OBJECT:
            return {
                type: action.objectType,
                id: action.objectId,
                title: action.objectTitle,
                eventURL: action.eventURL,
                eventTitle: action.eventTitle,
            };
        case linkingActions.CLEAR_OBJECT:
            return initialState;
        default:
            return state;
    }
};

<|edits_diff|>
--- indico/modules/rb_new/client/js/common/linking/reducers.js
+++ indico/modules/rb_new/client/js/common/linking/reducers.js
@@ -16,6 +16,7 @@
  */

 import * as linkingActions from './actions';
+import {actions as bookRoomActions} from '../../modules/bookRoom';

 const initialState = {
<|current_version|>
/* This file is part of Indico.
 * Copyright (C) 2002 - 2019 European Organization for Nuclear Research (CERN).
 *
 * Indico is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Indico is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indico; if not, see <http://www.gnu.org/licenses/>.
 */

import * as linkingActions from './actions';
import {actions as bookRoomActions} from '../../modules/bookRoom';

const initialState = {
    type: null,
    id: null,
    title: null,
    eventURL: null,
    eventTitle: null,
};

export default (state = initialState, action) => {
    switch (action.type) {
        case linkingActions.SET_OBJECT:
            return {
                type: action.objectType,
                id: action.objectId,
                title: action.objectTitle,
                eventURL: action.eventURL,
                eventTitle: action.eventTitle,
            };
        case linkingActions.CLEAR_OBJECT:
            return initialState;
        default:
            return state;
    }
};

<|next_version|>
/* This file is part of Indico.
 * Copyright (C) 2002 - 2019 European Organization for Nuclear Research (CERN).
 *
 * Indico is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * Indico is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indico; if not, see <http://www.gnu.org/licenses/>.
 */

import * as linkingActions from './actions';
import {actions as bookRoomActions} from '../../modules/bookRoom';

const initialState = {
    type: null,
    id: null,
    title: null,
    eventURL: null,
    eventTitle: null,
};

export default (state = initialState, action) => {
    switch (action.type) {
        case linkingActions.SET_OBJECT:
            return {
                type: action.objectType,
                id: action.objectId,
                title: action.objectTitle,
                eventURL: action.eventURL,
                eventTitle: action.eventTitle,
            };
        case linkingActions.CLEAR_OBJECT:
        case bookRoomActions.CREATE_BOOKING_SUCCESS:
            return initialState;
        default:
            return state;
    }
};