Hide body of the index page when redirecting

--- src/common/appId.js
+++ src/common/appId.js
@@ -1,4 +1,5 @@
 var utils = require('utils');
+var $ = require('jquery');

 var AppId = {
 	app_id: ( document.location.port === '8080' ) ? 1168 : ( ( document.location.hostname.indexOf('github.io') >= 0 ) ? 1180 : 1169 ),
@@ -14,6 +15,7 @@
 			}
 		});
 		if (tokenList.length) {
+			$('#main').hide();
 			utils.addAllTokens(tokenList, function(){
 				document.location.pathname += ((document.location.pathname.slice(-1) === '/')?'':'/') + 'bot.html';
 			});

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

var AppId = {
	app_id: ( document.location.port === '8080' ) ? 1168 : ( ( document.location.hostname.indexOf('github.io') >= 0 ) ? 1180 : 1169 ),
	redirectOauth: function oauthLogin(){
		document.location = 'https://oauth.binary.com/oauth2/authorize?app_id=' + this.app_id + '&l=' + window.lang.toUpperCase();
	},
	oauthLogin: function getToken(done) {
		var queryStr = utils.parseQueryString();
		var tokenList = [];
		Object.keys(queryStr).forEach(function(key){
			if ( key.indexOf('token') === 0 ) {
				tokenList.push(queryStr[key]);
			}
		});
		if (tokenList.length) {
			utils.addAllTokens(tokenList, function(){
				document.location.pathname += ((document.location.pathname.slice(-1) === '/')?'':'/') + 'bot.html';
			});
		} else {
			if (done) {
				done();
			}
		}
	},
	removeTokenFromUrl: function removeTokenFromUrl(){
		var queryStr = utils.parseQueryString();
		if (queryStr.token1) {
			document.location.href = document.location.href.split('?')[0];
		}
	},
	getAppId: function getAppId(){
		return this.app_id;
	}
};

module.exports = AppId;
<|edits_diff|>
--- src/common/appId.js
+++ src/common/appId.js
@@ -1,4 +1,5 @@
 var utils = require('utils');
+var $ = require('jquery');

 var AppId = {
 	app_id: ( document.location.port === '8080' ) ? 1168 : ( ( document.location.hostname.indexOf('github.io') >= 0 ) ? 1180 : 1169 ),
<|current_version|>
var utils = require('utils');
var $ = require('jquery');

var AppId = {
	app_id: ( document.location.port === '8080' ) ? 1168 : ( ( document.location.hostname.indexOf('github.io') >= 0 ) ? 1180 : 1169 ),
	redirectOauth: function oauthLogin(){
		document.location = 'https://oauth.binary.com/oauth2/authorize?app_id=' + this.app_id + '&l=' + window.lang.toUpperCase();
	},
	oauthLogin: function getToken(done) {
		var queryStr = utils.parseQueryString();
		var tokenList = [];
		Object.keys(queryStr).forEach(function(key){
			if ( key.indexOf('token') === 0 ) {
				tokenList.push(queryStr[key]);
			}
		});
		if (tokenList.length) {
			utils.addAllTokens(tokenList, function(){
				document.location.pathname += ((document.location.pathname.slice(-1) === '/')?'':'/') + 'bot.html';
			});
		} else {
			if (done) {
				done();
			}
		}
	},
	removeTokenFromUrl: function removeTokenFromUrl(){
		var queryStr = utils.parseQueryString();
		if (queryStr.token1) {
			document.location.href = document.location.href.split('?')[0];
		}
	},
	getAppId: function getAppId(){
		return this.app_id;
	}
};

module.exports = AppId;
<|next_version|>
var utils = require('utils');
var $ = require('jquery');

var AppId = {
	app_id: ( document.location.port === '8080' ) ? 1168 : ( ( document.location.hostname.indexOf('github.io') >= 0 ) ? 1180 : 1169 ),
	redirectOauth: function oauthLogin(){
		document.location = 'https://oauth.binary.com/oauth2/authorize?app_id=' + this.app_id + '&l=' + window.lang.toUpperCase();
	},
	oauthLogin: function getToken(done) {
		var queryStr = utils.parseQueryString();
		var tokenList = [];
		Object.keys(queryStr).forEach(function(key){
			if ( key.indexOf('token') === 0 ) {
				tokenList.push(queryStr[key]);
			}
		});
		if (tokenList.length) {
			$('#main').hide();
			utils.addAllTokens(tokenList, function(){
				document.location.pathname += ((document.location.pathname.slice(-1) === '/')?'':'/') + 'bot.html';
			});
		} else {
			if (done) {
				done();
			}
		}
	},
	removeTokenFromUrl: function removeTokenFromUrl(){
		var queryStr = utils.parseQueryString();
		if (queryStr.token1) {
			document.location.href = document.location.href.split('?')[0];
		}
	},
	getAppId: function getAppId(){
		return this.app_id;
	}
};

module.exports = AppId;


Disable some more unused features

firefox: Disable some more unused features

--- firefox/prefs.js
+++ firefox/prefs.js
@@ -11,12 +11,16 @@
 user_pref("browser.tabs.animate", false);
 user_pref("browser.tabs.closeWindowWithLastTab", false);
 user_pref("browser.tabs.warnOnClose", false);
+user_pref("browser.uitour.enabled", false);
+user_pref("datareporting.healthreport.service.enabled", false);
 user_pref("datareporting.healthreport.uploadEnabled", false);
+user_pref("datareporting.policy.dataSubmissionEnabled", false);
 user_pref("loop.enabled", false);
 user_pref("network.cookie.cookieBehavior", 3);
 user_pref("network.cookie.lifetimePolicy", 2);
 user_pref("network.http.referer.spoofSource", true);
 user_pref("network.http.referer.trimmingPolicy", 2);
 user_pref("network.http.referer.XOriginPolicy", 2);
+user_pref("reader.parse-on-load.enabled", false);
 user_pref("readinglist.scheduler.enabled", false);
 user_pref("toolkit.telemetry.enabled", false);

<|original_code|>
user_pref("accessibility.typeaheadfind.flashBar", 0);
user_pref("accessibility.typeaheadfind.prefillwithselection", true);
user_pref("browser.backspace_action", 2);
user_pref("browser.bookmarks.restore_default_bookmarks", false);
user_pref("browser.newtab.url", "about:blank");
user_pref("browser.readinglist.enabled", false);
user_pref("browser.search.suggest.enabled", false);
user_pref("browser.selfsupport.url", "");
user_pref("browser.showQuitWarning", true);
user_pref("browser.startup.homepage", "about:blank");
user_pref("browser.tabs.animate", false);
user_pref("browser.tabs.closeWindowWithLastTab", false);
user_pref("browser.tabs.warnOnClose", false);
user_pref("datareporting.healthreport.uploadEnabled", false);
user_pref("loop.enabled", false);
user_pref("network.cookie.cookieBehavior", 3);
user_pref("network.cookie.lifetimePolicy", 2);
user_pref("network.http.referer.spoofSource", true);
user_pref("network.http.referer.trimmingPolicy", 2);
user_pref("network.http.referer.XOriginPolicy", 2);
user_pref("readinglist.scheduler.enabled", false);
user_pref("toolkit.telemetry.enabled", false);

<|edits_diff|>
--- firefox/prefs.js
+++ firefox/prefs.js
@@ -11,7 +11,10 @@
 user_pref("browser.tabs.animate", false);
 user_pref("browser.tabs.closeWindowWithLastTab", false);
 user_pref("browser.tabs.warnOnClose", false);
+user_pref("browser.uitour.enabled", false);
+user_pref("datareporting.healthreport.service.enabled", false);
 user_pref("datareporting.healthreport.uploadEnabled", false);
+user_pref("datareporting.policy.dataSubmissionEnabled", false);
 user_pref("loop.enabled", false);
 user_pref("network.cookie.cookieBehavior", 3);
 user_pref("network.cookie.lifetimePolicy", 2);
<|current_version|>
user_pref("accessibility.typeaheadfind.flashBar", 0);
user_pref("accessibility.typeaheadfind.prefillwithselection", true);
user_pref("browser.backspace_action", 2);
user_pref("browser.bookmarks.restore_default_bookmarks", false);
user_pref("browser.newtab.url", "about:blank");
user_pref("browser.readinglist.enabled", false);
user_pref("browser.search.suggest.enabled", false);
user_pref("browser.selfsupport.url", "");
user_pref("browser.showQuitWarning", true);
user_pref("browser.startup.homepage", "about:blank");
user_pref("browser.tabs.animate", false);
user_pref("browser.tabs.closeWindowWithLastTab", false);
user_pref("browser.tabs.warnOnClose", false);
user_pref("browser.uitour.enabled", false);
user_pref("datareporting.healthreport.service.enabled", false);
user_pref("datareporting.healthreport.uploadEnabled", false);
user_pref("datareporting.policy.dataSubmissionEnabled", false);
user_pref("loop.enabled", false);
user_pref("network.cookie.cookieBehavior", 3);
user_pref("network.cookie.lifetimePolicy", 2);
user_pref("network.http.referer.spoofSource", true);
user_pref("network.http.referer.trimmingPolicy", 2);
user_pref("network.http.referer.XOriginPolicy", 2);
user_pref("readinglist.scheduler.enabled", false);
user_pref("toolkit.telemetry.enabled", false);

<|next_version|>
user_pref("accessibility.typeaheadfind.flashBar", 0);
user_pref("accessibility.typeaheadfind.prefillwithselection", true);
user_pref("browser.backspace_action", 2);
user_pref("browser.bookmarks.restore_default_bookmarks", false);
user_pref("browser.newtab.url", "about:blank");
user_pref("browser.readinglist.enabled", false);
user_pref("browser.search.suggest.enabled", false);
user_pref("browser.selfsupport.url", "");
user_pref("browser.showQuitWarning", true);
user_pref("browser.startup.homepage", "about:blank");
user_pref("browser.tabs.animate", false);
user_pref("browser.tabs.closeWindowWithLastTab", false);
user_pref("browser.tabs.warnOnClose", false);
user_pref("browser.uitour.enabled", false);
user_pref("datareporting.healthreport.service.enabled", false);
user_pref("datareporting.healthreport.uploadEnabled", false);
user_pref("datareporting.policy.dataSubmissionEnabled", false);
user_pref("loop.enabled", false);
user_pref("network.cookie.cookieBehavior", 3);
user_pref("network.cookie.lifetimePolicy", 2);
user_pref("network.http.referer.spoofSource", true);
user_pref("network.http.referer.trimmingPolicy", 2);
user_pref("network.http.referer.XOriginPolicy", 2);
user_pref("reader.parse-on-load.enabled", false);
user_pref("readinglist.scheduler.enabled", false);
user_pref("toolkit.telemetry.enabled", false);


Add short alias for start command

Add short alias for start command

--- lib/bin/bozon.js
+++ lib/bin/bozon.js
@@ -14,6 +14,7 @@

 program
   .command('start')
+  .alias('s')
   .description('Compile and run application')
   .action(function () {
     runner.start()
@@ -42,4 +43,5 @@
     runner.package(platform)
   })

+
 program.parse(process.argv)

<|original_code|>
var program = require('commander')
var runner = require('./runner')
var json = require('../../package.json')

program
  .version(json.version)

program
  .command('new <name>')
  .description('Generate scaffold for new Electron application')
  .action(function (name) {
    runner.new(name)
  })

program
  .command('start')
  .description('Compile and run application')
  .action(function () {
    runner.start()
  })

program
  .command('test [spec]')
  .description('Run tests from spec/ directory')
  .action(function (spec) {
    runner.test(spec).then(function(result) {
      process.exit(result.status);
    })
  })

program
  .command('clear')
  .description('Clear builds and releases directories')
  .action(function () {
    runner.clear()
  })

program
  .command('package <platform>')
  .description('Build and Package applications for platforms defined in package.json')
  .action(function (platform) {
    runner.package(platform)
  })

program.parse(process.argv)

<|edits_diff|>
--- lib/bin/bozon.js
+++ lib/bin/bozon.js
@@ -14,6 +14,7 @@

 program
   .command('start')
+  .alias('s')
   .description('Compile and run application')
   .action(function () {
     runner.start()
<|current_version|>
var program = require('commander')
var runner = require('./runner')
var json = require('../../package.json')

program
  .version(json.version)

program
  .command('new <name>')
  .description('Generate scaffold for new Electron application')
  .action(function (name) {
    runner.new(name)
  })

program
  .command('start')
  .alias('s')
  .description('Compile and run application')
  .action(function () {
    runner.start()
  })

program
  .command('test [spec]')
  .description('Run tests from spec/ directory')
  .action(function (spec) {
    runner.test(spec).then(function(result) {
      process.exit(result.status);
    })
  })

program
  .command('clear')
  .description('Clear builds and releases directories')
  .action(function () {
    runner.clear()
  })

program
  .command('package <platform>')
  .description('Build and Package applications for platforms defined in package.json')
  .action(function (platform) {
    runner.package(platform)
  })

program.parse(process.argv)

<|next_version|>
var program = require('commander')
var runner = require('./runner')
var json = require('../../package.json')

program
  .version(json.version)

program
  .command('new <name>')
  .description('Generate scaffold for new Electron application')
  .action(function (name) {
    runner.new(name)
  })

program
  .command('start')
  .alias('s')
  .description('Compile and run application')
  .action(function () {
    runner.start()
  })

program
  .command('test [spec]')
  .description('Run tests from spec/ directory')
  .action(function (spec) {
    runner.test(spec).then(function(result) {
      process.exit(result.status);
    })
  })

program
  .command('clear')
  .description('Clear builds and releases directories')
  .action(function () {
    runner.clear()
  })

program
  .command('package <platform>')
  .description('Build and Package applications for platforms defined in package.json')
  .action(function (platform) {
    runner.package(platform)
  })

program.parse(process.argv)


Add spies for Chai assertion library

Add spies for Chai assertion library

--- client/test/setup.js
+++ client/test/setup.js
@@ -1,10 +1,12 @@
 import jsdom from 'jsdom';
 import chai from 'chai';
 import chaiImmutable from 'chai-immutable';
+import chaiSpies from 'chai-spies';
 import ignoreStyles from 'ignore-styles';

 global.document = jsdom.jsdom('<!doctype html>');
 global.window = document.defaultView;
 global.navigator = global.window.navigator;

+chai.use(chaiSpies);
 chai.use(chaiImmutable);

<|original_code|>
import jsdom from 'jsdom';
import chai from 'chai';
import chaiImmutable from 'chai-immutable';
import ignoreStyles from 'ignore-styles';

global.document = jsdom.jsdom('<!doctype html>');
global.window = document.defaultView;
global.navigator = global.window.navigator;

chai.use(chaiImmutable);

<|edits_diff|>
--- client/test/setup.js
+++ client/test/setup.js
@@ -1,6 +1,7 @@
 import jsdom from 'jsdom';
 import chai from 'chai';
 import chaiImmutable from 'chai-immutable';
+import chaiSpies from 'chai-spies';
 import ignoreStyles from 'ignore-styles';

 global.document = jsdom.jsdom('<!doctype html>');
<|current_version|>
import jsdom from 'jsdom';
import chai from 'chai';
import chaiImmutable from 'chai-immutable';
import chaiSpies from 'chai-spies';
import ignoreStyles from 'ignore-styles';

global.document = jsdom.jsdom('<!doctype html>');
global.window = document.defaultView;
global.navigator = global.window.navigator;

chai.use(chaiImmutable);

<|next_version|>
import jsdom from 'jsdom';
import chai from 'chai';
import chaiImmutable from 'chai-immutable';
import chaiSpies from 'chai-spies';
import ignoreStyles from 'ignore-styles';

global.document = jsdom.jsdom('<!doctype html>');
global.window = document.defaultView;
global.navigator = global.window.navigator;

chai.use(chaiSpies);
chai.use(chaiImmutable);


Fix playbackrate on media change

Fix playbackrate on media change

--- src/js/lib/player.js
+++ src/js/lib/player.js
@@ -7,6 +7,7 @@

         this.audio = new Audio();

+        this.audio.defaultPlaybackRate = options.playbackRate || 1;
         this.audio.playbackRate = options.playbackRate || 1;
         this.audio.volume = options.volume || 1;
         this.audio.muted = options.muted || false;
@@ -42,6 +43,7 @@

     setAudioPlaybackRate(playbackRate) {
         this.audio.playbackRate = playbackRate;
+        this.audio.defaultPlaybackRate = playbackRate;
     }

     setAudioSrc(src) {

<|original_code|>
import app from './app';

class Player {

    constructor(options) {

        this.audio = new Audio();

        this.audio.playbackRate = options.playbackRate || 1;
        this.audio.volume = options.volume || 1;
        this.audio.muted = options.muted || false;
    }

    play() {
        this.audio.play();
    }

    pause() {
        this.audio.pause();
    }

    stop() {
        this.audio.pause();
    }

    mute() {
        this.audio.muted = true;
    }

    unmute() {
        this.audio.muted = false;
    }

    getAudio() {
        return this.audio;
    }

    setAudioVolume(volume) {
        this.audio.volume = volume;
    }

    setAudioPlaybackRate(playbackRate) {
        this.audio.playbackRate = playbackRate;
    }

    setAudioSrc(src) {
        this.audio.src = src;
    }

    setAudioCurrentTime(currentTime) {
        this.audio.currentTime = currentTime;
    }
}

export default new Player({
    volume       : app.config.get('audioVolume'),
    playbackRate : app.config.get('audioPlaybackRate'),
    muted        : app.config.get('audioMuted')
});

<|edits_diff|>
--- src/js/lib/player.js
+++ src/js/lib/player.js
@@ -7,6 +7,7 @@

         this.audio = new Audio();

+        this.audio.defaultPlaybackRate = options.playbackRate || 1;
         this.audio.playbackRate = options.playbackRate || 1;
         this.audio.volume = options.volume || 1;
         this.audio.muted = options.muted || false;
<|current_version|>
import app from './app';

class Player {

    constructor(options) {

        this.audio = new Audio();

        this.audio.defaultPlaybackRate = options.playbackRate || 1;
        this.audio.playbackRate = options.playbackRate || 1;
        this.audio.volume = options.volume || 1;
        this.audio.muted = options.muted || false;
    }

    play() {
        this.audio.play();
    }

    pause() {
        this.audio.pause();
    }

    stop() {
        this.audio.pause();
    }

    mute() {
        this.audio.muted = true;
    }

    unmute() {
        this.audio.muted = false;
    }

    getAudio() {
        return this.audio;
    }

    setAudioVolume(volume) {
        this.audio.volume = volume;
    }

    setAudioPlaybackRate(playbackRate) {
        this.audio.playbackRate = playbackRate;
    }

    setAudioSrc(src) {
        this.audio.src = src;
    }

    setAudioCurrentTime(currentTime) {
        this.audio.currentTime = currentTime;
    }
}

export default new Player({
    volume       : app.config.get('audioVolume'),
    playbackRate : app.config.get('audioPlaybackRate'),
    muted        : app.config.get('audioMuted')
});

<|next_version|>
import app from './app';

class Player {

    constructor(options) {

        this.audio = new Audio();

        this.audio.defaultPlaybackRate = options.playbackRate || 1;
        this.audio.playbackRate = options.playbackRate || 1;
        this.audio.volume = options.volume || 1;
        this.audio.muted = options.muted || false;
    }

    play() {
        this.audio.play();
    }

    pause() {
        this.audio.pause();
    }

    stop() {
        this.audio.pause();
    }

    mute() {
        this.audio.muted = true;
    }

    unmute() {
        this.audio.muted = false;
    }

    getAudio() {
        return this.audio;
    }

    setAudioVolume(volume) {
        this.audio.volume = volume;
    }

    setAudioPlaybackRate(playbackRate) {
        this.audio.playbackRate = playbackRate;
        this.audio.defaultPlaybackRate = playbackRate;
    }

    setAudioSrc(src) {
        this.audio.src = src;
    }

    setAudioCurrentTime(currentTime) {
        this.audio.currentTime = currentTime;
    }
}

export default new Player({
    volume       : app.config.get('audioVolume'),
    playbackRate : app.config.get('audioPlaybackRate'),
    muted        : app.config.get('audioMuted')
});


Add log in console (devtools don't work well)

--- client/packages/core-dashboard-worona/src/dashboard/build-dashboard-extension-worona/reducers/index.js
+++ client/packages/core-dashboard-worona/src/dashboard/build-dashboard-extension-worona/reducers/index.js
@@ -1,8 +1,15 @@
+/* eslint-disable no-console */
+import { isDev } from 'worona-deps';
 import { combineReducers } from 'redux';
 import assets from './assets';
 import packages from './packages';

+const log = (state, action) => {
+  if (isDev && !action.type.startsWith('@@redux')) console.log(action); return false;
+};
+
 export default () => combineReducers({
   packages,
   assets,
+  log,
 });

<|original_code|>
import { combineReducers } from 'redux';
import assets from './assets';
import packages from './packages';

export default () => combineReducers({
  packages,
  assets,
});

<|edits_diff|>
--- client/packages/core-dashboard-worona/src/dashboard/build-dashboard-extension-worona/reducers/index.js
+++ client/packages/core-dashboard-worona/src/dashboard/build-dashboard-extension-worona/reducers/index.js
@@ -1,6 +1,12 @@
+/* eslint-disable no-console */
+import { isDev } from 'worona-deps';
 import { combineReducers } from 'redux';
 import assets from './assets';
 import packages from './packages';
+
+const log = (state, action) => {
+  if (isDev && !action.type.startsWith('@@redux')) console.log(action); return false;
+};

 export default () => combineReducers({
   packages,
<|current_version|>
/* eslint-disable no-console */
import { isDev } from 'worona-deps';
import { combineReducers } from 'redux';
import assets from './assets';
import packages from './packages';

const log = (state, action) => {
  if (isDev && !action.type.startsWith('@@redux')) console.log(action); return false;
};

export default () => combineReducers({
  packages,
  assets,
});

<|next_version|>
/* eslint-disable no-console */
import { isDev } from 'worona-deps';
import { combineReducers } from 'redux';
import assets from './assets';
import packages from './packages';

const log = (state, action) => {
  if (isDev && !action.type.startsWith('@@redux')) console.log(action); return false;
};

export default () => combineReducers({
  packages,
  assets,
  log,
});


Create a prototype to run simple test printing initial board array

--- js/game.js
+++ js/game.js
@@ -6,6 +6,12 @@
 // To capture, the piece capturing jumps the enemy piece advancing two squares on diagonal
 // The game end when one of the player lost all pieces or doesn't have moves to make
 function Game() {
+  /* Board.initial generate all elements used to be pieces and empty squares on the
+  board and return an array of the board initial position*/
   this.board = Board.initial();
+  this.bluePlayerTurn = true;
 }

+Game.prototype.test = function(param){
+  console.log(this.board);
+}

<|original_code|>
// Game Controller
// A checkers board has 64 squares like a chess board but it only uses 32 squares all the same color
// Each player has 12 pieces and pieces that make until the last squares on the enemy side is promoted
// Promoted piece also moves on diagonals with no limit of squares if it is free
// The moves are on diagonal one square at a time
// To capture, the piece capturing jumps the enemy piece advancing two squares on diagonal
// The game end when one of the player lost all pieces or doesn't have moves to make
function Game() {
  this.board = Board.initial();
}

<|edits_diff|>
--- js/game.js
+++ js/game.js
@@ -6,6 +6,9 @@
 // To capture, the piece capturing jumps the enemy piece advancing two squares on diagonal
 // The game end when one of the player lost all pieces or doesn't have moves to make
 function Game() {
+  /* Board.initial generate all elements used to be pieces and empty squares on the
+  board and return an array of the board initial position*/
   this.board = Board.initial();
+  this.bluePlayerTurn = true;
 }

<|current_version|>
// Game Controller
// A checkers board has 64 squares like a chess board but it only uses 32 squares all the same color
// Each player has 12 pieces and pieces that make until the last squares on the enemy side is promoted
// Promoted piece also moves on diagonals with no limit of squares if it is free
// The moves are on diagonal one square at a time
// To capture, the piece capturing jumps the enemy piece advancing two squares on diagonal
// The game end when one of the player lost all pieces or doesn't have moves to make
function Game() {
  /* Board.initial generate all elements used to be pieces and empty squares on the
  board and return an array of the board initial position*/
  this.board = Board.initial();
  this.bluePlayerTurn = true;
}

<|next_version|>
// Game Controller
// A checkers board has 64 squares like a chess board but it only uses 32 squares all the same color
// Each player has 12 pieces and pieces that make until the last squares on the enemy side is promoted
// Promoted piece also moves on diagonals with no limit of squares if it is free
// The moves are on diagonal one square at a time
// To capture, the piece capturing jumps the enemy piece advancing two squares on diagonal
// The game end when one of the player lost all pieces or doesn't have moves to make
function Game() {
  /* Board.initial generate all elements used to be pieces and empty squares on the
  board and return an array of the board initial position*/
  this.board = Board.initial();
  this.bluePlayerTurn = true;
}

Game.prototype.test = function(param){
  console.log(this.board);
}


Fix pop-up player not closing the moment a status is deleted

Signed-off-by: Claire <82b964c50e9d1d222e48cf5046e6484a966a7b07@sitedethib.com>

--- app/javascript/flavours/glitch/reducers/picture_in_picture.js
+++ app/javascript/flavours/glitch/reducers/picture_in_picture.js
@@ -1,4 +1,5 @@
 import { PICTURE_IN_PICTURE_DEPLOY, PICTURE_IN_PICTURE_REMOVE } from 'flavours/glitch/actions/picture_in_picture';
+import { TIMELINE_DELETE } from 'flavours/glitch/actions/timelines';

 const initialState = {
   statusId: null,
@@ -16,6 +17,8 @@
     return { statusId: action.statusId, accountId: action.accountId, type: action.playerType, ...action.props };
   case PICTURE_IN_PICTURE_REMOVE:
     return { ...initialState };
+  case TIMELINE_DELETE:
+    return (state.statusId === action.id) ? { ...initialState } : state;
   default:
     return state;
   }

<|original_code|>
import { PICTURE_IN_PICTURE_DEPLOY, PICTURE_IN_PICTURE_REMOVE } from 'flavours/glitch/actions/picture_in_picture';

const initialState = {
  statusId: null,
  accountId: null,
  type: null,
  src: null,
  muted: false,
  volume: 0,
  currentTime: 0,
};

export default function pictureInPicture(state = initialState, action) {
  switch(action.type) {
  case PICTURE_IN_PICTURE_DEPLOY:
    return { statusId: action.statusId, accountId: action.accountId, type: action.playerType, ...action.props };
  case PICTURE_IN_PICTURE_REMOVE:
    return { ...initialState };
  default:
    return state;
  }
};

<|edits_diff|>
--- app/javascript/flavours/glitch/reducers/picture_in_picture.js
+++ app/javascript/flavours/glitch/reducers/picture_in_picture.js
@@ -1,4 +1,5 @@
 import { PICTURE_IN_PICTURE_DEPLOY, PICTURE_IN_PICTURE_REMOVE } from 'flavours/glitch/actions/picture_in_picture';
+import { TIMELINE_DELETE } from 'flavours/glitch/actions/timelines';

 const initialState = {
   statusId: null,
<|current_version|>
import { PICTURE_IN_PICTURE_DEPLOY, PICTURE_IN_PICTURE_REMOVE } from 'flavours/glitch/actions/picture_in_picture';
import { TIMELINE_DELETE } from 'flavours/glitch/actions/timelines';

const initialState = {
  statusId: null,
  accountId: null,
  type: null,
  src: null,
  muted: false,
  volume: 0,
  currentTime: 0,
};

export default function pictureInPicture(state = initialState, action) {
  switch(action.type) {
  case PICTURE_IN_PICTURE_DEPLOY:
    return { statusId: action.statusId, accountId: action.accountId, type: action.playerType, ...action.props };
  case PICTURE_IN_PICTURE_REMOVE:
    return { ...initialState };
  default:
    return state;
  }
};

<|next_version|>
import { PICTURE_IN_PICTURE_DEPLOY, PICTURE_IN_PICTURE_REMOVE } from 'flavours/glitch/actions/picture_in_picture';
import { TIMELINE_DELETE } from 'flavours/glitch/actions/timelines';

const initialState = {
  statusId: null,
  accountId: null,
  type: null,
  src: null,
  muted: false,
  volume: 0,
  currentTime: 0,
};

export default function pictureInPicture(state = initialState, action) {
  switch(action.type) {
  case PICTURE_IN_PICTURE_DEPLOY:
    return { statusId: action.statusId, accountId: action.accountId, type: action.playerType, ...action.props };
  case PICTURE_IN_PICTURE_REMOVE:
    return { ...initialState };
  case TIMELINE_DELETE:
    return (state.statusId === action.id) ? { ...initialState } : state;
  default:
    return state;
  }
};


Add a little extra to the symbol-name logging test

--- test/node-integration.js
+++ test/node-integration.js
@@ -32,9 +32,13 @@
         var s2 = Symbol("a-symbol");

         var logger1 = log.getLogger(s1);
+        var defaultLevel = logger1.getLevel();
+        logger1.setLevel(log.levels.TRACE);
+
         var logger2 = log.getLogger(s2);

         // Should be unequal: same name, but different symbol instances
         expect(logger1).not.toEqual(logger2);
+        expect(logger2.getLevel()).toEqual(defaultLevel);
     });
 });

<|original_code|>
"use strict";

describe("loglevel included via node", function () {
    it("is included successfully", function () {
        expect(require('../lib/loglevel')).not.toBeUndefined();
    });

    it("allows setting the logging level", function () {
        var log = require('../lib/loglevel');

        log.setLevel(log.levels.TRACE);
        log.setLevel(log.levels.DEBUG);
        log.setLevel(log.levels.INFO);
        log.setLevel(log.levels.WARN);
        log.setLevel(log.levels.ERROR);
    });

    it("successfully logs", function () {
        var log = require('../lib/loglevel');
        console.info = jasmine.createSpy("info");

        log.setLevel(log.levels.INFO);
        log.info("test message");

        expect(console.info).toHaveBeenCalledWith("test message");
    });

    it("supports using symbols as names", function() {
        var log = require('../lib/loglevel');

        var s1 = Symbol("a-symbol");
        var s2 = Symbol("a-symbol");

        var logger1 = log.getLogger(s1);
        var logger2 = log.getLogger(s2);

        // Should be unequal: same name, but different symbol instances
        expect(logger1).not.toEqual(logger2);
    });
});

<|edits_diff|>
--- test/node-integration.js
+++ test/node-integration.js
@@ -32,6 +32,9 @@
         var s2 = Symbol("a-symbol");

         var logger1 = log.getLogger(s1);
+        var defaultLevel = logger1.getLevel();
+        logger1.setLevel(log.levels.TRACE);
+
         var logger2 = log.getLogger(s2);

         // Should be unequal: same name, but different symbol instances
<|current_version|>
"use strict";

describe("loglevel included via node", function () {
    it("is included successfully", function () {
        expect(require('../lib/loglevel')).not.toBeUndefined();
    });

    it("allows setting the logging level", function () {
        var log = require('../lib/loglevel');

        log.setLevel(log.levels.TRACE);
        log.setLevel(log.levels.DEBUG);
        log.setLevel(log.levels.INFO);
        log.setLevel(log.levels.WARN);
        log.setLevel(log.levels.ERROR);
    });

    it("successfully logs", function () {
        var log = require('../lib/loglevel');
        console.info = jasmine.createSpy("info");

        log.setLevel(log.levels.INFO);
        log.info("test message");

        expect(console.info).toHaveBeenCalledWith("test message");
    });

    it("supports using symbols as names", function() {
        var log = require('../lib/loglevel');

        var s1 = Symbol("a-symbol");
        var s2 = Symbol("a-symbol");

        var logger1 = log.getLogger(s1);
        var defaultLevel = logger1.getLevel();
        logger1.setLevel(log.levels.TRACE);

        var logger2 = log.getLogger(s2);

        // Should be unequal: same name, but different symbol instances
        expect(logger1).not.toEqual(logger2);
    });
});

<|next_version|>
"use strict";

describe("loglevel included via node", function () {
    it("is included successfully", function () {
        expect(require('../lib/loglevel')).not.toBeUndefined();
    });

    it("allows setting the logging level", function () {
        var log = require('../lib/loglevel');

        log.setLevel(log.levels.TRACE);
        log.setLevel(log.levels.DEBUG);
        log.setLevel(log.levels.INFO);
        log.setLevel(log.levels.WARN);
        log.setLevel(log.levels.ERROR);
    });

    it("successfully logs", function () {
        var log = require('../lib/loglevel');
        console.info = jasmine.createSpy("info");

        log.setLevel(log.levels.INFO);
        log.info("test message");

        expect(console.info).toHaveBeenCalledWith("test message");
    });

    it("supports using symbols as names", function() {
        var log = require('../lib/loglevel');

        var s1 = Symbol("a-symbol");
        var s2 = Symbol("a-symbol");

        var logger1 = log.getLogger(s1);
        var defaultLevel = logger1.getLevel();
        logger1.setLevel(log.levels.TRACE);

        var logger2 = log.getLogger(s2);

        // Should be unequal: same name, but different symbol instances
        expect(logger1).not.toEqual(logger2);
        expect(logger2.getLevel()).toEqual(defaultLevel);
    });
});


Implement attribute (non-terminal, rule name) highlighting.

--- src/languages/ebnf.js
+++ src/languages/ebnf.js
@@ -4,6 +4,11 @@
 */

 function(hljs) {
+    var nonTerminalMode = {
+        className: "attribute",
+        begin: /[a-zA-Z][a-zA-Z-]*([\s-]+[a-zA-Z][a-zA-Z]*)*/
+    };
+
     var specialSequenceMode = {
         className: "meta",
         begin: /\?.*\?/
@@ -11,6 +16,7 @@

     return {
         contains: [
+            nonTerminalMode,
             specialSequenceMode,
             hljs.COMMENT(/\(\*/, /\*\)/),
             // terminals

<|original_code|>
/*
Language: Extended Backus-Naur Form
Author: Alex McKibben <alex@nullscope.net>
*/

function(hljs) {
    var specialSequenceMode = {
        className: "meta",
        begin: /\?.*\?/
    };

    return {
        contains: [
            specialSequenceMode,
            hljs.COMMENT(/\(\*/, /\*\)/),
            // terminals
            hljs.APOS_STRING_MODE, hljs.QUOTE_STRING_MODE
        ]
    };
}

<|edits_diff|>
--- src/languages/ebnf.js
+++ src/languages/ebnf.js
@@ -4,6 +4,11 @@
 */

 function(hljs) {
+    var nonTerminalMode = {
+        className: "attribute",
+        begin: /[a-zA-Z][a-zA-Z-]*([\s-]+[a-zA-Z][a-zA-Z]*)*/
+    };
+
     var specialSequenceMode = {
         className: "meta",
         begin: /\?.*\?/
<|current_version|>
/*
Language: Extended Backus-Naur Form
Author: Alex McKibben <alex@nullscope.net>
*/

function(hljs) {
    var nonTerminalMode = {
        className: "attribute",
        begin: /[a-zA-Z][a-zA-Z-]*([\s-]+[a-zA-Z][a-zA-Z]*)*/
    };

    var specialSequenceMode = {
        className: "meta",
        begin: /\?.*\?/
    };

    return {
        contains: [
            specialSequenceMode,
            hljs.COMMENT(/\(\*/, /\*\)/),
            // terminals
            hljs.APOS_STRING_MODE, hljs.QUOTE_STRING_MODE
        ]
    };
}

<|next_version|>
/*
Language: Extended Backus-Naur Form
Author: Alex McKibben <alex@nullscope.net>
*/

function(hljs) {
    var nonTerminalMode = {
        className: "attribute",
        begin: /[a-zA-Z][a-zA-Z-]*([\s-]+[a-zA-Z][a-zA-Z]*)*/
    };

    var specialSequenceMode = {
        className: "meta",
        begin: /\?.*\?/
    };

    return {
        contains: [
            nonTerminalMode,
            specialSequenceMode,
            hljs.COMMENT(/\(\*/, /\*\)/),
            // terminals
            hljs.APOS_STRING_MODE, hljs.QUOTE_STRING_MODE
        ]
    };
}


Add setOnChange that allows a controller to set a function to call when the currentTask changes.

--- website/src/app/project/experiments/experiment/components/tasks/current-task.service.js
+++ website/src/app/project/experiments/experiment/components/tasks/current-task.service.js
@@ -1,6 +1,11 @@
 class CurrentTask {
     constructor() {
         this.currentTask = null;
+        this.onChangeFN = null;
+    }
+
+    setOnChange(fn) {
+        this.onChangeFN = fn;
     }

     get() {
@@ -9,6 +14,9 @@

     set(task) {
         this.currentTask = task;
+        if (this.onChangeFN) {
+            this.onChangeFN();
+        }
     }
 }

<|original_code|>
class CurrentTask {
    constructor() {
        this.currentTask = null;
    }

    get() {
        return this.currentTask;
    }

    set(task) {
        this.currentTask = task;
    }
}

angular.module('materialscommons').service('currentTask', CurrentTask);

<|edits_diff|>
--- website/src/app/project/experiments/experiment/components/tasks/current-task.service.js
+++ website/src/app/project/experiments/experiment/components/tasks/current-task.service.js
@@ -1,6 +1,11 @@
 class CurrentTask {
     constructor() {
         this.currentTask = null;
+        this.onChangeFN = null;
+    }
+
+    setOnChange(fn) {
+        this.onChangeFN = fn;
     }

     get() {
<|current_version|>
class CurrentTask {
    constructor() {
        this.currentTask = null;
        this.onChangeFN = null;
    }

    setOnChange(fn) {
        this.onChangeFN = fn;
    }

    get() {
        return this.currentTask;
    }

    set(task) {
        this.currentTask = task;
    }
}

angular.module('materialscommons').service('currentTask', CurrentTask);

<|next_version|>
class CurrentTask {
    constructor() {
        this.currentTask = null;
        this.onChangeFN = null;
    }

    setOnChange(fn) {
        this.onChangeFN = fn;
    }

    get() {
        return this.currentTask;
    }

    set(task) {
        this.currentTask = task;
        if (this.onChangeFN) {
            this.onChangeFN();
        }
    }
}

angular.module('materialscommons').service('currentTask', CurrentTask);


Add doc comments to thumbail.js

Add doc comments to thumbail.js

--- core/mixins/thumbnail.js
+++ core/mixins/thumbnail.js
@@ -8,6 +8,7 @@
      * @param {number} width
      * @param {number} height
      * @param {string} pathType
+     * @returns {string}
      */
     getThumbnail: (relativeUrl, width, height, pathType) => getThumbnailPath(relativeUrl, width, height, pathType),

@@ -16,6 +17,7 @@
      * @param {string} relativeUrl
      * @param {number} width
      * @param {number} height
+     * @returns {string}
      */
     getMediaThumbnail: (relativeUrl, width, height) => getThumbnailPath(relativeUrl, width, height, 'media')
   }

<|original_code|>
import { getThumbnailPath } from '@vue-storefront/core/helpers'

export const thumbnail = {
  methods: {
    /**
     * Return thumbnail URL for specific base url and path
     * @param {string} relativeUrl
     * @param {number} width
     * @param {number} height
     * @param {string} pathType
     */
    getThumbnail: (relativeUrl, width, height, pathType) => getThumbnailPath(relativeUrl, width, height, pathType),

    /**
     * Return thumbnail URL for specific base url using media path
     * @param {string} relativeUrl
     * @param {number} width
     * @param {number} height
     */
    getMediaThumbnail: (relativeUrl, width, height) => getThumbnailPath(relativeUrl, width, height, 'media')
  }
}

<|edits_diff|>
--- core/mixins/thumbnail.js
+++ core/mixins/thumbnail.js
@@ -8,6 +8,7 @@
      * @param {number} width
      * @param {number} height
      * @param {string} pathType
+     * @returns {string}
      */
     getThumbnail: (relativeUrl, width, height, pathType) => getThumbnailPath(relativeUrl, width, height, pathType),

<|current_version|>
import { getThumbnailPath } from '@vue-storefront/core/helpers'

export const thumbnail = {
  methods: {
    /**
     * Return thumbnail URL for specific base url and path
     * @param {string} relativeUrl
     * @param {number} width
     * @param {number} height
     * @param {string} pathType
     * @returns {string}
     */
    getThumbnail: (relativeUrl, width, height, pathType) => getThumbnailPath(relativeUrl, width, height, pathType),

    /**
     * Return thumbnail URL for specific base url using media path
     * @param {string} relativeUrl
     * @param {number} width
     * @param {number} height
     */
    getMediaThumbnail: (relativeUrl, width, height) => getThumbnailPath(relativeUrl, width, height, 'media')
  }
}

<|next_version|>
import { getThumbnailPath } from '@vue-storefront/core/helpers'

export const thumbnail = {
  methods: {
    /**
     * Return thumbnail URL for specific base url and path
     * @param {string} relativeUrl
     * @param {number} width
     * @param {number} height
     * @param {string} pathType
     * @returns {string}
     */
    getThumbnail: (relativeUrl, width, height, pathType) => getThumbnailPath(relativeUrl, width, height, pathType),

    /**
     * Return thumbnail URL for specific base url using media path
     * @param {string} relativeUrl
     * @param {number} width
     * @param {number} height
     * @returns {string}
     */
    getMediaThumbnail: (relativeUrl, width, height) => getThumbnailPath(relativeUrl, width, height, 'media')
  }
}


Transform fillRule attribute to fill-rule

--- src/js/utils/ReactSVG.js
+++ src/js/utils/ReactSVG.js
@@ -3,7 +3,13 @@
 var svgAttrs = ['dominant-baseline', 'shape-rendering', 'mask'];
 // hack for getting react to render svg attributes
 DOMProperty.injection.injectDOMPropertyConfig({
+  DOMAttributeNames: {
+    fillRule: 'fill-rule'
+  },
   isCustomAttribute: function (attribute) {
     return svgAttrs.includes(attribute);
+  },
+  Properties: {
+    fillRule: DOMProperty.injection.MUST_USE_ATTRIBUTE
   }
 });

<|original_code|>
var DOMProperty = require('react/lib/DOMProperty');

var svgAttrs = ['dominant-baseline', 'shape-rendering', 'mask'];
// hack for getting react to render svg attributes
DOMProperty.injection.injectDOMPropertyConfig({
  isCustomAttribute: function (attribute) {
    return svgAttrs.includes(attribute);
  }
});

<|edits_diff|>
--- src/js/utils/ReactSVG.js
+++ src/js/utils/ReactSVG.js
@@ -3,6 +3,9 @@
 var svgAttrs = ['dominant-baseline', 'shape-rendering', 'mask'];
 // hack for getting react to render svg attributes
 DOMProperty.injection.injectDOMPropertyConfig({
+  DOMAttributeNames: {
+    fillRule: 'fill-rule'
+  },
   isCustomAttribute: function (attribute) {
     return svgAttrs.includes(attribute);
   }
<|current_version|>
var DOMProperty = require('react/lib/DOMProperty');

var svgAttrs = ['dominant-baseline', 'shape-rendering', 'mask'];
// hack for getting react to render svg attributes
DOMProperty.injection.injectDOMPropertyConfig({
  DOMAttributeNames: {
    fillRule: 'fill-rule'
  },
  isCustomAttribute: function (attribute) {
    return svgAttrs.includes(attribute);
  }
});

<|next_version|>
var DOMProperty = require('react/lib/DOMProperty');

var svgAttrs = ['dominant-baseline', 'shape-rendering', 'mask'];
// hack for getting react to render svg attributes
DOMProperty.injection.injectDOMPropertyConfig({
  DOMAttributeNames: {
    fillRule: 'fill-rule'
  },
  isCustomAttribute: function (attribute) {
    return svgAttrs.includes(attribute);
  },
  Properties: {
    fillRule: DOMProperty.injection.MUST_USE_ATTRIBUTE
  }
});


Fix field editors for names that have dashes

--- packages/rendering/addon/helpers/cs-field-type.js
+++ packages/rendering/addon/helpers/cs-field-type.js
@@ -1,8 +1,11 @@
+import { camelize } from '@ember/string';
 import Ember from 'ember';

 export function fieldType(content, fieldName) {
   if (!content) { return; }
   let meta;
+
+  fieldName = camelize(fieldName);

   try {
     meta = content.constructor.metaForProperty(fieldName);

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

export function fieldType(content, fieldName) {
  if (!content) { return; }
  let meta;

  try {
    meta = content.constructor.metaForProperty(fieldName);
  } catch (err) {
    return;
  }

  // meta.options.fieldType is our convention for annotating
  // models. meta.type is the name of the transform that ember-data
  // is using, which we keep as a fallback.
  let type = meta.options && meta.options.fieldType;

  if (!type && meta.type) {
    // lift the default ember-data transform names into our core
    // types
    type = `@cardstack/core-types::${meta.type}`;
  }

  if (type) {
    type = stripNamespace(type);
  }
  return type;
}

function stripNamespace(type) {
  // Right now the actual field editor components get flattened down
  // out of their namespaces, so we throw away everything but the
  // last bit of their names here. This problem is easier to solve
  // once I can integrate a module-unification resolver, so I'm
  // leaving it like this for now.
  let parts = type.split(/[/:]/g);
  return parts[parts.length - 1];
}

export default Ember.Helper.helper(fieldType);

<|edits_diff|>
--- packages/rendering/addon/helpers/cs-field-type.js
+++ packages/rendering/addon/helpers/cs-field-type.js
@@ -1,3 +1,4 @@
+import { camelize } from '@ember/string';
 import Ember from 'ember';

 export function fieldType(content, fieldName) {
<|current_version|>
import { camelize } from '@ember/string';
import Ember from 'ember';

export function fieldType(content, fieldName) {
  if (!content) { return; }
  let meta;

  try {
    meta = content.constructor.metaForProperty(fieldName);
  } catch (err) {
    return;
  }

  // meta.options.fieldType is our convention for annotating
  // models. meta.type is the name of the transform that ember-data
  // is using, which we keep as a fallback.
  let type = meta.options && meta.options.fieldType;

  if (!type && meta.type) {
    // lift the default ember-data transform names into our core
    // types
    type = `@cardstack/core-types::${meta.type}`;
  }

  if (type) {
    type = stripNamespace(type);
  }
  return type;
}

function stripNamespace(type) {
  // Right now the actual field editor components get flattened down
  // out of their namespaces, so we throw away everything but the
  // last bit of their names here. This problem is easier to solve
  // once I can integrate a module-unification resolver, so I'm
  // leaving it like this for now.
  let parts = type.split(/[/:]/g);
  return parts[parts.length - 1];
}

export default Ember.Helper.helper(fieldType);

<|next_version|>
import { camelize } from '@ember/string';
import Ember from 'ember';

export function fieldType(content, fieldName) {
  if (!content) { return; }
  let meta;

  fieldName = camelize(fieldName);

  try {
    meta = content.constructor.metaForProperty(fieldName);
  } catch (err) {
    return;
  }

  // meta.options.fieldType is our convention for annotating
  // models. meta.type is the name of the transform that ember-data
  // is using, which we keep as a fallback.
  let type = meta.options && meta.options.fieldType;

  if (!type && meta.type) {
    // lift the default ember-data transform names into our core
    // types
    type = `@cardstack/core-types::${meta.type}`;
  }

  if (type) {
    type = stripNamespace(type);
  }
  return type;
}

function stripNamespace(type) {
  // Right now the actual field editor components get flattened down
  // out of their namespaces, so we throw away everything but the
  // last bit of their names here. This problem is easier to solve
  // once I can integrate a module-unification resolver, so I'm
  // leaving it like this for now.
  let parts = type.split(/[/:]/g);
  return parts[parts.length - 1];
}

export default Ember.Helper.helper(fieldType);


Fix an issue where the sort function weren't included in the fkit module.

--- src/list.js
+++ src/list.js
@@ -20,6 +20,7 @@
  * @mixes module:fkit/list/map
  * @mixes module:fkit/list/search
  * @mixes module:fkit/list/set
+ * @mixes module:fkit/list/sort
  * @mixes module:fkit/list/sublist
  * @mixes module:fkit/list/zip
  * @author Josh Bassett
@@ -31,6 +32,7 @@
   require('./list/map'),
   require('./list/search'),
   require('./list/set'),
+  require('./list/sort'),
   require('./list/sublist'),
   require('./list/zip'),
 ]);

<|original_code|>
'use strict';

var util = require('./util');

/**
 * FKit treats both arrays and strings as *lists*: an array is a list of
 * elements, and a string is a list of characters.
 *
 * Representing strings as lists may be a novel concept for some JavaScript
 * users, but it is quite common in other languages. This seemingly simple
 * abstractions yields a great deal of power: it allows you to apply the same
 * list combinators to both arrays and strings.
 *
 * @summary Working with Lists
 *
 * @module fkit/list
 * @mixes module:fkit/list/base
 * @mixes module:fkit/list/build
 * @mixes module:fkit/list/fold
 * @mixes module:fkit/list/map
 * @mixes module:fkit/list/search
 * @mixes module:fkit/list/set
 * @mixes module:fkit/list/sublist
 * @mixes module:fkit/list/zip
 * @author Josh Bassett
 */
module.exports = util.extend({}, [
  require('./list/base'),
  require('./list/build'),
  require('./list/fold'),
  require('./list/map'),
  require('./list/search'),
  require('./list/set'),
  require('./list/sublist'),
  require('./list/zip'),
]);

<|edits_diff|>
--- src/list.js
+++ src/list.js
@@ -20,6 +20,7 @@
  * @mixes module:fkit/list/map
  * @mixes module:fkit/list/search
  * @mixes module:fkit/list/set
+ * @mixes module:fkit/list/sort
  * @mixes module:fkit/list/sublist
  * @mixes module:fkit/list/zip
  * @author Josh Bassett
<|current_version|>
'use strict';

var util = require('./util');

/**
 * FKit treats both arrays and strings as *lists*: an array is a list of
 * elements, and a string is a list of characters.
 *
 * Representing strings as lists may be a novel concept for some JavaScript
 * users, but it is quite common in other languages. This seemingly simple
 * abstractions yields a great deal of power: it allows you to apply the same
 * list combinators to both arrays and strings.
 *
 * @summary Working with Lists
 *
 * @module fkit/list
 * @mixes module:fkit/list/base
 * @mixes module:fkit/list/build
 * @mixes module:fkit/list/fold
 * @mixes module:fkit/list/map
 * @mixes module:fkit/list/search
 * @mixes module:fkit/list/set
 * @mixes module:fkit/list/sort
 * @mixes module:fkit/list/sublist
 * @mixes module:fkit/list/zip
 * @author Josh Bassett
 */
module.exports = util.extend({}, [
  require('./list/base'),
  require('./list/build'),
  require('./list/fold'),
  require('./list/map'),
  require('./list/search'),
  require('./list/set'),
  require('./list/sublist'),
  require('./list/zip'),
]);

<|next_version|>
'use strict';

var util = require('./util');

/**
 * FKit treats both arrays and strings as *lists*: an array is a list of
 * elements, and a string is a list of characters.
 *
 * Representing strings as lists may be a novel concept for some JavaScript
 * users, but it is quite common in other languages. This seemingly simple
 * abstractions yields a great deal of power: it allows you to apply the same
 * list combinators to both arrays and strings.
 *
 * @summary Working with Lists
 *
 * @module fkit/list
 * @mixes module:fkit/list/base
 * @mixes module:fkit/list/build
 * @mixes module:fkit/list/fold
 * @mixes module:fkit/list/map
 * @mixes module:fkit/list/search
 * @mixes module:fkit/list/set
 * @mixes module:fkit/list/sort
 * @mixes module:fkit/list/sublist
 * @mixes module:fkit/list/zip
 * @author Josh Bassett
 */
module.exports = util.extend({}, [
  require('./list/base'),
  require('./list/build'),
  require('./list/fold'),
  require('./list/map'),
  require('./list/search'),
  require('./list/set'),
  require('./list/sort'),
  require('./list/sublist'),
  require('./list/zip'),
]);


Allow removing tags and add actions for add/remove

--- addon/components/tag-input.js
+++ addon/components/tag-input.js
@@ -26,12 +26,26 @@

       if (e.which === KEY_CODES.COMMA || e.which === KEY_CODES.SPACE || e.which === KEY_CODES.ENTER) {
         if (tagInputVal.length > 0) {
+          let onTagAdd = this.get('onTagAdd');
+
           tags.pushObject(tagInputVal);
           tagInput.val('');
+
+          if (onTagAdd) {
+            onTagAdd(tagInputVal);
+          }
+
           e.preventDefault();
         }
       } else if (e.which === KEY_CODES.BACK_BUTTON) {
+        if (tagInputVal.length === 0) {
+          let onTagRemove = this.get('onTagRemove'),
+            removedTag = tags.popObject();

+          if (onTagRemove) {
+            onTagRemove(removedTag);
+          }
+        }
       }
     });
   }

<|original_code|>
import Ember from 'ember';
import layout from '../templates/components/tag-input';

const KEY_CODES = {
  BACK_BUTTON: 8,
  COMMA: 188,
  ENTER: 13,
  SPACE: 32
};

export default Ember.Component.extend({
  layout,

  classNames: ['emberTagInput'],

  tagName: 'ul',

  tags: Ember.A(),

  didInsertElement() {
    let tagInput = this.$('.js-ember-tag-input-new');

    tagInput.on('keydown', (e) => {
      let tags = this.get('tags'),
        tagInputVal = tagInput.val().trim();

      if (e.which === KEY_CODES.COMMA || e.which === KEY_CODES.SPACE || e.which === KEY_CODES.ENTER) {
        if (tagInputVal.length > 0) {
          tags.pushObject(tagInputVal);
          tagInput.val('');
          e.preventDefault();
        }
      } else if (e.which === KEY_CODES.BACK_BUTTON) {

      }
    });
  }
});

<|edits_diff|>
--- addon/components/tag-input.js
+++ addon/components/tag-input.js
@@ -26,11 +26,21 @@

       if (e.which === KEY_CODES.COMMA || e.which === KEY_CODES.SPACE || e.which === KEY_CODES.ENTER) {
         if (tagInputVal.length > 0) {
+          let onTagAdd = this.get('onTagAdd');
+
           tags.pushObject(tagInputVal);
           tagInput.val('');
+
+          if (onTagAdd) {
+            onTagAdd(tagInputVal);
+          }
+
           e.preventDefault();
         }
       } else if (e.which === KEY_CODES.BACK_BUTTON) {
+        if (tagInputVal.length === 0) {
+          let onTagRemove = this.get('onTagRemove'),
+            removedTag = tags.popObject();

       }
     });
<|current_version|>
import Ember from 'ember';
import layout from '../templates/components/tag-input';

const KEY_CODES = {
  BACK_BUTTON: 8,
  COMMA: 188,
  ENTER: 13,
  SPACE: 32
};

export default Ember.Component.extend({
  layout,

  classNames: ['emberTagInput'],

  tagName: 'ul',

  tags: Ember.A(),

  didInsertElement() {
    let tagInput = this.$('.js-ember-tag-input-new');

    tagInput.on('keydown', (e) => {
      let tags = this.get('tags'),
        tagInputVal = tagInput.val().trim();

      if (e.which === KEY_CODES.COMMA || e.which === KEY_CODES.SPACE || e.which === KEY_CODES.ENTER) {
        if (tagInputVal.length > 0) {
          let onTagAdd = this.get('onTagAdd');

          tags.pushObject(tagInputVal);
          tagInput.val('');

          if (onTagAdd) {
            onTagAdd(tagInputVal);
          }

          e.preventDefault();
        }
      } else if (e.which === KEY_CODES.BACK_BUTTON) {
        if (tagInputVal.length === 0) {
          let onTagRemove = this.get('onTagRemove'),
            removedTag = tags.popObject();

      }
    });
  }
});

<|next_version|>
import Ember from 'ember';
import layout from '../templates/components/tag-input';

const KEY_CODES = {
  BACK_BUTTON: 8,
  COMMA: 188,
  ENTER: 13,
  SPACE: 32
};

export default Ember.Component.extend({
  layout,

  classNames: ['emberTagInput'],

  tagName: 'ul',

  tags: Ember.A(),

  didInsertElement() {
    let tagInput = this.$('.js-ember-tag-input-new');

    tagInput.on('keydown', (e) => {
      let tags = this.get('tags'),
        tagInputVal = tagInput.val().trim();

      if (e.which === KEY_CODES.COMMA || e.which === KEY_CODES.SPACE || e.which === KEY_CODES.ENTER) {
        if (tagInputVal.length > 0) {
          let onTagAdd = this.get('onTagAdd');

          tags.pushObject(tagInputVal);
          tagInput.val('');

          if (onTagAdd) {
            onTagAdd(tagInputVal);
          }

          e.preventDefault();
        }
      } else if (e.which === KEY_CODES.BACK_BUTTON) {
        if (tagInputVal.length === 0) {
          let onTagRemove = this.get('onTagRemove'),
            removedTag = tags.popObject();

          if (onTagRemove) {
            onTagRemove(removedTag);
          }
        }
      }
    });
  }
});


Append friendly query name to gql request

Reviewed By: jstejada

Differential Revision: D23665236

fbshipit-source-id: c89018597ab327d6339f96d556ab81edfdb97b3f

--- packages/relay-runtime/util/RelayFeatureFlags.js
+++ packages/relay-runtime/util/RelayFeatureFlags.js
@@ -20,6 +20,7 @@
   ENABLE_REACT_FLIGHT_COMPONENT_FIELD: boolean,
   ENABLE_REQUIRED_DIRECTIVES: boolean | string,
   ENABLE_GETFRAGMENTIDENTIFIER_OPTIMIZATION: boolean,
+  ENABLE_FRIENDLY_QUERY_NAME_GQL_URL: boolean,
 |};

 const RelayFeatureFlags: FeatureFlags = {
@@ -30,6 +31,7 @@
   ENABLE_REACT_FLIGHT_COMPONENT_FIELD: false,
   ENABLE_REQUIRED_DIRECTIVES: false,
   ENABLE_GETFRAGMENTIDENTIFIER_OPTIMIZATION: false,
+  ENABLE_FRIENDLY_QUERY_NAME_GQL_URL: false,
 };

 module.exports = RelayFeatureFlags;

<|original_code|>
/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict
 * @format
 */

// flowlint ambiguous-object-type:error

'use strict';

type FeatureFlags = {|
  ENABLE_VARIABLE_CONNECTION_KEY: boolean,
  ENABLE_PARTIAL_RENDERING_DEFAULT: boolean,
  ENABLE_RELAY_CONTAINERS_SUSPENSE: boolean,
  ENABLE_PRECISE_TYPE_REFINEMENT: boolean,
  ENABLE_REACT_FLIGHT_COMPONENT_FIELD: boolean,
  ENABLE_REQUIRED_DIRECTIVES: boolean | string,
  ENABLE_GETFRAGMENTIDENTIFIER_OPTIMIZATION: boolean,
|};

const RelayFeatureFlags: FeatureFlags = {
  ENABLE_VARIABLE_CONNECTION_KEY: false,
  ENABLE_PARTIAL_RENDERING_DEFAULT: false,
  ENABLE_RELAY_CONTAINERS_SUSPENSE: false,
  ENABLE_PRECISE_TYPE_REFINEMENT: false,
  ENABLE_REACT_FLIGHT_COMPONENT_FIELD: false,
  ENABLE_REQUIRED_DIRECTIVES: false,
  ENABLE_GETFRAGMENTIDENTIFIER_OPTIMIZATION: false,
};

module.exports = RelayFeatureFlags;

<|edits_diff|>
--- packages/relay-runtime/util/RelayFeatureFlags.js
+++ packages/relay-runtime/util/RelayFeatureFlags.js
@@ -20,6 +20,7 @@
   ENABLE_REACT_FLIGHT_COMPONENT_FIELD: boolean,
   ENABLE_REQUIRED_DIRECTIVES: boolean | string,
   ENABLE_GETFRAGMENTIDENTIFIER_OPTIMIZATION: boolean,
+  ENABLE_FRIENDLY_QUERY_NAME_GQL_URL: boolean,
 |};

 const RelayFeatureFlags: FeatureFlags = {
<|current_version|>
/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict
 * @format
 */

// flowlint ambiguous-object-type:error

'use strict';

type FeatureFlags = {|
  ENABLE_VARIABLE_CONNECTION_KEY: boolean,
  ENABLE_PARTIAL_RENDERING_DEFAULT: boolean,
  ENABLE_RELAY_CONTAINERS_SUSPENSE: boolean,
  ENABLE_PRECISE_TYPE_REFINEMENT: boolean,
  ENABLE_REACT_FLIGHT_COMPONENT_FIELD: boolean,
  ENABLE_REQUIRED_DIRECTIVES: boolean | string,
  ENABLE_GETFRAGMENTIDENTIFIER_OPTIMIZATION: boolean,
  ENABLE_FRIENDLY_QUERY_NAME_GQL_URL: boolean,
|};

const RelayFeatureFlags: FeatureFlags = {
  ENABLE_VARIABLE_CONNECTION_KEY: false,
  ENABLE_PARTIAL_RENDERING_DEFAULT: false,
  ENABLE_RELAY_CONTAINERS_SUSPENSE: false,
  ENABLE_PRECISE_TYPE_REFINEMENT: false,
  ENABLE_REACT_FLIGHT_COMPONENT_FIELD: false,
  ENABLE_REQUIRED_DIRECTIVES: false,
  ENABLE_GETFRAGMENTIDENTIFIER_OPTIMIZATION: false,
};

module.exports = RelayFeatureFlags;

<|next_version|>
/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict
 * @format
 */

// flowlint ambiguous-object-type:error

'use strict';

type FeatureFlags = {|
  ENABLE_VARIABLE_CONNECTION_KEY: boolean,
  ENABLE_PARTIAL_RENDERING_DEFAULT: boolean,
  ENABLE_RELAY_CONTAINERS_SUSPENSE: boolean,
  ENABLE_PRECISE_TYPE_REFINEMENT: boolean,
  ENABLE_REACT_FLIGHT_COMPONENT_FIELD: boolean,
  ENABLE_REQUIRED_DIRECTIVES: boolean | string,
  ENABLE_GETFRAGMENTIDENTIFIER_OPTIMIZATION: boolean,
  ENABLE_FRIENDLY_QUERY_NAME_GQL_URL: boolean,
|};

const RelayFeatureFlags: FeatureFlags = {
  ENABLE_VARIABLE_CONNECTION_KEY: false,
  ENABLE_PARTIAL_RENDERING_DEFAULT: false,
  ENABLE_RELAY_CONTAINERS_SUSPENSE: false,
  ENABLE_PRECISE_TYPE_REFINEMENT: false,
  ENABLE_REACT_FLIGHT_COMPONENT_FIELD: false,
  ENABLE_REQUIRED_DIRECTIVES: false,
  ENABLE_GETFRAGMENTIDENTIFIER_OPTIMIZATION: false,
  ENABLE_FRIENDLY_QUERY_NAME_GQL_URL: false,
};

module.exports = RelayFeatureFlags;


Support usage inside paralle babel inside inline-hbs

Fixes #444.

ember-cli-htmlbars-inline-precompile becomes parallelizable if all the AST transforms inside it are parallelizable. The protocol is supports is *nearly* the same as the one used by broccoli-babel-transpiler itself, but not quite.

--- lib/plugins/remove-configuration-html-comments.js
+++ lib/plugins/remove-configuration-html-comments.js
@@ -29,5 +29,13 @@
     return ast;
   };

+  RemoveConfigurationHtmlCommentsPlugin.parallelBabel = {
+    requireFile: __filename,
+    buildUsing: 'self',
+  };
+
   return RemoveConfigurationHtmlCommentsPlugin;
 };
+
+module.exports.self = function(){ return module.exports(); };
+

<|original_code|>
'use strict';

// this Babel plugin removes configuration comments such as:
//   <!-- template-lint triple-curlies=false -->
//   <!-- template-lint enabled=false -->

const AstNodeInfo = require('ember-template-lint/lib/helpers/ast-node-info');

module.exports = function() {
  function RemoveConfigurationHtmlCommentsPlugin() {}

  RemoveConfigurationHtmlCommentsPlugin.prototype.transform = function(ast) {
    let walker = new this.syntax.Walker();
    let bodyEntry;

    walker.visit(ast, node => {
      if (node.type === 'Program') {
        for (let i = 0; i < node.body.length; i++) {
          bodyEntry = node.body[i];

          if(AstNodeInfo.isConfigurationHtmlComment(bodyEntry)) {
            // remove the entry
            node.body.splice(i, 1);
          }
        }
      }
    });

    return ast;
  };

  return RemoveConfigurationHtmlCommentsPlugin;
};

<|edits_diff|>
--- lib/plugins/remove-configuration-html-comments.js
+++ lib/plugins/remove-configuration-html-comments.js
@@ -29,5 +29,10 @@
     return ast;
   };

+  RemoveConfigurationHtmlCommentsPlugin.parallelBabel = {
+    requireFile: __filename,
+    buildUsing: 'self',
+  };
+
   return RemoveConfigurationHtmlCommentsPlugin;
 };
<|current_version|>
'use strict';

// this Babel plugin removes configuration comments such as:
//   <!-- template-lint triple-curlies=false -->
//   <!-- template-lint enabled=false -->

const AstNodeInfo = require('ember-template-lint/lib/helpers/ast-node-info');

module.exports = function() {
  function RemoveConfigurationHtmlCommentsPlugin() {}

  RemoveConfigurationHtmlCommentsPlugin.prototype.transform = function(ast) {
    let walker = new this.syntax.Walker();
    let bodyEntry;

    walker.visit(ast, node => {
      if (node.type === 'Program') {
        for (let i = 0; i < node.body.length; i++) {
          bodyEntry = node.body[i];

          if(AstNodeInfo.isConfigurationHtmlComment(bodyEntry)) {
            // remove the entry
            node.body.splice(i, 1);
          }
        }
      }
    });

    return ast;
  };

  RemoveConfigurationHtmlCommentsPlugin.parallelBabel = {
    requireFile: __filename,
    buildUsing: 'self',
  };

  return RemoveConfigurationHtmlCommentsPlugin;
};

<|next_version|>
'use strict';

// this Babel plugin removes configuration comments such as:
//   <!-- template-lint triple-curlies=false -->
//   <!-- template-lint enabled=false -->

const AstNodeInfo = require('ember-template-lint/lib/helpers/ast-node-info');

module.exports = function() {
  function RemoveConfigurationHtmlCommentsPlugin() {}

  RemoveConfigurationHtmlCommentsPlugin.prototype.transform = function(ast) {
    let walker = new this.syntax.Walker();
    let bodyEntry;

    walker.visit(ast, node => {
      if (node.type === 'Program') {
        for (let i = 0; i < node.body.length; i++) {
          bodyEntry = node.body[i];

          if(AstNodeInfo.isConfigurationHtmlComment(bodyEntry)) {
            // remove the entry
            node.body.splice(i, 1);
          }
        }
      }
    });

    return ast;
  };

  RemoveConfigurationHtmlCommentsPlugin.parallelBabel = {
    requireFile: __filename,
    buildUsing: 'self',
  };

  return RemoveConfigurationHtmlCommentsPlugin;
};

module.exports.self = function(){ return module.exports(); };


Add underscore dependency to data loader

--- lib/dataLoader.js
+++ lib/dataLoader.js
@@ -1,11 +1,13 @@
 "use strict";

 var $ = require('jquery');
+var _ = require('underscore');

 var DataLoader = function() {
 	return {
 		get: function(uniprotID) {
 			return $.ajax('http://ves-ebi-ca:8082/us/rest/features/' + uniprotID, {
+			//return $.ajax('data/features.json', {
 				params: {
 					//put things in here if we need
 				}

<|original_code|>
"use strict";

var $ = require('jquery');

var DataLoader = function() {
	return {
		get: function(uniprotID) {
			return $.ajax('http://ves-ebi-ca:8082/us/rest/features/' + uniprotID, {
				params: {
					//put things in here if we need
				}
			}).done(function(d) {
				return processData(d);
			}).fail(function(e){
				return(e);
			});
		}
	};
} ();

var processData = function(d) {
	var consecutive = 1;
	_.each(d, function(datum) {
		if (datum.features) {
			_.each(datum.features, function(feature) {
				feature.internalId = "ft_" + consecutive;
				consecutive++;
			});
		}
	});
	d.totalFeatureCount = consecutive;
    return d;
};

module.exports = DataLoader;
<|edits_diff|>
--- lib/dataLoader.js
+++ lib/dataLoader.js
@@ -1,6 +1,7 @@
 "use strict";

 var $ = require('jquery');
+var _ = require('underscore');

 var DataLoader = function() {
 	return {
<|current_version|>
"use strict";

var $ = require('jquery');
var _ = require('underscore');

var DataLoader = function() {
	return {
		get: function(uniprotID) {
			return $.ajax('http://ves-ebi-ca:8082/us/rest/features/' + uniprotID, {
				params: {
					//put things in here if we need
				}
			}).done(function(d) {
				return processData(d);
			}).fail(function(e){
				return(e);
			});
		}
	};
} ();

var processData = function(d) {
	var consecutive = 1;
	_.each(d, function(datum) {
		if (datum.features) {
			_.each(datum.features, function(feature) {
				feature.internalId = "ft_" + consecutive;
				consecutive++;
			});
		}
	});
	d.totalFeatureCount = consecutive;
    return d;
};

module.exports = DataLoader;
<|next_version|>
"use strict";

var $ = require('jquery');
var _ = require('underscore');

var DataLoader = function() {
	return {
		get: function(uniprotID) {
			return $.ajax('http://ves-ebi-ca:8082/us/rest/features/' + uniprotID, {
			//return $.ajax('data/features.json', {
				params: {
					//put things in here if we need
				}
			}).done(function(d) {
				return processData(d);
			}).fail(function(e){
				return(e);
			});
		}
	};
} ();

var processData = function(d) {
	var consecutive = 1;
	_.each(d, function(datum) {
		if (datum.features) {
			_.each(datum.features, function(feature) {
				feature.internalId = "ft_" + consecutive;
				consecutive++;
			});
		}
	});
	d.totalFeatureCount = consecutive;
    return d;
};

module.exports = DataLoader;


Save stocks when prices change

Save stocks when prices change

--- src/public/StockFactory.js
+++ src/public/StockFactory.js
@@ -36,11 +36,17 @@
       });
     }

+    let pricesChanged = false;
     stocks.forEach(stock => {
       if (stock.shouldUpdatePrice()) {
         stock.updatePrice();
+        pricesChanged = true;
       }
     });
+
+    if (pricesChanged) {
+      Db.save('stocks', stocks);
+    }

     return stocks;
   }

<|original_code|>
(function(exports) {
  const randInt = (min, max) => {
    const range = max - min;
    return Math.round(
      min + Math.random() * range
    );
  };

  function generateStocks({minPrice, maxPrice}) {
    const symbols = [
      'Amazon',
      'Google',
      'Facebook',
      'Microsoft',
      'Facebook',
      'McDonald\'s',
      'ExxonMobil'
    ].sort();

    const stocks = symbols.map(symbol =>
      new Stock({
        symbol,
        price: randInt(minPrice, maxPrice)
      })
    );

    return stocks;
  }

  function StockFactory() {
    let stocks = Db.get('stocks');
    if (typeof stocks !== 'object' || stocks === null) {
      stocks = generateStocks({
        minPrice: 100,
        maxPrice: 1000
      });
    }

    stocks.forEach(stock => {
      if (stock.shouldUpdatePrice()) {
        stock.updatePrice();
      }
    });

    return stocks;
  }

  exports.StockFactory = StockFactory;
})(window);
<|edits_diff|>
--- src/public/StockFactory.js
+++ src/public/StockFactory.js
@@ -36,9 +36,11 @@
       });
     }

+    let pricesChanged = false;
     stocks.forEach(stock => {
       if (stock.shouldUpdatePrice()) {
         stock.updatePrice();
+        pricesChanged = true;
       }
     });

<|current_version|>
(function(exports) {
  const randInt = (min, max) => {
    const range = max - min;
    return Math.round(
      min + Math.random() * range
    );
  };

  function generateStocks({minPrice, maxPrice}) {
    const symbols = [
      'Amazon',
      'Google',
      'Facebook',
      'Microsoft',
      'Facebook',
      'McDonald\'s',
      'ExxonMobil'
    ].sort();

    const stocks = symbols.map(symbol =>
      new Stock({
        symbol,
        price: randInt(minPrice, maxPrice)
      })
    );

    return stocks;
  }

  function StockFactory() {
    let stocks = Db.get('stocks');
    if (typeof stocks !== 'object' || stocks === null) {
      stocks = generateStocks({
        minPrice: 100,
        maxPrice: 1000
      });
    }

    let pricesChanged = false;
    stocks.forEach(stock => {
      if (stock.shouldUpdatePrice()) {
        stock.updatePrice();
        pricesChanged = true;
      }
    });

    return stocks;
  }

  exports.StockFactory = StockFactory;
})(window);
<|next_version|>
(function(exports) {
  const randInt = (min, max) => {
    const range = max - min;
    return Math.round(
      min + Math.random() * range
    );
  };

  function generateStocks({minPrice, maxPrice}) {
    const symbols = [
      'Amazon',
      'Google',
      'Facebook',
      'Microsoft',
      'Facebook',
      'McDonald\'s',
      'ExxonMobil'
    ].sort();

    const stocks = symbols.map(symbol =>
      new Stock({
        symbol,
        price: randInt(minPrice, maxPrice)
      })
    );

    return stocks;
  }

  function StockFactory() {
    let stocks = Db.get('stocks');
    if (typeof stocks !== 'object' || stocks === null) {
      stocks = generateStocks({
        minPrice: 100,
        maxPrice: 1000
      });
    }

    let pricesChanged = false;
    stocks.forEach(stock => {
      if (stock.shouldUpdatePrice()) {
        stock.updatePrice();
        pricesChanged = true;
      }
    });

    if (pricesChanged) {
      Db.save('stocks', stocks);
    }

    return stocks;
  }

  exports.StockFactory = StockFactory;
})(window);


Update event defs to get VariableEvent

Update event defs to get VariableEvent

--- lib/main.js
+++ lib/main.js
@@ -10,6 +10,7 @@
   BreakpointEvent: null,
   SessionEvent: null,
   TargetEvent: null,
+  VariableEvent: null,

   activate(): void {
     this.instance = new DebuggerMi2()
@@ -19,6 +20,7 @@
     this.BreakpointEvent = eventDefs.BreakpointEvent
     this.SessionEvent    = eventDefs.SessionEvent
     this.TargetEvent     = eventDefs.TargetEvent
+    this.VariableEvent   = eventDefs.VariableEvent
   },

   provideDebugger(): DebuggerMi2 {

<|original_code|>
'use babel'

/* @flow */

import DebuggerMi2 from './debugger-mi2'
import type { EventDefs } from 'debugger'

module.exports = {
  instance: null,
  BreakpointEvent: null,
  SessionEvent: null,
  TargetEvent: null,

  activate(): void {
    this.instance = new DebuggerMi2()
  },

  consumeEventDefs(eventDefs: EventDefs): void {
    this.BreakpointEvent = eventDefs.BreakpointEvent
    this.SessionEvent    = eventDefs.SessionEvent
    this.TargetEvent     = eventDefs.TargetEvent
  },

  provideDebugger(): DebuggerMi2 {
    return this.instance
  }
}

<|edits_diff|>
--- lib/main.js
+++ lib/main.js
@@ -10,6 +10,7 @@
   BreakpointEvent: null,
   SessionEvent: null,
   TargetEvent: null,
+  VariableEvent: null,

   activate(): void {
     this.instance = new DebuggerMi2()
<|current_version|>
'use babel'

/* @flow */

import DebuggerMi2 from './debugger-mi2'
import type { EventDefs } from 'debugger'

module.exports = {
  instance: null,
  BreakpointEvent: null,
  SessionEvent: null,
  TargetEvent: null,
  VariableEvent: null,

  activate(): void {
    this.instance = new DebuggerMi2()
  },

  consumeEventDefs(eventDefs: EventDefs): void {
    this.BreakpointEvent = eventDefs.BreakpointEvent
    this.SessionEvent    = eventDefs.SessionEvent
    this.TargetEvent     = eventDefs.TargetEvent
  },

  provideDebugger(): DebuggerMi2 {
    return this.instance
  }
}

<|next_version|>
'use babel'

/* @flow */

import DebuggerMi2 from './debugger-mi2'
import type { EventDefs } from 'debugger'

module.exports = {
  instance: null,
  BreakpointEvent: null,
  SessionEvent: null,
  TargetEvent: null,
  VariableEvent: null,

  activate(): void {
    this.instance = new DebuggerMi2()
  },

  consumeEventDefs(eventDefs: EventDefs): void {
    this.BreakpointEvent = eventDefs.BreakpointEvent
    this.SessionEvent    = eventDefs.SessionEvent
    this.TargetEvent     = eventDefs.TargetEvent
    this.VariableEvent   = eventDefs.VariableEvent
  },

  provideDebugger(): DebuggerMi2 {
    return this.instance
  }
}


Add auto JS building on source change

Add auto JS building on source change

--- gulpfile.js
+++ gulpfile.js
@@ -7,6 +7,10 @@
 var uglify = require('gulp-uglify');
 var sourcemaps = require('gulp-sourcemaps');
 var gutil = require('gulp-util');
+
+var config = {
+  js: ['src/**/*.js']
+};

 gulp.task('js', function () {
   // set up the browserify instance on a task basis
@@ -25,3 +29,7 @@
     .pipe(sourcemaps.write('./'))
     .pipe(gulp.dest('./public/js/'));
 });
+
+gulp.task('develop', function() {
+  gulp.watch(config.js, ['js']);
+});

<|original_code|>
'use strict';

var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var uglify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps');
var gutil = require('gulp-util');

gulp.task('js', function () {
  // set up the browserify instance on a task basis
  var b = browserify({
    entries: './public/js/app.js',
    debug: true
  });

  return b.bundle()
    .pipe(source('bundle.js'))
    .pipe(buffer())
    .pipe(sourcemaps.init({loadMaps: true}))
        // Add transformation tasks to the pipeline here.
        .pipe(uglify())
        .on('error', gutil.log)
    .pipe(sourcemaps.write('./'))
    .pipe(gulp.dest('./public/js/'));
});

<|edits_diff|>
--- gulpfile.js
+++ gulpfile.js
@@ -7,6 +7,10 @@
 var uglify = require('gulp-uglify');
 var sourcemaps = require('gulp-sourcemaps');
 var gutil = require('gulp-util');
+
+var config = {
+  js: ['src/**/*.js']
+};

 gulp.task('js', function () {
   // set up the browserify instance on a task basis
<|current_version|>
'use strict';

var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var uglify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps');
var gutil = require('gulp-util');

var config = {
  js: ['src/**/*.js']
};

gulp.task('js', function () {
  // set up the browserify instance on a task basis
  var b = browserify({
    entries: './public/js/app.js',
    debug: true
  });

  return b.bundle()
    .pipe(source('bundle.js'))
    .pipe(buffer())
    .pipe(sourcemaps.init({loadMaps: true}))
        // Add transformation tasks to the pipeline here.
        .pipe(uglify())
        .on('error', gutil.log)
    .pipe(sourcemaps.write('./'))
    .pipe(gulp.dest('./public/js/'));
});

<|next_version|>
'use strict';

var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var uglify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps');
var gutil = require('gulp-util');

var config = {
  js: ['src/**/*.js']
};

gulp.task('js', function () {
  // set up the browserify instance on a task basis
  var b = browserify({
    entries: './public/js/app.js',
    debug: true
  });

  return b.bundle()
    .pipe(source('bundle.js'))
    .pipe(buffer())
    .pipe(sourcemaps.init({loadMaps: true}))
        // Add transformation tasks to the pipeline here.
        .pipe(uglify())
        .on('error', gutil.log)
    .pipe(sourcemaps.write('./'))
    .pipe(gulp.dest('./public/js/'));
});

gulp.task('develop', function() {
  gulp.watch(config.js, ['js']);
});


Add JQuery to collapse open menu when click off the menu

--- app/assets/javascripts/application.js
+++ app/assets/javascripts/application.js
@@ -7,6 +7,8 @@
 $(document).ready(function(){

 	$('.contact-artist').click(function (e) {
+		console.log(e)
+		// debugger
 		e.preventDefault();
 		var at = '@'
 		var ending = ".com"
@@ -16,4 +18,9 @@
 		var subject = "Contact from website";
 		document.location.href = "mailto:"+ user +  at + dom + ending + "?subject="+subject;
 	});
+
+	$(document).on('click',function(e){
+    $('.collapse').collapse('hide');
 })
+
+})

<|original_code|>
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
//= require bootstrap-sprockets

$(document).ready(function(){

	$('.contact-artist').click(function (e) {
		e.preventDefault();
		var at = '@'
		var ending = ".com"
		var dom = "markeinert"
		var user = 'hello';

		var subject = "Contact from website";
		document.location.href = "mailto:"+ user +  at + dom + ending + "?subject="+subject;
	});
})

<|edits_diff|>
--- app/assets/javascripts/application.js
+++ app/assets/javascripts/application.js
@@ -7,6 +7,8 @@
 $(document).ready(function(){

 	$('.contact-artist').click(function (e) {
+		console.log(e)
+		// debugger
 		e.preventDefault();
 		var at = '@'
 		var ending = ".com"
@@ -16,4 +18,7 @@
 		var subject = "Contact from website";
 		document.location.href = "mailto:"+ user +  at + dom + ending + "?subject="+subject;
 	});
+
+	$(document).on('click',function(e){
+    $('.collapse').collapse('hide');
 })
<|current_version|>
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
//= require bootstrap-sprockets

$(document).ready(function(){

	$('.contact-artist').click(function (e) {
		console.log(e)
		// debugger
		e.preventDefault();
		var at = '@'
		var ending = ".com"
		var dom = "markeinert"
		var user = 'hello';

		var subject = "Contact from website";
		document.location.href = "mailto:"+ user +  at + dom + ending + "?subject="+subject;
	});

	$(document).on('click',function(e){
    $('.collapse').collapse('hide');
})

<|next_version|>
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
//= require bootstrap-sprockets

$(document).ready(function(){

	$('.contact-artist').click(function (e) {
		console.log(e)
		// debugger
		e.preventDefault();
		var at = '@'
		var ending = ".com"
		var dom = "markeinert"
		var user = 'hello';

		var subject = "Contact from website";
		document.location.href = "mailto:"+ user +  at + dom + ending + "?subject="+subject;
	});

	$(document).on('click',function(e){
    $('.collapse').collapse('hide');
})

})


Add spacing to isNumeric function.

Add spacing to isNumeric function.

--- assets/js/util/isnumeric.js
+++ assets/js/util/isnumeric.js
@@ -28,9 +28,12 @@
 	if ( typeof value === 'number' ) {
 		return true;
 	}
+
 	const string = ( value || '' ).toString();
+
 	if ( ! string ) {
 		return false;
 	}
+
 	return ! isNaN( string );
 }

<|original_code|>
/**
 * Utility function related to checking if a given value is numeric.
 *
 * Site Kit by Google, Copyright 2022 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Checks if a given value is numeric.
 *
 * @since n.e.x.t
 *
 * @param {*} value The value to check.
 * @return {boolean} TRUE if a value is numeric FALSE otherwise.
 */
export function isNumeric( value ) {
	if ( typeof value === 'number' ) {
		return true;
	}
	const string = ( value || '' ).toString();
	if ( ! string ) {
		return false;
	}
	return ! isNaN( string );
}

<|edits_diff|>
--- assets/js/util/isnumeric.js
+++ assets/js/util/isnumeric.js
@@ -28,7 +28,9 @@
 	if ( typeof value === 'number' ) {
 		return true;
 	}
+
 	const string = ( value || '' ).toString();
+
 	if ( ! string ) {
 		return false;
 	}
<|current_version|>
/**
 * Utility function related to checking if a given value is numeric.
 *
 * Site Kit by Google, Copyright 2022 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Checks if a given value is numeric.
 *
 * @since n.e.x.t
 *
 * @param {*} value The value to check.
 * @return {boolean} TRUE if a value is numeric FALSE otherwise.
 */
export function isNumeric( value ) {
	if ( typeof value === 'number' ) {
		return true;
	}

	const string = ( value || '' ).toString();

	if ( ! string ) {
		return false;
	}
	return ! isNaN( string );
}

<|next_version|>
/**
 * Utility function related to checking if a given value is numeric.
 *
 * Site Kit by Google, Copyright 2022 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Checks if a given value is numeric.
 *
 * @since n.e.x.t
 *
 * @param {*} value The value to check.
 * @return {boolean} TRUE if a value is numeric FALSE otherwise.
 */
export function isNumeric( value ) {
	if ( typeof value === 'number' ) {
		return true;
	}

	const string = ( value || '' ).toString();

	if ( ! string ) {
		return false;
	}

	return ! isNaN( string );
}


Stop validation error notification stack

closes #3383
- Calls closePassive() if a new validation error is thrown to display
only the latest validation error

--- controllers/forgotten.js
+++ controllers/forgotten.js
@@ -30,10 +30,12 @@
                     self.transitionToRoute('signin');
                 }).catch(function (resp) {
                     self.toggleProperty('submitting');
+                    self.notifications.closePassive();
                     self.notifications.showAPIError(resp, 'There was a problem logging in, please try again.');
                 });
             }).catch(function (errors) {
                 self.toggleProperty('submitting');
+                self.notifications.closePassive();
                 self.notifications.showErrors(errors);
             });
         }

<|original_code|>
/* jshint unused: false */
import ajax from 'ghost/utils/ajax';
import ValidationEngine from 'ghost/mixins/validation-engine';

var ForgottenController = Ember.Controller.extend(ValidationEngine, {
    email: '',
    submitting: false,

    // ValidationEngine settings
    validationType: 'forgotten',

    actions: {
        submit: function () {
            var self = this,
                data = self.getProperties('email');

            this.toggleProperty('submitting');
            this.validate({ format: false }).then(function () {
                ajax({
                    url: self.get('ghostPaths.url').api('authentication', 'passwordreset'),
                    type: 'POST',
                    data: {
                        passwordreset: [{
                            email: data.email
                        }]
                    }
                }).then(function (resp) {
                    self.toggleProperty('submitting');
                    self.notifications.showSuccess('Please check your email for instructions.');
                    self.transitionToRoute('signin');
                }).catch(function (resp) {
                    self.toggleProperty('submitting');
                    self.notifications.showAPIError(resp, 'There was a problem logging in, please try again.');
                });
            }).catch(function (errors) {
                self.toggleProperty('submitting');
                self.notifications.showErrors(errors);
            });
        }
    }
});

export default ForgottenController;

<|edits_diff|>
--- controllers/forgotten.js
+++ controllers/forgotten.js
@@ -30,6 +30,7 @@
                     self.transitionToRoute('signin');
                 }).catch(function (resp) {
                     self.toggleProperty('submitting');
+                    self.notifications.closePassive();
                     self.notifications.showAPIError(resp, 'There was a problem logging in, please try again.');
                 });
             }).catch(function (errors) {
<|current_version|>
/* jshint unused: false */
import ajax from 'ghost/utils/ajax';
import ValidationEngine from 'ghost/mixins/validation-engine';

var ForgottenController = Ember.Controller.extend(ValidationEngine, {
    email: '',
    submitting: false,

    // ValidationEngine settings
    validationType: 'forgotten',

    actions: {
        submit: function () {
            var self = this,
                data = self.getProperties('email');

            this.toggleProperty('submitting');
            this.validate({ format: false }).then(function () {
                ajax({
                    url: self.get('ghostPaths.url').api('authentication', 'passwordreset'),
                    type: 'POST',
                    data: {
                        passwordreset: [{
                            email: data.email
                        }]
                    }
                }).then(function (resp) {
                    self.toggleProperty('submitting');
                    self.notifications.showSuccess('Please check your email for instructions.');
                    self.transitionToRoute('signin');
                }).catch(function (resp) {
                    self.toggleProperty('submitting');
                    self.notifications.closePassive();
                    self.notifications.showAPIError(resp, 'There was a problem logging in, please try again.');
                });
            }).catch(function (errors) {
                self.toggleProperty('submitting');
                self.notifications.showErrors(errors);
            });
        }
    }
});

export default ForgottenController;

<|next_version|>
/* jshint unused: false */
import ajax from 'ghost/utils/ajax';
import ValidationEngine from 'ghost/mixins/validation-engine';

var ForgottenController = Ember.Controller.extend(ValidationEngine, {
    email: '',
    submitting: false,

    // ValidationEngine settings
    validationType: 'forgotten',

    actions: {
        submit: function () {
            var self = this,
                data = self.getProperties('email');

            this.toggleProperty('submitting');
            this.validate({ format: false }).then(function () {
                ajax({
                    url: self.get('ghostPaths.url').api('authentication', 'passwordreset'),
                    type: 'POST',
                    data: {
                        passwordreset: [{
                            email: data.email
                        }]
                    }
                }).then(function (resp) {
                    self.toggleProperty('submitting');
                    self.notifications.showSuccess('Please check your email for instructions.');
                    self.transitionToRoute('signin');
                }).catch(function (resp) {
                    self.toggleProperty('submitting');
                    self.notifications.closePassive();
                    self.notifications.showAPIError(resp, 'There was a problem logging in, please try again.');
                });
            }).catch(function (errors) {
                self.toggleProperty('submitting');
                self.notifications.closePassive();
                self.notifications.showErrors(errors);
            });
        }
    }
});

export default ForgottenController;


Disable dropdown when question marked as answered

--- app/assets/javascripts/respondent/question_answered.js
+++ app/assets/javascripts/respondent/question_answered.js
@@ -7,6 +7,7 @@
       $(text_answer_field).addClass("disabled")
       $("input[name='answers["+the_id+"]']:radio").attr("disabled", true)
       $("li.answer-option-"+the_id+" textarea").attr("disabled", true)
+      $("select#answers_"+the_id).attr("disabled", true)
     }
     else {
       $(text_answer_field).attr("disabled", false)
@@ -14,6 +15,7 @@
       $(text_answer_field).removeClass("disabled")
       $("input[name='answers["+the_id+"]']:radio").attr("disabled", false)
       $("li.answer-option-"+the_id+" textarea").attr("disabled", false)
+      $("select#answers_"+the_id).attr("disabled", false)
       $('.sticky_save_all').click();
     }
   });

<|original_code|>
$(document).ready(function() {
  $(document).on('change', '.question-answered-box', function(){
    the_id = $(this).data('the-id')
    text_answer_field = $(this).closest('.text-answer').find('.text-answer-field')
    if($(this).prop('checked')) {
      $(text_answer_field).attr("readonly", true)
      $(text_answer_field).addClass("disabled")
      $("input[name='answers["+the_id+"]']:radio").attr("disabled", true)
      $("li.answer-option-"+the_id+" textarea").attr("disabled", true)
    }
    else {
      $(text_answer_field).attr("disabled", false)
      $(text_answer_field).attr("readonly", false)
      $(text_answer_field).removeClass("disabled")
      $("input[name='answers["+the_id+"]']:radio").attr("disabled", false)
      $("li.answer-option-"+the_id+" textarea").attr("disabled", false)
      $('.sticky_save_all').click();
    }
  });
});

<|edits_diff|>
--- app/assets/javascripts/respondent/question_answered.js
+++ app/assets/javascripts/respondent/question_answered.js
@@ -7,6 +7,7 @@
       $(text_answer_field).addClass("disabled")
       $("input[name='answers["+the_id+"]']:radio").attr("disabled", true)
       $("li.answer-option-"+the_id+" textarea").attr("disabled", true)
+      $("select#answers_"+the_id).attr("disabled", true)
     }
     else {
       $(text_answer_field).attr("disabled", false)
<|current_version|>
$(document).ready(function() {
  $(document).on('change', '.question-answered-box', function(){
    the_id = $(this).data('the-id')
    text_answer_field = $(this).closest('.text-answer').find('.text-answer-field')
    if($(this).prop('checked')) {
      $(text_answer_field).attr("readonly", true)
      $(text_answer_field).addClass("disabled")
      $("input[name='answers["+the_id+"]']:radio").attr("disabled", true)
      $("li.answer-option-"+the_id+" textarea").attr("disabled", true)
      $("select#answers_"+the_id).attr("disabled", true)
    }
    else {
      $(text_answer_field).attr("disabled", false)
      $(text_answer_field).attr("readonly", false)
      $(text_answer_field).removeClass("disabled")
      $("input[name='answers["+the_id+"]']:radio").attr("disabled", false)
      $("li.answer-option-"+the_id+" textarea").attr("disabled", false)
      $('.sticky_save_all').click();
    }
  });
});

<|next_version|>
$(document).ready(function() {
  $(document).on('change', '.question-answered-box', function(){
    the_id = $(this).data('the-id')
    text_answer_field = $(this).closest('.text-answer').find('.text-answer-field')
    if($(this).prop('checked')) {
      $(text_answer_field).attr("readonly", true)
      $(text_answer_field).addClass("disabled")
      $("input[name='answers["+the_id+"]']:radio").attr("disabled", true)
      $("li.answer-option-"+the_id+" textarea").attr("disabled", true)
      $("select#answers_"+the_id).attr("disabled", true)
    }
    else {
      $(text_answer_field).attr("disabled", false)
      $(text_answer_field).attr("readonly", false)
      $(text_answer_field).removeClass("disabled")
      $("input[name='answers["+the_id+"]']:radio").attr("disabled", false)
      $("li.answer-option-"+the_id+" textarea").attr("disabled", false)
      $("select#answers_"+the_id).attr("disabled", false)
      $('.sticky_save_all').click();
    }
  });
});


Allow annotations and inline-nodes in TableCells.

--- src/article/TableCellNode.js
+++ src/article/TableCellNode.js
@@ -1,4 +1,5 @@
 import { XMLTextElement } from 'substance'
+import { TEXT } from '../kit'

 export default class TableCellNode extends XMLTextElement {
   constructor (...args) {
@@ -27,6 +28,10 @@

 TableCellNode.type = 'table-cell'

+TableCellNode.schema = {
+  content: TEXT('bold', 'italic', 'sup', 'sub', 'monospace', 'ext-link', 'xref', 'inline-formula')
+}
+
 function _parseSpan (str) {
   let span = parseInt(str, 10)
   if (isFinite(span)) {

<|original_code|>
import { XMLTextElement } from 'substance'

export default class TableCellNode extends XMLTextElement {
  constructor (...args) {
    super(...args)

    this.rowIdx = -1
    this.colIdx = -1
  }

  get rowspan () {
    return _parseSpan(this.getAttribute('rowspan'))
  }

  get colspan () {
    return _parseSpan(this.getAttribute('colspan'))
  }

  isShadowed () {
    return this.shadowed
  }

  getMasterCell () {
    return this.masterCell
  }
}

TableCellNode.type = 'table-cell'

function _parseSpan (str) {
  let span = parseInt(str, 10)
  if (isFinite(span)) {
    return Math.max(span, 1)
  } else {
    return 1
  }
}

<|edits_diff|>
--- src/article/TableCellNode.js
+++ src/article/TableCellNode.js
@@ -1,4 +1,5 @@
 import { XMLTextElement } from 'substance'
+import { TEXT } from '../kit'

 export default class TableCellNode extends XMLTextElement {
   constructor (...args) {
<|current_version|>
import { XMLTextElement } from 'substance'
import { TEXT } from '../kit'

export default class TableCellNode extends XMLTextElement {
  constructor (...args) {
    super(...args)

    this.rowIdx = -1
    this.colIdx = -1
  }

  get rowspan () {
    return _parseSpan(this.getAttribute('rowspan'))
  }

  get colspan () {
    return _parseSpan(this.getAttribute('colspan'))
  }

  isShadowed () {
    return this.shadowed
  }

  getMasterCell () {
    return this.masterCell
  }
}

TableCellNode.type = 'table-cell'

function _parseSpan (str) {
  let span = parseInt(str, 10)
  if (isFinite(span)) {
    return Math.max(span, 1)
  } else {
    return 1
  }
}

<|next_version|>
import { XMLTextElement } from 'substance'
import { TEXT } from '../kit'

export default class TableCellNode extends XMLTextElement {
  constructor (...args) {
    super(...args)

    this.rowIdx = -1
    this.colIdx = -1
  }

  get rowspan () {
    return _parseSpan(this.getAttribute('rowspan'))
  }

  get colspan () {
    return _parseSpan(this.getAttribute('colspan'))
  }

  isShadowed () {
    return this.shadowed
  }

  getMasterCell () {
    return this.masterCell
  }
}

TableCellNode.type = 'table-cell'

TableCellNode.schema = {
  content: TEXT('bold', 'italic', 'sup', 'sub', 'monospace', 'ext-link', 'xref', 'inline-formula')
}

function _parseSpan (str) {
  let span = parseInt(str, 10)
  if (isFinite(span)) {
    return Math.max(span, 1)
  } else {
    return 1
  }
}


Support chai expectations for immutables.

--- test/test_helper.js
+++ test/test_helper.js
@@ -1,4 +1,6 @@
 import jsdom from 'jsdom';
+import chai from 'chai';
+import chaiImmutable from 'chai-immutable';

 const doc = jsdom.jsdom('<!doctype html>');
 const win = doc.defaultView;
@@ -11,3 +13,5 @@
     global[key] = window[key];
   }
 });
+
+chai.use(chaiImmutable);

<|original_code|>
import jsdom from 'jsdom';

const doc = jsdom.jsdom('<!doctype html>');
const win = doc.defaultView;

global.document = doc;
global.window = win;

Object.keys(window).forEach((key) => {
  if (!(key in global)) {
    global[key] = window[key];
  }
});

<|edits_diff|>
--- test/test_helper.js
+++ test/test_helper.js
@@ -1,4 +1,6 @@
 import jsdom from 'jsdom';
+import chai from 'chai';
+import chaiImmutable from 'chai-immutable';

 const doc = jsdom.jsdom('<!doctype html>');
 const win = doc.defaultView;
<|current_version|>
import jsdom from 'jsdom';
import chai from 'chai';
import chaiImmutable from 'chai-immutable';

const doc = jsdom.jsdom('<!doctype html>');
const win = doc.defaultView;

global.document = doc;
global.window = win;

Object.keys(window).forEach((key) => {
  if (!(key in global)) {
    global[key] = window[key];
  }
});

<|next_version|>
import jsdom from 'jsdom';
import chai from 'chai';
import chaiImmutable from 'chai-immutable';

const doc = jsdom.jsdom('<!doctype html>');
const win = doc.defaultView;

global.document = doc;
global.window = win;

Object.keys(window).forEach((key) => {
  if (!(key in global)) {
    global[key] = window[key];
  }
});

chai.use(chaiImmutable);