public inbox for [email protected]  
help / color / mirror / Atom feed
From: George Gelashvili <[email protected]>
To: Dave Page <[email protected]>
Cc: Sarah McAlear <[email protected]>
Cc: Surinder Kumar <[email protected]>
Cc: Matthew Kleiman <[email protected]>
Cc: Joao Pedro De Almeida Pereira <[email protected]>
Cc: Murtuza Zabuawala <[email protected]>
Cc: pgadmin-hackers <[email protected]>
Subject: Re: [pgAdmin4] [PATCH] History Tab rewrite in React
Date: Fri, 9 Jun 2017 21:52:12 -0400
Message-ID: <CAHowoHZFkDQrMO0-e2gx3ffo3kB56Qwb3jT2S1M7Sc8Bpo0Usw@mail.gmail.com> (raw)
In-Reply-To: <CA+OCxoy3gVbGiPL3hi250Ya0qMOURYOERH5XxYrnrt2Co2v-xw@mail.gmail.com>
References: <CAFS4TJYDRi6fs+wrhkj89gFe3xaFOkoApCK+AU2O4DfU2XfXog@mail.gmail.com>
	<CAKKotZQSTtJVkBKMeFi9vTrPugBEbEE38dhbbn-hr+hpd0Qzbw@mail.gmail.com>
	<CA+OCxowChMtZzaSwrG5Sganh4LiMQcgL7BpoqhTxZga30DqDXw@mail.gmail.com>
	<CAKKotZRmvbScMdUtjMgUJdRe-EXaqDoDbYBxrMZrkbVDVR_iUg@mail.gmail.com>
	<CA+OCxow3e=q9w_e=Kkw1ebZJ82EM8RyFmiTaXihdqhX6HbzZhw@mail.gmail.com>
	<CAFS4TJapVubRP_BK01GwYjfN7zpaTbCMX+CROC4mV93aL08DOg@mail.gmail.com>
	<CA+OCxozPDpP_5zdLjOSQhiGHgzE-QDsFv8-WyC8-XTxf5+uSJg@mail.gmail.com>
	<CAE+jjakzXbzvtai0Z2q0bXfecMhDzZwtn+p8mQwV7kfRGPON_Q@mail.gmail.com>
	<CA+OCxoxkM--KjFWt7FkJm+mrL5sA9jdZ59YN7=n=2ZMrkH8iSg@mail.gmail.com>
	<CAFS4TJaZA0U_BtiUBdx1jT6Z5bQzMQbjhwrjimDLgfkTb=gWJg@mail.gmail.com>
	<CAE+jjakrsf7f41LyCy6BWvjrsH9FyKK98koO0MgUkR0qmbk-uw@mail.gmail.com>
	<CA+OCxoxH+5gAjw17rWQb-jqNBeQGgOs4Q=GZ7KEwdmGHOQrsBw@mail.gmail.com>
	<CAHowoHZJtKn8F9XuiK2yFT=_OcgG7Ctx83iiFab17PwexajfDQ@mail.gmail.com>
	<CA+OCxozz7pbMQi_h-USovgwA2XkVzpeAUuK-zcro4La-_KjgtA@mail.gmail.com>
	<CAFS4TJYRuaRkHguU_BkMAa4vv7Xy_aa37B4M4HSbJ0h2QpmcFA@mail.gmail.com>
	<CA+OCxozdD8_4uyu9f+CJTjtH0x8bcKoeNMtRDcSHXSKStAPwbQ@mail.gmail.com>
	<CAE+jjakmryMDovhh+OMsw=FSTj5W62CuhbdQS4WpjcLSs9jO4g@mail.gmail.com>
	<CAM5-9D-wS-9qwMwvpUCwc8s=KumY+k8pkM7tOt1eysuFvq3n-w@mail.gmail.com>
	<CAFS4TJZtCDqoenHREPC7PEopwYVyPGUh0_9WtxQKNV2w_WLAjw@mail.gmail.com>
	<CAM5-9D9cm88KaiNjAwyNQG7+0FNxcAEJ6xaWTS9+pJ-6uLLTWQ@mail.gmail.com>
	<CAGRPzo-byoSsREPZxJ-bEbse2c2emFF5UMD=SGCJHWduk8kiCA@mail.gmail.com>
	<CA+OCxox3vW4dpK5w9LuL=axXpp_xR5V14oLbGYoe4N9zSOP3dA@mail.gmail.com>
	<CAHowoHa-xGTQgckQC431jusaHD2Eg3S0KCrn24yt9uiS0ow7bA@mail.gmail.com>
	<CA+OCxoy3gVbGiPL3hi250Ya0qMOURYOERH5XxYrnrt2Co2v-xw@mail.gmail.com>
List-Unsubscribe:  <mailto:[email protected]?body=unsub%20pgadmin-hackers>

Hi Dave,

Our patch touches code also changed by the patches that were recently
committed.
That's likely what's causing this issue. We've rebased on top of the new
state of master.

We had initially kept the yarn.lock .gitignored, but ran into an issue
rather early on where an upgraded dependency introduced a regression.
Checking-in the yarn.lock provides the "know your dependency version"
benefit of vendorizing code without vendorization's drawback of having to
manually manage your dependencies.

It is safe to delete a yarn.lock before applying a patch, as you are
authoring master. It provides a history of the versions of each dependency
that were working at the point in time of the commit. By contrast,
package.json provides approximate versions and leaves room for
fixes/improvements by the dependency authors to be pulled in as they become
available.

*To run linter and tests:*

The tasks that Grunt used to manage are now defined as a set of scripts in
the package.json
After applying the patches--which may require deleting yarn.lock for the
first patch--you should cd web && yarn install

Then yarn test will run the linter, jasmine specs, and python tests
including feature tests, in that order, exiting early if there are
failures/errors.
At the moment, the CheckForViewData test is failing on master as well as in
each of these patches; that should be resolved as RM2477.

Thanks!
George and Matt


On Thu, Jun 8, 2017 at 9:15 AM, Dave Page <[email protected]> wrote:

> Hi George
>
> On Wed, Jun 7, 2017 at 10:21 PM, George Gelashvili
> <[email protected]> wrote:
> > Hi Dave,
> >
> > I split the linting out into an intermediate commit, and rebased on top
> of
> > master.
>
> Unfortunately, it still doesn't apply:
>
> error: patch failed: web/regression/javascript/test-main.js:1
> error: removal patch leaves file contents
> error: web/regression/javascript/test-main.js: patch does not apply
> Checking patch web/regression/requirements.txt...
> Checking patch web/webpack.config.js...
> Checking patch web/webpack.test.config.js...
> Checking patch web/yarn.lock...
> error: web/yarn.lock: already exists in working directory
> Applied patch .gitignore cleanly.
> Applied patch Make.bat cleanly.
> Applied patch README cleanly.
> Applied patch pkg/mac/build.sh cleanly.
> Applied patch pkg/pip/build.sh cleanly.
> Applied patch pkg/src/build.sh cleanly.
> Applied patch web/.eslintrc.js cleanly.
> Applied patch web/karma.conf.js cleanly.
> Applied patch web/package.json cleanly.
> Applied patch web/pgAdmin4.py cleanly.
> Applied patch web/pgadmin/static/jsx/components.jsx cleanly.
> Applied patch web/pgadmin/tools/javascript/__init__.py cleanly.
> Applied patch web/pgadmin/tools/javascript/javascript_bundler.py cleanly.
> Applied patch web/pgadmin/tools/javascript/tests/__init__.py cleanly.
> Applied patch web/pgadmin/tools/javascript/tests/test_javascript_bundler.
> py
> cleanly.
> Applied patch web/regression/README cleanly.
> Applied patch web/regression/javascript/jasmine_capture_warnings_
> beforeall.js
> cleanly.
> Applied patch web/regression/requirements.txt cleanly.
> Applied patch web/webpack.config.js cleanly.
> Applied patch web/webpack.test.config.js cleanly.
>
> The second (lint update) patch is even worse, with significant number
> change that just don't want to apply.
>
> Clearly yarn.lock needs to be removed from there repo.
>
> Once I can apply a version of this, how should I be running the linter
> and the unit tests?
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>

diff --git a/.gitignore b/.gitignore
index f6d14903..3ccd1782 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,4 +37,3 @@ runtime/ui_BrowserWindow.h
 web/config_local.py
 web/regression/test_config.json
 node_modules/
-yarn.lock
diff --git a/web/yarn.lock b/web/yarn.lock
new file mode 100644
index 00000000..e69de29b

diff --git a/.gitignore b/.gitignore
index 3ccd1782..67cec303 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,4 +36,5 @@ runtime/release/
 runtime/ui_BrowserWindow.h
 web/config_local.py
 web/regression/test_config.json
-node_modules/
+/web/pgadmin/static/**/generated/
+/web/node_modules/
diff --git a/Make.bat b/Make.bat
index 8082c56d..e73eb2f8 100644
--- a/Make.bat
+++ b/Make.bat
@@ -238,6 +238,12 @@ REM Main function Ends
     CD "%WD%\web"
 
     IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
+
+    ECHO Install Javascript dependencies
+    yarn install
+    ECHO Bundle all Javascript
+    yarn run webpacker
+
     XCOPY /S /I /E /H /Y "%WD%\web" "%PGBUILDPATH%\web" > nul
     IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
 
diff --git a/README b/README
index ff567db2..efa0bcff 100644
--- a/README
+++ b/README
@@ -196,6 +196,24 @@ http://www.tylerbutler.com/2012/05/how-to-install-python-pip-and-virtualenv-on-w
 Once a virtual environment has been created and enabled, setup can continue
 from step 4 above.
 
+
+Bundling Javascript
+-------------------
+
+The current build process for the JS frontend is: source_file.js(x) -> babel -> webpack -> bundled_output.js
+
+To setup your environment for building:
+
+    get Yarn: https://yarnpkg.com/en/docs/install
+
+Build:
+
+    (pgadmin4/web)$ yarn install
+    (pgadmin4/web)$ yarn run bundle
+
+This will also run a linter against the js source files.
+
+
 Configuring the Runtime
 -----------------------
 
diff --git a/pkg/mac/build.sh b/pkg/mac/build.sh
index a138a8a5..cd2908ef 100755
--- a/pkg/mac/build.sh
+++ b/pkg/mac/build.sh
@@ -178,6 +178,11 @@ _complete_bundle() {
     # run complete-bundle to copy the dependent libraries and frameworks and fix the rpaths
     ./complete-bundle.sh "$BUILDROOT/$APP_BUNDLE_NAME" || { echo complete-bundle.sh failed; exit 1; }
 
+    pushd $SOURCEDIR/web
+        yarn install
+        yarn run webpacker
+    popd
+
     # copy the web directory to the bundle as it is required by runtime
     cp -r $SOURCEDIR/web "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/" || exit 1
     cd "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/web"
diff --git a/pkg/pip/build.sh b/pkg/pip/build.sh
index 3ceaec4f..e9d88350 100755
--- a/pkg/pip/build.sh
+++ b/pkg/pip/build.sh
@@ -57,6 +57,15 @@ do
     tar cf - $FILE | (cd ../pip-build/pgadmin4; tar xf -)
 done
 
+yarn install
+yarn run webpacker
+
+for FILE in `ls -d pgAdmin/static/js/generated/*`
+do
+    echo Adding $FILE
+    tar cf - $FILE | (cd ../pip-build/pgadmin4; tar xf -)
+done
+
 cd ../docs
 for FILE in `git ls-files`
 do
diff --git a/pkg/src/build.sh b/pkg/src/build.sh
index a2c6f90e..bf6bc284 100755
--- a/pkg/src/build.sh
+++ b/pkg/src/build.sh
@@ -69,6 +69,17 @@ do
     tar cf - $FILE | (cd src-build/$TARBALL_NAME; tar xf -)
 done
 
+pushd web
+    yarn install
+    yarn run webpacker
+
+    for FILE in `ls -d pgAdmin/static/js/generated/*`
+    do
+        echo Adding $FILE
+        tar cf - $FILE | (cd ../src-build/$TARBALL_NAME/web; tar xf -)
+    done
+popd
+
 # Create the tarball
 echo Creating tarball...
 cd src-build
diff --git a/web/.eslintrc.js b/web/.eslintrc.js
new file mode 100644
index 00000000..c60569db
--- /dev/null
+++ b/web/.eslintrc.js
@@ -0,0 +1,45 @@
+module.exports = {
+  'env': {
+    'browser': true,
+    'es6': true,
+    'amd': true,
+    'jasmine': true,
+  },
+  'extends': 'eslint:recommended',
+  'parserOptions': {
+    'ecmaFeatures': {
+      'experimentalObjectRestSpread': true,
+      'jsx': true
+    },
+    'sourceType': 'module'
+  },
+  'plugins': [
+    'react'
+  ],
+  'globals': {
+    '_': true,
+    'module': true,
+  },
+  'rules': {
+    'indent': [
+      'error',
+      2
+    ],
+    'linebreak-style': [
+      'error',
+      'unix'
+    ],
+    'quotes': [
+      'error',
+      'single'
+    ],
+    'semi': [
+      'error',
+      'always'
+    ],
+    'comma-dangle': [
+      'error',
+      'always-multiline'
+    ]
+  }
+};
\ No newline at end of file
diff --git a/web/karma.conf.js b/web/karma.conf.js
index e7e26081..3c8e933c 100644
--- a/web/karma.conf.js
+++ b/web/karma.conf.js
@@ -1,62 +1,52 @@
-// Karma configuration
-// Generated on Wed Mar 01 2017 14:19:28 GMT-0500 (EST)
+const webpackConfig = require('./webpack.test.config.js');
 
-module.exports = function(config) {
+module.exports = function (config) {
   config.set({
-
-    // base path that will be used to resolve all patterns (eg. files, exclude)
-    basePath: '',
-
-
-    // frameworks to use
-    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
-    frameworks: ['jasmine', 'requirejs'],
-
-
-    // list of files / patterns to load in the browser
+    frameworks: ['jasmine'],
+    plugins: [
+      'karma-webpack',
+      'karma-phantomjs-launcher',
+      'karma-jasmine',
+      'karma-jasmine-html-reporter',
+    ],
     files: [
-      'regression/javascript/test-main.js',
-      {pattern: 'regression/javascript/**/*.js', included: false},
+      {pattern: 'pgadmin/static/**/*.js', included: false},
       {pattern: 'pgadmin/static/vendor/**/*.js', included: false},
-      {pattern: 'pgadmin/static/js/**/*.js', included: false},
-      {pattern: 'pgadmin/browser/static/js/**/*.js', included: false}
+      {pattern: 'pgadmin/browser/static/js/**/*.js', included: false},
+      'regression/javascript/**/*.jsx',
+      'regression/javascript/**/*.js',
     ],
 
-
     // list of files to exclude
     exclude: [
       'pgadmin/static/vendor/**/*[Tt]est.js',
       'pgadmin/static/vendor/**/*[Ss]pec.js'
     ],
 
-
     // preprocess matching files before serving them to the browser
     // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
     preprocessors: {
+      'regression/javascript/**/*.js': ['webpack'],
+      // 'regression/javascript/**/*.jsx': ['webpack'],
     },
 
+    webpack: webpackConfig,
+    webpackMiddleware: {
+      stats: 'errors-only',
+    },
 
-    // test results reporter to use
-    // possible values: 'dots', 'progress'
-    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
-    reporters: ['progress'],
-
-
-    // web server port
+    reporters: ['progress', 'kjhtml'],
     port: 9876,
 
-
     // enable / disable colors in the output (reporters and logs)
     colors: true,
 
-
     // level of logging
     // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
-    logLevel: config.LOG_INFO,
-
+    logLevel: config.LOG_WARN,
 
     // enable / disable watching file and executing tests whenever any file changes
-    autoWatch: false,
+    autoWatch: true,
 
 
     // start these browsers
@@ -66,10 +56,10 @@ module.exports = function(config) {
 
     // Continuous Integration mode
     // if true, Karma captures browsers, runs the tests and exits
-    singleRun: true,
+    singleRun: false,
 
     // Concurrency level
     // how many browser should be started simultaneous
     concurrency: Infinity
-  })
-}
+  });
+};
diff --git a/web/package.json b/web/package.json
index 80e4e5ad..40070da0 100644
--- a/web/package.json
+++ b/web/package.json
@@ -1,10 +1,43 @@
 {
   "devDependencies": {
-    "jasmine-core": "^2.5.2",
-    "karma": "^1.5.0",
-    "karma-jasmine": "^1.1.0",
-    "karma-phantomjs-launcher": "^1.0.2",
-    "karma-requirejs": "^1.1.0",
-    "requirejs": "^2.3.3"
+    "babel-core": "~6.24.0",
+    "babel-loader": "~6.4.1",
+    "babel-preset-es2015": "~6.24.0",
+    "babel-preset-react": "~6.23.0",
+    "enzyme": "~2.8.2",
+    "jasmine-core": "~2.5.2",
+    "karma": "~1.5.0",
+    "karma-babel-preprocessor": "^6.0.1",
+    "karma-browserify": "~5.1.1",
+    "karma-jasmine": "~1.1.0",
+    "karma-jasmine-html-reporter": "~0.2.2",
+    "karma-phantomjs-launcher": "~1.0.2",
+    "karma-requirejs": "~1.1.0",
+    "karma-sourcemap-loader": "~0.3.7",
+    "karma-webpack": "~2.0.3",
+    "react-addons-test-utils": "~15.4.2",
+    "webpack": "~2.3.1"
+  },
+  "dependencies": {
+    "axios": "^0.16.1",
+    "babel-plugin-transform-es2015-modules-amd": "^6.24.1",
+    "babel-polyfill": "^6.23.0",
+    "babel-preset-es2015-without-strict": "~0.0.4",
+    "babelify": "~7.3.0",
+    "browserify": "~14.1.0",
+    "exports-loader": "~0.6.4",
+    "imports-loader": "git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d";,
+    "react": "~15.4.2",
+    "react-dom": "~15.4.2",
+    "requirejs": "~2.3.3",
+    "underscore": "~1.8.3",
+    "watchify": "~3.9.0"
+  },
+  "scripts": {
+    "bundle": "yarn run webpack -- --optimize-minimize --config webpack.config.js",
+    "test:karma-once": "yarn run karma start -- --single-run",
+    "test:karma": "yarn run karma start",
+    "test:feature": "yarn run bundle && python regression/runtests.py --pkg feature_tests",
+    "test": "yarn run test:karma-once && yarn run bundle && python regression/runtests.py"
   }
 }
diff --git a/web/pgAdmin4.py b/web/pgAdmin4.py
index 24e1564a..81258079 100644
--- a/web/pgAdmin4.py
+++ b/web/pgAdmin4.py
@@ -14,25 +14,40 @@ to start a web server."""
 import os
 import sys
 
-# We need to include the root directory in sys.path to ensure that we can
-# find everything we need when running in the standalone runtime.
+from pgadmin.tools.javascript.javascript_bundler import JavascriptBundler, JsState
+
 root = os.path.dirname(os.path.realpath(__file__))
 if sys.path[0] != root:
     sys.path.insert(0, root)
 
 import config
 from pgadmin import create_app
+from pgadmin.utils import u, fs_encoding, file_quote
 
 # Get the config database schema version. We store this in pgadmin.model
 # as it turns out that putting it in the config files isn't a great idea
 from pgadmin.model import SCHEMA_VERSION
 config.SETTINGS_SCHEMA_VERSION = SCHEMA_VERSION
 
+##########################################################################
+# Sanity checks
+##########################################################################
+
+# Check if the database exists. If it does not, create it.
+if not os.path.isfile(config.SQLITE_PATH):
+    setupfile = os.path.join(
+        os.path.dirname(os.path.realpath(u(__file__, fs_encoding))), u'setup.py'
+    )
+    exec(open(file_quote(setupfile), 'r').read())
 
 ##########################################################################
-# Server starup
+# Server startup
 ##########################################################################
 
+# Build Javascript files
+javascriptBundler = JavascriptBundler()
+javascriptBundler.bundle()
+
 # Create the app!
 app = create_app()
 
@@ -41,6 +56,12 @@ if config.DEBUG:
 else:
     app.debug = False
 
+# respond to JS
+if javascriptBundler.report() == JsState.NONE:
+    app.logger.error("Unable to generate javascript")
+    app.logger.error("To run the app ensure that yarn install command runs successfully")
+    raise Exception("No generated javascript, aborting")
+
 # Start the web server. The port number should have already been set by the
 # runtime if we're running in desktop mode, otherwise we'll just use the
 # Flask default.
diff --git a/web/pgadmin/static/jsx/components.jsx b/web/pgadmin/static/jsx/components.jsx
new file mode 100644
index 00000000..5bcb5208
--- /dev/null
+++ b/web/pgadmin/static/jsx/components.jsx
@@ -0,0 +1,8 @@
+
+import React from 'react';
+import {render} from 'react-dom';
+
+export {
+  render,
+  React,
+};
\ No newline at end of file
diff --git a/web/pgadmin/tools/javascript/__init__.py b/web/pgadmin/tools/javascript/__init__.py
new file mode 100644
index 00000000..cac9a64a
--- /dev/null
+++ b/web/pgadmin/tools/javascript/__init__.py
@@ -0,0 +1,8 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2017, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+###########################################################################
diff --git a/web/pgadmin/tools/javascript/javascript_bundler.py b/web/pgadmin/tools/javascript/javascript_bundler.py
new file mode 100644
index 00000000..4ca2da67
--- /dev/null
+++ b/web/pgadmin/tools/javascript/javascript_bundler.py
@@ -0,0 +1,62 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2017, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import os
+from contextlib import contextmanager
+from subprocess import call
+from pgadmin.utils import u, fs_encoding, file_quote
+
+
+# enum-like for tracking whether we have
+class JsState:
+    NONE = 0
+    OLD = 1
+    NEW = 2
+
+
+class JavascriptBundler:
+    """Builds Javascript bundle files by delegating to webpack"""
+
+    def __init__(self):
+        self.jsState = JsState.NONE
+
+    def bundle(self):
+        try:
+            try_building_js()
+            self.jsState = JsState.NEW
+        except OSError:
+            webdir_path()
+            generatedJavascriptDir = os.path.join(webdir_path(), 'pgadmin', 'static', 'js', 'generated')
+            if os.path.exists(generatedJavascriptDir) and os.listdir(generatedJavascriptDir):
+                self.jsState = JsState.OLD
+            else:
+                self.jsState = JsState.NONE
+
+    def report(self):
+        return self.jsState
+
+
+@contextmanager
+def pushd(new_dir):
+    previous_dir = os.getcwd()
+    os.chdir(new_dir)
+    yield
+    os.chdir(previous_dir)
+
+
+def webdir_path():
+    dirname = os.path.dirname
+    thisPath = os.path.realpath(u(__file__, fs_encoding))
+    return dirname(dirname(dirname(dirname(thisPath))))
+
+
+def try_building_js():
+    with pushd(webdir_path()):
+        if call(['yarn', 'run', 'bundle']) != 0:
+            raise OSError('Error executing bundling the application')
diff --git a/web/pgadmin/tools/javascript/tests/__init__.py b/web/pgadmin/tools/javascript/tests/__init__.py
new file mode 100644
index 00000000..cac9a64a
--- /dev/null
+++ b/web/pgadmin/tools/javascript/tests/__init__.py
@@ -0,0 +1,8 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2017, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+###########################################################################
diff --git a/web/pgadmin/tools/javascript/tests/test_javascript_bundler.py b/web/pgadmin/tools/javascript/tests/test_javascript_bundler.py
new file mode 100644
index 00000000..e0cccdfd
--- /dev/null
+++ b/web/pgadmin/tools/javascript/tests/test_javascript_bundler.py
@@ -0,0 +1,117 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2017, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import sys
+
+from pgadmin.utils.route import BaseTestGenerator
+if sys.version_info < (3, 3):
+    import mock
+else:
+    import unittest.mock as mock
+
+
+class JavascriptBundlerTestCase(BaseTestGenerator):
+    """This tests that the javascript bundler tool causes the application to bundle,
+    and can be invoked before and after app start correctly"""
+
+    scenarios = [('scenario name: JavascriptBundlerTestCase', dict())]
+
+    def setUp(self):
+        self.mockSubprocess = mock.Mock()
+        self.mockOs = mock.Mock()
+        sys.modules['subprocess'] = self.mockSubprocess
+        sys.modules['os'] = self.mockOs
+
+    def runTest(self):
+        from pgadmin.tools.javascript.javascript_bundler import JavascriptBundler
+        from pgadmin.tools.javascript.javascript_bundler import JsState
+        self.JavascriptBundler = JavascriptBundler
+        self.JsState = JsState
+
+        self._bundling_succeeds()
+        self.resetTestState()
+        self._bundling_fails_and_there_is_no_existing_bundle()
+        self.resetTestState()
+        self._bundling_fails_when_bundling_returns_nonzero()
+        self.resetTestState()
+        self._bundling_fails_and_there_is_no_existing_bundle_directory()
+        self.resetTestState()
+        self._bundling_fails_but_there_was_existing_bundle()
+        self.resetTestState()
+
+    def resetTestState(self):
+        self.mockSubprocess.reset_mock()
+        self.mockSubprocess.call.side_effect = None
+        self.mockOs.reset_mock()
+        self.mockOs.listdir.side_effect = None
+        self.mockOs.path.exists.side_effect = None
+
+    def _bundling_succeeds(self):
+        javascriptBundler = self.JavascriptBundler()
+        self.assertEqual(len(self.mockSubprocess.method_calls), 0)
+        self.mockSubprocess.call.return_value = 0
+
+        self.mockOs.listdir.return_value = [u'history.js', u'reactComponents.js']
+
+        javascriptBundler.bundle()
+        self.mockSubprocess.call.assert_called_once_with(['yarn', 'run', 'bundle'])
+
+        reportedState = javascriptBundler.report()
+        expectedState = self.JsState.NEW
+        self.assertEqual(reportedState, expectedState)
+
+    def _bundling_fails_when_bundling_returns_nonzero(self):
+        javascriptBundler = self.JavascriptBundler()
+        self.assertEqual(len(self.mockSubprocess.method_calls), 0)
+        self.mockOs.listdir.return_value = []
+        self.mockSubprocess.call.return_value = 99
+
+        javascriptBundler.bundle()
+
+        reportedState = javascriptBundler.report()
+        expectedState = self.JsState.NONE
+        self.assertEqual(reportedState, expectedState)
+
+    def _bundling_fails_and_there_is_no_existing_bundle(self):
+        javascriptBundler = self.JavascriptBundler()
+        self.mockSubprocess.call.side_effect = OSError("mock exception behavior")
+        self.mockOs.path.exists.return_value = True
+        self.mockOs.listdir.return_value = []
+
+        javascriptBundler.bundle()
+
+        reportedState = javascriptBundler.report()
+        expectedState = self.JsState.NONE
+        self.assertEqual(reportedState, expectedState)
+
+    def _bundling_fails_and_there_is_no_existing_bundle_directory(self):
+        javascriptBundler = self.JavascriptBundler()
+        self.mockSubprocess.call.side_effect = OSError("mock exception behavior")
+        self.mockOs.path.exists.return_value = False
+        self.mockOs.listdir.side_effect = OSError("mock exception behavior")
+
+        javascriptBundler.bundle()
+
+        reportedState = javascriptBundler.report()
+        expectedState = self.JsState.NONE
+        self.assertEqual(reportedState, expectedState)
+
+    def _bundling_fails_but_there_was_existing_bundle(self):
+        javascriptBundler = self.JavascriptBundler()
+        self.mockSubprocess.call.side_effect = OSError("mock exception behavior")
+        self.mockOs.path.exists.return_value = True
+        self.mockOs.listdir.return_value = [u'history.js', u'reactComponents.js']
+
+        javascriptBundler.bundle()
+        self.mockSubprocess.call.assert_called_once_with(['yarn', 'run', 'bundle'])
+
+        reportedState = javascriptBundler.report()
+        expectedState = self.JsState.OLD
+        self.assertEqual(reportedState, expectedState)
diff --git a/web/regression/README b/web/regression/README
index f0d7282e..88edbd80 100644
--- a/web/regression/README
+++ b/web/regression/README
@@ -177,16 +177,17 @@ Javascript Tests:
 
     sudo port install nodejs7 yarn
 
+- See also the top-level pgadmin/README : Bundling Javascript
+
 - Javascript tests must be run from the web directory (since that is where node_modules and karma.conf reside):
 
     cd web/
 
 - Install the JS modules required for testing:
 
-    yarn
+    yarn install
 
 - Now run the tests:
 
-    yarn run karma start --single-run
-
-
+   yarn run test:karma
+   yarn run test:karma-once
diff --git a/web/regression/javascript/browser/menu_spec.js b/web/regression/javascript/browser/menu_spec.js
index 09f136fb..e13bfe5e 100644
--- a/web/regression/javascript/browser/menu_spec.js
+++ b/web/regression/javascript/browser/menu_spec.js
@@ -7,9 +7,11 @@
 //
 //////////////////////////////////////////////////////////////
 
-define(["browser/menu"
-], function () {
-  describe("MenuItem", function () {
+define([
+  'pgadmin',
+  'browser/menu',
+], function (pgAdmin) {
+  describe('MenuItem', function () {
     var MenuItem = pgAdmin.Browser.MenuItem;
     var menuItem;
 
diff --git a/web/regression/javascript/gettext_spec.js b/web/regression/javascript/gettext_spec.js
index 2ce98a23..54fc498d 100644
--- a/web/regression/javascript/gettext_spec.js
+++ b/web/regression/javascript/gettext_spec.js
@@ -7,45 +7,46 @@
 //
 //////////////////////////////////////////////////////////////////////////
 
-define(["sources/gettext", "translations"], function (gettext, translations) {
-  describe("translate", function () {
-    describe("when there is no translation", function () {
-      it("returns the original string", function () {
-        expect(gettext("something to be translated")).toEqual("something to be translated");
-      });
-
-      describe("when there are substitutions", function () {
-        it("interpolates a substitution", function () {
-          expect(gettext("translate text for %(person)s", {"person": "Sarah"})).toEqual("translate text for Sarah")
-        });
-
-        it("interpolates multiple substitutions", function () {
-          expect(gettext("translate '%(text)s' for %(person)s",
-            {
-              "text": "constitution",
-              "person": "Sarah"
-            }
-          )).toEqual("translate 'constitution' for Sarah")
-        });
-      });
+import gettext from 'sources/gettext';
+import translations from 'translations';
 
+describe('translate', function () {
+  describe('when there is no translation', function () {
+    it('returns the original string', function () {
+      expect(gettext('something to be translated')).toEqual('something to be translated');
     });
 
-    describe("when there is a translation", function () {
-      beforeEach(function () {
-        translations['something to be translated'] = 'etwas zum uebersetzen';
-        translations['another translation for %(person)s'] = 'eine weitere Uebersetzung fuer %(person)s';
+    describe('when there are substitutions', function () {
+      it('interpolates a substitution', function () {
+        expect(gettext('translate text for %(person)s', {'person': 'Sarah'})).toEqual('translate text for Sarah');
       });
 
-      it("returns the translation", function () {
-        expect(gettext("something to be translated")).toEqual("etwas zum uebersetzen");
+      it('interpolates multiple substitutions', function () {
+        expect(gettext('translate \'%(text)s\' for %(person)s',
+          {
+            'text': 'constitution',
+            'person': 'Sarah',
+          }
+        )).toEqual('translate \'constitution\' for Sarah');
       });
+    });
+
+  });
+
+  describe('when there is a translation', function () {
+    beforeEach(function () {
+      translations['something to be translated'] = 'etwas zum uebersetzen';
+      translations['another translation for %(person)s'] = 'eine weitere Uebersetzung fuer %(person)s';
+    });
+
+    it('returns the translation', function () {
+      expect(gettext('something to be translated')).toEqual('etwas zum uebersetzen');
+    });
 
-      describe("when there is a substitution", function () {
-        it("interpolates the substitution", function () {
-          expect(gettext("another translation for %(person)s", {"person": "Sarah"}))
-            .toEqual("eine weitere Uebersetzung fuer Sarah");
-        });
+    describe('when there is a substitution', function () {
+      it('interpolates the substitution', function () {
+        expect(gettext('another translation for %(person)s', {'person': 'Sarah'}))
+          .toEqual('eine weitere Uebersetzung fuer Sarah');
       });
     });
   });
diff --git a/web/regression/javascript/jasmine_capture_warnings_beforeall.js b/web/regression/javascript/jasmine_capture_warnings_beforeall.js
new file mode 100644
index 00000000..a83e6f61
--- /dev/null
+++ b/web/regression/javascript/jasmine_capture_warnings_beforeall.js
@@ -0,0 +1,23 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+/* eslint-disable no-console */
+
+beforeAll(function () {
+  spyOn(console, 'warn').and.callThrough();
+  spyOn(console, 'error').and.callThrough();
+});
+
+afterEach(function (done) {
+  setTimeout(function () {
+    expect(console.warn).not.toHaveBeenCalled();
+    expect(console.error).not.toHaveBeenCalled();
+    done();
+  }, 0);
+});
\ No newline at end of file
diff --git a/web/regression/javascript/selection/column_selector_spec.js b/web/regression/javascript/selection/column_selector_spec.js
index 7fa69aef..74d137a4 100644
--- a/web/regression/javascript/selection/column_selector_spec.js
+++ b/web/regression/javascript/selection/column_selector_spec.js
@@ -1,419 +1,423 @@
-define(
-  ["jquery",
-    "underscore",
-    "sources/selection/column_selector",
-    "sources/selection/active_cell_capture",
-    "sources/selection/grid_selector",
-    'sources/selection/xcell_selection_model',
-
-    "slickgrid",
-    'sources/slickgrid/pgslick.cellrangedecorator',
-    'sources/slickgrid/pgslick.cellrangeselector',
-    "slickgrid/slick.grid",
-  ],
-  function ($, _, ColumnSelector, ActiveCellCapture, GridSelector, XCellSelectionModel) {
-    var KEY_RIGHT = 39;
-    var KEY_LEFT = 37;
-    var KEY_UP = 38;
-    var KEY_DOWN = 40;
-
-    var Slick = window.Slick;
-    var SlickGrid = Slick.Grid;
-
-    describe("ColumnSelector", function () {
-      var container, data, columns, options;
-      beforeEach(function () {
-        container = $("<div></div>");
-        container.height(9999);
-        container.width(9999);
-
-        data = [{
-          'some-column-name': 'first value',
-          'second column': 'second value',
-          'third column': 'nonselectable value'
-        }, {
-          'some-column-name': 'row 1 - first value',
-          'second column': 'row 1 - second value',
-          'third column': 'row 1 - nonselectable value'
-        }];
-
-        columns = [
-          {
-            id: 'row-header-column',
-            name: 'row header column name',
-            selectable: false,
-            display_name: 'row header column name',
-            column_type: 'text'
-          },
-          {
-            id: '1',
-            name: 'some-column-name',
-            pos: 0,
-            display_name: 'some-column-name',
-            column_type: 'text'
-          },
-          {
-            id: '2',
-            name: 'second column',
-            pos: 1,
-            display_name: 'second column',
-            column_type: 'json'
-          },
-          {
-            id: 'third-column-id',
-            name: 'third column',
-            pos: 2,
-            display_name: 'third column',
-            column_type: 'text'
-          },
-          {
-            name: 'some-non-selectable-column',
-            selectable: false,
-            pos: 3,
-            display_name: 'some-non-selectable-column',
-            column_type: 'numeric'
-          }
-        ];
-      });
-
-      it("displays the name of the column", function () {
-        setupGrid(columns);
-
-        expect($(container.find('.slick-header-columns .slick-column-name')[1]).text())
-          .toContain('some-column-name');
-        expect($(container.find('.slick-header-columns .slick-column-name')[1]).text())
-          .toContain('text');
-        expect($(container.find('.slick-header-columns .slick-column-name')[2]).text())
-          .toContain('second column');
-        expect($(container.find('.slick-header-columns .slick-column-name')[2]).text())
-          .toContain('json');
-      });
-
-      it("preserves the other attributes of column definitions", function () {
-        var columnSelector = new ColumnSelector();
-        var selectableColumns = columnSelector.getColumnDefinitions(columns);
-
-        expect(selectableColumns[1].id).toBe('1');
-      });
-
-      describe("with ActiveCellCapture, CellSelectionModel, and GridSelector: selecting columns", function () {
-        var grid, cellSelectionModel;
-        beforeEach(function () {
-          var columnSelector = new ColumnSelector();
-          columns = columnSelector.getColumnDefinitions(columns);
-          data = [];
-          for (var i = 0; i < 10; i++) {
-            data.push({
-              'some-column-name': 'some-value-' + i,
-              'second column': 'second value ' + i,
-              'third column': 'third value ' + i,
-              'fourth column': 'fourth value ' + i,
-            });
-          }
-          grid = new SlickGrid(container, data, columns);
-
-          grid.registerPlugin(new ActiveCellCapture());
-          cellSelectionModel = new XCellSelectionModel();
-          grid.setSelectionModel(cellSelectionModel);
-
-          grid.registerPlugin(columnSelector);
-          grid.invalidate();
-          $("body").append(container);
-        });
-
-        afterEach(function () {
-          $("body").find(container).remove();
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+import $ from 'jquery';
+
+import Slick from 'slickgrid';
+import 'slickgrid.grid';
+
+import ColumnSelector from 'sources/selection/column_selector';
+import ActiveCellCapture from 'sources/selection/active_cell_capture';
+import 'sources/selection/grid_selector';
+import XCellSelectionModel from 'sources/selection/xcell_selection_model';
+import 'sources/slickgrid/pgslick.cellrangedecorator';
+import 'sources/slickgrid/pgslick.cellrangeselector';
+
+describe('ColumnSelector', function () {
+  var container, data, columns, options;
+  var SlickGrid = Slick.Grid;
+  var KEY_RIGHT = 39;
+  var KEY_LEFT = 37;
+  var KEY_UP = 38;
+  var KEY_DOWN = 40;
+
+  beforeEach(function () {
+    container = $('<div></div>');
+    container.height(9999);
+    container.width(9999);
+
+    data = [{
+      'some-column-name': 'first value',
+      'second column': 'second value',
+      'third column': 'nonselectable value'
+    }, {
+      'some-column-name': 'row 1 - first value',
+      'second column': 'row 1 - second value',
+      'third column': 'row 1 - nonselectable value'
+    }];
+
+    columns = [
+      {
+        id: 'row-header-column',
+        name: 'row header column name',
+        selectable: false,
+        display_name: 'row header column name',
+        column_type: 'text'
+      },
+      {
+        id: '1',
+        name: 'some-column-name',
+        pos: 0,
+        display_name: 'some-column-name',
+        column_type: 'text'
+      },
+      {
+        id: '2',
+        name: 'second column',
+        pos: 1,
+        display_name: 'second column',
+        column_type: 'json'
+      },
+      {
+        id: 'third-column-id',
+        name: 'third column',
+        pos: 2,
+        display_name: 'third column',
+        column_type: 'text'
+      },
+      {
+        name: 'some-non-selectable-column',
+        selectable: false,
+        pos: 3,
+        display_name: 'some-non-selectable-column',
+        column_type: 'numeric'
+      }
+    ];
+  });
+
+  it('displays the name of the column', function () {
+    setupGrid(columns);
+
+    expect($(container.find('.slick-header-columns .slick-column-name')[1]).text())
+      .toContain('some-column-name');
+    expect($(container.find('.slick-header-columns .slick-column-name')[1]).text())
+      .toContain('text');
+    expect($(container.find('.slick-header-columns .slick-column-name')[2]).text())
+      .toContain('second column');
+    expect($(container.find('.slick-header-columns .slick-column-name')[2]).text())
+      .toContain('json');
+  });
+
+  it('preserves the other attributes of column definitions', function () {
+    var columnSelector = new ColumnSelector();
+    var selectableColumns = columnSelector.getColumnDefinitions(columns);
+
+    expect(selectableColumns[1].id).toBe('1');
+  });
+
+  describe('with ActiveCellCapture, CellSelectionModel, and GridSelector: selecting columns', function () {
+    var grid, cellSelectionModel;
+    beforeEach(function () {
+      var columnSelector = new ColumnSelector();
+      columns = columnSelector.getColumnDefinitions(columns);
+      data = [];
+      for (var i = 0; i < 10; i++) {
+        data.push({
+          'some-column-name': 'some-value-' + i,
+          'second column': 'second value ' + i,
+          'third column': 'third value ' + i,
+          'fourth column': 'fourth value ' + i,
         });
+      }
+      grid = new SlickGrid(container, data, columns);
 
-        describe("when the user clicks a column header", function () {
-          it("selects the column", function () {
-            container.find('.slick-header-column:contains(some-column-name)').click();
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
-            expectOnlyTheFirstColumnToBeSelected(selectedRanges);
-          });
-
-          it("toggles a selected class to the header cell", function () {
-            container.find('.slick-header-column:contains(second column)').click();
-            expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
-              .toBe(true);
+      grid.registerPlugin(new ActiveCellCapture());
+      cellSelectionModel = new XCellSelectionModel();
+      grid.setSelectionModel(cellSelectionModel);
 
-            container.find('.slick-header-column:contains(second column)').click();
-            expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
-              .toBe(false);
-          });
-        });
+      grid.registerPlugin(columnSelector);
+      grid.invalidate();
+      $('body').append(container);
+    });
 
-        describe("when the user clicks an additional column header", function () {
-          beforeEach(function () {
-            container.find('.slick-header-column:contains(some-column-name)').click();
-            container.find('.slick-header-column:contains(second column)').click();
-          });
+    afterEach(function () {
+      $('body').find(container).remove();
+    });
 
-          it("selects additional columns", function () {
+    describe('when the user clicks a column header', function () {
+      it('selects the column', function () {
+        container.find('.slick-header-column:contains(some-column-name)').click();
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
+        expectOnlyTheFirstColumnToBeSelected(selectedRanges);
+      });
 
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
+      it('toggles a selected class to the header cell', function () {
+        container.find('.slick-header-column:contains(second column)').click();
+        expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
+          .toBe(true);
 
-            expect(selectedRanges.length).toBe(2);
-            var column1 = selectedRanges[0];
-            expect(column1.fromCell).toBe(1);
-            expect(column1.toCell).toBe(1);
+        container.find('.slick-header-column:contains(second column)').click();
+        expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
+          .toBe(false);
+      });
+    });
 
-            var column2 = selectedRanges[1];
-            expect(column2.fromCell).toBe(2);
-            expect(column2.toCell).toBe(2);
-          });
+    describe('when the user clicks an additional column header', function () {
+      beforeEach(function () {
+        container.find('.slick-header-column:contains(some-column-name)').click();
+        container.find('.slick-header-column:contains(second column)').click();
+      });
 
-          describe("and presses shift + right-arrow", function () {
-            beforeEach(function () {
-              pressShiftArrow(KEY_RIGHT);
-            });
-
-            it("keeps the last column selected", function () {
-              expect(cellSelectionModel.getSelectedRanges().length).toBe(1);
-            });
-
-            it("grows the selection to the right", function () {
-              var selectedRange = cellSelectionModel.getSelectedRanges()[0];
-              expect(selectedRange.fromCell).toBe(2);
-              expect(selectedRange.toCell).toBe(3);
-              expect(selectedRange.fromRow).toBe(0);
-              expect(selectedRange.toRow).toBe(9);
-            });
-
-            it("keeps selected class on columns 2 and 3", function () {
-              expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
-                .toBe(true);
-              expect($(container.find('.slick-header-column:contains(third column)')).hasClass('selected'))
-                .toBe(true);
-              expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
-                .toBe(false);
-            });
-          });
+      it('selects additional columns', function () {
 
-          describe("when the user deselects the last selected column header", function () {
-            beforeEach(function () {
-              container.find('.slick-header-column:contains(second column)').click();
-            });
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-            describe("and presses shift + right-arrow", function () {
-              it("first and second columns are selected", function () {
-                pressShiftArrow(KEY_RIGHT);
+        expect(selectedRanges.length).toBe(2);
+        var column1 = selectedRanges[0];
+        expect(column1.fromCell).toBe(1);
+        expect(column1.toCell).toBe(1);
 
-                var selectedRanges = cellSelectionModel.getSelectedRanges();
+        var column2 = selectedRanges[1];
+        expect(column2.fromCell).toBe(2);
+        expect(column2.toCell).toBe(2);
+      });
 
-                expect(selectedRanges.length).toBe(1);
-                expect(selectedRanges[0].fromCell).toBe(1);
-                expect(selectedRanges[0].toCell).toBe(2);
-                expect(selectedRanges[0].fromRow).toBe(0);
-                expect(selectedRanges[0].toRow).toBe(9);
-              });
-            });
-          });
+      describe('and presses shift + right-arrow', function () {
+        beforeEach(function () {
+          pressShiftArrow(KEY_RIGHT);
         });
 
-        describe("when the user clicks a column header description", function () {
-          it("selects the column", function () {
-            container.find('.slick-header-columns span.column-description:contains(some-column-name)').click();
+        it('keeps the last column selected', function () {
+          expect(cellSelectionModel.getSelectedRanges().length).toBe(1);
+        });
 
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
-            expectOnlyTheFirstColumnToBeSelected(selectedRanges);
-          });
+        it('grows the selection to the right', function () {
+          var selectedRange = cellSelectionModel.getSelectedRanges()[0];
+          expect(selectedRange.fromCell).toBe(2);
+          expect(selectedRange.toCell).toBe(3);
+          expect(selectedRange.fromRow).toBe(0);
+          expect(selectedRange.toRow).toBe(9);
+        });
 
-          it("toggles a selected class to the header cell", function () {
-            container.find('.slick-header-column span.column-description:contains(second column)').click();
-            expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
-              .toBe(true);
+        it('keeps selected class on columns 2 and 3', function () {
+          expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
+            .toBe(true);
+          expect($(container.find('.slick-header-column:contains(third column)')).hasClass('selected'))
+            .toBe(true);
+          expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
+            .toBe(false);
+        });
+      });
 
-            container.find('.slick-header-column span.column-description:contains(second column)').click();
-            expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
-              .toBe(false);
-          });
+      describe('when the user deselects the last selected column header', function () {
+        beforeEach(function () {
+          container.find('.slick-header-column:contains(second column)').click();
         });
 
-        describe("when a row is selected", function () {
-          beforeEach(function () {
-            var selectedRanges = [new Slick.Range(0, 0, 0, 1)];
-            cellSelectionModel.setSelectedRanges(selectedRanges);
-          });
+        describe('and presses shift + right-arrow', function () {
+          it('first and second columns are selected', function () {
+            pressShiftArrow(KEY_RIGHT);
 
-          it("deselects the row", function () {
-            container.find('.slick-header-column')[1].click();
             var selectedRanges = cellSelectionModel.getSelectedRanges();
 
             expect(selectedRanges.length).toBe(1);
-
-            var column = selectedRanges[0];
-
-            expect(column.fromCell).toBe(1);
-            expect(column.toCell).toBe(1);
-            expect(column.fromRow).toBe(0);
-            expect(column.toRow).toBe(9);
+            expect(selectedRanges[0].fromCell).toBe(1);
+            expect(selectedRanges[0].toCell).toBe(2);
+            expect(selectedRanges[0].fromRow).toBe(0);
+            expect(selectedRanges[0].toRow).toBe(9);
           });
         });
+      });
+    });
 
-        describe("clicking a second time", function () {
-          beforeEach(function () {
-            container.find('.slick-header-column')[1].click();
-          });
+    describe('when the user clicks a column header description', function () {
+      it('selects the column', function () {
+        container.find('.slick-header-columns span.column-description:contains(some-column-name)').click();
 
-          it("deselects the column", function () {
-            container.find('.slick-header-column')[1].click();
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
+        expectOnlyTheFirstColumnToBeSelected(selectedRanges);
+      });
 
-            expect(selectedRanges.length).toEqual(0);
-          })
-        });
+      it('toggles a selected class to the header cell', function () {
+        container.find('.slick-header-column span.column-description:contains(second column)').click();
+        expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
+          .toBe(true);
 
-        describe("when the column is not selectable", function () {
-          it("does not select the column", function () {
-            $(container.find('.slick-header-column:contains(some-non-selectable-column)')).click();
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
+        container.find('.slick-header-column span.column-description:contains(second column)').click();
+        expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
+          .toBe(false);
+      });
+    });
 
-            expect(selectedRanges.length).toEqual(0);
-          });
-        });
+    describe('when a row is selected', function () {
+      beforeEach(function () {
+        var selectedRanges = [new Slick.Range(0, 0, 0, 1)];
+        cellSelectionModel.setSelectedRanges(selectedRanges);
+      });
 
-        describe("when the column is deselected through setSelectedRanges", function () {
-          beforeEach(function () {
-            container.find('.slick-header-column')[1].click();
-          });
+      it('deselects the row', function () {
+        container.find('.slick-header-column')[1].click();
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-          it("removes selected class from header", function () {
-            cellSelectionModel.setSelectedRanges([]);
+        expect(selectedRanges.length).toBe(1);
 
-            expect($(container.find('.slick-header-column')[1]).hasClass('selected'))
-              .toBe(false);
-          });
-        });
+        var column = selectedRanges[0];
 
-        describe("when a non-column range was already selected", function () {
-          beforeEach(function () {
-            var selectedRanges = [new Slick.Range(0, 0, 2, 0)];
-            cellSelectionModel.setSelectedRanges(selectedRanges);
-          });
+        expect(column.fromCell).toBe(1);
+        expect(column.toCell).toBe(1);
+        expect(column.fromRow).toBe(0);
+        expect(column.toRow).toBe(9);
+      });
+    });
 
-          it("deselects the non-column range", function () {
-            container.find('.slick-header-column:contains(some-column-name)').click();
+    describe('clicking a second time', function () {
+      beforeEach(function () {
+        container.find('.slick-header-column')[1].click();
+      });
 
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
-            expectOnlyTheFirstColumnToBeSelected(selectedRanges);
-          });
-        });
+      it('deselects the column', function () {
+        container.find('.slick-header-column')[1].click();
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-        describe('when a column is selected', function () {
-          beforeEach(function () {
-            container.find('.slick-header-column:contains(some-column-name)').click();
-          });
+        expect(selectedRanges.length).toEqual(0);
+      })
+    });
 
-          describe('when the user click a cell on the current range', function () {
-            beforeEach(function () {
-              container.find('.slick-cell.l1.r1')[1].click();
-            });
+    describe('when the column is not selectable', function () {
+      it('does not select the column', function () {
+        $(container.find('.slick-header-column:contains(some-non-selectable-column)')).click();
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-            it('column is deselected', function () {
+        expect(selectedRanges.length).toEqual(0);
+      });
+    });
 
-              var selectedRanges = cellSelectionModel.getSelectedRanges();
+    describe('when the column is deselected through setSelectedRanges', function () {
+      beforeEach(function () {
+        container.find('.slick-header-column')[1].click();
+      });
 
-              expect(selectedRanges.length).toBe(1);
+      it('removes selected class from header', function () {
+        cellSelectionModel.setSelectedRanges([]);
 
-              var column = selectedRanges[0];
+        expect($(container.find('.slick-header-column')[1]).hasClass('selected'))
+          .toBe(false);
+      });
+    });
 
-              expect(column.fromCell).toBe(1);
-              expect(column.toCell).toBe(1);
-              expect(column.fromRow).toBe(1);
-              expect(column.toRow).toBe(1);
-            });
+    describe('when a non-column range was already selected', function () {
+      beforeEach(function () {
+        var selectedRanges = [new Slick.Range(0, 0, 2, 0)];
+        cellSelectionModel.setSelectedRanges(selectedRanges);
+      });
 
-            it('keep select class on column header', function () {
-              expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
-                .toBeTruthy();
-            });
-          });
+      it('deselects the non-column range', function () {
+        container.find('.slick-header-column:contains(some-column-name)').click();
 
-          describe('when the user click a cell outside the current range', function () {
-            beforeEach(function () {
-              container.find('.slick-cell.l2.r2')[2].click();
-            });
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
+        expectOnlyTheFirstColumnToBeSelected(selectedRanges);
+      });
+    });
 
-            it('column is deselected', function () {
+    describe('when a column is selected', function () {
+      beforeEach(function () {
+        container.find('.slick-header-column:contains(some-column-name)').click();
+      });
 
-              var selectedRanges = cellSelectionModel.getSelectedRanges();
+      describe('when the user click a cell on the current range', function () {
+        beforeEach(function () {
+          container.find('.slick-cell.l1.r1')[1].click();
+        });
 
-              expect(selectedRanges.length).toBe(1);
+        it('column is deselected', function () {
 
-              var column = selectedRanges[0];
+          var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-              expect(column.fromCell).toBe(2);
-              expect(column.toCell).toBe(2);
-              expect(column.fromRow).toBe(2);
-              expect(column.toRow).toBe(2);
-            });
+          expect(selectedRanges.length).toBe(1);
 
-            it('remove select class on "some-column-name" column header', function () {
-              expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
-                .toBeFalsy();
-              expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
-                .toBeTruthy();
-            });
-          });
+          var column = selectedRanges[0];
 
-          describe('when the user click in a row header', function () {
-            beforeEach(function () {
-              var selectedRanges = [new Slick.Range(1, 1, 1, 3)];
-              cellSelectionModel.setSelectedRanges(selectedRanges);
-            });
+          expect(column.fromCell).toBe(1);
+          expect(column.toCell).toBe(1);
+          expect(column.fromRow).toBe(1);
+          expect(column.toRow).toBe(1);
+        });
 
-            it('column is deselected', function () {
-              var selectedRanges = cellSelectionModel.getSelectedRanges();
+        it('keep select class on column header', function () {
+          expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
+            .toBeTruthy();
+        });
+      });
 
-              expect(selectedRanges.length).toBe(1);
+      describe('when the user click a cell outside the current range', function () {
+        beforeEach(function () {
+          container.find('.slick-cell.l2.r2')[2].click();
+        });
 
-              var column = selectedRanges[0];
+        it('column is deselected', function () {
 
-              expect(column.fromCell).toBe(1);
-              expect(column.toCell).toBe(3);
-              expect(column.fromRow).toBe(1);
-              expect(column.toRow).toBe(1);
-            });
+          var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-            it('no column should have the class "selected"', function () {
-              expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
-                .toBeFalsy();
-            });
-          });
+          expect(selectedRanges.length).toBe(1);
+
+          var column = selectedRanges[0];
+
+          expect(column.fromCell).toBe(2);
+          expect(column.toCell).toBe(2);
+          expect(column.fromRow).toBe(2);
+          expect(column.toRow).toBe(2);
+        });
+
+        it('remove select class on \'some-column-name\' column header', function () {
+          expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
+            .toBeFalsy();
+          expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
+            .toBeTruthy();
         });
       });
 
-      function setupGrid(columns) {
-        var columnSelector = new ColumnSelector();
-        columns = columnSelector.getColumnDefinitions(columns);
-        var grid = new SlickGrid(container, data, columns, options);
+      describe('when the user click in a row header', function () {
+        beforeEach(function () {
+          var selectedRanges = [new Slick.Range(1, 1, 1, 3)];
+          cellSelectionModel.setSelectedRanges(selectedRanges);
+        });
 
-        var cellSelectionModel = new XCellSelectionModel();
-        grid.setSelectionModel(cellSelectionModel);
+        it('column is deselected', function () {
+          var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-        grid.registerPlugin(columnSelector);
-        grid.invalidate();
-      }
+          expect(selectedRanges.length).toBe(1);
 
-      function expectOnlyTheFirstColumnToBeSelected(selectedRanges) {
-        var row = selectedRanges[0];
+          var column = selectedRanges[0];
 
-        expect(selectedRanges.length).toEqual(1);
-        expect(row.fromCell).toBe(1);
-        expect(row.toCell).toBe(1);
-        expect(row.fromRow).toBe(0);
-        expect(row.toRow).toBe(9);
-      }
-
-      function pressShiftArrow(keyCode) {
-        var pressEvent = new $.Event("keydown");
-        pressEvent.shiftKey = true;
-        pressEvent.ctrlKey = false;
-        pressEvent.altKey = false;
-        pressEvent.which = keyCode;
+          expect(column.fromCell).toBe(1);
+          expect(column.toCell).toBe(3);
+          expect(column.fromRow).toBe(1);
+          expect(column.toRow).toBe(1);
+        });
 
-        $(container.find('.grid-canvas')).trigger(pressEvent);
-      }
+        it('no column should have the class \'selected\'', function () {
+          expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
+            .toBeFalsy();
+        });
+      });
     });
-  });
\ No newline at end of file
+  });
+
+  function setupGrid(columns) {
+    var columnSelector = new ColumnSelector();
+    columns = columnSelector.getColumnDefinitions(columns);
+    var grid = new SlickGrid(container, data, columns, options);
+
+    var cellSelectionModel = new XCellSelectionModel();
+    grid.setSelectionModel(cellSelectionModel);
+
+    grid.registerPlugin(columnSelector);
+    grid.invalidate();
+  }
+
+  function expectOnlyTheFirstColumnToBeSelected(selectedRanges) {
+    var row = selectedRanges[0];
+
+    expect(selectedRanges.length).toEqual(1);
+    expect(row.fromCell).toBe(1);
+    expect(row.toCell).toBe(1);
+    expect(row.fromRow).toBe(0);
+    expect(row.toRow).toBe(9);
+  }
+
+  function pressShiftArrow(keyCode) {
+    var pressEvent = new $.Event('keydown');
+    pressEvent.shiftKey = true;
+    pressEvent.ctrlKey = false;
+    pressEvent.altKey = false;
+    pressEvent.which = keyCode;
+
+    $(container.find('.grid-canvas')).trigger(pressEvent);
+  }
+});
diff --git a/web/regression/javascript/selection/copy_data_spec.js b/web/regression/javascript/selection/copy_data_spec.js
index 561576ce..2d6b6ad2 100644
--- a/web/regression/javascript/selection/copy_data_spec.js
+++ b/web/regression/javascript/selection/copy_data_spec.js
@@ -1,139 +1,142 @@
-/////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
 //
 // pgAdmin 4 - PostgreSQL Tools
 //
 // Copyright (C) 2013 - 2017, The pgAdmin Development Team
 // This software is released under the PostgreSQL Licence
 //
-//////////////////////////////////////////////////////////////
-
-define(
-  ["jquery",
-    "slickgrid/slick.grid",
-    "sources/selection/xcell_selection_model",
-    "sources/selection/copy_data",
-    "sources/selection/clipboard",
-    "sources/selection/range_selection_helper"
-  ],
-  function ($, SlickGrid, XCellSelectionModel, copyData, clipboard, RangeSelectionHelper) {
-    describe('copyData', function () {
-      var grid, sqlEditor, gridContainer, buttonPasteRow;
-
-      beforeEach(function () {
-        var data = [[1, "leopord", "12"],
-          [2, "lion", "13"],
-          [3, "puma", "9"]];
-
-        var columns = [
-          {
-            id: 'row-header-column',
-            name: 'row header column name',
-            selectable: false,
-            display_name: 'row header column name',
-            column_type: 'text'
-          },
-          {
-            name: "id",
-            pos: 0,
-            label: "id<br> numeric",
-            cell: "number",
-            can_edit: false,
-            type: "numeric"
-          }, {
-            name: "brand",
-            pos: 1,
-            label: "flavor<br> character varying",
-            cell: "string",
-            can_edit: false,
-            type: "character varying"
-          }, {
-            name: "size",
-            pos: 2,
-            label: "size<br> numeric",
-            cell: "number",
-            can_edit: false,
-            type: "numeric"
-          }
-          ]
-        ;
-        gridContainer = $("<div id='grid'></div>");
-        $("body").append(gridContainer);
-        buttonPasteRow = $("<button id='btn-paste-row' disabled></button>");
-        $("body").append(buttonPasteRow);
-        grid = new Slick.Grid("#grid", data, columns, {});
-        grid.setSelectionModel(new XCellSelectionModel());
-        sqlEditor = {slickgrid: grid};
-      });
+//////////////////////////////////////////////////////////////////////////
+
+import $ from 'jquery';
+
+import Slick from 'slickgrid';
+import 'slickgrid.grid';
+
+import clipboard from '../../../pgadmin/static/js/selection/clipboard';
+import copyData from '../../../pgadmin/static/js/selection/copy_data';
+import RangeSelectionHelper from 'sources/selection/range_selection_helper';
+import XCellSelectionModel from 'sources/selection/xcell_selection_model';
+
+describe('copyData', function () {
+  var grid, sqlEditor, gridContainer, buttonPasteRow;
+  var Slick, SlickGrid;
+
+  beforeEach(function () {
+    Slick = window.Slick;
+    SlickGrid = Slick.Grid;
+    var data = [[1, 'leopord', '12'],
+      [2, 'lion', '13'],
+      [3, 'puma', '9']];
+
+    var columns = [
+        {
+          id: 'row-header-column',
+          name: 'row header column name',
+          selectable: false,
+          display_name: 'row header column name',
+          column_type: 'text'
+        },
+        {
+          name: 'id',
+          pos: 0,
+          label: 'id<br> numeric',
+          cell: 'number',
+          can_edit: false,
+          type: 'numeric'
+        }, {
+          name: 'brand',
+          pos: 1,
+          label: 'flavor<br> character varying',
+          cell: 'string',
+          can_edit: false,
+          type: 'character varying'
+        }, {
+          name: 'size',
+          pos: 2,
+          label: 'size<br> numeric',
+          cell: 'number',
+          can_edit: false,
+          type: 'numeric'
+        }
+      ]
+    ;
+    gridContainer = $('<div id=\'grid\'></div>');
+    $('body').append(gridContainer);
+    buttonPasteRow = $('<button id=\'btn-paste-row\' disabled></button>');
+    $('body').append(buttonPasteRow);
+    grid = new Slick.Grid('#grid', data, columns, {});
+    grid.setSelectionModel(new XCellSelectionModel());
+    sqlEditor = {slickgrid: grid};
+  });
 
-      afterEach(function() {
-        gridContainer.remove();
-        buttonPasteRow.remove();
-      });
+  afterEach(function () {
+    gridContainer.remove();
+    buttonPasteRow.remove();
+  });
 
-      describe("when rows are selected", function () {
-        beforeEach(function () {
-          grid.getSelectionModel().setSelectedRanges([
-            RangeSelectionHelper.rangeForRow(grid, 0),
-            RangeSelectionHelper.rangeForRow(grid, 2)]
-          );
-        });
+  describe('when rows are selected', function () {
+    beforeEach(function () {
+      grid.getSelectionModel().setSelectedRanges([
+        RangeSelectionHelper.rangeForRow(grid, 0),
+        RangeSelectionHelper.rangeForRow(grid, 2)]
+      );
+    });
 
-        it("copies them", function () {
-          spyOn(clipboard, 'copyTextToClipboard');
+    it('copies them', function () {
+      spyOn(clipboard, 'copyTextToClipboard');
 
-          copyData.apply(sqlEditor);
+      copyData.apply(sqlEditor);
 
-          expect(sqlEditor.copied_rows.length).toBe(2);
+      expect(sqlEditor.copied_rows.length).toBe(2);
 
-          expect(clipboard.copyTextToClipboard).toHaveBeenCalled();
-          expect(clipboard.copyTextToClipboard.calls.mostRecent().args[0]).toContain("1,'leopord','12'");
-          expect(clipboard.copyTextToClipboard.calls.mostRecent().args[0]).toContain("3,'puma','9'");
-        });
+      expect(clipboard.copyTextToClipboard).toHaveBeenCalled();
+      expect(clipboard.copyTextToClipboard.calls.mostRecent().args[0]).toContain('1,\'leopord\',\'12\'');
+      expect(clipboard.copyTextToClipboard.calls.mostRecent().args[0]).toContain('3,\'puma\',\'9\'');
+    });
 
-        describe("when the user can edit the grid", function () {
-          it("enables the paste row button", function () {
-            copyData.apply(_.extend({can_edit: true}, sqlEditor));
+    describe('when the user can edit the grid', function () {
+      it('enables the paste row button', function () {
+        copyData.apply(_.extend({can_edit: true}, sqlEditor));
 
-            expect($("#btn-paste-row").prop('disabled')).toBe(false);
-          });
-        });
+        expect($('#btn-paste-row').prop('disabled')).toBe(false);
       });
+    });
+  });
 
-      describe("when a column is selected", function () {
-        beforeEach(function () {
-          var firstDataColumn = RangeSelectionHelper.rangeForColumn(grid, 1);
-          grid.getSelectionModel().setSelectedRanges([firstDataColumn])
-        });
+  describe('when a column is selected', function () {
+    beforeEach(function () {
+      var firstDataColumn = RangeSelectionHelper.rangeForColumn(grid, 1);
+      grid.getSelectionModel().setSelectedRanges([firstDataColumn]);
+    });
 
-        it("copies text to the clipboard", function () {
-          spyOn(clipboard, 'copyTextToClipboard');
+    it('copies text to the clipboard', function () {
+      spyOn(clipboard, 'copyTextToClipboard');
 
-          copyData.apply(sqlEditor);
+      copyData.apply(sqlEditor);
 
-          expect(clipboard.copyTextToClipboard).toHaveBeenCalled();
+      expect(clipboard.copyTextToClipboard).toHaveBeenCalled();
 
-          var copyArg = clipboard.copyTextToClipboard.calls.mostRecent().args[0];
-          var rowStrings = copyArg.split('\n');
-          expect(rowStrings[0]).toBe("1");
-          expect(rowStrings[1]).toBe("2");
-          expect(rowStrings[2]).toBe("3");
-        });
+      var copyArg = clipboard.copyTextToClipboard.calls.mostRecent().args[0];
+      var rowStrings = copyArg.split('\n');
+      expect(rowStrings[0]).toBe('1');
+      expect(rowStrings[1]).toBe('2');
+      expect(rowStrings[2]).toBe('3');
+    });
 
-        it("sets copied_rows to empty", function () {
-          copyData.apply(sqlEditor);
+    it('sets copied_rows to empty', function () {
+      copyData.apply(sqlEditor);
 
-          expect(sqlEditor.copied_rows.length).toBe(0);
-        });
+      expect(sqlEditor.copied_rows.length).toBe(0);
+    });
 
-        describe("when the user can edit the grid", function () {
-          beforeEach(function () {
-            copyData.apply(_.extend({can_edit: true}, sqlEditor));
-          });
+    describe('when the user can edit the grid', function () {
+      beforeEach(function () {
+        copyData.apply(_.extend({can_edit: true}, sqlEditor));
+      });
 
-          it("disables the paste row button", function () {
-            expect($("#btn-paste-row").prop('disabled')).toBe(true);
-          });
-        });
+      it('disables the paste row button', function () {
+        expect($('#btn-paste-row').prop('disabled')).toBe(true);
       });
     });
   });
+});
diff --git a/web/regression/javascript/selection/grid_selector_spec.js b/web/regression/javascript/selection/grid_selector_spec.js
index d79d417f..f7b39ee8 100644
--- a/web/regression/javascript/selection/grid_selector_spec.js
+++ b/web/regression/javascript/selection/grid_selector_spec.js
@@ -1,117 +1,130 @@
-define(["jquery",
-    "underscore",
-    "slickgrid/slick.grid",
-    "sources/selection/xcell_selection_model",
-    "sources/selection/grid_selector"
-  ],
-  function ($, _, SlickGrid, XCellSelectionModel, GridSelector) {
-    describe("GridSelector", function () {
-      var container, data, columns, gridSelector, xCellSelectionModel;
-
-      beforeEach(function () {
-        container = $("<div></div>");
-        container.height(9999);
-        columns = [{
-          id: '1',
-          name: 'some-column-name',
-          pos: 0
-        }, {
-          id: '2',
-          name: 'second column',
-          pos: 1
-        }];
-
-        gridSelector = new GridSelector();
-        columns = gridSelector.getColumnDefinitions(columns);
-
-        data = [];
-        for (var i = 0; i < 10; i++) {
-          data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
-        }
-        var grid = new SlickGrid(container, data, columns);
-
-        xCellSelectionModel = new XCellSelectionModel();
-        grid.setSelectionModel(xCellSelectionModel);
-
-        grid.registerPlugin(gridSelector);
-        grid.invalidate();
-
-        $("body").append(container);
-      });
-
-      afterEach(function () {
-        $("body").find(container).remove();
-      });
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+import $ from 'jquery';
+import _ from 'underscore';
+
+import Slick from 'slickgrid';
+import 'slickgrid.grid';
+
+import GridSelector from 'sources/selection/grid_selector';
+import XCellSelectionModel from 'sources/selection/xcell_selection_model';
+
+describe('GridSelector', function () {
+  var container, data, columns, gridSelector, xCellSelectionModel;
+  var Slick, SlickGrid;
+
+  beforeEach(function () {
+    Slick = window.Slick;
+    SlickGrid = Slick.Grid;
+    container = $('<div></div>');
+    container.height(9999);
+    columns = [{
+      id: '1',
+      name: 'some-column-name',
+      pos: 0
+    }, {
+      id: '2',
+      name: 'second column',
+      pos: 1
+    }];
+
+    gridSelector = new GridSelector();
+    columns = gridSelector.getColumnDefinitions(columns);
+
+    data = [];
+    for (var i = 0; i < 10; i++) {
+      data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
+    }
+    var grid = new Slick.Grid(container, data, columns);
+
+    xCellSelectionModel = new XCellSelectionModel();
+    grid.setSelectionModel(xCellSelectionModel);
+
+    grid.registerPlugin(gridSelector);
+    grid.invalidate();
+
+    $('body').append(container);
+  });
 
-      it("renders an additional column on the left for selecting rows", function () {
-        expect(columns.length).toBe(3);
+  afterEach(function () {
+    $('body').find(container).remove();
+  });
 
-        var leftmostColumn = columns[0];
-        expect(leftmostColumn.id).toBe('row-header-column');
-      });
+  it('renders an additional column on the left for selecting rows', function () {
+    expect(columns.length).toBe(3);
 
-      it("renders a button for selecting all the cells", function () {
-        expect(container.find("[title='Select/Deselect All']").length).toBe(1);
-      });
+    var leftmostColumn = columns[0];
+    expect(leftmostColumn.id).toBe('row-header-column');
+  });
 
-      describe("when the cell for the select/deselect all is clicked", function () {
-        it("selects the whole grid", function () {
-          container.find("[title='Select/Deselect All']").parent().click();
+  it('renders a button for selecting all the cells', function () {
+    expect(container.find('[title=\'Select/Deselect All\']').length).toBe(1);
+  });
 
-          var selectedRanges = xCellSelectionModel.getSelectedRanges();
-          expect(selectedRanges.length).toBe(1);
-          var selectedRange = selectedRanges[0];
-          expect(selectedRange.fromCell).toBe(1);
-          expect(selectedRange.toCell).toBe(2);
-          expect(selectedRange.fromRow).toBe(0);
-          expect(selectedRange.toRow).toBe(9);
-        });
+  describe('when the cell for the select/deselect all is clicked', function () {
+    it('selects the whole grid', function () {
+      container.find('[title=\'Select/Deselect All\']').parent().click();
+
+      var selectedRanges = xCellSelectionModel.getSelectedRanges();
+      expect(selectedRanges.length).toBe(1);
+      var selectedRange = selectedRanges[0];
+      expect(selectedRange.fromCell).toBe(1);
+      expect(selectedRange.toCell).toBe(2);
+      expect(selectedRange.fromRow).toBe(0);
+      expect(selectedRange.toRow).toBe(9);
+    });
 
-        it("adds selected class", function () {
-          container.find("[title='Select/Deselect All']").parent().click();
+    it('adds selected class', function () {
+      container.find('[title=\'Select/Deselect All\']').parent().click();
 
-          expect($(container.find("[data-id='select-all']")).hasClass('selected')).toBeTruthy();
-        });
-      });
+      expect($(container.find('[data-id=\'select-all\']')).hasClass('selected')).toBeTruthy();
+    });
+  });
 
-      describe("when the select all button in the corner gets selected", function () {
+  describe('when the select all button in the corner gets selected', function () {
 
-        it("selects all the cells", function () {
-          container.find("[title='Select/Deselect All']").click();
+    it('selects all the cells', function () {
+      container.find('[title=\'Select/Deselect All\']').click();
 
-          var selectedRanges = xCellSelectionModel.getSelectedRanges();
-          expect(selectedRanges.length).toBe(1);
-          var selectedRange = selectedRanges[0];
-          expect(selectedRange.fromCell).toBe(1);
-          expect(selectedRange.toCell).toBe(2);
-          expect(selectedRange.fromRow).toBe(0);
-          expect(selectedRange.toRow).toBe(9);
-        });
+      var selectedRanges = xCellSelectionModel.getSelectedRanges();
+      expect(selectedRanges.length).toBe(1);
+      var selectedRange = selectedRanges[0];
+      expect(selectedRange.fromCell).toBe(1);
+      expect(selectedRange.toCell).toBe(2);
+      expect(selectedRange.fromRow).toBe(0);
+      expect(selectedRange.toRow).toBe(9);
+    });
 
-        describe("when the select all button in the corner gets deselected", function () {
-          beforeEach(function () {
-            container.find("[title='Select/Deselect All']").click();
-          });
+    describe('when the select all button in the corner gets deselected', function () {
+      beforeEach(function () {
+        container.find('[title=\'Select/Deselect All\']').click();
+      });
 
-          it("deselects all the cells", function () {
-            container.find("[title='Select/Deselect All']").click();
+      it('deselects all the cells', function () {
+        container.find('[title=\'Select/Deselect All\']').click();
 
-            var selectedRanges = xCellSelectionModel.getSelectedRanges();
-            expect(selectedRanges.length).toBe(0);
-          });
-        });
+        var selectedRanges = xCellSelectionModel.getSelectedRanges();
+        expect(selectedRanges.length).toBe(0);
+      });
+    });
 
-        describe("and then the underlying selection changes", function () {
-          beforeEach(function () {
-            container.find("[title='Select/Deselect All']").click();
-          });
+    describe('and then the underlying selection changes', function () {
+      beforeEach(function () {
+        container.find('[title=\'Select/Deselect All\']').click();
+      });
 
-          it("removes the selected class", function () {
-            container.find("[title='Select/Deselect All']").parent().click();
+      it('removes the selected class', function () {
+        container.find('[title=\'Select/Deselect All\']').parent().click();
 
-            expect($(container.find("[data-id='select-all']")).hasClass('selected')).toBeFalsy();
-          });
-        });
+        expect($(container.find('[data-id=\'select-all\']')).hasClass('selected')).toBeFalsy();
       });
     });
   });
+});
diff --git a/web/regression/javascript/selection/range_boundary_navigator_spec.js b/web/regression/javascript/selection/range_boundary_navigator_spec.js
index b1ca45eb..b57087f3 100644
--- a/web/regression/javascript/selection/range_boundary_navigator_spec.js
+++ b/web/regression/javascript/selection/range_boundary_navigator_spec.js
@@ -1,8 +1,20 @@
-define(['sources/selection/range_boundary_navigator'], function (rangeBoundaryNavigator) {
-
-  describe("#getUnion", function () {
-    describe("when the ranges completely overlap", function () {
-      it("returns a list with that range", function () {
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+import rangeBoundaryNavigator from 'sources/selection/range_boundary_navigator';
+import Slick from 'slickgrid';
+
+describe('RangeBoundaryNavigator', function () {
+
+  describe('#getUnion', function () {
+    describe('when the ranges completely overlap', function () {
+      it('returns a list with that range', function () {
         var ranges = [[1, 4], [1, 4], [1, 4]];
 
         var union = rangeBoundaryNavigator.getUnion(ranges);
diff --git a/web/regression/javascript/selection/range_selection_helper_spec.js b/web/regression/javascript/selection/range_selection_helper_spec.js
index cc81189b..63c07259 100644
--- a/web/regression/javascript/selection/range_selection_helper_spec.js
+++ b/web/regression/javascript/selection/range_selection_helper_spec.js
@@ -1,70 +1,82 @@
-define([
-  'jquery',
-  'slickgrid/slick.grid',
-  'sources/selection/range_selection_helper'
-], function ($, SlickGrid, RangeSelectionHelper) {
-  describe("RangeSelectionHelper utility functions", function () {
-    var grid;
-    beforeEach(function () {
-      var container, data, columns, options;
-      container = $("<div></div>");
-      container.height(9999);
+import $ from 'jquery';
+import Slick from 'slickgrid';
+import 'slickgrid.grid';
+import RangeSelectionHelper from 'sources/selection/range_selection_helper';
 
-      columns = [{
-        id: '1',
-        name: 'some-column-name',
-        pos: 0
-      }, {
-        id: 'second-column-id',
-        name: 'second column',
-        pos: 1
-      }];
+describe("RangeSelectionHelper utility functions", function () {
+  var grid;
+  beforeEach(function () {
+    var container, data, columns, options;
+    container = $("<div></div>");
+    container.height(9999);
 
-      data = [];
-      for (var i = 0; i < 10; i++) {
-        data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
-      }
+    columns = [{
+      id: '1',
+      name: 'some-column-name',
+      pos: 0
+    }, {
+      id: 'second-column-id',
+      name: 'second column',
+      pos: 1
+    }];
 
-      grid = new SlickGrid(container, data, columns, options);
-      grid.invalidate();
-    });
+    data = [];
+    for (var i = 0; i < 10; i++) {
+      data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
+    }
+
+    grid = new Slick.Grid(container, data, columns, options);
+    grid.invalidate();
+  });
 
-    describe("#getIndexesOfCompleteRows", function () {
-      describe("when selected ranges are not rows", function () {
-        it("returns an empty array", function () {
-          var rowlessRanges = [RangeSelectionHelper.rangeForColumn(grid, 1)];
+  describe("#getIndexesOfCompleteRows", function () {
+    describe("when selected ranges are not rows", function () {
+      it("returns an empty array", function () {
+        var rowlessRanges = [RangeSelectionHelper.rangeForColumn(grid, 1)];
 
-          expect(RangeSelectionHelper.getIndexesOfCompleteRows(grid, rowlessRanges))
-            .toEqual([]);
+        expect(RangeSelectionHelper.getIndexesOfCompleteRows(grid, rowlessRanges))
+          .toEqual([]);
+      });
+    });
+    describe("when selected range", function () {
+      describe("is a single row", function () {
+        it("returns an array with one index", function () {
+          var singleRowRange = [RangeSelectionHelper.rangeForRow(grid, 1)];
+
+          expect(RangeSelectionHelper.getIndexesOfCompleteRows(grid, singleRowRange))
+            .toEqual([1]);
         });
       });
-      describe("when selected range", function () {
-        describe("is a single row", function () {
-          it("returns an array with one index", function () {
-            var singleRowRange = [RangeSelectionHelper.rangeForRow(grid, 1)];
 
-            expect(RangeSelectionHelper.getIndexesOfCompleteRows(grid, singleRowRange))
-              .toEqual([1]);
-          });
+      describe("is multiple rows", function () {
+        it("returns an array of each row's index", function () {
+          var multipleRowRange = [
+            RangeSelectionHelper.rangeForRow(grid, 0),
+            RangeSelectionHelper.rangeForRow(grid, 3),
+            RangeSelectionHelper.rangeForRow(grid, 2),
+          ];
+
+          var indexesOfCompleteRows = RangeSelectionHelper.getIndexesOfCompleteRows(grid, multipleRowRange);
+          indexesOfCompleteRows.sort();
+          expect(indexesOfCompleteRows).toEqual([0, 2, 3]);
         });
+      });
 
-        describe("is multiple rows", function () {
-          it("returns an array of each row's index", function () {
-            var multipleRowRange = [
-              RangeSelectionHelper.rangeForRow(grid, 0),
-              RangeSelectionHelper.rangeForRow(grid, 3),
-              RangeSelectionHelper.rangeForRow(grid, 2),
-            ];
+      describe("contains a multi row selection", function () {
+        it("returns an array of each individual row's index", function () {
+          var multipleRowRange = [
+            new Slick.Range(3, 0, 5, 1)
+          ];
 
-            var indexesOfCompleteRows = RangeSelectionHelper.getIndexesOfCompleteRows(grid, multipleRowRange);
-            indexesOfCompleteRows.sort();
-            expect(indexesOfCompleteRows).toEqual([0, 2, 3]);
-          });
+          var indexesOfCompleteRows = RangeSelectionHelper.getIndexesOfCompleteRows(grid, multipleRowRange);
+          indexesOfCompleteRows.sort();
+          expect(indexesOfCompleteRows).toEqual([3, 4, 5]);
         });
 
-        describe("contains a multi row selection", function () {
-          it("returns an array of each individual row's index", function () {
+        describe("and also contains a selection that is not a row", function () {
+          it("returns an array of only the complete rows' indexes", function () {
             var multipleRowRange = [
+              new Slick.Range(8, 1, 9, 1),
               new Slick.Range(3, 0, 5, 1)
             ];
 
@@ -72,22 +84,8 @@ define([
             indexesOfCompleteRows.sort();
             expect(indexesOfCompleteRows).toEqual([3, 4, 5]);
           });
-
-          describe("and also contains a selection that is not a row", function () {
-            it("returns an array of only the complete rows' indexes", function () {
-              var multipleRowRange = [
-                new Slick.Range(8, 1, 9, 1),
-                new Slick.Range(3, 0, 5, 1)
-              ];
-
-              var indexesOfCompleteRows = RangeSelectionHelper.getIndexesOfCompleteRows(grid, multipleRowRange);
-              indexesOfCompleteRows.sort();
-              expect(indexesOfCompleteRows).toEqual([3, 4, 5]);
-            });
-          });
         });
       });
     });
   });
-
 });
diff --git a/web/regression/javascript/selection/row_selector_spec.js b/web/regression/javascript/selection/row_selector_spec.js
index 23a5b12e..dbf65e26 100644
--- a/web/regression/javascript/selection/row_selector_spec.js
+++ b/web/regression/javascript/selection/row_selector_spec.js
@@ -1,321 +1,327 @@
-define(
-  ["jquery",
-    "underscore",
-    "slickgrid/slick.grid",
-    "sources/selection/active_cell_capture",
-    "sources/selection/row_selector",
-    'sources/selection/xcell_selection_model',
-
-    "slickgrid",
-    'sources/slickgrid/pgslick.cellrangedecorator',
-    'sources/slickgrid/pgslick.cellrangeselector',
-  ],
-  function ($, _, SlickGrid, ActiveCellCapture, RowSelector, XCellSelectionModel, Slick) {
-    var KEY_RIGHT = 39;
-    var KEY_LEFT = 37;
-    var KEY_UP = 38;
-    var KEY_DOWN = 40;
-    describe("RowSelector", function () {
-      var container, data, columnDefinitions, grid, cellSelectionModel;
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+import $ from 'jquery';
+import _ from 'underscore';
+
+import Slick from 'slickgrid';
+import 'slickgrid.grid';
+
+import RowSelector from 'sources/selection/row_selector';
+import ActiveCellCapture from 'sources/selection/active_cell_capture';
+import XCellSelectionModel from 'sources/selection/xcell_selection_model';
+
+describe('RowSelector', function () {
+  var KEY_RIGHT = 39;
+  var KEY_LEFT = 37;
+  var KEY_UP = 38;
+  var KEY_DOWN = 40;
+  var container, data, columnDefinitions, grid, cellSelectionModel;
+  var SlickGrid = Slick.Grid;
+
+  beforeEach(function () {
+    container = $('<div></div>');
+    container.height(9999);
+    container.width(9999);
+
+    columnDefinitions = [{
+      id: '1',
+      name: 'some-column-name',
+      selectable: true,
+      pos: 0
+    }, {
+      id: '2',
+      name: 'second column',
+      selectable: true,
+      pos: 1
+    }];
+
+    var rowSelector = new RowSelector();
+    data = [];
+    for (var i = 0; i < 10; i++) {
+      data.push(['some-value-' + i, 'second value ' + i]);
+    }
+    columnDefinitions = rowSelector.getColumnDefinitions(columnDefinitions);
+    grid = new SlickGrid(container, data, columnDefinitions);
 
-      beforeEach(function () {
-        container = $("<div></div>");
-        container.height(9999);
-        container.width(9999);
-
-        columnDefinitions = [{
-          id: '1',
-          name: 'some-column-name',
-          selectable: true,
-          pos: 0
-        }, {
-          id: '2',
-          name: 'second column',
-          selectable: true,
-          pos: 1
-        }];
-
-        var rowSelector = new RowSelector();
-        data = [];
-        for (var i = 0; i < 10; i++) {
-          data.push(['some-value-' + i, 'second value ' + i]);
-        }
-        columnDefinitions = rowSelector.getColumnDefinitions(columnDefinitions);
-        grid = new SlickGrid(container, data, columnDefinitions);
-
-        grid.registerPlugin(new ActiveCellCapture());
-        cellSelectionModel = new XCellSelectionModel();
-        grid.setSelectionModel(cellSelectionModel);
-
-        grid.registerPlugin(rowSelector);
-        grid.invalidate();
-
-        $("body").append(container);
+    grid.registerPlugin(new ActiveCellCapture());
+    cellSelectionModel = new XCellSelectionModel();
+    grid.setSelectionModel(cellSelectionModel);
+
+    grid.registerPlugin(rowSelector);
+    grid.invalidate();
+
+    $('body').append(container);
+  });
+
+  afterEach(function () {
+    $('body').find(container).remove();
+  });
+
+  it('renders an additional column on the left', function () {
+    expect(columnDefinitions.length).toBe(3);
+
+    var leftmostColumn = columnDefinitions[0];
+    expect(leftmostColumn.id).toBe('row-header-column');
+    expect(leftmostColumn.name).toBe('');
+    expect(leftmostColumn.selectable).toBe(false);
+  });
+
+  it('renders a span on the leftmost column', function () {
+    expect(container.find('.slick-row').length).toBe(10);
+    expect(container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]').length).toBe(10);
+  });
+
+  it('preserves the other attributes of column definitions', function () {
+    expect(columnDefinitions[1].id).toBe('1');
+    expect(columnDefinitions[1].selectable).toBe(true);
+  });
+
+  describe('selecting rows', function () {
+    describe('when the user clicks a row header span', function () {
+      it('selects the row', function () {
+        container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[0].click();
+
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
+        expectOnlyTheFirstRowToBeSelected(selectedRanges);
       });
 
-      afterEach(function () {
-        $("body").find(container).remove();
+      it('add selected class to parent of the span', function () {
+        container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[5].click();
+
+        expect($(container.find('.slick-row .slick-cell:first-child ')[5])
+          .hasClass('selected')).toBeTruthy();
       });
+    });
 
-      it("renders an additional column on the left", function () {
-        expect(columnDefinitions.length).toBe(3);
+    describe('when the user clicks a row header', function () {
+      beforeEach(function () {
+        container.find('.slick-row .slick-cell:first-child')[1].click();
 
-        var leftmostColumn = columnDefinitions[0];
-        expect(leftmostColumn.id).toBe('row-header-column');
-        expect(leftmostColumn.name).toBe('');
-        expect(leftmostColumn.selectable).toBe(false);
       });
+      it('selects the row', function () {
 
-      it("renders a span on the leftmost column", function () {
-        expect(container.find('.slick-row').length).toBe(10);
-        expect(container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]').length).toBe(10);
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
+        var row = selectedRanges[0];
+
+        expect(selectedRanges.length).toEqual(1);
+        expect(row.fromCell).toBe(1);
+        expect(row.toCell).toBe(2);
+        expect(row.fromRow).toBe(1);
+        expect(row.toRow).toBe(1);
       });
 
-      it("preserves the other attributes of column definitions", function () {
-        expect(columnDefinitions[1].id).toBe('1');
-        expect(columnDefinitions[1].selectable).toBe(true);
+      it('add selected class to parent of the span', function () {
+
+        expect($(container.find('.slick-row .slick-cell:first-child ')[1])
+          .hasClass('selected')).toBeTruthy();
       });
 
-      describe("selecting rows", function () {
-        describe("when the user clicks a row header span", function () {
-          it("selects the row", function () {
-            container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[0].click();
+      describe('when the user clicks again the same row header', function () {
+        it('add selected class to parent of the span', function () {
+          container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[1].click();
 
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
-            expectOnlyTheFirstRowToBeSelected(selectedRanges);
-          });
+          expect($(container.find('.slick-row .slick-cell:first-child ')[1])
+            .hasClass('selected')).toBeFalsy();
+        });
+      });
 
-          it("add selected class to parent of the span", function () {
-            container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[5].click();
+      describe('and presses shift + down-arrow', function () {
+        beforeEach(function () {
+          pressShiftArrow(KEY_DOWN);
+        });
 
-            expect($(container.find('.slick-row .slick-cell:first-child ')[5])
-              .hasClass('selected')).toBeTruthy();
-          });
+        it('keeps the last row selected', function () {
+          expect(cellSelectionModel.getSelectedRanges().length).toBe(1);
         });
 
-        describe("when the user clicks a row header", function () {
-          beforeEach(function () {
-            container.find('.slick-row .slick-cell:first-child')[1].click();
+        it('grows the selection down', function () {
+          var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-          });
-          it("selects the row", function () {
+          var row = selectedRanges[0];
 
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
-            var row = selectedRanges[0];
+          expect(selectedRanges.length).toEqual(1);
+          expect(row.fromCell).toBe(1);
+          expect(row.toCell).toBe(2);
+          expect(row.fromRow).toBe(1);
+          expect(row.toRow).toBe(2);
+        });
 
-            expect(selectedRanges.length).toEqual(1);
-            expect(row.fromCell).toBe(1);
-            expect(row.toCell).toBe(2);
-            expect(row.fromRow).toBe(1);
-            expect(row.toRow).toBe(1);
-          });
+        it('keeps selected class on rows 1 and 2', function () {
+          expect($(container.find('.slick-row .slick-cell:first-child ')[0])
+            .hasClass('selected')).toBeFalsy();
+          expect($(container.find('.slick-row .slick-cell:first-child ')[1])
+            .hasClass('selected')).toBeTruthy();
+          expect($(container.find('.slick-row .slick-cell:first-child ')[2])
+            .hasClass('selected')).toBeTruthy();
+          expect($(container.find('.slick-row .slick-cell:first-child ')[3])
+            .hasClass('selected')).toBeFalsy();
+        });
+      });
 
-          it("add selected class to parent of the span", function () {
-
-            expect($(container.find('.slick-row .slick-cell:first-child ')[1])
-              .hasClass('selected')).toBeTruthy();
-          });
-
-          describe("when the user clicks again the same row header", function () {
-            it("add selected class to parent of the span", function () {
-              container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[1].click();
-
-              expect($(container.find('.slick-row .slick-cell:first-child ')[1])
-                .hasClass('selected')).toBeFalsy();
-            });
-          });
-
-          describe("and presses shift + down-arrow", function () {
-            beforeEach(function () {
-              pressShiftArrow(KEY_DOWN);
-            });
-
-            it("keeps the last row selected", function () {
-              expect(cellSelectionModel.getSelectedRanges().length).toBe(1);
-            });
-
-            it("grows the selection down", function () {
-              var selectedRanges = cellSelectionModel.getSelectedRanges();
-
-              var row = selectedRanges[0];
-
-              expect(selectedRanges.length).toEqual(1);
-              expect(row.fromCell).toBe(1);
-              expect(row.toCell).toBe(2);
-              expect(row.fromRow).toBe(1);
-              expect(row.toRow).toBe(2);
-            });
-
-            it("keeps selected class on rows 1 and 2", function () {
-              expect($(container.find('.slick-row .slick-cell:first-child ')[0])
-                .hasClass('selected')).toBeFalsy();
-              expect($(container.find('.slick-row .slick-cell:first-child ')[1])
-                .hasClass('selected')).toBeTruthy();
-              expect($(container.find('.slick-row .slick-cell:first-child ')[2])
-                .hasClass('selected')).toBeTruthy();
-              expect($(container.find('.slick-row .slick-cell:first-child ')[3])
-                .hasClass('selected')).toBeFalsy();
-            });
-          });
-
-          describe('when the user clicks a cell on the current range', function () {
-            beforeEach(function () {
-              container.find('.slick-cell.l1.r1')[5].click();
-            });
-
-            it('row gets deselected', function () {
-
-              var selectedRanges = cellSelectionModel.getSelectedRanges();
-
-              expect(selectedRanges.length).toBe(1);
-
-              var newSelection = selectedRanges[0];
-
-              expect(newSelection.fromCell).toBe(1);
-              expect(newSelection.fromRow).toBe(5);
-              expect(newSelection.toCell).toBe(1);
-              expect(newSelection.toRow).toBe(5);
-            });
-
-            it('keep select class on row header', function () {
-              expect($(container.find('.slick-cell.l0.r0')[5]).hasClass('selected'))
-                .toBeTruthy();
-            });
-          });
-
-          describe('when the user clicks a cell outside the current range', function () {
-            beforeEach(function () {
-              container.find('.slick-cell.l2.r2')[2].click();
-            });
-
-            it('row gets deselected', function () {
-
-              var selectedRanges = cellSelectionModel.getSelectedRanges();
-
-              expect(selectedRanges.length).toBe(1);
-
-              var newSelection = selectedRanges[0];
-
-              expect(newSelection.fromCell).toBe(2);
-              expect(newSelection.fromRow).toBe(2);
-              expect(newSelection.toCell).toBe(2);
-              expect(newSelection.toRow).toBe(2);
-            });
-
-            it('remove select class on "some-column-name" column header', function () {
-              expect($(container.find('.slick-cell.l0.r0')[5]).hasClass('selected'))
-                .toBeFalsy();
-              expect($(container.find('.slick-cell.l0.r0')[2]).hasClass('selected'))
-                .toBeTruthy();
-            });
-          });
-
-          describe('when the user has a column selected', function () {
-            beforeEach(function () {
-              var selectedRanges = [new Slick.Range(0, 1, 9, 1)];
-              cellSelectionModel.setSelectedRanges(selectedRanges);
-            });
-
-            it('no row should have the class "selected"', function () {
-              expect($(container.find('.slick-cell.l0.r0')[0]).hasClass('selected'))
-                .toBeFalsy();
-              expect($(container.find('.slick-cell.l0.r0')[1]).hasClass('selected'))
-                .toBeFalsy();
-              expect($(container.find('.slick-cell.l0.r0')[2]).hasClass('selected'))
-                .toBeFalsy();
-              expect($(container.find('.slick-cell.l0.r0')[3]).hasClass('selected'))
-                .toBeFalsy();
-              expect($(container.find('.slick-cell.l0.r0')[4]).hasClass('selected'))
-                .toBeFalsy();
-              expect($(container.find('.slick-cell.l0.r0')[5]).hasClass('selected'))
-                .toBeFalsy();
-            });
-          });
+      describe('when the user clicks a cell on the current range', function () {
+        beforeEach(function () {
+          container.find('.slick-cell.l1.r1')[5].click();
         });
 
-        describe("when the user clicks multiple row headers", function () {
-          it("selects another row", function () {
-            container.find('.slick-row .slick-cell:first-child')[4].click();
-            container.find('.slick-row .slick-cell:first-child')[0].click();
+        it('row gets deselected', function () {
+
+          var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
-            expect(selectedRanges.length).toEqual(2);
+          expect(selectedRanges.length).toBe(1);
 
-            var row1 = selectedRanges[0];
-            expect(row1.fromRow).toBe(4);
-            expect(row1.toRow).toBe(4);
+          var newSelection = selectedRanges[0];
+
+          expect(newSelection.fromCell).toBe(1);
+          expect(newSelection.fromRow).toBe(5);
+          expect(newSelection.toCell).toBe(1);
+          expect(newSelection.toRow).toBe(5);
+        });
+
+        it('keep select class on row header', function () {
+          expect($(container.find('.slick-cell.l0.r0')[5]).hasClass('selected'))
+            .toBeTruthy();
+        });
+      });
 
-            var row2 = selectedRanges[1];
-            expect(row2.fromRow).toBe(0);
-            expect(row2.toRow).toBe(0);
-          });
+      describe('when the user clicks a cell outside the current range', function () {
+        beforeEach(function () {
+          container.find('.slick-cell.l2.r2')[2].click();
         });
 
-        describe("when a column was already selected", function () {
-          beforeEach(function () {
-            var selectedRanges = [new Slick.Range(0, 0, 0, 1)];
-            cellSelectionModel.setSelectedRanges(selectedRanges);
-          });
+        it('row gets deselected', function () {
 
-          it("deselects the column", function () {
-            container.find('.slick-row .slick-cell:first-child')[0].click();
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
+          var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-            expectOnlyTheFirstRowToBeSelected(selectedRanges);
-          });
+          expect(selectedRanges.length).toBe(1);
+
+          var newSelection = selectedRanges[0];
+
+          expect(newSelection.fromCell).toBe(2);
+          expect(newSelection.fromRow).toBe(2);
+          expect(newSelection.toCell).toBe(2);
+          expect(newSelection.toRow).toBe(2);
         });
 
-        describe("when the row is deselected through setSelectedRanges", function () {
-          beforeEach(function () {
-            container.find('.slick-row .slick-cell:first-child')[4].click();
-          });
+        it('remove select class on "some-column-name" column header', function () {
+          expect($(container.find('.slick-cell.l0.r0')[5]).hasClass('selected'))
+            .toBeFalsy();
+          expect($(container.find('.slick-cell.l0.r0')[2]).hasClass('selected'))
+            .toBeTruthy();
+        });
+      });
 
-          it("should remove the selected class", function () {
-            cellSelectionModel.setSelectedRanges([]);
+      describe('when the user has a column selected', function () {
+        beforeEach(function () {
+          var selectedRanges = [new Slick.Range(0, 1, 9, 1)];
+          cellSelectionModel.setSelectedRanges(selectedRanges);
+        });
 
-            expect($(container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[4])
-              .hasClass('selected')).toBeFalsy();
-          });
+        it('no row should have the class "selected"', function () {
+          expect($(container.find('.slick-cell.l0.r0')[0]).hasClass('selected'))
+            .toBeFalsy();
+          expect($(container.find('.slick-cell.l0.r0')[1]).hasClass('selected'))
+            .toBeFalsy();
+          expect($(container.find('.slick-cell.l0.r0')[2]).hasClass('selected'))
+            .toBeFalsy();
+          expect($(container.find('.slick-cell.l0.r0')[3]).hasClass('selected'))
+            .toBeFalsy();
+          expect($(container.find('.slick-cell.l0.r0')[4]).hasClass('selected'))
+            .toBeFalsy();
+          expect($(container.find('.slick-cell.l0.r0')[5]).hasClass('selected'))
+            .toBeFalsy();
         });
+      });
+    });
 
-        describe("click a second time", function () {
-          beforeEach(function () {
-            container.find('.slick-row .slick-cell:first-child')[1].click();
-          });
+    describe('when the user clicks multiple row headers', function () {
+      it('selects another row', function () {
+        container.find('.slick-row .slick-cell:first-child')[4].click();
+        container.find('.slick-row .slick-cell:first-child')[0].click();
 
-          it("removes the selected class", function () {
-            container.find('.slick-row .slick-cell:first-child')[1].click();
-            expect($(container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[1])
-              .hasClass('selected')).toBeFalsy();
-          });
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
+        expect(selectedRanges.length).toEqual(2);
 
-          it("unselects the row", function () {
-            container.find('.slick-row .slick-cell:first-child')[1].click();
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
+        var row1 = selectedRanges[0];
+        expect(row1.fromRow).toBe(4);
+        expect(row1.toRow).toBe(4);
 
-            expect(selectedRanges.length).toEqual(0);
-          })
-        });
+        var row2 = selectedRanges[1];
+        expect(row2.fromRow).toBe(0);
+        expect(row2.toRow).toBe(0);
       });
+    });
 
-      function pressShiftArrow(keyCode) {
-        var pressEvent = new $.Event("keydown");
-        pressEvent.shiftKey = true;
-        pressEvent.ctrlKey = false;
-        pressEvent.altKey = false;
-        pressEvent.which = keyCode;
+    describe('when a column was already selected', function () {
+      beforeEach(function () {
+        var selectedRanges = [new Slick.Range(0, 0, 0, 1)];
+        cellSelectionModel.setSelectedRanges(selectedRanges);
+      });
 
-        $(container.find('.grid-canvas')).trigger(pressEvent);
-      }
+      it('deselects the column', function () {
+        container.find('.slick-row .slick-cell:first-child')[0].click();
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
+
+        expectOnlyTheFirstRowToBeSelected(selectedRanges);
+      });
     });
 
-    function expectOnlyTheFirstRowToBeSelected(selectedRanges) {
-      var row = selectedRanges[0];
+    describe('when the row is deselected through setSelectedRanges', function () {
+      beforeEach(function () {
+        container.find('.slick-row .slick-cell:first-child')[4].click();
+      });
 
-      expect(selectedRanges.length).toEqual(1);
-      expect(row.fromCell).toBe(1);
-      expect(row.toCell).toBe(2);
-      expect(row.fromRow).toBe(0);
-      expect(row.toRow).toBe(0);
-    }
-  });
\ No newline at end of file
+      it('should remove the selected class', function () {
+        cellSelectionModel.setSelectedRanges([]);
+
+        expect($(container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[4])
+          .hasClass('selected')).toBeFalsy();
+      });
+    });
+
+    describe('click a second time', function () {
+      beforeEach(function () {
+        container.find('.slick-row .slick-cell:first-child')[1].click();
+      });
+
+      it('removes the selected class', function () {
+        container.find('.slick-row .slick-cell:first-child')[1].click();
+        expect($(container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[1])
+          .hasClass('selected')).toBeFalsy();
+      });
+
+      it('unselects the row', function () {
+        container.find('.slick-row .slick-cell:first-child')[1].click();
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
+
+        expect(selectedRanges.length).toEqual(0);
+      })
+    });
+  });
+
+  function pressShiftArrow(keyCode) {
+    var pressEvent = new $.Event('keydown');
+    pressEvent.shiftKey = true;
+    pressEvent.ctrlKey = false;
+    pressEvent.altKey = false;
+    pressEvent.which = keyCode;
+
+    $(container.find('.grid-canvas')).trigger(pressEvent);
+  }
+
+  function expectOnlyTheFirstRowToBeSelected(selectedRanges) {
+    var row = selectedRanges[0];
+
+    expect(selectedRanges.length).toEqual(1);
+    expect(row.fromCell).toBe(1);
+    expect(row.toCell).toBe(2);
+    expect(row.fromRow).toBe(0);
+    expect(row.toRow).toBe(0);
+  }
+});
diff --git a/web/regression/javascript/selection/xcell_selection_model_spec.js b/web/regression/javascript/selection/xcell_selection_model_spec.js
index 1682c244..8f01035c 100644
--- a/web/regression/javascript/selection/xcell_selection_model_spec.js
+++ b/web/regression/javascript/selection/xcell_selection_model_spec.js
@@ -7,507 +7,505 @@
 //
 //////////////////////////////////////////////////////////////////////////
 
-define([
-  'sources/selection/xcell_selection_model',
-  "slickgrid/slick.grid",
-  'slickgrid',
-], function (XCellSelectionModel, SlickGrid, Slick) {
-  describe('XCellSelectionModel', function () {
-    var KEY_RIGHT = 39;
-    var KEY_LEFT = 37;
-    var KEY_UP = 38;
-    var KEY_DOWN = 40;
-
-    var container, grid;
-    var oldWindowParent = window.parent;
+import XCellSelectionModel from 'sources/selection/xcell_selection_model';
+import 'slickgrid.grid';
+import Slick from 'slickgrid';
+
+describe('XCellSelectionModel', function () {
+  var KEY_RIGHT = 39;
+  var KEY_LEFT = 37;
+  var KEY_UP = 38;
+  var KEY_DOWN = 40;
+
+  var container, grid;
+  var SlickGrid = Slick.Grid;
+  var oldWindowParent = window.parent;
+
+  beforeEach(function () {
+    window.parent = window;
+
+    var columns = [{
+      id: 'row-header-column',
+      name: 'row header column name',
+      selectable: false,
+    }, {
+      id: '1',
+      name: 'some-column-name',
+      pos: 0
+    }, {
+      id: 'second-column-id',
+      name: 'second column',
+      pos: 1
+    }, {
+      id: 'third-column-id',
+      name: 'third column',
+      pos: 2
+    },
+    ];
+
+    var data = [];
+    for (var i = 0; i < 10; i++) {
+      data.push({
+        'some-column-name': 'some-value-' + i,
+        'second column': 'second value ' + i,
+        'third column': 'third value ' + i,
+        'fourth column': 'fourth value ' + i,
+      });
+    }
+    container = $("<div></div>");
+    container.height(9999);
+    container.width(9999);
 
-    beforeEach(function () {
-      window.parent = window;
-
-      var columns = [{
-        id: 'row-header-column',
-        name: 'row header column name',
-        selectable: false,
-      }, {
-        id: '1',
-        name: 'some-column-name',
-        pos: 0
-      }, {
-        id: 'second-column-id',
-        name: 'second column',
-        pos: 1
-      }, {
-        id: 'third-column-id',
-        name: 'third column',
-        pos: 2
-      },
-      ];
-
-      var data = [];
-      for (var i = 0; i < 10; i++) {
-        data.push({
-          'some-column-name': 'some-value-' + i,
-          'second column': 'second value ' + i,
-          'third column': 'third value ' + i,
-          'fourth column': 'fourth value ' + i,
-        });
-      }
-      container = $("<div></div>");
-      container.height(9999);
-      container.width(9999);
-
-      grid = new SlickGrid(container, data, columns);
-      grid.setSelectionModel(new XCellSelectionModel());
-      $("body").append(container);
-    });
+    grid = new SlickGrid(container, data, columns);
+    grid.setSelectionModel(new XCellSelectionModel());
+    $("body").append(container);
+  });
 
-    afterEach(function () {
-      grid.destroy();
-      container.remove();
-      window.parent = oldWindowParent;
-    });
+  afterEach(function () {
+    grid.destroy();
+    container.remove();
+    window.parent = oldWindowParent;
+  });
 
-    describe('handleKeyDown', function () {
-      describe('when we press a random key', function () {
-        it('should not change the range', function () {
-          var range = new Slick.Range(1, 2);
-          grid.setActiveCell(1, 2);
-          grid.getSelectionModel().setSelectedRanges([range]);
-          pressKey(72);
+  describe('handleKeyDown', function () {
+    describe('when we press a random key', function () {
+      it('should not change the range', function () {
+        var range = new Slick.Range(1, 2);
+        grid.setActiveCell(1, 2);
+        grid.getSelectionModel().setSelectedRanges([range]);
+        pressKey(72);
 
-          expect(grid.getSelectionModel().getSelectedRanges()[0]).toEqual(range);
-        });
+        expect(grid.getSelectionModel().getSelectedRanges()[0]).toEqual(range);
       });
+    });
 
-      describe('when we press an arrow key ', function () {
-        it('should select the cell to the right', function () {
-          var range = new Slick.Range(1, 2);
-          grid.setActiveCell(1, 2);
-          grid.getSelectionModel().setSelectedRanges([range]);
-          pressKey(KEY_RIGHT);
+    describe('when we press an arrow key ', function () {
+      it('should select the cell to the right', function () {
+        var range = new Slick.Range(1, 2);
+        grid.setActiveCell(1, 2);
+        grid.getSelectionModel().setSelectedRanges([range]);
+        pressKey(KEY_RIGHT);
 
-          expectOneSelectedRange(1, 3, 1, 3);
-        });
+        expectOneSelectedRange(1, 3, 1, 3);
       });
+    });
 
-      describe('when we press shift', function () {
-        describe('and we press an arrow key', function () {
-          var scrollColumnIntoViewSpy, scrollRowIntoViewSpy;
+    describe('when we press shift', function () {
+      describe('and we press an arrow key', function () {
+        var scrollColumnIntoViewSpy, scrollRowIntoViewSpy;
 
-          beforeEach(function () {
-            scrollColumnIntoViewSpy = spyOn(grid, 'scrollColumnIntoView');
-            scrollRowIntoViewSpy = spyOn(grid, 'scrollRowIntoView');
-          });
+        beforeEach(function () {
+          scrollColumnIntoViewSpy = spyOn(grid, 'scrollColumnIntoView');
+          scrollRowIntoViewSpy = spyOn(grid, 'scrollRowIntoView');
+        });
 
-          describe('the right arrow', function () {
-            describe('when a cell is selected', function () {
-              beforeEach(function () {
-                var range = new Slick.Range(1, 1);
-                grid.setActiveCell(1, 1);
-                grid.getSelectionModel().setSelectedRanges([range]);
-                pressShiftPlusKey(KEY_RIGHT);
-              });
+        describe('the right arrow', function () {
+          describe('when a cell is selected', function () {
+            beforeEach(function () {
+              var range = new Slick.Range(1, 1);
+              grid.setActiveCell(1, 1);
+              grid.getSelectionModel().setSelectedRanges([range]);
+              pressShiftPlusKey(KEY_RIGHT);
+            });
 
-              it('increases the range by one to the right', function () {
-                expectOneSelectedRange(1, 1, 1, 2);
-              });
+            it('increases the range by one to the right', function () {
+              expectOneSelectedRange(1, 1, 1, 2);
+            });
 
-              it('should scroll the next column into view', function () {
-                expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(2);
-                expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
-              });
+            it('should scroll the next column into view', function () {
+              expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(2);
+              expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
+            });
 
-              it('pressing right again grows the range right', function () {
-                pressShiftPlusKey(KEY_RIGHT);
+            it('pressing right again grows the range right', function () {
+              pressShiftPlusKey(KEY_RIGHT);
 
-                expectOneSelectedRange(1, 1, 1, 3);
-              });
+              expectOneSelectedRange(1, 1, 1, 3);
+            });
 
-              it('then pressing left keeps the original selection', function () {
-                pressShiftPlusKey(KEY_LEFT);
+            it('then pressing left keeps the original selection', function () {
+              pressShiftPlusKey(KEY_LEFT);
 
-                expectOneSelectedRange(1, 1, 1, 1);
-              });
+              expectOneSelectedRange(1, 1, 1, 1);
             });
+          });
 
-            describe('when a column is selected', function () {
-              beforeEach(function () {
-                var range = new Slick.Range(0, 1, 9, 1);
-                grid.setActiveCell(0, 1);
-                grid.getSelectionModel().setSelectedRanges([range]);
-                pressShiftPlusKey(KEY_RIGHT);
-              });
+          describe('when a column is selected', function () {
+            beforeEach(function () {
+              var range = new Slick.Range(0, 1, 9, 1);
+              grid.setActiveCell(0, 1);
+              grid.getSelectionModel().setSelectedRanges([range]);
+              pressShiftPlusKey(KEY_RIGHT);
+            });
 
-              it('increases the range by one column to the right', function () {
-                expectOneSelectedRange(0, 1, 9, 2);
-              });
+            it('increases the range by one column to the right', function () {
+              expectOneSelectedRange(0, 1, 9, 2);
+            });
 
-              it('should scroll the next column into view', function () {
-                expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(2);
-                expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
-              });
+            it('should scroll the next column into view', function () {
+              expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(2);
+              expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
             });
           });
+        });
 
-          describe('the left arrow', function () {
-            describe('when a cell is selected', function () {
-              beforeEach(function () {
-                var range = new Slick.Range(1, 3);
-                grid.setActiveCell(1, 3);
-                grid.getSelectionModel().setSelectedRanges([range]);
-                pressShiftPlusKey(KEY_LEFT);
-              });
+        describe('the left arrow', function () {
+          describe('when a cell is selected', function () {
+            beforeEach(function () {
+              var range = new Slick.Range(1, 3);
+              grid.setActiveCell(1, 3);
+              grid.getSelectionModel().setSelectedRanges([range]);
+              pressShiftPlusKey(KEY_LEFT);
+            });
 
-              it('increases the range by one to the left', function () {
-                expectOneSelectedRange(1, 2, 1, 3);
-              });
+            it('increases the range by one to the left', function () {
+              expectOneSelectedRange(1, 2, 1, 3);
+            });
 
-              it('should scroll previous column into view', function () {
-                expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(2);
-                expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
-              });
+            it('should scroll previous column into view', function () {
+              expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(2);
+              expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
+            });
 
-              it('pressing left again grows the range the left', function () {
-                pressShiftPlusKey(KEY_LEFT);
+            it('pressing left again grows the range the left', function () {
+              pressShiftPlusKey(KEY_LEFT);
 
-                expectOneSelectedRange(1, 1, 1, 3);
-              });
+              expectOneSelectedRange(1, 1, 1, 3);
+            });
 
-              it('then pressing right keeps the original selection', function () {
-                pressShiftPlusKey(KEY_RIGHT);
+            it('then pressing right keeps the original selection', function () {
+              pressShiftPlusKey(KEY_RIGHT);
 
-                expectOneSelectedRange(1, 3, 1, 3);
-              });
+              expectOneSelectedRange(1, 3, 1, 3);
             });
+          });
 
-            describe('when a column is selected', function () {
-              beforeEach(function () {
-                var range = new Slick.Range(0, 2, 9, 2);
-                grid.setActiveCell(0, 2);
-                grid.getSelectionModel().setSelectedRanges([range]);
-                pressShiftPlusKey(KEY_LEFT);
-              });
+          describe('when a column is selected', function () {
+            beforeEach(function () {
+              var range = new Slick.Range(0, 2, 9, 2);
+              grid.setActiveCell(0, 2);
+              grid.getSelectionModel().setSelectedRanges([range]);
+              pressShiftPlusKey(KEY_LEFT);
+            });
 
-              it('increases the range by one column to the left', function () {
-                expectOneSelectedRange(0, 1, 9, 2);
-              });
+            it('increases the range by one column to the left', function () {
+              expectOneSelectedRange(0, 1, 9, 2);
+            });
 
-              it('should scroll previous column into view', function () {
-                expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(1);
-                expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
-              });
+            it('should scroll previous column into view', function () {
+              expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(1);
+              expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
             });
           });
+        });
 
-          describe('the up arrow', function () {
-            describe('when a cell is selected', function () {
-              beforeEach(function () {
-                var range = new Slick.Range(2, 2);
-                grid.setActiveCell(2, 2);
-                grid.getSelectionModel().setSelectedRanges([range]);
-                pressShiftPlusKey(KEY_UP);
-              });
+        describe('the up arrow', function () {
+          describe('when a cell is selected', function () {
+            beforeEach(function () {
+              var range = new Slick.Range(2, 2);
+              grid.setActiveCell(2, 2);
+              grid.getSelectionModel().setSelectedRanges([range]);
+              pressShiftPlusKey(KEY_UP);
+            });
 
-              it('increases the range by one up', function () {
-                expectOneSelectedRange(1, 2, 2, 2);
-              });
+            it('increases the range by one up', function () {
+              expectOneSelectedRange(1, 2, 2, 2);
+            });
 
-              it('should scroll the row above into view', function () {
-                expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(1);
-                expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
-              });
+            it('should scroll the row above into view', function () {
+              expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(1);
+              expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
+            });
 
-              it('pressing up again grows the range up', function () {
-                pressShiftPlusKey(KEY_UP);
+            it('pressing up again grows the range up', function () {
+              pressShiftPlusKey(KEY_UP);
 
-                expectOneSelectedRange(0, 2, 2, 2);
-              });
+              expectOneSelectedRange(0, 2, 2, 2);
+            });
 
-              it('then pressing down keeps the original selection', function () {
-                pressShiftPlusKey(KEY_DOWN);
+            it('then pressing down keeps the original selection', function () {
+              pressShiftPlusKey(KEY_DOWN);
 
-                expectOneSelectedRange(2, 2, 2, 2);
-              });
+              expectOneSelectedRange(2, 2, 2, 2);
             });
+          });
 
-            describe('when a row is selected', function () {
-              beforeEach(function () {
-                var range = new Slick.Range(2, 1, 2, 3);
-                grid.setActiveCell(2, 1);
-                grid.getSelectionModel().setSelectedRanges([range]);
-                pressShiftPlusKey(KEY_UP);
-              });
+          describe('when a row is selected', function () {
+            beforeEach(function () {
+              var range = new Slick.Range(2, 1, 2, 3);
+              grid.setActiveCell(2, 1);
+              grid.getSelectionModel().setSelectedRanges([range]);
+              pressShiftPlusKey(KEY_UP);
+            });
 
-              it('increases the range by one row up', function () {
-                expectOneSelectedRange(1, 1, 2, 3);
-              });
+            it('increases the range by one row up', function () {
+              expectOneSelectedRange(1, 1, 2, 3);
+            });
 
-              it('should scroll the row above into view', function () {
-                expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(1);
-                expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
-              });
+            it('should scroll the row above into view', function () {
+              expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(1);
+              expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
             });
           });
+        });
 
-          describe('the down arrow', function () {
-            describe('when a cell is selected', function () {
-              beforeEach(function () {
-                var range = new Slick.Range(2, 2);
-                grid.setActiveCell(2, 2);
-                grid.getSelectionModel().setSelectedRanges([range]);
-                pressShiftPlusKey(KEY_DOWN);
-              });
+        describe('the down arrow', function () {
+          describe('when a cell is selected', function () {
+            beforeEach(function () {
+              var range = new Slick.Range(2, 2);
+              grid.setActiveCell(2, 2);
+              grid.getSelectionModel().setSelectedRanges([range]);
+              pressShiftPlusKey(KEY_DOWN);
+            });
 
-              it('increases the range by one down', function () {
-                expectOneSelectedRange(2, 2, 3, 2);
-              });
+            it('increases the range by one down', function () {
+              expectOneSelectedRange(2, 2, 3, 2);
+            });
 
-              it('should scroll the row below into view', function () {
-                expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(3);
-                expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
-              });
+            it('should scroll the row below into view', function () {
+              expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(3);
+              expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
+            });
 
-              it('pressing down again grows the range down', function () {
-                pressShiftPlusKey(KEY_DOWN);
+            it('pressing down again grows the range down', function () {
+              pressShiftPlusKey(KEY_DOWN);
 
-                expectOneSelectedRange(2, 2, 4, 2);
-              });
+              expectOneSelectedRange(2, 2, 4, 2);
+            });
 
-              it('then pressing up keeps the original selection', function () {
-                pressShiftPlusKey(KEY_UP);
+            it('then pressing up keeps the original selection', function () {
+              pressShiftPlusKey(KEY_UP);
 
-                expectOneSelectedRange(2, 2, 2, 2);
-              });
+              expectOneSelectedRange(2, 2, 2, 2);
             });
+          });
 
-            describe('when a row is selected', function () {
-              beforeEach(function () {
-                var range = new Slick.Range(2, 1, 2, 3);
-                grid.setActiveCell(2, 1);
-                grid.getSelectionModel().setSelectedRanges([range]);
-                pressShiftPlusKey(KEY_DOWN);
-              });
+          describe('when a row is selected', function () {
+            beforeEach(function () {
+              var range = new Slick.Range(2, 1, 2, 3);
+              grid.setActiveCell(2, 1);
+              grid.getSelectionModel().setSelectedRanges([range]);
+              pressShiftPlusKey(KEY_DOWN);
+            });
 
-              it('increases the range by one row down', function () {
-                expectOneSelectedRange(2, 1, 3, 3);
-              });
+            it('increases the range by one row down', function () {
+              expectOneSelectedRange(2, 1, 3, 3);
+            });
 
-              it('should scroll the row below into view', function () {
-                expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(3);
-                expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
-              });
+            it('should scroll the row below into view', function () {
+              expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(3);
+              expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
             });
           });
+        });
 
-          describe('rectangular selection works', function () {
+        describe('rectangular selection works', function () {
 
-            it('in the down-and-rightward direction', function () {
-              var range = new Slick.Range(1, 1);
-              grid.setActiveCell(1, 1);
-              grid.getSelectionModel().setSelectedRanges([range]);
+          it('in the down-and-rightward direction', function () {
+            var range = new Slick.Range(1, 1);
+            grid.setActiveCell(1, 1);
+            grid.getSelectionModel().setSelectedRanges([range]);
 
-              pressShiftPlusKey(KEY_DOWN);
-              pressShiftPlusKey(KEY_DOWN);
-              pressShiftPlusKey(KEY_DOWN);
-              pressShiftPlusKey(KEY_RIGHT);
-              pressShiftPlusKey(KEY_RIGHT);
+            pressShiftPlusKey(KEY_DOWN);
+            pressShiftPlusKey(KEY_DOWN);
+            pressShiftPlusKey(KEY_DOWN);
+            pressShiftPlusKey(KEY_RIGHT);
+            pressShiftPlusKey(KEY_RIGHT);
 
-              expectOneSelectedRange(1, 1, 4, 3);
-            });
+            expectOneSelectedRange(1, 1, 4, 3);
+          });
 
-            it('in the up-and-leftward direction', function () {
-              var range = new Slick.Range(4, 3);
-              grid.setActiveCell(4, 3);
-              grid.getSelectionModel().setSelectedRanges([range]);
+          it('in the up-and-leftward direction', function () {
+            var range = new Slick.Range(4, 3);
+            grid.setActiveCell(4, 3);
+            grid.getSelectionModel().setSelectedRanges([range]);
 
-              pressShiftPlusKey(KEY_UP);
-              pressShiftPlusKey(KEY_UP);
-              pressShiftPlusKey(KEY_UP);
-              pressShiftPlusKey(KEY_LEFT);
-              pressShiftPlusKey(KEY_LEFT);
+            pressShiftPlusKey(KEY_UP);
+            pressShiftPlusKey(KEY_UP);
+            pressShiftPlusKey(KEY_UP);
+            pressShiftPlusKey(KEY_LEFT);
+            pressShiftPlusKey(KEY_LEFT);
 
-              expectOneSelectedRange(1, 1, 4, 3);
-            });
+            expectOneSelectedRange(1, 1, 4, 3);
+          });
 
-            it('in the up-and-rightward direction', function () {
-              var range = new Slick.Range(4, 1);
-              grid.setActiveCell(4, 1);
-              grid.getSelectionModel().setSelectedRanges([range]);
+          it('in the up-and-rightward direction', function () {
+            var range = new Slick.Range(4, 1);
+            grid.setActiveCell(4, 1);
+            grid.getSelectionModel().setSelectedRanges([range]);
 
-              pressShiftPlusKey(KEY_UP);
-              pressShiftPlusKey(KEY_UP);
-              pressShiftPlusKey(KEY_UP);
-              pressShiftPlusKey(KEY_RIGHT);
-              pressShiftPlusKey(KEY_RIGHT);
+            pressShiftPlusKey(KEY_UP);
+            pressShiftPlusKey(KEY_UP);
+            pressShiftPlusKey(KEY_UP);
+            pressShiftPlusKey(KEY_RIGHT);
+            pressShiftPlusKey(KEY_RIGHT);
 
-              expectOneSelectedRange(1, 1, 4, 3);
-            });
+            expectOneSelectedRange(1, 1, 4, 3);
+          });
 
-            it('in the down-and-leftward direction', function () {
-              var range = new Slick.Range(1, 3);
-              grid.setActiveCell(1, 3);
-              grid.getSelectionModel().setSelectedRanges([range]);
+          it('in the down-and-leftward direction', function () {
+            var range = new Slick.Range(1, 3);
+            grid.setActiveCell(1, 3);
+            grid.getSelectionModel().setSelectedRanges([range]);
 
-              pressShiftPlusKey(KEY_DOWN);
-              pressShiftPlusKey(KEY_DOWN);
-              pressShiftPlusKey(KEY_DOWN);
-              pressShiftPlusKey(KEY_LEFT);
-              pressShiftPlusKey(KEY_LEFT);
+            pressShiftPlusKey(KEY_DOWN);
+            pressShiftPlusKey(KEY_DOWN);
+            pressShiftPlusKey(KEY_DOWN);
+            pressShiftPlusKey(KEY_LEFT);
+            pressShiftPlusKey(KEY_LEFT);
 
-              expectOneSelectedRange(1, 1, 4, 3);
-            });
+            expectOneSelectedRange(1, 1, 4, 3);
           });
+        });
 
-          describe('and we are on an edge', function () {
-            var range;
+        describe('and we are on an edge', function () {
+          var range;
 
-            beforeEach(function () {
-              range = new Slick.Range(2, 1);
-              grid.setActiveCell(2, 1);
-              grid.getSelectionModel().setSelectedRanges([range]);
-            });
+          beforeEach(function () {
+            range = new Slick.Range(2, 1);
+            grid.setActiveCell(2, 1);
+            grid.getSelectionModel().setSelectedRanges([range]);
+          });
 
-            it('we still have the selected range before we arrowed', function () {
-              pressShiftPlusKey(KEY_LEFT);
-              expectOneSelectedRange(2, 1, 2, 1);
-            });
+          it('we still have the selected range before we arrowed', function () {
+            pressShiftPlusKey(KEY_LEFT);
+            expectOneSelectedRange(2, 1, 2, 1);
           });
         });
       });
     });
+  });
 
-    describe('when we drag and drop', function () {
-      var dd;
-      // We could not find an elegant way to calculate this value
-      // after changing window size we saw this was a constant value
-      var offsetLeftColumns = 100;
+  describe('when we drag and drop', function () {
+    var dd;
+    // We could not find an elegant way to calculate this value
+    // after changing window size we saw this was a constant value
+    var offsetLeftColumns = 100;
 
-      function cellTopPosition($cell, rowNumber) {
-        return $(grid.getCanvasNode()).offset().top + $cell[0].scrollHeight * rowNumber;
-      }
+    function cellTopPosition($cell, rowNumber) {
+      return $(grid.getCanvasNode()).offset().top + $cell[0].scrollHeight * rowNumber;
+    }
+
+    function cellLeftPosition(columnNumber) {
+      return $(grid.getCanvasNode()).offset().left + offsetLeftColumns * columnNumber;
+    }
 
-      function cellLeftPosition(columnNumber) {
-        return $(grid.getCanvasNode()).offset().left + offsetLeftColumns * columnNumber;
-      }
+    beforeEach(function () {
+      var initialPosition = {cell: 3, row: 4};
+      var $cell = $($('.slick-cell.l3')[initialPosition.row]);
+      var event = {
+        target: $cell,
+        isPropagationStopped: jasmine.createSpy('isPropagationStopped').and.returnValue(false),
+        isImmediatePropagationStopped: jasmine.createSpy('isImmediatePropagationStopped').and.returnValue(false),
+        stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation')
+      };
+
+      dd = {
+        grid: grid,
+        startX: cellLeftPosition(initialPosition.cell),
+        startY: cellTopPosition($cell, initialPosition.row)
+      };
+
+      grid.onDragStart.notify(dd, event, grid);
+    });
 
+    describe('when the drop happens outside of the grid', function () {
       beforeEach(function () {
-        var initialPosition = {cell: 3, row: 4};
-        var $cell = $($('.slick-cell.l3')[initialPosition.row]);
+        var $cell = $($('.slick-cell.l1')[1]);
+        var finalPosition = {cell: 1, row: 1};
+
         var event = {
           target: $cell,
           isPropagationStopped: jasmine.createSpy('isPropagationStopped').and.returnValue(false),
           isImmediatePropagationStopped: jasmine.createSpy('isImmediatePropagationStopped').and.returnValue(false),
-          stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation')
-        };
+          stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation'),
 
-        dd = {
-          grid: grid,
-          startX: cellLeftPosition(initialPosition.cell),
-          startY: cellTopPosition($cell, initialPosition.row)
+          pageX: cellLeftPosition(finalPosition.cell),
+          pageY: cellTopPosition($cell, finalPosition.row)
         };
 
-        grid.onDragStart.notify(dd, event, grid);
+        grid.onDrag.notify(dd, event, grid);
+        $(window).mouseup();
       });
+      it('should call handleDragEnd from CellRangeSelector', function () {
+        var newRange = grid.getSelectionModel().getSelectedRanges();
 
-      describe('when the drop happens outside of the grid', function () {
-        beforeEach(function () {
-          var $cell = $($('.slick-cell.l1')[1]);
-          var finalPosition = {cell: 1, row: 1};
-
-          var event = {
-            target: $cell,
-            isPropagationStopped: jasmine.createSpy('isPropagationStopped').and.returnValue(false),
-            isImmediatePropagationStopped: jasmine.createSpy('isImmediatePropagationStopped').and.returnValue(false),
-            stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation'),
-
-            pageX: cellLeftPosition(finalPosition.cell),
-            pageY: cellTopPosition($cell, finalPosition.row)
-          };
+        expect(newRange.length).toBe(1);
 
-          grid.onDrag.notify(dd, event, grid);
-          $(window).mouseup();
-        });
-        it('should call handleDragEnd from CellRangeSelector', function () {
-          var newRange = grid.getSelectionModel().getSelectedRanges();
-
-          expect(newRange.length).toBe(1);
-
-          expect(newRange[0].fromCell).toBe(1);
-          expect(newRange[0].toCell).toBe(3);
-          expect(newRange[0].fromRow).toBe(1);
-          expect(newRange[0].toRow).toBe(4);
-        });
+        expect(newRange[0].fromCell).toBe(1);
+        expect(newRange[0].toCell).toBe(3);
+        expect(newRange[0].fromRow).toBe(1);
+        expect(newRange[0].toRow).toBe(4);
       });
     });
+  });
 
-    describe('when we mouse up and no drag and drop occured', function () {
-      beforeEach(function () {
-        grid.onDragEnd.notify = jasmine.createSpy('notify');
-        grid.onDragEnd.notify.calls.reset();
-        $(window).mouseup();
-      });
+  describe('when we mouse up and no drag and drop occured', function () {
+    beforeEach(function () {
+      grid.onDragEnd.notify = jasmine.createSpy('notify');
+      grid.onDragEnd.notify.calls.reset();
+      $(window).mouseup();
+    });
 
-      it('do not notify onDragEnd', function () {
-        expect(grid.onDragEnd.notify).not.toHaveBeenCalled()
-      });
+    it('do not notify onDragEnd', function () {
+      expect(grid.onDragEnd.notify).not.toHaveBeenCalled()
     });
+  });
+
+  describe('setSelectedRows', function () {
 
-    describe('setSelectedRows', function () {
+    beforeEach(function () {
+      grid.getSelectionModel().setSelectedRanges(
+        [new Slick.Range(1, 1, 1, 1)]
+      );
+    });
 
+    describe('when passed an empty array', function () {
       beforeEach(function () {
-        grid.getSelectionModel().setSelectedRanges(
-          [new Slick.Range(1, 1, 1, 1)]
-        );
+        grid.getSelectionModel().setSelectedRows([]);
       });
-
-      describe('when passed an empty array', function () {
-        beforeEach(function () {
-          grid.getSelectionModel().setSelectedRows([]);
-        });
-        it('clears ranges', function () {
-          var newRanges = grid.getSelectionModel().getSelectedRanges();
-          expect(newRanges.length).toEqual(0);
-        });
-      });
-
-      it('sets ranges corresponding to rows', function () {
-        grid.getSelectionModel().setSelectedRows([0, 2]);
-
-        var selectedRanges = grid.getSelectionModel().getSelectedRanges();
-
-        expect(selectedRanges.length).toBe(2);
-        expectRangeToMatch(selectedRanges[0], 0, 1, 0, 3);
-        expectRangeToMatch(selectedRanges[1], 2, 1, 2, 3);
+      it('clears ranges', function () {
+        var newRanges = grid.getSelectionModel().getSelectedRanges();
+        expect(newRanges.length).toEqual(0);
       });
     });
 
-    function pressKey(keyCode) {
-      var pressEvent = new $.Event("keydown");
-      pressEvent.which = keyCode;
-
-      $(container.find('.grid-canvas')).trigger(pressEvent);
-    }
-
-    function pressShiftPlusKey(keyCode) {
-      var pressEvent = new $.Event("keydown");
-      pressEvent.shiftKey = true;
-      pressEvent.which = keyCode;
-
-      $(container.find('.grid-canvas')).trigger(pressEvent);
-    }
+    it('sets ranges corresponding to rows', function () {
+      grid.getSelectionModel().setSelectedRows([0, 2]);
 
-    function expectOneSelectedRange(fromRow, fromCell, toRow, toCell) {
       var selectedRanges = grid.getSelectionModel().getSelectedRanges();
-      expect(selectedRanges.length).toBe(1);
-      expectRangeToMatch(selectedRanges[0], fromRow, fromCell, toRow, toCell);
-    }
 
-    function expectRangeToMatch(range, fromRow, fromCell, toRow, toCell) {
-      expect(range.fromRow).toBe(fromRow);
-      expect(range.toRow).toBe(toRow);
-      expect(range.fromCell).toBe(fromCell);
-      expect(range.toCell).toBe(toCell);
-    }
+      expect(selectedRanges.length).toBe(2);
+      expectRangeToMatch(selectedRanges[0], 0, 1, 0, 3);
+      expectRangeToMatch(selectedRanges[1], 2, 1, 2, 3);
+    });
   });
-})
-;
\ No newline at end of file
+
+  function pressKey(keyCode) {
+    var pressEvent = new $.Event("keydown");
+    pressEvent.which = keyCode;
+
+    $(container.find('.grid-canvas')).trigger(pressEvent);
+  }
+
+  function pressShiftPlusKey(keyCode) {
+    var pressEvent = new $.Event("keydown");
+    pressEvent.shiftKey = true;
+    pressEvent.which = keyCode;
+
+    $(container.find('.grid-canvas')).trigger(pressEvent);
+  }
+
+  function expectOneSelectedRange(fromRow, fromCell, toRow, toCell) {
+    var selectedRanges = grid.getSelectionModel().getSelectedRanges();
+    expect(selectedRanges.length).toBe(1);
+    expectRangeToMatch(selectedRanges[0], fromRow, fromCell, toRow, toCell);
+  }
+
+  function expectRangeToMatch(range, fromRow, fromCell, toRow, toCell) {
+    expect(range.fromRow).toBe(fromRow);
+    expect(range.toRow).toBe(toRow);
+    expect(range.fromCell).toBe(fromCell);
+    expect(range.toCell).toBe(toCell);
+  }
+});
diff --git a/web/regression/javascript/slickgrid/cell_selector_spec.js b/web/regression/javascript/slickgrid/cell_selector_spec.js
index ed7c0aee..b32fdf80 100644
--- a/web/regression/javascript/slickgrid/cell_selector_spec.js
+++ b/web/regression/javascript/slickgrid/cell_selector_spec.js
@@ -7,71 +7,69 @@
 //
 //////////////////////////////////////////////////////////////
 
-define(["jquery",
-    "slickgrid/slick.grid",
-    "sources/selection/xcell_selection_model",
-    "sources/slickgrid/cell_selector",
-    "sources/selection/range_selection_helper"
-  ],
-  function ($, SlickGrid, XCellSelectionModel, CellSelector, RangeSelectionHelper) {
-    describe("CellSelector", function () {
-      var container, columns, cellSelector, data, cellSelectionModel, grid;
-      beforeEach(function () {
-        container = $("<div></div>");
-        container.height(9999);
-        container.width(9999);
-        columns = [{
-          name: 'some-column-name',
-        }, {
-          name: 'second column',
-        }];
+import $ from "jquery";
+import SlickGrid from "slickgrid.grid";
+import XCellSelectionModel from "sources/selection/xcell_selection_model";
+import CellSelector from "sources/slickgrid/cell_selector";
+import RangeSelectionHelper from "sources/selection/range_selection_helper";
 
-        cellSelector = new CellSelector();
+describe("CellSelector", function () {
+  var container, columns, cellSelector, data, cellSelectionModel, grid;
+  beforeEach(function () {
+    container = $("<div></div>");
+    container.height(9999);
+    container.width(9999);
+    columns = [{
+      name: 'some-column-name',
+    }, {
+      name: 'second column',
+    }];
 
-        data = [];
-        for (var i = 0; i < 10; i++) {
-          data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
-        }
-        grid = new SlickGrid(container, data, columns);
+    cellSelector = new CellSelector();
 
-        cellSelectionModel = new XCellSelectionModel();
-        grid.setSelectionModel(cellSelectionModel);
+    data = [];
+    for (var i = 0; i < 10; i++) {
+      data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
+    }
+    grid = new Slick.Grid(container, data, columns);
 
-        grid.registerPlugin(cellSelector);
-        grid.invalidate();
+    cellSelectionModel = new XCellSelectionModel();
+    grid.setSelectionModel(cellSelectionModel);
 
-        $("body").append(container);
-      });
+    grid.registerPlugin(cellSelector);
+    grid.invalidate();
 
-      afterEach(function () {
-        $("body").find(container).remove();
-      });
+    $("body").append(container);
+  });
 
-      describe("when the user clicks or tabs to a cell", function () {
-        it("sets the selected range to that cell", function () {
-          var row = 1, column = 0;
-          $(container.find(".slick-row .slick-cell.l" + column)[row]).click();
+  afterEach(function () {
+    $("body").find(container).remove();
+  });
 
-          var selectedRanges = cellSelectionModel.getSelectedRanges();
-          expect(selectedRanges.length).toBe(1);
-          expect(selectedRanges[0].fromCell).toBe(0);
-          expect(selectedRanges[0].toCell).toBe(0);
-          expect(selectedRanges[0].fromRow).toBe(1);
-          expect(selectedRanges[0].toRow).toBe(1);
-        });
+  describe("when the user clicks or tabs to a cell", function () {
+    it("sets the selected range to that cell", function () {
+      var row = 1, column = 0;
+      $(container.find(".slick-row .slick-cell.l" + column)[row]).click();
 
-        it("deselects previously selected ranges", function () {
-          var row2Range = RangeSelectionHelper.rangeForRow(grid, 2);
-          var ranges = RangeSelectionHelper.addRange(cellSelectionModel.getSelectedRanges(),
-            row2Range);
-          cellSelectionModel.setSelectedRanges(ranges);
+      var selectedRanges = cellSelectionModel.getSelectedRanges();
+      expect(selectedRanges.length).toBe(1);
+      expect(selectedRanges[0].fromCell).toBe(0);
+      expect(selectedRanges[0].toCell).toBe(0);
+      expect(selectedRanges[0].fromRow).toBe(1);
+      expect(selectedRanges[0].toRow).toBe(1);
+    });
+
+    it("deselects previously selected ranges", function () {
+      var row2Range = RangeSelectionHelper.rangeForRow(grid, 2);
+      var ranges = RangeSelectionHelper.addRange(cellSelectionModel.getSelectedRanges(),
+        row2Range);
+      cellSelectionModel.setSelectedRanges(ranges);
 
-          var row = 4, column = 1;
-          $(container.find(".slick-row .slick-cell.l" + column)[row]).click();
+      var row = 4, column = 1;
+      $(container.find(".slick-row .slick-cell.l" + column)[row]).click();
 
-          expect(RangeSelectionHelper.isRangeSelected(cellSelectionModel.getSelectedRanges(), row2Range))
-            .toBe(false);
-        });
-      });
+      expect(RangeSelectionHelper.isRangeSelected(cellSelectionModel.getSelectedRanges(), row2Range))
+        .toBe(false);
     });
-  });
\ No newline at end of file
+  });
+});
diff --git a/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js b/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js
index 14768227..884ae415 100644
--- a/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js
+++ b/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js
@@ -7,137 +7,139 @@
 //
 //////////////////////////////////////////////////////////////
 
-define([
-    'sources/slickgrid/event_handlers/handle_query_output_keyboard_event',
-    'sources/selection/clipboard',
-    'sources/selection/range_selection_helper',
-    'sources/selection/xcell_selection_model',
-    'slickgrid'
-  ],
-function (handleQueryOutputKeyboardEvent, clipboard, RangeSelectionHelper, XCellSelectionModel) {
-  var Slick = window.Slick;
-
-  describe('#handleQueryOutputKeyboardEvent', function () {
-    var event, view, grid, slickEvent;
+import HandleQueryOutputKeyboardEvent from 'sources/slickgrid/event_handlers/handle_query_output_keyboard_event';
+import clipboard from 'sources/selection/clipboard';
+import RangeSelectionHelper from 'sources/selection/range_selection_helper';
+import XCellSelectionModel from 'sources/selection/xcell_selection_model';
+import Slick from 'slickgrid';
+import 'slickgrid.grid';
+
+
+describe('#handleQueryOutputKeyboardEvent', function () {
+  var event, view, grid, slickEvent;
+  var SlickGrid = Slick.Grid;
+  var handleQueryOutputKeyboardEvent;
+
+  beforeEach(function () {
+    event = {
+      shiftKey: false,
+      ctrlKey: false,
+      metaKey: false,
+      which: -1,
+      keyCode: -1,
+      preventDefault: jasmine.createSpy('preventDefault')
+    };
+
+    var data = [['', '0,0-cell-content', '0,1-cell-content'],
+      ['', '1,0-cell-content', '1,1-cell-content'],
+      ['', '2,0-cell-content', '2,1-cell-content']];
+    var columnDefinitions = [{name: 'checkboxColumn'}, {pos: 1, name: 'firstColumn'}, {
+      pos: 2,
+      name: 'secondColumn'
+    }];
+    grid = new SlickGrid($('<div></div>'), data, columnDefinitions);
+    grid.setSelectionModel(new XCellSelectionModel());
+
+    slickEvent = {
+      grid: grid
+    };
+
+    view = {};
+    spyOn(clipboard, 'copyTextToClipboard');
+    handleQueryOutputKeyboardEvent = HandleQueryOutputKeyboardEvent.bind(window);
+    debugger
+  });
+
+  describe("when a range is selected", function () {
     beforeEach(function () {
-      event = {
-        shiftKey: false,
-        ctrlKey: false,
-        metaKey: false,
-        which: -1,
-        keyCode: -1,
-        preventDefault: jasmine.createSpy('preventDefault')
-      };
-
-      var data = [['', '0,0-cell-content', '0,1-cell-content'],
-        ['', '1,0-cell-content', '1,1-cell-content'],
-        ['', '2,0-cell-content', '2,1-cell-content']];
-      var columnDefinitions = [{name: 'checkboxColumn'}, {pos: 1, name: 'firstColumn'}, {
-        pos: 2,
-        name: 'secondColumn'
-      }];
-      grid = new Slick.Grid($('<div></div>'), data, columnDefinitions);
-      grid.setSelectionModel(new XCellSelectionModel());
-
-      slickEvent = {
-        grid: grid
-      };
-
-      view = {};
-      spyOn(clipboard, 'copyTextToClipboard');
+      grid.getSelectionModel().setSelectedRanges([
+        RangeSelectionHelper.rangeForRow(grid, 0),
+        RangeSelectionHelper.rangeForRow(grid, 2),
+      ]);
     });
 
-    describe("when a range is selected", function () {
+    describe("pressing Command + C", function () {
       beforeEach(function () {
-        grid.getSelectionModel().setSelectedRanges([
-          RangeSelectionHelper.rangeForRow(grid, 0),
-          RangeSelectionHelper.rangeForRow(grid, 2),
-        ]);
+        event.metaKey = true;
+        event.keyCode = 67;
       });
 
-      describe("pressing Command + C", function () {
-        beforeEach(function () {
-          event.metaKey = true;
-          event.keyCode = 67;
-        });
-
-        it("copies the cell content to the clipboard", function () {
-          handleQueryOutputKeyboardEvent(event, slickEvent);
+      it("copies the cell content to the clipboard", function () {
+        handleQueryOutputKeyboardEvent(event, slickEvent);
 
-          expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith("'0,0-cell-content','0,1-cell-content'\n'2,0-cell-content','2,1-cell-content'");
-        });
+        expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith("'0,0-cell-content','0,1-cell-content'\n'2,0-cell-content','2,1-cell-content'");
       });
+    });
 
-      describe("pressing Ctrl + C", function () {
-        beforeEach(function () {
-          event.ctrlKey = true;
-          event.keyCode = 67;
-        });
+    describe("pressing Ctrl + C", function () {
+      beforeEach(function () {
+        event.ctrlKey = true;
+        event.keyCode = 67;
+      });
 
-        it("copies the cell content to the clipboard", function () {
-          handleQueryOutputKeyboardEvent(event, slickEvent);
+      it("copies the cell content to the clipboard", function () {
+        handleQueryOutputKeyboardEvent(event, slickEvent);
 
-          expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith("'0,0-cell-content','0,1-cell-content'\n'2,0-cell-content','2,1-cell-content'");
-        });
+        expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith("'0,0-cell-content','0,1-cell-content'\n'2,0-cell-content','2,1-cell-content'");
       });
+    });
 
-      describe("pressing Command + A", function () {
-        beforeEach(function () {
-          event.metaKey = true;
-          event.keyCode = 65;
-        });
+    describe("pressing Command + A", function () {
+      beforeEach(function () {
+        event.metaKey = true;
+        event.keyCode = 65;
+      });
 
-        it("selects the entire grid to ranges", function () {
-          handleQueryOutputKeyboardEvent(event, slickEvent);
+      it("selects the entire grid to ranges", function () {
+        handleQueryOutputKeyboardEvent(event, slickEvent);
 
-          expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
-          expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
-        });
+        expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
+        expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
       });
+    });
 
-      describe("pressing Ctrl + A", function () {
-        beforeEach(function () {
-          event.ctrlKey = true;
-          event.keyCode = 65;
-        });
+    describe("pressing Ctrl + A", function () {
+      beforeEach(function () {
+        event.ctrlKey = true;
+        event.keyCode = 65;
+      });
 
-        it("selects the entire grid to ranges", function () {
-          handleQueryOutputKeyboardEvent(event, slickEvent);
+      it("selects the entire grid to ranges", function () {
+        handleQueryOutputKeyboardEvent(event, slickEvent);
 
-          expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
-          expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
-        });
+        expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
+        expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
       });
     });
+  });
 
-    describe("when no ranges are selected", function () {
-      describe("pressing Command + A", function () {
-        beforeEach(function () {
-          event.metaKey = true;
-          event.keyCode = 65;
-        });
+  describe("when no ranges are selected", function () {
+    describe("pressing Command + A", function () {
+      beforeEach(function () {
+        event.metaKey = true;
+        event.keyCode = 65;
+      });
 
-        it("selects the entire grid to ranges", function () {
-          handleQueryOutputKeyboardEvent(event, slickEvent);
+      it("selects the entire grid to ranges", function () {
+        handleQueryOutputKeyboardEvent(event, slickEvent);
 
-          expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
-          expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
-        });
+        expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
+        expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
       });
+    });
 
-      describe("pressing Ctrl + A", function () {
-        beforeEach(function () {
-          event.ctrlKey = true;
-          event.keyCode = 65;
-        });
+    describe("pressing Ctrl + A", function () {
+      beforeEach(function () {
+        event.ctrlKey = true;
+        event.keyCode = 65;
+      });
 
-        it("selects the entire grid to ranges", function () {
-          handleQueryOutputKeyboardEvent(event, slickEvent);
+      it("selects the entire grid to ranges", function () {
+        handleQueryOutputKeyboardEvent(event, slickEvent);
 
-          expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
-          expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
-        });
+        expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
+        expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
       });
     });
   });
-});
\ No newline at end of file
+});
diff --git a/web/regression/javascript/test-main.js b/web/regression/javascript/test-main.js
index 8bb8c027..e69de29b 100644
--- a/web/regression/javascript/test-main.js
+++ b/web/regression/javascript/test-main.js
@@ -1,138 +0,0 @@
-//////////////////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2017, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////////////////
-
-var allTestFiles = [];
-var TEST_REGEXP = /(spec|test)\.js$/i;
-
-// Get a list of all the test files to include
-Object.keys(window.__karma__.files).forEach(function (file) {
-  if (TEST_REGEXP.test(file)) {
-    // Normalize paths to RequireJS module names.
-    // If you require sub-dependencies of test files to be loaded as-is (requiring file extension)
-    // then do not normalize the paths
-    var normalizedTestModule = file.replace(/^\/base\/|\.js$/g, '');
-    allTestFiles.push(normalizedTestModule)
-  }
-});
-
-var sourcesDir = '/base/pgadmin/static/';
-require.config({
-  // Karma serves files under /base, which is the basePath from your config file
-  baseUrl: '/base',
-
-  paths: {
-    'pgadmin': sourcesDir + 'js/pgadmin',
-    'alertify': sourcesDir + 'vendor/alertifyjs/alertify',
-    'jquery': sourcesDir + 'vendor/jquery/jquery-1.11.2',
-    'jquery.ui': sourcesDir + 'vendor/jquery-ui/jquery-ui-1.11.3',
-    'jquery.event.drag': sourcesDir + 'vendor/jquery-ui/jquery.event.drag-2.2',
-    'underscore': sourcesDir + 'vendor/underscore/underscore',
-    'underscore.string': sourcesDir + 'vendor/underscore/underscore.string',
-    'slickgrid': sourcesDir + 'vendor/slickgrid/slick.core',
-    'slickgrid/slick.grid': sourcesDir + 'vendor/slickgrid/slick.grid',
-    'translations': '/base/regression/javascript/fake_translations',
-    'sources': sourcesDir + 'js',
-    'browser': '/base/pgadmin/browser/static/js'
-  },
-
-  shim: {
-    'underscore': {
-      exports: '_'
-    },
-    "slickgrid": {
-      "deps": [
-        'jquery', "jquery.ui", "jquery.event.drag"
-      ],
-      "exports": 'window.Slick'
-    },
-    "slickgrid/slick.grid": {
-      "deps": [
-        'jquery', "jquery.ui", "jquery.event.drag", "slickgrid"
-      ],
-      "exports": 'window.Slick.Grid'
-    },
-    "sources/slickgrid/pgslick.cellrangedecorator": {
-      "deps": [
-        "jquery"
-      ],
-      "exports": 'PGRowRangeDecorator'
-    },
-    "sources/slickgrid/pgslick.cellrangeselector": {
-      "deps": [
-        "jquery", "sources/slickgrid/pgslick.cellrangedecorator"
-      ],
-      "exports": 'PGCellRangeSelector'
-    },
-    "sources/selection/xcell_selection_model": {
-      "deps": [
-        "jquery", "sources/slickgrid/pgslick.cellrangeselector"
-      ],
-      "exports": 'XCellSelectionModel'
-    },
-    "backbone": {
-      "deps": ['underscore', 'jquery'],
-      "exports": 'Backbone'
-    },
-    "backbone.paginator": {
-      "deps": ['underscore', 'jquery', 'backbone']
-    },
-    "bootstrap": {
-      "deps": ['jquery'],
-    },
-    "backgrid": {
-      "deps": ['backform'],
-      "exports": 'Backgrid',
-    },
-    "backgrid.select.all": {
-      "deps": ['backgrid']
-    },
-    "backgrid.paginator": {
-      "deps": ['backgrid', 'backbone.paginator']
-    },
-    "backgrid.filter": {
-      "deps": ['backgrid']
-    },
-    "backgrid.sizeable.columns": {
-      "deps": ['backgrid']
-    },
-    "bootstrap.switch": {
-      "deps": ['jquery', 'bootstrap'],
-      "exports": 'jQuery.fn.bootstrapSwitch'
-    },
-    "select2": {
-      "deps": ['jquery'],
-      "exports": 'jQuery.fn.select2'
-    },
-    "bootstrap.datepicker": {
-      "deps": ['jquery', 'bootstrap'],
-      "exports": 'jQuery.fn.datepicker'
-    },
-    "bootstrap.datetimepicker": {
-      "deps": ['jquery', 'bootstrap', 'moment'],
-      "exports": 'jQuery.fn.datetimepicker'
-    },
-    "pgadmin.backgrid": {
-      "deps": ["backgrid", "bootstrap.datetimepicker", "bootstrap.switch"],
-    },
-    "pgadmin.backform": {
-      "deps": ['backform', "pgadmin.backgrid", "select2"],
-    },
-    "jquery.event.drag": {
-      "deps": ['jquery'], "exports": 'jQuery.fn.drag'
-    },
-    "jquery.ui": {"deps": ['jquery']}
-  },
-
-  // dynamically load all test files
-  deps: allTestFiles,
-
-  // we have to kickoff jasmine, as it is asynchronous
-  callback: window.__karma__.start
-});
-
diff --git a/web/regression/requirements.txt b/web/regression/requirements.txt
index 5959a183..f44953d5 100644
--- a/web/regression/requirements.txt
+++ b/web/regression/requirements.txt
@@ -4,6 +4,7 @@ testscenarios==0.5.0
 testtools==2.0.0
 traceback2==1.4.0
 unittest2==1.1.0
+mock~=2.0.0
 
 # Leave this at the end because there is a bug where the '--install-option' is applied to all subsequent requirements
 chromedriver_installer==0.0.6 --install-option='--chromedriver-version=2.29'
diff --git a/web/webpack.config.js b/web/webpack.config.js
new file mode 100644
index 00000000..91586592
--- /dev/null
+++ b/web/webpack.config.js
@@ -0,0 +1,29 @@
+/* eslint-env node */
+
+module.exports = {
+  context: __dirname + '/pgadmin/static/jsx',
+  entry: './components.jsx',
+  output: {
+    libraryTarget: 'amd',
+    path: __dirname + '/pgadmin/static/js/generated',
+    filename: 'reactComponents.js',
+  },
+
+  module: {
+    rules: [{
+      test: /\.jsx?$/,
+      exclude: /node_modules/,
+      use: {
+        loader: 'babel-loader',
+        options: {
+          presets: ['es2015', 'react'],
+        },
+      },
+    },
+    ],
+  },
+
+  resolve: {
+    extensions: ['.js', '.jsx'],
+  },
+};
\ No newline at end of file
diff --git a/web/webpack.test.config.js b/web/webpack.test.config.js
new file mode 100644
index 00000000..29df6dd8
--- /dev/null
+++ b/web/webpack.test.config.js
@@ -0,0 +1,70 @@
+/* eslint-env node */
+const path = require('path');
+const webpack = require('webpack');
+
+const sourcesDir = path.resolve(__dirname, 'pgadmin/static');
+const regressionDir = path.resolve(__dirname, 'regression');
+
+module.exports = {
+  plugins: [
+    new webpack.ProvidePlugin({
+      $: 'jquery',
+      jQuery: 'jquery',
+      _: 'underscore',
+      'underscore.string': 'underscore.string',
+    }),
+  ],
+
+  module: {
+    rules: [
+      {
+        test: /\.jsx?$/,
+        exclude: [/node_modules/, /vendor/],
+        use: {
+          loader: 'babel-loader',
+          options: {
+            presets: ['es2015'],
+          },
+        },
+      },
+      {
+        test: /.*slickgrid\/slick\.(?!core)*/,
+        loader: 'imports-loader?' +
+        'jquery.ui' +
+        ',jquery.event.drag' +
+        ',slickgrid',
+      }, {
+        test: /.*slickgrid\/plugins\/slick\.rowselectionmodel/,
+        loader: 'imports-loader?' +
+        'jquery.ui' +
+        ',jquery.event.drag' +
+        ',slickgrid' +
+        '!exports-loader?' +
+        'Slick.RowSelectionModel',
+      }, {
+        test: /.*slickgrid\/slick\.core.*/,
+        loader: 'imports-loader?' +
+        'jquery.ui' +
+        ',jquery.event.drag' +
+        '!exports-loader?' +
+        'Slick',
+      }],
+  },
+
+  resolve: {
+    alias: {
+      'alertify': sourcesDir + '/vendor/alertifyjs/alertify',
+      'jquery': sourcesDir + '/vendor/jquery/jquery-1.11.2',
+      'jquery.ui': sourcesDir + '/vendor/jquery-ui/jquery-ui-1.11.3',
+      'jquery.event.drag': sourcesDir + '/vendor/jquery-ui/jquery.event.drag-2.2',
+      'sources': sourcesDir + '/js',
+      'underscore.string': sourcesDir + '/vendor/underscore/underscore.string',
+      'translations': regressionDir + '/javascript/fake_translations',
+      'slickgrid': sourcesDir + '/vendor/slickgrid/slick.core',
+      'slickgrid.grid': sourcesDir + '/vendor/slickgrid/slick.grid',
+      'slickgrid.rowselectionmodel': sourcesDir + '/vendor/slickgrid/plugins/slick.rowselectionmodel',
+      'browser': path.resolve(__dirname, 'pgadmin/browser/static/js'),
+      'pgadmin': sourcesDir + '/js/pgadmin',
+    },
+  },
+};
diff --git a/web/yarn.lock b/web/yarn.lock
index e69de29b..bfd9171d 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -0,0 +1,4526 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+JSONStream@^1.0.3:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.1.tgz#707f761e01dae9e16f1bcf93703b78c709665...;
+  dependencies:
+    jsonparse "^1.2.0"
+    through ">=2.2.7 <3"
+
+abbrev@1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f";
+
[email protected]:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca...;
+  dependencies:
+    mime-types "~2.1.11"
+    negotiator "0.6.1"
+
+acorn-dynamic-import@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef67950...;
+  dependencies:
+    acorn "^4.0.3"
+
+acorn-jsx@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36...;
+  dependencies:
+    acorn "^3.0.4"
+
+acorn@^3.0.4:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a";
+
+acorn@^4.0.3, acorn@^4.0.4:
+  version "4.0.13"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787";
+
+acorn@^5.0.1:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d";
+
[email protected]:
+  version "0.8.2"
+  resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f";
+
+ajv-keywords@^1.0.0, ajv-keywords@^1.1.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b8...;
+
+ajv@^4.7.0, ajv@^4.9.1:
+  version "4.11.8"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536";
+  dependencies:
+    co "^4.6.0"
+    json-stable-stringify "^1.0.1"
+
+align-text@^0.1.1, align-text@^0.1.3:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad...;
+  dependencies:
+    kind-of "^3.0.2"
+    longest "^1.0.1"
+    repeat-string "^1.5.2"
+
+amdefine@>=0.0.4:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5&...;
+
+ansi-escapes@^1.1.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911...;
+
+ansi-regex@^2.0.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d65...;
+
+ansi-styles@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c...;
+
+anymatch@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507&...;
+  dependencies:
+    arrify "^1.0.0"
+    micromatch "^2.1.5"
+
+aproba@^1.0.3:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1";
+
+are-we-there-yet@~1.1.2:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373...;
+  dependencies:
+    delegates "^1.0.0"
+    readable-stream "^2.0.6"
+
+argparse@^1.0.7:
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86&...;
+  dependencies:
+    sprintf-js "~1.0.2"
+
+arr-diff@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf&...;
+  dependencies:
+    arr-flatten "^1.0.1"
+
+arr-flatten@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.3.tgz#a274ed85ac08849b6bd7847c4580745dc51...;
+
+array-filter@~0.0.0:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67ca...;
+
+array-map@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa66...;
+
+array-reduce@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278...;
+
+array-slice@^0.2.3:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86...;
+
+array-union@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f2477...;
+  dependencies:
+    array-uniq "^1.0.1"
+
+array-uniq@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24f...;
+
+array-unique@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c5...;
+
+array.prototype.find@^2.0.1:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.0.4.tgz#556a5c5362c086483...;
+  dependencies:
+    define-properties "^1.1.2"
+    es-abstract "^1.7.0"
+
[email protected]:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a27...;
+
+arrify@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d";
+
+asap@~2.0.3:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f";
+
+asn1.js@^4.0.0:
+  version "4.9.1"
+  resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40...;
+  dependencies:
+    bn.js "^4.0.0"
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+
+asn1@~0.2.3:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86";
+
[email protected], assert-plus@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4...;
+
+assert-plus@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101...;
+
+assert@^1.1.1, assert@^1.4.0:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91";
+  dependencies:
+    util "0.10.3"
+
+astw@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/astw/-/astw-2.2.0.tgz#7bd41784d32493987aeb239b6b4e1c57a873b917";
+  dependencies:
+    acorn "^4.0.3"
+
+async-each@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d336...;
+
+async@^2.1.2:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/async/-/async-2.4.1.tgz#62a56b279c98a11d0987096a01cc3eeb8eb7bbd7";
+  dependencies:
+    lodash "^4.14.0"
+
+async@~0.9.0:
+  version "0.9.2"
+  resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d";
+
+asynckit@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79&...;
+
+aws-sign2@~0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794...;
+
+aws4@^1.2.1:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e";
+
+axios@^0.16.1:
+  version "0.16.2"
+  resolved "https://registry.yarnpkg.com/axios/-/axios-0.16.2.tgz#ba4f92f17167dfbab40983785454b9ac149c3c6d";
+  dependencies:
+    follow-redirects "^1.2.3"
+    is-buffer "^1.1.5"
+
+babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574...;
+  dependencies:
+    chalk "^1.1.0"
+    esutils "^2.0.2"
+    js-tokens "^3.0.0"
+
+babel-core@^6.0.0, babel-core@^6.0.14, babel-core@^6.24.1, babel-core@~6.24.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.1.tgz#8c428564dce1e1f41fb337ec34f4c3b022b5...;
+  dependencies:
+    babel-code-frame "^6.22.0"
+    babel-generator "^6.24.1"
+    babel-helpers "^6.24.1"
+    babel-messages "^6.23.0"
+    babel-register "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+    babylon "^6.11.0"
+    convert-source-map "^1.1.0"
+    debug "^2.1.1"
+    json5 "^0.5.0"
+    lodash "^4.2.0"
+    minimatch "^3.0.2"
+    path-is-absolute "^1.0.0"
+    private "^0.1.6"
+    slash "^1.0.0"
+    source-map "^0.5.0"
+
+babel-generator@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.24.1.tgz#e715f486c58ded25649d888944...;
+  dependencies:
+    babel-messages "^6.23.0"
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+    detect-indent "^4.0.0"
+    jsesc "^1.3.0"
+    lodash "^4.2.0"
+    source-map "^0.5.0"
+    trim-right "^1.0.1"
+
+babel-helper-builder-react-jsx@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.24.1....;
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+    esutils "^2.0.0"
+
+babel-helper-call-delegate@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6...;
+  dependencies:
+    babel-helper-hoist-variables "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-helper-define-map@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz#7a9747f258...;
+  dependencies:
+    babel-helper-function-name "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+    lodash "^4.2.0"
+
+babel-helper-function-name@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d347...;
+  dependencies:
+    babel-helper-get-function-arity "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-helper-get-function-arity@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24....;
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-helper-hoist-variables@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#...;
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-helper-optimise-call-expression@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expr...;
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-helper-regex@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz#d36e22fab1008d79d886...;
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+    lodash "^4.2.0"
+
+babel-helper-replace-supers@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf...;
+  dependencies:
+    babel-helper-optimise-call-expression "^6.24.1"
+    babel-messages "^6.23.0"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-helpers@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26...;
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-loader@~6.4.1:
+  version "6.4.1"
+  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-6.4.1.tgz#0b34112d5b0748a8dcdbf51acf6f9bd42...;
+  dependencies:
+    find-cache-dir "^0.1.1"
+    loader-utils "^0.2.16"
+    mkdirp "^0.5.1"
+    object-assign "^4.0.1"
+
+babel-messages@^6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5e...;
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-check-es2015-constants@^6.22.0, babel-plugin-check-es2015-constants@^6.3.13:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constan...;
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-syntax-flow@^6.18.0:
+  version "6.18.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a...;
+
+babel-plugin-syntax-jsx@^6.3.13, babel-plugin-syntax-jsx@^6.8.0:
+  version "6.18.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e...;
+
+babel-plugin-transform-es2015-arrow-functions@^6.22.0, babel-plugin-transform-es2015-arrow-functions@^6.3.13:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-...;
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-block-scoped-functions@^6.22.0, babel-plugin-transform-es2015-block-scoped-functions@^6.3.13:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-tra...;
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-block-scoping@^6.24.1, babel-plugin-transform-es2015-block-scoping@^6.9.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es...;
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+    lodash "^4.2.0"
+
+babel-plugin-transform-es2015-classes@^6.24.1, babel-plugin-transform-es2015-classes@^6.9.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-c...;
+  dependencies:
+    babel-helper-define-map "^6.24.1"
+    babel-helper-function-name "^6.24.1"
+    babel-helper-optimise-call-expression "^6.24.1"
+    babel-helper-replace-supers "^6.24.1"
+    babel-messages "^6.23.0"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-computed-properties@^6.24.1, babel-plugin-transform-es2015-computed-properties@^6.3.13:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transf...;
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-destructuring@^6.22.0, babel-plugin-transform-es2015-destructuring@^6.9.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es...;
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-duplicate-keys@^6.24.1, babel-plugin-transform-es2015-duplicate-keys@^6.6.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-e...;
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-for-of@^6.22.0, babel-plugin-transform-es2015-for-of@^6.6.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-fo...;
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-function-name@^6.24.1, babel-plugin-transform-es2015-function-name@^6.9.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es...;
+  dependencies:
+    babel-helper-function-name "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-literals@^6.22.0, babel-plugin-transform-es2015-literals@^6.3.13:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-...;
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-modules-amd@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es20...;
+  dependencies:
+    babel-plugin-transform-es2015-modules-commonjs "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-modules-commonjs@^6.24.1, babel-plugin-transform-es2015-modules-commonjs@^6.6.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform...;
+  dependencies:
+    babel-plugin-transform-strict-mode "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-modules-systemjs@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform...;
+  dependencies:
+    babel-helper-hoist-variables "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-modules-umd@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es20...;
+  dependencies:
+    babel-plugin-transform-es2015-modules-amd "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-object-super@^6.24.1, babel-plugin-transform-es2015-object-super@^6.3.13:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2...;
+  dependencies:
+    babel-helper-replace-supers "^6.24.1"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-parameters@^6.24.1, babel-plugin-transform-es2015-parameters@^6.9.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es201...;
+  dependencies:
+    babel-helper-call-delegate "^6.24.1"
+    babel-helper-get-function-arity "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-shorthand-properties@^6.24.1, babel-plugin-transform-es2015-shorthand-properties@^6.3.13:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-trans...;
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-spread@^6.22.0, babel-plugin-transform-es2015-spread@^6.3.13:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-sp...;
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-sticky-regex@^6.24.1, babel-plugin-transform-es2015-sticky-regex@^6.3.13:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2...;
+  dependencies:
+    babel-helper-regex "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-template-literals@^6.22.0, babel-plugin-transform-es2015-template-literals@^6.6.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transfor...;
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-typeof-symbol@^6.22.0, babel-plugin-transform-es2015-typeof-symbol@^6.6.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es...;
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-unicode-regex@^6.24.1, babel-plugin-transform-es2015-unicode-regex@^6.3.13:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es...;
+  dependencies:
+    babel-helper-regex "^6.24.1"
+    babel-runtime "^6.22.0"
+    regexpu-core "^2.0.0"
+
+babel-plugin-transform-flow-strip-types@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-s...;
+  dependencies:
+    babel-plugin-syntax-flow "^6.18.0"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-react-display-name@^6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-reac...;
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-react-jsx-self@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-js...;
+  dependencies:
+    babel-plugin-syntax-jsx "^6.8.0"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-react-jsx-source@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-...;
+  dependencies:
+    babel-plugin-syntax-jsx "^6.8.0"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-react-jsx@^6.23.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.2...;
+  dependencies:
+    babel-helper-builder-react-jsx "^6.24.1"
+    babel-plugin-syntax-jsx "^6.8.0"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-regenerator@^6.24.1, babel-plugin-transform-regenerator@^6.9.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator...;
+  dependencies:
+    regenerator-transform "0.9.11"
+
+babel-plugin-transform-strict-mode@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode...;
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-polyfill@^6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177...;
+  dependencies:
+    babel-runtime "^6.22.0"
+    core-js "^2.4.0"
+    regenerator-runtime "^0.10.0"
+
+babel-preset-es2015-without-strict@~0.0.4:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/babel-preset-es2015-without-strict/-/babel-preset-es2015-without-strict...;
+  dependencies:
+    babel-plugin-check-es2015-constants "^6.3.13"
+    babel-plugin-transform-es2015-arrow-functions "^6.3.13"
+    babel-plugin-transform-es2015-block-scoped-functions "^6.3.13"
+    babel-plugin-transform-es2015-block-scoping "^6.9.0"
+    babel-plugin-transform-es2015-classes "^6.9.0"
+    babel-plugin-transform-es2015-computed-properties "^6.3.13"
+    babel-plugin-transform-es2015-destructuring "^6.9.0"
+    babel-plugin-transform-es2015-duplicate-keys "^6.6.0"
+    babel-plugin-transform-es2015-for-of "^6.6.0"
+    babel-plugin-transform-es2015-function-name "^6.9.0"
+    babel-plugin-transform-es2015-literals "^6.3.13"
+    babel-plugin-transform-es2015-modules-commonjs "^6.6.0"
+    babel-plugin-transform-es2015-object-super "^6.3.13"
+    babel-plugin-transform-es2015-parameters "^6.9.0"
+    babel-plugin-transform-es2015-shorthand-properties "^6.3.13"
+    babel-plugin-transform-es2015-spread "^6.3.13"
+    babel-plugin-transform-es2015-sticky-regex "^6.3.13"
+    babel-plugin-transform-es2015-template-literals "^6.6.0"
+    babel-plugin-transform-es2015-typeof-symbol "^6.6.0"
+    babel-plugin-transform-es2015-unicode-regex "^6.3.13"
+    babel-plugin-transform-regenerator "^6.9.0"
+
+babel-preset-es2015@~6.24.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea7...;
+  dependencies:
+    babel-plugin-check-es2015-constants "^6.22.0"
+    babel-plugin-transform-es2015-arrow-functions "^6.22.0"
+    babel-plugin-transform-es2015-block-scoped-functions "^6.22.0"
+    babel-plugin-transform-es2015-block-scoping "^6.24.1"
+    babel-plugin-transform-es2015-classes "^6.24.1"
+    babel-plugin-transform-es2015-computed-properties "^6.24.1"
+    babel-plugin-transform-es2015-destructuring "^6.22.0"
+    babel-plugin-transform-es2015-duplicate-keys "^6.24.1"
+    babel-plugin-transform-es2015-for-of "^6.22.0"
+    babel-plugin-transform-es2015-function-name "^6.24.1"
+    babel-plugin-transform-es2015-literals "^6.22.0"
+    babel-plugin-transform-es2015-modules-amd "^6.24.1"
+    babel-plugin-transform-es2015-modules-commonjs "^6.24.1"
+    babel-plugin-transform-es2015-modules-systemjs "^6.24.1"
+    babel-plugin-transform-es2015-modules-umd "^6.24.1"
+    babel-plugin-transform-es2015-object-super "^6.24.1"
+    babel-plugin-transform-es2015-parameters "^6.24.1"
+    babel-plugin-transform-es2015-shorthand-properties "^6.24.1"
+    babel-plugin-transform-es2015-spread "^6.22.0"
+    babel-plugin-transform-es2015-sticky-regex "^6.24.1"
+    babel-plugin-transform-es2015-template-literals "^6.22.0"
+    babel-plugin-transform-es2015-typeof-symbol "^6.22.0"
+    babel-plugin-transform-es2015-unicode-regex "^6.24.1"
+    babel-plugin-transform-regenerator "^6.24.1"
+
+babel-preset-flow@^6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz#e71218887085ae9a24b5be...;
+  dependencies:
+    babel-plugin-transform-flow-strip-types "^6.22.0"
+
+babel-preset-react@~6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-preset-react/-/babel-preset-react-6.23.0.tgz#eb7cee4de98a3f94502c...;
+  dependencies:
+    babel-plugin-syntax-jsx "^6.3.13"
+    babel-plugin-transform-react-display-name "^6.23.0"
+    babel-plugin-transform-react-jsx "^6.23.0"
+    babel-plugin-transform-react-jsx-self "^6.22.0"
+    babel-plugin-transform-react-jsx-source "^6.22.0"
+    babel-preset-flow "^6.23.0"
+
+babel-register@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.24.1.tgz#7e10e13a2f71065bdfad5a1787ba...;
+  dependencies:
+    babel-core "^6.24.1"
+    babel-runtime "^6.22.0"
+    core-js "^2.4.0"
+    home-or-tmp "^2.0.0"
+    lodash "^4.2.0"
+    mkdirp "^0.5.1"
+    source-map-support "^0.4.2"
+
+babel-runtime@^6.18.0, babel-runtime@^6.22.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb3...;
+  dependencies:
+    core-js "^2.4.0"
+    regenerator-runtime "^0.10.0"
+
+babel-template@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5...;
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+    babylon "^6.11.0"
+    lodash "^4.2.0"
+
+babel-traverse@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.24.1.tgz#ab36673fd356f9a0948659e7b338...;
+  dependencies:
+    babel-code-frame "^6.22.0"
+    babel-messages "^6.23.0"
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+    babylon "^6.15.0"
+    debug "^2.2.0"
+    globals "^9.0.0"
+    invariant "^2.2.0"
+    lodash "^4.2.0"
+
+babel-types@^6.19.0, babel-types@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.24.1.tgz#a136879dc15b3606bda0d90c1fc74304c2...;
+  dependencies:
+    babel-runtime "^6.22.0"
+    esutils "^2.0.2"
+    lodash "^4.2.0"
+    to-fast-properties "^1.0.1"
+
+babelify@~7.3.0:
+  version "7.3.0"
+  resolved "https://registry.yarnpkg.com/babelify/-/babelify-7.3.0.tgz#aa56aede7067fd7bd549666ee16dc285087e88e5&...;
+  dependencies:
+    babel-core "^6.0.14"
+    object-assign "^4.0.0"
+
+babylon@^6.11.0, babylon@^6.15.0:
+  version "6.17.2"
+  resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.2.tgz#201d25ef5f892c41bae49488b08db0dd476e9f5c&#...;
+
[email protected]:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947";
+
+balanced-match@^0.4.1:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302...;
+
[email protected]:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad...;
+
+base64-js@^1.0.2:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f...;
+
[email protected]:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6&...;
+
+bcrypt-pbkdf@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c3346...;
+  dependencies:
+    tweetnacl "^0.14.3"
+
+better-assert@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68fa...;
+  dependencies:
+    callsite "1.0.0"
+
+big.js@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978";
+
+binary-extensions@^1.0.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa588...;
+
[email protected]:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921";
+
+block-stream@*:
+  version "0.0.9"
+  resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b33...;
+  dependencies:
+    inherits "~2.0.0"
+
+bluebird@^3.3.0:
+  version "3.5.0"
+  resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c&...;
+
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
+  version "4.11.6"
+  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215";
+
+body-parser@^1.16.1:
+  version "1.17.2"
+  resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.17.2.tgz#f8892abc8f9e627d42aedafbca66bf5ab9...;
+  dependencies:
+    bytes "2.4.0"
+    content-type "~1.0.2"
+    debug "2.6.7"
+    depd "~1.1.0"
+    http-errors "~1.6.1"
+    iconv-lite "0.4.15"
+    on-finished "~2.3.0"
+    qs "6.4.0"
+    raw-body "~2.2.0"
+    type-is "~1.6.15"
+
+boolbase@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e&...;
+
[email protected]:
+  version "2.10.1"
+  resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f";
+  dependencies:
+    hoek "2.x.x"
+
+brace-expansion@^1.1.7:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80...;
+  dependencies:
+    balanced-match "^0.4.1"
+    concat-map "0.0.1"
+
+braces@^0.1.2:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-0.1.5.tgz#c085711085291d8b75fdd74eab0f8597280711e6";
+  dependencies:
+    expand-range "^0.1.0"
+
+braces@^1.8.2:
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7";
+  dependencies:
+    expand-range "^1.8.1"
+    preserve "^0.2.0"
+    repeat-element "^1.1.2"
+
+brorand@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f...;
+
+browser-pack@^6.0.1:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.2.tgz#f86cd6cef4f5300c8e63e07a4d512f65f...;
+  dependencies:
+    JSONStream "^1.0.3"
+    combine-source-map "~0.7.1"
+    defined "^1.0.0"
+    through2 "^2.0.0"
+    umd "^3.0.0"
+
+browser-resolve@^1.11.0, browser-resolve@^1.7.0:
+  version "1.11.2"
+  resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b...;
+  dependencies:
+    resolve "1.1.7"
+
+browserify-aes@^1.0.0, browserify-aes@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567...;
+  dependencies:
+    buffer-xor "^1.0.2"
+    cipher-base "^1.0.0"
+    create-hash "^1.1.0"
+    evp_bytestokey "^1.0.0"
+    inherits "^2.0.1"
+
+browserify-cipher@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95...;
+  dependencies:
+    browserify-aes "^1.0.4"
+    browserify-des "^1.0.0"
+    evp_bytestokey "^1.0.0"
+
+browserify-des@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a...;
+  dependencies:
+    cipher-base "^1.0.1"
+    des.js "^1.0.0"
+    inherits "^2.0.1"
+
+browserify-rsa@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a...;
+  dependencies:
+    bn.js "^4.1.0"
+    randombytes "^2.0.1"
+
+browserify-sign@^4.0.0:
+  version "4.0.4"
+  resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e6...;
+  dependencies:
+    bn.js "^4.1.1"
+    browserify-rsa "^4.0.0"
+    create-hash "^1.1.0"
+    create-hmac "^1.1.2"
+    elliptic "^6.0.0"
+    inherits "^2.0.1"
+    parse-asn1 "^5.0.0"
+
+browserify-zlib@^0.1.4, browserify-zlib@~0.1.2:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241...;
+  dependencies:
+    pako "~0.2.0"
+
+browserify@^14.0.0, browserify@~14.1.0:
+  version "14.1.0"
+  resolved "https://registry.yarnpkg.com/browserify/-/browserify-14.1.0.tgz#0508cc1e7bf4c152312c2fa523e676c0b0b9...;
+  dependencies:
+    JSONStream "^1.0.3"
+    assert "^1.4.0"
+    browser-pack "^6.0.1"
+    browser-resolve "^1.11.0"
+    browserify-zlib "~0.1.2"
+    buffer "^5.0.2"
+    cached-path-relative "^1.0.0"
+    concat-stream "~1.5.1"
+    console-browserify "^1.1.0"
+    constants-browserify "~1.0.0"
+    crypto-browserify "^3.0.0"
+    defined "^1.0.0"
+    deps-sort "^2.0.0"
+    domain-browser "~1.1.0"
+    duplexer2 "~0.1.2"
+    events "~1.1.0"
+    glob "^7.1.0"
+    has "^1.0.0"
+    htmlescape "^1.1.0"
+    https-browserify "~0.0.0"
+    inherits "~2.0.1"
+    insert-module-globals "^7.0.0"
+    labeled-stream-splicer "^2.0.0"
+    module-deps "^4.0.8"
+    os-browserify "~0.1.1"
+    parents "^1.0.1"
+    path-browserify "~0.0.0"
+    process "~0.11.0"
+    punycode "^1.3.2"
+    querystring-es3 "~0.2.0"
+    read-only-stream "^2.0.0"
+    readable-stream "^2.0.2"
+    resolve "^1.1.4"
+    shasum "^1.0.0"
+    shell-quote "^1.6.1"
+    stream-browserify "^2.0.0"
+    stream-http "^2.0.0"
+    string_decoder "~0.10.0"
+    subarg "^1.0.0"
+    syntax-error "^1.1.1"
+    through2 "^2.0.0"
+    timers-browserify "^1.0.1"
+    tty-browserify "~0.0.0"
+    url "~0.11.0"
+    util "~0.10.1"
+    vm-browserify "~0.0.1"
+    xtend "^4.0.0"
+
+buffer-xor@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe...;
+
+buffer@^4.3.0:
+  version "4.9.1"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298";
+  dependencies:
+    base64-js "^1.0.2"
+    ieee754 "^1.1.4"
+    isarray "^1.0.0"
+
+buffer@^5.0.2:
+  version "5.0.6"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.0.6.tgz#2ea669f7eec0b6eda05b08f8b5ff661b28573588";
+  dependencies:
+    base64-js "^1.0.2"
+    ieee754 "^1.1.4"
+
+builtin-modules@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94...;
+
+builtin-status-codes@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c...;
+
[email protected]:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339";
+
+cached-path-relative@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.1.tgz#d09c4b52800aa4c07...;
+
+caller-path@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e8257...;
+  dependencies:
+    callsites "^0.2.0"
+
[email protected]:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20&...;
+
+callsites@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350c...;
+
+camelcase@^1.0.2:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a3...;
+
+camelcase@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0...;
+
+caseless@~0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7...;
+
+caseless@~0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc...;
+
+center-align@^0.1.1:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907...;
+  dependencies:
+    align-text "^0.1.3"
+    lazy-cache "^1.0.3"
+
+chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98";
+  dependencies:
+    ansi-styles "^2.2.1"
+    escape-string-regexp "^1.0.2"
+    has-ansi "^2.0.0"
+    strip-ansi "^3.0.0"
+    supports-color "^2.0.0"
+
+cheerio@^0.22.0:
+  version "0.22.0"
+  resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e&#...;
+  dependencies:
+    css-select "~1.2.0"
+    dom-serializer "~0.1.0"
+    entities "~1.1.1"
+    htmlparser2 "^3.9.1"
+    lodash.assignin "^4.0.9"
+    lodash.bind "^4.1.4"
+    lodash.defaults "^4.0.1"
+    lodash.filter "^4.4.0"
+    lodash.flatten "^4.2.0"
+    lodash.foreach "^4.3.0"
+    lodash.map "^4.4.0"
+    lodash.merge "^4.4.0"
+    lodash.pick "^4.2.1"
+    lodash.reduce "^4.4.0"
+    lodash.reject "^4.4.0"
+    lodash.some "^4.4.0"
+
+chokidar@^1.0.0, chokidar@^1.4.1, chokidar@^1.4.3:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468&...;
+  dependencies:
+    anymatch "^1.3.0"
+    async-each "^1.0.0"
+    glob-parent "^2.0.0"
+    inherits "^2.0.1"
+    is-binary-path "^1.0.0"
+    is-glob "^2.0.0"
+    path-is-absolute "^1.0.0"
+    readdirp "^2.0.0"
+  optionalDependencies:
+    fsevents "^1.0.0"
+
+cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6d...;
+  dependencies:
+    inherits "^2.0.1"
+
+circular-json@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a3...;
+
+cli-cursor@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2...;
+  dependencies:
+    restore-cursor "^1.0.1"
+
+cli-width@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00...;
+
+cliui@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1";
+  dependencies:
+    center-align "^0.1.1"
+    right-align "^0.1.1"
+    wordwrap "0.0.2"
+
+cliui@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d";
+  dependencies:
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+    wrap-ansi "^2.0.0"
+
+co@^4.6.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184";
+
+code-point-at@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3...;
+
+colors@^1.1.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63";
+
+combine-lists@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/combine-lists/-/combine-lists-1.0.1.tgz#458c07e09e0d900fc28b70a3fec2dac...;
+  dependencies:
+    lodash "^4.5.0"
+
+combine-source-map@~0.7.1:
+  version "0.7.2"
+  resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.7.2.tgz#0870312856b307a87cc4a...;
+  dependencies:
+    convert-source-map "~1.1.0"
+    inline-source-map "~0.6.0"
+    lodash.memoize "~3.0.3"
+    source-map "~0.5.3"
+
+combined-stream@^1.0.5, combined-stream@~1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c...;
+  dependencies:
+    delayed-stream "~1.0.0"
+
+commander@^2.9.0:
+  version "2.9.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d...;
+  dependencies:
+    graceful-readlink ">= 1.0.0"
+
+commondir@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253...;
+
[email protected]:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3...;
+
[email protected]:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.1.2.tgz#296594f2753daa63996d2af...;
+
[email protected]:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7...;
+
[email protected]:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae...;
+
[email protected]:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d4...;
+
[email protected]:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.0.tgz#53f7d43c51c5e43f81c8fdd03321c63...;
+  dependencies:
+    inherits "~2.0.1"
+    readable-stream "~2.0.0"
+    typedarray "~0.0.5"
+
+concat-stream@^1.5.2:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e...;
+  dependencies:
+    inherits "^2.0.3"
+    readable-stream "^2.2.2"
+    typedarray "^0.0.6"
+
+concat-stream@~1.5.0, concat-stream@~1.5.1:
+  version "1.5.2"
+  resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261...;
+  dependencies:
+    inherits "~2.0.1"
+    readable-stream "~2.0.0"
+    typedarray "~0.0.5"
+
+connect@^3.6.0:
+  version "3.6.2"
+  resolved "https://registry.yarnpkg.com/connect/-/connect-3.6.2.tgz#694e8d20681bfe490282c8ab886be98f09f42fe7...;
+  dependencies:
+    debug "2.6.7"
+    finalhandler "1.0.3"
+    parseurl "~1.3.1"
+    utils-merge "1.0.0"
+
+console-browserify@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b2...;
+  dependencies:
+    date-now "^0.1.4"
+
+console-control-strings@^1.0.0, console-control-strings@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db...;
+
+constants-browserify@^1.0.0, constants-browserify@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aa...;
+
+content-type@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df...;
+
+convert-source-map@^1.1.0, convert-source-map@^1.1.3:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9...;
+
+convert-source-map@~1.1.0:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3...;
+
[email protected]:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb";
+
+core-js@^1.0.0:
+  version "1.2.7"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636...;
+
+core-js@^2.2.0, core-js@^2.4.0:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e...;
+
+core-util-is@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754...;
+
+create-ecdh@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a3530...;
+  dependencies:
+    bn.js "^4.1.0"
+    elliptic "^6.0.0"
+
+create-hash@^1.1.0, create-hash@^1.1.1, create-hash@^1.1.2:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f581917...;
+  dependencies:
+    cipher-base "^1.0.1"
+    inherits "^2.0.1"
+    ripemd160 "^2.0.0"
+    sha.js "^2.4.0"
+
+create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e372...;
+  dependencies:
+    cipher-base "^1.0.3"
+    create-hash "^1.1.0"
+    inherits "^2.0.1"
+    ripemd160 "^2.0.0"
+    safe-buffer "^5.0.1"
+    sha.js "^2.4.8"
+
[email protected]:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b...;
+  dependencies:
+    boom "2.x.x"
+
+crypto-browserify@^3.0.0, crypto-browserify@^3.11.0:
+  version "3.11.0"
+  resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.0.tgz#3652a0906ab9b2a7e0c3ce...;
+  dependencies:
+    browserify-cipher "^1.0.0"
+    browserify-sign "^4.0.0"
+    create-ecdh "^4.0.0"
+    create-hash "^1.1.0"
+    create-hmac "^1.1.0"
+    diffie-hellman "^5.0.0"
+    inherits "^2.0.1"
+    pbkdf2 "^3.0.3"
+    public-encrypt "^4.0.0"
+    randombytes "^2.0.0"
+
+css-select@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec...;
+  dependencies:
+    boolbase "~1.0.0"
+    css-what "2.1"
+    domutils "1.5.1"
+    nth-check "~1.0.1"
+
[email protected]:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd&...;
+
+custom-event@~1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5...;
+
+d@1:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f";
+  dependencies:
+    es5-ext "^0.10.9"
+
+dashdash@^1.12.0:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0...;
+  dependencies:
+    assert-plus "^1.0.0"
+
+date-now@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b&...;
+
[email protected]:
+  version "0.7.4"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39";
+
[email protected]:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da";
+  dependencies:
+    ms "0.7.1"
+
[email protected]:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.3.3.tgz#40c453e67e6e13c901ddec317af8986cda9eff8c";
+  dependencies:
+    ms "0.7.2"
+
[email protected]:
+  version "2.6.7"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.7.tgz#92bad1f6d05bbb6bba22cca88bcd0ec894c2861e";
+  dependencies:
+    ms "2.0.0"
+
+debug@^2.1.1, debug@^2.2.0, debug@^2.4.5:
+  version "2.6.8"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc";
+  dependencies:
+    ms "2.0.0"
+
+decamelize@^1.0.0, decamelize@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191...;
+
+deep-extend@~0.4.0:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d...;
+
+deep-is@~0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34...;
+
+define-properties@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb73719...;
+  dependencies:
+    foreach "^2.0.5"
+    object-keys "^1.0.8"
+
+defined@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693...;
+
+del@^2.0.2:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8";
+  dependencies:
+    globby "^5.0.0"
+    is-path-cwd "^1.0.0"
+    is-path-in-cwd "^1.0.0"
+    object-assign "^4.0.1"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+    rimraf "^2.2.8"
+
+delayed-stream@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2...;
+
+delegates@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9...;
+
[email protected], depd@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3";
+
+deps-sort@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.0.tgz#091724902e84658260eb910748cccd1af6e21fb...;
+  dependencies:
+    JSONStream "^1.0.3"
+    shasum "^1.0.0"
+    subarg "^1.0.0"
+    through2 "^2.0.0"
+
+des.js@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc";
+  dependencies:
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+
+detect-indent@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a...;
+  dependencies:
+    repeating "^2.0.0"
+
+detective@^4.0.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/detective/-/detective-4.5.0.tgz#6e5a8c6b26e6c7a254b1c6b6d7490d98ec91edd...;
+  dependencies:
+    acorn "^4.0.3"
+    defined "^1.0.0"
+
+di@^0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c";
+
+diffie-hellman@^5.0.0:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded...;
+  dependencies:
+    bn.js "^4.1.0"
+    miller-rabin "^4.0.0"
+    randombytes "^2.0.0"
+
+doctrine@^1.2.2:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa&...;
+  dependencies:
+    esutils "^2.0.2"
+    isarray "^1.0.0"
+
+doctrine@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63&...;
+  dependencies:
+    esutils "^2.0.2"
+    isarray "^1.0.0"
+
+dom-serialize@^2.2.0:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59...;
+  dependencies:
+    custom-event "~1.0.0"
+    ent "~2.2.0"
+    extend "^3.0.0"
+    void-elements "^2.0.0"
+
+dom-serializer@0, dom-serializer@~0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e29...;
+  dependencies:
+    domelementtype "~1.1.1"
+    entities "~1.1.1"
+
+domain-browser@^1.1.1, domain-browser@~1.1.0:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b...;
+
+domelementtype@1, domelementtype@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e...;
+
+domelementtype@~1.1.1:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299...;
+
+domhandler@^2.3.0:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c...;
+  dependencies:
+    domelementtype "1"
+
[email protected], domutils@^1.5.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf&...;
+  dependencies:
+    dom-serializer "0"
+    domelementtype "1"
+
+duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc...;
+  dependencies:
+    readable-stream "^2.0.2"
+
+ecc-jsbn@~0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505&...;
+  dependencies:
+    jsbn "~0.1.0"
+
[email protected]:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d&...;
+
+elliptic@^6.0.0:
+  version "6.4.0"
+  resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df&...;
+  dependencies:
+    bn.js "^4.4.0"
+    brorand "^1.0.1"
+    hash.js "^1.0.0"
+    hmac-drbg "^1.0.0"
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+    minimalistic-crypto-utils "^1.0.0"
+
+emojis-list@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1...;
+
+encodeurl@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d2...;
+
+encoding@^0.1.11:
+  version "0.1.12"
+  resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb...;
+  dependencies:
+    iconv-lite "~0.4.13"
+
[email protected]:
+  version "1.8.3"
+  resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.3.tgz#1798ed93451246453d4c6f635...;
+  dependencies:
+    component-emitter "1.2.1"
+    component-inherit "0.0.3"
+    debug "2.3.3"
+    engine.io-parser "1.3.2"
+    has-cors "1.1.0"
+    indexof "0.0.1"
+    parsejson "0.0.3"
+    parseqs "0.0.5"
+    parseuri "0.0.5"
+    ws "1.1.2"
+    xmlhttprequest-ssl "1.5.3"
+    yeast "0.1.2"
+
[email protected]:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-1.3.2.tgz#937b079f0007d0893ec56d46c...;
+  dependencies:
+    after "0.8.2"
+    arraybuffer.slice "0.0.6"
+    base64-arraybuffer "0.1.5"
+    blob "0.0.4"
+    has-binary "0.1.7"
+    wtf-8 "1.0.0"
+
[email protected]:
+  version "1.8.3"
+  resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-1.8.3.tgz#8de7f97895d20d39b85f88eeee777b2bd42b13d...;
+  dependencies:
+    accepts "1.3.3"
+    base64id "1.0.0"
+    cookie "0.3.1"
+    debug "2.3.3"
+    engine.io-parser "1.3.2"
+    ws "1.1.2"
+
+enhanced-resolve@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.1.0.tgz#9f4b626f577245edcf4b2ad83...;
+  dependencies:
+    graceful-fs "^4.1.2"
+    memory-fs "^0.4.0"
+    object-assign "^4.0.1"
+    tapable "^0.2.5"
+
+ent@~2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d";
+
+entities@^1.1.1, entities@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0&...;
+
+enzyme@~2.8.2:
+  version "2.8.2"
+  resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-2.8.2.tgz#6c8bcb05012abc4aa4bc3213fb23780b9b5b1714";
+  dependencies:
+    cheerio "^0.22.0"
+    function.prototype.name "^1.0.0"
+    is-subset "^0.1.1"
+    lodash "^4.17.2"
+    object-is "^1.0.1"
+    object.assign "^4.0.4"
+    object.entries "^1.0.3"
+    object.values "^1.0.3"
+    prop-types "^15.5.4"
+    uuid "^2.0.3"
+
+errno@^0.1.3:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d";
+  dependencies:
+    prr "~0.0.0"
+
+error-ex@^1.2.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc&...;
+  dependencies:
+    is-arrayish "^0.2.1"
+
+es-abstract@^1.6.1, es-abstract@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.7.0.tgz#dfade774e01bfcd97f96180298c449c8623...;
+  dependencies:
+    es-to-primitive "^1.1.1"
+    function-bind "^1.1.0"
+    is-callable "^1.1.3"
+    is-regex "^1.0.3"
+
+es-to-primitive@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb...;
+  dependencies:
+    is-callable "^1.1.1"
+    is-date-object "^1.0.1"
+    is-symbol "^1.0.1"
+
+es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14:
+  version "0.10.23"
+  resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.23.tgz#7578b51be974207a5487821b56538c224e4e7b38&...;
+  dependencies:
+    es6-iterator "2"
+    es6-symbol "~3.1"
+
+es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e5...;
+  dependencies:
+    d "1"
+    es5-ext "^0.10.14"
+    es6-symbol "^3.1"
+
+es6-map@^0.1.3:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0...;
+  dependencies:
+    d "1"
+    es5-ext "~0.10.14"
+    es6-iterator "~2.0.1"
+    es6-set "~0.1.5"
+    es6-symbol "~3.1.1"
+    event-emitter "~0.3.5"
+
+es6-promise@~4.0.3:
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.0.5.tgz#7882f30adde5b240ccfa7f7d78c54833095...;
+
+es6-set@~0.1.5:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1...;
+  dependencies:
+    d "1"
+    es5-ext "~0.10.14"
+    es6-iterator "~2.0.1"
+    es6-symbol "3.1.1"
+    event-emitter "~0.3.5"
+
[email protected], es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715c...;
+  dependencies:
+    d "1"
+    es5-ext "~0.10.14"
+
+es6-weak-map@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772...;
+  dependencies:
+    d "1"
+    es5-ext "^0.10.14"
+    es6-iterator "^2.0.1"
+    es6-symbol "^3.1.1"
+
+escape-html@~1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1...;
+
+escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff...;
+
+escope@^3.6.0:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3";
+  dependencies:
+    es6-map "^0.1.3"
+    es6-weak-map "^2.0.1"
+    esrecurse "^4.1.0"
+    estraverse "^4.1.1"
+
+eslint-plugin-react@^6.10.3:
+  version "6.10.3"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz#c5435beb06774e12c7...;
+  dependencies:
+    array.prototype.find "^2.0.1"
+    doctrine "^1.2.2"
+    has "^1.0.1"
+    jsx-ast-utils "^1.3.4"
+    object.assign "^4.0.4"
+
+eslint@^3.19.0:
+  version "3.19.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc";
+  dependencies:
+    babel-code-frame "^6.16.0"
+    chalk "^1.1.3"
+    concat-stream "^1.5.2"
+    debug "^2.1.1"
+    doctrine "^2.0.0"
+    escope "^3.6.0"
+    espree "^3.4.0"
+    esquery "^1.0.0"
+    estraverse "^4.2.0"
+    esutils "^2.0.2"
+    file-entry-cache "^2.0.0"
+    glob "^7.0.3"
+    globals "^9.14.0"
+    ignore "^3.2.0"
+    imurmurhash "^0.1.4"
+    inquirer "^0.12.0"
+    is-my-json-valid "^2.10.0"
+    is-resolvable "^1.0.0"
+    js-yaml "^3.5.1"
+    json-stable-stringify "^1.0.0"
+    levn "^0.3.0"
+    lodash "^4.0.0"
+    mkdirp "^0.5.0"
+    natural-compare "^1.4.0"
+    optionator "^0.8.2"
+    path-is-inside "^1.0.1"
+    pluralize "^1.2.1"
+    progress "^1.1.8"
+    require-uncached "^1.0.2"
+    shelljs "^0.7.5"
+    strip-bom "^3.0.0"
+    strip-json-comments "~2.0.1"
+    table "^3.7.8"
+    text-table "~0.2.0"
+    user-home "^2.0.0"
+
+espree@^3.4.0:
+  version "3.4.3"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.3.tgz#2910b5ccd49ce893c2ffffaab4fd8b3a31b82374";
+  dependencies:
+    acorn "^5.0.1"
+    acorn-jsx "^3.0.0"
+
+esprima@^3.1.1:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633...;
+
+esquery@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa...;
+  dependencies:
+    estraverse "^4.0.0"
+
+esrecurse@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff64822...;
+  dependencies:
+    estraverse "~4.1.0"
+    object-assign "^4.0.1"
+
+estraverse@^4.0.0, estraverse@^4.1.1, estraverse@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bd...;
+
+estraverse@~4.1.0:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba4711...;
+
+esutils@^2.0.0, esutils@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b...;
+
+event-emitter@~0.3.5:
+  version "0.3.5"
+  resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce838406...;
+  dependencies:
+    d "1"
+    es5-ext "~0.10.14"
+
[email protected]:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224e...;
+
+events@^1.0.0, events@~1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924";
+
+evp_bytestokey@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz#497b66ad9fef65cd7c08a6180824b...;
+  dependencies:
+    create-hash "^1.1.1"
+
+exit-hook@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff...;
+
+expand-braces@^0.1.1:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/expand-braces/-/expand-braces-0.1.2.tgz#488b1d1d2451cb3d3a6b192cfc030f4...;
+  dependencies:
+    array-slice "^0.2.3"
+    array-unique "^0.2.1"
+    braces "^0.1.2"
+
+expand-brackets@^0.1.4:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72...;
+  dependencies:
+    is-posix-bracket "^0.1.0"
+
+expand-range@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-0.1.1.tgz#4cb8eda0993ca56fa4f41fc42f3cbb4cc...;
+  dependencies:
+    is-number "^0.1.1"
+    repeat-string "^0.2.2"
+
+expand-range@^1.8.1:
+  version "1.8.2"
+  resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644...;
+  dependencies:
+    fill-range "^2.1.0"
+
+exports-loader@~0.6.4:
+  version "0.6.4"
+  resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.6.4.tgz#d70fc6121975b35fc12830cf52754...;
+  dependencies:
+    loader-utils "^1.0.2"
+    source-map "0.5.x"
+
+extend@^3.0.0, extend@~3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444";
+
+extglob@^0.3.1:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1...;
+  dependencies:
+    is-extglob "^1.0.0"
+
+extract-zip@~1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.5.0.tgz#92ccf6d81ef70a9fa4c1747114ccef6d868...;
+  dependencies:
+    concat-stream "1.5.0"
+    debug "0.7.4"
+    mkdirp "0.5.0"
+    yauzl "2.4.1"
+
[email protected]:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd...;
+
+fast-levenshtein@~2.0.4:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e8...;
+
+fbjs@^0.8.1, fbjs@^0.8.4, fbjs@^0.8.9:
+  version "0.8.12"
+  resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.12.tgz#10b5d92f76d45575fd63a217d4ea02bea2f8ed04";
+  dependencies:
+    core-js "^1.0.0"
+    isomorphic-fetch "^2.1.1"
+    loose-envify "^1.0.0"
+    object-assign "^4.1.0"
+    promise "^7.1.1"
+    setimmediate "^1.0.5"
+    ua-parser-js "^0.7.9"
+
+fd-slicer@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e6...;
+  dependencies:
+    pend "~1.2.0"
+
+figures@^1.3.5:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e...;
+  dependencies:
+    escape-string-regexp "^1.0.5"
+    object-assign "^4.1.0"
+
+file-entry-cache@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84...;
+  dependencies:
+    flat-cache "^1.2.1"
+    object-assign "^4.0.1"
+
+filename-regex@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e3...;
+
+fill-range@^2.1.0:
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a...;
+  dependencies:
+    is-number "^2.1.0"
+    isobject "^2.0.0"
+    randomatic "^1.1.3"
+    repeat-element "^1.1.2"
+    repeat-string "^1.5.2"
+
[email protected]:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.3.tgz#ef47e77950e999780e86022a560e3217e...;
+  dependencies:
+    debug "2.6.7"
+    encodeurl "~1.0.1"
+    escape-html "~1.0.3"
+    on-finished "~2.3.0"
+    parseurl "~1.3.1"
+    statuses "~1.3.1"
+    unpipe "~1.0.0"
+
+find-cache-dir@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c...;
+  dependencies:
+    commondir "^1.0.1"
+    mkdirp "^0.5.1"
+    pkg-dir "^1.0.0"
+
+find-up@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f...;
+  dependencies:
+    path-exists "^2.0.0"
+    pinkie-promise "^2.0.0"
+
+flat-cache@^1.2.1:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6...;
+  dependencies:
+    circular-json "^0.3.1"
+    del "^2.0.2"
+    graceful-fs "^4.1.2"
+    write "^0.2.1"
+
+follow-redirects@^1.2.3:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.3.tgz#01abaeca85e3609837d9fcda3...;
+  dependencies:
+    debug "^2.4.5"
+
+for-in@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80";
+
+for-own@^0.1.4:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce...;
+  dependencies:
+    for-in "^1.0.1"
+
+foreach@^2.0.5:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99...;
+
+forever-agent@~0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d...;
+
+form-data@~2.1.1:
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d...;
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.5"
+    mime-types "^2.1.12"
+
+fs-extra@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950&...;
+  dependencies:
+    graceful-fs "^4.1.2"
+    jsonfile "^2.1.0"
+    klaw "^1.0.0"
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb0141199...;
+
+fsevents@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.1.tgz#f19fd28f43eeaf761680e519a203c4d0b3d31aff&...;
+  dependencies:
+    nan "^2.3.0"
+    node-pre-gyp "^0.6.29"
+
+fstream-ignore@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada...;
+  dependencies:
+    fstream "^1.0.0"
+    inherits "2"
+    minimatch "^3.0.0"
+
+fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2:
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171&#...;
+  dependencies:
+    graceful-fs "^4.1.2"
+    inherits "~2.0.0"
+    mkdirp ">=0.5 0"
+    rimraf "2"
+
+function-bind@^1.0.2, function-bind@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f752946...;
+
+function.prototype.name@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.0.0.tgz#5f523ca64e4...;
+  dependencies:
+    define-properties "^1.1.2"
+    function-bind "^1.1.0"
+    is-callable "^1.1.2"
+
+gauge@~2.7.3:
+  version "2.7.4"
+  resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7";
+  dependencies:
+    aproba "^1.0.3"
+    console-control-strings "^1.0.0"
+    has-unicode "^2.0.0"
+    object-assign "^4.1.0"
+    signal-exit "^3.0.0"
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+    wide-align "^1.1.0"
+
+generate-function@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e909333...;
+
+generate-object-property@^1.1.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c403...;
+  dependencies:
+    is-property "^1.0.0"
+
+get-caller-file@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c155...;
+
+getpass@^0.1.1:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa...;
+  dependencies:
+    assert-plus "^1.0.0"
+
+glob-base@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c...;
+  dependencies:
+    glob-parent "^2.0.0"
+    is-glob "^2.0.0"
+
+glob-parent@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6e...;
+  dependencies:
+    is-glob "^2.0.0"
+
+glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.0, glob@^7.1.1:
+  version "7.1.2"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15";
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.0.4"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+globals@^9.0.0, globals@^9.14.0:
+  version "9.18.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a&#...;
+
+globby@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d";
+  dependencies:
+    array-union "^1.0.1"
+    arrify "^1.0.0"
+    glob "^7.0.3"
+    object-assign "^4.0.1"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+
+graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
+  version "4.1.11"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a02...;
+
+"graceful-readlink@>= 1.0.0":
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f...;
+
+har-schema@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c01513...;
+
+har-validator@~2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70e...;
+  dependencies:
+    chalk "^1.1.1"
+    commander "^2.9.0"
+    is-my-json-valid "^2.12.4"
+    pinkie-promise "^2.0.0"
+
+har-validator@~4.2.1:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5...;
+  dependencies:
+    ajv "^4.9.1"
+    har-schema "^1.0.5"
+
+has-ansi@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91&...;
+  dependencies:
+    ansi-regex "^2.0.0"
+
[email protected]:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/has-binary/-/has-binary-0.1.7.tgz#68e61eb16210c9545a0a5cce06a873912fe1e...;
+  dependencies:
+    isarray "0.0.1"
+
[email protected]:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39&...;
+
+has-flag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa&...;
+
+has-unicode@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de...;
+
+has@^1.0.0, has@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28";
+  dependencies:
+    function-bind "^1.0.2"
+
+hash-base@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e...;
+  dependencies:
+    inherits "^2.0.1"
+
+hash.js@^1.0.0, hash.js@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573...;
+  dependencies:
+    inherits "^2.0.1"
+
+hasha@~2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/hasha/-/hasha-2.2.0.tgz#78d7cbfc1e6d66303fe79837365984517b2f6ee1";
+  dependencies:
+    is-stream "^1.0.1"
+    pinkie-promise "^2.0.0"
+
+hat@^0.0.3:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/hat/-/hat-0.0.3.tgz#bb014a9e64b3788aed8005917413d4ff3d502d8a";
+
+hawk@~3.1.3:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4";
+  dependencies:
+    boom "2.x.x"
+    cryptiles "2.x.x"
+    hoek "2.x.x"
+    sntp "1.x.x"
+
+hmac-drbg@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a...;
+  dependencies:
+    hash.js "^1.0.3"
+    minimalistic-assert "^1.0.0"
+    minimalistic-crypto-utils "^1.0.1"
+
[email protected]:
+  version "2.16.3"
+  resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed";
+
+home-or-tmp@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a78...;
+  dependencies:
+    os-homedir "^1.0.0"
+    os-tmpdir "^1.0.1"
+
+hosted-git-info@^2.1.4:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.2.tgz#0076b9f46a270506ddbaaea5649...;
+
+htmlescape@^1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0...;
+
+htmlparser2@^3.9.1:
+  version "3.9.2"
+  resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb0...;
+  dependencies:
+    domelementtype "^1.3.0"
+    domhandler "^2.3.0"
+    domutils "^1.5.1"
+    entities "^1.1.1"
+    inherits "^2.0.1"
+    readable-stream "^2.0.2"
+
+http-errors@~1.6.1:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.1.tgz#5f8b8ed98aca545656bf572997387f904a7...;
+  dependencies:
+    depd "1.1.0"
+    inherits "2.0.3"
+    setprototypeof "1.0.3"
+    statuses ">= 1.3.1 < 2"
+
+http-proxy@^1.13.0:
+  version "1.16.2"
+  resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f3...;
+  dependencies:
+    eventemitter3 "1.x.x"
+    requires-port "1.x.x"
+
+http-signature@~1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e13...;
+  dependencies:
+    assert-plus "^0.2.0"
+    jsprim "^1.2.2"
+    sshpk "^1.7.0"
+
[email protected], https-browserify@~0.0.0:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24...;
+
[email protected], iconv-lite@~0.4.13:
+  version "0.4.15"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825e...;
+
+ieee754@^1.1.4:
+  version "1.1.8"
+  resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4...;
+
+ignore@^3.2.0:
+  version "3.3.3"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d";
+
+"imports-loader@git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d";:
+  version "0.7.1"
+  resolved "git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d";
+  dependencies:
+    loader-utils "^1.0.2"
+    source-map "^0.5.6"
+
+imurmurhash@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231...;
+
[email protected]:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d...;
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9&...;
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2, [email protected], inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de&...;
+
[email protected]:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1&...;
+
+ini@~1.3.0:
+  version "1.3.4"
+  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e";
+
+inline-source-map@~0.6.0:
+  version "0.6.2"
+  resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.6.2.tgz#f9393471c18a79d1724f863...;
+  dependencies:
+    source-map "~0.5.3"
+
+inquirer@^0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e...;
+  dependencies:
+    ansi-escapes "^1.1.0"
+    ansi-regex "^2.0.0"
+    chalk "^1.0.0"
+    cli-cursor "^1.0.1"
+    cli-width "^2.0.0"
+    figures "^1.3.5"
+    lodash "^4.3.0"
+    readline2 "^1.0.1"
+    run-async "^0.1.0"
+    rx-lite "^3.1.2"
+    string-width "^1.0.1"
+    strip-ansi "^3.0.0"
+    through "^2.3.6"
+
+insert-module-globals@^7.0.0:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.0.1.tgz#c03bf4e01cb086d...;
+  dependencies:
+    JSONStream "^1.0.3"
+    combine-source-map "~0.7.1"
+    concat-stream "~1.5.1"
+    is-buffer "^1.1.0"
+    lexical-scope "^1.2.0"
+    process "~0.11.0"
+    through2 "^2.0.0"
+    xtend "^4.0.0"
+
+interpret@^1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f9...;
+
+invariant@^2.2.0:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae6036...;
+  dependencies:
+    loose-envify "^1.0.0"
+
+invert-kv@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb...;
+
+is-arrayish@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a9...;
+
+is-binary-path@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd...;
+  dependencies:
+    binary-extensions "^1.0.0"
+
+is-buffer@^1.1.0, is-buffer@^1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eec...;
+
+is-builtin-module@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c...;
+  dependencies:
+    builtin-modules "^1.0.0"
+
+is-callable@^1.1.1, is-callable@^1.1.2, is-callable@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7...;
+
+is-date-object@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01...;
+
+is-dotfile@^1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a...;
+
+is-equal-shallow@^0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac...;
+  dependencies:
+    is-primitive "^2.0.0"
+
+is-extendable@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472...;
+
+is-extglob@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc620...;
+
+is-finite@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0a...;
+  dependencies:
+    number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f0...;
+  dependencies:
+    number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f1...;
+
+is-glob@^2.0.0, is-glob@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863...;
+  dependencies:
+    is-extglob "^1.0.0"
+
+is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4:
+  version "2.16.0"
+  resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8...;
+  dependencies:
+    generate-function "^2.0.0"
+    generate-object-property "^1.1.0"
+    jsonpointer "^4.0.0"
+    xtend "^4.0.0"
+
+is-number@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e380...;
+
+is-number@^2.0.2, is-number@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908...;
+  dependencies:
+    kind-of "^3.0.2"
+
+is-path-cwd@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f...;
+
+is-path-in-cwd@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003b...;
+  dependencies:
+    is-path-inside "^1.0.0"
+
+is-path-inside@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717b...;
+  dependencies:
+    path-is-inside "^1.0.1"
+
+is-posix-bracket@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fb...;
+
+is-primitive@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a8721...;
+
+is-property@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095...;
+
+is-regex@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491&...;
+  dependencies:
+    has "^1.0.1"
+
+is-resolvable@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf...;
+  dependencies:
+    tryit "^1.0.1"
+
+is-stream@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca4...;
+
+is-subset@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a...;
+
+is-symbol@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b66057...;
+
+is-typedarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f9601...;
+
+is-utf8@^0.2.0:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72...;
+
[email protected], isarray@~0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf...;
+
[email protected], isarray@^1.0.0, isarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11...;
+
+isbinaryfile@^3.0.0:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69...;
+
+isexe@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10";
+
+isobject@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89&...;
+  dependencies:
+    isarray "1.0.0"
+
+isomorphic-fetch@^2.1.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f72950747...;
+  dependencies:
+    node-fetch "^1.0.1"
+    whatwg-fetch ">=0.10.0"
+
+isstream@~0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a&...;
+
+jasmine-core@~2.5.2:
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.5.2.tgz#6f61bd79061e27f43e6f9355e44b3c6ca...;
+
+jodid25519@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782...;
+  dependencies:
+    jsbn "~0.1.0"
+
+js-string-escape@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1...;
+
+js-tokens@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d...;
+
+js-yaml@^3.5.1:
+  version "3.8.4"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6...;
+  dependencies:
+    argparse "^1.0.7"
+    esprima "^3.1.1"
+
+jsbn@~0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513";
+
+jsesc@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b";
+
+jsesc@~0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d";
+
+json-loader@^0.5.4:
+  version "0.5.4"
+  resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.4.tgz#8baa1365a632f58a3c46d20175fc6002c96...;
+
[email protected]:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882...;
+
+json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff5...;
+  dependencies:
+    jsonify "~0.0.0"
+
+json-stable-stringify@~0.0.0:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz#611c23e814db375...;
+  dependencies:
+    jsonify "~0.0.0"
+
+json-stringify-safe@~5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f...;
+
[email protected]:
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1";
+
+json5@^0.5.0:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821";
+
+jsonfile@^2.1.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8&...;
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
+jsonify@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73...;
+
+jsonparse@^1.2.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f136628...;
+
+jsonpointer@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978...;
+
+jsprim@^1.2.2:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918";
+  dependencies:
+    assert-plus "1.0.0"
+    extsprintf "1.0.2"
+    json-schema "0.2.3"
+    verror "1.3.6"
+
+jsx-ast-utils@^1.3.4:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1ef...;
+
+karma-babel-preprocessor@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/karma-babel-preprocessor/-/karma-babel-preprocessor-6.0.1.tgz#7ae1d3e64...;
+  dependencies:
+    babel-core "^6.0.0"
+
+karma-browserify@~5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/karma-browserify/-/karma-browserify-5.1.1.tgz#f642d70d776d9ab3b73526c57...;
+  dependencies:
+    convert-source-map "^1.1.3"
+    hat "^0.0.3"
+    js-string-escape "^1.0.0"
+    lodash "^3.10.1"
+    minimatch "^3.0.0"
+    os-shim "^0.1.3"
+
+karma-jasmine-html-reporter@~0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz#48a...;
+  dependencies:
+    karma-jasmine "^1.0.2"
+
+karma-jasmine@^1.0.2, karma-jasmine@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.0.tgz#22e4c06bf9a182e5294d1f705e37338...;
+
+karma-phantomjs-launcher@~1.0.2:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/karma-phantomjs-launcher/-/karma-phantomjs-launcher-1.0.4.tgz#d23ca3480...;
+  dependencies:
+    lodash "^4.0.1"
+    phantomjs-prebuilt "^2.1.7"
+
+karma-requirejs@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/karma-requirejs/-/karma-requirejs-1.1.0.tgz#fddae2cb87d7ebc16fb02228935...;
+
+karma-sourcemap-loader@~0.3.7:
+  version "0.3.7"
+  resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.7.tgz#91322c77f8f13...;
+  dependencies:
+    graceful-fs "^4.1.2"
+
+karma-webpack@~2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-2.0.3.tgz#39cebf5ca2580139b27f9ae69b78816...;
+  dependencies:
+    async "~0.9.0"
+    loader-utils "^0.2.5"
+    lodash "^3.8.0"
+    source-map "^0.1.41"
+    webpack-dev-middleware "^1.0.11"
+
+karma@~1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/karma/-/karma-1.5.0.tgz#9c4c14f0400bef2c04c8e8e6bff59371025cc009";
+  dependencies:
+    bluebird "^3.3.0"
+    body-parser "^1.16.1"
+    chokidar "^1.4.1"
+    colors "^1.1.0"
+    combine-lists "^1.0.0"
+    connect "^3.6.0"
+    core-js "^2.2.0"
+    di "^0.0.1"
+    dom-serialize "^2.2.0"
+    expand-braces "^0.1.1"
+    glob "^7.1.1"
+    graceful-fs "^4.1.2"
+    http-proxy "^1.13.0"
+    isbinaryfile "^3.0.0"
+    lodash "^3.8.0"
+    log4js "^0.6.31"
+    mime "^1.3.4"
+    minimatch "^3.0.0"
+    optimist "^0.6.1"
+    qjobs "^1.1.4"
+    range-parser "^1.2.0"
+    rimraf "^2.6.0"
+    safe-buffer "^5.0.1"
+    socket.io "1.7.3"
+    source-map "^0.5.3"
+    tmp "0.0.31"
+    useragent "^2.1.12"
+
+kew@~0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b";
+
+kind-of@^3.0.2:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64...;
+  dependencies:
+    is-buffer "^1.1.5"
+
+klaw@^1.0.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439";
+  optionalDependencies:
+    graceful-fs "^4.1.9"
+
+labeled-stream-splicer@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz#a52e1d138024c...;
+  dependencies:
+    inherits "^2.0.1"
+    isarray "~0.0.1"
+    stream-splicer "^2.0.0"
+
+lazy-cache@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446...;
+
+lcid@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835";
+  dependencies:
+    invert-kv "^1.0.0"
+
+levn@^0.3.0, levn@~0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee";
+  dependencies:
+    prelude-ls "~1.1.2"
+    type-check "~0.3.2"
+
+lexical-scope@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/lexical-scope/-/lexical-scope-1.2.0.tgz#fcea5edc704a4b3a8796cdca419c3a0...;
+  dependencies:
+    astw "^2.0.0"
+
+load-json-file@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59...;
+  dependencies:
+    graceful-fs "^4.1.2"
+    parse-json "^2.2.0"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+    strip-bom "^2.0.0"
+
+loader-runner@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26...;
+
+loader-utils@^0.2.16, loader-utils@^0.2.5:
+  version "0.2.17"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c02...;
+  dependencies:
+    big.js "^3.1.3"
+    emojis-list "^2.0.0"
+    json5 "^0.5.0"
+    object-assign "^4.0.1"
+
+loader-utils@^1.0.2:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754...;
+  dependencies:
+    big.js "^3.1.3"
+    emojis-list "^2.0.0"
+    json5 "^0.5.0"
+
+lodash.assignin@^4.0.9:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e...;
+
+lodash.bind@^4.1.4:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db16...;
+
+lodash.defaults@^4.0.1:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f...;
+
+lodash.filter@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e4...;
+
+lodash.flatten@^4.2.0:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef2...;
+
+lodash.foreach@^4.3.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec351...;
+
+lodash.map@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f...;
+
+lodash.memoize@~3.0.3:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c7...;
+
+lodash.merge@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffad...;
+
+lodash.pick@^4.2.1:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03...;
+
+lodash.reduce@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f...;
+
+lodash.reject@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f4...;
+
+lodash.some@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb...;
+
+lodash@^3.10.1, lodash@^3.8.0:
+  version "3.10.1"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6";
+
+lodash@^4.0.0, lodash@^4.0.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
+  version "4.17.4"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae";
+
+log4js@^0.6.31:
+  version "0.6.38"
+  resolved "https://registry.yarnpkg.com/log4js/-/log4js-0.6.38.tgz#2c494116695d6fb25480943d3fc872e662a522fd";
+  dependencies:
+    readable-stream "~1.0.2"
+    semver "~4.3.3"
+
+longest@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097...;
+
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d...;
+  dependencies:
+    js-tokens "^3.0.0"
+
[email protected]:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063...;
+
[email protected]:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce001685452632...;
+
+memory-fs@^0.4.0, memory-fs@~0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc55...;
+  dependencies:
+    errno "^0.1.3"
+    readable-stream "^2.0.1"
+
+micromatch@^2.1.5:
+  version "2.3.11"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c...;
+  dependencies:
+    arr-diff "^2.0.0"
+    array-unique "^0.2.1"
+    braces "^1.8.2"
+    expand-brackets "^0.1.4"
+    extglob "^0.3.1"
+    filename-regex "^2.0.0"
+    is-extglob "^1.0.0"
+    is-glob "^2.0.1"
+    kind-of "^3.0.2"
+    normalize-path "^2.0.1"
+    object.omit "^2.0.0"
+    parse-glob "^3.0.4"
+    regex-cache "^0.4.2"
+
+miller-rabin@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9...;
+  dependencies:
+    bn.js "^4.0.0"
+    brorand "^1.0.1"
+
+mime-db@~1.27.0:
+  version "1.27.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1&#...;
+
+mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7:
+  version "2.1.15"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc9...;
+  dependencies:
+    mime-db "~1.27.0"
+
+mime@^1.3.4:
+  version "1.3.6"
+  resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0";
+
+minimalistic-assert@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836b...;
+
+minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1...;
+
+minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d3208...;
+  dependencies:
+    brace-expansion "^1.1.7"
+
[email protected], minimist@~0.0.1:
+  version "0.0.8"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d&...;
+
+minimist@^1.1.0, minimist@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284&...;
+
[email protected]:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12";
+  dependencies:
+    minimist "0.0.8"
+
+"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903";
+  dependencies:
+    minimist "0.0.8"
+
+module-deps@^4.0.8:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-4.1.1.tgz#23215833f1da13fd606ccb8087b44852dcb...;
+  dependencies:
+    JSONStream "^1.0.3"
+    browser-resolve "^1.7.0"
+    cached-path-relative "^1.0.0"
+    concat-stream "~1.5.0"
+    defined "^1.0.0"
+    detective "^4.0.0"
+    duplexer2 "^0.1.2"
+    inherits "^2.0.1"
+    parents "^1.0.0"
+    readable-stream "^2.0.2"
+    resolve "^1.1.3"
+    stream-combiner2 "^1.1.1"
+    subarg "^1.0.0"
+    through2 "^2.0.0"
+    xtend "^4.0.0"
+
[email protected]:
+  version "0.7.1"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098";
+
[email protected]:
+  version "0.7.2"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765";
+
[email protected]:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8";
+
[email protected]:
+  version "0.0.5"
+  resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372f...;
+
+nan@^2.3.0:
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45";
+
+natural-compare@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdb...;
+
[email protected]:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0...;
+
+node-fetch@^1.0.1:
+  version "1.7.1"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.1.tgz#899cb3d0a3c92f952c47f1b876f4c8aeabd40...;
+  dependencies:
+    encoding "^0.1.11"
+    is-stream "^1.0.1"
+
+node-libs-browser@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.0.0.tgz#a3a59ec97024985b46e9583...;
+  dependencies:
+    assert "^1.1.1"
+    browserify-zlib "^0.1.4"
+    buffer "^4.3.0"
+    console-browserify "^1.1.0"
+    constants-browserify "^1.0.0"
+    crypto-browserify "^3.11.0"
+    domain-browser "^1.1.1"
+    events "^1.0.0"
+    https-browserify "0.0.1"
+    os-browserify "^0.2.0"
+    path-browserify "0.0.0"
+    process "^0.11.0"
+    punycode "^1.2.4"
+    querystring-es3 "^0.2.0"
+    readable-stream "^2.0.5"
+    stream-browserify "^2.0.1"
+    stream-http "^2.3.1"
+    string_decoder "^0.10.25"
+    timers-browserify "^2.0.2"
+    tty-browserify "0.0.0"
+    url "^0.11.0"
+    util "^0.10.3"
+    vm-browserify "0.0.4"
+
+node-pre-gyp@^0.6.29:
+  version "0.6.36"
+  resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17ab...;
+  dependencies:
+    mkdirp "^0.5.1"
+    nopt "^4.0.1"
+    npmlog "^4.0.2"
+    rc "^1.1.7"
+    request "^2.81.0"
+    rimraf "^2.6.1"
+    semver "^5.3.0"
+    tar "^2.2.1"
+    tar-pack "^3.4.0"
+
+nopt@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d";
+  dependencies:
+    abbrev "1"
+    osenv "^0.1.4"
+
+normalize-package-data@^2.3.2:
+  version "2.3.8"
+  resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.8.tgz#d819eda2a9ded...;
+  dependencies:
+    hosted-git-info "^2.1.4"
+    is-builtin-module "^1.0.0"
+    semver "2 || 3 || 4 || 5"
+    validate-npm-package-license "^3.0.1"
+
+normalize-path@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa2...;
+  dependencies:
+    remove-trailing-separator "^1.0.1"
+
+npmlog@^4.0.2:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.0.tgz#dc59bee85f64f00ed424efb2af0783df25d1c0b5";
+  dependencies:
+    are-we-there-yet "~1.1.2"
+    console-control-strings "~1.1.0"
+    gauge "~2.7.3"
+    set-blocking "~2.0.0"
+
+nth-check@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae...;
+  dependencies:
+    boolbase "~1.0.0"
+
+number-is-nan@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb879031833...;
+
+oauth-sign@~0.8.1:
+  version "0.8.2"
+  resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9...;
+
[email protected]:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd5...;
+
+object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8b...;
+
[email protected]:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f40...;
+
+object-is@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b...;
+
+object-keys@^1.0.10, object-keys@^1.0.8:
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d...;
+
+object.assign@^4.0.4:
+  version "4.0.4"
+  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.0.4.tgz#b1c9cc044ef1b9fe63606fc141abbb3...;
+  dependencies:
+    define-properties "^1.1.2"
+    function-bind "^1.1.0"
+    object-keys "^1.0.10"
+
+object.entries@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.0.4.tgz#1bf9a4dd2288f5b33f3a993d25766...;
+  dependencies:
+    define-properties "^1.1.2"
+    es-abstract "^1.6.1"
+    function-bind "^1.1.0"
+    has "^1.0.1"
+
+object.omit@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54e...;
+  dependencies:
+    for-own "^0.1.4"
+    is-extendable "^0.1.1"
+
+object.values@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.0.4.tgz#e524da09b4f66ff05df457546ec72ac...;
+  dependencies:
+    define-properties "^1.1.2"
+    es-abstract "^1.6.1"
+    function-bind "^1.1.0"
+    has "^1.0.1"
+
+on-finished@~2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d9...;
+  dependencies:
+    ee-first "1.1.1"
+
+once@^1.3.0, once@^1.3.3:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1";
+  dependencies:
+    wrappy "1"
+
+onetime@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789...;
+
+optimist@^0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686&...;
+  dependencies:
+    minimist "~0.0.1"
+    wordwrap "~0.0.2"
+
+optionator@^0.8.2:
+  version "0.8.2"
+  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180ae...;
+  dependencies:
+    deep-is "~0.1.3"
+    fast-levenshtein "~2.0.4"
+    levn "~0.3.0"
+    prelude-ls "~1.1.2"
+    type-check "~0.3.2"
+    wordwrap "~1.0.0"
+
+options@>=0.0.5:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f...;
+
+os-browserify@^0.2.0:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e...;
+
+os-browserify@~0.1.1:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.1.2.tgz#49ca0293e0b19590a5f5de10c7f265a...;
+
+os-homedir@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7...;
+
+os-locale@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d...;
+  dependencies:
+    lcid "^1.0.0"
+
+os-shim@^0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917...;
+
+os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa127...;
+
+osenv@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644";
+  dependencies:
+    os-homedir "^1.0.0"
+    os-tmpdir "^1.0.0"
+
+outpipe@^1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/outpipe/-/outpipe-1.1.1.tgz#50cf8616365e87e031e29a5ec9339a3da4725fa2...;
+  dependencies:
+    shell-quote "^1.4.2"
+
+pako@~0.2.0:
+  version "0.2.9"
+  resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75";
+
+parents@^1.0.0, parents@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751...;
+  dependencies:
+    path-platform "~0.11.15"
+
+parse-asn1@^5.0.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c...;
+  dependencies:
+    asn1.js "^4.0.0"
+    browserify-aes "^1.0.0"
+    create-hash "^1.1.0"
+    evp_bytestokey "^1.0.0"
+    pbkdf2 "^3.0.3"
+
+parse-glob@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a3883...;
+  dependencies:
+    glob-base "^0.3.0"
+    is-dotfile "^1.0.0"
+    is-extglob "^1.0.0"
+    is-glob "^2.0.0"
+
+parse-json@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4...;
+  dependencies:
+    error-ex "^1.2.0"
+
[email protected]:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/parsejson/-/parsejson-0.0.3.tgz#ab7e3759f209ece99437973f7d0f1f64ae0e64a...;
+  dependencies:
+    better-assert "~1.0.0"
+
[email protected]:
+  version "0.0.5"
+  resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d...;
+  dependencies:
+    better-assert "~1.0.0"
+
[email protected]:
+  version "0.0.5"
+  resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a&...;
+  dependencies:
+    better-assert "~1.0.0"
+
+parseurl@~1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56&...;
+
[email protected], path-browserify@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec...;
+
+path-exists@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c70227...;
+  dependencies:
+    pinkie-promise "^2.0.0"
+
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf...;
+
+path-is-inside@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027fa...;
+
+path-parse@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c...;
+
+path-platform@~0.11.15:
+  version "0.11.15"
+  resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf...;
+
+path-type@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe44...;
+  dependencies:
+    graceful-fs "^4.1.2"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+
+pbkdf2@^3.0.3:
+  version "3.0.12"
+  resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.12.tgz#be36785c5067ea48d806ff923288c5f750b6b8a2";
+  dependencies:
+    create-hash "^1.1.2"
+    create-hmac "^1.1.4"
+    ripemd160 "^2.0.1"
+    safe-buffer "^5.0.1"
+    sha.js "^2.4.8"
+
+pend@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50";
+
+performance-now@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d9...;
+
+phantomjs-prebuilt@^2.1.7:
+  version "2.1.14"
+  resolved "https://registry.yarnpkg.com/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.14.tgz#d53d311fcfb7d1d08ddb...;
+  dependencies:
+    es6-promise "~4.0.3"
+    extract-zip "~1.5.0"
+    fs-extra "~1.0.0"
+    hasha "~2.2.0"
+    kew "~0.7.0"
+    progress "~1.1.8"
+    request "~2.79.0"
+    request-progress "~2.0.1"
+    which "~1.2.10"
+
+pify@^2.0.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c";
+
+pinkie-promise@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b1787762...;
+  dependencies:
+    pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870";
+
+pkg-dir@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4...;
+  dependencies:
+    find-up "^1.0.0"
+
+pluralize@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c4...;
+
+prelude-ls@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97d...;
+
+preserve@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b&...;
+
+private@^0.1.6:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1...;
+
+process-nextick-args@~1.0.6:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f...;
+
+process@^0.11.0, process@~0.11.0:
+  version "0.11.10"
+  resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182&...;
+
+progress@^1.1.8, progress@~1.1.8:
+  version "1.1.8"
+  resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be&...;
+
+promise@^7.1.1:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf...;
+  dependencies:
+    asap "~2.0.3"
+
+prop-types@^15.5.4:
+  version "15.5.10"
+  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd74...;
+  dependencies:
+    fbjs "^0.8.9"
+    loose-envify "^1.3.1"
+
+prr@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a";
+
+public-encrypt@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693ca...;
+  dependencies:
+    bn.js "^4.1.0"
+    browserify-rsa "^4.0.0"
+    create-hash "^1.1.0"
+    parse-asn1 "^5.0.0"
+    randombytes "^2.0.1"
+
[email protected]:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d&...;
+
+punycode@^1.2.4, punycode@^1.3.2, punycode@^1.4.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e&...;
+
+qjobs@^1.1.4:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73";
+
[email protected], qs@~6.4.0:
+  version "6.4.0"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233";
+
+qs@~6.3.0:
+  version "6.3.2"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c";
+
+querystring-es3@^0.2.0, querystring-es3@~0.2.0:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596f...;
+
[email protected]:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e7470058785...;
+
+randomatic@^1.1.3:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec...;
+  dependencies:
+    is-number "^2.0.2"
+    kind-of "^3.0.2"
+
+randombytes@^2.0.0, randombytes@^2.0.1:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.4.tgz#9551df208422c8f80eb58e2326dd0b840ff...;
+  dependencies:
+    safe-buffer "^5.0.1"
+
+range-parser@^1.0.3, range-parser@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f61109...;
+
+raw-body@~2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.2.0.tgz#994976cf6a5096a41162840492f0bdc5d6e7fb96&...;
+  dependencies:
+    bytes "2.4.0"
+    iconv-lite "0.4.15"
+    unpipe "1.0.0"
+
+rc@^1.1.7:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95";
+  dependencies:
+    deep-extend "~0.4.0"
+    ini "~1.3.0"
+    minimist "^1.2.0"
+    strip-json-comments "~2.0.1"
+
+react-addons-test-utils@~15.4.2:
+  version "15.4.2"
+  resolved "https://registry.yarnpkg.com/react-addons-test-utils/-/react-addons-test-utils-15.4.2.tgz#93bcaa718f...;
+  dependencies:
+    fbjs "^0.8.4"
+    object-assign "^4.1.0"
+
+react-dom@~15.4.2:
+  version "15.4.2"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.4.2.tgz#015363f05b0a1fd52ae9efdd3a0060d9069520...;
+  dependencies:
+    fbjs "^0.8.1"
+    loose-envify "^1.1.0"
+    object-assign "^4.1.0"
+
+react@~15.4.2:
+  version "15.4.2"
+  resolved "https://registry.yarnpkg.com/react/-/react-15.4.2.tgz#41f7991b26185392ba9bae96c8889e7e018397ef";
+  dependencies:
+    fbjs "^0.8.4"
+    loose-envify "^1.1.0"
+    object-assign "^4.1.0"
+
+read-only-stream@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4...;
+  dependencies:
+    readable-stream "^2.0.2"
+
+read-pkg-up@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b64...;
+  dependencies:
+    find-up "^1.0.0"
+    read-pkg "^1.0.0"
+
+read-pkg@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28&...;
+  dependencies:
+    load-json-file "^1.0.0"
+    normalize-package-data "^2.3.2"
+    path-type "^1.0.0"
+
+readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6:
+  version "2.2.11"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.11.tgz#0796b31f8d7688007ff0b93a80...;
+  dependencies:
+    core-util-is "~1.0.0"
+    inherits "~2.0.1"
+    isarray "~1.0.0"
+    process-nextick-args "~1.0.6"
+    safe-buffer "~5.0.1"
+    string_decoder "~1.0.0"
+    util-deprecate "~1.0.1"
+
+readable-stream@~1.0.2:
+  version "1.0.34"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4...;
+  dependencies:
+    core-util-is "~1.0.0"
+    inherits "~2.0.1"
+    isarray "0.0.1"
+    string_decoder "~0.10.x"
+
+readable-stream@~2.0.0:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfc...;
+  dependencies:
+    core-util-is "~1.0.0"
+    inherits "~2.0.1"
+    isarray "~1.0.0"
+    process-nextick-args "~1.0.6"
+    string_decoder "~0.10.x"
+    util-deprecate "~1.0.1"
+
+readdirp@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78&...;
+  dependencies:
+    graceful-fs "^4.1.2"
+    minimatch "^3.0.2"
+    readable-stream "^2.0.2"
+    set-immediate-shim "^1.0.1"
+
+readline2@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e3...;
+  dependencies:
+    code-point-at "^1.0.0"
+    is-fullwidth-code-point "^1.0.0"
+    mute-stream "0.0.5"
+
+rechoir@^0.6.2:
+  version "0.6.2"
+  resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384...;
+  dependencies:
+    resolve "^1.1.6"
+
+regenerate@^1.2.1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19...;
+
+regenerator-runtime@^0.10.0:
+  version "0.10.5"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedd...;
+
[email protected]:
+  version "0.9.11"
+  resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b...;
+  dependencies:
+    babel-runtime "^6.18.0"
+    babel-types "^6.19.0"
+    private "^0.1.6"
+
+regex-cache@^0.4.2:
+  version "0.4.3"
+  resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d71...;
+  dependencies:
+    is-equal-shallow "^0.1.3"
+    is-primitive "^2.0.0"
+
+regexpu-core@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6e...;
+  dependencies:
+    regenerate "^1.2.1"
+    regjsgen "^0.2.0"
+    regjsparser "^0.1.4"
+
+regjsgen@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7&...;
+
+regjsparser@^0.1.4:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ea...;
+  dependencies:
+    jsesc "~0.5.0"
+
+remove-trailing-separator@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz#69b062d...;
+
+repeat-element@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f...;
+
+repeat-string@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-0.2.2.tgz#c7a8d3236068362059a7e4651fc6884...;
+
+repeat-string@^1.5.2:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a77628...;
+
+repeating@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dd...;
+  dependencies:
+    is-finite "^1.0.0"
+
+request-progress@~2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-2.0.1.tgz#5d36bb57961c673aa5b788dbc...;
+  dependencies:
+    throttleit "^1.0.0"
+
+request@^2.81.0:
+  version "2.81.0"
+  resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0&#...;
+  dependencies:
+    aws-sign2 "~0.6.0"
+    aws4 "^1.2.1"
+    caseless "~0.12.0"
+    combined-stream "~1.0.5"
+    extend "~3.0.0"
+    forever-agent "~0.6.1"
+    form-data "~2.1.1"
+    har-validator "~4.2.1"
+    hawk "~3.1.3"
+    http-signature "~1.1.0"
+    is-typedarray "~1.0.0"
+    isstream "~0.1.2"
+    json-stringify-safe "~5.0.1"
+    mime-types "~2.1.7"
+    oauth-sign "~0.8.1"
+    performance-now "^0.2.0"
+    qs "~6.4.0"
+    safe-buffer "^5.0.1"
+    stringstream "~0.0.4"
+    tough-cookie "~2.3.0"
+    tunnel-agent "^0.6.0"
+    uuid "^3.0.0"
+
+request@~2.79.0:
+  version "2.79.0"
+  resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de&#...;
+  dependencies:
+    aws-sign2 "~0.6.0"
+    aws4 "^1.2.1"
+    caseless "~0.11.0"
+    combined-stream "~1.0.5"
+    extend "~3.0.0"
+    forever-agent "~0.6.1"
+    form-data "~2.1.1"
+    har-validator "~2.0.6"
+    hawk "~3.1.3"
+    http-signature "~1.1.0"
+    is-typedarray "~1.0.0"
+    isstream "~0.1.2"
+    json-stringify-safe "~5.0.1"
+    mime-types "~2.1.7"
+    oauth-sign "~0.8.1"
+    qs "~6.3.0"
+    stringstream "~0.0.4"
+    tough-cookie "~2.3.0"
+    tunnel-agent "~0.4.1"
+    uuid "^3.0.0"
+
+require-directory@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e234...;
+
+require-main-filename@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784...;
+
+require-uncached@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4...;
+  dependencies:
+    caller-path "^0.1.0"
+    resolve-from "^1.0.0"
+
+requirejs@~2.3.3:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.3.tgz#aa59fd3a0287eaf407959a138228044b5dd6a6a...;
+
[email protected]:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e69...;
+
+resolve-from@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e9...;
+
[email protected]:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b...;
+
+resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.6:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5...;
+  dependencies:
+    path-parse "^1.0.5"
+
+restore-cursor@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed299147915225...;
+  dependencies:
+    exit-hook "^1.0.0"
+    onetime "^1.0.0"
+
+right-align@^0.1.1:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148...;
+  dependencies:
+    align-text "^0.1.1"
+
+rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.0, rimraf@^2.6.1:
+  version "2.6.1"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d";
+  dependencies:
+    glob "^7.0.5"
+
+ripemd160@^2.0.0, ripemd160@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e...;
+  dependencies:
+    hash-base "^2.0.0"
+    inherits "^2.0.1"
+
+run-async@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e38...;
+  dependencies:
+    once "^1.3.0"
+
+rx-lite@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102...;
+
+safe-buffer@^5.0.1:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a4...;
+
+safe-buffer@~5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de5791...;
+
+"semver@2 || 3 || 4 || 5", semver@^5.3.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f";
+
+semver@~4.3.3:
+  version "4.3.6"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da";
+
+set-blocking@^2.0.0, set-blocking@~2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b...;
+
+set-immediate-shim@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc4...;
+
+setimmediate@^1.0.4, setimmediate@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab78...;
+
[email protected]:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0c...;
+
+sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4:
+  version "2.4.8"
+  resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f";
+  dependencies:
+    inherits "^2.0.1"
+
+shasum@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/shasum/-/shasum-1.0.2.tgz#e7012310d8f417f4deb5712150e5678b87ae565f";
+  dependencies:
+    json-stable-stringify "~0.0.0"
+    sha.js "~2.4.4"
+
+shell-quote@^1.4.2, shell-quote@^1.6.1:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f4...;
+  dependencies:
+    array-filter "~0.0.0"
+    array-map "~0.0.0"
+    array-reduce "~0.0.0"
+    jsonify "~0.0.0"
+
+shelljs@^0.7.5:
+  version "0.7.8"
+  resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3...;
+  dependencies:
+    glob "^7.0.0"
+    interpret "^1.0.0"
+    rechoir "^0.6.2"
+
+signal-exit@^3.0.0:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b736...;
+
+slash@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55";
+
[email protected]:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831...;
+
[email protected]:
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198";
+  dependencies:
+    hoek "2.x.x"
+
[email protected]:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz#cb6d4bb8bec81e1078b9967...;
+  dependencies:
+    debug "2.3.3"
+    socket.io-parser "2.3.1"
+
[email protected]:
+  version "1.7.3"
+  resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.7.3.tgz#b30e86aa10d5ef3546601c09c...;
+  dependencies:
+    backo2 "1.0.2"
+    component-bind "1.0.0"
+    component-emitter "1.2.1"
+    debug "2.3.3"
+    engine.io-client "1.8.3"
+    has-binary "0.1.7"
+    indexof "0.0.1"
+    object-component "0.0.3"
+    parseuri "0.0.5"
+    socket.io-parser "2.3.1"
+    to-array "0.1.4"
+
[email protected]:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-2.3.1.tgz#dd532025103ce429697326bef...;
+  dependencies:
+    component-emitter "1.1.2"
+    debug "2.2.0"
+    isarray "0.0.1"
+    json3 "3.3.2"
+
[email protected]:
+  version "1.7.3"
+  resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.7.3.tgz#b8af9caba00949e568e369f1327ea9be9ea2461...;
+  dependencies:
+    debug "2.3.3"
+    engine.io "1.8.3"
+    has-binary "0.1.7"
+    object-assign "4.1.0"
+    socket.io-adapter "0.5.0"
+    socket.io-client "1.7.3"
+    socket.io-parser "2.3.1"
+
+source-list-map@^1.1.1:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.2.tgz#9889019d1024cce55cdc0694983...;
+
+source-map-support@^0.4.2:
+  version "0.4.15"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7e...;
+  dependencies:
+    source-map "^0.5.6"
+
[email protected], source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3:
+  version "0.5.6"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f...;
+
+source-map@^0.1.41:
+  version "0.1.43"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e...;
+  dependencies:
+    amdefine ">=0.0.4"
+
+spdx-correct@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4...;
+  dependencies:
+    spdx-license-ids "^1.0.2"
+
+spdx-expression-parse@~1.0.0:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed...;
+
+spdx-license-ids@^1.0.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d...;
+
+sprintf-js@~1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297...;
+
+sshpk@^1.7.0:
+  version "1.13.0"
+  resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c";
+  dependencies:
+    asn1 "~0.2.3"
+    assert-plus "^1.0.0"
+    dashdash "^1.12.0"
+    getpass "^0.1.1"
+  optionalDependencies:
+    bcrypt-pbkdf "^1.0.0"
+    ecc-jsbn "~0.1.1"
+    jodid25519 "^1.0.0"
+    jsbn "~0.1.0"
+    tweetnacl "~0.14.0"
+
+"statuses@>= 1.3.1 < 2", statuses@~1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e&...;
+
+stream-browserify@^2.0.0, stream-browserify@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e451...;
+  dependencies:
+    inherits "~2.0.1"
+    readable-stream "^2.0.2"
+
+stream-combiner2@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad47...;
+  dependencies:
+    duplexer2 "~0.1.0"
+    readable-stream "^2.0.2"
+
+stream-http@^2.0.0, stream-http@^2.3.1:
+  version "2.7.1"
+  resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.1.tgz#546a51741ad5a6b07e9e31b0b10441a917d...;
+  dependencies:
+    builtin-status-codes "^3.0.0"
+    inherits "^2.0.1"
+    readable-stream "^2.2.6"
+    to-arraybuffer "^1.0.0"
+    xtend "^4.0.0"
+
+stream-splicer@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.0.tgz#1b63be438a133e4b671cc19351976...;
+  dependencies:
+    inherits "^2.0.1"
+    readable-stream "^2.0.2"
+
+string-width@^1.0.1, string-width@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b...;
+  dependencies:
+    code-point-at "^1.0.0"
+    is-fullwidth-code-point "^1.0.0"
+    strip-ansi "^3.0.0"
+
+string-width@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.0.0.tgz#635c5436cc72a6e0c387ceca278d4e2ee...;
+  dependencies:
+    is-fullwidth-code-point "^2.0.0"
+    strip-ansi "^3.0.0"
+
+string_decoder@^0.10.25, string_decoder@~0.10.0, string_decoder@~0.10.x:
+  version "0.10.31"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301...;
+
+string_decoder@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.2.tgz#b29e1f4e1125fa97a10382b8a5337...;
+  dependencies:
+    safe-buffer "~5.0.1"
+
+stringstream@~0.0.4:
+  version "0.0.5"
+  resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a8...;
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63...;
+  dependencies:
+    ansi-regex "^2.0.0"
+
+strip-bom@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0...;
+  dependencies:
+    is-utf8 "^0.2.0"
+
+strip-bom@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed...;
+
+strip-json-comments@~2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0...;
+
+subarg@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2";
+  dependencies:
+    minimist "^1.1.0"
+
+supports-color@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa401170846299...;
+
+supports-color@^3.1.0:
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3...;
+  dependencies:
+    has-flag "^1.0.0"
+
+syntax-error@^1.1.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.3.0.tgz#1ed9266c4d40be75dc55bf9bb1cb77062...;
+  dependencies:
+    acorn "^4.0.3"
+
+table@^3.7.8:
+  version "3.8.3"
+  resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f";
+  dependencies:
+    ajv "^4.7.0"
+    ajv-keywords "^1.0.0"
+    chalk "^1.1.1"
+    lodash "^4.0.0"
+    slice-ansi "0.0.4"
+    string-width "^2.0.0"
+
+tapable@^0.2.5, tapable@~0.2.5:
+  version "0.2.6"
+  resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.6.tgz#206be8e188860b514425375e6f1ae89bfb01fd8d...;
+
+tar-pack@^3.4.0:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984&...;
+  dependencies:
+    debug "^2.2.0"
+    fstream "^1.0.10"
+    fstream-ignore "^1.0.5"
+    once "^1.3.3"
+    readable-stream "^2.1.4"
+    rimraf "^2.5.1"
+    tar "^2.2.1"
+    uid-number "^0.0.6"
+
+tar@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1";
+  dependencies:
+    block-stream "*"
+    fstream "^1.0.2"
+    inherits "2"
+
+text-table@~0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa57...;
+
+throttleit@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528a...;
+
+through2@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be&...;
+  dependencies:
+    readable-stream "^2.1.5"
+    xtend "~4.0.1"
+
+"through@>=2.2.7 <3", through@^2.3.6:
+  version "2.3.8"
+  resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5...;
+
+timers-browserify@^1.0.1:
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2...;
+  dependencies:
+    process "~0.11.0"
+
+timers-browserify@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af21134...;
+  dependencies:
+    setimmediate "^1.0.4"
+
[email protected], [email protected]:
+  version "0.0.31"
+  resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7";
+  dependencies:
+    os-tmpdir "~1.0.1"
+
[email protected]:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890&...;
+
+to-arraybuffer@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a...;
+
+to-fast-properties@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231...;
+
+tough-cookie@~2.3.0:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150...;
+  dependencies:
+    punycode "^1.4.1"
+
+trim-right@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6...;
+
+tryit@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb";
+
[email protected], tty-browserify@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d52...;
+
+tunnel-agent@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f...;
+  dependencies:
+    safe-buffer "^5.0.1"
+
+tunnel-agent@~0.4.1:
+  version "0.4.3"
+  resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828...;
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+  version "0.14.5"
+  resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f...;
+
+type-check@~0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520d...;
+  dependencies:
+    prelude-ls "~1.1.2"
+
+type-is@~1.6.15:
+  version "1.6.15"
+  resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410&#...;
+  dependencies:
+    media-typer "0.3.0"
+    mime-types "~2.1.15"
+
+typedarray@^0.0.6, typedarray@~0.0.5:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830...;
+
+ua-parser-js@^0.7.9:
+  version "0.7.12"
+  resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d...;
+
+uglify-js@^2.8.5:
+  version "2.8.28"
+  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.28.tgz#e335032df9bb20dcb918f164589d5af47f3883...;
+  dependencies:
+    source-map "~0.5.1"
+    yargs "~3.10.0"
+  optionalDependencies:
+    uglify-to-browserify "~1.0.0"
+
+uglify-to-browserify@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe...;
+
+uid-number@^0.0.6:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663ba...;
+
[email protected]:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa";
+
+umd@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.1.tgz#8ae556e11011f63c2596708a8837259f01b3d60e";
+
+underscore@~1.8.3:
+  version "1.8.3"
+  resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5...;
+
[email protected], unpipe@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec";
+
+url@^0.11.0, url@~0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1";
+  dependencies:
+    punycode "1.3.2"
+    querystring "0.2.0"
+
+user-home@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9...;
+  dependencies:
+    os-homedir "^1.0.0"
+
+useragent@^2.1.12:
+  version "2.1.13"
+  resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.1.13.tgz#bba43e8aa24d5ceb83c2937473e102e21df74c...;
+  dependencies:
+    lru-cache "2.2.x"
+    tmp "0.0.x"
+
+util-deprecate@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28...;
+
[email protected], util@^0.10.3, util@~0.10.1:
+  version "0.10.3"
+  resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9";
+  dependencies:
+    inherits "2.0.1"
+
[email protected]:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287...;
+
+uuid@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a";
+
+uuid@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1";
+
+validate-npm-package-license@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2...;
+  dependencies:
+    spdx-correct "~1.0.0"
+    spdx-expression-parse "~1.0.0"
+
[email protected]:
+  version "1.3.6"
+  resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c";
+  dependencies:
+    extsprintf "1.0.2"
+
[email protected], vm-browserify@~0.0.1:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87...;
+  dependencies:
+    indexof "0.0.1"
+
+void-elements@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e9...;
+
+watchify@~3.9.0:
+  version "3.9.0"
+  resolved "https://registry.yarnpkg.com/watchify/-/watchify-3.9.0.tgz#f075fd2e8a86acde84cedba6e5c2a0bedd523d9e&...;
+  dependencies:
+    anymatch "^1.3.0"
+    browserify "^14.0.0"
+    chokidar "^1.0.0"
+    defined "^1.0.0"
+    outpipe "^1.1.0"
+    through2 "^2.0.0"
+    xtend "^4.0.0"
+
+watchpack@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.3.1.tgz#7d8693907b28ce6013e7f3610aa2a1acf07dad8...;
+  dependencies:
+    async "^2.1.2"
+    chokidar "^1.4.3"
+    graceful-fs "^4.1.2"
+
+webpack-dev-middleware@^1.0.11:
+  version "1.10.2"
+  resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.10.2.tgz#2e252ce1dfb0...;
+  dependencies:
+    memory-fs "~0.4.1"
+    mime "^1.3.4"
+    path-is-absolute "^1.0.0"
+    range-parser "^1.0.3"
+
+webpack-sources@^0.2.3:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.2.3.tgz#17c62bfaf13c707f9d02c479e0d...;
+  dependencies:
+    source-list-map "^1.1.1"
+    source-map "~0.5.3"
+
+webpack@~2.3.1:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.3.3.tgz#eecc083c18fb7bf958ea4f40b57a6640c5a0cc78...;
+  dependencies:
+    acorn "^4.0.4"
+    acorn-dynamic-import "^2.0.0"
+    ajv "^4.7.0"
+    ajv-keywords "^1.1.1"
+    async "^2.1.2"
+    enhanced-resolve "^3.0.0"
+    interpret "^1.0.0"
+    json-loader "^0.5.4"
+    loader-runner "^2.3.0"
+    loader-utils "^0.2.16"
+    memory-fs "~0.4.1"
+    mkdirp "~0.5.0"
+    node-libs-browser "^2.0.0"
+    source-map "^0.5.3"
+    supports-color "^3.1.0"
+    tapable "~0.2.5"
+    uglify-js "^2.8.5"
+    watchpack "^1.3.1"
+    webpack-sources "^0.2.3"
+    yargs "^6.0.0"
+
+whatwg-fetch@>=0.10.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b4421...;
+
+which-module@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b960...;
+
+which@~1.2.10:
+  version "1.2.14"
+  resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5";
+  dependencies:
+    isexe "^2.0.0"
+
+wide-align@^1.1.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341...;
+  dependencies:
+    string-width "^1.0.2"
+
[email protected]:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94...;
+
[email protected]:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f&...;
+
+wordwrap@~0.0.2:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107&...;
+
+wordwrap@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb&...;
+
+wrap-ansi@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd8...;
+  dependencies:
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f";
+
+write@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757";
+  dependencies:
+    mkdirp "^0.5.1"
+
[email protected]:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.2.tgz#8a244fa052401e08c9886cf44a85189e1fd4067f";
+  dependencies:
+    options ">=0.0.5"
+    ultron "1.0.x"
+
[email protected]:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/wtf-8/-/wtf-8-1.0.0.tgz#392d8ba2d0f1c34d1ee2d630f15d0efb68e1048a";
+
[email protected]:
+  version "1.5.3"
+  resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz#185a888c04eca46c3e407...;
+
+xtend@^4.0.0, xtend@~4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af";
+
+y18n@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41";
+
+yargs-parser@^4.2.0:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9...;
+  dependencies:
+    camelcase "^3.0.0"
+
+yargs@^6.0.0:
+  version "6.6.0"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208";
+  dependencies:
+    camelcase "^3.0.0"
+    cliui "^3.2.0"
+    decamelize "^1.1.1"
+    get-caller-file "^1.0.1"
+    os-locale "^1.4.0"
+    read-pkg-up "^1.0.1"
+    require-directory "^2.1.1"
+    require-main-filename "^1.0.1"
+    set-blocking "^2.0.0"
+    string-width "^1.0.2"
+    which-module "^1.0.0"
+    y18n "^3.2.1"
+    yargs-parser "^4.2.0"
+
+yargs@~3.10.0:
+  version "3.10.0"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1";
+  dependencies:
+    camelcase "^1.0.2"
+    cliui "^2.1.0"
+    decamelize "^1.0.0"
+    window-size "0.1.0"
+
[email protected]:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005";
+  dependencies:
+    fd-slicer "~1.0.1"
+
[email protected]:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419";

diff --git a/web/karma.conf.js b/web/karma.conf.js
index 3c8e933c..5b0ab57b 100644
--- a/web/karma.conf.js
+++ b/web/karma.conf.js
@@ -20,7 +20,7 @@ module.exports = function (config) {
     // list of files to exclude
     exclude: [
       'pgadmin/static/vendor/**/*[Tt]est.js',
-      'pgadmin/static/vendor/**/*[Ss]pec.js'
+      'pgadmin/static/vendor/**/*[Ss]pec.js',
     ],
 
     // preprocess matching files before serving them to the browser
@@ -60,6 +60,6 @@ module.exports = function (config) {
 
     // Concurrency level
     // how many browser should be started simultaneous
-    concurrency: Infinity
+    concurrency: Infinity,
   });
 };
diff --git a/web/package.json b/web/package.json
index 40070da0..b5d2e0cb 100644
--- a/web/package.json
+++ b/web/package.json
@@ -5,6 +5,8 @@
     "babel-preset-es2015": "~6.24.0",
     "babel-preset-react": "~6.23.0",
     "enzyme": "~2.8.2",
+    "eslint": "^3.19.0",
+    "eslint-plugin-react": "^6.10.3",
     "jasmine-core": "~2.5.2",
     "karma": "~1.5.0",
     "karma-babel-preprocessor": "^6.0.1",
@@ -34,9 +36,11 @@
     "watchify": "~3.9.0"
   },
   "scripts": {
-    "bundle": "yarn run webpack -- --optimize-minimize --config webpack.config.js",
-    "test:karma-once": "yarn run karma start -- --single-run",
-    "test:karma": "yarn run karma start",
+    "linter": "yarn run eslint pgadmin/static/jsx/**/*.jsx pgadmin/static/js/selection/*.js regression/javascript/**/*.jsx regression/javascript/**/*.js *.js",
+    "webpacker": "yarn run webpack -- --optimize-minimize --config webpack.config.js",
+    "bundle": "yarn run linter && yarn run webpacker",
+    "test:karma-once": "yarn run linter && yarn run karma start -- --single-run",
+    "test:karma": "yarn run linter && yarn run karma start",
     "test:feature": "yarn run bundle && python regression/runtests.py --pkg feature_tests",
     "test": "yarn run test:karma-once && yarn run bundle && python regression/runtests.py"
   }
diff --git a/web/pgadmin/static/js/selection/active_cell_capture.js b/web/pgadmin/static/js/selection/active_cell_capture.js
index 10572736..4f87886c 100644
--- a/web/pgadmin/static/js/selection/active_cell_capture.js
+++ b/web/pgadmin/static/js/selection/active_cell_capture.js
@@ -1,6 +1,6 @@
 define([
   'jquery',
-  'sources/selection/range_selection_helper'
+  'sources/selection/range_selection_helper',
 ], function ($, RangeSelectionHelper) {
 
   var ActiveCellCapture = function () {
@@ -37,8 +37,8 @@ define([
     };
 
     $.extend(this, {
-      "init": init,
-      "destroy": destroy,
+      'init': init,
+      'destroy': destroy,
     });
 
     function onDragEndHandler(event, dragData) {
@@ -105,7 +105,7 @@ define([
       }
     }
 
-    function onActiveCellChangedHandler(event, args) {
+    function onActiveCellChangedHandler(event) {
       if (bypassDefaultActiveCellRangeChange) {
         bypassDefaultActiveCellRangeChange = false;
         event.stopPropagation();
@@ -188,4 +188,4 @@ define([
   };
 
   return ActiveCellCapture;
-});
\ No newline at end of file
+});
diff --git a/web/pgadmin/static/js/selection/clipboard.js b/web/pgadmin/static/js/selection/clipboard.js
index 9e20da7e..9794ae7e 100644
--- a/web/pgadmin/static/js/selection/clipboard.js
+++ b/web/pgadmin/static/js/selection/clipboard.js
@@ -1,7 +1,7 @@
 define(['sources/gettext', 'alertify'], function (gettext, alertify) {
   var clipboard = {
     copyTextToClipboard: function (text) {
-      var textArea = document.createElement("textarea");
+      var textArea = document.createElement('textarea');
 
       //
       // *** This styling is an extra step which is likely not required. ***
@@ -55,7 +55,7 @@ define(['sources/gettext', 'alertify'], function (gettext, alertify) {
       }
 
       document.body.removeChild(textArea);
-    }
+    },
   };
   return clipboard;
 });
\ No newline at end of file
diff --git a/web/pgadmin/static/js/selection/column_selector.js b/web/pgadmin/static/js/selection/column_selector.js
index 53cdad82..305b438d 100644
--- a/web/pgadmin/static/js/selection/column_selector.js
+++ b/web/pgadmin/static/js/selection/column_selector.js
@@ -1,7 +1,7 @@
 define([
   'jquery',
   'sources/selection/range_selection_helper',
-  'slickgrid'
+  'slickgrid',
 ], function ($, RangeSelectionHelper) {
   var ColumnSelector = function () {
     var Slick = window.Slick;
@@ -21,7 +21,7 @@ define([
       if (isColumnSelectable(columnDefinition)) {
         var $columnHeader = $(event.target);
         if (hasClickedChildOfColumnHeader(event)) {
-          $columnHeader = $(event.target).parents(".slick-header-column");
+          $columnHeader = $(event.target).parents('.slick-header-column');
         }
         $columnHeader.toggleClass('selected');
 
@@ -63,7 +63,7 @@ define([
     };
 
     var hasClickedChildOfColumnHeader = function (event) {
-      return !$(event.target).hasClass("slick-header-column");
+      return !$(event.target).hasClass('slick-header-column');
     };
 
     var isColumnSelectable = function (columnDefinition) {
@@ -87,16 +87,16 @@ define([
       return _.map(columnDefinitions, function (columnDefinition) {
         if (isColumnSelectable(columnDefinition)) {
           var name =
-            "<span data-cell-type='column-header-row' " +
-            "       data-test='output-column-header'" +
-            "       data-column-id='" + columnDefinition.id + "'>" +
-            "  <span class='column-description'>" +
-            "    <span class='column-name'>" + columnDefinition.display_name + "</span>" +
-            "    <span class='column-type'>" + columnDefinition.column_type + "</span>" +
-            "  </span>" +
-            "</span>";
+            '<span data-cell-type=\'column-header-row\' ' +
+            '       data-test=\'output-column-header\'' +
+            '       data-column-id=\'' + columnDefinition.id + '\'>' +
+            '  <span class=\'column-description\'>' +
+            '    <span class=\'column-name\'>' + columnDefinition.display_name + '</span>' +
+            '    <span class=\'column-type\'>' + columnDefinition.column_type + '</span>' +
+            '  </span>' +
+            '</span>';
           return _.extend(columnDefinition, {
-            name: name
+            name: name,
           });
         } else {
           return columnDefinition;
@@ -105,8 +105,8 @@ define([
     };
 
     $.extend(this, {
-      "init": init,
-      "getColumnDefinitions": getColumnDefinitions
+      'init': init,
+      'getColumnDefinitions': getColumnDefinitions,
     });
   };
   return ColumnSelector;
diff --git a/web/pgadmin/static/js/selection/copy_data.js b/web/pgadmin/static/js/selection/copy_data.js
index ad79d3eb..c2263231 100644
--- a/web/pgadmin/static/js/selection/copy_data.js
+++ b/web/pgadmin/static/js/selection/copy_data.js
@@ -3,8 +3,8 @@ define([
   'underscore',
   'sources/selection/clipboard',
   'sources/selection/range_selection_helper',
-  'sources/selection/range_boundary_navigator'
-  ],
+  'sources/selection/range_boundary_navigator',
+],
 function ($, _, clipboard, RangeSelectionHelper, rangeBoundaryNavigator) {
   var copyData = function () {
     var self = this;
@@ -32,20 +32,8 @@ function ($, _, clipboard, RangeSelectionHelper, rangeBoundaryNavigator) {
 
   var setPasteRowButtonEnablement = function (canEditFlag, isEnabled) {
     if (canEditFlag) {
-      $("#btn-paste-row").prop('disabled', !isEnabled);
+      $('#btn-paste-row').prop('disabled', !isEnabled);
     }
   };
-
-  var allTheRangesAreFullRows = function (ranges, columnDefinitions) {
-    var colRangeBounds = ranges.map(function (range) {
-      return [range.fromCell, range.toCell];
-    });
-
-    if(RangeSelectionHelper.isFirstColumnData(columnDefinitions)) {
-      return _.isEqual(_.union.apply(null, colRangeBounds), [0, columnDefinitions.length - 1]);
-    }
-    return _.isEqual(_.union.apply(null, colRangeBounds), [0, columnDefinitions.length - 1]);
-  };
-
-  return copyData
-});
\ No newline at end of file
+  return copyData;
+});
diff --git a/web/pgadmin/static/js/selection/grid_selector.js b/web/pgadmin/static/js/selection/grid_selector.js
index ae4ab96d..8d7ba75d 100644
--- a/web/pgadmin/static/js/selection/grid_selector.js
+++ b/web/pgadmin/static/js/selection/grid_selector.js
@@ -1,10 +1,8 @@
 define(['jquery',
-    'sources/selection/column_selector',
-    'sources/selection/row_selector',
-    'sources/selection/range_selection_helper'],
+  'sources/selection/column_selector',
+  'sources/selection/row_selector',
+  'sources/selection/range_selection_helper'],
   function ($, ColumnSelector, RowSelector, RangeSelectionHelper) {
-    var Slick = window.Slick;
-
     var GridSelector = function (columnDefinitions) {
       var rowSelector = new RowSelector(columnDefinitions);
       var columnSelector = new ColumnSelector(columnDefinitions);
@@ -39,9 +37,9 @@ define(['jquery',
 
       function handleSelectedRangesChanged(grid) {
         if(RangeSelectionHelper.isEntireGridSelected(grid)) {
-          $("[data-id='select-all']").addClass("selected");
+          $('[data-id=\'select-all\']').addClass('selected');
         } else {
-          $("[data-id='select-all']").removeClass("selected");
+          $('[data-id=\'select-all\']').removeClass('selected');
         }
       }
 
@@ -59,8 +57,8 @@ define(['jquery',
       }
 
       $.extend(this, {
-        "init": init,
-        "getColumnDefinitions": getColumnDefinitions
+        'init': init,
+        'getColumnDefinitions': getColumnDefinitions,
       });
     };
 
diff --git a/web/pgadmin/static/js/selection/range_boundary_navigator.js b/web/pgadmin/static/js/selection/range_boundary_navigator.js
index f88a6533..803f75af 100644
--- a/web/pgadmin/static/js/selection/range_boundary_navigator.js
+++ b/web/pgadmin/static/js/selection/range_boundary_navigator.js
@@ -104,13 +104,13 @@ function (RangeSelectionHelper) {
       var val = data[rowId][columnDefinitions[colId].pos];
 
       if (val && _.isObject(val)) {
-        val = "'" + JSON.stringify(val) + "'";
-      } else if (val && typeof val != "number" && typeof val != "boolean") {
-        val = "'" + val.toString() + "'";
+        val = '\'' + JSON.stringify(val) + '\'';
+      } else if (val && typeof val != 'number' && typeof val != 'boolean') {
+        val = '\'' + val.toString() + '\'';
       } else if (_.isNull(val) || _.isUndefined(val)) {
         val = '';
       }
       return val;
-    }
+    },
   };
 });
\ No newline at end of file
diff --git a/web/pgadmin/static/js/selection/range_selection_helper.js b/web/pgadmin/static/js/selection/range_selection_helper.js
index 936530ff..25cd63c9 100644
--- a/web/pgadmin/static/js/selection/range_selection_helper.js
+++ b/web/pgadmin/static/js/selection/range_selection_helper.js
@@ -8,19 +8,19 @@ define(['slickgrid'], function () {
 
   var isRangeSelected = function (selectedRanges, range) {
     return _.any(selectedRanges, function (selectedRange) {
-      return isSameRange(selectedRange, range)
+      return isSameRange(selectedRange, range);
     });
   };
 
   var isAnyCellOfColumnSelected = function (selectedRanges, column) {
     return _.any(selectedRanges, function (selectedRange) {
-      return selectedRange.fromCell <= column && selectedRange.toCell >= column
+      return selectedRange.fromCell <= column && selectedRange.toCell >= column;
     });
   };
 
   var isAnyCellOfRowSelected = function (selectedRanges, row) {
     return _.any(selectedRanges, function (selectedRange) {
-      return selectedRange.fromRow <= row && selectedRange.toRow >= row
+      return selectedRange.fromRow <= row && selectedRange.toRow >= row;
     });
   };
 
@@ -33,8 +33,8 @@ define(['slickgrid'], function () {
 
   var removeRange = function (selectedRanges, range) {
     return _.filter(selectedRanges, function (selectedRange) {
-      return !(isSameRange(selectedRange, range))
-    })
+      return !(isSameRange(selectedRange, range));
+    });
   };
 
   var addRange = function (ranges, range) {
@@ -45,11 +45,11 @@ define(['slickgrid'], function () {
   var areAllRangesSingleRows = function (ranges, grid) {
     return _.every(ranges, function (range) {
       return range.fromRow == range.toRow && rangeHasCompleteRows(grid, range);
-    })
+    });
   };
 
   var areAllRangesSingleColumns = function (ranges, grid) {
-    return _.every(ranges, isRangeAColumn.bind(this, grid))
+    return _.every(ranges, isRangeAColumn.bind(this, grid));
   };
 
   var rangeForRow = function (grid, rowId) {
@@ -61,7 +61,7 @@ define(['slickgrid'], function () {
   };
 
   var rangeForColumn = function (grid, columnIndex) {
-    return new Slick.Range(0, columnIndex, grid.getDataLength() - 1, columnIndex)
+    return new Slick.Range(0, columnIndex, grid.getDataLength() - 1, columnIndex);
   };
 
   var getRangeOfWholeGrid = function (grid) {
@@ -81,13 +81,13 @@ define(['slickgrid'], function () {
   var areAllRangesCompleteColumns = function (grid, ranges) {
     return _.every(ranges, function (range) {
       return rangeHasCompleteColumns(grid, range);
-    })
+    });
   };
 
   var areAllRangesCompleteRows = function (grid, ranges) {
     return _.every(ranges, function (range) {
       return rangeHasCompleteRows(grid, range);
-    })
+    });
   };
 
   var getIndexesOfCompleteRows = function (grid, ranges) {
@@ -158,5 +158,5 @@ define(['slickgrid'], function () {
     isAnyCellOfColumnSelected: isAnyCellOfColumnSelected,
     isRangeEntirelyWithinSelectedRanges: isRangeEntirelyWithinSelectedRanges,
     isAnyCellOfRowSelected: isAnyCellOfRowSelected,
-  }
-});
\ No newline at end of file
+  };
+});
diff --git a/web/pgadmin/static/js/selection/row_selector.js b/web/pgadmin/static/js/selection/row_selector.js
index 33d5a72e..236ed629 100644
--- a/web/pgadmin/static/js/selection/row_selector.js
+++ b/web/pgadmin/static/js/selection/row_selector.js
@@ -1,7 +1,7 @@
 define([
   'jquery',
   'sources/selection/range_selection_helper',
-  'slickgrid'
+  'slickgrid',
 ], function ($, RangeSelectionHelper) {
   var RowSelector = function () {
     var Slick = window.Slick;
@@ -19,7 +19,7 @@ define([
       if (grid.getColumns()[args.cell].id === 'row-header-column') {
         var $rowHeaderSpan = $(event.target);
 
-        if ($rowHeaderSpan.data('cell-type') != "row-header-selector") {
+        if ($rowHeaderSpan.data('cell-type') != 'row-header-selector') {
           $rowHeaderSpan = $(event.target).find('[data-cell-type="row-header-selector"]');
         }
 
@@ -82,16 +82,16 @@ define([
         formatter: function (rowIndex) {
           return '<span ' +
             'data-row="' + rowIndex + '" ' +
-            'data-cell-type="row-header-selector"/>'
+            'data-cell-type="row-header-selector"/>';
         },
-        width: 30
+        width: 30,
       });
       return columnDefinitions;
     };
 
     $.extend(this, {
-      "init": init,
-      "getColumnDefinitions": getColumnDefinitions
+      'init': init,
+      'getColumnDefinitions': getColumnDefinitions,
     });
   };
 
diff --git a/web/pgadmin/static/js/selection/set_staged_rows.js b/web/pgadmin/static/js/selection/set_staged_rows.js
index 249a6414..46977e3a 100644
--- a/web/pgadmin/static/js/selection/set_staged_rows.js
+++ b/web/pgadmin/static/js/selection/set_staged_rows.js
@@ -11,7 +11,7 @@ define(
   [
     'jquery',
     'underscore',
-    'sources/selection/range_selection_helper'
+    'sources/selection/range_selection_helper',
   ],
   function ($, _, RangeSelectionHelper) {
     function disableButton(selector) {
@@ -55,7 +55,7 @@ define(
         _.each(_.keys(gridRow), function (columnPos) {
           if (_.contains(primaryKeyColumnIndices, Number(columnPos)))
             rowToStage[columnPos] = gridRow[columnPos];
-        })
+        });
       }
       return rowToStage;
     }
@@ -72,7 +72,7 @@ define(
       return stagedRows;
     }
 
-    var setStagedRows = function (e, args) {
+    var setStagedRows = function () {
       var self = this;
 
       function setStagedRows(rowsToStage) {
@@ -88,7 +88,7 @@ define(
 
       function areAllSelectionsEntireRows() {
         return RangeSelectionHelper.areAllRangesCompleteRows(self.grid,
-          self.selection.getSelectedRanges())
+          self.selection.getSelectedRanges());
       }
 
       var selectedRanges = this.selection.getSelectedRanges();
@@ -98,7 +98,7 @@ define(
       }
 
       if (areAllSelectionsEntireRows()) {
-        var selectedRows = RangeSelectionHelper.getIndexesOfCompleteRows(this.grid, this.selection.getSelectedRanges())
+        var selectedRows = RangeSelectionHelper.getIndexesOfCompleteRows(this.grid, this.selection.getSelectedRanges());
         var stagedRows = getPrimaryKeysForSelectedRows(self, selectedRows);
         setStagedRows(stagedRows);
         if (_.isEmpty(stagedRows)) {
diff --git a/web/pgadmin/static/js/selection/xcell_selection_model.js b/web/pgadmin/static/js/selection/xcell_selection_model.js
index 32fed09f..974b7479 100644
--- a/web/pgadmin/static/js/selection/xcell_selection_model.js
+++ b/web/pgadmin/static/js/selection/xcell_selection_model.js
@@ -4,7 +4,7 @@ define([
   'sources/selection/range_selection_helper',
   'sources/slickgrid/pgslick.cellrangeselector',
 
-  'slickgrid'
+  'slickgrid',
 ], function ($, _, RangeSelectionHelper, PGCellRangeSelector) {
   var XCellSelectionModel = function (options) {
 
@@ -13,25 +13,24 @@ define([
     var KEY_ARROW_UP = 38;
     var KEY_ARROW_DOWN = 40;
 
+    var Slick = window.Slick;
     var _grid;
-    var _canvas;
     var _ranges = [];
     var _self = this;
     var _selector = new PGCellRangeSelector({
-      "selectionCss": {
-        "border": "2px solid black"
-      }
+      'selectionCss': {
+        'border': '2px solid black',
+      },
     });
     var _options;
     var _defaults = {
-      selectActiveCell: true
+      selectActiveCell: true,
     };
 
 
     function init(grid) {
       _options = $.extend(true, {}, _defaults, options);
       _grid = grid;
-      _canvas = _grid.getCanvasNode();
       _grid.onActiveCellChanged.subscribe(handleActiveCellChange);
       _grid.onKeyDown.subscribe(handleKeyDown);
       grid.registerPlugin(_selector);
@@ -82,7 +81,7 @@ define([
       }
     }
 
-    function handleBeforeCellRangeSelected(e, args) {
+    function handleBeforeCellRangeSelected(e) {
       if (_grid.getEditorLock().isActive()) {
         e.stopPropagation();
         return false;
@@ -169,18 +168,18 @@ define([
         var mobileCell = getMobileCellFromRange(lastSelectedRange, anchorActiveCell);
 
         switch (getKeycode()) {
-          case KEY_ARROW_LEFT:
-            mobileCell.cell -= 1;
-            break;
-          case KEY_ARROW_RIGHT:
-            mobileCell.cell += 1;
-            break;
-          case KEY_ARROW_UP:
-            mobileCell.row -= 1;
-            break;
-          case KEY_ARROW_DOWN:
-            mobileCell.row += 1;
-            break;
+        case KEY_ARROW_LEFT:
+          mobileCell.cell -= 1;
+          break;
+        case KEY_ARROW_RIGHT:
+          mobileCell.cell += 1;
+          break;
+        case KEY_ARROW_UP:
+          mobileCell.row -= 1;
+          break;
+        case KEY_ARROW_DOWN:
+          mobileCell.row += 1;
+          break;
         }
 
         var newSelectedRange = getNewRange(anchorActiveCell, mobileCell);
@@ -206,7 +205,7 @@ define([
       }
     }
 
-    function handleWindowMouseUp(event) {
+    function handleWindowMouseUp() {
       var selectedRange = _selector.getCurrentRange();
       if (!_.isUndefined(selectedRange)) {
         _grid.onDragEnd.notify({range: selectedRange});
@@ -214,14 +213,14 @@ define([
     }
 
     $.extend(this, {
-      "getSelectedRanges": getSelectedRanges,
-      "setSelectedRanges": setSelectedRanges,
-      "setSelectedRows": setSelectedRows,
+      'getSelectedRanges': getSelectedRanges,
+      'setSelectedRanges': setSelectedRanges,
+      'setSelectedRows': setSelectedRows,
 
-      "init": init,
-      "destroy": destroy,
+      'init': init,
+      'destroy': destroy,
 
-      "onSelectedRangesChanged": new Slick.Event()
+      'onSelectedRangesChanged': new Slick.Event(),
     });
   };
   return XCellSelectionModel;
diff --git a/web/regression/javascript/browser/menu_spec.js b/web/regression/javascript/browser/menu_spec.js
index e13bfe5e..8def5a0d 100644
--- a/web/regression/javascript/browser/menu_spec.js
+++ b/web/regression/javascript/browser/menu_spec.js
@@ -15,7 +15,7 @@ define([
     var MenuItem = pgAdmin.Browser.MenuItem;
     var menuItem;
 
-    describe("when we create a menu item", function () {
+    describe('when we create a menu item', function () {
       describe('and it is disabled', function () {
         beforeEach(function () {
           menuItem = new MenuItem({enable: false, icon: 'fa fa-car'});
diff --git a/web/regression/javascript/check_node_visiblity_spec.js b/web/regression/javascript/check_node_visiblity_spec.js
index 130b1c21..96a8961f 100644
--- a/web/regression/javascript/check_node_visiblity_spec.js
+++ b/web/regression/javascript/check_node_visiblity_spec.js
@@ -7,24 +7,24 @@
 //
 //////////////////////////////////////////////////////////////////////////
 
-define(["sources/check_node_visibility"],
-function (checkNodeVisibility, pgBrowser) {
-  describe("checkNodeVisibility", function () {
+define(['sources/check_node_visibility'],
+function (checkNodeVisibility) {
+  describe('checkNodeVisibility', function () {
 
     var browser;
 
     browser = jasmine.createSpyObj('browser', [
-                    'node_preference_data', 'get_preference']
+      'node_preference_data', 'get_preference']
                 );
 
-    describe("when node is server collection", function () {
-      it("returns true", function () {
+    describe('when node is server collection', function () {
+      it('returns true', function () {
         expect(checkNodeVisibility(browser, 'coll-server')).toEqual(true);
       });
     });
 
-    describe("when node is server", function () {
-      it("returns true", function () {
+    describe('when node is server', function () {
+      it('returns true', function () {
         expect(checkNodeVisibility(browser, 'server')).toEqual(true);
       });
     });
diff --git a/web/regression/javascript/fake_translations.js b/web/regression/javascript/fake_translations.js
index 9516fd63..a3d4f5bd 100644
--- a/web/regression/javascript/fake_translations.js
+++ b/web/regression/javascript/fake_translations.js
@@ -8,5 +8,5 @@
 //////////////////////////////////////////////////////////////////////////
 
 define(function () {
-  return {}
+  return {};
 });
\ No newline at end of file
diff --git a/web/regression/javascript/selection/active_cell_capture_spec.js b/web/regression/javascript/selection/active_cell_capture_spec.js
index a0bd3848..4ed6c12f 100644
--- a/web/regression/javascript/selection/active_cell_capture_spec.js
+++ b/web/regression/javascript/selection/active_cell_capture_spec.js
@@ -18,6 +18,7 @@ define([
     var onColumnsResizedFunction;
     var onHeaderMouseEnterFunction;
     var onHeaderMouseLeaveFunction;
+    var Slick = window.Slick;
 
     beforeEach(function () {
       getSelectedRangesSpy = jasmine.createSpy('getSelectedRangesSpy');
@@ -27,15 +28,15 @@ define([
           return {
             getSelectedRanges: getSelectedRangesSpy,
             setSelectedRanges: setSelectedRangesSpy,
-          }
+          };
         },
 
         getColumns: function () {
           return [
             {id: 'row-header-column'},
             {id: 'column-1'},
-            {id: 'column-2'}
-          ]
+            {id: 'column-2'},
+          ];
         },
 
         onDragEnd: jasmine.createSpyObj('onDragEnd', ['subscribe']),
@@ -47,7 +48,7 @@ define([
         onHeaderMouseEnter: jasmine.createSpyObj('onHeaderMouseEnter', ['subscribe']),
         onHeaderMouseLeave: jasmine.createSpyObj('onHeaderMouseLeave', ['subscribe']),
 
-        getDataLength: function () { return 10 },
+        getDataLength: function () { return 10; },
 
         setActiveCell: jasmine.createSpy('setActiveCell'),
         getActiveCell: jasmine.createSpy('getActiveCell'),
@@ -108,7 +109,7 @@ define([
 
         describe('when a different column is clicked', function () {
           beforeEach(function () {
-            onHeaderClickFunction({}, {column: {pos: 4}})
+            onHeaderClickFunction({}, {column: {pos: 4}});
           });
 
           it('should set the active cell to the newly clicked columns top cell', function () {
@@ -217,7 +218,7 @@ define([
 
         describe('when the third column is clicked (thereby deselecting it)', function () {
           beforeEach(function () {
-            onHeaderClickFunction({}, {column: {pos: 21}})
+            onHeaderClickFunction({}, {column: {pos: 21}});
           });
 
           it('should set the active cell to the second column', function () {
@@ -227,7 +228,7 @@ define([
 
         describe('when the second column is clicked (thereby deselecting it)', function () {
           beforeEach(function () {
-            onHeaderClickFunction({}, {column: {pos: 5}})
+            onHeaderClickFunction({}, {column: {pos: 5}});
           });
 
           it('should not set the active cell', function () {
@@ -268,7 +269,7 @@ define([
           beforeEach(function () {
             grid.getActiveCell.and.returnValue({row: 4, cell: 5});
             getSelectedRangesSpy.and.returnValue([
-              new Slick.Range(4, 5)
+              new Slick.Range(4, 5),
             ]);
           });
 
@@ -283,7 +284,7 @@ define([
           beforeEach(function () {
             grid.getActiveCell.and.returnValue({row: 3, cell: 1});
             getSelectedRangesSpy.and.returnValue([
-              RangeSelectionHelper.rangeForRow(grid, 3)
+              RangeSelectionHelper.rangeForRow(grid, 3),
             ]);
           });
 
@@ -307,7 +308,7 @@ define([
             grid.getActiveCell.and.returnValue({row: 3, cell: 1});
             getSelectedRangesSpy.and.returnValue([
               RangeSelectionHelper.rangeForRow(grid, 5),
-              RangeSelectionHelper.rangeForRow(grid, 3)
+              RangeSelectionHelper.rangeForRow(grid, 3),
             ]);
           });
 
@@ -330,7 +331,7 @@ define([
 
         describe('and the editable new row', function () {
           beforeEach(function () {
-            onClickFunction({}, {row: 10, cell: 0})
+            onClickFunction({}, {row: 10, cell: 0});
           });
           it('does not select the row', function () {
             expect(grid.setActiveCell).not.toHaveBeenCalled();
diff --git a/web/regression/javascript/selection/column_selector_spec.js b/web/regression/javascript/selection/column_selector_spec.js
index 74d137a4..61115495 100644
--- a/web/regression/javascript/selection/column_selector_spec.js
+++ b/web/regression/javascript/selection/column_selector_spec.js
@@ -22,10 +22,12 @@ import 'sources/slickgrid/pgslick.cellrangeselector';
 describe('ColumnSelector', function () {
   var container, data, columns, options;
   var SlickGrid = Slick.Grid;
-  var KEY_RIGHT = 39;
-  var KEY_LEFT = 37;
-  var KEY_UP = 38;
-  var KEY_DOWN = 40;
+  var KEY = {
+    RIGHT: 39,
+    LEFT: 37,
+    UP: 38,
+    DOWN: 40,
+  };
 
   beforeEach(function () {
     container = $('<div></div>');
@@ -35,11 +37,11 @@ describe('ColumnSelector', function () {
     data = [{
       'some-column-name': 'first value',
       'second column': 'second value',
-      'third column': 'nonselectable value'
+      'third column': 'nonselectable value',
     }, {
       'some-column-name': 'row 1 - first value',
       'second column': 'row 1 - second value',
-      'third column': 'row 1 - nonselectable value'
+      'third column': 'row 1 - nonselectable value',
     }];
 
     columns = [
@@ -48,36 +50,36 @@ describe('ColumnSelector', function () {
         name: 'row header column name',
         selectable: false,
         display_name: 'row header column name',
-        column_type: 'text'
+        column_type: 'text',
       },
       {
         id: '1',
         name: 'some-column-name',
         pos: 0,
         display_name: 'some-column-name',
-        column_type: 'text'
+        column_type: 'text',
       },
       {
         id: '2',
         name: 'second column',
         pos: 1,
         display_name: 'second column',
-        column_type: 'json'
+        column_type: 'json',
       },
       {
         id: 'third-column-id',
         name: 'third column',
         pos: 2,
         display_name: 'third column',
-        column_type: 'text'
+        column_type: 'text',
       },
       {
         name: 'some-non-selectable-column',
         selectable: false,
         pos: 3,
         display_name: 'some-non-selectable-column',
-        column_type: 'numeric'
-      }
+        column_type: 'numeric',
+      },
     ];
   });
 
@@ -170,7 +172,7 @@ describe('ColumnSelector', function () {
 
       describe('and presses shift + right-arrow', function () {
         beforeEach(function () {
-          pressShiftArrow(KEY_RIGHT);
+          pressShiftArrow(KEY.RIGHT);
         });
 
         it('keeps the last column selected', function () {
@@ -202,7 +204,7 @@ describe('ColumnSelector', function () {
 
         describe('and presses shift + right-arrow', function () {
           it('first and second columns are selected', function () {
-            pressShiftArrow(KEY_RIGHT);
+            pressShiftArrow(KEY.RIGHT);
 
             var selectedRanges = cellSelectionModel.getSelectedRanges();
 
@@ -266,7 +268,7 @@ describe('ColumnSelector', function () {
         var selectedRanges = cellSelectionModel.getSelectedRanges();
 
         expect(selectedRanges.length).toEqual(0);
-      })
+      });
     });
 
     describe('when the column is not selectable', function () {
diff --git a/web/regression/javascript/selection/copy_data_spec.js b/web/regression/javascript/selection/copy_data_spec.js
index 2d6b6ad2..9e05e845 100644
--- a/web/regression/javascript/selection/copy_data_spec.js
+++ b/web/regression/javascript/selection/copy_data_spec.js
@@ -19,52 +19,51 @@ import XCellSelectionModel from 'sources/selection/xcell_selection_model';
 
 describe('copyData', function () {
   var grid, sqlEditor, gridContainer, buttonPasteRow;
-  var Slick, SlickGrid;
+  var SlickGrid;
 
   beforeEach(function () {
-    Slick = window.Slick;
     SlickGrid = Slick.Grid;
     var data = [[1, 'leopord', '12'],
       [2, 'lion', '13'],
       [3, 'puma', '9']];
 
     var columns = [
-        {
-          id: 'row-header-column',
-          name: 'row header column name',
-          selectable: false,
-          display_name: 'row header column name',
-          column_type: 'text'
-        },
-        {
-          name: 'id',
-          pos: 0,
-          label: 'id<br> numeric',
-          cell: 'number',
-          can_edit: false,
-          type: 'numeric'
-        }, {
-          name: 'brand',
-          pos: 1,
-          label: 'flavor<br> character varying',
-          cell: 'string',
-          can_edit: false,
-          type: 'character varying'
-        }, {
-          name: 'size',
-          pos: 2,
-          label: 'size<br> numeric',
-          cell: 'number',
-          can_edit: false,
-          type: 'numeric'
-        }
-      ]
+      {
+        id: 'row-header-column',
+        name: 'row header column name',
+        selectable: false,
+        display_name: 'row header column name',
+        column_type: 'text',
+      },
+      {
+        name: 'id',
+        pos: 0,
+        label: 'id<br> numeric',
+        cell: 'number',
+        can_edit: false,
+        type: 'numeric',
+      }, {
+        name: 'brand',
+        pos: 1,
+        label: 'flavor<br> character varying',
+        cell: 'string',
+        can_edit: false,
+        type: 'character varying',
+      }, {
+        name: 'size',
+        pos: 2,
+        label: 'size<br> numeric',
+        cell: 'number',
+        can_edit: false,
+        type: 'numeric',
+      },
+    ]
     ;
     gridContainer = $('<div id=\'grid\'></div>');
     $('body').append(gridContainer);
     buttonPasteRow = $('<button id=\'btn-paste-row\' disabled></button>');
     $('body').append(buttonPasteRow);
-    grid = new Slick.Grid('#grid', data, columns, {});
+    grid = new SlickGrid('#grid', data, columns, {});
     grid.setSelectionModel(new XCellSelectionModel());
     sqlEditor = {slickgrid: grid};
   });
diff --git a/web/regression/javascript/selection/grid_selector_spec.js b/web/regression/javascript/selection/grid_selector_spec.js
index f7b39ee8..ce54cc69 100644
--- a/web/regression/javascript/selection/grid_selector_spec.js
+++ b/web/regression/javascript/selection/grid_selector_spec.js
@@ -8,7 +8,6 @@
 //////////////////////////////////////////////////////////////////////////
 
 import $ from 'jquery';
-import _ from 'underscore';
 
 import Slick from 'slickgrid';
 import 'slickgrid.grid';
@@ -18,21 +17,20 @@ import XCellSelectionModel from 'sources/selection/xcell_selection_model';
 
 describe('GridSelector', function () {
   var container, data, columns, gridSelector, xCellSelectionModel;
-  var Slick, SlickGrid;
+  var SlickGrid;
 
   beforeEach(function () {
-    Slick = window.Slick;
     SlickGrid = Slick.Grid;
     container = $('<div></div>');
     container.height(9999);
     columns = [{
       id: '1',
       name: 'some-column-name',
-      pos: 0
+      pos: 0,
     }, {
       id: '2',
       name: 'second column',
-      pos: 1
+      pos: 1,
     }];
 
     gridSelector = new GridSelector();
@@ -42,7 +40,7 @@ describe('GridSelector', function () {
     for (var i = 0; i < 10; i++) {
       data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
     }
-    var grid = new Slick.Grid(container, data, columns);
+    var grid = new SlickGrid(container, data, columns);
 
     xCellSelectionModel = new XCellSelectionModel();
     grid.setSelectionModel(xCellSelectionModel);
diff --git a/web/regression/javascript/selection/range_boundary_navigator_spec.js b/web/regression/javascript/selection/range_boundary_navigator_spec.js
index b57087f3..1de6d812 100644
--- a/web/regression/javascript/selection/range_boundary_navigator_spec.js
+++ b/web/regression/javascript/selection/range_boundary_navigator_spec.js
@@ -23,8 +23,8 @@ describe('RangeBoundaryNavigator', function () {
       });
     });
 
-    describe("when the ranges all overlap partially or touch", function () {
-      it("returns one long range", function () {
+    describe('when the ranges all overlap partially or touch', function () {
+      it('returns one long range', function () {
         var rangeBounds = [[3, 6], [1, 4], [7, 14]];
 
         var union = rangeBoundaryNavigator.getUnion(rangeBounds);
@@ -32,7 +32,7 @@ describe('RangeBoundaryNavigator', function () {
         expect(union).toEqual([[1, 14]]);
       });
 
-      it("returns them in order from lowest to highest", function () {
+      it('returns them in order from lowest to highest', function () {
         var rangeBounds = [[3, 6], [2, 3], [10, 12]];
 
         var union = rangeBoundaryNavigator.getUnion(rangeBounds);
@@ -40,9 +40,9 @@ describe('RangeBoundaryNavigator', function () {
         expect(union).toEqual([[2, 6], [10, 12]]);
       });
 
-      describe("when one range completely overlaps another", function() {
+      describe('when one range completely overlaps another', function() {
 
-        it("returns them in order from lowest to highest", function () {
+        it('returns them in order from lowest to highest', function () {
           var rangeBounds = [[9, 14], [2, 3], [11, 13]];
 
           var union = rangeBoundaryNavigator.getUnion(rangeBounds);
@@ -51,19 +51,19 @@ describe('RangeBoundaryNavigator', function () {
         });
       });
 
-      describe("when one range is a subset of another", function () {
-        it("returns the larger range", function () {
+      describe('when one range is a subset of another', function () {
+        it('returns the larger range', function () {
           var rangeBounds = [[2, 6], [1, 14], [8, 10]];
 
           var union = rangeBoundaryNavigator.getUnion(rangeBounds);
 
           expect(union).toEqual([[1, 14]]);
-        })
-      })
+        });
+      });
     });
 
-    describe("when the ranges do not touch", function () {
-      it("returns them in order from lowest to highest", function () {
+    describe('when the ranges do not touch', function () {
+      it('returns them in order from lowest to highest', function () {
         var rangeBounds = [[3, 6], [1, 1], [8, 10]];
 
         var union = rangeBoundaryNavigator.getUnion(rangeBounds);
@@ -73,8 +73,8 @@ describe('RangeBoundaryNavigator', function () {
     });
   });
 
-  describe("#mapDimensionBoundaryUnion", function () {
-    it("returns a list of the results of the callback", function () {
+  describe('#mapDimensionBoundaryUnion', function () {
+    it('returns a list of the results of the callback', function () {
       var rangeBounds = [[0, 1], [3, 3]];
       var callback = function () {
         return 'hello';
@@ -83,7 +83,7 @@ describe('RangeBoundaryNavigator', function () {
       expect(result).toEqual(['hello', 'hello', 'hello']);
     });
 
-    it("calls the callback with each index in the dimension", function () {
+    it('calls the callback with each index in the dimension', function () {
       var rangeBounds = [[0, 1], [3, 3]];
       var callback = jasmine.createSpy('callbackSpy');
       rangeBoundaryNavigator.mapDimensionBoundaryUnion(rangeBounds, callback);
@@ -91,7 +91,7 @@ describe('RangeBoundaryNavigator', function () {
     });
   });
 
-  describe("#mapOver2DArray", function () {
+  describe('#mapOver2DArray', function () {
     var data, rowCollector, processCell;
     beforeEach(function () {
       data = [[0, 1, 2, 3], [2, 2, 2, 2], [4, 5, 6, 7]];
@@ -103,76 +103,75 @@ describe('RangeBoundaryNavigator', function () {
       };
     });
 
-    it("calls the callback for each item in the ranges", function () {
+    it('calls the callback for each item in the ranges', function () {
       var rowRanges = [[0, 0], [2, 2]];
       var colRanges = [[0, 3]];
 
       var selectionResult = rangeBoundaryNavigator.mapOver2DArray(rowRanges, colRanges, processCell, rowCollector);
 
-      expect(selectionResult).toEqual(["[0,1,2,3]", "[4,5,6,7]"]);
+      expect(selectionResult).toEqual(['[0,1,2,3]', '[4,5,6,7]']);
     });
 
-    describe("when the ranges are out of order/duplicated", function () {
+    describe('when the ranges are out of order/duplicated', function () {
       var rowRanges, colRanges;
       beforeEach(function () {
         rowRanges = [[2, 2], [2, 2], [0, 0]];
         colRanges = [[0, 3]];
       });
 
-      it("uses the union of the ranges", function () {
-        spyOn(rangeBoundaryNavigator, "getUnion").and.callThrough();
+      it('uses the union of the ranges', function () {
+        spyOn(rangeBoundaryNavigator, 'getUnion').and.callThrough();
 
         var selectionResult = rangeBoundaryNavigator.mapOver2DArray(rowRanges, colRanges, processCell, rowCollector);
 
         expect(rangeBoundaryNavigator.getUnion).toHaveBeenCalledWith(rowRanges);
         expect(rangeBoundaryNavigator.getUnion).toHaveBeenCalledWith(colRanges);
-        expect(selectionResult).toEqual(["[0,1,2,3]", "[4,5,6,7]"]);
+        expect(selectionResult).toEqual(['[0,1,2,3]', '[4,5,6,7]']);
       });
     });
   });
 
-  describe("#rangesToCsv", function () {
+  describe('#rangesToCsv', function () {
     var data, columnDefinitions, ranges;
     beforeEach(function () {
-      data = [[1, "leopard", "12"],
-        [2, "lion", "13"],
-        [3, "cougar", "9"],
-        [4, "tiger", "10"]];
+      data = [[1, 'leopard', '12'],
+        [2, 'lion', '13'],
+        [3, 'cougar', '9'],
+        [4, 'tiger', '10']];
       columnDefinitions = [{name: 'id', pos: 0}, {name: 'animal', pos: 1}, {name: 'size', pos: 2}];
       ranges = [new Slick.Range(0, 0, 0, 2), new Slick.Range(3, 0, 3, 2)];
     });
 
-    it("returns csv for the provided ranges", function () {
+    it('returns csv for the provided ranges', function () {
       var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, ranges);
 
-      expect(csvResult).toEqual("1,'leopard','12'\n4,'tiger','10'");
+      expect(csvResult).toEqual('1,\'leopard\',\'12\'\n4,\'tiger\',\'10\'');
     });
 
-    describe("when no cells are selected", function () {
-      it("should return an empty string", function () {
+    describe('when no cells are selected', function () {
+      it('should return an empty string', function () {
         var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, []);
 
         expect(csvResult).toEqual('');
       });
     });
 
-    describe("when there is an extra column with checkboxes", function () {
-
+    describe('when there is an extra column with checkboxes', function () {
       beforeEach(function () {
         columnDefinitions = [{name: 'not-a-data-column'}, {name: 'id', pos: 0}, {name: 'animal', pos: 1}, {
           name: 'size',
-          pos: 2
+          pos: 2,
         }];
         ranges = [new Slick.Range(0, 0, 0, 3), new Slick.Range(3, 0, 3, 3)];
       });
 
-      it("returns csv for the columns with data", function () {
+      it('returns csv for the columns with data', function () {
         var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, ranges);
 
-        expect(csvResult).toEqual("1,'leopard','12'\n4,'tiger','10'");
+        expect(csvResult).toEqual('1,\'leopard\',\'12\'\n4,\'tiger\',\'10\'');
       });
-      describe("when no cells are selected", function () {
-        it("should return an empty string", function () {
+      describe('when no cells are selected', function () {
+        it('should return an empty string', function () {
           var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, []);
 
           expect(csvResult).toEqual('');
@@ -180,4 +179,4 @@ describe('RangeBoundaryNavigator', function () {
       });
     });
   });
-});
\ No newline at end of file
+});
diff --git a/web/regression/javascript/selection/range_selection_helper_spec.js b/web/regression/javascript/selection/range_selection_helper_spec.js
index 63c07259..62200085 100644
--- a/web/regression/javascript/selection/range_selection_helper_spec.js
+++ b/web/regression/javascript/selection/range_selection_helper_spec.js
@@ -3,21 +3,21 @@ import Slick from 'slickgrid';
 import 'slickgrid.grid';
 import RangeSelectionHelper from 'sources/selection/range_selection_helper';
 
-describe("RangeSelectionHelper utility functions", function () {
+describe('RangeSelectionHelper utility functions', function () {
   var grid;
   beforeEach(function () {
     var container, data, columns, options;
-    container = $("<div></div>");
+    container = $('<div></div>');
     container.height(9999);
 
     columns = [{
       id: '1',
       name: 'some-column-name',
-      pos: 0
+      pos: 0,
     }, {
       id: 'second-column-id',
       name: 'second column',
-      pos: 1
+      pos: 1,
     }];
 
     data = [];
@@ -29,18 +29,18 @@ describe("RangeSelectionHelper utility functions", function () {
     grid.invalidate();
   });
 
-  describe("#getIndexesOfCompleteRows", function () {
-    describe("when selected ranges are not rows", function () {
-      it("returns an empty array", function () {
+  describe('#getIndexesOfCompleteRows', function () {
+    describe('when selected ranges are not rows', function () {
+      it('returns an empty array', function () {
         var rowlessRanges = [RangeSelectionHelper.rangeForColumn(grid, 1)];
 
         expect(RangeSelectionHelper.getIndexesOfCompleteRows(grid, rowlessRanges))
           .toEqual([]);
       });
     });
-    describe("when selected range", function () {
-      describe("is a single row", function () {
-        it("returns an array with one index", function () {
+    describe('when selected range', function () {
+      describe('is a single row', function () {
+        it('returns an array with one index', function () {
           var singleRowRange = [RangeSelectionHelper.rangeForRow(grid, 1)];
 
           expect(RangeSelectionHelper.getIndexesOfCompleteRows(grid, singleRowRange))
@@ -48,8 +48,8 @@ describe("RangeSelectionHelper utility functions", function () {
         });
       });
 
-      describe("is multiple rows", function () {
-        it("returns an array of each row's index", function () {
+      describe('is multiple rows', function () {
+        it('returns an array of each row\'s index', function () {
           var multipleRowRange = [
             RangeSelectionHelper.rangeForRow(grid, 0),
             RangeSelectionHelper.rangeForRow(grid, 3),
@@ -62,10 +62,10 @@ describe("RangeSelectionHelper utility functions", function () {
         });
       });
 
-      describe("contains a multi row selection", function () {
-        it("returns an array of each individual row's index", function () {
+      describe('contains a multi row selection', function () {
+        it('returns an array of each individual row\'s index', function () {
           var multipleRowRange = [
-            new Slick.Range(3, 0, 5, 1)
+            new Slick.Range(3, 0, 5, 1),
           ];
 
           var indexesOfCompleteRows = RangeSelectionHelper.getIndexesOfCompleteRows(grid, multipleRowRange);
@@ -73,11 +73,11 @@ describe("RangeSelectionHelper utility functions", function () {
           expect(indexesOfCompleteRows).toEqual([3, 4, 5]);
         });
 
-        describe("and also contains a selection that is not a row", function () {
-          it("returns an array of only the complete rows' indexes", function () {
+        describe('and also contains a selection that is not a row', function () {
+          it('returns an array of only the complete rows\' indexes', function () {
             var multipleRowRange = [
               new Slick.Range(8, 1, 9, 1),
-              new Slick.Range(3, 0, 5, 1)
+              new Slick.Range(3, 0, 5, 1),
             ];
 
             var indexesOfCompleteRows = RangeSelectionHelper.getIndexesOfCompleteRows(grid, multipleRowRange);
diff --git a/web/regression/javascript/selection/row_selector_spec.js b/web/regression/javascript/selection/row_selector_spec.js
index dbf65e26..db3c0b4f 100644
--- a/web/regression/javascript/selection/row_selector_spec.js
+++ b/web/regression/javascript/selection/row_selector_spec.js
@@ -8,7 +8,6 @@
 //////////////////////////////////////////////////////////////////////////
 
 import $ from 'jquery';
-import _ from 'underscore';
 
 import Slick from 'slickgrid';
 import 'slickgrid.grid';
@@ -18,10 +17,12 @@ import ActiveCellCapture from 'sources/selection/active_cell_capture';
 import XCellSelectionModel from 'sources/selection/xcell_selection_model';
 
 describe('RowSelector', function () {
-  var KEY_RIGHT = 39;
-  var KEY_LEFT = 37;
-  var KEY_UP = 38;
-  var KEY_DOWN = 40;
+  var KEY = {
+    RIGHT: 39,
+    LEFT: 37,
+    UP: 38,
+    DOWN: 40,
+  };
   var container, data, columnDefinitions, grid, cellSelectionModel;
   var SlickGrid = Slick.Grid;
 
@@ -34,12 +35,12 @@ describe('RowSelector', function () {
       id: '1',
       name: 'some-column-name',
       selectable: true,
-      pos: 0
+      pos: 0,
     }, {
       id: '2',
       name: 'second column',
       selectable: true,
-      pos: 1
+      pos: 1,
     }];
 
     var rowSelector = new RowSelector();
@@ -134,7 +135,7 @@ describe('RowSelector', function () {
 
       describe('and presses shift + down-arrow', function () {
         beforeEach(function () {
-          pressShiftArrow(KEY_DOWN);
+          pressShiftArrow(KEY.DOWN);
         });
 
         it('keeps the last row selected', function () {
@@ -301,7 +302,7 @@ describe('RowSelector', function () {
         var selectedRanges = cellSelectionModel.getSelectedRanges();
 
         expect(selectedRanges.length).toEqual(0);
-      })
+      });
     });
   });
 
diff --git a/web/regression/javascript/selection/set_staged_rows_spec.js b/web/regression/javascript/selection/set_staged_rows_spec.js
index 99c6d3a6..67353144 100644
--- a/web/regression/javascript/selection/set_staged_rows_spec.js
+++ b/web/regression/javascript/selection/set_staged_rows_spec.js
@@ -8,9 +8,9 @@
 //////////////////////////////////////////////////////////////
 
 define([
-  "jquery",
-  "underscore",
-  "sources/selection/set_staged_rows",
+  'jquery',
+  'underscore',
+  'sources/selection/set_staged_rows',
 ], function ($, _, SetStagedRows) {
   describe('set_staged_rows', function () {
     var sqlEditorObj, gridSpy, deleteButton, copyButton, selectionSpy;
@@ -20,7 +20,7 @@ define([
         {0: 'one', 1: 'two', __temp_PK: '123'},
         {0: 'three', 1: 'four', __temp_PK: '456'},
         {0: 'five', 1: 'six', __temp_PK: '789'},
-        {0: 'seven', 1: 'eight', __temp_PK: '432'}
+        {0: 'seven', 1: 'eight', __temp_PK: '432'},
       ]);
       gridSpy.getColumns.and.returnValue([
         {
@@ -29,7 +29,7 @@ define([
         }, {
           pos: 1,
           selectable: true,
-        }
+        },
       ]);
 
       selectionSpy = jasmine.createSpyObj('selectionSpy', ['setSelectedRows', 'getSelectedRanges']);
@@ -42,23 +42,23 @@ define([
         editor: {
           handler: {
             data_store: {
-              staged_rows: {'456': {}}
+              staged_rows: {'456': {}},
             },
-            can_edit: false
-          }
+            can_edit: false,
+          },
         },
         keys: null,
         selection: selectionSpy,
         columns: [
           {
             name: 'a pk column',
-            pos: 0
+            pos: 0,
           },
           {
             name: 'some column',
-            pos: 1
-          }
-        ]
+            pos: 1,
+          },
+        ],
       };
 
       $('body').append(deleteButton);
@@ -195,7 +195,7 @@ define([
               {0: 'one', 1: 'two', __temp_PK: '123'},
               {1: 'four', __temp_PK: '456'},
               {1: 'six', __temp_PK: '789'},
-              {0: 'seven', 1: 'eight', __temp_PK: '432'}
+              {0: 'seven', 1: 'eight', __temp_PK: '432'},
             ]);
           });
 
@@ -228,7 +228,7 @@ define([
           it('should not clear the staged rows', function () {
             expect(sqlEditorObj.editor.handler.data_store.staged_rows).toEqual({
               '456': {0: 'three'},
-              '789': {0: 'five'}
+              '789': {0: 'five'},
             });
           });
 
diff --git a/web/regression/javascript/selection/xcell_selection_model_spec.js b/web/regression/javascript/selection/xcell_selection_model_spec.js
index 8f01035c..db3f6d60 100644
--- a/web/regression/javascript/selection/xcell_selection_model_spec.js
+++ b/web/regression/javascript/selection/xcell_selection_model_spec.js
@@ -10,6 +10,7 @@
 import XCellSelectionModel from 'sources/selection/xcell_selection_model';
 import 'slickgrid.grid';
 import Slick from 'slickgrid';
+import $ from 'jquery';
 
 describe('XCellSelectionModel', function () {
   var KEY_RIGHT = 39;
@@ -31,15 +32,15 @@ describe('XCellSelectionModel', function () {
     }, {
       id: '1',
       name: 'some-column-name',
-      pos: 0
+      pos: 0,
     }, {
       id: 'second-column-id',
       name: 'second column',
-      pos: 1
+      pos: 1,
     }, {
       id: 'third-column-id',
       name: 'third column',
-      pos: 2
+      pos: 2,
     },
     ];
 
@@ -52,13 +53,13 @@ describe('XCellSelectionModel', function () {
         'fourth column': 'fourth value ' + i,
       });
     }
-    container = $("<div></div>");
+    container = $('<div></div>');
     container.height(9999);
     container.width(9999);
 
     grid = new SlickGrid(container, data, columns);
     grid.setSelectionModel(new XCellSelectionModel());
-    $("body").append(container);
+    $('body').append(container);
   });
 
   afterEach(function () {
@@ -397,13 +398,13 @@ describe('XCellSelectionModel', function () {
         target: $cell,
         isPropagationStopped: jasmine.createSpy('isPropagationStopped').and.returnValue(false),
         isImmediatePropagationStopped: jasmine.createSpy('isImmediatePropagationStopped').and.returnValue(false),
-        stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation')
+        stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation'),
       };
 
       dd = {
         grid: grid,
         startX: cellLeftPosition(initialPosition.cell),
-        startY: cellTopPosition($cell, initialPosition.row)
+        startY: cellTopPosition($cell, initialPosition.row),
       };
 
       grid.onDragStart.notify(dd, event, grid);
@@ -421,7 +422,7 @@ describe('XCellSelectionModel', function () {
           stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation'),
 
           pageX: cellLeftPosition(finalPosition.cell),
-          pageY: cellTopPosition($cell, finalPosition.row)
+          pageY: cellTopPosition($cell, finalPosition.row),
         };
 
         grid.onDrag.notify(dd, event, grid);
@@ -448,7 +449,7 @@ describe('XCellSelectionModel', function () {
     });
 
     it('do not notify onDragEnd', function () {
-      expect(grid.onDragEnd.notify).not.toHaveBeenCalled()
+      expect(grid.onDragEnd.notify).not.toHaveBeenCalled();
     });
   });
 
@@ -482,14 +483,14 @@ describe('XCellSelectionModel', function () {
   });
 
   function pressKey(keyCode) {
-    var pressEvent = new $.Event("keydown");
+    var pressEvent = new $.Event('keydown');
     pressEvent.which = keyCode;
 
     $(container.find('.grid-canvas')).trigger(pressEvent);
   }
 
   function pressShiftPlusKey(keyCode) {
-    var pressEvent = new $.Event("keydown");
+    var pressEvent = new $.Event('keydown');
     pressEvent.shiftKey = true;
     pressEvent.which = keyCode;
 
diff --git a/web/regression/javascript/size_prettify_spec.js b/web/regression/javascript/size_prettify_spec.js
index 7d370fcb..f60c79c0 100644
--- a/web/regression/javascript/size_prettify_spec.js
+++ b/web/regression/javascript/size_prettify_spec.js
@@ -7,59 +7,59 @@
 //
 //////////////////////////////////////////////////////////////////////////
 
-define(["sources/size_prettify"], function (sizePrettify) {
-  describe("sizePrettify", function () {
-    describe("when size is 0", function () {
-      it("returns 0 bytes", function () {
-        expect(sizePrettify(0)).toEqual("0 bytes");
+define(['sources/size_prettify'], function (sizePrettify) {
+  describe('sizePrettify', function () {
+    describe('when size is 0', function () {
+      it('returns 0 bytes', function () {
+        expect(sizePrettify(0)).toEqual('0 bytes');
       });
     });
 
-    describe("when size >= 10kB and size < 10 MB", function () {
-      it("returns size in kB", function () {
-        expect(sizePrettify(10240)).toEqual("10 kB");
+    describe('when size >= 10kB and size < 10 MB', function () {
+      it('returns size in kB', function () {
+        expect(sizePrettify(10240)).toEqual('10 kB');
       });
 
-      it("returns size in kB", function () {
-        expect(sizePrettify(99999)).toEqual("98 kB");
+      it('returns size in kB', function () {
+        expect(sizePrettify(99999)).toEqual('98 kB');
       });
     });
 
 
-    describe("when size >= 10MB and size < 10 GB", function () {
-      it("returns size in MB", function () {
-        expect(sizePrettify(10485760)).toEqual("10 MB");
+    describe('when size >= 10MB and size < 10 GB', function () {
+      it('returns size in MB', function () {
+        expect(sizePrettify(10485760)).toEqual('10 MB');
       });
 
-      it("returns size in MB", function () {
-        expect(sizePrettify(44040192)).toEqual("42 MB");
+      it('returns size in MB', function () {
+        expect(sizePrettify(44040192)).toEqual('42 MB');
       });
     });
 
 
-    describe("when size >= 10GB and size < 10 TB", function () {
-      it("returns size in GB", function () {
-        expect(sizePrettify(10737418240)).toEqual("10 GB");
+    describe('when size >= 10GB and size < 10 TB', function () {
+      it('returns size in GB', function () {
+        expect(sizePrettify(10737418240)).toEqual('10 GB');
       });
 
-      it("returns size in GB", function () {
-        expect(sizePrettify(10736344498176)).toEqual("9999 GB");
+      it('returns size in GB', function () {
+        expect(sizePrettify(10736344498176)).toEqual('9999 GB');
       });
     });
 
-    describe("when size >= 10TB and size < 10 PB", function () {
-      it("returns size in TB", function () {
-        expect(sizePrettify(10995116277760)).toEqual("10 TB");
+    describe('when size >= 10TB and size < 10 PB', function () {
+      it('returns size in TB', function () {
+        expect(sizePrettify(10995116277760)).toEqual('10 TB');
       });
 
-      it("returns size in TB", function () {
-        expect(sizePrettify(29995116277760)).toEqual("27 TB");
+      it('returns size in TB', function () {
+        expect(sizePrettify(29995116277760)).toEqual('27 TB');
       });
     });
 
-    describe("when size >= 10 PB", function () {
-      it("returns size in PB", function () {
-        expect(sizePrettify(11258999068426200)).toEqual("10 PB");
+    describe('when size >= 10 PB', function () {
+      it('returns size in PB', function () {
+        expect(sizePrettify(11258999068426200)).toEqual('10 PB');
       });
 
     });
diff --git a/web/regression/javascript/slickgrid/cell_selector_spec.js b/web/regression/javascript/slickgrid/cell_selector_spec.js
index b32fdf80..7ad72e32 100644
--- a/web/regression/javascript/slickgrid/cell_selector_spec.js
+++ b/web/regression/javascript/slickgrid/cell_selector_spec.js
@@ -7,16 +7,17 @@
 //
 //////////////////////////////////////////////////////////////
 
-import $ from "jquery";
-import SlickGrid from "slickgrid.grid";
-import XCellSelectionModel from "sources/selection/xcell_selection_model";
-import CellSelector from "sources/slickgrid/cell_selector";
-import RangeSelectionHelper from "sources/selection/range_selection_helper";
+import $ from 'jquery';
+import Slick from 'slickgrid';
+import 'slickgrid.grid';
+import XCellSelectionModel from 'sources/selection/xcell_selection_model';
+import CellSelector from 'sources/slickgrid/cell_selector';
+import RangeSelectionHelper from 'sources/selection/range_selection_helper';
 
-describe("CellSelector", function () {
+describe('CellSelector', function () {
   var container, columns, cellSelector, data, cellSelectionModel, grid;
   beforeEach(function () {
-    container = $("<div></div>");
+    container = $('<div></div>');
     container.height(9999);
     container.width(9999);
     columns = [{
@@ -39,17 +40,17 @@ describe("CellSelector", function () {
     grid.registerPlugin(cellSelector);
     grid.invalidate();
 
-    $("body").append(container);
+    $('body').append(container);
   });
 
   afterEach(function () {
-    $("body").find(container).remove();
+    $('body').find(container).remove();
   });
 
-  describe("when the user clicks or tabs to a cell", function () {
-    it("sets the selected range to that cell", function () {
+  describe('when the user clicks or tabs to a cell', function () {
+    it('sets the selected range to that cell', function () {
       var row = 1, column = 0;
-      $(container.find(".slick-row .slick-cell.l" + column)[row]).click();
+      $(container.find('.slick-row .slick-cell.l' + column)[row]).click();
 
       var selectedRanges = cellSelectionModel.getSelectedRanges();
       expect(selectedRanges.length).toBe(1);
@@ -59,14 +60,14 @@ describe("CellSelector", function () {
       expect(selectedRanges[0].toRow).toBe(1);
     });
 
-    it("deselects previously selected ranges", function () {
+    it('deselects previously selected ranges', function () {
       var row2Range = RangeSelectionHelper.rangeForRow(grid, 2);
       var ranges = RangeSelectionHelper.addRange(cellSelectionModel.getSelectedRanges(),
         row2Range);
       cellSelectionModel.setSelectedRanges(ranges);
 
       var row = 4, column = 1;
-      $(container.find(".slick-row .slick-cell.l" + column)[row]).click();
+      $(container.find('.slick-row .slick-cell.l' + column)[row]).click();
 
       expect(RangeSelectionHelper.isRangeSelected(cellSelectionModel.getSelectedRanges(), row2Range))
         .toBe(false);
diff --git a/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js b/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js
index 884ae415..eb4411dc 100644
--- a/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js
+++ b/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js
@@ -13,10 +13,10 @@ import RangeSelectionHelper from 'sources/selection/range_selection_helper';
 import XCellSelectionModel from 'sources/selection/xcell_selection_model';
 import Slick from 'slickgrid';
 import 'slickgrid.grid';
-
+import $ from 'jquery';
 
 describe('#handleQueryOutputKeyboardEvent', function () {
-  var event, view, grid, slickEvent;
+  var event, grid, slickEvent;
   var SlickGrid = Slick.Grid;
   var handleQueryOutputKeyboardEvent;
 
@@ -27,7 +27,7 @@ describe('#handleQueryOutputKeyboardEvent', function () {
       metaKey: false,
       which: -1,
       keyCode: -1,
-      preventDefault: jasmine.createSpy('preventDefault')
+      preventDefault: jasmine.createSpy('preventDefault'),
     };
 
     var data = [['', '0,0-cell-content', '0,1-cell-content'],
@@ -35,22 +35,20 @@ describe('#handleQueryOutputKeyboardEvent', function () {
       ['', '2,0-cell-content', '2,1-cell-content']];
     var columnDefinitions = [{name: 'checkboxColumn'}, {pos: 1, name: 'firstColumn'}, {
       pos: 2,
-      name: 'secondColumn'
+      name: 'secondColumn',
     }];
     grid = new SlickGrid($('<div></div>'), data, columnDefinitions);
     grid.setSelectionModel(new XCellSelectionModel());
 
     slickEvent = {
-      grid: grid
+      grid: grid,
     };
 
-    view = {};
     spyOn(clipboard, 'copyTextToClipboard');
     handleQueryOutputKeyboardEvent = HandleQueryOutputKeyboardEvent.bind(window);
-    debugger
   });
 
-  describe("when a range is selected", function () {
+  describe('when a range is selected', function () {
     beforeEach(function () {
       grid.getSelectionModel().setSelectedRanges([
         RangeSelectionHelper.rangeForRow(grid, 0),
@@ -58,39 +56,39 @@ describe('#handleQueryOutputKeyboardEvent', function () {
       ]);
     });
 
-    describe("pressing Command + C", function () {
+    describe('pressing Command + C', function () {
       beforeEach(function () {
         event.metaKey = true;
         event.keyCode = 67;
       });
 
-      it("copies the cell content to the clipboard", function () {
+      it('copies the cell content to the clipboard', function () {
         handleQueryOutputKeyboardEvent(event, slickEvent);
 
-        expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith("'0,0-cell-content','0,1-cell-content'\n'2,0-cell-content','2,1-cell-content'");
+        expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith('\'0,0-cell-content\',\'0,1-cell-content\'\n\'2,0-cell-content\',\'2,1-cell-content\'');
       });
     });
 
-    describe("pressing Ctrl + C", function () {
+    describe('pressing Ctrl + C', function () {
       beforeEach(function () {
         event.ctrlKey = true;
         event.keyCode = 67;
       });
 
-      it("copies the cell content to the clipboard", function () {
+      it('copies the cell content to the clipboard', function () {
         handleQueryOutputKeyboardEvent(event, slickEvent);
 
-        expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith("'0,0-cell-content','0,1-cell-content'\n'2,0-cell-content','2,1-cell-content'");
+        expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith('\'0,0-cell-content\',\'0,1-cell-content\'\n\'2,0-cell-content\',\'2,1-cell-content\'');
       });
     });
 
-    describe("pressing Command + A", function () {
+    describe('pressing Command + A', function () {
       beforeEach(function () {
         event.metaKey = true;
         event.keyCode = 65;
       });
 
-      it("selects the entire grid to ranges", function () {
+      it('selects the entire grid to ranges', function () {
         handleQueryOutputKeyboardEvent(event, slickEvent);
 
         expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
@@ -98,13 +96,13 @@ describe('#handleQueryOutputKeyboardEvent', function () {
       });
     });
 
-    describe("pressing Ctrl + A", function () {
+    describe('pressing Ctrl + A', function () {
       beforeEach(function () {
         event.ctrlKey = true;
         event.keyCode = 65;
       });
 
-      it("selects the entire grid to ranges", function () {
+      it('selects the entire grid to ranges', function () {
         handleQueryOutputKeyboardEvent(event, slickEvent);
 
         expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
@@ -113,14 +111,14 @@ describe('#handleQueryOutputKeyboardEvent', function () {
     });
   });
 
-  describe("when no ranges are selected", function () {
-    describe("pressing Command + A", function () {
+  describe('when no ranges are selected', function () {
+    describe('pressing Command + A', function () {
       beforeEach(function () {
         event.metaKey = true;
         event.keyCode = 65;
       });
 
-      it("selects the entire grid to ranges", function () {
+      it('selects the entire grid to ranges', function () {
         handleQueryOutputKeyboardEvent(event, slickEvent);
 
         expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
@@ -128,13 +126,13 @@ describe('#handleQueryOutputKeyboardEvent', function () {
       });
     });
 
-    describe("pressing Ctrl + A", function () {
+    describe('pressing Ctrl + A', function () {
       beforeEach(function () {
         event.ctrlKey = true;
         event.keyCode = 65;
       });
 
-      it("selects the entire grid to ranges", function () {
+      it('selects the entire grid to ranges', function () {
         handleQueryOutputKeyboardEvent(event, slickEvent);
 
         expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
diff --git a/web/regression/javascript/sqleditor_utils_spec.js b/web/regression/javascript/sqleditor_utils_spec.js
index 4b79dc03..b1564b37 100644
--- a/web/regression/javascript/sqleditor_utils_spec.js
+++ b/web/regression/javascript/sqleditor_utils_spec.js
@@ -7,18 +7,18 @@
 //
 //////////////////////////////////////////////////////////////////////////
 
-define(["sources/sqleditor_utils"],
+define(['sources/sqleditor_utils'],
 function (SqlEditorUtils) {
-  describe("SqlEditorUtils", function () {
+  describe('SqlEditorUtils', function () {
 
-    describe("Generate a random string of size 10", function () {
-      it("returns string of length 10", function () {
+    describe('Generate a random string of size 10', function () {
+      it('returns string of length 10', function () {
         expect(SqlEditorUtils.epicRandomString(10).length).toEqual(10);
       });
     });
 
-    describe("Generate a unique hash for given string", function () {
-      it("returns unique hash", function () {
+    describe('Generate a unique hash for given string', function () {
+      it('returns unique hash', function () {
         expect(SqlEditorUtils.getHash('select * from test')).toEqual(403379630);
       });
     });
diff --git a/web/webpack.test.config.js b/web/webpack.test.config.js
index 29df6dd8..b156f147 100644
--- a/web/webpack.test.config.js
+++ b/web/webpack.test.config.js
@@ -8,7 +8,6 @@ const regressionDir = path.resolve(__dirname, 'regression');
 module.exports = {
   plugins: [
     new webpack.ProvidePlugin({
-      $: 'jquery',
       jQuery: 'jquery',
       _: 'underscore',
       'underscore.string': 'underscore.string',
diff --git a/web/yarn.lock b/web/yarn.lock
index bfd9171d..b04caa5c 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -1199,15 +1199,7 @@ [email protected]:
     readable-stream "~2.0.0"
     typedarray "~0.0.5"
 
-concat-stream@^1.5.2:
-  version "1.6.0"
-  resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e...;
-  dependencies:
-    inherits "^2.0.3"
-    readable-stream "^2.2.2"
-    typedarray "^0.0.6"
-
-concat-stream@~1.5.0, concat-stream@~1.5.1:
+concat-stream@^1.5.2, concat-stream@~1.5.0, concat-stream@~1.5.1:
   version "1.5.2"
   resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261...;
   dependencies:
@@ -2329,7 +2321,7 @@ inflight@^1.0.4:
     once "^1.3.0"
     wrappy "1"
 
-inherits@2, [email protected], inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1:
+inherits@2, [email protected], inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de&...;
 
@@ -3577,7 +3569,7 @@ read-pkg@^1.0.0:
     normalize-package-data "^2.3.2"
     path-type "^1.0.0"
 
-readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6:
+readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.6:
   version "2.2.11"
   resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.11.tgz#0796b31f8d7688007ff0b93a80...;
   dependencies:
@@ -4239,7 +4231,7 @@ type-is@~1.6.15:
     media-typer "0.3.0"
     mime-types "~2.1.15"
 
-typedarray@^0.0.6, typedarray@~0.0.5:
+typedarray@~0.0.5:
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830...;
 

diff --git a/web/.eslintrc.js b/web/.eslintrc.js
index c60569db..98486dcd 100644
--- a/web/.eslintrc.js
+++ b/web/.eslintrc.js
@@ -5,7 +5,10 @@ module.exports = {
     'amd': true,
     'jasmine': true,
   },
-  'extends': 'eslint:recommended',
+  'extends': [
+    'eslint:recommended',
+    "plugin:react/recommended",
+  ],
   'parserOptions': {
     'ecmaFeatures': {
       'experimentalObjectRestSpread': true,
@@ -40,6 +43,6 @@ module.exports = {
     'comma-dangle': [
       'error',
       'always-multiline'
-    ]
+    ],
   }
 };
\ No newline at end of file
diff --git a/web/karma.conf.js b/web/karma.conf.js
index 5b0ab57b..a194bd16 100644
--- a/web/karma.conf.js
+++ b/web/karma.conf.js
@@ -27,7 +27,7 @@ module.exports = function (config) {
     // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
     preprocessors: {
       'regression/javascript/**/*.js': ['webpack'],
-      // 'regression/javascript/**/*.jsx': ['webpack'],
+      'regression/javascript/**/*.jsx': ['webpack'],
     },
 
     webpack: webpackConfig,
diff --git a/web/package.json b/web/package.json
index b5d2e0cb..e72138c2 100644
--- a/web/package.json
+++ b/web/package.json
@@ -5,9 +5,11 @@
     "babel-preset-es2015": "~6.24.0",
     "babel-preset-react": "~6.23.0",
     "enzyme": "~2.8.2",
+    "enzyme-matchers": "^3.1.0",
     "eslint": "^3.19.0",
     "eslint-plugin-react": "^6.10.3",
     "jasmine-core": "~2.5.2",
+    "jasmine-enzyme": "^3.1.0",
     "karma": "~1.5.0",
     "karma-babel-preprocessor": "^6.0.1",
     "karma-browserify": "~5.1.1",
@@ -28,7 +30,9 @@
     "babelify": "~7.3.0",
     "browserify": "~14.1.0",
     "exports-loader": "~0.6.4",
+    "immutability-helper": "^2.2.0",
     "imports-loader": "git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d";,
+    "moment": "^2.18.1",
     "react": "~15.4.2",
     "react-dom": "~15.4.2",
     "requirejs": "~2.3.3",
diff --git a/web/pgadmin/feature_tests/query_tool_journey_test.py b/web/pgadmin/feature_tests/query_tool_journey_test.py
new file mode 100644
index 00000000..0766193e
--- /dev/null
+++ b/web/pgadmin/feature_tests/query_tool_journey_test.py
@@ -0,0 +1,111 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2017, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import pyperclip
+import time
+
+from selenium.webdriver import ActionChains
+
+from regression.python_test_utils import test_utils
+from regression.feature_utils.base_feature_test import BaseFeatureTest
+
+
+class QueryToolJourneyTest(BaseFeatureTest):
+    """
+    Tests the path through the query tool
+    """
+
+    scenarios = [
+        ("Tests the path through the query tool", dict())
+    ]
+
+    def before(self):
+        connection = test_utils.get_db_connection(self.server['db'],
+                                                  self.server['username'],
+                                                  self.server['db_password'],
+                                                  self.server['host'],
+                                                  self.server['port'])
+        test_utils.drop_database(connection, "acceptance_test_db")
+        test_utils.create_database(self.server, "acceptance_test_db")
+        test_utils.create_table(self.server, "acceptance_test_db", "test_table")
+        self.page.add_server(self.server)
+
+    def runTest(self):
+        self._navigate_to_query_tool()
+        self._execute_query("SELECT * FROM test_table ORDER BY value")
+
+        self._test_copies_rows()
+        self._test_copies_columns()
+        self._test_history_tab()
+
+    def _test_copies_rows(self):
+        pyperclip.copy("old clipboard contents")
+        time.sleep(5)
+        self.page.driver.switch_to.default_content()
+        self.page.driver.switch_to_frame(self.page.driver.find_element_by_tag_name("iframe"))
+        self.page.find_by_xpath("//*[contains(@class, 'slick-row')]/*[1]").click()
+        self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
+
+        self.assertEqual("'Some-Name','6','some info'",
+                         pyperclip.paste())
+
+    def _test_copies_columns(self):
+        pyperclip.copy("old clipboard contents")
+
+        self.page.driver.switch_to.default_content()
+        self.page.driver.switch_to_frame(self.page.driver.find_element_by_tag_name("iframe"))
+        self.page.find_by_xpath("//*[@data-test='output-column-header' and contains(., 'some_column')]").click()
+        self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
+
+        self.assertTrue("'Some-Name'" in pyperclip.paste())
+        self.assertTrue("'Some-Other-Name'" in pyperclip.paste())
+        self.assertTrue("'Yet-Another-Name'" in pyperclip.paste())
+
+    def _test_history_tab(self):
+        self.__clear_query_tool()
+
+        editor_input = self.page.find_by_id("output-panel")
+        self.page.click_element(editor_input)
+        self._execute_query("SELECT * FROM shoes")
+
+        self.page.click_tab("History")
+        history_element = self.page.find_by_id("history_grid")
+        self.assertIn("SELECT * FROM test_table", history_element.text)
+        self.assertIn("SELECT * FROM shoes", history_element.text)
+
+    def __clear_query_tool(self):
+        self.page.click_element(self.page.find_by_xpath("//*[@id='btn-edit']"))
+        self.page.click_modal('Yes')
+
+    def _navigate_to_query_tool(self):
+        self.page.toggle_open_tree_item(self.server['name'])
+        self.page.toggle_open_tree_item('Databases')
+        self.page.toggle_open_tree_item('acceptance_test_db')
+        time.sleep(5)
+        self.page.find_by_partial_link_text("Tools").click()
+        self.page.find_by_partial_link_text("Query Tool").click()
+        self.page.click_tab('Query-1')
+        time.sleep(5)
+
+    def _execute_query(self, query):
+        ActionChains(self.page.driver).send_keys(query).perform()
+        self.page.driver.switch_to.default_content()
+        self.page.driver.switch_to_frame(self.page.driver.find_element_by_tag_name("iframe"))
+        self.page.find_by_id("btn-flash").click()
+
+    def after(self):
+        self.page.close_query_tool()
+        self.page.remove_server(self.server)
+
+        connection = test_utils.get_db_connection(self.server['db'],
+                                                  self.server['username'],
+                                                  self.server['db_password'],
+                                                  self.server['host'],
+                                                  self.server['port'])
+        test_utils.drop_database(connection, "acceptance_test_db")
diff --git a/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py b/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py
index 959b2c19..094dfed6 100644
--- a/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py
+++ b/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py
@@ -83,7 +83,7 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest):
 
         # If debugger plugin is not found
         if is_error and is_error == "Debugger Error":
-            self.page.click_modal_ok()
+            self.page.click_modal('OK')
             self.skipTest("Please make sure that debugger plugin is properly configured")
         else:
             time.sleep(2)
diff --git a/web/pgadmin/static/js/history/history_collection.js b/web/pgadmin/static/js/history/history_collection.js
new file mode 100644
index 00000000..f7b6acd7
--- /dev/null
+++ b/web/pgadmin/static/js/history/history_collection.js
@@ -0,0 +1,34 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+export default class HistoryCollection {
+
+  constructor(history_model) {
+    this.historyList = history_model;
+    this.onChange(() => {});
+  }
+
+  length() {
+    return this.historyList.length;
+  }
+
+  add(object) {
+    this.historyList.push(object);
+    this.onChangeHandler(this.historyList);
+  }
+
+  reset() {
+    this.historyList = [];
+    this.onChangeHandler(this.historyList);
+  }
+
+  onChange(onChangeHandler) {
+    this.onChangeHandler = onChangeHandler;
+  }
+}
\ No newline at end of file
diff --git a/web/pgadmin/static/js/history/index.js b/web/pgadmin/static/js/history/index.js
new file mode 100644
index 00000000..834878a8
--- /dev/null
+++ b/web/pgadmin/static/js/history/index.js
@@ -0,0 +1,14 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import historyCollection from './history_collection';
+
+export {
+  historyCollection,
+};
diff --git a/web/pgadmin/static/jsx/components.jsx b/web/pgadmin/static/jsx/components.jsx
index 5bcb5208..6ff34e4c 100644
--- a/web/pgadmin/static/jsx/components.jsx
+++ b/web/pgadmin/static/jsx/components.jsx
@@ -1,8 +1,10 @@
 
 import React from 'react';
 import {render} from 'react-dom';
+import QueryHistory from './history/query_history';
 
 export {
   render,
   React,
+  QueryHistory,
 };
\ No newline at end of file
diff --git a/web/pgadmin/static/jsx/history/query_history.jsx b/web/pgadmin/static/jsx/history/query_history.jsx
new file mode 100644
index 00000000..d36f5ce9
--- /dev/null
+++ b/web/pgadmin/static/jsx/history/query_history.jsx
@@ -0,0 +1,49 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import React from 'react';
+import QueryHistoryEntry from './query_history_entry';
+
+const liStyle = {
+  borderBottom: '1px solid #cccccc',
+};
+
+export default class QueryHistory extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      history: [],
+    };
+  }
+
+  componentWillMount() {
+    this.setState({history: this.props.historyCollection.historyList});
+    this.props.historyCollection.onChange((historyList) => this.setState({history: historyList}));
+  }
+
+  render() {
+    return <ul>
+      {_.chain(this.state.history)
+        .sortBy(historyEntry => historyEntry.start_time)
+        .reverse()
+        .map((entry, index) =>
+        <li key={index} style={liStyle}>
+          <QueryHistoryEntry historyEntry={entry}/>
+        </li>)
+        .value()
+      }
+    </ul>;
+  }
+}
+
+QueryHistory.propTypes = {
+  historyCollection: React.PropTypes.object.isRequired,
+};
\ No newline at end of file
diff --git a/web/pgadmin/static/jsx/history/query_history_entry.jsx b/web/pgadmin/static/jsx/history/query_history_entry.jsx
new file mode 100644
index 00000000..d66cb3a7
--- /dev/null
+++ b/web/pgadmin/static/jsx/history/query_history_entry.jsx
@@ -0,0 +1,93 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import React from 'react';
+import update from 'immutability-helper';
+import moment from 'moment';
+
+const outerDivStyle = {
+  paddingLeft: '10px',
+  fontFamily: 'monospace',
+  paddingRight: '20px',
+  fontSize: '14px',
+  backgroundColor: '#FFF',
+};
+const sqlStyle = {
+  textOverflow: 'ellipsis',
+  overflow: 'hidden',
+  whiteSpace: 'nowrap',
+  userSelect: 'auto',
+};
+const secondLineStyle = {
+  display: 'flex',
+  flexDirection: 'row',
+  justifyContent: 'space-between',
+  fontSize: '13px',
+  color: '#888888',
+};
+const timestampStyle = {
+  alignSelf: 'flex-start',
+};
+const rowsAffectedStyle = {
+  alignSelf: 'flex-end',
+};
+const errorMessageStyle = {
+  textOverflow: 'ellipsis',
+  overflow: 'hidden',
+  whiteSpace: 'nowrap',
+  userSelect: 'auto',
+  fontSize: '13px',
+  color: '#888888',
+};
+
+export default class QueryHistoryEntry extends React.Component {
+  formatDate(date) {
+    return (moment(date).format('MMM D YYYY [–] HH:mm:ss'));
+  }
+
+  render() {
+    return (
+      <div style={this.queryEntryBackgroundColor()}>
+        <div style={sqlStyle}>
+          {this.props.historyEntry.query}
+        </div>
+        <div style={secondLineStyle}>
+          <div style={timestampStyle}>
+            {this.formatDate(this.props.historyEntry.start_time)} /
+            total time: {this.props.historyEntry.total_time}
+          </div>
+          <div style={rowsAffectedStyle}>
+            {this.props.historyEntry.row_affected} rows affected
+          </div>
+        </div>
+        <div style={errorMessageStyle}>
+          {this.props.historyEntry.message}
+        </div>
+      </div>
+    );
+  }
+
+  queryEntryBackgroundColor() {
+    if (!this.props.historyEntry.status) {
+      return update(outerDivStyle, {$merge: {backgroundColor: '#F7D0D5'}});
+    }
+    return outerDivStyle;
+  }
+}
+
+QueryHistoryEntry.propTypes = {
+  historyEntry: React.PropTypes.shape({
+    query: React.PropTypes.string,
+    start_time: React.PropTypes.instanceOf(Date),
+    status: React.PropTypes.bool,
+    total_time: React.PropTypes.string,
+    row_affected: React.PropTypes.int,
+    message: React.PropTypes.string,
+  }),
+};
\ No newline at end of file
diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js b/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js
index da24f771..c6e02e6e 100644
--- a/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js
@@ -9,7 +9,10 @@ define([
     'sources/slickgrid/event_handlers/handle_query_output_keyboard_event',
     'sources/selection/xcell_selection_model',
     'sources/selection/set_staged_rows',
-    'sources/gettext', 'sources/sqleditor_utils',
+    'sources/gettext',
+    'sources/sqleditor_utils',
+    'sources/generated/history',
+    'sources/generated/reactComponents',
 
     'slickgrid', 'bootstrap', 'pgadmin.browser', 'wcdocker',
     'codemirror/mode/sql/sql', 'codemirror/addon/selection/mark-selection',
@@ -32,9 +35,9 @@ define([
     'slickgrid/plugins/slick.rowselectionmodel',
     'slickgrid/slick.grid'
 ], function(
-    $, _, S, alertify, pgAdmin, Backbone, Backgrid, CodeMirror, pgExplain, GridSelector,
-    ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
-    XCellSelectionModel, setStagedRows, gettext, SqlEditorUtils
+  $, _, S, alertify, pgAdmin, Backbone, Backgrid, CodeMirror,
+  pgExplain, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
+    XCellSelectionModel, setStagedRows, gettext, SqlEditorUtils, HistoryBundle, reactComponents
 ) {
     /* Return back, this has been called more than once */
     if (pgAdmin.SqlEditor)
@@ -864,147 +867,14 @@ define([
 
         // Remove any existing grid first
         if (self.history_grid) {
-            self.history_grid.remove();
+          self.history_grid.remove();
         }
 
-        var history_model = Backbone.Model.extend({
-          defaults: {
-            status: undefined,
-            start_time: undefined,
-            query: undefined,
-            row_affected: 0,
-            row_retrieved: 0,
-            total_time: undefined,
-            message: ''
-          }
-        });
-
-        var history_collection = self.history_collection = new (Backbone.Collection.extend({
-            model: history_model,
-            // comparator to sort the history in reverse order of the start_time
-            comparator: function(a, b) {
-              return -a.get('start_time').localeCompare(b.get('start_time'));
-            }
-        }));
-        var columns = [{
-            name: "status",
-            label: "",
-            cell: Backgrid.Cell.extend({
-              class: 'sql-status-cell',
-              render: function() {
-                this.$el.empty();
-                var $btn = $('<button></button>', {
-                  class: 'btn btn-circle'
-                }).appendTo(this.$el);
-                var $circleDiv = $('<i></i>', {class: 'fa'}).appendTo($btn);
-                if (this.model.get('status')) {
-                  $btn.addClass('btn-success');
-                  $circleDiv.addClass('fa-check');
-                } else {
-                  $btn.addClass('btn-danger');
-                  $circleDiv.addClass('fa-times');
-                }
-
-                return this;
-              },
-              editable: false
-            }),
-            editable: false
-          }, {
-            name: "start_time",
-            label: "Date",
-            cell: "string",
-            editable: false,
-            resizeable: true
-          }, {
-            name: "query",
-            label: "Query",
-            cell: "string",
-            editable: false,
-            resizeable: true
-          }, {
-            name: "row_affected",
-            label: "Rows affected",
-            cell: "integer",
-            editable: false,
-            resizeable: true
-          }, {
-            name: "total_time",
-            label: "Total Time",
-            cell: "string",
-            editable: false,
-            resizeable: true
-          }, {
-            name: "message",
-            label: "Message",
-            cell: "string",
-            editable: false,
-            resizeable: true
-        }];
-
-
-        // Create Collection of Backgrid columns
-        var columnsColl = new Backgrid.Columns(columns);
-        var $history_grid = self.$el.find('#history_grid');
-
-        var grid = self.history_grid = new Backgrid.Grid({
-            columns: columnsColl,
-            collection: history_collection,
-            className: "backgrid table-bordered presentation table backgrid-striped"
-        });
-
-        // Render the grid
-        $history_grid.append(grid.render().$el);
-
-        var sizeAbleCol = new Backgrid.Extension.SizeAbleColumns({
-          collection: history_collection,
-          columns: columnsColl,
-          grid: self.history_grid
-        });
-
-        $history_grid.find('thead').before(sizeAbleCol.render().el);
+        self.history_collection = new HistoryBundle.historyCollection([]);
 
-        // Add resize handlers
-        var sizeHandler = new Backgrid.Extension.SizeAbleColumnsHandlers({
-          sizeAbleColumns: sizeAbleCol,
-          grid: self.history_grid,
-          saveColumnWidth: true
-        });
-
-        // sizeHandler should render only when table grid loaded completely.
-        setTimeout(function() {
-          $history_grid.find('thead').before(sizeHandler.render().el);
-        }, 1000);
-
-        // re render sizeHandler whenever history panel tab becomes visible
-        self.history_panel.on(wcDocker.EVENT.VISIBILITY_CHANGED, function(ev) {
-          $history_grid.find('thead').before(sizeHandler.render().el);
-        });
-
-        // Initialized table width 0 still not calculated
-        var table_width = 0;
-        // Listen to resize events
-        columnsColl.on('resize',
-          function(columnModel, newWidth, oldWidth, offset) {
-            var $grid_el = $history_grid.find('table'),
-                tbl_orig_width = $grid_el.width(),
-                offset = oldWidth - newWidth,
-                tbl_new_width = tbl_orig_width - offset;
-
-            if (table_width == 0) {
-              table_width = tbl_orig_width
-            }
-            // Table new width cannot be less than original width
-            if (tbl_new_width >= table_width) {
-              $($grid_el).css('width', tbl_new_width + 'px');
-            }
-            else {
-              // reset if calculated tbl_new_width is less than original
-              // table width
-              tbl_new_width = table_width;
-              $($grid_el).css('width', tbl_new_width + 'px');
-            }
-        });
+        let queryHistoryElement = reactComponents.React.createElement(
+          reactComponents.QueryHistory, {historyCollection: self.history_collection});
+        reactComponents.render(queryHistoryElement, $('#history_grid')[0]);
       },
 
       // Callback function for Add New Row button click.
@@ -1307,7 +1177,7 @@ define([
         this._stopEventPropogation(ev);
         this._closeDropDown(ev);
         // ask for confirmation only if anything to clear
-        if(!self.history_collection.length) { return; }
+        if(!self.history_collection.length()) { return; }
 
         alertify.confirm(gettext("Clear history"),
           gettext("Are you sure you wish to clear the history?"),
@@ -2130,11 +2000,13 @@ define([
             $("#btn-flash").prop('disabled', false);
             self.trigger('pgadmin-sqleditor:loading-icon:hide');
             self.gridView.history_collection.add({
-              'status' : status, 'start_time': self.query_start_time.toString(),
-              'query': self.query, 'row_affected': self.rows_affected,
-              'total_time': self.total_time, 'message':msg
+              'status' : status,
+              'start_time': self.query_start_time,
+              'query': self.query,
+              'row_affected': self.rows_affected,
+              'total_time': self.total_time,
+              'message':msg,
             });
-            self.gridView.history_collection.sort();
           }
         },
 
@@ -2407,10 +2279,13 @@ define([
 
                 // Update the sql results in history tab
                 _.each(res.data.query_result, function(r) {
-                  self.gridView.history_collection.add(
-                    {'status' : r.status, 'start_time': self.query_start_time.toString(),
-                    'query': r.sql, 'row_affected': r.rows_affected,
-                    'total_time': self.total_time, 'message': r.result
+                  self.gridView.history_collection.add({
+                    'status': r.status,
+                    'start_time': self.query_start_time,
+                    'query': r.sql,
+                    'row_affected': r.rows_affected,
+                    'total_time': self.total_time,
+                    'message': r.result,
                   });
                 });
                 self.trigger('pgadmin-sqleditor:loading-icon:hide');
@@ -3356,7 +3231,7 @@ define([
 
                 var msg = e.responseText;
                 if (e.responseJSON != undefined &&
-                  e.responseJSON.errormsg != undefined)
+                    e.responseJSON.errormsg != undefined)
                   msg = e.responseJSON.errormsg;
 
                 alertify.alert('Get Object Name Error', msg);
diff --git a/web/regression/feature_utils/app_starter.py b/web/regression/feature_utils/app_starter.py
index 96cc516b..f40d6921 100644
--- a/web/regression/feature_utils/app_starter.py
+++ b/web/regression/feature_utils/app_starter.py
@@ -11,6 +11,7 @@ import subprocess
 import signal
 import random
 
+import time
 
 class AppStarter:
     """ Helper for starting the full pgadmin4 app and loading the page via
@@ -40,6 +41,7 @@ class AppStarter:
         )
 
         self.driver.set_window_size(1024, 1024)
+        time.sleep(10)
         self.driver.get(
             "http://"; + self.app_config.DEFAULT_SERVER + ":" +
             random_server_port)
diff --git a/web/regression/feature_utils/pgadmin_page.py b/web/regression/feature_utils/pgadmin_page.py
index 46d50156..a1cb7147 100644
--- a/web/regression/feature_utils/pgadmin_page.py
+++ b/web/regression/feature_utils/pgadmin_page.py
@@ -33,15 +33,16 @@ class PgadminPage:
     def reset_layout(self):
         self.click_element(self.find_by_partial_link_text("File"))
         self.find_by_partial_link_text("Reset Layout").click()
-        self.click_modal_ok()
+        self.click_modal('OK')
         self.wait_for_reloading_indicator_to_disappear()
 
-    def click_modal_ok(self):
+    def click_modal(self, button_text):
         time.sleep(0.5)
         # Find active alertify dialog in case of multiple alertify dialog & click on that dialog
-        self.click_element(
-            self.find_by_xpath("//div[contains(@class, 'alertify') and not(contains(@class, 'ajs-hidden'))]//button[.='OK']")
-        )
+        modal_button = self.find_by_xpath(
+            "//div[contains(@class, 'alertify') and not(contains(@class, 'ajs-hidden'))]//button[.='%s']"
+            % button_text)
+        self.click_element(modal_button)
 
     def add_server(self, server_config):
         self.find_by_xpath("//*[@class='aciTreeText' and contains(.,'Servers')]").click()
@@ -78,10 +79,13 @@ class PgadminPage:
 
     def remove_server(self, server_config):
         self.driver.switch_to.default_content()
-        self.find_by_xpath("//*[@id='tree']//*[.='" + server_config['name'] + "' and @class='aciTreeItem']").click()
-        self.find_by_partial_link_text("Object").click()
-        self.find_by_partial_link_text("Delete/Drop").click()
-        self.click_modal_ok()
+        server_to_remove = self.find_by_xpath("//*[@id='tree']//*[.='" + server_config['name'] + "' and @class='aciTreeItem']")
+        self.click_element(server_to_remove)
+        object_menu_item = self.find_by_partial_link_text("Object")
+        self.click_element(object_menu_item)
+        delete_menu_item = self.find_by_partial_link_text("Delete/Drop")
+        self.click_element(delete_menu_item)
+        self.click_modal('OK')
 
     def select_tree_item(self, tree_item_text):
         self.find_by_xpath("//*[@id='tree']//*[.='" + tree_item_text + "' and @class='aciTreeItem']").click()
@@ -130,6 +134,7 @@ class PgadminPage:
         )
 
     def click_element(self, element):
+        # driver must be here to adhere to the method contract in selenium.webdriver.support.wait.WebDriverWait.until()
         def click_succeeded(driver):
             try:
                 element.click()
@@ -175,8 +180,9 @@ class PgadminPage:
         time.sleep(sleep_time)
 
     def click_tab(self, tab_name):
-        self.find_by_xpath("//*[contains(@class,'wcTabTop')]//*[contains(@class,'wcPanelTab') "
-                           "and contains(.,'" + tab_name + "')]").click()
+        tab = self.find_by_xpath("//*[contains(@class,'wcTabTop')]//*[contains(@class,'wcPanelTab') "
+                           "and contains(.,'" + tab_name + "')]")
+        self.click_element(tab)
 
     def wait_for_input_field_content(self, field_name, content):
         def input_field_has_content(driver):
diff --git a/web/regression/javascript/check_node_visiblity_spec.js b/web/regression/javascript/check_node_visibility_spec.js
similarity index 100%
rename from web/regression/javascript/check_node_visiblity_spec.js
rename to web/regression/javascript/check_node_visibility_spec.js
diff --git a/web/regression/javascript/history/history_collection_spec.js b/web/regression/javascript/history/history_collection_spec.js
new file mode 100644
index 00000000..e1baa554
--- /dev/null
+++ b/web/regression/javascript/history/history_collection_spec.js
@@ -0,0 +1,83 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import HistoryCollection from '../../../pgadmin/static/js/history/history_collection';
+
+describe('historyCollection', function () {
+  let historyCollection, historyModel, onChangeSpy;
+  beforeEach(() => {
+    historyModel = [{some: 'thing', someOther: ['array element']}];
+    historyCollection = new HistoryCollection(historyModel);
+    onChangeSpy = jasmine.createSpy('onChangeHandler');
+
+    historyCollection.onChange(onChangeSpy);
+  });
+
+  describe('length', function () {
+    it('returns 0 when underlying history model has no elements', function () {
+      historyCollection = new HistoryCollection([]);
+
+      expect(historyCollection.length()).toBe(0);
+    });
+
+    it('returns the length of the underlying history model', function () {
+      expect(historyCollection.length()).toBe(1);
+    });
+  });
+
+  describe('add', function () {
+    let expectedHistory;
+    beforeEach(() => {
+      historyCollection.add({some: 'new thing', someOther: ['value1', 'value2']});
+
+      expectedHistory = [
+        {some: 'thing', someOther: ['array element']},
+        {some: 'new thing', someOther: ['value1', 'value2']},
+      ];
+    });
+
+    it('adds a passed entry', function () {
+      expect(historyCollection.historyList).toEqual(expectedHistory);
+    });
+
+    it('calls the onChange function', function () {
+      expect(onChangeSpy).toHaveBeenCalledWith(expectedHistory);
+    });
+  });
+
+  describe('reset', function () {
+    beforeEach(() => {
+      historyCollection.reset();
+    });
+
+    it('drops the history', function () {
+      expect(historyCollection.historyList).toEqual([]);
+      expect(historyCollection.length()).toBe(0);
+    });
+
+    it('calls the onChange function', function () {
+      expect(onChangeSpy).toHaveBeenCalledWith([]);
+    });
+  });
+
+  describe('sort', function () {
+    it('doesn\'t sort');
+  });
+
+  describe('when instantiated', function () {
+    describe('from a history model', function () {
+      it('has the historyModel', () => {
+        let content = historyCollection.historyList;
+
+        expect(content).toEqual(historyModel);
+      });
+
+    });
+  });
+});
\ No newline at end of file
diff --git a/web/regression/javascript/history/query_history_entry_spec.jsx b/web/regression/javascript/history/query_history_entry_spec.jsx
new file mode 100644
index 00000000..c86a1cfc
--- /dev/null
+++ b/web/regression/javascript/history/query_history_entry_spec.jsx
@@ -0,0 +1,50 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import React from 'react';
+
+import QueryHistoryEntry from '../../../pgadmin/static/jsx/history/query_history_entry';
+
+import {mount} from 'enzyme';
+import jasmineEnzyme from 'jasmine-enzyme';
+
+describe('QueryHistoryEntry', () => {
+  let historyWrapper;
+  beforeEach(() => {
+    jasmineEnzyme();
+  });
+
+  describe('for a failed query', () => {
+    beforeEach(() => {
+      const historyEntry = {
+        query: 'second sql statement',
+        start_time: new Date(2016, 11, 11, 1, 33, 5, 99),
+        status: false,
+      };
+      historyWrapper = mount(<QueryHistoryEntry historyEntry={historyEntry}/>);
+    });
+    it('displays a pink background color', () => {
+      expect(historyWrapper.find('div').first()).toHaveStyle('backgroundColor', '#F7D0D5');
+    });
+  });
+
+  describe('for a successful query', () => {
+    beforeEach(() => {
+      const historyEntry = {
+        query: 'second sql statement',
+        start_time: new Date(2016, 11, 11, 1, 33, 5, 99),
+        status: true,
+      };
+      historyWrapper = mount(<QueryHistoryEntry historyEntry={historyEntry}/>);
+    });
+    it('does not display a pink background color', () => {
+      expect(historyWrapper.find('div').first()).toHaveStyle('backgroundColor', '#FFF');
+    });
+  });
+});
diff --git a/web/regression/javascript/history/query_history_spec.jsx b/web/regression/javascript/history/query_history_spec.jsx
new file mode 100644
index 00000000..e36988a8
--- /dev/null
+++ b/web/regression/javascript/history/query_history_spec.jsx
@@ -0,0 +1,103 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import React from 'react';
+import QueryHistory from '../../../pgadmin/static/jsx/history/query_history';
+import QueryHistoryEntry from '../../../pgadmin/static/jsx/history/query_history_entry';
+import HistoryCollection from '../../../pgadmin/static/js/history/history_collection';
+import jasmineEnzyme from 'jasmine-enzyme';
+
+import {mount, shallow} from 'enzyme';
+
+describe('QueryHistory', () => {
+  let historyWrapper;
+  beforeEach(() => {
+    jasmineEnzyme();
+    const historyCollection = new HistoryCollection([]);
+    historyWrapper = shallow(<QueryHistory historyCollection={historyCollection}/>);
+  });
+
+  describe('on construction', () => {
+    it('has no entries', (done) => {
+      let foundChildren = historyWrapper.find(QueryHistoryEntry);
+      expect(foundChildren.length).toBe(0);
+      done();
+    });
+  });
+
+  describe('when it has history', () => {
+    describe('when two SQL queries were executed', () => {
+      let foundChildren;
+
+      beforeEach(() => {
+        const historyObjects = [
+          {
+            query: 'second sql statement',
+            start_time: new Date(2016, 11, 11, 1, 33, 5, 99),
+            status: false,
+            row_affected: 1,
+            total_time: '234 msec',
+            message: 'some other message',
+          },
+          {
+            query: 'first sql statement',
+            start_time: new Date(2017, 5, 3, 14, 3, 15, 150),
+            status: true,
+            row_affected: 2,
+            total_time: '14 msec',
+            message: 'a very important message',
+          },
+        ];
+        const historyCollection = new HistoryCollection(historyObjects);
+
+        historyWrapper = mount(<QueryHistory historyCollection={historyCollection}/>);
+
+        foundChildren = historyWrapper.find(QueryHistoryEntry);
+      });
+
+      it('has two query history entries', () => {
+        expect(foundChildren.length).toBe(2);
+      });
+
+      it('displays the SQL of the queries in order', () => {
+        expect(foundChildren.at(0).text()).toContain('first sql statement');
+        expect(foundChildren.at(1).text()).toContain('second sql statement');
+      });
+
+      it('displays the formatted timestamp of the queries in chronological order by most recent first', () => {
+        expect(foundChildren.at(0).text()).toContain('Jun 3 2017 – 14:03:15');
+        expect(foundChildren.at(1).text()).toContain('Dec 11 2016 – 01:33:05');
+      });
+
+      it('displays the number of rows affected', () => {
+        expect(foundChildren.at(1).text()).toContain('1 rows affected');
+        expect(foundChildren.at(0).text()).toContain('2 rows affected');
+      });
+
+      it('displays the total time', () => {
+        expect(foundChildren.at(0).text()).toContain('total time: 14 msec');
+        expect(foundChildren.at(1).text()).toContain('total time: 234 msec');
+      });
+
+      it('displays the truncated message', () => {
+        expect(foundChildren.at(0).text()).toContain('a very important message');
+        expect(foundChildren.at(1).text()).toContain('some other message');
+      });
+
+      describe('when there are one failing and one successful query each', () => {
+        it('adds a white background color for the successful query', () => {
+          expect(foundChildren.at(0).find('div').first()).toHaveStyle('backgroundColor', '#FFF');
+        });
+        it('adds a red background color for the failed query', () => {
+          expect(foundChildren.at(1).find('div').first()).toHaveStyle('backgroundColor', '#F7D0D5');
+        });
+      });
+    });
+  });
+});
\ No newline at end of file
diff --git a/web/regression/python_test_utils/test_utils.py b/web/regression/python_test_utils/test_utils.py
index cf559c44..f3e7ed01 100644
--- a/web/regression/python_test_utils/test_utils.py
+++ b/web/regression/python_test_utils/test_utils.py
@@ -227,6 +227,7 @@ def create_constraint(
     except Exception:
         traceback.print_exc(file=sys.stderr)
 
+
 def create_debug_function(server, db_name, function_name="test_func"):
     try:
         connection = get_db_connection(db_name,
@@ -305,6 +306,7 @@ def drop_database(connection, database_name):
             connection.commit()
             connection.close()
 
+
 def drop_tablespace(connection):
     """This function used to drop the tablespace"""
     pg_cursor = connection.cursor()
diff --git a/web/webpack.config.js b/web/webpack.config.js
index 91586592..fc6d29e0 100644
--- a/web/webpack.config.js
+++ b/web/webpack.config.js
@@ -1,18 +1,21 @@
 /* eslint-env node */
 
 module.exports = {
-  context: __dirname + '/pgadmin/static/jsx',
-  entry: './components.jsx',
+  context: __dirname + '/pgadmin/static',
+  entry: {
+    reactComponents: './jsx/components.jsx',
+    history: './js/history/index.js',
+  },
   output: {
     libraryTarget: 'amd',
     path: __dirname + '/pgadmin/static/js/generated',
-    filename: 'reactComponents.js',
+    filename: '[name].js',
   },
 
   module: {
     rules: [{
       test: /\.jsx?$/,
-      exclude: /node_modules/,
+      exclude: [/node_modules/, /vendor/],
       use: {
         loader: 'babel-loader',
         options: {
diff --git a/web/webpack.test.config.js b/web/webpack.test.config.js
index b156f147..4909f4a7 100644
--- a/web/webpack.test.config.js
+++ b/web/webpack.test.config.js
@@ -22,7 +22,7 @@ module.exports = {
         use: {
           loader: 'babel-loader',
           options: {
-            presets: ['es2015'],
+            presets: ['es2015', 'react'],
           },
         },
       },
@@ -51,6 +51,7 @@ module.exports = {
   },
 
   resolve: {
+    extensions: ['.js', '.jsx'],
     alias: {
       'alertify': sourcesDir + '/vendor/alertifyjs/alertify',
       'jquery': sourcesDir + '/vendor/jquery/jquery-1.11.2',
@@ -66,4 +67,11 @@ module.exports = {
       'pgadmin': sourcesDir + '/js/pgadmin',
     },
   },
+  externals: {
+    'react/addons': true,
+    'react/lib/ReactContext': true,
+    'react/lib/ExecutionEnvironment': true,
+    'react-dom/test-utils': true,
+    'react-test-renderer/shallow': true,
+  },
 };
diff --git a/web/yarn.lock b/web/yarn.lock
index b04caa5c..ce72d41b 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -1371,6 +1371,12 @@ decamelize@^1.0.0, decamelize@^1.1.1:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191...;
 
+deep-equal-ident@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/deep-equal-ident/-/deep-equal-ident-1.1.1.tgz#06f4b89e53710cd6cea4a7781...;
+  dependencies:
+    lodash.isequal "^3.0"
+
 deep-extend@~0.4.0:
   version "0.4.2"
   resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d...;
@@ -1608,6 +1614,12 @@ entities@^1.1.1, entities@~1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0&...;
 
+enzyme-matchers@^3.1.0, enzyme-matchers@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/enzyme-matchers/-/enzyme-matchers-3.2.0.tgz#4718779a3b9eb5e8ebad46804f8...;
+  dependencies:
+    deep-equal-ident "^1.1.1"
+
 enzyme@~2.8.2:
   version "2.8.2"
   resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-2.8.2.tgz#6c8bcb05012abc4aa4bc3213fb23780b9b5b1714";
@@ -2299,6 +2311,12 @@ ignore@^3.2.0:
   version "3.3.3"
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d";
 
+immutability-helper@^2.2.0:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/immutability-helper/-/immutability-helper-2.2.2.tgz#e7e9da728b3de2fad34...;
+  dependencies:
+    invariant "^2.2.0"
+
 "imports-loader@git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d";:
   version "0.7.1"
   resolved "git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d";
@@ -2568,6 +2586,12 @@ jasmine-core@~2.5.2:
   version "2.5.2"
   resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.5.2.tgz#6f61bd79061e27f43e6f9355e44b3c6ca...;
 
+jasmine-enzyme@^3.1.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/jasmine-enzyme/-/jasmine-enzyme-3.2.0.tgz#0eeb370d4fa965db03e04347ca9c4...;
+  dependencies:
+    enzyme-matchers "^3.2.0"
+
 jodid25519@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782...;
@@ -2828,6 +2852,22 @@ loader-utils@^1.0.2:
     emojis-list "^2.0.0"
     json5 "^0.5.0"
 
+lodash._baseisequal@^3.0.0:
+  version "3.0.7"
+  resolved "https://registry.yarnpkg.com/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz#d8025f76339d2934276...;
+  dependencies:
+    lodash.isarray "^3.0.0"
+    lodash.istypedarray "^3.0.0"
+    lodash.keys "^3.0.0"
+
+lodash._bindcallback@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a...;
+
+lodash._getnative@^3.0.0:
+  version "3.9.1"
+  resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687...;
+
 lodash.assignin@^4.0.9:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e...;
@@ -2852,6 +2892,33 @@ lodash.foreach@^4.3.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec351...;
 
+lodash.isarguments@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff006...;
+
+lodash.isarray@^3.0.0:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bc...;
+
+lodash.isequal@^3.0:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-3.0.4.tgz#1c35eb3b6ef0cd1ff51743e3ea3cf...;
+  dependencies:
+    lodash._baseisequal "^3.0.0"
+    lodash._bindcallback "^3.0.0"
+
+lodash.istypedarray@^3.0.0:
+  version "3.0.6"
+  resolved "https://registry.yarnpkg.com/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz#c9a477498607501d8e8...;
+
+lodash.keys@^3.0.0:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c65...;
+  dependencies:
+    lodash._getnative "^3.0.0"
+    lodash.isarguments "^3.0.0"
+    lodash.isarray "^3.0.0"
+
 lodash.map@^4.4.0:
   version "4.6.0"
   resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f...;
@@ -3013,6 +3080,10 @@ module-deps@^4.0.8:
     through2 "^2.0.0"
     xtend "^4.0.0"
 
+moment@^2.18.1:
+  version "2.18.1"
+  resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f";
+
 [email protected]:
   version "0.7.1"
   resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098";
@@ -3499,10 +3570,10 @@ randomatic@^1.1.3:
     kind-of "^3.0.2"
 
 randombytes@^2.0.0, randombytes@^2.0.1:
-  version "2.0.4"
-  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.4.tgz#9551df208422c8f80eb58e2326dd0b840ff...;
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.5.tgz#dc009a246b8d09a177b4b7a0ae77bc570f4...;
   dependencies:
-    safe-buffer "^5.0.1"
+    safe-buffer "^5.1.0"
 
 range-parser@^1.0.3, range-parser@^1.2.0:
   version "1.2.0"
@@ -3818,7 +3889,7 @@ rx-lite@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102...;
 
-safe-buffer@^5.0.1:
+safe-buffer@^5.0.1, safe-buffer@^5.1.0:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a4...;
 


-- 
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers


Attachments:

  [text/plain] 01-start-tracking-yarn-lock.diff (317B, 3-01-start-tracking-yarn-lock.diff)
  download | inline diff:
diff --git a/.gitignore b/.gitignore
index f6d14903..3ccd1782 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,4 +37,3 @@ runtime/ui_BrowserWindow.h
 web/config_local.py
 web/regression/test_config.json
 node_modules/
-yarn.lock
diff --git a/web/yarn.lock b/web/yarn.lock
new file mode 100644
index 00000000..e69de29b


  [text/plain] 02-bring-react-into-the-codebase.diff (319.1K, 4-02-bring-react-into-the-codebase.diff)
  download | inline diff:
diff --git a/.gitignore b/.gitignore
index 3ccd1782..67cec303 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,4 +36,5 @@ runtime/release/
 runtime/ui_BrowserWindow.h
 web/config_local.py
 web/regression/test_config.json
-node_modules/
+/web/pgadmin/static/**/generated/
+/web/node_modules/
diff --git a/Make.bat b/Make.bat
index 8082c56d..e73eb2f8 100644
--- a/Make.bat
+++ b/Make.bat
@@ -238,6 +238,12 @@ REM Main function Ends
     CD "%WD%\web"
 
     IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
+
+    ECHO Install Javascript dependencies
+    yarn install
+    ECHO Bundle all Javascript
+    yarn run webpacker
+
     XCOPY /S /I /E /H /Y "%WD%\web" "%PGBUILDPATH%\web" > nul
     IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
 
diff --git a/README b/README
index ff567db2..efa0bcff 100644
--- a/README
+++ b/README
@@ -196,6 +196,24 @@ http://www.tylerbutler.com/2012/05/how-to-install-python-pip-and-virtualenv-on-w
 Once a virtual environment has been created and enabled, setup can continue
 from step 4 above.
 
+
+Bundling Javascript
+-------------------
+
+The current build process for the JS frontend is: source_file.js(x) -> babel -> webpack -> bundled_output.js
+
+To setup your environment for building:
+
+    get Yarn: https://yarnpkg.com/en/docs/install
+
+Build:
+
+    (pgadmin4/web)$ yarn install
+    (pgadmin4/web)$ yarn run bundle
+
+This will also run a linter against the js source files.
+
+
 Configuring the Runtime
 -----------------------
 
diff --git a/pkg/mac/build.sh b/pkg/mac/build.sh
index a138a8a5..cd2908ef 100755
--- a/pkg/mac/build.sh
+++ b/pkg/mac/build.sh
@@ -178,6 +178,11 @@ _complete_bundle() {
     # run complete-bundle to copy the dependent libraries and frameworks and fix the rpaths
     ./complete-bundle.sh "$BUILDROOT/$APP_BUNDLE_NAME" || { echo complete-bundle.sh failed; exit 1; }
 
+    pushd $SOURCEDIR/web
+        yarn install
+        yarn run webpacker
+    popd
+
     # copy the web directory to the bundle as it is required by runtime
     cp -r $SOURCEDIR/web "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/" || exit 1
     cd "$BUILDROOT/$APP_BUNDLE_NAME/Contents/Resources/web"
diff --git a/pkg/pip/build.sh b/pkg/pip/build.sh
index 3ceaec4f..e9d88350 100755
--- a/pkg/pip/build.sh
+++ b/pkg/pip/build.sh
@@ -57,6 +57,15 @@ do
     tar cf - $FILE | (cd ../pip-build/pgadmin4; tar xf -)
 done
 
+yarn install
+yarn run webpacker
+
+for FILE in `ls -d pgAdmin/static/js/generated/*`
+do
+    echo Adding $FILE
+    tar cf - $FILE | (cd ../pip-build/pgadmin4; tar xf -)
+done
+
 cd ../docs
 for FILE in `git ls-files`
 do
diff --git a/pkg/src/build.sh b/pkg/src/build.sh
index a2c6f90e..bf6bc284 100755
--- a/pkg/src/build.sh
+++ b/pkg/src/build.sh
@@ -69,6 +69,17 @@ do
     tar cf - $FILE | (cd src-build/$TARBALL_NAME; tar xf -)
 done
 
+pushd web
+    yarn install
+    yarn run webpacker
+
+    for FILE in `ls -d pgAdmin/static/js/generated/*`
+    do
+        echo Adding $FILE
+        tar cf - $FILE | (cd ../src-build/$TARBALL_NAME/web; tar xf -)
+    done
+popd
+
 # Create the tarball
 echo Creating tarball...
 cd src-build
diff --git a/web/.eslintrc.js b/web/.eslintrc.js
new file mode 100644
index 00000000..c60569db
--- /dev/null
+++ b/web/.eslintrc.js
@@ -0,0 +1,45 @@
+module.exports = {
+  'env': {
+    'browser': true,
+    'es6': true,
+    'amd': true,
+    'jasmine': true,
+  },
+  'extends': 'eslint:recommended',
+  'parserOptions': {
+    'ecmaFeatures': {
+      'experimentalObjectRestSpread': true,
+      'jsx': true
+    },
+    'sourceType': 'module'
+  },
+  'plugins': [
+    'react'
+  ],
+  'globals': {
+    '_': true,
+    'module': true,
+  },
+  'rules': {
+    'indent': [
+      'error',
+      2
+    ],
+    'linebreak-style': [
+      'error',
+      'unix'
+    ],
+    'quotes': [
+      'error',
+      'single'
+    ],
+    'semi': [
+      'error',
+      'always'
+    ],
+    'comma-dangle': [
+      'error',
+      'always-multiline'
+    ]
+  }
+};
\ No newline at end of file
diff --git a/web/karma.conf.js b/web/karma.conf.js
index e7e26081..3c8e933c 100644
--- a/web/karma.conf.js
+++ b/web/karma.conf.js
@@ -1,62 +1,52 @@
-// Karma configuration
-// Generated on Wed Mar 01 2017 14:19:28 GMT-0500 (EST)
+const webpackConfig = require('./webpack.test.config.js');
 
-module.exports = function(config) {
+module.exports = function (config) {
   config.set({
-
-    // base path that will be used to resolve all patterns (eg. files, exclude)
-    basePath: '',
-
-
-    // frameworks to use
-    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
-    frameworks: ['jasmine', 'requirejs'],
-
-
-    // list of files / patterns to load in the browser
+    frameworks: ['jasmine'],
+    plugins: [
+      'karma-webpack',
+      'karma-phantomjs-launcher',
+      'karma-jasmine',
+      'karma-jasmine-html-reporter',
+    ],
     files: [
-      'regression/javascript/test-main.js',
-      {pattern: 'regression/javascript/**/*.js', included: false},
+      {pattern: 'pgadmin/static/**/*.js', included: false},
       {pattern: 'pgadmin/static/vendor/**/*.js', included: false},
-      {pattern: 'pgadmin/static/js/**/*.js', included: false},
-      {pattern: 'pgadmin/browser/static/js/**/*.js', included: false}
+      {pattern: 'pgadmin/browser/static/js/**/*.js', included: false},
+      'regression/javascript/**/*.jsx',
+      'regression/javascript/**/*.js',
     ],
 
-
     // list of files to exclude
     exclude: [
       'pgadmin/static/vendor/**/*[Tt]est.js',
       'pgadmin/static/vendor/**/*[Ss]pec.js'
     ],
 
-
     // preprocess matching files before serving them to the browser
     // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
     preprocessors: {
+      'regression/javascript/**/*.js': ['webpack'],
+      // 'regression/javascript/**/*.jsx': ['webpack'],
     },
 
+    webpack: webpackConfig,
+    webpackMiddleware: {
+      stats: 'errors-only',
+    },
 
-    // test results reporter to use
-    // possible values: 'dots', 'progress'
-    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
-    reporters: ['progress'],
-
-
-    // web server port
+    reporters: ['progress', 'kjhtml'],
     port: 9876,
 
-
     // enable / disable colors in the output (reporters and logs)
     colors: true,
 
-
     // level of logging
     // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
-    logLevel: config.LOG_INFO,
-
+    logLevel: config.LOG_WARN,
 
     // enable / disable watching file and executing tests whenever any file changes
-    autoWatch: false,
+    autoWatch: true,
 
 
     // start these browsers
@@ -66,10 +56,10 @@ module.exports = function(config) {
 
     // Continuous Integration mode
     // if true, Karma captures browsers, runs the tests and exits
-    singleRun: true,
+    singleRun: false,
 
     // Concurrency level
     // how many browser should be started simultaneous
     concurrency: Infinity
-  })
-}
+  });
+};
diff --git a/web/package.json b/web/package.json
index 80e4e5ad..40070da0 100644
--- a/web/package.json
+++ b/web/package.json
@@ -1,10 +1,43 @@
 {
   "devDependencies": {
-    "jasmine-core": "^2.5.2",
-    "karma": "^1.5.0",
-    "karma-jasmine": "^1.1.0",
-    "karma-phantomjs-launcher": "^1.0.2",
-    "karma-requirejs": "^1.1.0",
-    "requirejs": "^2.3.3"
+    "babel-core": "~6.24.0",
+    "babel-loader": "~6.4.1",
+    "babel-preset-es2015": "~6.24.0",
+    "babel-preset-react": "~6.23.0",
+    "enzyme": "~2.8.2",
+    "jasmine-core": "~2.5.2",
+    "karma": "~1.5.0",
+    "karma-babel-preprocessor": "^6.0.1",
+    "karma-browserify": "~5.1.1",
+    "karma-jasmine": "~1.1.0",
+    "karma-jasmine-html-reporter": "~0.2.2",
+    "karma-phantomjs-launcher": "~1.0.2",
+    "karma-requirejs": "~1.1.0",
+    "karma-sourcemap-loader": "~0.3.7",
+    "karma-webpack": "~2.0.3",
+    "react-addons-test-utils": "~15.4.2",
+    "webpack": "~2.3.1"
+  },
+  "dependencies": {
+    "axios": "^0.16.1",
+    "babel-plugin-transform-es2015-modules-amd": "^6.24.1",
+    "babel-polyfill": "^6.23.0",
+    "babel-preset-es2015-without-strict": "~0.0.4",
+    "babelify": "~7.3.0",
+    "browserify": "~14.1.0",
+    "exports-loader": "~0.6.4",
+    "imports-loader": "git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d",
+    "react": "~15.4.2",
+    "react-dom": "~15.4.2",
+    "requirejs": "~2.3.3",
+    "underscore": "~1.8.3",
+    "watchify": "~3.9.0"
+  },
+  "scripts": {
+    "bundle": "yarn run webpack -- --optimize-minimize --config webpack.config.js",
+    "test:karma-once": "yarn run karma start -- --single-run",
+    "test:karma": "yarn run karma start",
+    "test:feature": "yarn run bundle && python regression/runtests.py --pkg feature_tests",
+    "test": "yarn run test:karma-once && yarn run bundle && python regression/runtests.py"
   }
 }
diff --git a/web/pgAdmin4.py b/web/pgAdmin4.py
index 24e1564a..81258079 100644
--- a/web/pgAdmin4.py
+++ b/web/pgAdmin4.py
@@ -14,25 +14,40 @@ to start a web server."""
 import os
 import sys
 
-# We need to include the root directory in sys.path to ensure that we can
-# find everything we need when running in the standalone runtime.
+from pgadmin.tools.javascript.javascript_bundler import JavascriptBundler, JsState
+
 root = os.path.dirname(os.path.realpath(__file__))
 if sys.path[0] != root:
     sys.path.insert(0, root)
 
 import config
 from pgadmin import create_app
+from pgadmin.utils import u, fs_encoding, file_quote
 
 # Get the config database schema version. We store this in pgadmin.model
 # as it turns out that putting it in the config files isn't a great idea
 from pgadmin.model import SCHEMA_VERSION
 config.SETTINGS_SCHEMA_VERSION = SCHEMA_VERSION
 
+##########################################################################
+# Sanity checks
+##########################################################################
+
+# Check if the database exists. If it does not, create it.
+if not os.path.isfile(config.SQLITE_PATH):
+    setupfile = os.path.join(
+        os.path.dirname(os.path.realpath(u(__file__, fs_encoding))), u'setup.py'
+    )
+    exec(open(file_quote(setupfile), 'r').read())
 
 ##########################################################################
-# Server starup
+# Server startup
 ##########################################################################
 
+# Build Javascript files
+javascriptBundler = JavascriptBundler()
+javascriptBundler.bundle()
+
 # Create the app!
 app = create_app()
 
@@ -41,6 +56,12 @@ if config.DEBUG:
 else:
     app.debug = False
 
+# respond to JS
+if javascriptBundler.report() == JsState.NONE:
+    app.logger.error("Unable to generate javascript")
+    app.logger.error("To run the app ensure that yarn install command runs successfully")
+    raise Exception("No generated javascript, aborting")
+
 # Start the web server. The port number should have already been set by the
 # runtime if we're running in desktop mode, otherwise we'll just use the
 # Flask default.
diff --git a/web/pgadmin/static/jsx/components.jsx b/web/pgadmin/static/jsx/components.jsx
new file mode 100644
index 00000000..5bcb5208
--- /dev/null
+++ b/web/pgadmin/static/jsx/components.jsx
@@ -0,0 +1,8 @@
+
+import React from 'react';
+import {render} from 'react-dom';
+
+export {
+  render,
+  React,
+};
\ No newline at end of file
diff --git a/web/pgadmin/tools/javascript/__init__.py b/web/pgadmin/tools/javascript/__init__.py
new file mode 100644
index 00000000..cac9a64a
--- /dev/null
+++ b/web/pgadmin/tools/javascript/__init__.py
@@ -0,0 +1,8 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2017, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+###########################################################################
diff --git a/web/pgadmin/tools/javascript/javascript_bundler.py b/web/pgadmin/tools/javascript/javascript_bundler.py
new file mode 100644
index 00000000..4ca2da67
--- /dev/null
+++ b/web/pgadmin/tools/javascript/javascript_bundler.py
@@ -0,0 +1,62 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2017, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import os
+from contextlib import contextmanager
+from subprocess import call
+from pgadmin.utils import u, fs_encoding, file_quote
+
+
+# enum-like for tracking whether we have
+class JsState:
+    NONE = 0
+    OLD = 1
+    NEW = 2
+
+
+class JavascriptBundler:
+    """Builds Javascript bundle files by delegating to webpack"""
+
+    def __init__(self):
+        self.jsState = JsState.NONE
+
+    def bundle(self):
+        try:
+            try_building_js()
+            self.jsState = JsState.NEW
+        except OSError:
+            webdir_path()
+            generatedJavascriptDir = os.path.join(webdir_path(), 'pgadmin', 'static', 'js', 'generated')
+            if os.path.exists(generatedJavascriptDir) and os.listdir(generatedJavascriptDir):
+                self.jsState = JsState.OLD
+            else:
+                self.jsState = JsState.NONE
+
+    def report(self):
+        return self.jsState
+
+
+@contextmanager
+def pushd(new_dir):
+    previous_dir = os.getcwd()
+    os.chdir(new_dir)
+    yield
+    os.chdir(previous_dir)
+
+
+def webdir_path():
+    dirname = os.path.dirname
+    thisPath = os.path.realpath(u(__file__, fs_encoding))
+    return dirname(dirname(dirname(dirname(thisPath))))
+
+
+def try_building_js():
+    with pushd(webdir_path()):
+        if call(['yarn', 'run', 'bundle']) != 0:
+            raise OSError('Error executing bundling the application')
diff --git a/web/pgadmin/tools/javascript/tests/__init__.py b/web/pgadmin/tools/javascript/tests/__init__.py
new file mode 100644
index 00000000..cac9a64a
--- /dev/null
+++ b/web/pgadmin/tools/javascript/tests/__init__.py
@@ -0,0 +1,8 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2017, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+###########################################################################
diff --git a/web/pgadmin/tools/javascript/tests/test_javascript_bundler.py b/web/pgadmin/tools/javascript/tests/test_javascript_bundler.py
new file mode 100644
index 00000000..e0cccdfd
--- /dev/null
+++ b/web/pgadmin/tools/javascript/tests/test_javascript_bundler.py
@@ -0,0 +1,117 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2017, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+
+import sys
+
+from pgadmin.utils.route import BaseTestGenerator
+if sys.version_info < (3, 3):
+    import mock
+else:
+    import unittest.mock as mock
+
+
+class JavascriptBundlerTestCase(BaseTestGenerator):
+    """This tests that the javascript bundler tool causes the application to bundle,
+    and can be invoked before and after app start correctly"""
+
+    scenarios = [('scenario name: JavascriptBundlerTestCase', dict())]
+
+    def setUp(self):
+        self.mockSubprocess = mock.Mock()
+        self.mockOs = mock.Mock()
+        sys.modules['subprocess'] = self.mockSubprocess
+        sys.modules['os'] = self.mockOs
+
+    def runTest(self):
+        from pgadmin.tools.javascript.javascript_bundler import JavascriptBundler
+        from pgadmin.tools.javascript.javascript_bundler import JsState
+        self.JavascriptBundler = JavascriptBundler
+        self.JsState = JsState
+
+        self._bundling_succeeds()
+        self.resetTestState()
+        self._bundling_fails_and_there_is_no_existing_bundle()
+        self.resetTestState()
+        self._bundling_fails_when_bundling_returns_nonzero()
+        self.resetTestState()
+        self._bundling_fails_and_there_is_no_existing_bundle_directory()
+        self.resetTestState()
+        self._bundling_fails_but_there_was_existing_bundle()
+        self.resetTestState()
+
+    def resetTestState(self):
+        self.mockSubprocess.reset_mock()
+        self.mockSubprocess.call.side_effect = None
+        self.mockOs.reset_mock()
+        self.mockOs.listdir.side_effect = None
+        self.mockOs.path.exists.side_effect = None
+
+    def _bundling_succeeds(self):
+        javascriptBundler = self.JavascriptBundler()
+        self.assertEqual(len(self.mockSubprocess.method_calls), 0)
+        self.mockSubprocess.call.return_value = 0
+
+        self.mockOs.listdir.return_value = [u'history.js', u'reactComponents.js']
+
+        javascriptBundler.bundle()
+        self.mockSubprocess.call.assert_called_once_with(['yarn', 'run', 'bundle'])
+
+        reportedState = javascriptBundler.report()
+        expectedState = self.JsState.NEW
+        self.assertEqual(reportedState, expectedState)
+
+    def _bundling_fails_when_bundling_returns_nonzero(self):
+        javascriptBundler = self.JavascriptBundler()
+        self.assertEqual(len(self.mockSubprocess.method_calls), 0)
+        self.mockOs.listdir.return_value = []
+        self.mockSubprocess.call.return_value = 99
+
+        javascriptBundler.bundle()
+
+        reportedState = javascriptBundler.report()
+        expectedState = self.JsState.NONE
+        self.assertEqual(reportedState, expectedState)
+
+    def _bundling_fails_and_there_is_no_existing_bundle(self):
+        javascriptBundler = self.JavascriptBundler()
+        self.mockSubprocess.call.side_effect = OSError("mock exception behavior")
+        self.mockOs.path.exists.return_value = True
+        self.mockOs.listdir.return_value = []
+
+        javascriptBundler.bundle()
+
+        reportedState = javascriptBundler.report()
+        expectedState = self.JsState.NONE
+        self.assertEqual(reportedState, expectedState)
+
+    def _bundling_fails_and_there_is_no_existing_bundle_directory(self):
+        javascriptBundler = self.JavascriptBundler()
+        self.mockSubprocess.call.side_effect = OSError("mock exception behavior")
+        self.mockOs.path.exists.return_value = False
+        self.mockOs.listdir.side_effect = OSError("mock exception behavior")
+
+        javascriptBundler.bundle()
+
+        reportedState = javascriptBundler.report()
+        expectedState = self.JsState.NONE
+        self.assertEqual(reportedState, expectedState)
+
+    def _bundling_fails_but_there_was_existing_bundle(self):
+        javascriptBundler = self.JavascriptBundler()
+        self.mockSubprocess.call.side_effect = OSError("mock exception behavior")
+        self.mockOs.path.exists.return_value = True
+        self.mockOs.listdir.return_value = [u'history.js', u'reactComponents.js']
+
+        javascriptBundler.bundle()
+        self.mockSubprocess.call.assert_called_once_with(['yarn', 'run', 'bundle'])
+
+        reportedState = javascriptBundler.report()
+        expectedState = self.JsState.OLD
+        self.assertEqual(reportedState, expectedState)
diff --git a/web/regression/README b/web/regression/README
index f0d7282e..88edbd80 100644
--- a/web/regression/README
+++ b/web/regression/README
@@ -177,16 +177,17 @@ Javascript Tests:
 
     sudo port install nodejs7 yarn
 
+- See also the top-level pgadmin/README : Bundling Javascript
+
 - Javascript tests must be run from the web directory (since that is where node_modules and karma.conf reside):
 
     cd web/
 
 - Install the JS modules required for testing:
 
-    yarn
+    yarn install
 
 - Now run the tests:
 
-    yarn run karma start --single-run
-
-
+   yarn run test:karma
+   yarn run test:karma-once
diff --git a/web/regression/javascript/browser/menu_spec.js b/web/regression/javascript/browser/menu_spec.js
index 09f136fb..e13bfe5e 100644
--- a/web/regression/javascript/browser/menu_spec.js
+++ b/web/regression/javascript/browser/menu_spec.js
@@ -7,9 +7,11 @@
 //
 //////////////////////////////////////////////////////////////
 
-define(["browser/menu"
-], function () {
-  describe("MenuItem", function () {
+define([
+  'pgadmin',
+  'browser/menu',
+], function (pgAdmin) {
+  describe('MenuItem', function () {
     var MenuItem = pgAdmin.Browser.MenuItem;
     var menuItem;
 
diff --git a/web/regression/javascript/gettext_spec.js b/web/regression/javascript/gettext_spec.js
index 2ce98a23..54fc498d 100644
--- a/web/regression/javascript/gettext_spec.js
+++ b/web/regression/javascript/gettext_spec.js
@@ -7,45 +7,46 @@
 //
 //////////////////////////////////////////////////////////////////////////
 
-define(["sources/gettext", "translations"], function (gettext, translations) {
-  describe("translate", function () {
-    describe("when there is no translation", function () {
-      it("returns the original string", function () {
-        expect(gettext("something to be translated")).toEqual("something to be translated");
-      });
-
-      describe("when there are substitutions", function () {
-        it("interpolates a substitution", function () {
-          expect(gettext("translate text for %(person)s", {"person": "Sarah"})).toEqual("translate text for Sarah")
-        });
-
-        it("interpolates multiple substitutions", function () {
-          expect(gettext("translate '%(text)s' for %(person)s",
-            {
-              "text": "constitution",
-              "person": "Sarah"
-            }
-          )).toEqual("translate 'constitution' for Sarah")
-        });
-      });
+import gettext from 'sources/gettext';
+import translations from 'translations';
 
+describe('translate', function () {
+  describe('when there is no translation', function () {
+    it('returns the original string', function () {
+      expect(gettext('something to be translated')).toEqual('something to be translated');
     });
 
-    describe("when there is a translation", function () {
-      beforeEach(function () {
-        translations['something to be translated'] = 'etwas zum uebersetzen';
-        translations['another translation for %(person)s'] = 'eine weitere Uebersetzung fuer %(person)s';
+    describe('when there are substitutions', function () {
+      it('interpolates a substitution', function () {
+        expect(gettext('translate text for %(person)s', {'person': 'Sarah'})).toEqual('translate text for Sarah');
       });
 
-      it("returns the translation", function () {
-        expect(gettext("something to be translated")).toEqual("etwas zum uebersetzen");
+      it('interpolates multiple substitutions', function () {
+        expect(gettext('translate \'%(text)s\' for %(person)s',
+          {
+            'text': 'constitution',
+            'person': 'Sarah',
+          }
+        )).toEqual('translate \'constitution\' for Sarah');
       });
+    });
+
+  });
+
+  describe('when there is a translation', function () {
+    beforeEach(function () {
+      translations['something to be translated'] = 'etwas zum uebersetzen';
+      translations['another translation for %(person)s'] = 'eine weitere Uebersetzung fuer %(person)s';
+    });
+
+    it('returns the translation', function () {
+      expect(gettext('something to be translated')).toEqual('etwas zum uebersetzen');
+    });
 
-      describe("when there is a substitution", function () {
-        it("interpolates the substitution", function () {
-          expect(gettext("another translation for %(person)s", {"person": "Sarah"}))
-            .toEqual("eine weitere Uebersetzung fuer Sarah");
-        });
+    describe('when there is a substitution', function () {
+      it('interpolates the substitution', function () {
+        expect(gettext('another translation for %(person)s', {'person': 'Sarah'}))
+          .toEqual('eine weitere Uebersetzung fuer Sarah');
       });
     });
   });
diff --git a/web/regression/javascript/jasmine_capture_warnings_beforeall.js b/web/regression/javascript/jasmine_capture_warnings_beforeall.js
new file mode 100644
index 00000000..a83e6f61
--- /dev/null
+++ b/web/regression/javascript/jasmine_capture_warnings_beforeall.js
@@ -0,0 +1,23 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+/* eslint-disable no-console */
+
+beforeAll(function () {
+  spyOn(console, 'warn').and.callThrough();
+  spyOn(console, 'error').and.callThrough();
+});
+
+afterEach(function (done) {
+  setTimeout(function () {
+    expect(console.warn).not.toHaveBeenCalled();
+    expect(console.error).not.toHaveBeenCalled();
+    done();
+  }, 0);
+});
\ No newline at end of file
diff --git a/web/regression/javascript/selection/column_selector_spec.js b/web/regression/javascript/selection/column_selector_spec.js
index 7fa69aef..74d137a4 100644
--- a/web/regression/javascript/selection/column_selector_spec.js
+++ b/web/regression/javascript/selection/column_selector_spec.js
@@ -1,419 +1,423 @@
-define(
-  ["jquery",
-    "underscore",
-    "sources/selection/column_selector",
-    "sources/selection/active_cell_capture",
-    "sources/selection/grid_selector",
-    'sources/selection/xcell_selection_model',
-
-    "slickgrid",
-    'sources/slickgrid/pgslick.cellrangedecorator',
-    'sources/slickgrid/pgslick.cellrangeselector',
-    "slickgrid/slick.grid",
-  ],
-  function ($, _, ColumnSelector, ActiveCellCapture, GridSelector, XCellSelectionModel) {
-    var KEY_RIGHT = 39;
-    var KEY_LEFT = 37;
-    var KEY_UP = 38;
-    var KEY_DOWN = 40;
-
-    var Slick = window.Slick;
-    var SlickGrid = Slick.Grid;
-
-    describe("ColumnSelector", function () {
-      var container, data, columns, options;
-      beforeEach(function () {
-        container = $("<div></div>");
-        container.height(9999);
-        container.width(9999);
-
-        data = [{
-          'some-column-name': 'first value',
-          'second column': 'second value',
-          'third column': 'nonselectable value'
-        }, {
-          'some-column-name': 'row 1 - first value',
-          'second column': 'row 1 - second value',
-          'third column': 'row 1 - nonselectable value'
-        }];
-
-        columns = [
-          {
-            id: 'row-header-column',
-            name: 'row header column name',
-            selectable: false,
-            display_name: 'row header column name',
-            column_type: 'text'
-          },
-          {
-            id: '1',
-            name: 'some-column-name',
-            pos: 0,
-            display_name: 'some-column-name',
-            column_type: 'text'
-          },
-          {
-            id: '2',
-            name: 'second column',
-            pos: 1,
-            display_name: 'second column',
-            column_type: 'json'
-          },
-          {
-            id: 'third-column-id',
-            name: 'third column',
-            pos: 2,
-            display_name: 'third column',
-            column_type: 'text'
-          },
-          {
-            name: 'some-non-selectable-column',
-            selectable: false,
-            pos: 3,
-            display_name: 'some-non-selectable-column',
-            column_type: 'numeric'
-          }
-        ];
-      });
-
-      it("displays the name of the column", function () {
-        setupGrid(columns);
-
-        expect($(container.find('.slick-header-columns .slick-column-name')[1]).text())
-          .toContain('some-column-name');
-        expect($(container.find('.slick-header-columns .slick-column-name')[1]).text())
-          .toContain('text');
-        expect($(container.find('.slick-header-columns .slick-column-name')[2]).text())
-          .toContain('second column');
-        expect($(container.find('.slick-header-columns .slick-column-name')[2]).text())
-          .toContain('json');
-      });
-
-      it("preserves the other attributes of column definitions", function () {
-        var columnSelector = new ColumnSelector();
-        var selectableColumns = columnSelector.getColumnDefinitions(columns);
-
-        expect(selectableColumns[1].id).toBe('1');
-      });
-
-      describe("with ActiveCellCapture, CellSelectionModel, and GridSelector: selecting columns", function () {
-        var grid, cellSelectionModel;
-        beforeEach(function () {
-          var columnSelector = new ColumnSelector();
-          columns = columnSelector.getColumnDefinitions(columns);
-          data = [];
-          for (var i = 0; i < 10; i++) {
-            data.push({
-              'some-column-name': 'some-value-' + i,
-              'second column': 'second value ' + i,
-              'third column': 'third value ' + i,
-              'fourth column': 'fourth value ' + i,
-            });
-          }
-          grid = new SlickGrid(container, data, columns);
-
-          grid.registerPlugin(new ActiveCellCapture());
-          cellSelectionModel = new XCellSelectionModel();
-          grid.setSelectionModel(cellSelectionModel);
-
-          grid.registerPlugin(columnSelector);
-          grid.invalidate();
-          $("body").append(container);
-        });
-
-        afterEach(function () {
-          $("body").find(container).remove();
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+import $ from 'jquery';
+
+import Slick from 'slickgrid';
+import 'slickgrid.grid';
+
+import ColumnSelector from 'sources/selection/column_selector';
+import ActiveCellCapture from 'sources/selection/active_cell_capture';
+import 'sources/selection/grid_selector';
+import XCellSelectionModel from 'sources/selection/xcell_selection_model';
+import 'sources/slickgrid/pgslick.cellrangedecorator';
+import 'sources/slickgrid/pgslick.cellrangeselector';
+
+describe('ColumnSelector', function () {
+  var container, data, columns, options;
+  var SlickGrid = Slick.Grid;
+  var KEY_RIGHT = 39;
+  var KEY_LEFT = 37;
+  var KEY_UP = 38;
+  var KEY_DOWN = 40;
+
+  beforeEach(function () {
+    container = $('<div></div>');
+    container.height(9999);
+    container.width(9999);
+
+    data = [{
+      'some-column-name': 'first value',
+      'second column': 'second value',
+      'third column': 'nonselectable value'
+    }, {
+      'some-column-name': 'row 1 - first value',
+      'second column': 'row 1 - second value',
+      'third column': 'row 1 - nonselectable value'
+    }];
+
+    columns = [
+      {
+        id: 'row-header-column',
+        name: 'row header column name',
+        selectable: false,
+        display_name: 'row header column name',
+        column_type: 'text'
+      },
+      {
+        id: '1',
+        name: 'some-column-name',
+        pos: 0,
+        display_name: 'some-column-name',
+        column_type: 'text'
+      },
+      {
+        id: '2',
+        name: 'second column',
+        pos: 1,
+        display_name: 'second column',
+        column_type: 'json'
+      },
+      {
+        id: 'third-column-id',
+        name: 'third column',
+        pos: 2,
+        display_name: 'third column',
+        column_type: 'text'
+      },
+      {
+        name: 'some-non-selectable-column',
+        selectable: false,
+        pos: 3,
+        display_name: 'some-non-selectable-column',
+        column_type: 'numeric'
+      }
+    ];
+  });
+
+  it('displays the name of the column', function () {
+    setupGrid(columns);
+
+    expect($(container.find('.slick-header-columns .slick-column-name')[1]).text())
+      .toContain('some-column-name');
+    expect($(container.find('.slick-header-columns .slick-column-name')[1]).text())
+      .toContain('text');
+    expect($(container.find('.slick-header-columns .slick-column-name')[2]).text())
+      .toContain('second column');
+    expect($(container.find('.slick-header-columns .slick-column-name')[2]).text())
+      .toContain('json');
+  });
+
+  it('preserves the other attributes of column definitions', function () {
+    var columnSelector = new ColumnSelector();
+    var selectableColumns = columnSelector.getColumnDefinitions(columns);
+
+    expect(selectableColumns[1].id).toBe('1');
+  });
+
+  describe('with ActiveCellCapture, CellSelectionModel, and GridSelector: selecting columns', function () {
+    var grid, cellSelectionModel;
+    beforeEach(function () {
+      var columnSelector = new ColumnSelector();
+      columns = columnSelector.getColumnDefinitions(columns);
+      data = [];
+      for (var i = 0; i < 10; i++) {
+        data.push({
+          'some-column-name': 'some-value-' + i,
+          'second column': 'second value ' + i,
+          'third column': 'third value ' + i,
+          'fourth column': 'fourth value ' + i,
         });
+      }
+      grid = new SlickGrid(container, data, columns);
 
-        describe("when the user clicks a column header", function () {
-          it("selects the column", function () {
-            container.find('.slick-header-column:contains(some-column-name)').click();
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
-            expectOnlyTheFirstColumnToBeSelected(selectedRanges);
-          });
-
-          it("toggles a selected class to the header cell", function () {
-            container.find('.slick-header-column:contains(second column)').click();
-            expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
-              .toBe(true);
+      grid.registerPlugin(new ActiveCellCapture());
+      cellSelectionModel = new XCellSelectionModel();
+      grid.setSelectionModel(cellSelectionModel);
 
-            container.find('.slick-header-column:contains(second column)').click();
-            expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
-              .toBe(false);
-          });
-        });
+      grid.registerPlugin(columnSelector);
+      grid.invalidate();
+      $('body').append(container);
+    });
 
-        describe("when the user clicks an additional column header", function () {
-          beforeEach(function () {
-            container.find('.slick-header-column:contains(some-column-name)').click();
-            container.find('.slick-header-column:contains(second column)').click();
-          });
+    afterEach(function () {
+      $('body').find(container).remove();
+    });
 
-          it("selects additional columns", function () {
+    describe('when the user clicks a column header', function () {
+      it('selects the column', function () {
+        container.find('.slick-header-column:contains(some-column-name)').click();
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
+        expectOnlyTheFirstColumnToBeSelected(selectedRanges);
+      });
 
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
+      it('toggles a selected class to the header cell', function () {
+        container.find('.slick-header-column:contains(second column)').click();
+        expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
+          .toBe(true);
 
-            expect(selectedRanges.length).toBe(2);
-            var column1 = selectedRanges[0];
-            expect(column1.fromCell).toBe(1);
-            expect(column1.toCell).toBe(1);
+        container.find('.slick-header-column:contains(second column)').click();
+        expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
+          .toBe(false);
+      });
+    });
 
-            var column2 = selectedRanges[1];
-            expect(column2.fromCell).toBe(2);
-            expect(column2.toCell).toBe(2);
-          });
+    describe('when the user clicks an additional column header', function () {
+      beforeEach(function () {
+        container.find('.slick-header-column:contains(some-column-name)').click();
+        container.find('.slick-header-column:contains(second column)').click();
+      });
 
-          describe("and presses shift + right-arrow", function () {
-            beforeEach(function () {
-              pressShiftArrow(KEY_RIGHT);
-            });
-
-            it("keeps the last column selected", function () {
-              expect(cellSelectionModel.getSelectedRanges().length).toBe(1);
-            });
-
-            it("grows the selection to the right", function () {
-              var selectedRange = cellSelectionModel.getSelectedRanges()[0];
-              expect(selectedRange.fromCell).toBe(2);
-              expect(selectedRange.toCell).toBe(3);
-              expect(selectedRange.fromRow).toBe(0);
-              expect(selectedRange.toRow).toBe(9);
-            });
-
-            it("keeps selected class on columns 2 and 3", function () {
-              expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
-                .toBe(true);
-              expect($(container.find('.slick-header-column:contains(third column)')).hasClass('selected'))
-                .toBe(true);
-              expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
-                .toBe(false);
-            });
-          });
+      it('selects additional columns', function () {
 
-          describe("when the user deselects the last selected column header", function () {
-            beforeEach(function () {
-              container.find('.slick-header-column:contains(second column)').click();
-            });
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-            describe("and presses shift + right-arrow", function () {
-              it("first and second columns are selected", function () {
-                pressShiftArrow(KEY_RIGHT);
+        expect(selectedRanges.length).toBe(2);
+        var column1 = selectedRanges[0];
+        expect(column1.fromCell).toBe(1);
+        expect(column1.toCell).toBe(1);
 
-                var selectedRanges = cellSelectionModel.getSelectedRanges();
+        var column2 = selectedRanges[1];
+        expect(column2.fromCell).toBe(2);
+        expect(column2.toCell).toBe(2);
+      });
 
-                expect(selectedRanges.length).toBe(1);
-                expect(selectedRanges[0].fromCell).toBe(1);
-                expect(selectedRanges[0].toCell).toBe(2);
-                expect(selectedRanges[0].fromRow).toBe(0);
-                expect(selectedRanges[0].toRow).toBe(9);
-              });
-            });
-          });
+      describe('and presses shift + right-arrow', function () {
+        beforeEach(function () {
+          pressShiftArrow(KEY_RIGHT);
         });
 
-        describe("when the user clicks a column header description", function () {
-          it("selects the column", function () {
-            container.find('.slick-header-columns span.column-description:contains(some-column-name)').click();
+        it('keeps the last column selected', function () {
+          expect(cellSelectionModel.getSelectedRanges().length).toBe(1);
+        });
 
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
-            expectOnlyTheFirstColumnToBeSelected(selectedRanges);
-          });
+        it('grows the selection to the right', function () {
+          var selectedRange = cellSelectionModel.getSelectedRanges()[0];
+          expect(selectedRange.fromCell).toBe(2);
+          expect(selectedRange.toCell).toBe(3);
+          expect(selectedRange.fromRow).toBe(0);
+          expect(selectedRange.toRow).toBe(9);
+        });
 
-          it("toggles a selected class to the header cell", function () {
-            container.find('.slick-header-column span.column-description:contains(second column)').click();
-            expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
-              .toBe(true);
+        it('keeps selected class on columns 2 and 3', function () {
+          expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
+            .toBe(true);
+          expect($(container.find('.slick-header-column:contains(third column)')).hasClass('selected'))
+            .toBe(true);
+          expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
+            .toBe(false);
+        });
+      });
 
-            container.find('.slick-header-column span.column-description:contains(second column)').click();
-            expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
-              .toBe(false);
-          });
+      describe('when the user deselects the last selected column header', function () {
+        beforeEach(function () {
+          container.find('.slick-header-column:contains(second column)').click();
         });
 
-        describe("when a row is selected", function () {
-          beforeEach(function () {
-            var selectedRanges = [new Slick.Range(0, 0, 0, 1)];
-            cellSelectionModel.setSelectedRanges(selectedRanges);
-          });
+        describe('and presses shift + right-arrow', function () {
+          it('first and second columns are selected', function () {
+            pressShiftArrow(KEY_RIGHT);
 
-          it("deselects the row", function () {
-            container.find('.slick-header-column')[1].click();
             var selectedRanges = cellSelectionModel.getSelectedRanges();
 
             expect(selectedRanges.length).toBe(1);
-
-            var column = selectedRanges[0];
-
-            expect(column.fromCell).toBe(1);
-            expect(column.toCell).toBe(1);
-            expect(column.fromRow).toBe(0);
-            expect(column.toRow).toBe(9);
+            expect(selectedRanges[0].fromCell).toBe(1);
+            expect(selectedRanges[0].toCell).toBe(2);
+            expect(selectedRanges[0].fromRow).toBe(0);
+            expect(selectedRanges[0].toRow).toBe(9);
           });
         });
+      });
+    });
 
-        describe("clicking a second time", function () {
-          beforeEach(function () {
-            container.find('.slick-header-column')[1].click();
-          });
+    describe('when the user clicks a column header description', function () {
+      it('selects the column', function () {
+        container.find('.slick-header-columns span.column-description:contains(some-column-name)').click();
 
-          it("deselects the column", function () {
-            container.find('.slick-header-column')[1].click();
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
+        expectOnlyTheFirstColumnToBeSelected(selectedRanges);
+      });
 
-            expect(selectedRanges.length).toEqual(0);
-          })
-        });
+      it('toggles a selected class to the header cell', function () {
+        container.find('.slick-header-column span.column-description:contains(second column)').click();
+        expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
+          .toBe(true);
 
-        describe("when the column is not selectable", function () {
-          it("does not select the column", function () {
-            $(container.find('.slick-header-column:contains(some-non-selectable-column)')).click();
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
+        container.find('.slick-header-column span.column-description:contains(second column)').click();
+        expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
+          .toBe(false);
+      });
+    });
 
-            expect(selectedRanges.length).toEqual(0);
-          });
-        });
+    describe('when a row is selected', function () {
+      beforeEach(function () {
+        var selectedRanges = [new Slick.Range(0, 0, 0, 1)];
+        cellSelectionModel.setSelectedRanges(selectedRanges);
+      });
 
-        describe("when the column is deselected through setSelectedRanges", function () {
-          beforeEach(function () {
-            container.find('.slick-header-column')[1].click();
-          });
+      it('deselects the row', function () {
+        container.find('.slick-header-column')[1].click();
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-          it("removes selected class from header", function () {
-            cellSelectionModel.setSelectedRanges([]);
+        expect(selectedRanges.length).toBe(1);
 
-            expect($(container.find('.slick-header-column')[1]).hasClass('selected'))
-              .toBe(false);
-          });
-        });
+        var column = selectedRanges[0];
 
-        describe("when a non-column range was already selected", function () {
-          beforeEach(function () {
-            var selectedRanges = [new Slick.Range(0, 0, 2, 0)];
-            cellSelectionModel.setSelectedRanges(selectedRanges);
-          });
+        expect(column.fromCell).toBe(1);
+        expect(column.toCell).toBe(1);
+        expect(column.fromRow).toBe(0);
+        expect(column.toRow).toBe(9);
+      });
+    });
 
-          it("deselects the non-column range", function () {
-            container.find('.slick-header-column:contains(some-column-name)').click();
+    describe('clicking a second time', function () {
+      beforeEach(function () {
+        container.find('.slick-header-column')[1].click();
+      });
 
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
-            expectOnlyTheFirstColumnToBeSelected(selectedRanges);
-          });
-        });
+      it('deselects the column', function () {
+        container.find('.slick-header-column')[1].click();
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-        describe('when a column is selected', function () {
-          beforeEach(function () {
-            container.find('.slick-header-column:contains(some-column-name)').click();
-          });
+        expect(selectedRanges.length).toEqual(0);
+      })
+    });
 
-          describe('when the user click a cell on the current range', function () {
-            beforeEach(function () {
-              container.find('.slick-cell.l1.r1')[1].click();
-            });
+    describe('when the column is not selectable', function () {
+      it('does not select the column', function () {
+        $(container.find('.slick-header-column:contains(some-non-selectable-column)')).click();
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-            it('column is deselected', function () {
+        expect(selectedRanges.length).toEqual(0);
+      });
+    });
 
-              var selectedRanges = cellSelectionModel.getSelectedRanges();
+    describe('when the column is deselected through setSelectedRanges', function () {
+      beforeEach(function () {
+        container.find('.slick-header-column')[1].click();
+      });
 
-              expect(selectedRanges.length).toBe(1);
+      it('removes selected class from header', function () {
+        cellSelectionModel.setSelectedRanges([]);
 
-              var column = selectedRanges[0];
+        expect($(container.find('.slick-header-column')[1]).hasClass('selected'))
+          .toBe(false);
+      });
+    });
 
-              expect(column.fromCell).toBe(1);
-              expect(column.toCell).toBe(1);
-              expect(column.fromRow).toBe(1);
-              expect(column.toRow).toBe(1);
-            });
+    describe('when a non-column range was already selected', function () {
+      beforeEach(function () {
+        var selectedRanges = [new Slick.Range(0, 0, 2, 0)];
+        cellSelectionModel.setSelectedRanges(selectedRanges);
+      });
 
-            it('keep select class on column header', function () {
-              expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
-                .toBeTruthy();
-            });
-          });
+      it('deselects the non-column range', function () {
+        container.find('.slick-header-column:contains(some-column-name)').click();
 
-          describe('when the user click a cell outside the current range', function () {
-            beforeEach(function () {
-              container.find('.slick-cell.l2.r2')[2].click();
-            });
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
+        expectOnlyTheFirstColumnToBeSelected(selectedRanges);
+      });
+    });
 
-            it('column is deselected', function () {
+    describe('when a column is selected', function () {
+      beforeEach(function () {
+        container.find('.slick-header-column:contains(some-column-name)').click();
+      });
 
-              var selectedRanges = cellSelectionModel.getSelectedRanges();
+      describe('when the user click a cell on the current range', function () {
+        beforeEach(function () {
+          container.find('.slick-cell.l1.r1')[1].click();
+        });
 
-              expect(selectedRanges.length).toBe(1);
+        it('column is deselected', function () {
 
-              var column = selectedRanges[0];
+          var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-              expect(column.fromCell).toBe(2);
-              expect(column.toCell).toBe(2);
-              expect(column.fromRow).toBe(2);
-              expect(column.toRow).toBe(2);
-            });
+          expect(selectedRanges.length).toBe(1);
 
-            it('remove select class on "some-column-name" column header', function () {
-              expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
-                .toBeFalsy();
-              expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
-                .toBeTruthy();
-            });
-          });
+          var column = selectedRanges[0];
 
-          describe('when the user click in a row header', function () {
-            beforeEach(function () {
-              var selectedRanges = [new Slick.Range(1, 1, 1, 3)];
-              cellSelectionModel.setSelectedRanges(selectedRanges);
-            });
+          expect(column.fromCell).toBe(1);
+          expect(column.toCell).toBe(1);
+          expect(column.fromRow).toBe(1);
+          expect(column.toRow).toBe(1);
+        });
 
-            it('column is deselected', function () {
-              var selectedRanges = cellSelectionModel.getSelectedRanges();
+        it('keep select class on column header', function () {
+          expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
+            .toBeTruthy();
+        });
+      });
 
-              expect(selectedRanges.length).toBe(1);
+      describe('when the user click a cell outside the current range', function () {
+        beforeEach(function () {
+          container.find('.slick-cell.l2.r2')[2].click();
+        });
 
-              var column = selectedRanges[0];
+        it('column is deselected', function () {
 
-              expect(column.fromCell).toBe(1);
-              expect(column.toCell).toBe(3);
-              expect(column.fromRow).toBe(1);
-              expect(column.toRow).toBe(1);
-            });
+          var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-            it('no column should have the class "selected"', function () {
-              expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
-                .toBeFalsy();
-            });
-          });
+          expect(selectedRanges.length).toBe(1);
+
+          var column = selectedRanges[0];
+
+          expect(column.fromCell).toBe(2);
+          expect(column.toCell).toBe(2);
+          expect(column.fromRow).toBe(2);
+          expect(column.toRow).toBe(2);
+        });
+
+        it('remove select class on \'some-column-name\' column header', function () {
+          expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
+            .toBeFalsy();
+          expect($(container.find('.slick-header-column:contains(second column)')).hasClass('selected'))
+            .toBeTruthy();
         });
       });
 
-      function setupGrid(columns) {
-        var columnSelector = new ColumnSelector();
-        columns = columnSelector.getColumnDefinitions(columns);
-        var grid = new SlickGrid(container, data, columns, options);
+      describe('when the user click in a row header', function () {
+        beforeEach(function () {
+          var selectedRanges = [new Slick.Range(1, 1, 1, 3)];
+          cellSelectionModel.setSelectedRanges(selectedRanges);
+        });
 
-        var cellSelectionModel = new XCellSelectionModel();
-        grid.setSelectionModel(cellSelectionModel);
+        it('column is deselected', function () {
+          var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-        grid.registerPlugin(columnSelector);
-        grid.invalidate();
-      }
+          expect(selectedRanges.length).toBe(1);
 
-      function expectOnlyTheFirstColumnToBeSelected(selectedRanges) {
-        var row = selectedRanges[0];
+          var column = selectedRanges[0];
 
-        expect(selectedRanges.length).toEqual(1);
-        expect(row.fromCell).toBe(1);
-        expect(row.toCell).toBe(1);
-        expect(row.fromRow).toBe(0);
-        expect(row.toRow).toBe(9);
-      }
-
-      function pressShiftArrow(keyCode) {
-        var pressEvent = new $.Event("keydown");
-        pressEvent.shiftKey = true;
-        pressEvent.ctrlKey = false;
-        pressEvent.altKey = false;
-        pressEvent.which = keyCode;
+          expect(column.fromCell).toBe(1);
+          expect(column.toCell).toBe(3);
+          expect(column.fromRow).toBe(1);
+          expect(column.toRow).toBe(1);
+        });
 
-        $(container.find('.grid-canvas')).trigger(pressEvent);
-      }
+        it('no column should have the class \'selected\'', function () {
+          expect($(container.find('.slick-header-column:contains(some-column-name)')).hasClass('selected'))
+            .toBeFalsy();
+        });
+      });
     });
-  });
\ No newline at end of file
+  });
+
+  function setupGrid(columns) {
+    var columnSelector = new ColumnSelector();
+    columns = columnSelector.getColumnDefinitions(columns);
+    var grid = new SlickGrid(container, data, columns, options);
+
+    var cellSelectionModel = new XCellSelectionModel();
+    grid.setSelectionModel(cellSelectionModel);
+
+    grid.registerPlugin(columnSelector);
+    grid.invalidate();
+  }
+
+  function expectOnlyTheFirstColumnToBeSelected(selectedRanges) {
+    var row = selectedRanges[0];
+
+    expect(selectedRanges.length).toEqual(1);
+    expect(row.fromCell).toBe(1);
+    expect(row.toCell).toBe(1);
+    expect(row.fromRow).toBe(0);
+    expect(row.toRow).toBe(9);
+  }
+
+  function pressShiftArrow(keyCode) {
+    var pressEvent = new $.Event('keydown');
+    pressEvent.shiftKey = true;
+    pressEvent.ctrlKey = false;
+    pressEvent.altKey = false;
+    pressEvent.which = keyCode;
+
+    $(container.find('.grid-canvas')).trigger(pressEvent);
+  }
+});
diff --git a/web/regression/javascript/selection/copy_data_spec.js b/web/regression/javascript/selection/copy_data_spec.js
index 561576ce..2d6b6ad2 100644
--- a/web/regression/javascript/selection/copy_data_spec.js
+++ b/web/regression/javascript/selection/copy_data_spec.js
@@ -1,139 +1,142 @@
-/////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
 //
 // pgAdmin 4 - PostgreSQL Tools
 //
 // Copyright (C) 2013 - 2017, The pgAdmin Development Team
 // This software is released under the PostgreSQL Licence
 //
-//////////////////////////////////////////////////////////////
-
-define(
-  ["jquery",
-    "slickgrid/slick.grid",
-    "sources/selection/xcell_selection_model",
-    "sources/selection/copy_data",
-    "sources/selection/clipboard",
-    "sources/selection/range_selection_helper"
-  ],
-  function ($, SlickGrid, XCellSelectionModel, copyData, clipboard, RangeSelectionHelper) {
-    describe('copyData', function () {
-      var grid, sqlEditor, gridContainer, buttonPasteRow;
-
-      beforeEach(function () {
-        var data = [[1, "leopord", "12"],
-          [2, "lion", "13"],
-          [3, "puma", "9"]];
-
-        var columns = [
-          {
-            id: 'row-header-column',
-            name: 'row header column name',
-            selectable: false,
-            display_name: 'row header column name',
-            column_type: 'text'
-          },
-          {
-            name: "id",
-            pos: 0,
-            label: "id<br> numeric",
-            cell: "number",
-            can_edit: false,
-            type: "numeric"
-          }, {
-            name: "brand",
-            pos: 1,
-            label: "flavor<br> character varying",
-            cell: "string",
-            can_edit: false,
-            type: "character varying"
-          }, {
-            name: "size",
-            pos: 2,
-            label: "size<br> numeric",
-            cell: "number",
-            can_edit: false,
-            type: "numeric"
-          }
-          ]
-        ;
-        gridContainer = $("<div id='grid'></div>");
-        $("body").append(gridContainer);
-        buttonPasteRow = $("<button id='btn-paste-row' disabled></button>");
-        $("body").append(buttonPasteRow);
-        grid = new Slick.Grid("#grid", data, columns, {});
-        grid.setSelectionModel(new XCellSelectionModel());
-        sqlEditor = {slickgrid: grid};
-      });
+//////////////////////////////////////////////////////////////////////////
+
+import $ from 'jquery';
+
+import Slick from 'slickgrid';
+import 'slickgrid.grid';
+
+import clipboard from '../../../pgadmin/static/js/selection/clipboard';
+import copyData from '../../../pgadmin/static/js/selection/copy_data';
+import RangeSelectionHelper from 'sources/selection/range_selection_helper';
+import XCellSelectionModel from 'sources/selection/xcell_selection_model';
+
+describe('copyData', function () {
+  var grid, sqlEditor, gridContainer, buttonPasteRow;
+  var Slick, SlickGrid;
+
+  beforeEach(function () {
+    Slick = window.Slick;
+    SlickGrid = Slick.Grid;
+    var data = [[1, 'leopord', '12'],
+      [2, 'lion', '13'],
+      [3, 'puma', '9']];
+
+    var columns = [
+        {
+          id: 'row-header-column',
+          name: 'row header column name',
+          selectable: false,
+          display_name: 'row header column name',
+          column_type: 'text'
+        },
+        {
+          name: 'id',
+          pos: 0,
+          label: 'id<br> numeric',
+          cell: 'number',
+          can_edit: false,
+          type: 'numeric'
+        }, {
+          name: 'brand',
+          pos: 1,
+          label: 'flavor<br> character varying',
+          cell: 'string',
+          can_edit: false,
+          type: 'character varying'
+        }, {
+          name: 'size',
+          pos: 2,
+          label: 'size<br> numeric',
+          cell: 'number',
+          can_edit: false,
+          type: 'numeric'
+        }
+      ]
+    ;
+    gridContainer = $('<div id=\'grid\'></div>');
+    $('body').append(gridContainer);
+    buttonPasteRow = $('<button id=\'btn-paste-row\' disabled></button>');
+    $('body').append(buttonPasteRow);
+    grid = new Slick.Grid('#grid', data, columns, {});
+    grid.setSelectionModel(new XCellSelectionModel());
+    sqlEditor = {slickgrid: grid};
+  });
 
-      afterEach(function() {
-        gridContainer.remove();
-        buttonPasteRow.remove();
-      });
+  afterEach(function () {
+    gridContainer.remove();
+    buttonPasteRow.remove();
+  });
 
-      describe("when rows are selected", function () {
-        beforeEach(function () {
-          grid.getSelectionModel().setSelectedRanges([
-            RangeSelectionHelper.rangeForRow(grid, 0),
-            RangeSelectionHelper.rangeForRow(grid, 2)]
-          );
-        });
+  describe('when rows are selected', function () {
+    beforeEach(function () {
+      grid.getSelectionModel().setSelectedRanges([
+        RangeSelectionHelper.rangeForRow(grid, 0),
+        RangeSelectionHelper.rangeForRow(grid, 2)]
+      );
+    });
 
-        it("copies them", function () {
-          spyOn(clipboard, 'copyTextToClipboard');
+    it('copies them', function () {
+      spyOn(clipboard, 'copyTextToClipboard');
 
-          copyData.apply(sqlEditor);
+      copyData.apply(sqlEditor);
 
-          expect(sqlEditor.copied_rows.length).toBe(2);
+      expect(sqlEditor.copied_rows.length).toBe(2);
 
-          expect(clipboard.copyTextToClipboard).toHaveBeenCalled();
-          expect(clipboard.copyTextToClipboard.calls.mostRecent().args[0]).toContain("1,'leopord','12'");
-          expect(clipboard.copyTextToClipboard.calls.mostRecent().args[0]).toContain("3,'puma','9'");
-        });
+      expect(clipboard.copyTextToClipboard).toHaveBeenCalled();
+      expect(clipboard.copyTextToClipboard.calls.mostRecent().args[0]).toContain('1,\'leopord\',\'12\'');
+      expect(clipboard.copyTextToClipboard.calls.mostRecent().args[0]).toContain('3,\'puma\',\'9\'');
+    });
 
-        describe("when the user can edit the grid", function () {
-          it("enables the paste row button", function () {
-            copyData.apply(_.extend({can_edit: true}, sqlEditor));
+    describe('when the user can edit the grid', function () {
+      it('enables the paste row button', function () {
+        copyData.apply(_.extend({can_edit: true}, sqlEditor));
 
-            expect($("#btn-paste-row").prop('disabled')).toBe(false);
-          });
-        });
+        expect($('#btn-paste-row').prop('disabled')).toBe(false);
       });
+    });
+  });
 
-      describe("when a column is selected", function () {
-        beforeEach(function () {
-          var firstDataColumn = RangeSelectionHelper.rangeForColumn(grid, 1);
-          grid.getSelectionModel().setSelectedRanges([firstDataColumn])
-        });
+  describe('when a column is selected', function () {
+    beforeEach(function () {
+      var firstDataColumn = RangeSelectionHelper.rangeForColumn(grid, 1);
+      grid.getSelectionModel().setSelectedRanges([firstDataColumn]);
+    });
 
-        it("copies text to the clipboard", function () {
-          spyOn(clipboard, 'copyTextToClipboard');
+    it('copies text to the clipboard', function () {
+      spyOn(clipboard, 'copyTextToClipboard');
 
-          copyData.apply(sqlEditor);
+      copyData.apply(sqlEditor);
 
-          expect(clipboard.copyTextToClipboard).toHaveBeenCalled();
+      expect(clipboard.copyTextToClipboard).toHaveBeenCalled();
 
-          var copyArg = clipboard.copyTextToClipboard.calls.mostRecent().args[0];
-          var rowStrings = copyArg.split('\n');
-          expect(rowStrings[0]).toBe("1");
-          expect(rowStrings[1]).toBe("2");
-          expect(rowStrings[2]).toBe("3");
-        });
+      var copyArg = clipboard.copyTextToClipboard.calls.mostRecent().args[0];
+      var rowStrings = copyArg.split('\n');
+      expect(rowStrings[0]).toBe('1');
+      expect(rowStrings[1]).toBe('2');
+      expect(rowStrings[2]).toBe('3');
+    });
 
-        it("sets copied_rows to empty", function () {
-          copyData.apply(sqlEditor);
+    it('sets copied_rows to empty', function () {
+      copyData.apply(sqlEditor);
 
-          expect(sqlEditor.copied_rows.length).toBe(0);
-        });
+      expect(sqlEditor.copied_rows.length).toBe(0);
+    });
 
-        describe("when the user can edit the grid", function () {
-          beforeEach(function () {
-            copyData.apply(_.extend({can_edit: true}, sqlEditor));
-          });
+    describe('when the user can edit the grid', function () {
+      beforeEach(function () {
+        copyData.apply(_.extend({can_edit: true}, sqlEditor));
+      });
 
-          it("disables the paste row button", function () {
-            expect($("#btn-paste-row").prop('disabled')).toBe(true);
-          });
-        });
+      it('disables the paste row button', function () {
+        expect($('#btn-paste-row').prop('disabled')).toBe(true);
       });
     });
   });
+});
diff --git a/web/regression/javascript/selection/grid_selector_spec.js b/web/regression/javascript/selection/grid_selector_spec.js
index d79d417f..f7b39ee8 100644
--- a/web/regression/javascript/selection/grid_selector_spec.js
+++ b/web/regression/javascript/selection/grid_selector_spec.js
@@ -1,117 +1,130 @@
-define(["jquery",
-    "underscore",
-    "slickgrid/slick.grid",
-    "sources/selection/xcell_selection_model",
-    "sources/selection/grid_selector"
-  ],
-  function ($, _, SlickGrid, XCellSelectionModel, GridSelector) {
-    describe("GridSelector", function () {
-      var container, data, columns, gridSelector, xCellSelectionModel;
-
-      beforeEach(function () {
-        container = $("<div></div>");
-        container.height(9999);
-        columns = [{
-          id: '1',
-          name: 'some-column-name',
-          pos: 0
-        }, {
-          id: '2',
-          name: 'second column',
-          pos: 1
-        }];
-
-        gridSelector = new GridSelector();
-        columns = gridSelector.getColumnDefinitions(columns);
-
-        data = [];
-        for (var i = 0; i < 10; i++) {
-          data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
-        }
-        var grid = new SlickGrid(container, data, columns);
-
-        xCellSelectionModel = new XCellSelectionModel();
-        grid.setSelectionModel(xCellSelectionModel);
-
-        grid.registerPlugin(gridSelector);
-        grid.invalidate();
-
-        $("body").append(container);
-      });
-
-      afterEach(function () {
-        $("body").find(container).remove();
-      });
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+import $ from 'jquery';
+import _ from 'underscore';
+
+import Slick from 'slickgrid';
+import 'slickgrid.grid';
+
+import GridSelector from 'sources/selection/grid_selector';
+import XCellSelectionModel from 'sources/selection/xcell_selection_model';
+
+describe('GridSelector', function () {
+  var container, data, columns, gridSelector, xCellSelectionModel;
+  var Slick, SlickGrid;
+
+  beforeEach(function () {
+    Slick = window.Slick;
+    SlickGrid = Slick.Grid;
+    container = $('<div></div>');
+    container.height(9999);
+    columns = [{
+      id: '1',
+      name: 'some-column-name',
+      pos: 0
+    }, {
+      id: '2',
+      name: 'second column',
+      pos: 1
+    }];
+
+    gridSelector = new GridSelector();
+    columns = gridSelector.getColumnDefinitions(columns);
+
+    data = [];
+    for (var i = 0; i < 10; i++) {
+      data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
+    }
+    var grid = new Slick.Grid(container, data, columns);
+
+    xCellSelectionModel = new XCellSelectionModel();
+    grid.setSelectionModel(xCellSelectionModel);
+
+    grid.registerPlugin(gridSelector);
+    grid.invalidate();
+
+    $('body').append(container);
+  });
 
-      it("renders an additional column on the left for selecting rows", function () {
-        expect(columns.length).toBe(3);
+  afterEach(function () {
+    $('body').find(container).remove();
+  });
 
-        var leftmostColumn = columns[0];
-        expect(leftmostColumn.id).toBe('row-header-column');
-      });
+  it('renders an additional column on the left for selecting rows', function () {
+    expect(columns.length).toBe(3);
 
-      it("renders a button for selecting all the cells", function () {
-        expect(container.find("[title='Select/Deselect All']").length).toBe(1);
-      });
+    var leftmostColumn = columns[0];
+    expect(leftmostColumn.id).toBe('row-header-column');
+  });
 
-      describe("when the cell for the select/deselect all is clicked", function () {
-        it("selects the whole grid", function () {
-          container.find("[title='Select/Deselect All']").parent().click();
+  it('renders a button for selecting all the cells', function () {
+    expect(container.find('[title=\'Select/Deselect All\']').length).toBe(1);
+  });
 
-          var selectedRanges = xCellSelectionModel.getSelectedRanges();
-          expect(selectedRanges.length).toBe(1);
-          var selectedRange = selectedRanges[0];
-          expect(selectedRange.fromCell).toBe(1);
-          expect(selectedRange.toCell).toBe(2);
-          expect(selectedRange.fromRow).toBe(0);
-          expect(selectedRange.toRow).toBe(9);
-        });
+  describe('when the cell for the select/deselect all is clicked', function () {
+    it('selects the whole grid', function () {
+      container.find('[title=\'Select/Deselect All\']').parent().click();
+
+      var selectedRanges = xCellSelectionModel.getSelectedRanges();
+      expect(selectedRanges.length).toBe(1);
+      var selectedRange = selectedRanges[0];
+      expect(selectedRange.fromCell).toBe(1);
+      expect(selectedRange.toCell).toBe(2);
+      expect(selectedRange.fromRow).toBe(0);
+      expect(selectedRange.toRow).toBe(9);
+    });
 
-        it("adds selected class", function () {
-          container.find("[title='Select/Deselect All']").parent().click();
+    it('adds selected class', function () {
+      container.find('[title=\'Select/Deselect All\']').parent().click();
 
-          expect($(container.find("[data-id='select-all']")).hasClass('selected')).toBeTruthy();
-        });
-      });
+      expect($(container.find('[data-id=\'select-all\']')).hasClass('selected')).toBeTruthy();
+    });
+  });
 
-      describe("when the select all button in the corner gets selected", function () {
+  describe('when the select all button in the corner gets selected', function () {
 
-        it("selects all the cells", function () {
-          container.find("[title='Select/Deselect All']").click();
+    it('selects all the cells', function () {
+      container.find('[title=\'Select/Deselect All\']').click();
 
-          var selectedRanges = xCellSelectionModel.getSelectedRanges();
-          expect(selectedRanges.length).toBe(1);
-          var selectedRange = selectedRanges[0];
-          expect(selectedRange.fromCell).toBe(1);
-          expect(selectedRange.toCell).toBe(2);
-          expect(selectedRange.fromRow).toBe(0);
-          expect(selectedRange.toRow).toBe(9);
-        });
+      var selectedRanges = xCellSelectionModel.getSelectedRanges();
+      expect(selectedRanges.length).toBe(1);
+      var selectedRange = selectedRanges[0];
+      expect(selectedRange.fromCell).toBe(1);
+      expect(selectedRange.toCell).toBe(2);
+      expect(selectedRange.fromRow).toBe(0);
+      expect(selectedRange.toRow).toBe(9);
+    });
 
-        describe("when the select all button in the corner gets deselected", function () {
-          beforeEach(function () {
-            container.find("[title='Select/Deselect All']").click();
-          });
+    describe('when the select all button in the corner gets deselected', function () {
+      beforeEach(function () {
+        container.find('[title=\'Select/Deselect All\']').click();
+      });
 
-          it("deselects all the cells", function () {
-            container.find("[title='Select/Deselect All']").click();
+      it('deselects all the cells', function () {
+        container.find('[title=\'Select/Deselect All\']').click();
 
-            var selectedRanges = xCellSelectionModel.getSelectedRanges();
-            expect(selectedRanges.length).toBe(0);
-          });
-        });
+        var selectedRanges = xCellSelectionModel.getSelectedRanges();
+        expect(selectedRanges.length).toBe(0);
+      });
+    });
 
-        describe("and then the underlying selection changes", function () {
-          beforeEach(function () {
-            container.find("[title='Select/Deselect All']").click();
-          });
+    describe('and then the underlying selection changes', function () {
+      beforeEach(function () {
+        container.find('[title=\'Select/Deselect All\']').click();
+      });
 
-          it("removes the selected class", function () {
-            container.find("[title='Select/Deselect All']").parent().click();
+      it('removes the selected class', function () {
+        container.find('[title=\'Select/Deselect All\']').parent().click();
 
-            expect($(container.find("[data-id='select-all']")).hasClass('selected')).toBeFalsy();
-          });
-        });
+        expect($(container.find('[data-id=\'select-all\']')).hasClass('selected')).toBeFalsy();
       });
     });
   });
+});
diff --git a/web/regression/javascript/selection/range_boundary_navigator_spec.js b/web/regression/javascript/selection/range_boundary_navigator_spec.js
index b1ca45eb..b57087f3 100644
--- a/web/regression/javascript/selection/range_boundary_navigator_spec.js
+++ b/web/regression/javascript/selection/range_boundary_navigator_spec.js
@@ -1,8 +1,20 @@
-define(['sources/selection/range_boundary_navigator'], function (rangeBoundaryNavigator) {
-
-  describe("#getUnion", function () {
-    describe("when the ranges completely overlap", function () {
-      it("returns a list with that range", function () {
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+import rangeBoundaryNavigator from 'sources/selection/range_boundary_navigator';
+import Slick from 'slickgrid';
+
+describe('RangeBoundaryNavigator', function () {
+
+  describe('#getUnion', function () {
+    describe('when the ranges completely overlap', function () {
+      it('returns a list with that range', function () {
         var ranges = [[1, 4], [1, 4], [1, 4]];
 
         var union = rangeBoundaryNavigator.getUnion(ranges);
diff --git a/web/regression/javascript/selection/range_selection_helper_spec.js b/web/regression/javascript/selection/range_selection_helper_spec.js
index cc81189b..63c07259 100644
--- a/web/regression/javascript/selection/range_selection_helper_spec.js
+++ b/web/regression/javascript/selection/range_selection_helper_spec.js
@@ -1,70 +1,82 @@
-define([
-  'jquery',
-  'slickgrid/slick.grid',
-  'sources/selection/range_selection_helper'
-], function ($, SlickGrid, RangeSelectionHelper) {
-  describe("RangeSelectionHelper utility functions", function () {
-    var grid;
-    beforeEach(function () {
-      var container, data, columns, options;
-      container = $("<div></div>");
-      container.height(9999);
+import $ from 'jquery';
+import Slick from 'slickgrid';
+import 'slickgrid.grid';
+import RangeSelectionHelper from 'sources/selection/range_selection_helper';
 
-      columns = [{
-        id: '1',
-        name: 'some-column-name',
-        pos: 0
-      }, {
-        id: 'second-column-id',
-        name: 'second column',
-        pos: 1
-      }];
+describe("RangeSelectionHelper utility functions", function () {
+  var grid;
+  beforeEach(function () {
+    var container, data, columns, options;
+    container = $("<div></div>");
+    container.height(9999);
 
-      data = [];
-      for (var i = 0; i < 10; i++) {
-        data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
-      }
+    columns = [{
+      id: '1',
+      name: 'some-column-name',
+      pos: 0
+    }, {
+      id: 'second-column-id',
+      name: 'second column',
+      pos: 1
+    }];
 
-      grid = new SlickGrid(container, data, columns, options);
-      grid.invalidate();
-    });
+    data = [];
+    for (var i = 0; i < 10; i++) {
+      data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
+    }
+
+    grid = new Slick.Grid(container, data, columns, options);
+    grid.invalidate();
+  });
 
-    describe("#getIndexesOfCompleteRows", function () {
-      describe("when selected ranges are not rows", function () {
-        it("returns an empty array", function () {
-          var rowlessRanges = [RangeSelectionHelper.rangeForColumn(grid, 1)];
+  describe("#getIndexesOfCompleteRows", function () {
+    describe("when selected ranges are not rows", function () {
+      it("returns an empty array", function () {
+        var rowlessRanges = [RangeSelectionHelper.rangeForColumn(grid, 1)];
 
-          expect(RangeSelectionHelper.getIndexesOfCompleteRows(grid, rowlessRanges))
-            .toEqual([]);
+        expect(RangeSelectionHelper.getIndexesOfCompleteRows(grid, rowlessRanges))
+          .toEqual([]);
+      });
+    });
+    describe("when selected range", function () {
+      describe("is a single row", function () {
+        it("returns an array with one index", function () {
+          var singleRowRange = [RangeSelectionHelper.rangeForRow(grid, 1)];
+
+          expect(RangeSelectionHelper.getIndexesOfCompleteRows(grid, singleRowRange))
+            .toEqual([1]);
         });
       });
-      describe("when selected range", function () {
-        describe("is a single row", function () {
-          it("returns an array with one index", function () {
-            var singleRowRange = [RangeSelectionHelper.rangeForRow(grid, 1)];
 
-            expect(RangeSelectionHelper.getIndexesOfCompleteRows(grid, singleRowRange))
-              .toEqual([1]);
-          });
+      describe("is multiple rows", function () {
+        it("returns an array of each row's index", function () {
+          var multipleRowRange = [
+            RangeSelectionHelper.rangeForRow(grid, 0),
+            RangeSelectionHelper.rangeForRow(grid, 3),
+            RangeSelectionHelper.rangeForRow(grid, 2),
+          ];
+
+          var indexesOfCompleteRows = RangeSelectionHelper.getIndexesOfCompleteRows(grid, multipleRowRange);
+          indexesOfCompleteRows.sort();
+          expect(indexesOfCompleteRows).toEqual([0, 2, 3]);
         });
+      });
 
-        describe("is multiple rows", function () {
-          it("returns an array of each row's index", function () {
-            var multipleRowRange = [
-              RangeSelectionHelper.rangeForRow(grid, 0),
-              RangeSelectionHelper.rangeForRow(grid, 3),
-              RangeSelectionHelper.rangeForRow(grid, 2),
-            ];
+      describe("contains a multi row selection", function () {
+        it("returns an array of each individual row's index", function () {
+          var multipleRowRange = [
+            new Slick.Range(3, 0, 5, 1)
+          ];
 
-            var indexesOfCompleteRows = RangeSelectionHelper.getIndexesOfCompleteRows(grid, multipleRowRange);
-            indexesOfCompleteRows.sort();
-            expect(indexesOfCompleteRows).toEqual([0, 2, 3]);
-          });
+          var indexesOfCompleteRows = RangeSelectionHelper.getIndexesOfCompleteRows(grid, multipleRowRange);
+          indexesOfCompleteRows.sort();
+          expect(indexesOfCompleteRows).toEqual([3, 4, 5]);
         });
 
-        describe("contains a multi row selection", function () {
-          it("returns an array of each individual row's index", function () {
+        describe("and also contains a selection that is not a row", function () {
+          it("returns an array of only the complete rows' indexes", function () {
             var multipleRowRange = [
+              new Slick.Range(8, 1, 9, 1),
               new Slick.Range(3, 0, 5, 1)
             ];
 
@@ -72,22 +84,8 @@ define([
             indexesOfCompleteRows.sort();
             expect(indexesOfCompleteRows).toEqual([3, 4, 5]);
           });
-
-          describe("and also contains a selection that is not a row", function () {
-            it("returns an array of only the complete rows' indexes", function () {
-              var multipleRowRange = [
-                new Slick.Range(8, 1, 9, 1),
-                new Slick.Range(3, 0, 5, 1)
-              ];
-
-              var indexesOfCompleteRows = RangeSelectionHelper.getIndexesOfCompleteRows(grid, multipleRowRange);
-              indexesOfCompleteRows.sort();
-              expect(indexesOfCompleteRows).toEqual([3, 4, 5]);
-            });
-          });
         });
       });
     });
   });
-
 });
diff --git a/web/regression/javascript/selection/row_selector_spec.js b/web/regression/javascript/selection/row_selector_spec.js
index 23a5b12e..dbf65e26 100644
--- a/web/regression/javascript/selection/row_selector_spec.js
+++ b/web/regression/javascript/selection/row_selector_spec.js
@@ -1,321 +1,327 @@
-define(
-  ["jquery",
-    "underscore",
-    "slickgrid/slick.grid",
-    "sources/selection/active_cell_capture",
-    "sources/selection/row_selector",
-    'sources/selection/xcell_selection_model',
-
-    "slickgrid",
-    'sources/slickgrid/pgslick.cellrangedecorator',
-    'sources/slickgrid/pgslick.cellrangeselector',
-  ],
-  function ($, _, SlickGrid, ActiveCellCapture, RowSelector, XCellSelectionModel, Slick) {
-    var KEY_RIGHT = 39;
-    var KEY_LEFT = 37;
-    var KEY_UP = 38;
-    var KEY_DOWN = 40;
-    describe("RowSelector", function () {
-      var container, data, columnDefinitions, grid, cellSelectionModel;
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+import $ from 'jquery';
+import _ from 'underscore';
+
+import Slick from 'slickgrid';
+import 'slickgrid.grid';
+
+import RowSelector from 'sources/selection/row_selector';
+import ActiveCellCapture from 'sources/selection/active_cell_capture';
+import XCellSelectionModel from 'sources/selection/xcell_selection_model';
+
+describe('RowSelector', function () {
+  var KEY_RIGHT = 39;
+  var KEY_LEFT = 37;
+  var KEY_UP = 38;
+  var KEY_DOWN = 40;
+  var container, data, columnDefinitions, grid, cellSelectionModel;
+  var SlickGrid = Slick.Grid;
+
+  beforeEach(function () {
+    container = $('<div></div>');
+    container.height(9999);
+    container.width(9999);
+
+    columnDefinitions = [{
+      id: '1',
+      name: 'some-column-name',
+      selectable: true,
+      pos: 0
+    }, {
+      id: '2',
+      name: 'second column',
+      selectable: true,
+      pos: 1
+    }];
+
+    var rowSelector = new RowSelector();
+    data = [];
+    for (var i = 0; i < 10; i++) {
+      data.push(['some-value-' + i, 'second value ' + i]);
+    }
+    columnDefinitions = rowSelector.getColumnDefinitions(columnDefinitions);
+    grid = new SlickGrid(container, data, columnDefinitions);
 
-      beforeEach(function () {
-        container = $("<div></div>");
-        container.height(9999);
-        container.width(9999);
-
-        columnDefinitions = [{
-          id: '1',
-          name: 'some-column-name',
-          selectable: true,
-          pos: 0
-        }, {
-          id: '2',
-          name: 'second column',
-          selectable: true,
-          pos: 1
-        }];
-
-        var rowSelector = new RowSelector();
-        data = [];
-        for (var i = 0; i < 10; i++) {
-          data.push(['some-value-' + i, 'second value ' + i]);
-        }
-        columnDefinitions = rowSelector.getColumnDefinitions(columnDefinitions);
-        grid = new SlickGrid(container, data, columnDefinitions);
-
-        grid.registerPlugin(new ActiveCellCapture());
-        cellSelectionModel = new XCellSelectionModel();
-        grid.setSelectionModel(cellSelectionModel);
-
-        grid.registerPlugin(rowSelector);
-        grid.invalidate();
-
-        $("body").append(container);
+    grid.registerPlugin(new ActiveCellCapture());
+    cellSelectionModel = new XCellSelectionModel();
+    grid.setSelectionModel(cellSelectionModel);
+
+    grid.registerPlugin(rowSelector);
+    grid.invalidate();
+
+    $('body').append(container);
+  });
+
+  afterEach(function () {
+    $('body').find(container).remove();
+  });
+
+  it('renders an additional column on the left', function () {
+    expect(columnDefinitions.length).toBe(3);
+
+    var leftmostColumn = columnDefinitions[0];
+    expect(leftmostColumn.id).toBe('row-header-column');
+    expect(leftmostColumn.name).toBe('');
+    expect(leftmostColumn.selectable).toBe(false);
+  });
+
+  it('renders a span on the leftmost column', function () {
+    expect(container.find('.slick-row').length).toBe(10);
+    expect(container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]').length).toBe(10);
+  });
+
+  it('preserves the other attributes of column definitions', function () {
+    expect(columnDefinitions[1].id).toBe('1');
+    expect(columnDefinitions[1].selectable).toBe(true);
+  });
+
+  describe('selecting rows', function () {
+    describe('when the user clicks a row header span', function () {
+      it('selects the row', function () {
+        container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[0].click();
+
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
+        expectOnlyTheFirstRowToBeSelected(selectedRanges);
       });
 
-      afterEach(function () {
-        $("body").find(container).remove();
+      it('add selected class to parent of the span', function () {
+        container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[5].click();
+
+        expect($(container.find('.slick-row .slick-cell:first-child ')[5])
+          .hasClass('selected')).toBeTruthy();
       });
+    });
 
-      it("renders an additional column on the left", function () {
-        expect(columnDefinitions.length).toBe(3);
+    describe('when the user clicks a row header', function () {
+      beforeEach(function () {
+        container.find('.slick-row .slick-cell:first-child')[1].click();
 
-        var leftmostColumn = columnDefinitions[0];
-        expect(leftmostColumn.id).toBe('row-header-column');
-        expect(leftmostColumn.name).toBe('');
-        expect(leftmostColumn.selectable).toBe(false);
       });
+      it('selects the row', function () {
 
-      it("renders a span on the leftmost column", function () {
-        expect(container.find('.slick-row').length).toBe(10);
-        expect(container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]').length).toBe(10);
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
+        var row = selectedRanges[0];
+
+        expect(selectedRanges.length).toEqual(1);
+        expect(row.fromCell).toBe(1);
+        expect(row.toCell).toBe(2);
+        expect(row.fromRow).toBe(1);
+        expect(row.toRow).toBe(1);
       });
 
-      it("preserves the other attributes of column definitions", function () {
-        expect(columnDefinitions[1].id).toBe('1');
-        expect(columnDefinitions[1].selectable).toBe(true);
+      it('add selected class to parent of the span', function () {
+
+        expect($(container.find('.slick-row .slick-cell:first-child ')[1])
+          .hasClass('selected')).toBeTruthy();
       });
 
-      describe("selecting rows", function () {
-        describe("when the user clicks a row header span", function () {
-          it("selects the row", function () {
-            container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[0].click();
+      describe('when the user clicks again the same row header', function () {
+        it('add selected class to parent of the span', function () {
+          container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[1].click();
 
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
-            expectOnlyTheFirstRowToBeSelected(selectedRanges);
-          });
+          expect($(container.find('.slick-row .slick-cell:first-child ')[1])
+            .hasClass('selected')).toBeFalsy();
+        });
+      });
 
-          it("add selected class to parent of the span", function () {
-            container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[5].click();
+      describe('and presses shift + down-arrow', function () {
+        beforeEach(function () {
+          pressShiftArrow(KEY_DOWN);
+        });
 
-            expect($(container.find('.slick-row .slick-cell:first-child ')[5])
-              .hasClass('selected')).toBeTruthy();
-          });
+        it('keeps the last row selected', function () {
+          expect(cellSelectionModel.getSelectedRanges().length).toBe(1);
         });
 
-        describe("when the user clicks a row header", function () {
-          beforeEach(function () {
-            container.find('.slick-row .slick-cell:first-child')[1].click();
+        it('grows the selection down', function () {
+          var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-          });
-          it("selects the row", function () {
+          var row = selectedRanges[0];
 
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
-            var row = selectedRanges[0];
+          expect(selectedRanges.length).toEqual(1);
+          expect(row.fromCell).toBe(1);
+          expect(row.toCell).toBe(2);
+          expect(row.fromRow).toBe(1);
+          expect(row.toRow).toBe(2);
+        });
 
-            expect(selectedRanges.length).toEqual(1);
-            expect(row.fromCell).toBe(1);
-            expect(row.toCell).toBe(2);
-            expect(row.fromRow).toBe(1);
-            expect(row.toRow).toBe(1);
-          });
+        it('keeps selected class on rows 1 and 2', function () {
+          expect($(container.find('.slick-row .slick-cell:first-child ')[0])
+            .hasClass('selected')).toBeFalsy();
+          expect($(container.find('.slick-row .slick-cell:first-child ')[1])
+            .hasClass('selected')).toBeTruthy();
+          expect($(container.find('.slick-row .slick-cell:first-child ')[2])
+            .hasClass('selected')).toBeTruthy();
+          expect($(container.find('.slick-row .slick-cell:first-child ')[3])
+            .hasClass('selected')).toBeFalsy();
+        });
+      });
 
-          it("add selected class to parent of the span", function () {
-
-            expect($(container.find('.slick-row .slick-cell:first-child ')[1])
-              .hasClass('selected')).toBeTruthy();
-          });
-
-          describe("when the user clicks again the same row header", function () {
-            it("add selected class to parent of the span", function () {
-              container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[1].click();
-
-              expect($(container.find('.slick-row .slick-cell:first-child ')[1])
-                .hasClass('selected')).toBeFalsy();
-            });
-          });
-
-          describe("and presses shift + down-arrow", function () {
-            beforeEach(function () {
-              pressShiftArrow(KEY_DOWN);
-            });
-
-            it("keeps the last row selected", function () {
-              expect(cellSelectionModel.getSelectedRanges().length).toBe(1);
-            });
-
-            it("grows the selection down", function () {
-              var selectedRanges = cellSelectionModel.getSelectedRanges();
-
-              var row = selectedRanges[0];
-
-              expect(selectedRanges.length).toEqual(1);
-              expect(row.fromCell).toBe(1);
-              expect(row.toCell).toBe(2);
-              expect(row.fromRow).toBe(1);
-              expect(row.toRow).toBe(2);
-            });
-
-            it("keeps selected class on rows 1 and 2", function () {
-              expect($(container.find('.slick-row .slick-cell:first-child ')[0])
-                .hasClass('selected')).toBeFalsy();
-              expect($(container.find('.slick-row .slick-cell:first-child ')[1])
-                .hasClass('selected')).toBeTruthy();
-              expect($(container.find('.slick-row .slick-cell:first-child ')[2])
-                .hasClass('selected')).toBeTruthy();
-              expect($(container.find('.slick-row .slick-cell:first-child ')[3])
-                .hasClass('selected')).toBeFalsy();
-            });
-          });
-
-          describe('when the user clicks a cell on the current range', function () {
-            beforeEach(function () {
-              container.find('.slick-cell.l1.r1')[5].click();
-            });
-
-            it('row gets deselected', function () {
-
-              var selectedRanges = cellSelectionModel.getSelectedRanges();
-
-              expect(selectedRanges.length).toBe(1);
-
-              var newSelection = selectedRanges[0];
-
-              expect(newSelection.fromCell).toBe(1);
-              expect(newSelection.fromRow).toBe(5);
-              expect(newSelection.toCell).toBe(1);
-              expect(newSelection.toRow).toBe(5);
-            });
-
-            it('keep select class on row header', function () {
-              expect($(container.find('.slick-cell.l0.r0')[5]).hasClass('selected'))
-                .toBeTruthy();
-            });
-          });
-
-          describe('when the user clicks a cell outside the current range', function () {
-            beforeEach(function () {
-              container.find('.slick-cell.l2.r2')[2].click();
-            });
-
-            it('row gets deselected', function () {
-
-              var selectedRanges = cellSelectionModel.getSelectedRanges();
-
-              expect(selectedRanges.length).toBe(1);
-
-              var newSelection = selectedRanges[0];
-
-              expect(newSelection.fromCell).toBe(2);
-              expect(newSelection.fromRow).toBe(2);
-              expect(newSelection.toCell).toBe(2);
-              expect(newSelection.toRow).toBe(2);
-            });
-
-            it('remove select class on "some-column-name" column header', function () {
-              expect($(container.find('.slick-cell.l0.r0')[5]).hasClass('selected'))
-                .toBeFalsy();
-              expect($(container.find('.slick-cell.l0.r0')[2]).hasClass('selected'))
-                .toBeTruthy();
-            });
-          });
-
-          describe('when the user has a column selected', function () {
-            beforeEach(function () {
-              var selectedRanges = [new Slick.Range(0, 1, 9, 1)];
-              cellSelectionModel.setSelectedRanges(selectedRanges);
-            });
-
-            it('no row should have the class "selected"', function () {
-              expect($(container.find('.slick-cell.l0.r0')[0]).hasClass('selected'))
-                .toBeFalsy();
-              expect($(container.find('.slick-cell.l0.r0')[1]).hasClass('selected'))
-                .toBeFalsy();
-              expect($(container.find('.slick-cell.l0.r0')[2]).hasClass('selected'))
-                .toBeFalsy();
-              expect($(container.find('.slick-cell.l0.r0')[3]).hasClass('selected'))
-                .toBeFalsy();
-              expect($(container.find('.slick-cell.l0.r0')[4]).hasClass('selected'))
-                .toBeFalsy();
-              expect($(container.find('.slick-cell.l0.r0')[5]).hasClass('selected'))
-                .toBeFalsy();
-            });
-          });
+      describe('when the user clicks a cell on the current range', function () {
+        beforeEach(function () {
+          container.find('.slick-cell.l1.r1')[5].click();
         });
 
-        describe("when the user clicks multiple row headers", function () {
-          it("selects another row", function () {
-            container.find('.slick-row .slick-cell:first-child')[4].click();
-            container.find('.slick-row .slick-cell:first-child')[0].click();
+        it('row gets deselected', function () {
+
+          var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
-            expect(selectedRanges.length).toEqual(2);
+          expect(selectedRanges.length).toBe(1);
 
-            var row1 = selectedRanges[0];
-            expect(row1.fromRow).toBe(4);
-            expect(row1.toRow).toBe(4);
+          var newSelection = selectedRanges[0];
+
+          expect(newSelection.fromCell).toBe(1);
+          expect(newSelection.fromRow).toBe(5);
+          expect(newSelection.toCell).toBe(1);
+          expect(newSelection.toRow).toBe(5);
+        });
+
+        it('keep select class on row header', function () {
+          expect($(container.find('.slick-cell.l0.r0')[5]).hasClass('selected'))
+            .toBeTruthy();
+        });
+      });
 
-            var row2 = selectedRanges[1];
-            expect(row2.fromRow).toBe(0);
-            expect(row2.toRow).toBe(0);
-          });
+      describe('when the user clicks a cell outside the current range', function () {
+        beforeEach(function () {
+          container.find('.slick-cell.l2.r2')[2].click();
         });
 
-        describe("when a column was already selected", function () {
-          beforeEach(function () {
-            var selectedRanges = [new Slick.Range(0, 0, 0, 1)];
-            cellSelectionModel.setSelectedRanges(selectedRanges);
-          });
+        it('row gets deselected', function () {
 
-          it("deselects the column", function () {
-            container.find('.slick-row .slick-cell:first-child')[0].click();
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
+          var selectedRanges = cellSelectionModel.getSelectedRanges();
 
-            expectOnlyTheFirstRowToBeSelected(selectedRanges);
-          });
+          expect(selectedRanges.length).toBe(1);
+
+          var newSelection = selectedRanges[0];
+
+          expect(newSelection.fromCell).toBe(2);
+          expect(newSelection.fromRow).toBe(2);
+          expect(newSelection.toCell).toBe(2);
+          expect(newSelection.toRow).toBe(2);
         });
 
-        describe("when the row is deselected through setSelectedRanges", function () {
-          beforeEach(function () {
-            container.find('.slick-row .slick-cell:first-child')[4].click();
-          });
+        it('remove select class on "some-column-name" column header', function () {
+          expect($(container.find('.slick-cell.l0.r0')[5]).hasClass('selected'))
+            .toBeFalsy();
+          expect($(container.find('.slick-cell.l0.r0')[2]).hasClass('selected'))
+            .toBeTruthy();
+        });
+      });
 
-          it("should remove the selected class", function () {
-            cellSelectionModel.setSelectedRanges([]);
+      describe('when the user has a column selected', function () {
+        beforeEach(function () {
+          var selectedRanges = [new Slick.Range(0, 1, 9, 1)];
+          cellSelectionModel.setSelectedRanges(selectedRanges);
+        });
 
-            expect($(container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[4])
-              .hasClass('selected')).toBeFalsy();
-          });
+        it('no row should have the class "selected"', function () {
+          expect($(container.find('.slick-cell.l0.r0')[0]).hasClass('selected'))
+            .toBeFalsy();
+          expect($(container.find('.slick-cell.l0.r0')[1]).hasClass('selected'))
+            .toBeFalsy();
+          expect($(container.find('.slick-cell.l0.r0')[2]).hasClass('selected'))
+            .toBeFalsy();
+          expect($(container.find('.slick-cell.l0.r0')[3]).hasClass('selected'))
+            .toBeFalsy();
+          expect($(container.find('.slick-cell.l0.r0')[4]).hasClass('selected'))
+            .toBeFalsy();
+          expect($(container.find('.slick-cell.l0.r0')[5]).hasClass('selected'))
+            .toBeFalsy();
         });
+      });
+    });
 
-        describe("click a second time", function () {
-          beforeEach(function () {
-            container.find('.slick-row .slick-cell:first-child')[1].click();
-          });
+    describe('when the user clicks multiple row headers', function () {
+      it('selects another row', function () {
+        container.find('.slick-row .slick-cell:first-child')[4].click();
+        container.find('.slick-row .slick-cell:first-child')[0].click();
 
-          it("removes the selected class", function () {
-            container.find('.slick-row .slick-cell:first-child')[1].click();
-            expect($(container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[1])
-              .hasClass('selected')).toBeFalsy();
-          });
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
+        expect(selectedRanges.length).toEqual(2);
 
-          it("unselects the row", function () {
-            container.find('.slick-row .slick-cell:first-child')[1].click();
-            var selectedRanges = cellSelectionModel.getSelectedRanges();
+        var row1 = selectedRanges[0];
+        expect(row1.fromRow).toBe(4);
+        expect(row1.toRow).toBe(4);
 
-            expect(selectedRanges.length).toEqual(0);
-          })
-        });
+        var row2 = selectedRanges[1];
+        expect(row2.fromRow).toBe(0);
+        expect(row2.toRow).toBe(0);
       });
+    });
 
-      function pressShiftArrow(keyCode) {
-        var pressEvent = new $.Event("keydown");
-        pressEvent.shiftKey = true;
-        pressEvent.ctrlKey = false;
-        pressEvent.altKey = false;
-        pressEvent.which = keyCode;
+    describe('when a column was already selected', function () {
+      beforeEach(function () {
+        var selectedRanges = [new Slick.Range(0, 0, 0, 1)];
+        cellSelectionModel.setSelectedRanges(selectedRanges);
+      });
 
-        $(container.find('.grid-canvas')).trigger(pressEvent);
-      }
+      it('deselects the column', function () {
+        container.find('.slick-row .slick-cell:first-child')[0].click();
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
+
+        expectOnlyTheFirstRowToBeSelected(selectedRanges);
+      });
     });
 
-    function expectOnlyTheFirstRowToBeSelected(selectedRanges) {
-      var row = selectedRanges[0];
+    describe('when the row is deselected through setSelectedRanges', function () {
+      beforeEach(function () {
+        container.find('.slick-row .slick-cell:first-child')[4].click();
+      });
 
-      expect(selectedRanges.length).toEqual(1);
-      expect(row.fromCell).toBe(1);
-      expect(row.toCell).toBe(2);
-      expect(row.fromRow).toBe(0);
-      expect(row.toRow).toBe(0);
-    }
-  });
\ No newline at end of file
+      it('should remove the selected class', function () {
+        cellSelectionModel.setSelectedRanges([]);
+
+        expect($(container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[4])
+          .hasClass('selected')).toBeFalsy();
+      });
+    });
+
+    describe('click a second time', function () {
+      beforeEach(function () {
+        container.find('.slick-row .slick-cell:first-child')[1].click();
+      });
+
+      it('removes the selected class', function () {
+        container.find('.slick-row .slick-cell:first-child')[1].click();
+        expect($(container.find('.slick-row .slick-cell:first-child span[data-cell-type="row-header-selector"]')[1])
+          .hasClass('selected')).toBeFalsy();
+      });
+
+      it('unselects the row', function () {
+        container.find('.slick-row .slick-cell:first-child')[1].click();
+        var selectedRanges = cellSelectionModel.getSelectedRanges();
+
+        expect(selectedRanges.length).toEqual(0);
+      })
+    });
+  });
+
+  function pressShiftArrow(keyCode) {
+    var pressEvent = new $.Event('keydown');
+    pressEvent.shiftKey = true;
+    pressEvent.ctrlKey = false;
+    pressEvent.altKey = false;
+    pressEvent.which = keyCode;
+
+    $(container.find('.grid-canvas')).trigger(pressEvent);
+  }
+
+  function expectOnlyTheFirstRowToBeSelected(selectedRanges) {
+    var row = selectedRanges[0];
+
+    expect(selectedRanges.length).toEqual(1);
+    expect(row.fromCell).toBe(1);
+    expect(row.toCell).toBe(2);
+    expect(row.fromRow).toBe(0);
+    expect(row.toRow).toBe(0);
+  }
+});
diff --git a/web/regression/javascript/selection/xcell_selection_model_spec.js b/web/regression/javascript/selection/xcell_selection_model_spec.js
index 1682c244..8f01035c 100644
--- a/web/regression/javascript/selection/xcell_selection_model_spec.js
+++ b/web/regression/javascript/selection/xcell_selection_model_spec.js
@@ -7,507 +7,505 @@
 //
 //////////////////////////////////////////////////////////////////////////
 
-define([
-  'sources/selection/xcell_selection_model',
-  "slickgrid/slick.grid",
-  'slickgrid',
-], function (XCellSelectionModel, SlickGrid, Slick) {
-  describe('XCellSelectionModel', function () {
-    var KEY_RIGHT = 39;
-    var KEY_LEFT = 37;
-    var KEY_UP = 38;
-    var KEY_DOWN = 40;
-
-    var container, grid;
-    var oldWindowParent = window.parent;
+import XCellSelectionModel from 'sources/selection/xcell_selection_model';
+import 'slickgrid.grid';
+import Slick from 'slickgrid';
+
+describe('XCellSelectionModel', function () {
+  var KEY_RIGHT = 39;
+  var KEY_LEFT = 37;
+  var KEY_UP = 38;
+  var KEY_DOWN = 40;
+
+  var container, grid;
+  var SlickGrid = Slick.Grid;
+  var oldWindowParent = window.parent;
+
+  beforeEach(function () {
+    window.parent = window;
+
+    var columns = [{
+      id: 'row-header-column',
+      name: 'row header column name',
+      selectable: false,
+    }, {
+      id: '1',
+      name: 'some-column-name',
+      pos: 0
+    }, {
+      id: 'second-column-id',
+      name: 'second column',
+      pos: 1
+    }, {
+      id: 'third-column-id',
+      name: 'third column',
+      pos: 2
+    },
+    ];
+
+    var data = [];
+    for (var i = 0; i < 10; i++) {
+      data.push({
+        'some-column-name': 'some-value-' + i,
+        'second column': 'second value ' + i,
+        'third column': 'third value ' + i,
+        'fourth column': 'fourth value ' + i,
+      });
+    }
+    container = $("<div></div>");
+    container.height(9999);
+    container.width(9999);
 
-    beforeEach(function () {
-      window.parent = window;
-
-      var columns = [{
-        id: 'row-header-column',
-        name: 'row header column name',
-        selectable: false,
-      }, {
-        id: '1',
-        name: 'some-column-name',
-        pos: 0
-      }, {
-        id: 'second-column-id',
-        name: 'second column',
-        pos: 1
-      }, {
-        id: 'third-column-id',
-        name: 'third column',
-        pos: 2
-      },
-      ];
-
-      var data = [];
-      for (var i = 0; i < 10; i++) {
-        data.push({
-          'some-column-name': 'some-value-' + i,
-          'second column': 'second value ' + i,
-          'third column': 'third value ' + i,
-          'fourth column': 'fourth value ' + i,
-        });
-      }
-      container = $("<div></div>");
-      container.height(9999);
-      container.width(9999);
-
-      grid = new SlickGrid(container, data, columns);
-      grid.setSelectionModel(new XCellSelectionModel());
-      $("body").append(container);
-    });
+    grid = new SlickGrid(container, data, columns);
+    grid.setSelectionModel(new XCellSelectionModel());
+    $("body").append(container);
+  });
 
-    afterEach(function () {
-      grid.destroy();
-      container.remove();
-      window.parent = oldWindowParent;
-    });
+  afterEach(function () {
+    grid.destroy();
+    container.remove();
+    window.parent = oldWindowParent;
+  });
 
-    describe('handleKeyDown', function () {
-      describe('when we press a random key', function () {
-        it('should not change the range', function () {
-          var range = new Slick.Range(1, 2);
-          grid.setActiveCell(1, 2);
-          grid.getSelectionModel().setSelectedRanges([range]);
-          pressKey(72);
+  describe('handleKeyDown', function () {
+    describe('when we press a random key', function () {
+      it('should not change the range', function () {
+        var range = new Slick.Range(1, 2);
+        grid.setActiveCell(1, 2);
+        grid.getSelectionModel().setSelectedRanges([range]);
+        pressKey(72);
 
-          expect(grid.getSelectionModel().getSelectedRanges()[0]).toEqual(range);
-        });
+        expect(grid.getSelectionModel().getSelectedRanges()[0]).toEqual(range);
       });
+    });
 
-      describe('when we press an arrow key ', function () {
-        it('should select the cell to the right', function () {
-          var range = new Slick.Range(1, 2);
-          grid.setActiveCell(1, 2);
-          grid.getSelectionModel().setSelectedRanges([range]);
-          pressKey(KEY_RIGHT);
+    describe('when we press an arrow key ', function () {
+      it('should select the cell to the right', function () {
+        var range = new Slick.Range(1, 2);
+        grid.setActiveCell(1, 2);
+        grid.getSelectionModel().setSelectedRanges([range]);
+        pressKey(KEY_RIGHT);
 
-          expectOneSelectedRange(1, 3, 1, 3);
-        });
+        expectOneSelectedRange(1, 3, 1, 3);
       });
+    });
 
-      describe('when we press shift', function () {
-        describe('and we press an arrow key', function () {
-          var scrollColumnIntoViewSpy, scrollRowIntoViewSpy;
+    describe('when we press shift', function () {
+      describe('and we press an arrow key', function () {
+        var scrollColumnIntoViewSpy, scrollRowIntoViewSpy;
 
-          beforeEach(function () {
-            scrollColumnIntoViewSpy = spyOn(grid, 'scrollColumnIntoView');
-            scrollRowIntoViewSpy = spyOn(grid, 'scrollRowIntoView');
-          });
+        beforeEach(function () {
+          scrollColumnIntoViewSpy = spyOn(grid, 'scrollColumnIntoView');
+          scrollRowIntoViewSpy = spyOn(grid, 'scrollRowIntoView');
+        });
 
-          describe('the right arrow', function () {
-            describe('when a cell is selected', function () {
-              beforeEach(function () {
-                var range = new Slick.Range(1, 1);
-                grid.setActiveCell(1, 1);
-                grid.getSelectionModel().setSelectedRanges([range]);
-                pressShiftPlusKey(KEY_RIGHT);
-              });
+        describe('the right arrow', function () {
+          describe('when a cell is selected', function () {
+            beforeEach(function () {
+              var range = new Slick.Range(1, 1);
+              grid.setActiveCell(1, 1);
+              grid.getSelectionModel().setSelectedRanges([range]);
+              pressShiftPlusKey(KEY_RIGHT);
+            });
 
-              it('increases the range by one to the right', function () {
-                expectOneSelectedRange(1, 1, 1, 2);
-              });
+            it('increases the range by one to the right', function () {
+              expectOneSelectedRange(1, 1, 1, 2);
+            });
 
-              it('should scroll the next column into view', function () {
-                expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(2);
-                expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
-              });
+            it('should scroll the next column into view', function () {
+              expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(2);
+              expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
+            });
 
-              it('pressing right again grows the range right', function () {
-                pressShiftPlusKey(KEY_RIGHT);
+            it('pressing right again grows the range right', function () {
+              pressShiftPlusKey(KEY_RIGHT);
 
-                expectOneSelectedRange(1, 1, 1, 3);
-              });
+              expectOneSelectedRange(1, 1, 1, 3);
+            });
 
-              it('then pressing left keeps the original selection', function () {
-                pressShiftPlusKey(KEY_LEFT);
+            it('then pressing left keeps the original selection', function () {
+              pressShiftPlusKey(KEY_LEFT);
 
-                expectOneSelectedRange(1, 1, 1, 1);
-              });
+              expectOneSelectedRange(1, 1, 1, 1);
             });
+          });
 
-            describe('when a column is selected', function () {
-              beforeEach(function () {
-                var range = new Slick.Range(0, 1, 9, 1);
-                grid.setActiveCell(0, 1);
-                grid.getSelectionModel().setSelectedRanges([range]);
-                pressShiftPlusKey(KEY_RIGHT);
-              });
+          describe('when a column is selected', function () {
+            beforeEach(function () {
+              var range = new Slick.Range(0, 1, 9, 1);
+              grid.setActiveCell(0, 1);
+              grid.getSelectionModel().setSelectedRanges([range]);
+              pressShiftPlusKey(KEY_RIGHT);
+            });
 
-              it('increases the range by one column to the right', function () {
-                expectOneSelectedRange(0, 1, 9, 2);
-              });
+            it('increases the range by one column to the right', function () {
+              expectOneSelectedRange(0, 1, 9, 2);
+            });
 
-              it('should scroll the next column into view', function () {
-                expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(2);
-                expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
-              });
+            it('should scroll the next column into view', function () {
+              expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(2);
+              expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
             });
           });
+        });
 
-          describe('the left arrow', function () {
-            describe('when a cell is selected', function () {
-              beforeEach(function () {
-                var range = new Slick.Range(1, 3);
-                grid.setActiveCell(1, 3);
-                grid.getSelectionModel().setSelectedRanges([range]);
-                pressShiftPlusKey(KEY_LEFT);
-              });
+        describe('the left arrow', function () {
+          describe('when a cell is selected', function () {
+            beforeEach(function () {
+              var range = new Slick.Range(1, 3);
+              grid.setActiveCell(1, 3);
+              grid.getSelectionModel().setSelectedRanges([range]);
+              pressShiftPlusKey(KEY_LEFT);
+            });
 
-              it('increases the range by one to the left', function () {
-                expectOneSelectedRange(1, 2, 1, 3);
-              });
+            it('increases the range by one to the left', function () {
+              expectOneSelectedRange(1, 2, 1, 3);
+            });
 
-              it('should scroll previous column into view', function () {
-                expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(2);
-                expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
-              });
+            it('should scroll previous column into view', function () {
+              expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(2);
+              expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
+            });
 
-              it('pressing left again grows the range the left', function () {
-                pressShiftPlusKey(KEY_LEFT);
+            it('pressing left again grows the range the left', function () {
+              pressShiftPlusKey(KEY_LEFT);
 
-                expectOneSelectedRange(1, 1, 1, 3);
-              });
+              expectOneSelectedRange(1, 1, 1, 3);
+            });
 
-              it('then pressing right keeps the original selection', function () {
-                pressShiftPlusKey(KEY_RIGHT);
+            it('then pressing right keeps the original selection', function () {
+              pressShiftPlusKey(KEY_RIGHT);
 
-                expectOneSelectedRange(1, 3, 1, 3);
-              });
+              expectOneSelectedRange(1, 3, 1, 3);
             });
+          });
 
-            describe('when a column is selected', function () {
-              beforeEach(function () {
-                var range = new Slick.Range(0, 2, 9, 2);
-                grid.setActiveCell(0, 2);
-                grid.getSelectionModel().setSelectedRanges([range]);
-                pressShiftPlusKey(KEY_LEFT);
-              });
+          describe('when a column is selected', function () {
+            beforeEach(function () {
+              var range = new Slick.Range(0, 2, 9, 2);
+              grid.setActiveCell(0, 2);
+              grid.getSelectionModel().setSelectedRanges([range]);
+              pressShiftPlusKey(KEY_LEFT);
+            });
 
-              it('increases the range by one column to the left', function () {
-                expectOneSelectedRange(0, 1, 9, 2);
-              });
+            it('increases the range by one column to the left', function () {
+              expectOneSelectedRange(0, 1, 9, 2);
+            });
 
-              it('should scroll previous column into view', function () {
-                expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(1);
-                expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
-              });
+            it('should scroll previous column into view', function () {
+              expect(scrollColumnIntoViewSpy).toHaveBeenCalledWith(1);
+              expect(scrollRowIntoViewSpy).not.toHaveBeenCalled();
             });
           });
+        });
 
-          describe('the up arrow', function () {
-            describe('when a cell is selected', function () {
-              beforeEach(function () {
-                var range = new Slick.Range(2, 2);
-                grid.setActiveCell(2, 2);
-                grid.getSelectionModel().setSelectedRanges([range]);
-                pressShiftPlusKey(KEY_UP);
-              });
+        describe('the up arrow', function () {
+          describe('when a cell is selected', function () {
+            beforeEach(function () {
+              var range = new Slick.Range(2, 2);
+              grid.setActiveCell(2, 2);
+              grid.getSelectionModel().setSelectedRanges([range]);
+              pressShiftPlusKey(KEY_UP);
+            });
 
-              it('increases the range by one up', function () {
-                expectOneSelectedRange(1, 2, 2, 2);
-              });
+            it('increases the range by one up', function () {
+              expectOneSelectedRange(1, 2, 2, 2);
+            });
 
-              it('should scroll the row above into view', function () {
-                expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(1);
-                expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
-              });
+            it('should scroll the row above into view', function () {
+              expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(1);
+              expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
+            });
 
-              it('pressing up again grows the range up', function () {
-                pressShiftPlusKey(KEY_UP);
+            it('pressing up again grows the range up', function () {
+              pressShiftPlusKey(KEY_UP);
 
-                expectOneSelectedRange(0, 2, 2, 2);
-              });
+              expectOneSelectedRange(0, 2, 2, 2);
+            });
 
-              it('then pressing down keeps the original selection', function () {
-                pressShiftPlusKey(KEY_DOWN);
+            it('then pressing down keeps the original selection', function () {
+              pressShiftPlusKey(KEY_DOWN);
 
-                expectOneSelectedRange(2, 2, 2, 2);
-              });
+              expectOneSelectedRange(2, 2, 2, 2);
             });
+          });
 
-            describe('when a row is selected', function () {
-              beforeEach(function () {
-                var range = new Slick.Range(2, 1, 2, 3);
-                grid.setActiveCell(2, 1);
-                grid.getSelectionModel().setSelectedRanges([range]);
-                pressShiftPlusKey(KEY_UP);
-              });
+          describe('when a row is selected', function () {
+            beforeEach(function () {
+              var range = new Slick.Range(2, 1, 2, 3);
+              grid.setActiveCell(2, 1);
+              grid.getSelectionModel().setSelectedRanges([range]);
+              pressShiftPlusKey(KEY_UP);
+            });
 
-              it('increases the range by one row up', function () {
-                expectOneSelectedRange(1, 1, 2, 3);
-              });
+            it('increases the range by one row up', function () {
+              expectOneSelectedRange(1, 1, 2, 3);
+            });
 
-              it('should scroll the row above into view', function () {
-                expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(1);
-                expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
-              });
+            it('should scroll the row above into view', function () {
+              expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(1);
+              expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
             });
           });
+        });
 
-          describe('the down arrow', function () {
-            describe('when a cell is selected', function () {
-              beforeEach(function () {
-                var range = new Slick.Range(2, 2);
-                grid.setActiveCell(2, 2);
-                grid.getSelectionModel().setSelectedRanges([range]);
-                pressShiftPlusKey(KEY_DOWN);
-              });
+        describe('the down arrow', function () {
+          describe('when a cell is selected', function () {
+            beforeEach(function () {
+              var range = new Slick.Range(2, 2);
+              grid.setActiveCell(2, 2);
+              grid.getSelectionModel().setSelectedRanges([range]);
+              pressShiftPlusKey(KEY_DOWN);
+            });
 
-              it('increases the range by one down', function () {
-                expectOneSelectedRange(2, 2, 3, 2);
-              });
+            it('increases the range by one down', function () {
+              expectOneSelectedRange(2, 2, 3, 2);
+            });
 
-              it('should scroll the row below into view', function () {
-                expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(3);
-                expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
-              });
+            it('should scroll the row below into view', function () {
+              expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(3);
+              expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
+            });
 
-              it('pressing down again grows the range down', function () {
-                pressShiftPlusKey(KEY_DOWN);
+            it('pressing down again grows the range down', function () {
+              pressShiftPlusKey(KEY_DOWN);
 
-                expectOneSelectedRange(2, 2, 4, 2);
-              });
+              expectOneSelectedRange(2, 2, 4, 2);
+            });
 
-              it('then pressing up keeps the original selection', function () {
-                pressShiftPlusKey(KEY_UP);
+            it('then pressing up keeps the original selection', function () {
+              pressShiftPlusKey(KEY_UP);
 
-                expectOneSelectedRange(2, 2, 2, 2);
-              });
+              expectOneSelectedRange(2, 2, 2, 2);
             });
+          });
 
-            describe('when a row is selected', function () {
-              beforeEach(function () {
-                var range = new Slick.Range(2, 1, 2, 3);
-                grid.setActiveCell(2, 1);
-                grid.getSelectionModel().setSelectedRanges([range]);
-                pressShiftPlusKey(KEY_DOWN);
-              });
+          describe('when a row is selected', function () {
+            beforeEach(function () {
+              var range = new Slick.Range(2, 1, 2, 3);
+              grid.setActiveCell(2, 1);
+              grid.getSelectionModel().setSelectedRanges([range]);
+              pressShiftPlusKey(KEY_DOWN);
+            });
 
-              it('increases the range by one row down', function () {
-                expectOneSelectedRange(2, 1, 3, 3);
-              });
+            it('increases the range by one row down', function () {
+              expectOneSelectedRange(2, 1, 3, 3);
+            });
 
-              it('should scroll the row below into view', function () {
-                expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(3);
-                expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
-              });
+            it('should scroll the row below into view', function () {
+              expect(scrollRowIntoViewSpy).toHaveBeenCalledWith(3);
+              expect(scrollColumnIntoViewSpy).not.toHaveBeenCalled();
             });
           });
+        });
 
-          describe('rectangular selection works', function () {
+        describe('rectangular selection works', function () {
 
-            it('in the down-and-rightward direction', function () {
-              var range = new Slick.Range(1, 1);
-              grid.setActiveCell(1, 1);
-              grid.getSelectionModel().setSelectedRanges([range]);
+          it('in the down-and-rightward direction', function () {
+            var range = new Slick.Range(1, 1);
+            grid.setActiveCell(1, 1);
+            grid.getSelectionModel().setSelectedRanges([range]);
 
-              pressShiftPlusKey(KEY_DOWN);
-              pressShiftPlusKey(KEY_DOWN);
-              pressShiftPlusKey(KEY_DOWN);
-              pressShiftPlusKey(KEY_RIGHT);
-              pressShiftPlusKey(KEY_RIGHT);
+            pressShiftPlusKey(KEY_DOWN);
+            pressShiftPlusKey(KEY_DOWN);
+            pressShiftPlusKey(KEY_DOWN);
+            pressShiftPlusKey(KEY_RIGHT);
+            pressShiftPlusKey(KEY_RIGHT);
 
-              expectOneSelectedRange(1, 1, 4, 3);
-            });
+            expectOneSelectedRange(1, 1, 4, 3);
+          });
 
-            it('in the up-and-leftward direction', function () {
-              var range = new Slick.Range(4, 3);
-              grid.setActiveCell(4, 3);
-              grid.getSelectionModel().setSelectedRanges([range]);
+          it('in the up-and-leftward direction', function () {
+            var range = new Slick.Range(4, 3);
+            grid.setActiveCell(4, 3);
+            grid.getSelectionModel().setSelectedRanges([range]);
 
-              pressShiftPlusKey(KEY_UP);
-              pressShiftPlusKey(KEY_UP);
-              pressShiftPlusKey(KEY_UP);
-              pressShiftPlusKey(KEY_LEFT);
-              pressShiftPlusKey(KEY_LEFT);
+            pressShiftPlusKey(KEY_UP);
+            pressShiftPlusKey(KEY_UP);
+            pressShiftPlusKey(KEY_UP);
+            pressShiftPlusKey(KEY_LEFT);
+            pressShiftPlusKey(KEY_LEFT);
 
-              expectOneSelectedRange(1, 1, 4, 3);
-            });
+            expectOneSelectedRange(1, 1, 4, 3);
+          });
 
-            it('in the up-and-rightward direction', function () {
-              var range = new Slick.Range(4, 1);
-              grid.setActiveCell(4, 1);
-              grid.getSelectionModel().setSelectedRanges([range]);
+          it('in the up-and-rightward direction', function () {
+            var range = new Slick.Range(4, 1);
+            grid.setActiveCell(4, 1);
+            grid.getSelectionModel().setSelectedRanges([range]);
 
-              pressShiftPlusKey(KEY_UP);
-              pressShiftPlusKey(KEY_UP);
-              pressShiftPlusKey(KEY_UP);
-              pressShiftPlusKey(KEY_RIGHT);
-              pressShiftPlusKey(KEY_RIGHT);
+            pressShiftPlusKey(KEY_UP);
+            pressShiftPlusKey(KEY_UP);
+            pressShiftPlusKey(KEY_UP);
+            pressShiftPlusKey(KEY_RIGHT);
+            pressShiftPlusKey(KEY_RIGHT);
 
-              expectOneSelectedRange(1, 1, 4, 3);
-            });
+            expectOneSelectedRange(1, 1, 4, 3);
+          });
 
-            it('in the down-and-leftward direction', function () {
-              var range = new Slick.Range(1, 3);
-              grid.setActiveCell(1, 3);
-              grid.getSelectionModel().setSelectedRanges([range]);
+          it('in the down-and-leftward direction', function () {
+            var range = new Slick.Range(1, 3);
+            grid.setActiveCell(1, 3);
+            grid.getSelectionModel().setSelectedRanges([range]);
 
-              pressShiftPlusKey(KEY_DOWN);
-              pressShiftPlusKey(KEY_DOWN);
-              pressShiftPlusKey(KEY_DOWN);
-              pressShiftPlusKey(KEY_LEFT);
-              pressShiftPlusKey(KEY_LEFT);
+            pressShiftPlusKey(KEY_DOWN);
+            pressShiftPlusKey(KEY_DOWN);
+            pressShiftPlusKey(KEY_DOWN);
+            pressShiftPlusKey(KEY_LEFT);
+            pressShiftPlusKey(KEY_LEFT);
 
-              expectOneSelectedRange(1, 1, 4, 3);
-            });
+            expectOneSelectedRange(1, 1, 4, 3);
           });
+        });
 
-          describe('and we are on an edge', function () {
-            var range;
+        describe('and we are on an edge', function () {
+          var range;
 
-            beforeEach(function () {
-              range = new Slick.Range(2, 1);
-              grid.setActiveCell(2, 1);
-              grid.getSelectionModel().setSelectedRanges([range]);
-            });
+          beforeEach(function () {
+            range = new Slick.Range(2, 1);
+            grid.setActiveCell(2, 1);
+            grid.getSelectionModel().setSelectedRanges([range]);
+          });
 
-            it('we still have the selected range before we arrowed', function () {
-              pressShiftPlusKey(KEY_LEFT);
-              expectOneSelectedRange(2, 1, 2, 1);
-            });
+          it('we still have the selected range before we arrowed', function () {
+            pressShiftPlusKey(KEY_LEFT);
+            expectOneSelectedRange(2, 1, 2, 1);
           });
         });
       });
     });
+  });
 
-    describe('when we drag and drop', function () {
-      var dd;
-      // We could not find an elegant way to calculate this value
-      // after changing window size we saw this was a constant value
-      var offsetLeftColumns = 100;
+  describe('when we drag and drop', function () {
+    var dd;
+    // We could not find an elegant way to calculate this value
+    // after changing window size we saw this was a constant value
+    var offsetLeftColumns = 100;
 
-      function cellTopPosition($cell, rowNumber) {
-        return $(grid.getCanvasNode()).offset().top + $cell[0].scrollHeight * rowNumber;
-      }
+    function cellTopPosition($cell, rowNumber) {
+      return $(grid.getCanvasNode()).offset().top + $cell[0].scrollHeight * rowNumber;
+    }
+
+    function cellLeftPosition(columnNumber) {
+      return $(grid.getCanvasNode()).offset().left + offsetLeftColumns * columnNumber;
+    }
 
-      function cellLeftPosition(columnNumber) {
-        return $(grid.getCanvasNode()).offset().left + offsetLeftColumns * columnNumber;
-      }
+    beforeEach(function () {
+      var initialPosition = {cell: 3, row: 4};
+      var $cell = $($('.slick-cell.l3')[initialPosition.row]);
+      var event = {
+        target: $cell,
+        isPropagationStopped: jasmine.createSpy('isPropagationStopped').and.returnValue(false),
+        isImmediatePropagationStopped: jasmine.createSpy('isImmediatePropagationStopped').and.returnValue(false),
+        stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation')
+      };
+
+      dd = {
+        grid: grid,
+        startX: cellLeftPosition(initialPosition.cell),
+        startY: cellTopPosition($cell, initialPosition.row)
+      };
+
+      grid.onDragStart.notify(dd, event, grid);
+    });
 
+    describe('when the drop happens outside of the grid', function () {
       beforeEach(function () {
-        var initialPosition = {cell: 3, row: 4};
-        var $cell = $($('.slick-cell.l3')[initialPosition.row]);
+        var $cell = $($('.slick-cell.l1')[1]);
+        var finalPosition = {cell: 1, row: 1};
+
         var event = {
           target: $cell,
           isPropagationStopped: jasmine.createSpy('isPropagationStopped').and.returnValue(false),
           isImmediatePropagationStopped: jasmine.createSpy('isImmediatePropagationStopped').and.returnValue(false),
-          stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation')
-        };
+          stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation'),
 
-        dd = {
-          grid: grid,
-          startX: cellLeftPosition(initialPosition.cell),
-          startY: cellTopPosition($cell, initialPosition.row)
+          pageX: cellLeftPosition(finalPosition.cell),
+          pageY: cellTopPosition($cell, finalPosition.row)
         };
 
-        grid.onDragStart.notify(dd, event, grid);
+        grid.onDrag.notify(dd, event, grid);
+        $(window).mouseup();
       });
+      it('should call handleDragEnd from CellRangeSelector', function () {
+        var newRange = grid.getSelectionModel().getSelectedRanges();
 
-      describe('when the drop happens outside of the grid', function () {
-        beforeEach(function () {
-          var $cell = $($('.slick-cell.l1')[1]);
-          var finalPosition = {cell: 1, row: 1};
-
-          var event = {
-            target: $cell,
-            isPropagationStopped: jasmine.createSpy('isPropagationStopped').and.returnValue(false),
-            isImmediatePropagationStopped: jasmine.createSpy('isImmediatePropagationStopped').and.returnValue(false),
-            stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation'),
-
-            pageX: cellLeftPosition(finalPosition.cell),
-            pageY: cellTopPosition($cell, finalPosition.row)
-          };
+        expect(newRange.length).toBe(1);
 
-          grid.onDrag.notify(dd, event, grid);
-          $(window).mouseup();
-        });
-        it('should call handleDragEnd from CellRangeSelector', function () {
-          var newRange = grid.getSelectionModel().getSelectedRanges();
-
-          expect(newRange.length).toBe(1);
-
-          expect(newRange[0].fromCell).toBe(1);
-          expect(newRange[0].toCell).toBe(3);
-          expect(newRange[0].fromRow).toBe(1);
-          expect(newRange[0].toRow).toBe(4);
-        });
+        expect(newRange[0].fromCell).toBe(1);
+        expect(newRange[0].toCell).toBe(3);
+        expect(newRange[0].fromRow).toBe(1);
+        expect(newRange[0].toRow).toBe(4);
       });
     });
+  });
 
-    describe('when we mouse up and no drag and drop occured', function () {
-      beforeEach(function () {
-        grid.onDragEnd.notify = jasmine.createSpy('notify');
-        grid.onDragEnd.notify.calls.reset();
-        $(window).mouseup();
-      });
+  describe('when we mouse up and no drag and drop occured', function () {
+    beforeEach(function () {
+      grid.onDragEnd.notify = jasmine.createSpy('notify');
+      grid.onDragEnd.notify.calls.reset();
+      $(window).mouseup();
+    });
 
-      it('do not notify onDragEnd', function () {
-        expect(grid.onDragEnd.notify).not.toHaveBeenCalled()
-      });
+    it('do not notify onDragEnd', function () {
+      expect(grid.onDragEnd.notify).not.toHaveBeenCalled()
     });
+  });
+
+  describe('setSelectedRows', function () {
 
-    describe('setSelectedRows', function () {
+    beforeEach(function () {
+      grid.getSelectionModel().setSelectedRanges(
+        [new Slick.Range(1, 1, 1, 1)]
+      );
+    });
 
+    describe('when passed an empty array', function () {
       beforeEach(function () {
-        grid.getSelectionModel().setSelectedRanges(
-          [new Slick.Range(1, 1, 1, 1)]
-        );
+        grid.getSelectionModel().setSelectedRows([]);
       });
-
-      describe('when passed an empty array', function () {
-        beforeEach(function () {
-          grid.getSelectionModel().setSelectedRows([]);
-        });
-        it('clears ranges', function () {
-          var newRanges = grid.getSelectionModel().getSelectedRanges();
-          expect(newRanges.length).toEqual(0);
-        });
-      });
-
-      it('sets ranges corresponding to rows', function () {
-        grid.getSelectionModel().setSelectedRows([0, 2]);
-
-        var selectedRanges = grid.getSelectionModel().getSelectedRanges();
-
-        expect(selectedRanges.length).toBe(2);
-        expectRangeToMatch(selectedRanges[0], 0, 1, 0, 3);
-        expectRangeToMatch(selectedRanges[1], 2, 1, 2, 3);
+      it('clears ranges', function () {
+        var newRanges = grid.getSelectionModel().getSelectedRanges();
+        expect(newRanges.length).toEqual(0);
       });
     });
 
-    function pressKey(keyCode) {
-      var pressEvent = new $.Event("keydown");
-      pressEvent.which = keyCode;
-
-      $(container.find('.grid-canvas')).trigger(pressEvent);
-    }
-
-    function pressShiftPlusKey(keyCode) {
-      var pressEvent = new $.Event("keydown");
-      pressEvent.shiftKey = true;
-      pressEvent.which = keyCode;
-
-      $(container.find('.grid-canvas')).trigger(pressEvent);
-    }
+    it('sets ranges corresponding to rows', function () {
+      grid.getSelectionModel().setSelectedRows([0, 2]);
 
-    function expectOneSelectedRange(fromRow, fromCell, toRow, toCell) {
       var selectedRanges = grid.getSelectionModel().getSelectedRanges();
-      expect(selectedRanges.length).toBe(1);
-      expectRangeToMatch(selectedRanges[0], fromRow, fromCell, toRow, toCell);
-    }
 
-    function expectRangeToMatch(range, fromRow, fromCell, toRow, toCell) {
-      expect(range.fromRow).toBe(fromRow);
-      expect(range.toRow).toBe(toRow);
-      expect(range.fromCell).toBe(fromCell);
-      expect(range.toCell).toBe(toCell);
-    }
+      expect(selectedRanges.length).toBe(2);
+      expectRangeToMatch(selectedRanges[0], 0, 1, 0, 3);
+      expectRangeToMatch(selectedRanges[1], 2, 1, 2, 3);
+    });
   });
-})
-;
\ No newline at end of file
+
+  function pressKey(keyCode) {
+    var pressEvent = new $.Event("keydown");
+    pressEvent.which = keyCode;
+
+    $(container.find('.grid-canvas')).trigger(pressEvent);
+  }
+
+  function pressShiftPlusKey(keyCode) {
+    var pressEvent = new $.Event("keydown");
+    pressEvent.shiftKey = true;
+    pressEvent.which = keyCode;
+
+    $(container.find('.grid-canvas')).trigger(pressEvent);
+  }
+
+  function expectOneSelectedRange(fromRow, fromCell, toRow, toCell) {
+    var selectedRanges = grid.getSelectionModel().getSelectedRanges();
+    expect(selectedRanges.length).toBe(1);
+    expectRangeToMatch(selectedRanges[0], fromRow, fromCell, toRow, toCell);
+  }
+
+  function expectRangeToMatch(range, fromRow, fromCell, toRow, toCell) {
+    expect(range.fromRow).toBe(fromRow);
+    expect(range.toRow).toBe(toRow);
+    expect(range.fromCell).toBe(fromCell);
+    expect(range.toCell).toBe(toCell);
+  }
+});
diff --git a/web/regression/javascript/slickgrid/cell_selector_spec.js b/web/regression/javascript/slickgrid/cell_selector_spec.js
index ed7c0aee..b32fdf80 100644
--- a/web/regression/javascript/slickgrid/cell_selector_spec.js
+++ b/web/regression/javascript/slickgrid/cell_selector_spec.js
@@ -7,71 +7,69 @@
 //
 //////////////////////////////////////////////////////////////
 
-define(["jquery",
-    "slickgrid/slick.grid",
-    "sources/selection/xcell_selection_model",
-    "sources/slickgrid/cell_selector",
-    "sources/selection/range_selection_helper"
-  ],
-  function ($, SlickGrid, XCellSelectionModel, CellSelector, RangeSelectionHelper) {
-    describe("CellSelector", function () {
-      var container, columns, cellSelector, data, cellSelectionModel, grid;
-      beforeEach(function () {
-        container = $("<div></div>");
-        container.height(9999);
-        container.width(9999);
-        columns = [{
-          name: 'some-column-name',
-        }, {
-          name: 'second column',
-        }];
+import $ from "jquery";
+import SlickGrid from "slickgrid.grid";
+import XCellSelectionModel from "sources/selection/xcell_selection_model";
+import CellSelector from "sources/slickgrid/cell_selector";
+import RangeSelectionHelper from "sources/selection/range_selection_helper";
 
-        cellSelector = new CellSelector();
+describe("CellSelector", function () {
+  var container, columns, cellSelector, data, cellSelectionModel, grid;
+  beforeEach(function () {
+    container = $("<div></div>");
+    container.height(9999);
+    container.width(9999);
+    columns = [{
+      name: 'some-column-name',
+    }, {
+      name: 'second column',
+    }];
 
-        data = [];
-        for (var i = 0; i < 10; i++) {
-          data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
-        }
-        grid = new SlickGrid(container, data, columns);
+    cellSelector = new CellSelector();
 
-        cellSelectionModel = new XCellSelectionModel();
-        grid.setSelectionModel(cellSelectionModel);
+    data = [];
+    for (var i = 0; i < 10; i++) {
+      data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
+    }
+    grid = new Slick.Grid(container, data, columns);
 
-        grid.registerPlugin(cellSelector);
-        grid.invalidate();
+    cellSelectionModel = new XCellSelectionModel();
+    grid.setSelectionModel(cellSelectionModel);
 
-        $("body").append(container);
-      });
+    grid.registerPlugin(cellSelector);
+    grid.invalidate();
 
-      afterEach(function () {
-        $("body").find(container).remove();
-      });
+    $("body").append(container);
+  });
 
-      describe("when the user clicks or tabs to a cell", function () {
-        it("sets the selected range to that cell", function () {
-          var row = 1, column = 0;
-          $(container.find(".slick-row .slick-cell.l" + column)[row]).click();
+  afterEach(function () {
+    $("body").find(container).remove();
+  });
 
-          var selectedRanges = cellSelectionModel.getSelectedRanges();
-          expect(selectedRanges.length).toBe(1);
-          expect(selectedRanges[0].fromCell).toBe(0);
-          expect(selectedRanges[0].toCell).toBe(0);
-          expect(selectedRanges[0].fromRow).toBe(1);
-          expect(selectedRanges[0].toRow).toBe(1);
-        });
+  describe("when the user clicks or tabs to a cell", function () {
+    it("sets the selected range to that cell", function () {
+      var row = 1, column = 0;
+      $(container.find(".slick-row .slick-cell.l" + column)[row]).click();
 
-        it("deselects previously selected ranges", function () {
-          var row2Range = RangeSelectionHelper.rangeForRow(grid, 2);
-          var ranges = RangeSelectionHelper.addRange(cellSelectionModel.getSelectedRanges(),
-            row2Range);
-          cellSelectionModel.setSelectedRanges(ranges);
+      var selectedRanges = cellSelectionModel.getSelectedRanges();
+      expect(selectedRanges.length).toBe(1);
+      expect(selectedRanges[0].fromCell).toBe(0);
+      expect(selectedRanges[0].toCell).toBe(0);
+      expect(selectedRanges[0].fromRow).toBe(1);
+      expect(selectedRanges[0].toRow).toBe(1);
+    });
+
+    it("deselects previously selected ranges", function () {
+      var row2Range = RangeSelectionHelper.rangeForRow(grid, 2);
+      var ranges = RangeSelectionHelper.addRange(cellSelectionModel.getSelectedRanges(),
+        row2Range);
+      cellSelectionModel.setSelectedRanges(ranges);
 
-          var row = 4, column = 1;
-          $(container.find(".slick-row .slick-cell.l" + column)[row]).click();
+      var row = 4, column = 1;
+      $(container.find(".slick-row .slick-cell.l" + column)[row]).click();
 
-          expect(RangeSelectionHelper.isRangeSelected(cellSelectionModel.getSelectedRanges(), row2Range))
-            .toBe(false);
-        });
-      });
+      expect(RangeSelectionHelper.isRangeSelected(cellSelectionModel.getSelectedRanges(), row2Range))
+        .toBe(false);
     });
-  });
\ No newline at end of file
+  });
+});
diff --git a/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js b/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js
index 14768227..884ae415 100644
--- a/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js
+++ b/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js
@@ -7,137 +7,139 @@
 //
 //////////////////////////////////////////////////////////////
 
-define([
-    'sources/slickgrid/event_handlers/handle_query_output_keyboard_event',
-    'sources/selection/clipboard',
-    'sources/selection/range_selection_helper',
-    'sources/selection/xcell_selection_model',
-    'slickgrid'
-  ],
-function (handleQueryOutputKeyboardEvent, clipboard, RangeSelectionHelper, XCellSelectionModel) {
-  var Slick = window.Slick;
-
-  describe('#handleQueryOutputKeyboardEvent', function () {
-    var event, view, grid, slickEvent;
+import HandleQueryOutputKeyboardEvent from 'sources/slickgrid/event_handlers/handle_query_output_keyboard_event';
+import clipboard from 'sources/selection/clipboard';
+import RangeSelectionHelper from 'sources/selection/range_selection_helper';
+import XCellSelectionModel from 'sources/selection/xcell_selection_model';
+import Slick from 'slickgrid';
+import 'slickgrid.grid';
+
+
+describe('#handleQueryOutputKeyboardEvent', function () {
+  var event, view, grid, slickEvent;
+  var SlickGrid = Slick.Grid;
+  var handleQueryOutputKeyboardEvent;
+
+  beforeEach(function () {
+    event = {
+      shiftKey: false,
+      ctrlKey: false,
+      metaKey: false,
+      which: -1,
+      keyCode: -1,
+      preventDefault: jasmine.createSpy('preventDefault')
+    };
+
+    var data = [['', '0,0-cell-content', '0,1-cell-content'],
+      ['', '1,0-cell-content', '1,1-cell-content'],
+      ['', '2,0-cell-content', '2,1-cell-content']];
+    var columnDefinitions = [{name: 'checkboxColumn'}, {pos: 1, name: 'firstColumn'}, {
+      pos: 2,
+      name: 'secondColumn'
+    }];
+    grid = new SlickGrid($('<div></div>'), data, columnDefinitions);
+    grid.setSelectionModel(new XCellSelectionModel());
+
+    slickEvent = {
+      grid: grid
+    };
+
+    view = {};
+    spyOn(clipboard, 'copyTextToClipboard');
+    handleQueryOutputKeyboardEvent = HandleQueryOutputKeyboardEvent.bind(window);
+    debugger
+  });
+
+  describe("when a range is selected", function () {
     beforeEach(function () {
-      event = {
-        shiftKey: false,
-        ctrlKey: false,
-        metaKey: false,
-        which: -1,
-        keyCode: -1,
-        preventDefault: jasmine.createSpy('preventDefault')
-      };
-
-      var data = [['', '0,0-cell-content', '0,1-cell-content'],
-        ['', '1,0-cell-content', '1,1-cell-content'],
-        ['', '2,0-cell-content', '2,1-cell-content']];
-      var columnDefinitions = [{name: 'checkboxColumn'}, {pos: 1, name: 'firstColumn'}, {
-        pos: 2,
-        name: 'secondColumn'
-      }];
-      grid = new Slick.Grid($('<div></div>'), data, columnDefinitions);
-      grid.setSelectionModel(new XCellSelectionModel());
-
-      slickEvent = {
-        grid: grid
-      };
-
-      view = {};
-      spyOn(clipboard, 'copyTextToClipboard');
+      grid.getSelectionModel().setSelectedRanges([
+        RangeSelectionHelper.rangeForRow(grid, 0),
+        RangeSelectionHelper.rangeForRow(grid, 2),
+      ]);
     });
 
-    describe("when a range is selected", function () {
+    describe("pressing Command + C", function () {
       beforeEach(function () {
-        grid.getSelectionModel().setSelectedRanges([
-          RangeSelectionHelper.rangeForRow(grid, 0),
-          RangeSelectionHelper.rangeForRow(grid, 2),
-        ]);
+        event.metaKey = true;
+        event.keyCode = 67;
       });
 
-      describe("pressing Command + C", function () {
-        beforeEach(function () {
-          event.metaKey = true;
-          event.keyCode = 67;
-        });
-
-        it("copies the cell content to the clipboard", function () {
-          handleQueryOutputKeyboardEvent(event, slickEvent);
+      it("copies the cell content to the clipboard", function () {
+        handleQueryOutputKeyboardEvent(event, slickEvent);
 
-          expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith("'0,0-cell-content','0,1-cell-content'\n'2,0-cell-content','2,1-cell-content'");
-        });
+        expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith("'0,0-cell-content','0,1-cell-content'\n'2,0-cell-content','2,1-cell-content'");
       });
+    });
 
-      describe("pressing Ctrl + C", function () {
-        beforeEach(function () {
-          event.ctrlKey = true;
-          event.keyCode = 67;
-        });
+    describe("pressing Ctrl + C", function () {
+      beforeEach(function () {
+        event.ctrlKey = true;
+        event.keyCode = 67;
+      });
 
-        it("copies the cell content to the clipboard", function () {
-          handleQueryOutputKeyboardEvent(event, slickEvent);
+      it("copies the cell content to the clipboard", function () {
+        handleQueryOutputKeyboardEvent(event, slickEvent);
 
-          expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith("'0,0-cell-content','0,1-cell-content'\n'2,0-cell-content','2,1-cell-content'");
-        });
+        expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith("'0,0-cell-content','0,1-cell-content'\n'2,0-cell-content','2,1-cell-content'");
       });
+    });
 
-      describe("pressing Command + A", function () {
-        beforeEach(function () {
-          event.metaKey = true;
-          event.keyCode = 65;
-        });
+    describe("pressing Command + A", function () {
+      beforeEach(function () {
+        event.metaKey = true;
+        event.keyCode = 65;
+      });
 
-        it("selects the entire grid to ranges", function () {
-          handleQueryOutputKeyboardEvent(event, slickEvent);
+      it("selects the entire grid to ranges", function () {
+        handleQueryOutputKeyboardEvent(event, slickEvent);
 
-          expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
-          expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
-        });
+        expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
+        expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
       });
+    });
 
-      describe("pressing Ctrl + A", function () {
-        beforeEach(function () {
-          event.ctrlKey = true;
-          event.keyCode = 65;
-        });
+    describe("pressing Ctrl + A", function () {
+      beforeEach(function () {
+        event.ctrlKey = true;
+        event.keyCode = 65;
+      });
 
-        it("selects the entire grid to ranges", function () {
-          handleQueryOutputKeyboardEvent(event, slickEvent);
+      it("selects the entire grid to ranges", function () {
+        handleQueryOutputKeyboardEvent(event, slickEvent);
 
-          expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
-          expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
-        });
+        expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
+        expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
       });
     });
+  });
 
-    describe("when no ranges are selected", function () {
-      describe("pressing Command + A", function () {
-        beforeEach(function () {
-          event.metaKey = true;
-          event.keyCode = 65;
-        });
+  describe("when no ranges are selected", function () {
+    describe("pressing Command + A", function () {
+      beforeEach(function () {
+        event.metaKey = true;
+        event.keyCode = 65;
+      });
 
-        it("selects the entire grid to ranges", function () {
-          handleQueryOutputKeyboardEvent(event, slickEvent);
+      it("selects the entire grid to ranges", function () {
+        handleQueryOutputKeyboardEvent(event, slickEvent);
 
-          expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
-          expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
-        });
+        expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
+        expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
       });
+    });
 
-      describe("pressing Ctrl + A", function () {
-        beforeEach(function () {
-          event.ctrlKey = true;
-          event.keyCode = 65;
-        });
+    describe("pressing Ctrl + A", function () {
+      beforeEach(function () {
+        event.ctrlKey = true;
+        event.keyCode = 65;
+      });
 
-        it("selects the entire grid to ranges", function () {
-          handleQueryOutputKeyboardEvent(event, slickEvent);
+      it("selects the entire grid to ranges", function () {
+        handleQueryOutputKeyboardEvent(event, slickEvent);
 
-          expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
-          expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
-        });
+        expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
+        expect(grid.getSelectionModel().getSelectedRanges().length).toBe(1);
       });
     });
   });
-});
\ No newline at end of file
+});
diff --git a/web/regression/javascript/test-main.js b/web/regression/javascript/test-main.js
index 8bb8c027..e69de29b 100644
--- a/web/regression/javascript/test-main.js
+++ b/web/regression/javascript/test-main.js
@@ -1,138 +0,0 @@
-//////////////////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2017, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////////////////
-
-var allTestFiles = [];
-var TEST_REGEXP = /(spec|test)\.js$/i;
-
-// Get a list of all the test files to include
-Object.keys(window.__karma__.files).forEach(function (file) {
-  if (TEST_REGEXP.test(file)) {
-    // Normalize paths to RequireJS module names.
-    // If you require sub-dependencies of test files to be loaded as-is (requiring file extension)
-    // then do not normalize the paths
-    var normalizedTestModule = file.replace(/^\/base\/|\.js$/g, '');
-    allTestFiles.push(normalizedTestModule)
-  }
-});
-
-var sourcesDir = '/base/pgadmin/static/';
-require.config({
-  // Karma serves files under /base, which is the basePath from your config file
-  baseUrl: '/base',
-
-  paths: {
-    'pgadmin': sourcesDir + 'js/pgadmin',
-    'alertify': sourcesDir + 'vendor/alertifyjs/alertify',
-    'jquery': sourcesDir + 'vendor/jquery/jquery-1.11.2',
-    'jquery.ui': sourcesDir + 'vendor/jquery-ui/jquery-ui-1.11.3',
-    'jquery.event.drag': sourcesDir + 'vendor/jquery-ui/jquery.event.drag-2.2',
-    'underscore': sourcesDir + 'vendor/underscore/underscore',
-    'underscore.string': sourcesDir + 'vendor/underscore/underscore.string',
-    'slickgrid': sourcesDir + 'vendor/slickgrid/slick.core',
-    'slickgrid/slick.grid': sourcesDir + 'vendor/slickgrid/slick.grid',
-    'translations': '/base/regression/javascript/fake_translations',
-    'sources': sourcesDir + 'js',
-    'browser': '/base/pgadmin/browser/static/js'
-  },
-
-  shim: {
-    'underscore': {
-      exports: '_'
-    },
-    "slickgrid": {
-      "deps": [
-        'jquery', "jquery.ui", "jquery.event.drag"
-      ],
-      "exports": 'window.Slick'
-    },
-    "slickgrid/slick.grid": {
-      "deps": [
-        'jquery', "jquery.ui", "jquery.event.drag", "slickgrid"
-      ],
-      "exports": 'window.Slick.Grid'
-    },
-    "sources/slickgrid/pgslick.cellrangedecorator": {
-      "deps": [
-        "jquery"
-      ],
-      "exports": 'PGRowRangeDecorator'
-    },
-    "sources/slickgrid/pgslick.cellrangeselector": {
-      "deps": [
-        "jquery", "sources/slickgrid/pgslick.cellrangedecorator"
-      ],
-      "exports": 'PGCellRangeSelector'
-    },
-    "sources/selection/xcell_selection_model": {
-      "deps": [
-        "jquery", "sources/slickgrid/pgslick.cellrangeselector"
-      ],
-      "exports": 'XCellSelectionModel'
-    },
-    "backbone": {
-      "deps": ['underscore', 'jquery'],
-      "exports": 'Backbone'
-    },
-    "backbone.paginator": {
-      "deps": ['underscore', 'jquery', 'backbone']
-    },
-    "bootstrap": {
-      "deps": ['jquery'],
-    },
-    "backgrid": {
-      "deps": ['backform'],
-      "exports": 'Backgrid',
-    },
-    "backgrid.select.all": {
-      "deps": ['backgrid']
-    },
-    "backgrid.paginator": {
-      "deps": ['backgrid', 'backbone.paginator']
-    },
-    "backgrid.filter": {
-      "deps": ['backgrid']
-    },
-    "backgrid.sizeable.columns": {
-      "deps": ['backgrid']
-    },
-    "bootstrap.switch": {
-      "deps": ['jquery', 'bootstrap'],
-      "exports": 'jQuery.fn.bootstrapSwitch'
-    },
-    "select2": {
-      "deps": ['jquery'],
-      "exports": 'jQuery.fn.select2'
-    },
-    "bootstrap.datepicker": {
-      "deps": ['jquery', 'bootstrap'],
-      "exports": 'jQuery.fn.datepicker'
-    },
-    "bootstrap.datetimepicker": {
-      "deps": ['jquery', 'bootstrap', 'moment'],
-      "exports": 'jQuery.fn.datetimepicker'
-    },
-    "pgadmin.backgrid": {
-      "deps": ["backgrid", "bootstrap.datetimepicker", "bootstrap.switch"],
-    },
-    "pgadmin.backform": {
-      "deps": ['backform', "pgadmin.backgrid", "select2"],
-    },
-    "jquery.event.drag": {
-      "deps": ['jquery'], "exports": 'jQuery.fn.drag'
-    },
-    "jquery.ui": {"deps": ['jquery']}
-  },
-
-  // dynamically load all test files
-  deps: allTestFiles,
-
-  // we have to kickoff jasmine, as it is asynchronous
-  callback: window.__karma__.start
-});
-
diff --git a/web/regression/requirements.txt b/web/regression/requirements.txt
index 5959a183..f44953d5 100644
--- a/web/regression/requirements.txt
+++ b/web/regression/requirements.txt
@@ -4,6 +4,7 @@ testscenarios==0.5.0
 testtools==2.0.0
 traceback2==1.4.0
 unittest2==1.1.0
+mock~=2.0.0
 
 # Leave this at the end because there is a bug where the '--install-option' is applied to all subsequent requirements
 chromedriver_installer==0.0.6 --install-option='--chromedriver-version=2.29'
diff --git a/web/webpack.config.js b/web/webpack.config.js
new file mode 100644
index 00000000..91586592
--- /dev/null
+++ b/web/webpack.config.js
@@ -0,0 +1,29 @@
+/* eslint-env node */
+
+module.exports = {
+  context: __dirname + '/pgadmin/static/jsx',
+  entry: './components.jsx',
+  output: {
+    libraryTarget: 'amd',
+    path: __dirname + '/pgadmin/static/js/generated',
+    filename: 'reactComponents.js',
+  },
+
+  module: {
+    rules: [{
+      test: /\.jsx?$/,
+      exclude: /node_modules/,
+      use: {
+        loader: 'babel-loader',
+        options: {
+          presets: ['es2015', 'react'],
+        },
+      },
+    },
+    ],
+  },
+
+  resolve: {
+    extensions: ['.js', '.jsx'],
+  },
+};
\ No newline at end of file
diff --git a/web/webpack.test.config.js b/web/webpack.test.config.js
new file mode 100644
index 00000000..29df6dd8
--- /dev/null
+++ b/web/webpack.test.config.js
@@ -0,0 +1,70 @@
+/* eslint-env node */
+const path = require('path');
+const webpack = require('webpack');
+
+const sourcesDir = path.resolve(__dirname, 'pgadmin/static');
+const regressionDir = path.resolve(__dirname, 'regression');
+
+module.exports = {
+  plugins: [
+    new webpack.ProvidePlugin({
+      $: 'jquery',
+      jQuery: 'jquery',
+      _: 'underscore',
+      'underscore.string': 'underscore.string',
+    }),
+  ],
+
+  module: {
+    rules: [
+      {
+        test: /\.jsx?$/,
+        exclude: [/node_modules/, /vendor/],
+        use: {
+          loader: 'babel-loader',
+          options: {
+            presets: ['es2015'],
+          },
+        },
+      },
+      {
+        test: /.*slickgrid\/slick\.(?!core)*/,
+        loader: 'imports-loader?' +
+        'jquery.ui' +
+        ',jquery.event.drag' +
+        ',slickgrid',
+      }, {
+        test: /.*slickgrid\/plugins\/slick\.rowselectionmodel/,
+        loader: 'imports-loader?' +
+        'jquery.ui' +
+        ',jquery.event.drag' +
+        ',slickgrid' +
+        '!exports-loader?' +
+        'Slick.RowSelectionModel',
+      }, {
+        test: /.*slickgrid\/slick\.core.*/,
+        loader: 'imports-loader?' +
+        'jquery.ui' +
+        ',jquery.event.drag' +
+        '!exports-loader?' +
+        'Slick',
+      }],
+  },
+
+  resolve: {
+    alias: {
+      'alertify': sourcesDir + '/vendor/alertifyjs/alertify',
+      'jquery': sourcesDir + '/vendor/jquery/jquery-1.11.2',
+      'jquery.ui': sourcesDir + '/vendor/jquery-ui/jquery-ui-1.11.3',
+      'jquery.event.drag': sourcesDir + '/vendor/jquery-ui/jquery.event.drag-2.2',
+      'sources': sourcesDir + '/js',
+      'underscore.string': sourcesDir + '/vendor/underscore/underscore.string',
+      'translations': regressionDir + '/javascript/fake_translations',
+      'slickgrid': sourcesDir + '/vendor/slickgrid/slick.core',
+      'slickgrid.grid': sourcesDir + '/vendor/slickgrid/slick.grid',
+      'slickgrid.rowselectionmodel': sourcesDir + '/vendor/slickgrid/plugins/slick.rowselectionmodel',
+      'browser': path.resolve(__dirname, 'pgadmin/browser/static/js'),
+      'pgadmin': sourcesDir + '/js/pgadmin',
+    },
+  },
+};
diff --git a/web/yarn.lock b/web/yarn.lock
index e69de29b..bfd9171d 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -0,0 +1,4526 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+JSONStream@^1.0.3:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.1.tgz#707f761e01dae9e16f1bcf93703b78c70966579a"
+  dependencies:
+    jsonparse "^1.2.0"
+    through ">=2.2.7 <3"
+
+abbrev@1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f"
+
[email protected]:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca"
+  dependencies:
+    mime-types "~2.1.11"
+    negotiator "0.6.1"
+
+acorn-dynamic-import@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4"
+  dependencies:
+    acorn "^4.0.3"
+
+acorn-jsx@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
+  dependencies:
+    acorn "^3.0.4"
+
+acorn@^3.0.4:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
+
+acorn@^4.0.3, acorn@^4.0.4:
+  version "4.0.13"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
+
+acorn@^5.0.1:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d"
+
[email protected]:
+  version "0.8.2"
+  resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
+
+ajv-keywords@^1.0.0, ajv-keywords@^1.1.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c"
+
+ajv@^4.7.0, ajv@^4.9.1:
+  version "4.11.8"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
+  dependencies:
+    co "^4.6.0"
+    json-stable-stringify "^1.0.1"
+
+align-text@^0.1.1, align-text@^0.1.3:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
+  dependencies:
+    kind-of "^3.0.2"
+    longest "^1.0.1"
+    repeat-string "^1.5.2"
+
+amdefine@>=0.0.4:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
+
+ansi-escapes@^1.1.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
+
+ansi-regex@^2.0.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+
+ansi-styles@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+
+anymatch@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507"
+  dependencies:
+    arrify "^1.0.0"
+    micromatch "^2.1.5"
+
+aproba@^1.0.3:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1"
+
+are-we-there-yet@~1.1.2:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d"
+  dependencies:
+    delegates "^1.0.0"
+    readable-stream "^2.0.6"
+
+argparse@^1.0.7:
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86"
+  dependencies:
+    sprintf-js "~1.0.2"
+
+arr-diff@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf"
+  dependencies:
+    arr-flatten "^1.0.1"
+
+arr-flatten@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.3.tgz#a274ed85ac08849b6bd7847c4580745dc51adfb1"
+
+array-filter@~0.0.0:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec"
+
+array-map@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662"
+
+array-reduce@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b"
+
+array-slice@^0.2.3:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5"
+
+array-union@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
+  dependencies:
+    array-uniq "^1.0.1"
+
+array-uniq@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
+
+array-unique@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53"
+
+array.prototype.find@^2.0.1:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.0.4.tgz#556a5c5362c08648323ddaeb9de9d14bc1864c90"
+  dependencies:
+    define-properties "^1.1.2"
+    es-abstract "^1.7.0"
+
[email protected]:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a272c0ccfbd1ad2979ca"
+
+arrify@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
+
+asap@~2.0.3:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f"
+
+asn1.js@^4.0.0:
+  version "4.9.1"
+  resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40"
+  dependencies:
+    bn.js "^4.0.0"
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+
+asn1@~0.2.3:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
+
[email protected], assert-plus@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
+
+assert-plus@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
+
+assert@^1.1.1, assert@^1.4.0:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91"
+  dependencies:
+    util "0.10.3"
+
+astw@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/astw/-/astw-2.2.0.tgz#7bd41784d32493987aeb239b6b4e1c57a873b917"
+  dependencies:
+    acorn "^4.0.3"
+
+async-each@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
+
+async@^2.1.2:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/async/-/async-2.4.1.tgz#62a56b279c98a11d0987096a01cc3eeb8eb7bbd7"
+  dependencies:
+    lodash "^4.14.0"
+
+async@~0.9.0:
+  version "0.9.2"
+  resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
+
+asynckit@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+
+aws-sign2@~0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
+
+aws4@^1.2.1:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
+
+axios@^0.16.1:
+  version "0.16.2"
+  resolved "https://registry.yarnpkg.com/axios/-/axios-0.16.2.tgz#ba4f92f17167dfbab40983785454b9ac149c3c6d"
+  dependencies:
+    follow-redirects "^1.2.3"
+    is-buffer "^1.1.5"
+
+babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4"
+  dependencies:
+    chalk "^1.1.0"
+    esutils "^2.0.2"
+    js-tokens "^3.0.0"
+
+babel-core@^6.0.0, babel-core@^6.0.14, babel-core@^6.24.1, babel-core@~6.24.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.1.tgz#8c428564dce1e1f41fb337ec34f4c3b022b5ad83"
+  dependencies:
+    babel-code-frame "^6.22.0"
+    babel-generator "^6.24.1"
+    babel-helpers "^6.24.1"
+    babel-messages "^6.23.0"
+    babel-register "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+    babylon "^6.11.0"
+    convert-source-map "^1.1.0"
+    debug "^2.1.1"
+    json5 "^0.5.0"
+    lodash "^4.2.0"
+    minimatch "^3.0.2"
+    path-is-absolute "^1.0.0"
+    private "^0.1.6"
+    slash "^1.0.0"
+    source-map "^0.5.0"
+
+babel-generator@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.24.1.tgz#e715f486c58ded25649d888944d52aa07c5d9497"
+  dependencies:
+    babel-messages "^6.23.0"
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+    detect-indent "^4.0.0"
+    jsesc "^1.3.0"
+    lodash "^4.2.0"
+    source-map "^0.5.0"
+    trim-right "^1.0.1"
+
+babel-helper-builder-react-jsx@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.24.1.tgz#0ad7917e33c8d751e646daca4e77cc19377d2cbc"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+    esutils "^2.0.0"
+
+babel-helper-call-delegate@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d"
+  dependencies:
+    babel-helper-hoist-variables "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-helper-define-map@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz#7a9747f258d8947d32d515f6aa1c7bd02204a080"
+  dependencies:
+    babel-helper-function-name "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+    lodash "^4.2.0"
+
+babel-helper-function-name@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9"
+  dependencies:
+    babel-helper-get-function-arity "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-helper-get-function-arity@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-helper-hoist-variables@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-helper-optimise-call-expression@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-helper-regex@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz#d36e22fab1008d79d88648e32116868128456ce8"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+    lodash "^4.2.0"
+
+babel-helper-replace-supers@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a"
+  dependencies:
+    babel-helper-optimise-call-expression "^6.24.1"
+    babel-messages "^6.23.0"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-helpers@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-loader@~6.4.1:
+  version "6.4.1"
+  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-6.4.1.tgz#0b34112d5b0748a8dcdbf51acf6f9bd42d50b8ca"
+  dependencies:
+    find-cache-dir "^0.1.1"
+    loader-utils "^0.2.16"
+    mkdirp "^0.5.1"
+    object-assign "^4.0.1"
+
+babel-messages@^6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-check-es2015-constants@^6.22.0, babel-plugin-check-es2015-constants@^6.3.13:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-syntax-flow@^6.18.0:
+  version "6.18.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d"
+
+babel-plugin-syntax-jsx@^6.3.13, babel-plugin-syntax-jsx@^6.8.0:
+  version "6.18.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946"
+
+babel-plugin-transform-es2015-arrow-functions@^6.22.0, babel-plugin-transform-es2015-arrow-functions@^6.3.13:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-block-scoped-functions@^6.22.0, babel-plugin-transform-es2015-block-scoped-functions@^6.3.13:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-block-scoping@^6.24.1, babel-plugin-transform-es2015-block-scoping@^6.9.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz#76c295dc3a4741b1665adfd3167215dcff32a576"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+    lodash "^4.2.0"
+
+babel-plugin-transform-es2015-classes@^6.24.1, babel-plugin-transform-es2015-classes@^6.9.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db"
+  dependencies:
+    babel-helper-define-map "^6.24.1"
+    babel-helper-function-name "^6.24.1"
+    babel-helper-optimise-call-expression "^6.24.1"
+    babel-helper-replace-supers "^6.24.1"
+    babel-messages "^6.23.0"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-computed-properties@^6.24.1, babel-plugin-transform-es2015-computed-properties@^6.3.13:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-destructuring@^6.22.0, babel-plugin-transform-es2015-destructuring@^6.9.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-duplicate-keys@^6.24.1, babel-plugin-transform-es2015-duplicate-keys@^6.6.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-for-of@^6.22.0, babel-plugin-transform-es2015-for-of@^6.6.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-function-name@^6.24.1, babel-plugin-transform-es2015-function-name@^6.9.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b"
+  dependencies:
+    babel-helper-function-name "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-literals@^6.22.0, babel-plugin-transform-es2015-literals@^6.3.13:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-modules-amd@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154"
+  dependencies:
+    babel-plugin-transform-es2015-modules-commonjs "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-modules-commonjs@^6.24.1, babel-plugin-transform-es2015-modules-commonjs@^6.6.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz#d3e310b40ef664a36622200097c6d440298f2bfe"
+  dependencies:
+    babel-plugin-transform-strict-mode "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-modules-systemjs@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23"
+  dependencies:
+    babel-helper-hoist-variables "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-modules-umd@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468"
+  dependencies:
+    babel-plugin-transform-es2015-modules-amd "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+
+babel-plugin-transform-es2015-object-super@^6.24.1, babel-plugin-transform-es2015-object-super@^6.3.13:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d"
+  dependencies:
+    babel-helper-replace-supers "^6.24.1"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-parameters@^6.24.1, babel-plugin-transform-es2015-parameters@^6.9.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b"
+  dependencies:
+    babel-helper-call-delegate "^6.24.1"
+    babel-helper-get-function-arity "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-template "^6.24.1"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-shorthand-properties@^6.24.1, babel-plugin-transform-es2015-shorthand-properties@^6.3.13:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-spread@^6.22.0, babel-plugin-transform-es2015-spread@^6.3.13:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-sticky-regex@^6.24.1, babel-plugin-transform-es2015-sticky-regex@^6.3.13:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc"
+  dependencies:
+    babel-helper-regex "^6.24.1"
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-plugin-transform-es2015-template-literals@^6.22.0, babel-plugin-transform-es2015-template-literals@^6.6.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-typeof-symbol@^6.22.0, babel-plugin-transform-es2015-typeof-symbol@^6.6.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-es2015-unicode-regex@^6.24.1, babel-plugin-transform-es2015-unicode-regex@^6.3.13:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9"
+  dependencies:
+    babel-helper-regex "^6.24.1"
+    babel-runtime "^6.22.0"
+    regexpu-core "^2.0.0"
+
+babel-plugin-transform-flow-strip-types@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz#84cb672935d43714fdc32bce84568d87441cf7cf"
+  dependencies:
+    babel-plugin-syntax-flow "^6.18.0"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-react-display-name@^6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.23.0.tgz#4398910c358441dc4cef18787264d0412ed36b37"
+  dependencies:
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-react-jsx-self@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz#df6d80a9da2612a121e6ddd7558bcbecf06e636e"
+  dependencies:
+    babel-plugin-syntax-jsx "^6.8.0"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-react-jsx-source@^6.22.0:
+  version "6.22.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz#66ac12153f5cd2d17b3c19268f4bf0197f44ecd6"
+  dependencies:
+    babel-plugin-syntax-jsx "^6.8.0"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-react-jsx@^6.23.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz#840a028e7df460dfc3a2d29f0c0d91f6376e66a3"
+  dependencies:
+    babel-helper-builder-react-jsx "^6.24.1"
+    babel-plugin-syntax-jsx "^6.8.0"
+    babel-runtime "^6.22.0"
+
+babel-plugin-transform-regenerator@^6.24.1, babel-plugin-transform-regenerator@^6.9.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz#b8da305ad43c3c99b4848e4fe4037b770d23c418"
+  dependencies:
+    regenerator-transform "0.9.11"
+
+babel-plugin-transform-strict-mode@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+
+babel-polyfill@^6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d"
+  dependencies:
+    babel-runtime "^6.22.0"
+    core-js "^2.4.0"
+    regenerator-runtime "^0.10.0"
+
+babel-preset-es2015-without-strict@~0.0.4:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/babel-preset-es2015-without-strict/-/babel-preset-es2015-without-strict-0.0.4.tgz#88c9f36e79d4762c58347b1a698a07c35b6bda5d"
+  dependencies:
+    babel-plugin-check-es2015-constants "^6.3.13"
+    babel-plugin-transform-es2015-arrow-functions "^6.3.13"
+    babel-plugin-transform-es2015-block-scoped-functions "^6.3.13"
+    babel-plugin-transform-es2015-block-scoping "^6.9.0"
+    babel-plugin-transform-es2015-classes "^6.9.0"
+    babel-plugin-transform-es2015-computed-properties "^6.3.13"
+    babel-plugin-transform-es2015-destructuring "^6.9.0"
+    babel-plugin-transform-es2015-duplicate-keys "^6.6.0"
+    babel-plugin-transform-es2015-for-of "^6.6.0"
+    babel-plugin-transform-es2015-function-name "^6.9.0"
+    babel-plugin-transform-es2015-literals "^6.3.13"
+    babel-plugin-transform-es2015-modules-commonjs "^6.6.0"
+    babel-plugin-transform-es2015-object-super "^6.3.13"
+    babel-plugin-transform-es2015-parameters "^6.9.0"
+    babel-plugin-transform-es2015-shorthand-properties "^6.3.13"
+    babel-plugin-transform-es2015-spread "^6.3.13"
+    babel-plugin-transform-es2015-sticky-regex "^6.3.13"
+    babel-plugin-transform-es2015-template-literals "^6.6.0"
+    babel-plugin-transform-es2015-typeof-symbol "^6.6.0"
+    babel-plugin-transform-es2015-unicode-regex "^6.3.13"
+    babel-plugin-transform-regenerator "^6.9.0"
+
+babel-preset-es2015@~6.24.0:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939"
+  dependencies:
+    babel-plugin-check-es2015-constants "^6.22.0"
+    babel-plugin-transform-es2015-arrow-functions "^6.22.0"
+    babel-plugin-transform-es2015-block-scoped-functions "^6.22.0"
+    babel-plugin-transform-es2015-block-scoping "^6.24.1"
+    babel-plugin-transform-es2015-classes "^6.24.1"
+    babel-plugin-transform-es2015-computed-properties "^6.24.1"
+    babel-plugin-transform-es2015-destructuring "^6.22.0"
+    babel-plugin-transform-es2015-duplicate-keys "^6.24.1"
+    babel-plugin-transform-es2015-for-of "^6.22.0"
+    babel-plugin-transform-es2015-function-name "^6.24.1"
+    babel-plugin-transform-es2015-literals "^6.22.0"
+    babel-plugin-transform-es2015-modules-amd "^6.24.1"
+    babel-plugin-transform-es2015-modules-commonjs "^6.24.1"
+    babel-plugin-transform-es2015-modules-systemjs "^6.24.1"
+    babel-plugin-transform-es2015-modules-umd "^6.24.1"
+    babel-plugin-transform-es2015-object-super "^6.24.1"
+    babel-plugin-transform-es2015-parameters "^6.24.1"
+    babel-plugin-transform-es2015-shorthand-properties "^6.24.1"
+    babel-plugin-transform-es2015-spread "^6.22.0"
+    babel-plugin-transform-es2015-sticky-regex "^6.24.1"
+    babel-plugin-transform-es2015-template-literals "^6.22.0"
+    babel-plugin-transform-es2015-typeof-symbol "^6.22.0"
+    babel-plugin-transform-es2015-unicode-regex "^6.24.1"
+    babel-plugin-transform-regenerator "^6.24.1"
+
+babel-preset-flow@^6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz#e71218887085ae9a24b5be4169affb599816c49d"
+  dependencies:
+    babel-plugin-transform-flow-strip-types "^6.22.0"
+
+babel-preset-react@~6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-preset-react/-/babel-preset-react-6.23.0.tgz#eb7cee4de98a3f94502c28565332da9819455195"
+  dependencies:
+    babel-plugin-syntax-jsx "^6.3.13"
+    babel-plugin-transform-react-display-name "^6.23.0"
+    babel-plugin-transform-react-jsx "^6.23.0"
+    babel-plugin-transform-react-jsx-self "^6.22.0"
+    babel-plugin-transform-react-jsx-source "^6.22.0"
+    babel-preset-flow "^6.23.0"
+
+babel-register@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.24.1.tgz#7e10e13a2f71065bdfad5a1787ba45bca6ded75f"
+  dependencies:
+    babel-core "^6.24.1"
+    babel-runtime "^6.22.0"
+    core-js "^2.4.0"
+    home-or-tmp "^2.0.0"
+    lodash "^4.2.0"
+    mkdirp "^0.5.1"
+    source-map-support "^0.4.2"
+
+babel-runtime@^6.18.0, babel-runtime@^6.22.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b"
+  dependencies:
+    core-js "^2.4.0"
+    regenerator-runtime "^0.10.0"
+
+babel-template@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5a45fb8308333"
+  dependencies:
+    babel-runtime "^6.22.0"
+    babel-traverse "^6.24.1"
+    babel-types "^6.24.1"
+    babylon "^6.11.0"
+    lodash "^4.2.0"
+
+babel-traverse@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.24.1.tgz#ab36673fd356f9a0948659e7b338d5feadb31695"
+  dependencies:
+    babel-code-frame "^6.22.0"
+    babel-messages "^6.23.0"
+    babel-runtime "^6.22.0"
+    babel-types "^6.24.1"
+    babylon "^6.15.0"
+    debug "^2.2.0"
+    globals "^9.0.0"
+    invariant "^2.2.0"
+    lodash "^4.2.0"
+
+babel-types@^6.19.0, babel-types@^6.24.1:
+  version "6.24.1"
+  resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.24.1.tgz#a136879dc15b3606bda0d90c1fc74304c2ff0975"
+  dependencies:
+    babel-runtime "^6.22.0"
+    esutils "^2.0.2"
+    lodash "^4.2.0"
+    to-fast-properties "^1.0.1"
+
+babelify@~7.3.0:
+  version "7.3.0"
+  resolved "https://registry.yarnpkg.com/babelify/-/babelify-7.3.0.tgz#aa56aede7067fd7bd549666ee16dc285087e88e5"
+  dependencies:
+    babel-core "^6.0.14"
+    object-assign "^4.0.0"
+
+babylon@^6.11.0, babylon@^6.15.0:
+  version "6.17.2"
+  resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.2.tgz#201d25ef5f892c41bae49488b08db0dd476e9f5c"
+
[email protected]:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
+
+balanced-match@^0.4.1:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
+
[email protected]:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
+
+base64-js@^1.0.2:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1"
+
[email protected]:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6"
+
+bcrypt-pbkdf@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
+  dependencies:
+    tweetnacl "^0.14.3"
+
+better-assert@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522"
+  dependencies:
+    callsite "1.0.0"
+
+big.js@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978"
+
+binary-extensions@^1.0.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774"
+
[email protected]:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921"
+
+block-stream@*:
+  version "0.0.9"
+  resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
+  dependencies:
+    inherits "~2.0.0"
+
+bluebird@^3.3.0:
+  version "3.5.0"
+  resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c"
+
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
+  version "4.11.6"
+  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215"
+
+body-parser@^1.16.1:
+  version "1.17.2"
+  resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.17.2.tgz#f8892abc8f9e627d42aedafbca66bf5ab99104ee"
+  dependencies:
+    bytes "2.4.0"
+    content-type "~1.0.2"
+    debug "2.6.7"
+    depd "~1.1.0"
+    http-errors "~1.6.1"
+    iconv-lite "0.4.15"
+    on-finished "~2.3.0"
+    qs "6.4.0"
+    raw-body "~2.2.0"
+    type-is "~1.6.15"
+
+boolbase@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+
[email protected]:
+  version "2.10.1"
+  resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f"
+  dependencies:
+    hoek "2.x.x"
+
+brace-expansion@^1.1.7:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59"
+  dependencies:
+    balanced-match "^0.4.1"
+    concat-map "0.0.1"
+
+braces@^0.1.2:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-0.1.5.tgz#c085711085291d8b75fdd74eab0f8597280711e6"
+  dependencies:
+    expand-range "^0.1.0"
+
+braces@^1.8.2:
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
+  dependencies:
+    expand-range "^1.8.1"
+    preserve "^0.2.0"
+    repeat-element "^1.1.2"
+
+brorand@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
+
+browser-pack@^6.0.1:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.2.tgz#f86cd6cef4f5300c8e63e07a4d512f65fbff4531"
+  dependencies:
+    JSONStream "^1.0.3"
+    combine-source-map "~0.7.1"
+    defined "^1.0.0"
+    through2 "^2.0.0"
+    umd "^3.0.0"
+
+browser-resolve@^1.11.0, browser-resolve@^1.7.0:
+  version "1.11.2"
+  resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce"
+  dependencies:
+    resolve "1.1.7"
+
+browserify-aes@^1.0.0, browserify-aes@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567ce451c48a0a"
+  dependencies:
+    buffer-xor "^1.0.2"
+    cipher-base "^1.0.0"
+    create-hash "^1.1.0"
+    evp_bytestokey "^1.0.0"
+    inherits "^2.0.1"
+
+browserify-cipher@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a"
+  dependencies:
+    browserify-aes "^1.0.4"
+    browserify-des "^1.0.0"
+    evp_bytestokey "^1.0.0"
+
+browserify-des@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd"
+  dependencies:
+    cipher-base "^1.0.1"
+    des.js "^1.0.0"
+    inherits "^2.0.1"
+
+browserify-rsa@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524"
+  dependencies:
+    bn.js "^4.1.0"
+    randombytes "^2.0.1"
+
+browserify-sign@^4.0.0:
+  version "4.0.4"
+  resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298"
+  dependencies:
+    bn.js "^4.1.1"
+    browserify-rsa "^4.0.0"
+    create-hash "^1.1.0"
+    create-hmac "^1.1.2"
+    elliptic "^6.0.0"
+    inherits "^2.0.1"
+    parse-asn1 "^5.0.0"
+
+browserify-zlib@^0.1.4, browserify-zlib@~0.1.2:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d"
+  dependencies:
+    pako "~0.2.0"
+
+browserify@^14.0.0, browserify@~14.1.0:
+  version "14.1.0"
+  resolved "https://registry.yarnpkg.com/browserify/-/browserify-14.1.0.tgz#0508cc1e7bf4c152312c2fa523e676c0b0b92311"
+  dependencies:
+    JSONStream "^1.0.3"
+    assert "^1.4.0"
+    browser-pack "^6.0.1"
+    browser-resolve "^1.11.0"
+    browserify-zlib "~0.1.2"
+    buffer "^5.0.2"
+    cached-path-relative "^1.0.0"
+    concat-stream "~1.5.1"
+    console-browserify "^1.1.0"
+    constants-browserify "~1.0.0"
+    crypto-browserify "^3.0.0"
+    defined "^1.0.0"
+    deps-sort "^2.0.0"
+    domain-browser "~1.1.0"
+    duplexer2 "~0.1.2"
+    events "~1.1.0"
+    glob "^7.1.0"
+    has "^1.0.0"
+    htmlescape "^1.1.0"
+    https-browserify "~0.0.0"
+    inherits "~2.0.1"
+    insert-module-globals "^7.0.0"
+    labeled-stream-splicer "^2.0.0"
+    module-deps "^4.0.8"
+    os-browserify "~0.1.1"
+    parents "^1.0.1"
+    path-browserify "~0.0.0"
+    process "~0.11.0"
+    punycode "^1.3.2"
+    querystring-es3 "~0.2.0"
+    read-only-stream "^2.0.0"
+    readable-stream "^2.0.2"
+    resolve "^1.1.4"
+    shasum "^1.0.0"
+    shell-quote "^1.6.1"
+    stream-browserify "^2.0.0"
+    stream-http "^2.0.0"
+    string_decoder "~0.10.0"
+    subarg "^1.0.0"
+    syntax-error "^1.1.1"
+    through2 "^2.0.0"
+    timers-browserify "^1.0.1"
+    tty-browserify "~0.0.0"
+    url "~0.11.0"
+    util "~0.10.1"
+    vm-browserify "~0.0.1"
+    xtend "^4.0.0"
+
+buffer-xor@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
+
+buffer@^4.3.0:
+  version "4.9.1"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298"
+  dependencies:
+    base64-js "^1.0.2"
+    ieee754 "^1.1.4"
+    isarray "^1.0.0"
+
+buffer@^5.0.2:
+  version "5.0.6"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.0.6.tgz#2ea669f7eec0b6eda05b08f8b5ff661b28573588"
+  dependencies:
+    base64-js "^1.0.2"
+    ieee754 "^1.1.4"
+
+builtin-modules@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
+
+builtin-status-codes@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
+
[email protected]:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339"
+
+cached-path-relative@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.1.tgz#d09c4b52800aa4c078e2dd81a869aac90d2e54e7"
+
+caller-path@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
+  dependencies:
+    callsites "^0.2.0"
+
[email protected]:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20"
+
+callsites@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
+
+camelcase@^1.0.2:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
+
+camelcase@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
+
+caseless@~0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7"
+
+caseless@~0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+
+center-align@^0.1.1:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad"
+  dependencies:
+    align-text "^0.1.3"
+    lazy-cache "^1.0.3"
+
+chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+  dependencies:
+    ansi-styles "^2.2.1"
+    escape-string-regexp "^1.0.2"
+    has-ansi "^2.0.0"
+    strip-ansi "^3.0.0"
+    supports-color "^2.0.0"
+
+cheerio@^0.22.0:
+  version "0.22.0"
+  resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e"
+  dependencies:
+    css-select "~1.2.0"
+    dom-serializer "~0.1.0"
+    entities "~1.1.1"
+    htmlparser2 "^3.9.1"
+    lodash.assignin "^4.0.9"
+    lodash.bind "^4.1.4"
+    lodash.defaults "^4.0.1"
+    lodash.filter "^4.4.0"
+    lodash.flatten "^4.2.0"
+    lodash.foreach "^4.3.0"
+    lodash.map "^4.4.0"
+    lodash.merge "^4.4.0"
+    lodash.pick "^4.2.1"
+    lodash.reduce "^4.4.0"
+    lodash.reject "^4.4.0"
+    lodash.some "^4.4.0"
+
+chokidar@^1.0.0, chokidar@^1.4.1, chokidar@^1.4.3:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
+  dependencies:
+    anymatch "^1.3.0"
+    async-each "^1.0.0"
+    glob-parent "^2.0.0"
+    inherits "^2.0.1"
+    is-binary-path "^1.0.0"
+    is-glob "^2.0.0"
+    path-is-absolute "^1.0.0"
+    readdirp "^2.0.0"
+  optionalDependencies:
+    fsevents "^1.0.0"
+
+cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07"
+  dependencies:
+    inherits "^2.0.1"
+
+circular-json@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d"
+
+cli-cursor@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
+  dependencies:
+    restore-cursor "^1.0.1"
+
+cli-width@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a"
+
+cliui@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1"
+  dependencies:
+    center-align "^0.1.1"
+    right-align "^0.1.1"
+    wordwrap "0.0.2"
+
+cliui@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
+  dependencies:
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+    wrap-ansi "^2.0.0"
+
+co@^4.6.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+
+code-point-at@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+
+colors@^1.1.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
+
+combine-lists@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/combine-lists/-/combine-lists-1.0.1.tgz#458c07e09e0d900fc28b70a3fec2dacd1d2cb7f6"
+  dependencies:
+    lodash "^4.5.0"
+
+combine-source-map@~0.7.1:
+  version "0.7.2"
+  resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.7.2.tgz#0870312856b307a87cc4ac486f3a9a62aeccc09e"
+  dependencies:
+    convert-source-map "~1.1.0"
+    inline-source-map "~0.6.0"
+    lodash.memoize "~3.0.3"
+    source-map "~0.5.3"
+
+combined-stream@^1.0.5, combined-stream@~1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009"
+  dependencies:
+    delayed-stream "~1.0.0"
+
+commander@^2.9.0:
+  version "2.9.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
+  dependencies:
+    graceful-readlink ">= 1.0.0"
+
+commondir@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+
[email protected]:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
+
[email protected]:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.1.2.tgz#296594f2753daa63996d2af08d15a95116c9aec3"
+
[email protected]:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
+
[email protected]:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143"
+
[email protected]:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+
[email protected]:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.0.tgz#53f7d43c51c5e43f81c8fdd03321c631be68d611"
+  dependencies:
+    inherits "~2.0.1"
+    readable-stream "~2.0.0"
+    typedarray "~0.0.5"
+
+concat-stream@^1.5.2:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
+  dependencies:
+    inherits "^2.0.3"
+    readable-stream "^2.2.2"
+    typedarray "^0.0.6"
+
+concat-stream@~1.5.0, concat-stream@~1.5.1:
+  version "1.5.2"
+  resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266"
+  dependencies:
+    inherits "~2.0.1"
+    readable-stream "~2.0.0"
+    typedarray "~0.0.5"
+
+connect@^3.6.0:
+  version "3.6.2"
+  resolved "https://registry.yarnpkg.com/connect/-/connect-3.6.2.tgz#694e8d20681bfe490282c8ab886be98f09f42fe7"
+  dependencies:
+    debug "2.6.7"
+    finalhandler "1.0.3"
+    parseurl "~1.3.1"
+    utils-merge "1.0.0"
+
+console-browserify@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10"
+  dependencies:
+    date-now "^0.1.4"
+
+console-control-strings@^1.0.0, console-control-strings@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
+
+constants-browserify@^1.0.0, constants-browserify@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
+
+content-type@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed"
+
+convert-source-map@^1.1.0, convert-source-map@^1.1.3:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5"
+
+convert-source-map@~1.1.0:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860"
+
[email protected]:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
+
+core-js@^1.0.0:
+  version "1.2.7"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
+
+core-js@^2.2.0, core-js@^2.4.0:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
+
+core-util-is@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+
+create-ecdh@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d"
+  dependencies:
+    bn.js "^4.1.0"
+    elliptic "^6.0.0"
+
+create-hash@^1.1.0, create-hash@^1.1.1, create-hash@^1.1.2:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd"
+  dependencies:
+    cipher-base "^1.0.1"
+    inherits "^2.0.1"
+    ripemd160 "^2.0.0"
+    sha.js "^2.4.0"
+
+create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06"
+  dependencies:
+    cipher-base "^1.0.3"
+    create-hash "^1.1.0"
+    inherits "^2.0.1"
+    ripemd160 "^2.0.0"
+    safe-buffer "^5.0.1"
+    sha.js "^2.4.8"
+
[email protected]:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
+  dependencies:
+    boom "2.x.x"
+
+crypto-browserify@^3.0.0, crypto-browserify@^3.11.0:
+  version "3.11.0"
+  resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.0.tgz#3652a0906ab9b2a7e0c3ce66a408e957a2485522"
+  dependencies:
+    browserify-cipher "^1.0.0"
+    browserify-sign "^4.0.0"
+    create-ecdh "^4.0.0"
+    create-hash "^1.1.0"
+    create-hmac "^1.1.0"
+    diffie-hellman "^5.0.0"
+    inherits "^2.0.1"
+    pbkdf2 "^3.0.3"
+    public-encrypt "^4.0.0"
+    randombytes "^2.0.0"
+
+css-select@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
+  dependencies:
+    boolbase "~1.0.0"
+    css-what "2.1"
+    domutils "1.5.1"
+    nth-check "~1.0.1"
+
[email protected]:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd"
+
+custom-event@~1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425"
+
+d@1:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f"
+  dependencies:
+    es5-ext "^0.10.9"
+
+dashdash@^1.12.0:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+  dependencies:
+    assert-plus "^1.0.0"
+
+date-now@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
+
[email protected]:
+  version "0.7.4"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39"
+
[email protected]:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
+  dependencies:
+    ms "0.7.1"
+
[email protected]:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.3.3.tgz#40c453e67e6e13c901ddec317af8986cda9eff8c"
+  dependencies:
+    ms "0.7.2"
+
[email protected]:
+  version "2.6.7"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.7.tgz#92bad1f6d05bbb6bba22cca88bcd0ec894c2861e"
+  dependencies:
+    ms "2.0.0"
+
+debug@^2.1.1, debug@^2.2.0, debug@^2.4.5:
+  version "2.6.8"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
+  dependencies:
+    ms "2.0.0"
+
+decamelize@^1.0.0, decamelize@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+
+deep-extend@~0.4.0:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
+
+deep-is@~0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+
+define-properties@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94"
+  dependencies:
+    foreach "^2.0.5"
+    object-keys "^1.0.8"
+
+defined@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
+
+del@^2.0.2:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8"
+  dependencies:
+    globby "^5.0.0"
+    is-path-cwd "^1.0.0"
+    is-path-in-cwd "^1.0.0"
+    object-assign "^4.0.1"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+    rimraf "^2.2.8"
+
+delayed-stream@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+
+delegates@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
+
[email protected], depd@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3"
+
+deps-sort@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.0.tgz#091724902e84658260eb910748cccd1af6e21fb5"
+  dependencies:
+    JSONStream "^1.0.3"
+    shasum "^1.0.0"
+    subarg "^1.0.0"
+    through2 "^2.0.0"
+
+des.js@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc"
+  dependencies:
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+
+detect-indent@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208"
+  dependencies:
+    repeating "^2.0.0"
+
+detective@^4.0.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/detective/-/detective-4.5.0.tgz#6e5a8c6b26e6c7a254b1c6b6d7490d98ec91edd1"
+  dependencies:
+    acorn "^4.0.3"
+    defined "^1.0.0"
+
+di@^0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c"
+
+diffie-hellman@^5.0.0:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e"
+  dependencies:
+    bn.js "^4.1.0"
+    miller-rabin "^4.0.0"
+    randombytes "^2.0.0"
+
+doctrine@^1.2.2:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
+  dependencies:
+    esutils "^2.0.2"
+    isarray "^1.0.0"
+
+doctrine@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63"
+  dependencies:
+    esutils "^2.0.2"
+    isarray "^1.0.0"
+
+dom-serialize@^2.2.0:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b"
+  dependencies:
+    custom-event "~1.0.0"
+    ent "~2.2.0"
+    extend "^3.0.0"
+    void-elements "^2.0.0"
+
+dom-serializer@0, dom-serializer@~0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
+  dependencies:
+    domelementtype "~1.1.1"
+    entities "~1.1.1"
+
+domain-browser@^1.1.1, domain-browser@~1.1.0:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
+
+domelementtype@1, domelementtype@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
+
+domelementtype@~1.1.1:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
+
+domhandler@^2.3.0:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259"
+  dependencies:
+    domelementtype "1"
+
[email protected], domutils@^1.5.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
+  dependencies:
+    dom-serializer "0"
+    domelementtype "1"
+
+duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
+  dependencies:
+    readable-stream "^2.0.2"
+
+ecc-jsbn@~0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
+  dependencies:
+    jsbn "~0.1.0"
+
[email protected]:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+
+elliptic@^6.0.0:
+  version "6.4.0"
+  resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df"
+  dependencies:
+    bn.js "^4.4.0"
+    brorand "^1.0.1"
+    hash.js "^1.0.0"
+    hmac-drbg "^1.0.0"
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+    minimalistic-crypto-utils "^1.0.0"
+
+emojis-list@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
+
+encodeurl@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20"
+
+encoding@^0.1.11:
+  version "0.1.12"
+  resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
+  dependencies:
+    iconv-lite "~0.4.13"
+
[email protected]:
+  version "1.8.3"
+  resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.3.tgz#1798ed93451246453d4c6f635d7a201fe940d5ab"
+  dependencies:
+    component-emitter "1.2.1"
+    component-inherit "0.0.3"
+    debug "2.3.3"
+    engine.io-parser "1.3.2"
+    has-cors "1.1.0"
+    indexof "0.0.1"
+    parsejson "0.0.3"
+    parseqs "0.0.5"
+    parseuri "0.0.5"
+    ws "1.1.2"
+    xmlhttprequest-ssl "1.5.3"
+    yeast "0.1.2"
+
[email protected]:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-1.3.2.tgz#937b079f0007d0893ec56d46cb220b8cb435220a"
+  dependencies:
+    after "0.8.2"
+    arraybuffer.slice "0.0.6"
+    base64-arraybuffer "0.1.5"
+    blob "0.0.4"
+    has-binary "0.1.7"
+    wtf-8 "1.0.0"
+
[email protected]:
+  version "1.8.3"
+  resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-1.8.3.tgz#8de7f97895d20d39b85f88eeee777b2bd42b13d4"
+  dependencies:
+    accepts "1.3.3"
+    base64id "1.0.0"
+    cookie "0.3.1"
+    debug "2.3.3"
+    engine.io-parser "1.3.2"
+    ws "1.1.2"
+
+enhanced-resolve@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.1.0.tgz#9f4b626f577245edcf4b2ad83d86e17f4f421dec"
+  dependencies:
+    graceful-fs "^4.1.2"
+    memory-fs "^0.4.0"
+    object-assign "^4.0.1"
+    tapable "^0.2.5"
+
+ent@~2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d"
+
+entities@^1.1.1, entities@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
+
+enzyme@~2.8.2:
+  version "2.8.2"
+  resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-2.8.2.tgz#6c8bcb05012abc4aa4bc3213fb23780b9b5b1714"
+  dependencies:
+    cheerio "^0.22.0"
+    function.prototype.name "^1.0.0"
+    is-subset "^0.1.1"
+    lodash "^4.17.2"
+    object-is "^1.0.1"
+    object.assign "^4.0.4"
+    object.entries "^1.0.3"
+    object.values "^1.0.3"
+    prop-types "^15.5.4"
+    uuid "^2.0.3"
+
+errno@^0.1.3:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d"
+  dependencies:
+    prr "~0.0.0"
+
+error-ex@^1.2.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc"
+  dependencies:
+    is-arrayish "^0.2.1"
+
+es-abstract@^1.6.1, es-abstract@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.7.0.tgz#dfade774e01bfcd97f96180298c449c8623fb94c"
+  dependencies:
+    es-to-primitive "^1.1.1"
+    function-bind "^1.1.0"
+    is-callable "^1.1.3"
+    is-regex "^1.0.3"
+
+es-to-primitive@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d"
+  dependencies:
+    is-callable "^1.1.1"
+    is-date-object "^1.0.1"
+    is-symbol "^1.0.1"
+
+es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14:
+  version "0.10.23"
+  resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.23.tgz#7578b51be974207a5487821b56538c224e4e7b38"
+  dependencies:
+    es6-iterator "2"
+    es6-symbol "~3.1"
+
+es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512"
+  dependencies:
+    d "1"
+    es5-ext "^0.10.14"
+    es6-symbol "^3.1"
+
+es6-map@^0.1.3:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0"
+  dependencies:
+    d "1"
+    es5-ext "~0.10.14"
+    es6-iterator "~2.0.1"
+    es6-set "~0.1.5"
+    es6-symbol "~3.1.1"
+    event-emitter "~0.3.5"
+
+es6-promise@~4.0.3:
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.0.5.tgz#7882f30adde5b240ccfa7f7d78c548330951ae42"
+
+es6-set@~0.1.5:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1"
+  dependencies:
+    d "1"
+    es5-ext "~0.10.14"
+    es6-iterator "~2.0.1"
+    es6-symbol "3.1.1"
+    event-emitter "~0.3.5"
+
[email protected], es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77"
+  dependencies:
+    d "1"
+    es5-ext "~0.10.14"
+
+es6-weak-map@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f"
+  dependencies:
+    d "1"
+    es5-ext "^0.10.14"
+    es6-iterator "^2.0.1"
+    es6-symbol "^3.1.1"
+
+escape-html@~1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+
+escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+
+escope@^3.6.0:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3"
+  dependencies:
+    es6-map "^0.1.3"
+    es6-weak-map "^2.0.1"
+    esrecurse "^4.1.0"
+    estraverse "^4.1.1"
+
+eslint-plugin-react@^6.10.3:
+  version "6.10.3"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz#c5435beb06774e12c7db2f6abaddcbf900cd3f78"
+  dependencies:
+    array.prototype.find "^2.0.1"
+    doctrine "^1.2.2"
+    has "^1.0.1"
+    jsx-ast-utils "^1.3.4"
+    object.assign "^4.0.4"
+
+eslint@^3.19.0:
+  version "3.19.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc"
+  dependencies:
+    babel-code-frame "^6.16.0"
+    chalk "^1.1.3"
+    concat-stream "^1.5.2"
+    debug "^2.1.1"
+    doctrine "^2.0.0"
+    escope "^3.6.0"
+    espree "^3.4.0"
+    esquery "^1.0.0"
+    estraverse "^4.2.0"
+    esutils "^2.0.2"
+    file-entry-cache "^2.0.0"
+    glob "^7.0.3"
+    globals "^9.14.0"
+    ignore "^3.2.0"
+    imurmurhash "^0.1.4"
+    inquirer "^0.12.0"
+    is-my-json-valid "^2.10.0"
+    is-resolvable "^1.0.0"
+    js-yaml "^3.5.1"
+    json-stable-stringify "^1.0.0"
+    levn "^0.3.0"
+    lodash "^4.0.0"
+    mkdirp "^0.5.0"
+    natural-compare "^1.4.0"
+    optionator "^0.8.2"
+    path-is-inside "^1.0.1"
+    pluralize "^1.2.1"
+    progress "^1.1.8"
+    require-uncached "^1.0.2"
+    shelljs "^0.7.5"
+    strip-bom "^3.0.0"
+    strip-json-comments "~2.0.1"
+    table "^3.7.8"
+    text-table "~0.2.0"
+    user-home "^2.0.0"
+
+espree@^3.4.0:
+  version "3.4.3"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.3.tgz#2910b5ccd49ce893c2ffffaab4fd8b3a31b82374"
+  dependencies:
+    acorn "^5.0.1"
+    acorn-jsx "^3.0.0"
+
+esprima@^3.1.1:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
+
+esquery@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa"
+  dependencies:
+    estraverse "^4.0.0"
+
+esrecurse@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220"
+  dependencies:
+    estraverse "~4.1.0"
+    object-assign "^4.0.1"
+
+estraverse@^4.0.0, estraverse@^4.1.1, estraverse@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
+
+estraverse@~4.1.0:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2"
+
+esutils@^2.0.0, esutils@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
+
+event-emitter@~0.3.5:
+  version "0.3.5"
+  resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"
+  dependencies:
+    d "1"
+    es5-ext "~0.10.14"
+
[email protected]:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508"
+
+events@^1.0.0, events@~1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
+
+evp_bytestokey@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz#497b66ad9fef65cd7c08a6180824ba1476b66e53"
+  dependencies:
+    create-hash "^1.1.1"
+
+exit-hook@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
+
+expand-braces@^0.1.1:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/expand-braces/-/expand-braces-0.1.2.tgz#488b1d1d2451cb3d3a6b192cfc030f44c5855fea"
+  dependencies:
+    array-slice "^0.2.3"
+    array-unique "^0.2.1"
+    braces "^0.1.2"
+
+expand-brackets@^0.1.4:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
+  dependencies:
+    is-posix-bracket "^0.1.0"
+
+expand-range@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-0.1.1.tgz#4cb8eda0993ca56fa4f41fc42f3cbb4ccadff044"
+  dependencies:
+    is-number "^0.1.1"
+    repeat-string "^0.2.2"
+
+expand-range@^1.8.1:
+  version "1.8.2"
+  resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337"
+  dependencies:
+    fill-range "^2.1.0"
+
+exports-loader@~0.6.4:
+  version "0.6.4"
+  resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.6.4.tgz#d70fc6121975b35fc12830cf52754be2740fc886"
+  dependencies:
+    loader-utils "^1.0.2"
+    source-map "0.5.x"
+
+extend@^3.0.0, extend@~3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
+
+extglob@^0.3.1:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
+  dependencies:
+    is-extglob "^1.0.0"
+
+extract-zip@~1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.5.0.tgz#92ccf6d81ef70a9fa4c1747114ccef6d8688a6c4"
+  dependencies:
+    concat-stream "1.5.0"
+    debug "0.7.4"
+    mkdirp "0.5.0"
+    yauzl "2.4.1"
+
[email protected]:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550"
+
+fast-levenshtein@~2.0.4:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+
+fbjs@^0.8.1, fbjs@^0.8.4, fbjs@^0.8.9:
+  version "0.8.12"
+  resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.12.tgz#10b5d92f76d45575fd63a217d4ea02bea2f8ed04"
+  dependencies:
+    core-js "^1.0.0"
+    isomorphic-fetch "^2.1.1"
+    loose-envify "^1.0.0"
+    object-assign "^4.1.0"
+    promise "^7.1.1"
+    setimmediate "^1.0.5"
+    ua-parser-js "^0.7.9"
+
+fd-slicer@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
+  dependencies:
+    pend "~1.2.0"
+
+figures@^1.3.5:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
+  dependencies:
+    escape-string-regexp "^1.0.5"
+    object-assign "^4.1.0"
+
+file-entry-cache@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361"
+  dependencies:
+    flat-cache "^1.2.1"
+    object-assign "^4.0.1"
+
+filename-regex@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
+
+fill-range@^2.1.0:
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723"
+  dependencies:
+    is-number "^2.1.0"
+    isobject "^2.0.0"
+    randomatic "^1.1.3"
+    repeat-element "^1.1.2"
+    repeat-string "^1.5.2"
+
[email protected]:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.3.tgz#ef47e77950e999780e86022a560e3217e0d0cc89"
+  dependencies:
+    debug "2.6.7"
+    encodeurl "~1.0.1"
+    escape-html "~1.0.3"
+    on-finished "~2.3.0"
+    parseurl "~1.3.1"
+    statuses "~1.3.1"
+    unpipe "~1.0.0"
+
+find-cache-dir@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9"
+  dependencies:
+    commondir "^1.0.1"
+    mkdirp "^0.5.1"
+    pkg-dir "^1.0.0"
+
+find-up@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
+  dependencies:
+    path-exists "^2.0.0"
+    pinkie-promise "^2.0.0"
+
+flat-cache@^1.2.1:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96"
+  dependencies:
+    circular-json "^0.3.1"
+    del "^2.0.2"
+    graceful-fs "^4.1.2"
+    write "^0.2.1"
+
+follow-redirects@^1.2.3:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.3.tgz#01abaeca85e3609837d9fcda3167a7e42fdaca21"
+  dependencies:
+    debug "^2.4.5"
+
+for-in@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
+
+for-own@^0.1.4:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce"
+  dependencies:
+    for-in "^1.0.1"
+
+foreach@^2.0.5:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
+
+forever-agent@~0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+
+form-data@~2.1.1:
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.5"
+    mime-types "^2.1.12"
+
+fs-extra@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950"
+  dependencies:
+    graceful-fs "^4.1.2"
+    jsonfile "^2.1.0"
+    klaw "^1.0.0"
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+
+fsevents@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.1.tgz#f19fd28f43eeaf761680e519a203c4d0b3d31aff"
+  dependencies:
+    nan "^2.3.0"
+    node-pre-gyp "^0.6.29"
+
+fstream-ignore@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105"
+  dependencies:
+    fstream "^1.0.0"
+    inherits "2"
+    minimatch "^3.0.0"
+
+fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2:
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171"
+  dependencies:
+    graceful-fs "^4.1.2"
+    inherits "~2.0.0"
+    mkdirp ">=0.5 0"
+    rimraf "2"
+
+function-bind@^1.0.2, function-bind@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771"
+
+function.prototype.name@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.0.0.tgz#5f523ca64e491a5f95aba80cc1e391080a14482e"
+  dependencies:
+    define-properties "^1.1.2"
+    function-bind "^1.1.0"
+    is-callable "^1.1.2"
+
+gauge@~2.7.3:
+  version "2.7.4"
+  resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
+  dependencies:
+    aproba "^1.0.3"
+    console-control-strings "^1.0.0"
+    has-unicode "^2.0.0"
+    object-assign "^4.1.0"
+    signal-exit "^3.0.0"
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+    wide-align "^1.1.0"
+
+generate-function@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74"
+
+generate-object-property@^1.1.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0"
+  dependencies:
+    is-property "^1.0.0"
+
+get-caller-file@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
+
+getpass@^0.1.1:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+  dependencies:
+    assert-plus "^1.0.0"
+
+glob-base@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
+  dependencies:
+    glob-parent "^2.0.0"
+    is-glob "^2.0.0"
+
+glob-parent@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28"
+  dependencies:
+    is-glob "^2.0.0"
+
+glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.0, glob@^7.1.1:
+  version "7.1.2"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.0.4"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+globals@^9.0.0, globals@^9.14.0:
+  version "9.18.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
+
+globby@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
+  dependencies:
+    array-union "^1.0.1"
+    arrify "^1.0.0"
+    glob "^7.0.3"
+    object-assign "^4.0.1"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+
+graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
+  version "4.1.11"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
+
+"graceful-readlink@>= 1.0.0":
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
+
+har-schema@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
+
+har-validator@~2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d"
+  dependencies:
+    chalk "^1.1.1"
+    commander "^2.9.0"
+    is-my-json-valid "^2.12.4"
+    pinkie-promise "^2.0.0"
+
+har-validator@~4.2.1:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a"
+  dependencies:
+    ajv "^4.9.1"
+    har-schema "^1.0.5"
+
+has-ansi@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+  dependencies:
+    ansi-regex "^2.0.0"
+
[email protected]:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/has-binary/-/has-binary-0.1.7.tgz#68e61eb16210c9545a0a5cce06a873912fe1e68c"
+  dependencies:
+    isarray "0.0.1"
+
[email protected]:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39"
+
+has-flag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
+
+has-unicode@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
+
+has@^1.0.0, has@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
+  dependencies:
+    function-bind "^1.0.2"
+
+hash-base@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1"
+  dependencies:
+    inherits "^2.0.1"
+
+hash.js@^1.0.0, hash.js@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573"
+  dependencies:
+    inherits "^2.0.1"
+
+hasha@~2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/hasha/-/hasha-2.2.0.tgz#78d7cbfc1e6d66303fe79837365984517b2f6ee1"
+  dependencies:
+    is-stream "^1.0.1"
+    pinkie-promise "^2.0.0"
+
+hat@^0.0.3:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/hat/-/hat-0.0.3.tgz#bb014a9e64b3788aed8005917413d4ff3d502d8a"
+
+hawk@~3.1.3:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
+  dependencies:
+    boom "2.x.x"
+    cryptiles "2.x.x"
+    hoek "2.x.x"
+    sntp "1.x.x"
+
+hmac-drbg@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
+  dependencies:
+    hash.js "^1.0.3"
+    minimalistic-assert "^1.0.0"
+    minimalistic-crypto-utils "^1.0.1"
+
[email protected]:
+  version "2.16.3"
+  resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
+
+home-or-tmp@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
+  dependencies:
+    os-homedir "^1.0.0"
+    os-tmpdir "^1.0.1"
+
+hosted-git-info@^2.1.4:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.2.tgz#0076b9f46a270506ddbaaea56496897460612a67"
+
+htmlescape@^1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351"
+
+htmlparser2@^3.9.1:
+  version "3.9.2"
+  resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
+  dependencies:
+    domelementtype "^1.3.0"
+    domhandler "^2.3.0"
+    domutils "^1.5.1"
+    entities "^1.1.1"
+    inherits "^2.0.1"
+    readable-stream "^2.0.2"
+
+http-errors@~1.6.1:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.1.tgz#5f8b8ed98aca545656bf572997387f904a722257"
+  dependencies:
+    depd "1.1.0"
+    inherits "2.0.3"
+    setprototypeof "1.0.3"
+    statuses ">= 1.3.1 < 2"
+
+http-proxy@^1.13.0:
+  version "1.16.2"
+  resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f37742"
+  dependencies:
+    eventemitter3 "1.x.x"
+    requires-port "1.x.x"
+
+http-signature@~1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf"
+  dependencies:
+    assert-plus "^0.2.0"
+    jsprim "^1.2.2"
+    sshpk "^1.7.0"
+
[email protected], https-browserify@~0.0.0:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82"
+
[email protected], iconv-lite@~0.4.13:
+  version "0.4.15"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
+
+ieee754@^1.1.4:
+  version "1.1.8"
+  resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
+
+ignore@^3.2.0:
+  version "3.3.3"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d"
+
+"imports-loader@git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d":
+  version "0.7.1"
+  resolved "git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d"
+  dependencies:
+    loader-utils "^1.0.2"
+    source-map "^0.5.6"
+
+imurmurhash@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+
[email protected]:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2, [email protected], inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+
[email protected]:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
+
+ini@~1.3.0:
+  version "1.3.4"
+  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
+
+inline-source-map@~0.6.0:
+  version "0.6.2"
+  resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.6.2.tgz#f9393471c18a79d1724f863fa38b586370ade2a5"
+  dependencies:
+    source-map "~0.5.3"
+
+inquirer@^0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e"
+  dependencies:
+    ansi-escapes "^1.1.0"
+    ansi-regex "^2.0.0"
+    chalk "^1.0.0"
+    cli-cursor "^1.0.1"
+    cli-width "^2.0.0"
+    figures "^1.3.5"
+    lodash "^4.3.0"
+    readline2 "^1.0.1"
+    run-async "^0.1.0"
+    rx-lite "^3.1.2"
+    string-width "^1.0.1"
+    strip-ansi "^3.0.0"
+    through "^2.3.6"
+
+insert-module-globals@^7.0.0:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.0.1.tgz#c03bf4e01cb086d5b5e5ace8ad0afe7889d638c3"
+  dependencies:
+    JSONStream "^1.0.3"
+    combine-source-map "~0.7.1"
+    concat-stream "~1.5.1"
+    is-buffer "^1.1.0"
+    lexical-scope "^1.2.0"
+    process "~0.11.0"
+    through2 "^2.0.0"
+    xtend "^4.0.0"
+
+interpret@^1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90"
+
+invariant@^2.2.0:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
+  dependencies:
+    loose-envify "^1.0.0"
+
+invert-kv@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
+
+is-arrayish@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+
+is-binary-path@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
+  dependencies:
+    binary-extensions "^1.0.0"
+
+is-buffer@^1.1.0, is-buffer@^1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc"
+
+is-builtin-module@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
+  dependencies:
+    builtin-modules "^1.0.0"
+
+is-callable@^1.1.1, is-callable@^1.1.2, is-callable@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2"
+
+is-date-object@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
+
+is-dotfile@^1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1"
+
+is-equal-shallow@^0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534"
+  dependencies:
+    is-primitive "^2.0.0"
+
+is-extendable@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+
+is-extglob@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
+
+is-finite@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa"
+  dependencies:
+    number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+  dependencies:
+    number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+
+is-glob@^2.0.0, is-glob@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
+  dependencies:
+    is-extglob "^1.0.0"
+
+is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4:
+  version "2.16.0"
+  resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693"
+  dependencies:
+    generate-function "^2.0.0"
+    generate-object-property "^1.1.0"
+    jsonpointer "^4.0.0"
+    xtend "^4.0.0"
+
+is-number@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806"
+
+is-number@^2.0.2, is-number@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
+  dependencies:
+    kind-of "^3.0.2"
+
+is-path-cwd@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
+
+is-path-in-cwd@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc"
+  dependencies:
+    is-path-inside "^1.0.0"
+
+is-path-inside@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f"
+  dependencies:
+    path-is-inside "^1.0.1"
+
+is-posix-bracket@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
+
+is-primitive@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
+
+is-property@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
+
+is-regex@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
+  dependencies:
+    has "^1.0.1"
+
+is-resolvable@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62"
+  dependencies:
+    tryit "^1.0.1"
+
+is-stream@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+
+is-subset@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6"
+
+is-symbol@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572"
+
+is-typedarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+
+is-utf8@^0.2.0:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
+
[email protected], isarray@~0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
+
[email protected], isarray@^1.0.0, isarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+
+isbinaryfile@^3.0.0:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621"
+
+isexe@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+
+isobject@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
+  dependencies:
+    isarray "1.0.0"
+
+isomorphic-fetch@^2.1.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
+  dependencies:
+    node-fetch "^1.0.1"
+    whatwg-fetch ">=0.10.0"
+
+isstream@~0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+
+jasmine-core@~2.5.2:
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.5.2.tgz#6f61bd79061e27f43e6f9355e44b3c6cab6ff297"
+
+jodid25519@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967"
+  dependencies:
+    jsbn "~0.1.0"
+
+js-string-escape@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef"
+
+js-tokens@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7"
+
+js-yaml@^3.5.1:
+  version "3.8.4"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6"
+  dependencies:
+    argparse "^1.0.7"
+    esprima "^3.1.1"
+
+jsbn@~0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+
+jsesc@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
+
+jsesc@~0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
+
+json-loader@^0.5.4:
+  version "0.5.4"
+  resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.4.tgz#8baa1365a632f58a3c46d20175fc6002c96e37de"
+
[email protected]:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
+
+json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
+  dependencies:
+    jsonify "~0.0.0"
+
+json-stable-stringify@~0.0.0:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz#611c23e814db375527df851193db59dd2af27f45"
+  dependencies:
+    jsonify "~0.0.0"
+
+json-stringify-safe@~5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+
[email protected]:
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
+
+json5@^0.5.0:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
+
+jsonfile@^2.1.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8"
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
+jsonify@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
+
+jsonparse@^1.2.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
+
+jsonpointer@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9"
+
+jsprim@^1.2.2:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918"
+  dependencies:
+    assert-plus "1.0.0"
+    extsprintf "1.0.2"
+    json-schema "0.2.3"
+    verror "1.3.6"
+
+jsx-ast-utils@^1.3.4:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1"
+
+karma-babel-preprocessor@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/karma-babel-preprocessor/-/karma-babel-preprocessor-6.0.1.tgz#7ae1d3e64950dbe11f421b74040ab08fb5a66c21"
+  dependencies:
+    babel-core "^6.0.0"
+
+karma-browserify@~5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/karma-browserify/-/karma-browserify-5.1.1.tgz#f642d70d776d9ab3b73526c5732abcfea2400319"
+  dependencies:
+    convert-source-map "^1.1.3"
+    hat "^0.0.3"
+    js-string-escape "^1.0.0"
+    lodash "^3.10.1"
+    minimatch "^3.0.0"
+    os-shim "^0.1.3"
+
+karma-jasmine-html-reporter@~0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz#48a8e5ef18807617ee2b5e33c1194c35b439524c"
+  dependencies:
+    karma-jasmine "^1.0.2"
+
+karma-jasmine@^1.0.2, karma-jasmine@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.0.tgz#22e4c06bf9a182e5294d1f705e3733811b810acf"
+
+karma-phantomjs-launcher@~1.0.2:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/karma-phantomjs-launcher/-/karma-phantomjs-launcher-1.0.4.tgz#d23ca34801bda9863ad318e3bb4bd4062b13acd2"
+  dependencies:
+    lodash "^4.0.1"
+    phantomjs-prebuilt "^2.1.7"
+
+karma-requirejs@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/karma-requirejs/-/karma-requirejs-1.1.0.tgz#fddae2cb87d7ebc16fb0222893564d7fee578798"
+
+karma-sourcemap-loader@~0.3.7:
+  version "0.3.7"
+  resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.7.tgz#91322c77f8f13d46fed062b042e1009d4c4505d8"
+  dependencies:
+    graceful-fs "^4.1.2"
+
+karma-webpack@~2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-2.0.3.tgz#39cebf5ca2580139b27f9ae69b78816b9c82fae6"
+  dependencies:
+    async "~0.9.0"
+    loader-utils "^0.2.5"
+    lodash "^3.8.0"
+    source-map "^0.1.41"
+    webpack-dev-middleware "^1.0.11"
+
+karma@~1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/karma/-/karma-1.5.0.tgz#9c4c14f0400bef2c04c8e8e6bff59371025cc009"
+  dependencies:
+    bluebird "^3.3.0"
+    body-parser "^1.16.1"
+    chokidar "^1.4.1"
+    colors "^1.1.0"
+    combine-lists "^1.0.0"
+    connect "^3.6.0"
+    core-js "^2.2.0"
+    di "^0.0.1"
+    dom-serialize "^2.2.0"
+    expand-braces "^0.1.1"
+    glob "^7.1.1"
+    graceful-fs "^4.1.2"
+    http-proxy "^1.13.0"
+    isbinaryfile "^3.0.0"
+    lodash "^3.8.0"
+    log4js "^0.6.31"
+    mime "^1.3.4"
+    minimatch "^3.0.0"
+    optimist "^0.6.1"
+    qjobs "^1.1.4"
+    range-parser "^1.2.0"
+    rimraf "^2.6.0"
+    safe-buffer "^5.0.1"
+    socket.io "1.7.3"
+    source-map "^0.5.3"
+    tmp "0.0.31"
+    useragent "^2.1.12"
+
+kew@~0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b"
+
+kind-of@^3.0.2:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+  dependencies:
+    is-buffer "^1.1.5"
+
+klaw@^1.0.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439"
+  optionalDependencies:
+    graceful-fs "^4.1.9"
+
+labeled-stream-splicer@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz#a52e1d138024c00b86b1c0c91f677918b8ae0a59"
+  dependencies:
+    inherits "^2.0.1"
+    isarray "~0.0.1"
+    stream-splicer "^2.0.0"
+
+lazy-cache@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
+
+lcid@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
+  dependencies:
+    invert-kv "^1.0.0"
+
+levn@^0.3.0, levn@~0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
+  dependencies:
+    prelude-ls "~1.1.2"
+    type-check "~0.3.2"
+
+lexical-scope@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/lexical-scope/-/lexical-scope-1.2.0.tgz#fcea5edc704a4b3a8796cdca419c3a0afaf22df4"
+  dependencies:
+    astw "^2.0.0"
+
+load-json-file@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
+  dependencies:
+    graceful-fs "^4.1.2"
+    parse-json "^2.2.0"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+    strip-bom "^2.0.0"
+
+loader-runner@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2"
+
+loader-utils@^0.2.16, loader-utils@^0.2.5:
+  version "0.2.17"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348"
+  dependencies:
+    big.js "^3.1.3"
+    emojis-list "^2.0.0"
+    json5 "^0.5.0"
+    object-assign "^4.0.1"
+
+loader-utils@^1.0.2:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd"
+  dependencies:
+    big.js "^3.1.3"
+    emojis-list "^2.0.0"
+    json5 "^0.5.0"
+
+lodash.assignin@^4.0.9:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2"
+
+lodash.bind@^4.1.4:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35"
+
+lodash.defaults@^4.0.1:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
+
+lodash.filter@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace"
+
+lodash.flatten@^4.2.0:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
+
+lodash.foreach@^4.3.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
+
+lodash.map@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3"
+
+lodash.memoize@~3.0.3:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f"
+
+lodash.merge@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5"
+
+lodash.pick@^4.2.1:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
+
+lodash.reduce@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b"
+
+lodash.reject@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415"
+
+lodash.some@^4.4.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
+
+lodash@^3.10.1, lodash@^3.8.0:
+  version "3.10.1"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
+
+lodash@^4.0.0, lodash@^4.0.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
+  version "4.17.4"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
+
+log4js@^0.6.31:
+  version "0.6.38"
+  resolved "https://registry.yarnpkg.com/log4js/-/log4js-0.6.38.tgz#2c494116695d6fb25480943d3fc872e662a522fd"
+  dependencies:
+    readable-stream "~1.0.2"
+    semver "~4.3.3"
+
+longest@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
+
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
+  dependencies:
+    js-tokens "^3.0.0"
+
[email protected]:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d"
+
[email protected]:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
+
+memory-fs@^0.4.0, memory-fs@~0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
+  dependencies:
+    errno "^0.1.3"
+    readable-stream "^2.0.1"
+
+micromatch@^2.1.5:
+  version "2.3.11"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
+  dependencies:
+    arr-diff "^2.0.0"
+    array-unique "^0.2.1"
+    braces "^1.8.2"
+    expand-brackets "^0.1.4"
+    extglob "^0.3.1"
+    filename-regex "^2.0.0"
+    is-extglob "^1.0.0"
+    is-glob "^2.0.1"
+    kind-of "^3.0.2"
+    normalize-path "^2.0.1"
+    object.omit "^2.0.0"
+    parse-glob "^3.0.4"
+    regex-cache "^0.4.2"
+
+miller-rabin@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d"
+  dependencies:
+    bn.js "^4.0.0"
+    brorand "^1.0.1"
+
+mime-db@~1.27.0:
+  version "1.27.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1"
+
+mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7:
+  version "2.1.15"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed"
+  dependencies:
+    mime-db "~1.27.0"
+
+mime@^1.3.4:
+  version "1.3.6"
+  resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0"
+
+minimalistic-assert@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
+
+minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
+
+minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+  dependencies:
+    brace-expansion "^1.1.7"
+
[email protected], minimist@~0.0.1:
+  version "0.0.8"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+
+minimist@^1.1.0, minimist@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+
[email protected]:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12"
+  dependencies:
+    minimist "0.0.8"
+
+"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+  dependencies:
+    minimist "0.0.8"
+
+module-deps@^4.0.8:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-4.1.1.tgz#23215833f1da13fd606ccb8087b44852dcb821fd"
+  dependencies:
+    JSONStream "^1.0.3"
+    browser-resolve "^1.7.0"
+    cached-path-relative "^1.0.0"
+    concat-stream "~1.5.0"
+    defined "^1.0.0"
+    detective "^4.0.0"
+    duplexer2 "^0.1.2"
+    inherits "^2.0.1"
+    parents "^1.0.0"
+    readable-stream "^2.0.2"
+    resolve "^1.1.3"
+    stream-combiner2 "^1.1.1"
+    subarg "^1.0.0"
+    through2 "^2.0.0"
+    xtend "^4.0.0"
+
[email protected]:
+  version "0.7.1"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
+
[email protected]:
+  version "0.7.2"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765"
+
[email protected]:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+
[email protected]:
+  version "0.0.5"
+  resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0"
+
+nan@^2.3.0:
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45"
+
+natural-compare@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+
[email protected]:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
+
+node-fetch@^1.0.1:
+  version "1.7.1"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.1.tgz#899cb3d0a3c92f952c47f1b876f4c8aeabd400d5"
+  dependencies:
+    encoding "^0.1.11"
+    is-stream "^1.0.1"
+
+node-libs-browser@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.0.0.tgz#a3a59ec97024985b46e958379646f96c4b616646"
+  dependencies:
+    assert "^1.1.1"
+    browserify-zlib "^0.1.4"
+    buffer "^4.3.0"
+    console-browserify "^1.1.0"
+    constants-browserify "^1.0.0"
+    crypto-browserify "^3.11.0"
+    domain-browser "^1.1.1"
+    events "^1.0.0"
+    https-browserify "0.0.1"
+    os-browserify "^0.2.0"
+    path-browserify "0.0.0"
+    process "^0.11.0"
+    punycode "^1.2.4"
+    querystring-es3 "^0.2.0"
+    readable-stream "^2.0.5"
+    stream-browserify "^2.0.1"
+    stream-http "^2.3.1"
+    string_decoder "^0.10.25"
+    timers-browserify "^2.0.2"
+    tty-browserify "0.0.0"
+    url "^0.11.0"
+    util "^0.10.3"
+    vm-browserify "0.0.4"
+
+node-pre-gyp@^0.6.29:
+  version "0.6.36"
+  resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786"
+  dependencies:
+    mkdirp "^0.5.1"
+    nopt "^4.0.1"
+    npmlog "^4.0.2"
+    rc "^1.1.7"
+    request "^2.81.0"
+    rimraf "^2.6.1"
+    semver "^5.3.0"
+    tar "^2.2.1"
+    tar-pack "^3.4.0"
+
+nopt@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
+  dependencies:
+    abbrev "1"
+    osenv "^0.1.4"
+
+normalize-package-data@^2.3.2:
+  version "2.3.8"
+  resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.8.tgz#d819eda2a9dedbd1ffa563ea4071d936782295bb"
+  dependencies:
+    hosted-git-info "^2.1.4"
+    is-builtin-module "^1.0.0"
+    semver "2 || 3 || 4 || 5"
+    validate-npm-package-license "^3.0.1"
+
+normalize-path@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
+  dependencies:
+    remove-trailing-separator "^1.0.1"
+
+npmlog@^4.0.2:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.0.tgz#dc59bee85f64f00ed424efb2af0783df25d1c0b5"
+  dependencies:
+    are-we-there-yet "~1.1.2"
+    console-control-strings "~1.1.0"
+    gauge "~2.7.3"
+    set-blocking "~2.0.0"
+
+nth-check@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4"
+  dependencies:
+    boolbase "~1.0.0"
+
+number-is-nan@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+
+oauth-sign@~0.8.1:
+  version "0.8.2"
+  resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
+
[email protected]:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0"
+
+object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+
[email protected]:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291"
+
+object-is@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6"
+
+object-keys@^1.0.10, object-keys@^1.0.8:
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d"
+
+object.assign@^4.0.4:
+  version "4.0.4"
+  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.0.4.tgz#b1c9cc044ef1b9fe63606fc141abbb32e14730cc"
+  dependencies:
+    define-properties "^1.1.2"
+    function-bind "^1.1.0"
+    object-keys "^1.0.10"
+
+object.entries@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.0.4.tgz#1bf9a4dd2288f5b33f3a993d257661f05d161a5f"
+  dependencies:
+    define-properties "^1.1.2"
+    es-abstract "^1.6.1"
+    function-bind "^1.1.0"
+    has "^1.0.1"
+
+object.omit@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
+  dependencies:
+    for-own "^0.1.4"
+    is-extendable "^0.1.1"
+
+object.values@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.0.4.tgz#e524da09b4f66ff05df457546ec72ac99f13069a"
+  dependencies:
+    define-properties "^1.1.2"
+    es-abstract "^1.6.1"
+    function-bind "^1.1.0"
+    has "^1.0.1"
+
+on-finished@~2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
+  dependencies:
+    ee-first "1.1.1"
+
+once@^1.3.0, once@^1.3.3:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  dependencies:
+    wrappy "1"
+
+onetime@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
+
+optimist@^0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
+  dependencies:
+    minimist "~0.0.1"
+    wordwrap "~0.0.2"
+
+optionator@^0.8.2:
+  version "0.8.2"
+  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
+  dependencies:
+    deep-is "~0.1.3"
+    fast-levenshtein "~2.0.4"
+    levn "~0.3.0"
+    prelude-ls "~1.1.2"
+    type-check "~0.3.2"
+    wordwrap "~1.0.0"
+
+options@>=0.0.5:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f"
+
+os-browserify@^0.2.0:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f"
+
+os-browserify@~0.1.1:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.1.2.tgz#49ca0293e0b19590a5f5de10c7f265a617d8fe54"
+
+os-homedir@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
+
+os-locale@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9"
+  dependencies:
+    lcid "^1.0.0"
+
+os-shim@^0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917"
+
+os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+
+osenv@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644"
+  dependencies:
+    os-homedir "^1.0.0"
+    os-tmpdir "^1.0.0"
+
+outpipe@^1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/outpipe/-/outpipe-1.1.1.tgz#50cf8616365e87e031e29a5ec9339a3da4725fa2"
+  dependencies:
+    shell-quote "^1.4.2"
+
+pako@~0.2.0:
+  version "0.2.9"
+  resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
+
+parents@^1.0.0, parents@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751"
+  dependencies:
+    path-platform "~0.11.15"
+
+parse-asn1@^5.0.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712"
+  dependencies:
+    asn1.js "^4.0.0"
+    browserify-aes "^1.0.0"
+    create-hash "^1.1.0"
+    evp_bytestokey "^1.0.0"
+    pbkdf2 "^3.0.3"
+
+parse-glob@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
+  dependencies:
+    glob-base "^0.3.0"
+    is-dotfile "^1.0.0"
+    is-extglob "^1.0.0"
+    is-glob "^2.0.0"
+
+parse-json@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
+  dependencies:
+    error-ex "^1.2.0"
+
[email protected]:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/parsejson/-/parsejson-0.0.3.tgz#ab7e3759f209ece99437973f7d0f1f64ae0e64ab"
+  dependencies:
+    better-assert "~1.0.0"
+
[email protected]:
+  version "0.0.5"
+  resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
+  dependencies:
+    better-assert "~1.0.0"
+
[email protected]:
+  version "0.0.5"
+  resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a"
+  dependencies:
+    better-assert "~1.0.0"
+
+parseurl@~1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56"
+
[email protected], path-browserify@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a"
+
+path-exists@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
+  dependencies:
+    pinkie-promise "^2.0.0"
+
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+
+path-is-inside@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
+
+path-parse@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
+
+path-platform@~0.11.15:
+  version "0.11.15"
+  resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2"
+
+path-type@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
+  dependencies:
+    graceful-fs "^4.1.2"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+
+pbkdf2@^3.0.3:
+  version "3.0.12"
+  resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.12.tgz#be36785c5067ea48d806ff923288c5f750b6b8a2"
+  dependencies:
+    create-hash "^1.1.2"
+    create-hmac "^1.1.4"
+    ripemd160 "^2.0.1"
+    safe-buffer "^5.0.1"
+    sha.js "^2.4.8"
+
+pend@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
+
+performance-now@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
+
+phantomjs-prebuilt@^2.1.7:
+  version "2.1.14"
+  resolved "https://registry.yarnpkg.com/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.14.tgz#d53d311fcfb7d1d08ddb24014558f1188c516da0"
+  dependencies:
+    es6-promise "~4.0.3"
+    extract-zip "~1.5.0"
+    fs-extra "~1.0.0"
+    hasha "~2.2.0"
+    kew "~0.7.0"
+    progress "~1.1.8"
+    request "~2.79.0"
+    request-progress "~2.0.1"
+    which "~1.2.10"
+
+pify@^2.0.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+
+pinkie-promise@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+  dependencies:
+    pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+
+pkg-dir@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4"
+  dependencies:
+    find-up "^1.0.0"
+
+pluralize@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45"
+
+prelude-ls@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+
+preserve@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
+
+private@^0.1.6:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1"
+
+process-nextick-args@~1.0.6:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
+
+process@^0.11.0, process@~0.11.0:
+  version "0.11.10"
+  resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+
+progress@^1.1.8, progress@~1.1.8:
+  version "1.1.8"
+  resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
+
+promise@^7.1.1:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf"
+  dependencies:
+    asap "~2.0.3"
+
+prop-types@^15.5.4:
+  version "15.5.10"
+  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
+  dependencies:
+    fbjs "^0.8.9"
+    loose-envify "^1.3.1"
+
+prr@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a"
+
+public-encrypt@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6"
+  dependencies:
+    bn.js "^4.1.0"
+    browserify-rsa "^4.0.0"
+    create-hash "^1.1.0"
+    parse-asn1 "^5.0.0"
+    randombytes "^2.0.1"
+
[email protected]:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
+
+punycode@^1.2.4, punycode@^1.3.2, punycode@^1.4.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+
+qjobs@^1.1.4:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73"
+
[email protected], qs@~6.4.0:
+  version "6.4.0"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
+
+qs@~6.3.0:
+  version "6.3.2"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c"
+
+querystring-es3@^0.2.0, querystring-es3@~0.2.0:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
+
[email protected]:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
+
+randomatic@^1.1.3:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb"
+  dependencies:
+    is-number "^2.0.2"
+    kind-of "^3.0.2"
+
+randombytes@^2.0.0, randombytes@^2.0.1:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.4.tgz#9551df208422c8f80eb58e2326dd0b840ff22efd"
+  dependencies:
+    safe-buffer "^5.0.1"
+
+range-parser@^1.0.3, range-parser@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
+
+raw-body@~2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.2.0.tgz#994976cf6a5096a41162840492f0bdc5d6e7fb96"
+  dependencies:
+    bytes "2.4.0"
+    iconv-lite "0.4.15"
+    unpipe "1.0.0"
+
+rc@^1.1.7:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95"
+  dependencies:
+    deep-extend "~0.4.0"
+    ini "~1.3.0"
+    minimist "^1.2.0"
+    strip-json-comments "~2.0.1"
+
+react-addons-test-utils@~15.4.2:
+  version "15.4.2"
+  resolved "https://registry.yarnpkg.com/react-addons-test-utils/-/react-addons-test-utils-15.4.2.tgz#93bcaa718fcae7360d42e8fb1c09756cc36302a2"
+  dependencies:
+    fbjs "^0.8.4"
+    object-assign "^4.1.0"
+
+react-dom@~15.4.2:
+  version "15.4.2"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.4.2.tgz#015363f05b0a1fd52ae9efdd3a0060d90695208f"
+  dependencies:
+    fbjs "^0.8.1"
+    loose-envify "^1.1.0"
+    object-assign "^4.1.0"
+
+react@~15.4.2:
+  version "15.4.2"
+  resolved "https://registry.yarnpkg.com/react/-/react-15.4.2.tgz#41f7991b26185392ba9bae96c8889e7e018397ef"
+  dependencies:
+    fbjs "^0.8.4"
+    loose-envify "^1.1.0"
+    object-assign "^4.1.0"
+
+read-only-stream@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0"
+  dependencies:
+    readable-stream "^2.0.2"
+
+read-pkg-up@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
+  dependencies:
+    find-up "^1.0.0"
+    read-pkg "^1.0.0"
+
+read-pkg@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
+  dependencies:
+    load-json-file "^1.0.0"
+    normalize-package-data "^2.3.2"
+    path-type "^1.0.0"
+
+readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6:
+  version "2.2.11"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.11.tgz#0796b31f8d7688007ff0b93a8088d34aa17c0f72"
+  dependencies:
+    core-util-is "~1.0.0"
+    inherits "~2.0.1"
+    isarray "~1.0.0"
+    process-nextick-args "~1.0.6"
+    safe-buffer "~5.0.1"
+    string_decoder "~1.0.0"
+    util-deprecate "~1.0.1"
+
+readable-stream@~1.0.2:
+  version "1.0.34"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
+  dependencies:
+    core-util-is "~1.0.0"
+    inherits "~2.0.1"
+    isarray "0.0.1"
+    string_decoder "~0.10.x"
+
+readable-stream@~2.0.0:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e"
+  dependencies:
+    core-util-is "~1.0.0"
+    inherits "~2.0.1"
+    isarray "~1.0.0"
+    process-nextick-args "~1.0.6"
+    string_decoder "~0.10.x"
+    util-deprecate "~1.0.1"
+
+readdirp@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78"
+  dependencies:
+    graceful-fs "^4.1.2"
+    minimatch "^3.0.2"
+    readable-stream "^2.0.2"
+    set-immediate-shim "^1.0.1"
+
+readline2@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35"
+  dependencies:
+    code-point-at "^1.0.0"
+    is-fullwidth-code-point "^1.0.0"
+    mute-stream "0.0.5"
+
+rechoir@^0.6.2:
+  version "0.6.2"
+  resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
+  dependencies:
+    resolve "^1.1.6"
+
+regenerate@^1.2.1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
+
+regenerator-runtime@^0.10.0:
+  version "0.10.5"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
+
[email protected]:
+  version "0.9.11"
+  resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b7176769eb5ff868691befe1283"
+  dependencies:
+    babel-runtime "^6.18.0"
+    babel-types "^6.19.0"
+    private "^0.1.6"
+
+regex-cache@^0.4.2:
+  version "0.4.3"
+  resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145"
+  dependencies:
+    is-equal-shallow "^0.1.3"
+    is-primitive "^2.0.0"
+
+regexpu-core@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240"
+  dependencies:
+    regenerate "^1.2.1"
+    regjsgen "^0.2.0"
+    regjsparser "^0.1.4"
+
+regjsgen@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7"
+
+regjsparser@^0.1.4:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c"
+  dependencies:
+    jsesc "~0.5.0"
+
+remove-trailing-separator@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz#69b062d978727ad14dc6b56ba4ab772fd8d70511"
+
+repeat-element@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a"
+
+repeat-string@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-0.2.2.tgz#c7a8d3236068362059a7e4651fc6884e8b1fb4ae"
+
+repeat-string@^1.5.2:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
+
+repeating@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda"
+  dependencies:
+    is-finite "^1.0.0"
+
+request-progress@~2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-2.0.1.tgz#5d36bb57961c673aa5b788dbc8141fdf23b44e08"
+  dependencies:
+    throttleit "^1.0.0"
+
+request@^2.81.0:
+  version "2.81.0"
+  resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
+  dependencies:
+    aws-sign2 "~0.6.0"
+    aws4 "^1.2.1"
+    caseless "~0.12.0"
+    combined-stream "~1.0.5"
+    extend "~3.0.0"
+    forever-agent "~0.6.1"
+    form-data "~2.1.1"
+    har-validator "~4.2.1"
+    hawk "~3.1.3"
+    http-signature "~1.1.0"
+    is-typedarray "~1.0.0"
+    isstream "~0.1.2"
+    json-stringify-safe "~5.0.1"
+    mime-types "~2.1.7"
+    oauth-sign "~0.8.1"
+    performance-now "^0.2.0"
+    qs "~6.4.0"
+    safe-buffer "^5.0.1"
+    stringstream "~0.0.4"
+    tough-cookie "~2.3.0"
+    tunnel-agent "^0.6.0"
+    uuid "^3.0.0"
+
+request@~2.79.0:
+  version "2.79.0"
+  resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
+  dependencies:
+    aws-sign2 "~0.6.0"
+    aws4 "^1.2.1"
+    caseless "~0.11.0"
+    combined-stream "~1.0.5"
+    extend "~3.0.0"
+    forever-agent "~0.6.1"
+    form-data "~2.1.1"
+    har-validator "~2.0.6"
+    hawk "~3.1.3"
+    http-signature "~1.1.0"
+    is-typedarray "~1.0.0"
+    isstream "~0.1.2"
+    json-stringify-safe "~5.0.1"
+    mime-types "~2.1.7"
+    oauth-sign "~0.8.1"
+    qs "~6.3.0"
+    stringstream "~0.0.4"
+    tough-cookie "~2.3.0"
+    tunnel-agent "~0.4.1"
+    uuid "^3.0.0"
+
+require-directory@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+
+require-main-filename@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
+
+require-uncached@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3"
+  dependencies:
+    caller-path "^0.1.0"
+    resolve-from "^1.0.0"
+
+requirejs@~2.3.3:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.3.tgz#aa59fd3a0287eaf407959a138228044b5dd6a6a3"
+
[email protected]:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+
+resolve-from@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
+
[email protected]:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
+
+resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.6:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5"
+  dependencies:
+    path-parse "^1.0.5"
+
+restore-cursor@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
+  dependencies:
+    exit-hook "^1.0.0"
+    onetime "^1.0.0"
+
+right-align@^0.1.1:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
+  dependencies:
+    align-text "^0.1.1"
+
+rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.0, rimraf@^2.6.1:
+  version "2.6.1"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d"
+  dependencies:
+    glob "^7.0.5"
+
+ripemd160@^2.0.0, ripemd160@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7"
+  dependencies:
+    hash-base "^2.0.0"
+    inherits "^2.0.1"
+
+run-async@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389"
+  dependencies:
+    once "^1.3.0"
+
+rx-lite@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
+
+safe-buffer@^5.0.1:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a45e223"
+
+safe-buffer@~5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
+
+"semver@2 || 3 || 4 || 5", semver@^5.3.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
+
+semver@~4.3.3:
+  version "4.3.6"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da"
+
+set-blocking@^2.0.0, set-blocking@~2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+
+set-immediate-shim@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
+
+setimmediate@^1.0.4, setimmediate@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
+
[email protected]:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04"
+
+sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4:
+  version "2.4.8"
+  resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f"
+  dependencies:
+    inherits "^2.0.1"
+
+shasum@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/shasum/-/shasum-1.0.2.tgz#e7012310d8f417f4deb5712150e5678b87ae565f"
+  dependencies:
+    json-stable-stringify "~0.0.0"
+    sha.js "~2.4.4"
+
+shell-quote@^1.4.2, shell-quote@^1.6.1:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767"
+  dependencies:
+    array-filter "~0.0.0"
+    array-map "~0.0.0"
+    array-reduce "~0.0.0"
+    jsonify "~0.0.0"
+
+shelljs@^0.7.5:
+  version "0.7.8"
+  resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3"
+  dependencies:
+    glob "^7.0.0"
+    interpret "^1.0.0"
+    rechoir "^0.6.2"
+
+signal-exit@^3.0.0:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+
+slash@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
+
[email protected]:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
+
[email protected]:
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
+  dependencies:
+    hoek "2.x.x"
+
[email protected]:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz#cb6d4bb8bec81e1078b99677f9ced0046066bb8b"
+  dependencies:
+    debug "2.3.3"
+    socket.io-parser "2.3.1"
+
[email protected]:
+  version "1.7.3"
+  resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.7.3.tgz#b30e86aa10d5ef3546601c09cde4765e381da377"
+  dependencies:
+    backo2 "1.0.2"
+    component-bind "1.0.0"
+    component-emitter "1.2.1"
+    debug "2.3.3"
+    engine.io-client "1.8.3"
+    has-binary "0.1.7"
+    indexof "0.0.1"
+    object-component "0.0.3"
+    parseuri "0.0.5"
+    socket.io-parser "2.3.1"
+    to-array "0.1.4"
+
[email protected]:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-2.3.1.tgz#dd532025103ce429697326befd64005fcfe5b4a0"
+  dependencies:
+    component-emitter "1.1.2"
+    debug "2.2.0"
+    isarray "0.0.1"
+    json3 "3.3.2"
+
[email protected]:
+  version "1.7.3"
+  resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.7.3.tgz#b8af9caba00949e568e369f1327ea9be9ea2461b"
+  dependencies:
+    debug "2.3.3"
+    engine.io "1.8.3"
+    has-binary "0.1.7"
+    object-assign "4.1.0"
+    socket.io-adapter "0.5.0"
+    socket.io-client "1.7.3"
+    socket.io-parser "2.3.1"
+
+source-list-map@^1.1.1:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.2.tgz#9889019d1024cce55cdc069498337ef6186a11a1"
+
+source-map-support@^0.4.2:
+  version "0.4.15"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1"
+  dependencies:
+    source-map "^0.5.6"
+
[email protected], source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3:
+  version "0.5.6"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
+
+source-map@^0.1.41:
+  version "0.1.43"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346"
+  dependencies:
+    amdefine ">=0.0.4"
+
+spdx-correct@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
+  dependencies:
+    spdx-license-ids "^1.0.2"
+
+spdx-expression-parse@~1.0.0:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c"
+
+spdx-license-ids@^1.0.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57"
+
+sprintf-js@~1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+
+sshpk@^1.7.0:
+  version "1.13.0"
+  resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c"
+  dependencies:
+    asn1 "~0.2.3"
+    assert-plus "^1.0.0"
+    dashdash "^1.12.0"
+    getpass "^0.1.1"
+  optionalDependencies:
+    bcrypt-pbkdf "^1.0.0"
+    ecc-jsbn "~0.1.1"
+    jodid25519 "^1.0.0"
+    jsbn "~0.1.0"
+    tweetnacl "~0.14.0"
+
+"statuses@>= 1.3.1 < 2", statuses@~1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
+
+stream-browserify@^2.0.0, stream-browserify@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"
+  dependencies:
+    inherits "~2.0.1"
+    readable-stream "^2.0.2"
+
+stream-combiner2@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe"
+  dependencies:
+    duplexer2 "~0.1.0"
+    readable-stream "^2.0.2"
+
+stream-http@^2.0.0, stream-http@^2.3.1:
+  version "2.7.1"
+  resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.1.tgz#546a51741ad5a6b07e9e31b0b10441a917df528a"
+  dependencies:
+    builtin-status-codes "^3.0.0"
+    inherits "^2.0.1"
+    readable-stream "^2.2.6"
+    to-arraybuffer "^1.0.0"
+    xtend "^4.0.0"
+
+stream-splicer@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.0.tgz#1b63be438a133e4b671cc1935197600175910d83"
+  dependencies:
+    inherits "^2.0.1"
+    readable-stream "^2.0.2"
+
+string-width@^1.0.1, string-width@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+  dependencies:
+    code-point-at "^1.0.0"
+    is-fullwidth-code-point "^1.0.0"
+    strip-ansi "^3.0.0"
+
+string-width@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.0.0.tgz#635c5436cc72a6e0c387ceca278d4e2eec52687e"
+  dependencies:
+    is-fullwidth-code-point "^2.0.0"
+    strip-ansi "^3.0.0"
+
+string_decoder@^0.10.25, string_decoder@~0.10.0, string_decoder@~0.10.x:
+  version "0.10.31"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
+
+string_decoder@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.2.tgz#b29e1f4e1125fa97a10382b8a533737b7491e179"
+  dependencies:
+    safe-buffer "~5.0.1"
+
+stringstream@~0.0.4:
+  version "0.0.5"
+  resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+  dependencies:
+    ansi-regex "^2.0.0"
+
+strip-bom@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
+  dependencies:
+    is-utf8 "^0.2.0"
+
+strip-bom@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+
+strip-json-comments@~2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+
+subarg@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2"
+  dependencies:
+    minimist "^1.1.0"
+
+supports-color@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+
+supports-color@^3.1.0:
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
+  dependencies:
+    has-flag "^1.0.0"
+
+syntax-error@^1.1.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.3.0.tgz#1ed9266c4d40be75dc55bf9bb1cb77062bb96ca1"
+  dependencies:
+    acorn "^4.0.3"
+
+table@^3.7.8:
+  version "3.8.3"
+  resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f"
+  dependencies:
+    ajv "^4.7.0"
+    ajv-keywords "^1.0.0"
+    chalk "^1.1.1"
+    lodash "^4.0.0"
+    slice-ansi "0.0.4"
+    string-width "^2.0.0"
+
+tapable@^0.2.5, tapable@~0.2.5:
+  version "0.2.6"
+  resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.6.tgz#206be8e188860b514425375e6f1ae89bfb01fd8d"
+
+tar-pack@^3.4.0:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984"
+  dependencies:
+    debug "^2.2.0"
+    fstream "^1.0.10"
+    fstream-ignore "^1.0.5"
+    once "^1.3.3"
+    readable-stream "^2.1.4"
+    rimraf "^2.5.1"
+    tar "^2.2.1"
+    uid-number "^0.0.6"
+
+tar@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1"
+  dependencies:
+    block-stream "*"
+    fstream "^1.0.2"
+    inherits "2"
+
+text-table@~0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+
+throttleit@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c"
+
+through2@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"
+  dependencies:
+    readable-stream "^2.1.5"
+    xtend "~4.0.1"
+
+"through@>=2.2.7 <3", through@^2.3.6:
+  version "2.3.8"
+  resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+
+timers-browserify@^1.0.1:
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d"
+  dependencies:
+    process "~0.11.0"
+
+timers-browserify@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af211349a00fbca56ac86b86"
+  dependencies:
+    setimmediate "^1.0.4"
+
[email protected], [email protected]:
+  version "0.0.31"
+  resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7"
+  dependencies:
+    os-tmpdir "~1.0.1"
+
[email protected]:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890"
+
+to-arraybuffer@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
+
+to-fast-properties@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
+
+tough-cookie@~2.3.0:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a"
+  dependencies:
+    punycode "^1.4.1"
+
+trim-right@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
+
+tryit@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb"
+
[email protected], tty-browserify@~0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
+
+tunnel-agent@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+  dependencies:
+    safe-buffer "^5.0.1"
+
+tunnel-agent@~0.4.1:
+  version "0.4.3"
+  resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb"
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+  version "0.14.5"
+  resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+
+type-check@~0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
+  dependencies:
+    prelude-ls "~1.1.2"
+
+type-is@~1.6.15:
+  version "1.6.15"
+  resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410"
+  dependencies:
+    media-typer "0.3.0"
+    mime-types "~2.1.15"
+
+typedarray@^0.0.6, typedarray@~0.0.5:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+
+ua-parser-js@^0.7.9:
+  version "0.7.12"
+  resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb"
+
+uglify-js@^2.8.5:
+  version "2.8.28"
+  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.28.tgz#e335032df9bb20dcb918f164589d5af47f38834a"
+  dependencies:
+    source-map "~0.5.1"
+    yargs "~3.10.0"
+  optionalDependencies:
+    uglify-to-browserify "~1.0.0"
+
+uglify-to-browserify@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
+
+uid-number@^0.0.6:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
+
[email protected]:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa"
+
+umd@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.1.tgz#8ae556e11011f63c2596708a8837259f01b3d60e"
+
+underscore@~1.8.3:
+  version "1.8.3"
+  resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022"
+
[email protected], unpipe@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+
+url@^0.11.0, url@~0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
+  dependencies:
+    punycode "1.3.2"
+    querystring "0.2.0"
+
+user-home@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f"
+  dependencies:
+    os-homedir "^1.0.0"
+
+useragent@^2.1.12:
+  version "2.1.13"
+  resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.1.13.tgz#bba43e8aa24d5ceb83c2937473e102e21df74c10"
+  dependencies:
+    lru-cache "2.2.x"
+    tmp "0.0.x"
+
+util-deprecate@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+
[email protected], util@^0.10.3, util@~0.10.1:
+  version "0.10.3"
+  resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
+  dependencies:
+    inherits "2.0.1"
+
[email protected]:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8"
+
+uuid@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
+
+uuid@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"
+
+validate-npm-package-license@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc"
+  dependencies:
+    spdx-correct "~1.0.0"
+    spdx-expression-parse "~1.0.0"
+
[email protected]:
+  version "1.3.6"
+  resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c"
+  dependencies:
+    extsprintf "1.0.2"
+
[email protected], vm-browserify@~0.0.1:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73"
+  dependencies:
+    indexof "0.0.1"
+
+void-elements@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
+
+watchify@~3.9.0:
+  version "3.9.0"
+  resolved "https://registry.yarnpkg.com/watchify/-/watchify-3.9.0.tgz#f075fd2e8a86acde84cedba6e5c2a0bedd523d9e"
+  dependencies:
+    anymatch "^1.3.0"
+    browserify "^14.0.0"
+    chokidar "^1.0.0"
+    defined "^1.0.0"
+    outpipe "^1.1.0"
+    through2 "^2.0.0"
+    xtend "^4.0.0"
+
+watchpack@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.3.1.tgz#7d8693907b28ce6013e7f3610aa2a1acf07dad87"
+  dependencies:
+    async "^2.1.2"
+    chokidar "^1.4.3"
+    graceful-fs "^4.1.2"
+
+webpack-dev-middleware@^1.0.11:
+  version "1.10.2"
+  resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.10.2.tgz#2e252ce1dfb020dbda1ccb37df26f30ab014dbd1"
+  dependencies:
+    memory-fs "~0.4.1"
+    mime "^1.3.4"
+    path-is-absolute "^1.0.0"
+    range-parser "^1.0.3"
+
+webpack-sources@^0.2.3:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.2.3.tgz#17c62bfaf13c707f9d02c479e0dcdde8380697fb"
+  dependencies:
+    source-list-map "^1.1.1"
+    source-map "~0.5.3"
+
+webpack@~2.3.1:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.3.3.tgz#eecc083c18fb7bf958ea4f40b57a6640c5a0cc78"
+  dependencies:
+    acorn "^4.0.4"
+    acorn-dynamic-import "^2.0.0"
+    ajv "^4.7.0"
+    ajv-keywords "^1.1.1"
+    async "^2.1.2"
+    enhanced-resolve "^3.0.0"
+    interpret "^1.0.0"
+    json-loader "^0.5.4"
+    loader-runner "^2.3.0"
+    loader-utils "^0.2.16"
+    memory-fs "~0.4.1"
+    mkdirp "~0.5.0"
+    node-libs-browser "^2.0.0"
+    source-map "^0.5.3"
+    supports-color "^3.1.0"
+    tapable "~0.2.5"
+    uglify-js "^2.8.5"
+    watchpack "^1.3.1"
+    webpack-sources "^0.2.3"
+    yargs "^6.0.0"
+
+whatwg-fetch@>=0.10.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84"
+
+which-module@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
+
+which@~1.2.10:
+  version "1.2.14"
+  resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5"
+  dependencies:
+    isexe "^2.0.0"
+
+wide-align@^1.1.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710"
+  dependencies:
+    string-width "^1.0.2"
+
[email protected]:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
+
[email protected]:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
+
+wordwrap@~0.0.2:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
+
+wordwrap@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
+
+wrap-ansi@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
+  dependencies:
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+
+write@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757"
+  dependencies:
+    mkdirp "^0.5.1"
+
[email protected]:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.2.tgz#8a244fa052401e08c9886cf44a85189e1fd4067f"
+  dependencies:
+    options ">=0.0.5"
+    ultron "1.0.x"
+
[email protected]:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/wtf-8/-/wtf-8-1.0.0.tgz#392d8ba2d0f1c34d1ee2d630f15d0efb68e1048a"
+
[email protected]:
+  version "1.5.3"
+  resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz#185a888c04eca46c3e4070d99f7b49de3528992d"
+
+xtend@^4.0.0, xtend@~4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
+
+y18n@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
+
+yargs-parser@^4.2.0:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c"
+  dependencies:
+    camelcase "^3.0.0"
+
+yargs@^6.0.0:
+  version "6.6.0"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208"
+  dependencies:
+    camelcase "^3.0.0"
+    cliui "^3.2.0"
+    decamelize "^1.1.1"
+    get-caller-file "^1.0.1"
+    os-locale "^1.4.0"
+    read-pkg-up "^1.0.1"
+    require-directory "^2.1.1"
+    require-main-filename "^1.0.1"
+    set-blocking "^2.0.0"
+    string-width "^1.0.2"
+    which-module "^1.0.0"
+    y18n "^3.2.1"
+    yargs-parser "^4.2.0"
+
+yargs@~3.10.0:
+  version "3.10.0"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"
+  dependencies:
+    camelcase "^1.0.2"
+    cliui "^2.1.0"
+    decamelize "^1.0.0"
+    window-size "0.1.0"
+
[email protected]:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"
+  dependencies:
+    fd-slicer "~1.0.1"
+
[email protected]:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"


  [text/plain] 03-lint.diff (68.8K, 5-03-lint.diff)
  download | inline diff:
diff --git a/web/karma.conf.js b/web/karma.conf.js
index 3c8e933c..5b0ab57b 100644
--- a/web/karma.conf.js
+++ b/web/karma.conf.js
@@ -20,7 +20,7 @@ module.exports = function (config) {
     // list of files to exclude
     exclude: [
       'pgadmin/static/vendor/**/*[Tt]est.js',
-      'pgadmin/static/vendor/**/*[Ss]pec.js'
+      'pgadmin/static/vendor/**/*[Ss]pec.js',
     ],
 
     // preprocess matching files before serving them to the browser
@@ -60,6 +60,6 @@ module.exports = function (config) {
 
     // Concurrency level
     // how many browser should be started simultaneous
-    concurrency: Infinity
+    concurrency: Infinity,
   });
 };
diff --git a/web/package.json b/web/package.json
index 40070da0..b5d2e0cb 100644
--- a/web/package.json
+++ b/web/package.json
@@ -5,6 +5,8 @@
     "babel-preset-es2015": "~6.24.0",
     "babel-preset-react": "~6.23.0",
     "enzyme": "~2.8.2",
+    "eslint": "^3.19.0",
+    "eslint-plugin-react": "^6.10.3",
     "jasmine-core": "~2.5.2",
     "karma": "~1.5.0",
     "karma-babel-preprocessor": "^6.0.1",
@@ -34,9 +36,11 @@
     "watchify": "~3.9.0"
   },
   "scripts": {
-    "bundle": "yarn run webpack -- --optimize-minimize --config webpack.config.js",
-    "test:karma-once": "yarn run karma start -- --single-run",
-    "test:karma": "yarn run karma start",
+    "linter": "yarn run eslint pgadmin/static/jsx/**/*.jsx pgadmin/static/js/selection/*.js regression/javascript/**/*.jsx regression/javascript/**/*.js *.js",
+    "webpacker": "yarn run webpack -- --optimize-minimize --config webpack.config.js",
+    "bundle": "yarn run linter && yarn run webpacker",
+    "test:karma-once": "yarn run linter && yarn run karma start -- --single-run",
+    "test:karma": "yarn run linter && yarn run karma start",
     "test:feature": "yarn run bundle && python regression/runtests.py --pkg feature_tests",
     "test": "yarn run test:karma-once && yarn run bundle && python regression/runtests.py"
   }
diff --git a/web/pgadmin/static/js/selection/active_cell_capture.js b/web/pgadmin/static/js/selection/active_cell_capture.js
index 10572736..4f87886c 100644
--- a/web/pgadmin/static/js/selection/active_cell_capture.js
+++ b/web/pgadmin/static/js/selection/active_cell_capture.js
@@ -1,6 +1,6 @@
 define([
   'jquery',
-  'sources/selection/range_selection_helper'
+  'sources/selection/range_selection_helper',
 ], function ($, RangeSelectionHelper) {
 
   var ActiveCellCapture = function () {
@@ -37,8 +37,8 @@ define([
     };
 
     $.extend(this, {
-      "init": init,
-      "destroy": destroy,
+      'init': init,
+      'destroy': destroy,
     });
 
     function onDragEndHandler(event, dragData) {
@@ -105,7 +105,7 @@ define([
       }
     }
 
-    function onActiveCellChangedHandler(event, args) {
+    function onActiveCellChangedHandler(event) {
       if (bypassDefaultActiveCellRangeChange) {
         bypassDefaultActiveCellRangeChange = false;
         event.stopPropagation();
@@ -188,4 +188,4 @@ define([
   };
 
   return ActiveCellCapture;
-});
\ No newline at end of file
+});
diff --git a/web/pgadmin/static/js/selection/clipboard.js b/web/pgadmin/static/js/selection/clipboard.js
index 9e20da7e..9794ae7e 100644
--- a/web/pgadmin/static/js/selection/clipboard.js
+++ b/web/pgadmin/static/js/selection/clipboard.js
@@ -1,7 +1,7 @@
 define(['sources/gettext', 'alertify'], function (gettext, alertify) {
   var clipboard = {
     copyTextToClipboard: function (text) {
-      var textArea = document.createElement("textarea");
+      var textArea = document.createElement('textarea');
 
       //
       // *** This styling is an extra step which is likely not required. ***
@@ -55,7 +55,7 @@ define(['sources/gettext', 'alertify'], function (gettext, alertify) {
       }
 
       document.body.removeChild(textArea);
-    }
+    },
   };
   return clipboard;
 });
\ No newline at end of file
diff --git a/web/pgadmin/static/js/selection/column_selector.js b/web/pgadmin/static/js/selection/column_selector.js
index 53cdad82..305b438d 100644
--- a/web/pgadmin/static/js/selection/column_selector.js
+++ b/web/pgadmin/static/js/selection/column_selector.js
@@ -1,7 +1,7 @@
 define([
   'jquery',
   'sources/selection/range_selection_helper',
-  'slickgrid'
+  'slickgrid',
 ], function ($, RangeSelectionHelper) {
   var ColumnSelector = function () {
     var Slick = window.Slick;
@@ -21,7 +21,7 @@ define([
       if (isColumnSelectable(columnDefinition)) {
         var $columnHeader = $(event.target);
         if (hasClickedChildOfColumnHeader(event)) {
-          $columnHeader = $(event.target).parents(".slick-header-column");
+          $columnHeader = $(event.target).parents('.slick-header-column');
         }
         $columnHeader.toggleClass('selected');
 
@@ -63,7 +63,7 @@ define([
     };
 
     var hasClickedChildOfColumnHeader = function (event) {
-      return !$(event.target).hasClass("slick-header-column");
+      return !$(event.target).hasClass('slick-header-column');
     };
 
     var isColumnSelectable = function (columnDefinition) {
@@ -87,16 +87,16 @@ define([
       return _.map(columnDefinitions, function (columnDefinition) {
         if (isColumnSelectable(columnDefinition)) {
           var name =
-            "<span data-cell-type='column-header-row' " +
-            "       data-test='output-column-header'" +
-            "       data-column-id='" + columnDefinition.id + "'>" +
-            "  <span class='column-description'>" +
-            "    <span class='column-name'>" + columnDefinition.display_name + "</span>" +
-            "    <span class='column-type'>" + columnDefinition.column_type + "</span>" +
-            "  </span>" +
-            "</span>";
+            '<span data-cell-type=\'column-header-row\' ' +
+            '       data-test=\'output-column-header\'' +
+            '       data-column-id=\'' + columnDefinition.id + '\'>' +
+            '  <span class=\'column-description\'>' +
+            '    <span class=\'column-name\'>' + columnDefinition.display_name + '</span>' +
+            '    <span class=\'column-type\'>' + columnDefinition.column_type + '</span>' +
+            '  </span>' +
+            '</span>';
           return _.extend(columnDefinition, {
-            name: name
+            name: name,
           });
         } else {
           return columnDefinition;
@@ -105,8 +105,8 @@ define([
     };
 
     $.extend(this, {
-      "init": init,
-      "getColumnDefinitions": getColumnDefinitions
+      'init': init,
+      'getColumnDefinitions': getColumnDefinitions,
     });
   };
   return ColumnSelector;
diff --git a/web/pgadmin/static/js/selection/copy_data.js b/web/pgadmin/static/js/selection/copy_data.js
index ad79d3eb..c2263231 100644
--- a/web/pgadmin/static/js/selection/copy_data.js
+++ b/web/pgadmin/static/js/selection/copy_data.js
@@ -3,8 +3,8 @@ define([
   'underscore',
   'sources/selection/clipboard',
   'sources/selection/range_selection_helper',
-  'sources/selection/range_boundary_navigator'
-  ],
+  'sources/selection/range_boundary_navigator',
+],
 function ($, _, clipboard, RangeSelectionHelper, rangeBoundaryNavigator) {
   var copyData = function () {
     var self = this;
@@ -32,20 +32,8 @@ function ($, _, clipboard, RangeSelectionHelper, rangeBoundaryNavigator) {
 
   var setPasteRowButtonEnablement = function (canEditFlag, isEnabled) {
     if (canEditFlag) {
-      $("#btn-paste-row").prop('disabled', !isEnabled);
+      $('#btn-paste-row').prop('disabled', !isEnabled);
     }
   };
-
-  var allTheRangesAreFullRows = function (ranges, columnDefinitions) {
-    var colRangeBounds = ranges.map(function (range) {
-      return [range.fromCell, range.toCell];
-    });
-
-    if(RangeSelectionHelper.isFirstColumnData(columnDefinitions)) {
-      return _.isEqual(_.union.apply(null, colRangeBounds), [0, columnDefinitions.length - 1]);
-    }
-    return _.isEqual(_.union.apply(null, colRangeBounds), [0, columnDefinitions.length - 1]);
-  };
-
-  return copyData
-});
\ No newline at end of file
+  return copyData;
+});
diff --git a/web/pgadmin/static/js/selection/grid_selector.js b/web/pgadmin/static/js/selection/grid_selector.js
index ae4ab96d..8d7ba75d 100644
--- a/web/pgadmin/static/js/selection/grid_selector.js
+++ b/web/pgadmin/static/js/selection/grid_selector.js
@@ -1,10 +1,8 @@
 define(['jquery',
-    'sources/selection/column_selector',
-    'sources/selection/row_selector',
-    'sources/selection/range_selection_helper'],
+  'sources/selection/column_selector',
+  'sources/selection/row_selector',
+  'sources/selection/range_selection_helper'],
   function ($, ColumnSelector, RowSelector, RangeSelectionHelper) {
-    var Slick = window.Slick;
-
     var GridSelector = function (columnDefinitions) {
       var rowSelector = new RowSelector(columnDefinitions);
       var columnSelector = new ColumnSelector(columnDefinitions);
@@ -39,9 +37,9 @@ define(['jquery',
 
       function handleSelectedRangesChanged(grid) {
         if(RangeSelectionHelper.isEntireGridSelected(grid)) {
-          $("[data-id='select-all']").addClass("selected");
+          $('[data-id=\'select-all\']').addClass('selected');
         } else {
-          $("[data-id='select-all']").removeClass("selected");
+          $('[data-id=\'select-all\']').removeClass('selected');
         }
       }
 
@@ -59,8 +57,8 @@ define(['jquery',
       }
 
       $.extend(this, {
-        "init": init,
-        "getColumnDefinitions": getColumnDefinitions
+        'init': init,
+        'getColumnDefinitions': getColumnDefinitions,
       });
     };
 
diff --git a/web/pgadmin/static/js/selection/range_boundary_navigator.js b/web/pgadmin/static/js/selection/range_boundary_navigator.js
index f88a6533..803f75af 100644
--- a/web/pgadmin/static/js/selection/range_boundary_navigator.js
+++ b/web/pgadmin/static/js/selection/range_boundary_navigator.js
@@ -104,13 +104,13 @@ function (RangeSelectionHelper) {
       var val = data[rowId][columnDefinitions[colId].pos];
 
       if (val && _.isObject(val)) {
-        val = "'" + JSON.stringify(val) + "'";
-      } else if (val && typeof val != "number" && typeof val != "boolean") {
-        val = "'" + val.toString() + "'";
+        val = '\'' + JSON.stringify(val) + '\'';
+      } else if (val && typeof val != 'number' && typeof val != 'boolean') {
+        val = '\'' + val.toString() + '\'';
       } else if (_.isNull(val) || _.isUndefined(val)) {
         val = '';
       }
       return val;
-    }
+    },
   };
 });
\ No newline at end of file
diff --git a/web/pgadmin/static/js/selection/range_selection_helper.js b/web/pgadmin/static/js/selection/range_selection_helper.js
index 936530ff..25cd63c9 100644
--- a/web/pgadmin/static/js/selection/range_selection_helper.js
+++ b/web/pgadmin/static/js/selection/range_selection_helper.js
@@ -8,19 +8,19 @@ define(['slickgrid'], function () {
 
   var isRangeSelected = function (selectedRanges, range) {
     return _.any(selectedRanges, function (selectedRange) {
-      return isSameRange(selectedRange, range)
+      return isSameRange(selectedRange, range);
     });
   };
 
   var isAnyCellOfColumnSelected = function (selectedRanges, column) {
     return _.any(selectedRanges, function (selectedRange) {
-      return selectedRange.fromCell <= column && selectedRange.toCell >= column
+      return selectedRange.fromCell <= column && selectedRange.toCell >= column;
     });
   };
 
   var isAnyCellOfRowSelected = function (selectedRanges, row) {
     return _.any(selectedRanges, function (selectedRange) {
-      return selectedRange.fromRow <= row && selectedRange.toRow >= row
+      return selectedRange.fromRow <= row && selectedRange.toRow >= row;
     });
   };
 
@@ -33,8 +33,8 @@ define(['slickgrid'], function () {
 
   var removeRange = function (selectedRanges, range) {
     return _.filter(selectedRanges, function (selectedRange) {
-      return !(isSameRange(selectedRange, range))
-    })
+      return !(isSameRange(selectedRange, range));
+    });
   };
 
   var addRange = function (ranges, range) {
@@ -45,11 +45,11 @@ define(['slickgrid'], function () {
   var areAllRangesSingleRows = function (ranges, grid) {
     return _.every(ranges, function (range) {
       return range.fromRow == range.toRow && rangeHasCompleteRows(grid, range);
-    })
+    });
   };
 
   var areAllRangesSingleColumns = function (ranges, grid) {
-    return _.every(ranges, isRangeAColumn.bind(this, grid))
+    return _.every(ranges, isRangeAColumn.bind(this, grid));
   };
 
   var rangeForRow = function (grid, rowId) {
@@ -61,7 +61,7 @@ define(['slickgrid'], function () {
   };
 
   var rangeForColumn = function (grid, columnIndex) {
-    return new Slick.Range(0, columnIndex, grid.getDataLength() - 1, columnIndex)
+    return new Slick.Range(0, columnIndex, grid.getDataLength() - 1, columnIndex);
   };
 
   var getRangeOfWholeGrid = function (grid) {
@@ -81,13 +81,13 @@ define(['slickgrid'], function () {
   var areAllRangesCompleteColumns = function (grid, ranges) {
     return _.every(ranges, function (range) {
       return rangeHasCompleteColumns(grid, range);
-    })
+    });
   };
 
   var areAllRangesCompleteRows = function (grid, ranges) {
     return _.every(ranges, function (range) {
       return rangeHasCompleteRows(grid, range);
-    })
+    });
   };
 
   var getIndexesOfCompleteRows = function (grid, ranges) {
@@ -158,5 +158,5 @@ define(['slickgrid'], function () {
     isAnyCellOfColumnSelected: isAnyCellOfColumnSelected,
     isRangeEntirelyWithinSelectedRanges: isRangeEntirelyWithinSelectedRanges,
     isAnyCellOfRowSelected: isAnyCellOfRowSelected,
-  }
-});
\ No newline at end of file
+  };
+});
diff --git a/web/pgadmin/static/js/selection/row_selector.js b/web/pgadmin/static/js/selection/row_selector.js
index 33d5a72e..236ed629 100644
--- a/web/pgadmin/static/js/selection/row_selector.js
+++ b/web/pgadmin/static/js/selection/row_selector.js
@@ -1,7 +1,7 @@
 define([
   'jquery',
   'sources/selection/range_selection_helper',
-  'slickgrid'
+  'slickgrid',
 ], function ($, RangeSelectionHelper) {
   var RowSelector = function () {
     var Slick = window.Slick;
@@ -19,7 +19,7 @@ define([
       if (grid.getColumns()[args.cell].id === 'row-header-column') {
         var $rowHeaderSpan = $(event.target);
 
-        if ($rowHeaderSpan.data('cell-type') != "row-header-selector") {
+        if ($rowHeaderSpan.data('cell-type') != 'row-header-selector') {
           $rowHeaderSpan = $(event.target).find('[data-cell-type="row-header-selector"]');
         }
 
@@ -82,16 +82,16 @@ define([
         formatter: function (rowIndex) {
           return '<span ' +
             'data-row="' + rowIndex + '" ' +
-            'data-cell-type="row-header-selector"/>'
+            'data-cell-type="row-header-selector"/>';
         },
-        width: 30
+        width: 30,
       });
       return columnDefinitions;
     };
 
     $.extend(this, {
-      "init": init,
-      "getColumnDefinitions": getColumnDefinitions
+      'init': init,
+      'getColumnDefinitions': getColumnDefinitions,
     });
   };
 
diff --git a/web/pgadmin/static/js/selection/set_staged_rows.js b/web/pgadmin/static/js/selection/set_staged_rows.js
index 249a6414..46977e3a 100644
--- a/web/pgadmin/static/js/selection/set_staged_rows.js
+++ b/web/pgadmin/static/js/selection/set_staged_rows.js
@@ -11,7 +11,7 @@ define(
   [
     'jquery',
     'underscore',
-    'sources/selection/range_selection_helper'
+    'sources/selection/range_selection_helper',
   ],
   function ($, _, RangeSelectionHelper) {
     function disableButton(selector) {
@@ -55,7 +55,7 @@ define(
         _.each(_.keys(gridRow), function (columnPos) {
           if (_.contains(primaryKeyColumnIndices, Number(columnPos)))
             rowToStage[columnPos] = gridRow[columnPos];
-        })
+        });
       }
       return rowToStage;
     }
@@ -72,7 +72,7 @@ define(
       return stagedRows;
     }
 
-    var setStagedRows = function (e, args) {
+    var setStagedRows = function () {
       var self = this;
 
       function setStagedRows(rowsToStage) {
@@ -88,7 +88,7 @@ define(
 
       function areAllSelectionsEntireRows() {
         return RangeSelectionHelper.areAllRangesCompleteRows(self.grid,
-          self.selection.getSelectedRanges())
+          self.selection.getSelectedRanges());
       }
 
       var selectedRanges = this.selection.getSelectedRanges();
@@ -98,7 +98,7 @@ define(
       }
 
       if (areAllSelectionsEntireRows()) {
-        var selectedRows = RangeSelectionHelper.getIndexesOfCompleteRows(this.grid, this.selection.getSelectedRanges())
+        var selectedRows = RangeSelectionHelper.getIndexesOfCompleteRows(this.grid, this.selection.getSelectedRanges());
         var stagedRows = getPrimaryKeysForSelectedRows(self, selectedRows);
         setStagedRows(stagedRows);
         if (_.isEmpty(stagedRows)) {
diff --git a/web/pgadmin/static/js/selection/xcell_selection_model.js b/web/pgadmin/static/js/selection/xcell_selection_model.js
index 32fed09f..974b7479 100644
--- a/web/pgadmin/static/js/selection/xcell_selection_model.js
+++ b/web/pgadmin/static/js/selection/xcell_selection_model.js
@@ -4,7 +4,7 @@ define([
   'sources/selection/range_selection_helper',
   'sources/slickgrid/pgslick.cellrangeselector',
 
-  'slickgrid'
+  'slickgrid',
 ], function ($, _, RangeSelectionHelper, PGCellRangeSelector) {
   var XCellSelectionModel = function (options) {
 
@@ -13,25 +13,24 @@ define([
     var KEY_ARROW_UP = 38;
     var KEY_ARROW_DOWN = 40;
 
+    var Slick = window.Slick;
     var _grid;
-    var _canvas;
     var _ranges = [];
     var _self = this;
     var _selector = new PGCellRangeSelector({
-      "selectionCss": {
-        "border": "2px solid black"
-      }
+      'selectionCss': {
+        'border': '2px solid black',
+      },
     });
     var _options;
     var _defaults = {
-      selectActiveCell: true
+      selectActiveCell: true,
     };
 
 
     function init(grid) {
       _options = $.extend(true, {}, _defaults, options);
       _grid = grid;
-      _canvas = _grid.getCanvasNode();
       _grid.onActiveCellChanged.subscribe(handleActiveCellChange);
       _grid.onKeyDown.subscribe(handleKeyDown);
       grid.registerPlugin(_selector);
@@ -82,7 +81,7 @@ define([
       }
     }
 
-    function handleBeforeCellRangeSelected(e, args) {
+    function handleBeforeCellRangeSelected(e) {
       if (_grid.getEditorLock().isActive()) {
         e.stopPropagation();
         return false;
@@ -169,18 +168,18 @@ define([
         var mobileCell = getMobileCellFromRange(lastSelectedRange, anchorActiveCell);
 
         switch (getKeycode()) {
-          case KEY_ARROW_LEFT:
-            mobileCell.cell -= 1;
-            break;
-          case KEY_ARROW_RIGHT:
-            mobileCell.cell += 1;
-            break;
-          case KEY_ARROW_UP:
-            mobileCell.row -= 1;
-            break;
-          case KEY_ARROW_DOWN:
-            mobileCell.row += 1;
-            break;
+        case KEY_ARROW_LEFT:
+          mobileCell.cell -= 1;
+          break;
+        case KEY_ARROW_RIGHT:
+          mobileCell.cell += 1;
+          break;
+        case KEY_ARROW_UP:
+          mobileCell.row -= 1;
+          break;
+        case KEY_ARROW_DOWN:
+          mobileCell.row += 1;
+          break;
         }
 
         var newSelectedRange = getNewRange(anchorActiveCell, mobileCell);
@@ -206,7 +205,7 @@ define([
       }
     }
 
-    function handleWindowMouseUp(event) {
+    function handleWindowMouseUp() {
       var selectedRange = _selector.getCurrentRange();
       if (!_.isUndefined(selectedRange)) {
         _grid.onDragEnd.notify({range: selectedRange});
@@ -214,14 +213,14 @@ define([
     }
 
     $.extend(this, {
-      "getSelectedRanges": getSelectedRanges,
-      "setSelectedRanges": setSelectedRanges,
-      "setSelectedRows": setSelectedRows,
+      'getSelectedRanges': getSelectedRanges,
+      'setSelectedRanges': setSelectedRanges,
+      'setSelectedRows': setSelectedRows,
 
-      "init": init,
-      "destroy": destroy,
+      'init': init,
+      'destroy': destroy,
 
-      "onSelectedRangesChanged": new Slick.Event()
+      'onSelectedRangesChanged': new Slick.Event(),
     });
   };
   return XCellSelectionModel;
diff --git a/web/regression/javascript/browser/menu_spec.js b/web/regression/javascript/browser/menu_spec.js
index e13bfe5e..8def5a0d 100644
--- a/web/regression/javascript/browser/menu_spec.js
+++ b/web/regression/javascript/browser/menu_spec.js
@@ -15,7 +15,7 @@ define([
     var MenuItem = pgAdmin.Browser.MenuItem;
     var menuItem;
 
-    describe("when we create a menu item", function () {
+    describe('when we create a menu item', function () {
       describe('and it is disabled', function () {
         beforeEach(function () {
           menuItem = new MenuItem({enable: false, icon: 'fa fa-car'});
diff --git a/web/regression/javascript/check_node_visiblity_spec.js b/web/regression/javascript/check_node_visiblity_spec.js
index 130b1c21..96a8961f 100644
--- a/web/regression/javascript/check_node_visiblity_spec.js
+++ b/web/regression/javascript/check_node_visiblity_spec.js
@@ -7,24 +7,24 @@
 //
 //////////////////////////////////////////////////////////////////////////
 
-define(["sources/check_node_visibility"],
-function (checkNodeVisibility, pgBrowser) {
-  describe("checkNodeVisibility", function () {
+define(['sources/check_node_visibility'],
+function (checkNodeVisibility) {
+  describe('checkNodeVisibility', function () {
 
     var browser;
 
     browser = jasmine.createSpyObj('browser', [
-                    'node_preference_data', 'get_preference']
+      'node_preference_data', 'get_preference']
                 );
 
-    describe("when node is server collection", function () {
-      it("returns true", function () {
+    describe('when node is server collection', function () {
+      it('returns true', function () {
         expect(checkNodeVisibility(browser, 'coll-server')).toEqual(true);
       });
     });
 
-    describe("when node is server", function () {
-      it("returns true", function () {
+    describe('when node is server', function () {
+      it('returns true', function () {
         expect(checkNodeVisibility(browser, 'server')).toEqual(true);
       });
     });
diff --git a/web/regression/javascript/fake_translations.js b/web/regression/javascript/fake_translations.js
index 9516fd63..a3d4f5bd 100644
--- a/web/regression/javascript/fake_translations.js
+++ b/web/regression/javascript/fake_translations.js
@@ -8,5 +8,5 @@
 //////////////////////////////////////////////////////////////////////////
 
 define(function () {
-  return {}
+  return {};
 });
\ No newline at end of file
diff --git a/web/regression/javascript/selection/active_cell_capture_spec.js b/web/regression/javascript/selection/active_cell_capture_spec.js
index a0bd3848..4ed6c12f 100644
--- a/web/regression/javascript/selection/active_cell_capture_spec.js
+++ b/web/regression/javascript/selection/active_cell_capture_spec.js
@@ -18,6 +18,7 @@ define([
     var onColumnsResizedFunction;
     var onHeaderMouseEnterFunction;
     var onHeaderMouseLeaveFunction;
+    var Slick = window.Slick;
 
     beforeEach(function () {
       getSelectedRangesSpy = jasmine.createSpy('getSelectedRangesSpy');
@@ -27,15 +28,15 @@ define([
           return {
             getSelectedRanges: getSelectedRangesSpy,
             setSelectedRanges: setSelectedRangesSpy,
-          }
+          };
         },
 
         getColumns: function () {
           return [
             {id: 'row-header-column'},
             {id: 'column-1'},
-            {id: 'column-2'}
-          ]
+            {id: 'column-2'},
+          ];
         },
 
         onDragEnd: jasmine.createSpyObj('onDragEnd', ['subscribe']),
@@ -47,7 +48,7 @@ define([
         onHeaderMouseEnter: jasmine.createSpyObj('onHeaderMouseEnter', ['subscribe']),
         onHeaderMouseLeave: jasmine.createSpyObj('onHeaderMouseLeave', ['subscribe']),
 
-        getDataLength: function () { return 10 },
+        getDataLength: function () { return 10; },
 
         setActiveCell: jasmine.createSpy('setActiveCell'),
         getActiveCell: jasmine.createSpy('getActiveCell'),
@@ -108,7 +109,7 @@ define([
 
         describe('when a different column is clicked', function () {
           beforeEach(function () {
-            onHeaderClickFunction({}, {column: {pos: 4}})
+            onHeaderClickFunction({}, {column: {pos: 4}});
           });
 
           it('should set the active cell to the newly clicked columns top cell', function () {
@@ -217,7 +218,7 @@ define([
 
         describe('when the third column is clicked (thereby deselecting it)', function () {
           beforeEach(function () {
-            onHeaderClickFunction({}, {column: {pos: 21}})
+            onHeaderClickFunction({}, {column: {pos: 21}});
           });
 
           it('should set the active cell to the second column', function () {
@@ -227,7 +228,7 @@ define([
 
         describe('when the second column is clicked (thereby deselecting it)', function () {
           beforeEach(function () {
-            onHeaderClickFunction({}, {column: {pos: 5}})
+            onHeaderClickFunction({}, {column: {pos: 5}});
           });
 
           it('should not set the active cell', function () {
@@ -268,7 +269,7 @@ define([
           beforeEach(function () {
             grid.getActiveCell.and.returnValue({row: 4, cell: 5});
             getSelectedRangesSpy.and.returnValue([
-              new Slick.Range(4, 5)
+              new Slick.Range(4, 5),
             ]);
           });
 
@@ -283,7 +284,7 @@ define([
           beforeEach(function () {
             grid.getActiveCell.and.returnValue({row: 3, cell: 1});
             getSelectedRangesSpy.and.returnValue([
-              RangeSelectionHelper.rangeForRow(grid, 3)
+              RangeSelectionHelper.rangeForRow(grid, 3),
             ]);
           });
 
@@ -307,7 +308,7 @@ define([
             grid.getActiveCell.and.returnValue({row: 3, cell: 1});
             getSelectedRangesSpy.and.returnValue([
               RangeSelectionHelper.rangeForRow(grid, 5),
-              RangeSelectionHelper.rangeForRow(grid, 3)
+              RangeSelectionHelper.rangeForRow(grid, 3),
             ]);
           });
 
@@ -330,7 +331,7 @@ define([
 
         describe('and the editable new row', function () {
           beforeEach(function () {
-            onClickFunction({}, {row: 10, cell: 0})
+            onClickFunction({}, {row: 10, cell: 0});
           });
           it('does not select the row', function () {
             expect(grid.setActiveCell).not.toHaveBeenCalled();
diff --git a/web/regression/javascript/selection/column_selector_spec.js b/web/regression/javascript/selection/column_selector_spec.js
index 74d137a4..61115495 100644
--- a/web/regression/javascript/selection/column_selector_spec.js
+++ b/web/regression/javascript/selection/column_selector_spec.js
@@ -22,10 +22,12 @@ import 'sources/slickgrid/pgslick.cellrangeselector';
 describe('ColumnSelector', function () {
   var container, data, columns, options;
   var SlickGrid = Slick.Grid;
-  var KEY_RIGHT = 39;
-  var KEY_LEFT = 37;
-  var KEY_UP = 38;
-  var KEY_DOWN = 40;
+  var KEY = {
+    RIGHT: 39,
+    LEFT: 37,
+    UP: 38,
+    DOWN: 40,
+  };
 
   beforeEach(function () {
     container = $('<div></div>');
@@ -35,11 +37,11 @@ describe('ColumnSelector', function () {
     data = [{
       'some-column-name': 'first value',
       'second column': 'second value',
-      'third column': 'nonselectable value'
+      'third column': 'nonselectable value',
     }, {
       'some-column-name': 'row 1 - first value',
       'second column': 'row 1 - second value',
-      'third column': 'row 1 - nonselectable value'
+      'third column': 'row 1 - nonselectable value',
     }];
 
     columns = [
@@ -48,36 +50,36 @@ describe('ColumnSelector', function () {
         name: 'row header column name',
         selectable: false,
         display_name: 'row header column name',
-        column_type: 'text'
+        column_type: 'text',
       },
       {
         id: '1',
         name: 'some-column-name',
         pos: 0,
         display_name: 'some-column-name',
-        column_type: 'text'
+        column_type: 'text',
       },
       {
         id: '2',
         name: 'second column',
         pos: 1,
         display_name: 'second column',
-        column_type: 'json'
+        column_type: 'json',
       },
       {
         id: 'third-column-id',
         name: 'third column',
         pos: 2,
         display_name: 'third column',
-        column_type: 'text'
+        column_type: 'text',
       },
       {
         name: 'some-non-selectable-column',
         selectable: false,
         pos: 3,
         display_name: 'some-non-selectable-column',
-        column_type: 'numeric'
-      }
+        column_type: 'numeric',
+      },
     ];
   });
 
@@ -170,7 +172,7 @@ describe('ColumnSelector', function () {
 
       describe('and presses shift + right-arrow', function () {
         beforeEach(function () {
-          pressShiftArrow(KEY_RIGHT);
+          pressShiftArrow(KEY.RIGHT);
         });
 
         it('keeps the last column selected', function () {
@@ -202,7 +204,7 @@ describe('ColumnSelector', function () {
 
         describe('and presses shift + right-arrow', function () {
           it('first and second columns are selected', function () {
-            pressShiftArrow(KEY_RIGHT);
+            pressShiftArrow(KEY.RIGHT);
 
             var selectedRanges = cellSelectionModel.getSelectedRanges();
 
@@ -266,7 +268,7 @@ describe('ColumnSelector', function () {
         var selectedRanges = cellSelectionModel.getSelectedRanges();
 
         expect(selectedRanges.length).toEqual(0);
-      })
+      });
     });
 
     describe('when the column is not selectable', function () {
diff --git a/web/regression/javascript/selection/copy_data_spec.js b/web/regression/javascript/selection/copy_data_spec.js
index 2d6b6ad2..9e05e845 100644
--- a/web/regression/javascript/selection/copy_data_spec.js
+++ b/web/regression/javascript/selection/copy_data_spec.js
@@ -19,52 +19,51 @@ import XCellSelectionModel from 'sources/selection/xcell_selection_model';
 
 describe('copyData', function () {
   var grid, sqlEditor, gridContainer, buttonPasteRow;
-  var Slick, SlickGrid;
+  var SlickGrid;
 
   beforeEach(function () {
-    Slick = window.Slick;
     SlickGrid = Slick.Grid;
     var data = [[1, 'leopord', '12'],
       [2, 'lion', '13'],
       [3, 'puma', '9']];
 
     var columns = [
-        {
-          id: 'row-header-column',
-          name: 'row header column name',
-          selectable: false,
-          display_name: 'row header column name',
-          column_type: 'text'
-        },
-        {
-          name: 'id',
-          pos: 0,
-          label: 'id<br> numeric',
-          cell: 'number',
-          can_edit: false,
-          type: 'numeric'
-        }, {
-          name: 'brand',
-          pos: 1,
-          label: 'flavor<br> character varying',
-          cell: 'string',
-          can_edit: false,
-          type: 'character varying'
-        }, {
-          name: 'size',
-          pos: 2,
-          label: 'size<br> numeric',
-          cell: 'number',
-          can_edit: false,
-          type: 'numeric'
-        }
-      ]
+      {
+        id: 'row-header-column',
+        name: 'row header column name',
+        selectable: false,
+        display_name: 'row header column name',
+        column_type: 'text',
+      },
+      {
+        name: 'id',
+        pos: 0,
+        label: 'id<br> numeric',
+        cell: 'number',
+        can_edit: false,
+        type: 'numeric',
+      }, {
+        name: 'brand',
+        pos: 1,
+        label: 'flavor<br> character varying',
+        cell: 'string',
+        can_edit: false,
+        type: 'character varying',
+      }, {
+        name: 'size',
+        pos: 2,
+        label: 'size<br> numeric',
+        cell: 'number',
+        can_edit: false,
+        type: 'numeric',
+      },
+    ]
     ;
     gridContainer = $('<div id=\'grid\'></div>');
     $('body').append(gridContainer);
     buttonPasteRow = $('<button id=\'btn-paste-row\' disabled></button>');
     $('body').append(buttonPasteRow);
-    grid = new Slick.Grid('#grid', data, columns, {});
+    grid = new SlickGrid('#grid', data, columns, {});
     grid.setSelectionModel(new XCellSelectionModel());
     sqlEditor = {slickgrid: grid};
   });
diff --git a/web/regression/javascript/selection/grid_selector_spec.js b/web/regression/javascript/selection/grid_selector_spec.js
index f7b39ee8..ce54cc69 100644
--- a/web/regression/javascript/selection/grid_selector_spec.js
+++ b/web/regression/javascript/selection/grid_selector_spec.js
@@ -8,7 +8,6 @@
 //////////////////////////////////////////////////////////////////////////
 
 import $ from 'jquery';
-import _ from 'underscore';
 
 import Slick from 'slickgrid';
 import 'slickgrid.grid';
@@ -18,21 +17,20 @@ import XCellSelectionModel from 'sources/selection/xcell_selection_model';
 
 describe('GridSelector', function () {
   var container, data, columns, gridSelector, xCellSelectionModel;
-  var Slick, SlickGrid;
+  var SlickGrid;
 
   beforeEach(function () {
-    Slick = window.Slick;
     SlickGrid = Slick.Grid;
     container = $('<div></div>');
     container.height(9999);
     columns = [{
       id: '1',
       name: 'some-column-name',
-      pos: 0
+      pos: 0,
     }, {
       id: '2',
       name: 'second column',
-      pos: 1
+      pos: 1,
     }];
 
     gridSelector = new GridSelector();
@@ -42,7 +40,7 @@ describe('GridSelector', function () {
     for (var i = 0; i < 10; i++) {
       data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i});
     }
-    var grid = new Slick.Grid(container, data, columns);
+    var grid = new SlickGrid(container, data, columns);
 
     xCellSelectionModel = new XCellSelectionModel();
     grid.setSelectionModel(xCellSelectionModel);
diff --git a/web/regression/javascript/selection/range_boundary_navigator_spec.js b/web/regression/javascript/selection/range_boundary_navigator_spec.js
index b57087f3..1de6d812 100644
--- a/web/regression/javascript/selection/range_boundary_navigator_spec.js
+++ b/web/regression/javascript/selection/range_boundary_navigator_spec.js
@@ -23,8 +23,8 @@ describe('RangeBoundaryNavigator', function () {
       });
     });
 
-    describe("when the ranges all overlap partially or touch", function () {
-      it("returns one long range", function () {
+    describe('when the ranges all overlap partially or touch', function () {
+      it('returns one long range', function () {
         var rangeBounds = [[3, 6], [1, 4], [7, 14]];
 
         var union = rangeBoundaryNavigator.getUnion(rangeBounds);
@@ -32,7 +32,7 @@ describe('RangeBoundaryNavigator', function () {
         expect(union).toEqual([[1, 14]]);
       });
 
-      it("returns them in order from lowest to highest", function () {
+      it('returns them in order from lowest to highest', function () {
         var rangeBounds = [[3, 6], [2, 3], [10, 12]];
 
         var union = rangeBoundaryNavigator.getUnion(rangeBounds);
@@ -40,9 +40,9 @@ describe('RangeBoundaryNavigator', function () {
         expect(union).toEqual([[2, 6], [10, 12]]);
       });
 
-      describe("when one range completely overlaps another", function() {
+      describe('when one range completely overlaps another', function() {
 
-        it("returns them in order from lowest to highest", function () {
+        it('returns them in order from lowest to highest', function () {
           var rangeBounds = [[9, 14], [2, 3], [11, 13]];
 
           var union = rangeBoundaryNavigator.getUnion(rangeBounds);
@@ -51,19 +51,19 @@ describe('RangeBoundaryNavigator', function () {
         });
       });
 
-      describe("when one range is a subset of another", function () {
-        it("returns the larger range", function () {
+      describe('when one range is a subset of another', function () {
+        it('returns the larger range', function () {
           var rangeBounds = [[2, 6], [1, 14], [8, 10]];
 
           var union = rangeBoundaryNavigator.getUnion(rangeBounds);
 
           expect(union).toEqual([[1, 14]]);
-        })
-      })
+        });
+      });
     });
 
-    describe("when the ranges do not touch", function () {
-      it("returns them in order from lowest to highest", function () {
+    describe('when the ranges do not touch', function () {
+      it('returns them in order from lowest to highest', function () {
         var rangeBounds = [[3, 6], [1, 1], [8, 10]];
 
         var union = rangeBoundaryNavigator.getUnion(rangeBounds);
@@ -73,8 +73,8 @@ describe('RangeBoundaryNavigator', function () {
     });
   });
 
-  describe("#mapDimensionBoundaryUnion", function () {
-    it("returns a list of the results of the callback", function () {
+  describe('#mapDimensionBoundaryUnion', function () {
+    it('returns a list of the results of the callback', function () {
       var rangeBounds = [[0, 1], [3, 3]];
       var callback = function () {
         return 'hello';
@@ -83,7 +83,7 @@ describe('RangeBoundaryNavigator', function () {
       expect(result).toEqual(['hello', 'hello', 'hello']);
     });
 
-    it("calls the callback with each index in the dimension", function () {
+    it('calls the callback with each index in the dimension', function () {
       var rangeBounds = [[0, 1], [3, 3]];
       var callback = jasmine.createSpy('callbackSpy');
       rangeBoundaryNavigator.mapDimensionBoundaryUnion(rangeBounds, callback);
@@ -91,7 +91,7 @@ describe('RangeBoundaryNavigator', function () {
     });
   });
 
-  describe("#mapOver2DArray", function () {
+  describe('#mapOver2DArray', function () {
     var data, rowCollector, processCell;
     beforeEach(function () {
       data = [[0, 1, 2, 3], [2, 2, 2, 2], [4, 5, 6, 7]];
@@ -103,76 +103,75 @@ describe('RangeBoundaryNavigator', function () {
       };
     });
 
-    it("calls the callback for each item in the ranges", function () {
+    it('calls the callback for each item in the ranges', function () {
       var rowRanges = [[0, 0], [2, 2]];
       var colRanges = [[0, 3]];
 
       var selectionResult = rangeBoundaryNavigator.mapOver2DArray(rowRanges, colRanges, processCell, rowCollector);
 
-      expect(selectionResult).toEqual(["[0,1,2,3]", "[4,5,6,7]"]);
+      expect(selectionResult).toEqual(['[0,1,2,3]', '[4,5,6,7]']);
     });
 
-    describe("when the ranges are out of order/duplicated", function () {
+    describe('when the ranges are out of order/duplicated', function () {
       var rowRanges, colRanges;
       beforeEach(function () {
         rowRanges = [[2, 2], [2, 2], [0, 0]];
         colRanges = [[0, 3]];
       });
 
-      it("uses the union of the ranges", function () {
-        spyOn(rangeBoundaryNavigator, "getUnion").and.callThrough();
+      it('uses the union of the ranges', function () {
+        spyOn(rangeBoundaryNavigator, 'getUnion').and.callThrough();
 
         var selectionResult = rangeBoundaryNavigator.mapOver2DArray(rowRanges, colRanges, processCell, rowCollector);
 
         expect(rangeBoundaryNavigator.getUnion).toHaveBeenCalledWith(rowRanges);
         expect(rangeBoundaryNavigator.getUnion).toHaveBeenCalledWith(colRanges);
-        expect(selectionResult).toEqual(["[0,1,2,3]", "[4,5,6,7]"]);
+        expect(selectionResult).toEqual(['[0,1,2,3]', '[4,5,6,7]']);
       });
     });
   });
 
-  describe("#rangesToCsv", function () {
+  describe('#rangesToCsv', function () {
     var data, columnDefinitions, ranges;
     beforeEach(function () {
-      data = [[1, "leopard", "12"],
-        [2, "lion", "13"],
-        [3, "cougar", "9"],
-        [4, "tiger", "10"]];
+      data = [[1, 'leopard', '12'],
+        [2, 'lion', '13'],
+        [3, 'cougar', '9'],
+        [4, 'tiger', '10']];
       columnDefinitions = [{name: 'id', pos: 0}, {name: 'animal', pos: 1}, {name: 'size', pos: 2}];
       ranges = [new Slick.Range(0, 0, 0, 2), new Slick.Range(3, 0, 3, 2)];
     });
 
-    it("returns csv for the provided ranges", function () {
+    it('returns csv for the provided ranges', function () {
       var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, ranges);
 
-      expect(csvResult).toEqual("1,'leopard','12'\n4,'tiger','10'");
+      expect(csvResult).toEqual('1,\'leopard\',\'12\'\n4,\'tiger\',\'10\'');
     });
 
-    describe("when no cells are selected", function () {
-      it("should return an empty string", function () {
+    describe('when no cells are selected', function () {
+      it('should return an empty string', function () {
         var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, []);
 
         expect(csvResult).toEqual('');
       });
     });
 
-    describe("when there is an extra column with checkboxes", function () {
-
+    describe('when there is an extra column with checkboxes', function () {
       beforeEach(function () {
         columnDefinitions = [{name: 'not-a-data-column'}, {name: 'id', pos: 0}, {name: 'animal', pos: 1}, {
           name: 'size',
-          pos: 2
+          pos: 2,
         }];
         ranges = [new Slick.Range(0, 0, 0, 3), new Slick.Range(3, 0, 3, 3)];
       });
 
-      it("returns csv for the columns with data", function () {
+      it('returns csv for the columns with data', function () {
         var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, ranges);
 
-        expect(csvResult).toEqual("1,'leopard','12'\n4,'tiger','10'");
+        expect(csvResult).toEqual('1,\'leopard\',\'12\'\n4,\'tiger\',\'10\'');
       });
-      describe("when no cells are selected", function () {
-        it("should return an empty string", function () {
+      describe('when no cells are selected', function () {
+        it('should return an empty string', function () {
           var csvResult = rangeBoundaryNavigator.rangesToCsv(data, columnDefinitions, []);
 
           expect(csvResult).toEqual('');
@@ -180,4 +179,4 @@ describe('RangeBoundaryNavigator', function () {
       });
     });
   });
-});
\ No newline at end of file
+});
diff --git a/web/regression/javascript/selection/range_selection_helper_spec.js b/web/regression/javascript/selection/range_selection_helper_spec.js
index 63c07259..62200085 100644
--- a/web/regression/javascript/selection/range_selection_helper_spec.js
+++ b/web/regression/javascript/selection/range_selection_helper_spec.js
@@ -3,21 +3,21 @@ import Slick from 'slickgrid';
 import 'slickgrid.grid';
 import RangeSelectionHelper from 'sources/selection/range_selection_helper';
 
-describe("RangeSelectionHelper utility functions", function () {
+describe('RangeSelectionHelper utility functions', function () {
   var grid;
   beforeEach(function () {
     var container, data, columns, options;
-    container = $("<div></div>");
+    container = $('<div></div>');
     container.height(9999);
 
     columns = [{
       id: '1',
       name: 'some-column-name',
-      pos: 0
+      pos: 0,
     }, {
       id: 'second-column-id',
       name: 'second column',
-      pos: 1
+      pos: 1,
     }];
 
     data = [];
@@ -29,18 +29,18 @@ describe("RangeSelectionHelper utility functions", function () {
     grid.invalidate();
   });
 
-  describe("#getIndexesOfCompleteRows", function () {
-    describe("when selected ranges are not rows", function () {
-      it("returns an empty array", function () {
+  describe('#getIndexesOfCompleteRows', function () {
+    describe('when selected ranges are not rows', function () {
+      it('returns an empty array', function () {
         var rowlessRanges = [RangeSelectionHelper.rangeForColumn(grid, 1)];
 
         expect(RangeSelectionHelper.getIndexesOfCompleteRows(grid, rowlessRanges))
           .toEqual([]);
       });
     });
-    describe("when selected range", function () {
-      describe("is a single row", function () {
-        it("returns an array with one index", function () {
+    describe('when selected range', function () {
+      describe('is a single row', function () {
+        it('returns an array with one index', function () {
           var singleRowRange = [RangeSelectionHelper.rangeForRow(grid, 1)];
 
           expect(RangeSelectionHelper.getIndexesOfCompleteRows(grid, singleRowRange))
@@ -48,8 +48,8 @@ describe("RangeSelectionHelper utility functions", function () {
         });
       });
 
-      describe("is multiple rows", function () {
-        it("returns an array of each row's index", function () {
+      describe('is multiple rows', function () {
+        it('returns an array of each row\'s index', function () {
           var multipleRowRange = [
             RangeSelectionHelper.rangeForRow(grid, 0),
             RangeSelectionHelper.rangeForRow(grid, 3),
@@ -62,10 +62,10 @@ describe("RangeSelectionHelper utility functions", function () {
         });
       });
 
-      describe("contains a multi row selection", function () {
-        it("returns an array of each individual row's index", function () {
+      describe('contains a multi row selection', function () {
+        it('returns an array of each individual row\'s index', function () {
           var multipleRowRange = [
-            new Slick.Range(3, 0, 5, 1)
+            new Slick.Range(3, 0, 5, 1),
           ];
 
           var indexesOfCompleteRows = RangeSelectionHelper.getIndexesOfCompleteRows(grid, multipleRowRange);
@@ -73,11 +73,11 @@ describe("RangeSelectionHelper utility functions", function () {
           expect(indexesOfCompleteRows).toEqual([3, 4, 5]);
         });
 
-        describe("and also contains a selection that is not a row", function () {
-          it("returns an array of only the complete rows' indexes", function () {
+        describe('and also contains a selection that is not a row', function () {
+          it('returns an array of only the complete rows\' indexes', function () {
             var multipleRowRange = [
               new Slick.Range(8, 1, 9, 1),
-              new Slick.Range(3, 0, 5, 1)
+              new Slick.Range(3, 0, 5, 1),
             ];
 
             var indexesOfCompleteRows = RangeSelectionHelper.getIndexesOfCompleteRows(grid, multipleRowRange);
diff --git a/web/regression/javascript/selection/row_selector_spec.js b/web/regression/javascript/selection/row_selector_spec.js
index dbf65e26..db3c0b4f 100644
--- a/web/regression/javascript/selection/row_selector_spec.js
+++ b/web/regression/javascript/selection/row_selector_spec.js
@@ -8,7 +8,6 @@
 //////////////////////////////////////////////////////////////////////////
 
 import $ from 'jquery';
-import _ from 'underscore';
 
 import Slick from 'slickgrid';
 import 'slickgrid.grid';
@@ -18,10 +17,12 @@ import ActiveCellCapture from 'sources/selection/active_cell_capture';
 import XCellSelectionModel from 'sources/selection/xcell_selection_model';
 
 describe('RowSelector', function () {
-  var KEY_RIGHT = 39;
-  var KEY_LEFT = 37;
-  var KEY_UP = 38;
-  var KEY_DOWN = 40;
+  var KEY = {
+    RIGHT: 39,
+    LEFT: 37,
+    UP: 38,
+    DOWN: 40,
+  };
   var container, data, columnDefinitions, grid, cellSelectionModel;
   var SlickGrid = Slick.Grid;
 
@@ -34,12 +35,12 @@ describe('RowSelector', function () {
       id: '1',
       name: 'some-column-name',
       selectable: true,
-      pos: 0
+      pos: 0,
     }, {
       id: '2',
       name: 'second column',
       selectable: true,
-      pos: 1
+      pos: 1,
     }];
 
     var rowSelector = new RowSelector();
@@ -134,7 +135,7 @@ describe('RowSelector', function () {
 
       describe('and presses shift + down-arrow', function () {
         beforeEach(function () {
-          pressShiftArrow(KEY_DOWN);
+          pressShiftArrow(KEY.DOWN);
         });
 
         it('keeps the last row selected', function () {
@@ -301,7 +302,7 @@ describe('RowSelector', function () {
         var selectedRanges = cellSelectionModel.getSelectedRanges();
 
         expect(selectedRanges.length).toEqual(0);
-      })
+      });
     });
   });
 
diff --git a/web/regression/javascript/selection/set_staged_rows_spec.js b/web/regression/javascript/selection/set_staged_rows_spec.js
index 99c6d3a6..67353144 100644
--- a/web/regression/javascript/selection/set_staged_rows_spec.js
+++ b/web/regression/javascript/selection/set_staged_rows_spec.js
@@ -8,9 +8,9 @@
 //////////////////////////////////////////////////////////////
 
 define([
-  "jquery",
-  "underscore",
-  "sources/selection/set_staged_rows",
+  'jquery',
+  'underscore',
+  'sources/selection/set_staged_rows',
 ], function ($, _, SetStagedRows) {
   describe('set_staged_rows', function () {
     var sqlEditorObj, gridSpy, deleteButton, copyButton, selectionSpy;
@@ -20,7 +20,7 @@ define([
         {0: 'one', 1: 'two', __temp_PK: '123'},
         {0: 'three', 1: 'four', __temp_PK: '456'},
         {0: 'five', 1: 'six', __temp_PK: '789'},
-        {0: 'seven', 1: 'eight', __temp_PK: '432'}
+        {0: 'seven', 1: 'eight', __temp_PK: '432'},
       ]);
       gridSpy.getColumns.and.returnValue([
         {
@@ -29,7 +29,7 @@ define([
         }, {
           pos: 1,
           selectable: true,
-        }
+        },
       ]);
 
       selectionSpy = jasmine.createSpyObj('selectionSpy', ['setSelectedRows', 'getSelectedRanges']);
@@ -42,23 +42,23 @@ define([
         editor: {
           handler: {
             data_store: {
-              staged_rows: {'456': {}}
+              staged_rows: {'456': {}},
             },
-            can_edit: false
-          }
+            can_edit: false,
+          },
         },
         keys: null,
         selection: selectionSpy,
         columns: [
           {
             name: 'a pk column',
-            pos: 0
+            pos: 0,
           },
           {
             name: 'some column',
-            pos: 1
-          }
-        ]
+            pos: 1,
+          },
+        ],
       };
 
       $('body').append(deleteButton);
@@ -195,7 +195,7 @@ define([
               {0: 'one', 1: 'two', __temp_PK: '123'},
               {1: 'four', __temp_PK: '456'},
               {1: 'six', __temp_PK: '789'},
-              {0: 'seven', 1: 'eight', __temp_PK: '432'}
+              {0: 'seven', 1: 'eight', __temp_PK: '432'},
             ]);
           });
 
@@ -228,7 +228,7 @@ define([
           it('should not clear the staged rows', function () {
             expect(sqlEditorObj.editor.handler.data_store.staged_rows).toEqual({
               '456': {0: 'three'},
-              '789': {0: 'five'}
+              '789': {0: 'five'},
             });
           });
 
diff --git a/web/regression/javascript/selection/xcell_selection_model_spec.js b/web/regression/javascript/selection/xcell_selection_model_spec.js
index 8f01035c..db3f6d60 100644
--- a/web/regression/javascript/selection/xcell_selection_model_spec.js
+++ b/web/regression/javascript/selection/xcell_selection_model_spec.js
@@ -10,6 +10,7 @@
 import XCellSelectionModel from 'sources/selection/xcell_selection_model';
 import 'slickgrid.grid';
 import Slick from 'slickgrid';
+import $ from 'jquery';
 
 describe('XCellSelectionModel', function () {
   var KEY_RIGHT = 39;
@@ -31,15 +32,15 @@ describe('XCellSelectionModel', function () {
     }, {
       id: '1',
       name: 'some-column-name',
-      pos: 0
+      pos: 0,
     }, {
       id: 'second-column-id',
       name: 'second column',
-      pos: 1
+      pos: 1,
     }, {
       id: 'third-column-id',
       name: 'third column',
-      pos: 2
+      pos: 2,
     },
     ];
 
@@ -52,13 +53,13 @@ describe('XCellSelectionModel', function () {
         'fourth column': 'fourth value ' + i,
       });
     }
-    container = $("<div></div>");
+    container = $('<div></div>');
     container.height(9999);
     container.width(9999);
 
     grid = new SlickGrid(container, data, columns);
     grid.setSelectionModel(new XCellSelectionModel());
-    $("body").append(container);
+    $('body').append(container);
   });
 
   afterEach(function () {
@@ -397,13 +398,13 @@ describe('XCellSelectionModel', function () {
         target: $cell,
         isPropagationStopped: jasmine.createSpy('isPropagationStopped').and.returnValue(false),
         isImmediatePropagationStopped: jasmine.createSpy('isImmediatePropagationStopped').and.returnValue(false),
-        stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation')
+        stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation'),
       };
 
       dd = {
         grid: grid,
         startX: cellLeftPosition(initialPosition.cell),
-        startY: cellTopPosition($cell, initialPosition.row)
+        startY: cellTopPosition($cell, initialPosition.row),
       };
 
       grid.onDragStart.notify(dd, event, grid);
@@ -421,7 +422,7 @@ describe('XCellSelectionModel', function () {
           stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation'),
 
           pageX: cellLeftPosition(finalPosition.cell),
-          pageY: cellTopPosition($cell, finalPosition.row)
+          pageY: cellTopPosition($cell, finalPosition.row),
         };
 
         grid.onDrag.notify(dd, event, grid);
@@ -448,7 +449,7 @@ describe('XCellSelectionModel', function () {
     });
 
     it('do not notify onDragEnd', function () {
-      expect(grid.onDragEnd.notify).not.toHaveBeenCalled()
+      expect(grid.onDragEnd.notify).not.toHaveBeenCalled();
     });
   });
 
@@ -482,14 +483,14 @@ describe('XCellSelectionModel', function () {
   });
 
   function pressKey(keyCode) {
-    var pressEvent = new $.Event("keydown");
+    var pressEvent = new $.Event('keydown');
     pressEvent.which = keyCode;
 
     $(container.find('.grid-canvas')).trigger(pressEvent);
   }
 
   function pressShiftPlusKey(keyCode) {
-    var pressEvent = new $.Event("keydown");
+    var pressEvent = new $.Event('keydown');
     pressEvent.shiftKey = true;
     pressEvent.which = keyCode;
 
diff --git a/web/regression/javascript/size_prettify_spec.js b/web/regression/javascript/size_prettify_spec.js
index 7d370fcb..f60c79c0 100644
--- a/web/regression/javascript/size_prettify_spec.js
+++ b/web/regression/javascript/size_prettify_spec.js
@@ -7,59 +7,59 @@
 //
 //////////////////////////////////////////////////////////////////////////
 
-define(["sources/size_prettify"], function (sizePrettify) {
-  describe("sizePrettify", function () {
-    describe("when size is 0", function () {
-      it("returns 0 bytes", function () {
-        expect(sizePrettify(0)).toEqual("0 bytes");
+define(['sources/size_prettify'], function (sizePrettify) {
+  describe('sizePrettify', function () {
+    describe('when size is 0', function () {
+      it('returns 0 bytes', function () {
+        expect(sizePrettify(0)).toEqual('0 bytes');
       });
     });
 
-    describe("when size >= 10kB and size < 10 MB", function () {
-      it("returns size in kB", function () {
-        expect(sizePrettify(10240)).toEqual("10 kB");
+    describe('when size >= 10kB and size < 10 MB', function () {
+      it('returns size in kB', function () {
+        expect(sizePrettify(10240)).toEqual('10 kB');
       });
 
-      it("returns size in kB", function () {
-        expect(sizePrettify(99999)).toEqual("98 kB");
+      it('returns size in kB', function () {
+        expect(sizePrettify(99999)).toEqual('98 kB');
       });
     });
 
 
-    describe("when size >= 10MB and size < 10 GB", function () {
-      it("returns size in MB", function () {
-        expect(sizePrettify(10485760)).toEqual("10 MB");
+    describe('when size >= 10MB and size < 10 GB', function () {
+      it('returns size in MB', function () {
+        expect(sizePrettify(10485760)).toEqual('10 MB');
       });
 
-      it("returns size in MB", function () {
-        expect(sizePrettify(44040192)).toEqual("42 MB");
+      it('returns size in MB', function () {
+        expect(sizePrettify(44040192)).toEqual('42 MB');
       });
     });
 
 
-    describe("when size >= 10GB and size < 10 TB", function () {
-      it("returns size in GB", function () {
-        expect(sizePrettify(10737418240)).toEqual("10 GB");
+    describe('when size >= 10GB and size < 10 TB', function () {
+      it('returns size in GB', function () {
+        expect(sizePrettify(10737418240)).toEqual('10 GB');
       });
 
-      it("returns size in GB", function () {
-        expect(sizePrettify(10736344498176)).toEqual("9999 GB");
+      it('returns size in GB', function () {
+        expect(sizePrettify(10736344498176)).toEqual('9999 GB');
       });
     });
 
-    describe("when size >= 10TB and size < 10 PB", function () {
-      it("returns size in TB", function () {
-        expect(sizePrettify(10995116277760)).toEqual("10 TB");
+    describe('when size >= 10TB and size < 10 PB', function () {
+      it('returns size in TB', function () {
+        expect(sizePrettify(10995116277760)).toEqual('10 TB');
       });
 
-      it("returns size in TB", function () {
-        expect(sizePrettify(29995116277760)).toEqual("27 TB");
+      it('returns size in TB', function () {
+        expect(sizePrettify(29995116277760)).toEqual('27 TB');
       });
     });
 
-    describe("when size >= 10 PB", function () {
-      it("returns size in PB", function () {
-        expect(sizePrettify(11258999068426200)).toEqual("10 PB");
+    describe('when size >= 10 PB', function () {
+      it('returns size in PB', function () {
+        expect(sizePrettify(11258999068426200)).toEqual('10 PB');
       });
 
     });
diff --git a/web/regression/javascript/slickgrid/cell_selector_spec.js b/web/regression/javascript/slickgrid/cell_selector_spec.js
index b32fdf80..7ad72e32 100644
--- a/web/regression/javascript/slickgrid/cell_selector_spec.js
+++ b/web/regression/javascript/slickgrid/cell_selector_spec.js
@@ -7,16 +7,17 @@
 //
 //////////////////////////////////////////////////////////////
 
-import $ from "jquery";
-import SlickGrid from "slickgrid.grid";
-import XCellSelectionModel from "sources/selection/xcell_selection_model";
-import CellSelector from "sources/slickgrid/cell_selector";
-import RangeSelectionHelper from "sources/selection/range_selection_helper";
+import $ from 'jquery';
+import Slick from 'slickgrid';
+import 'slickgrid.grid';
+import XCellSelectionModel from 'sources/selection/xcell_selection_model';
+import CellSelector from 'sources/slickgrid/cell_selector';
+import RangeSelectionHelper from 'sources/selection/range_selection_helper';
 
-describe("CellSelector", function () {
+describe('CellSelector', function () {
   var container, columns, cellSelector, data, cellSelectionModel, grid;
   beforeEach(function () {
-    container = $("<div></div>");
+    container = $('<div></div>');
     container.height(9999);
     container.width(9999);
     columns = [{
@@ -39,17 +40,17 @@ describe("CellSelector", function () {
     grid.registerPlugin(cellSelector);
     grid.invalidate();
 
-    $("body").append(container);
+    $('body').append(container);
   });
 
   afterEach(function () {
-    $("body").find(container).remove();
+    $('body').find(container).remove();
   });
 
-  describe("when the user clicks or tabs to a cell", function () {
-    it("sets the selected range to that cell", function () {
+  describe('when the user clicks or tabs to a cell', function () {
+    it('sets the selected range to that cell', function () {
       var row = 1, column = 0;
-      $(container.find(".slick-row .slick-cell.l" + column)[row]).click();
+      $(container.find('.slick-row .slick-cell.l' + column)[row]).click();
 
       var selectedRanges = cellSelectionModel.getSelectedRanges();
       expect(selectedRanges.length).toBe(1);
@@ -59,14 +60,14 @@ describe("CellSelector", function () {
       expect(selectedRanges[0].toRow).toBe(1);
     });
 
-    it("deselects previously selected ranges", function () {
+    it('deselects previously selected ranges', function () {
       var row2Range = RangeSelectionHelper.rangeForRow(grid, 2);
       var ranges = RangeSelectionHelper.addRange(cellSelectionModel.getSelectedRanges(),
         row2Range);
       cellSelectionModel.setSelectedRanges(ranges);
 
       var row = 4, column = 1;
-      $(container.find(".slick-row .slick-cell.l" + column)[row]).click();
+      $(container.find('.slick-row .slick-cell.l' + column)[row]).click();
 
       expect(RangeSelectionHelper.isRangeSelected(cellSelectionModel.getSelectedRanges(), row2Range))
         .toBe(false);
diff --git a/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js b/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js
index 884ae415..eb4411dc 100644
--- a/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js
+++ b/web/regression/javascript/slickgrid/event_handlers/handle_query_output_keyboard_event_spec.js
@@ -13,10 +13,10 @@ import RangeSelectionHelper from 'sources/selection/range_selection_helper';
 import XCellSelectionModel from 'sources/selection/xcell_selection_model';
 import Slick from 'slickgrid';
 import 'slickgrid.grid';
-
+import $ from 'jquery';
 
 describe('#handleQueryOutputKeyboardEvent', function () {
-  var event, view, grid, slickEvent;
+  var event, grid, slickEvent;
   var SlickGrid = Slick.Grid;
   var handleQueryOutputKeyboardEvent;
 
@@ -27,7 +27,7 @@ describe('#handleQueryOutputKeyboardEvent', function () {
       metaKey: false,
       which: -1,
       keyCode: -1,
-      preventDefault: jasmine.createSpy('preventDefault')
+      preventDefault: jasmine.createSpy('preventDefault'),
     };
 
     var data = [['', '0,0-cell-content', '0,1-cell-content'],
@@ -35,22 +35,20 @@ describe('#handleQueryOutputKeyboardEvent', function () {
       ['', '2,0-cell-content', '2,1-cell-content']];
     var columnDefinitions = [{name: 'checkboxColumn'}, {pos: 1, name: 'firstColumn'}, {
       pos: 2,
-      name: 'secondColumn'
+      name: 'secondColumn',
     }];
     grid = new SlickGrid($('<div></div>'), data, columnDefinitions);
     grid.setSelectionModel(new XCellSelectionModel());
 
     slickEvent = {
-      grid: grid
+      grid: grid,
     };
 
-    view = {};
     spyOn(clipboard, 'copyTextToClipboard');
     handleQueryOutputKeyboardEvent = HandleQueryOutputKeyboardEvent.bind(window);
-    debugger
   });
 
-  describe("when a range is selected", function () {
+  describe('when a range is selected', function () {
     beforeEach(function () {
       grid.getSelectionModel().setSelectedRanges([
         RangeSelectionHelper.rangeForRow(grid, 0),
@@ -58,39 +56,39 @@ describe('#handleQueryOutputKeyboardEvent', function () {
       ]);
     });
 
-    describe("pressing Command + C", function () {
+    describe('pressing Command + C', function () {
       beforeEach(function () {
         event.metaKey = true;
         event.keyCode = 67;
       });
 
-      it("copies the cell content to the clipboard", function () {
+      it('copies the cell content to the clipboard', function () {
         handleQueryOutputKeyboardEvent(event, slickEvent);
 
-        expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith("'0,0-cell-content','0,1-cell-content'\n'2,0-cell-content','2,1-cell-content'");
+        expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith('\'0,0-cell-content\',\'0,1-cell-content\'\n\'2,0-cell-content\',\'2,1-cell-content\'');
       });
     });
 
-    describe("pressing Ctrl + C", function () {
+    describe('pressing Ctrl + C', function () {
       beforeEach(function () {
         event.ctrlKey = true;
         event.keyCode = 67;
       });
 
-      it("copies the cell content to the clipboard", function () {
+      it('copies the cell content to the clipboard', function () {
         handleQueryOutputKeyboardEvent(event, slickEvent);
 
-        expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith("'0,0-cell-content','0,1-cell-content'\n'2,0-cell-content','2,1-cell-content'");
+        expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith('\'0,0-cell-content\',\'0,1-cell-content\'\n\'2,0-cell-content\',\'2,1-cell-content\'');
       });
     });
 
-    describe("pressing Command + A", function () {
+    describe('pressing Command + A', function () {
       beforeEach(function () {
         event.metaKey = true;
         event.keyCode = 65;
       });
 
-      it("selects the entire grid to ranges", function () {
+      it('selects the entire grid to ranges', function () {
         handleQueryOutputKeyboardEvent(event, slickEvent);
 
         expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
@@ -98,13 +96,13 @@ describe('#handleQueryOutputKeyboardEvent', function () {
       });
     });
 
-    describe("pressing Ctrl + A", function () {
+    describe('pressing Ctrl + A', function () {
       beforeEach(function () {
         event.ctrlKey = true;
         event.keyCode = 65;
       });
 
-      it("selects the entire grid to ranges", function () {
+      it('selects the entire grid to ranges', function () {
         handleQueryOutputKeyboardEvent(event, slickEvent);
 
         expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
@@ -113,14 +111,14 @@ describe('#handleQueryOutputKeyboardEvent', function () {
     });
   });
 
-  describe("when no ranges are selected", function () {
-    describe("pressing Command + A", function () {
+  describe('when no ranges are selected', function () {
+    describe('pressing Command + A', function () {
       beforeEach(function () {
         event.metaKey = true;
         event.keyCode = 65;
       });
 
-      it("selects the entire grid to ranges", function () {
+      it('selects the entire grid to ranges', function () {
         handleQueryOutputKeyboardEvent(event, slickEvent);
 
         expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
@@ -128,13 +126,13 @@ describe('#handleQueryOutputKeyboardEvent', function () {
       });
     });
 
-    describe("pressing Ctrl + A", function () {
+    describe('pressing Ctrl + A', function () {
       beforeEach(function () {
         event.ctrlKey = true;
         event.keyCode = 65;
       });
 
-      it("selects the entire grid to ranges", function () {
+      it('selects the entire grid to ranges', function () {
         handleQueryOutputKeyboardEvent(event, slickEvent);
 
         expect(RangeSelectionHelper.isEntireGridSelected(grid)).toBeTruthy();
diff --git a/web/regression/javascript/sqleditor_utils_spec.js b/web/regression/javascript/sqleditor_utils_spec.js
index 4b79dc03..b1564b37 100644
--- a/web/regression/javascript/sqleditor_utils_spec.js
+++ b/web/regression/javascript/sqleditor_utils_spec.js
@@ -7,18 +7,18 @@
 //
 //////////////////////////////////////////////////////////////////////////
 
-define(["sources/sqleditor_utils"],
+define(['sources/sqleditor_utils'],
 function (SqlEditorUtils) {
-  describe("SqlEditorUtils", function () {
+  describe('SqlEditorUtils', function () {
 
-    describe("Generate a random string of size 10", function () {
-      it("returns string of length 10", function () {
+    describe('Generate a random string of size 10', function () {
+      it('returns string of length 10', function () {
         expect(SqlEditorUtils.epicRandomString(10).length).toEqual(10);
       });
     });
 
-    describe("Generate a unique hash for given string", function () {
-      it("returns unique hash", function () {
+    describe('Generate a unique hash for given string', function () {
+      it('returns unique hash', function () {
         expect(SqlEditorUtils.getHash('select * from test')).toEqual(403379630);
       });
     });
diff --git a/web/webpack.test.config.js b/web/webpack.test.config.js
index 29df6dd8..b156f147 100644
--- a/web/webpack.test.config.js
+++ b/web/webpack.test.config.js
@@ -8,7 +8,6 @@ const regressionDir = path.resolve(__dirname, 'regression');
 module.exports = {
   plugins: [
     new webpack.ProvidePlugin({
-      $: 'jquery',
       jQuery: 'jquery',
       _: 'underscore',
       'underscore.string': 'underscore.string',
diff --git a/web/yarn.lock b/web/yarn.lock
index bfd9171d..b04caa5c 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -1199,15 +1199,7 @@ [email protected]:
     readable-stream "~2.0.0"
     typedarray "~0.0.5"
 
-concat-stream@^1.5.2:
-  version "1.6.0"
-  resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
-  dependencies:
-    inherits "^2.0.3"
-    readable-stream "^2.2.2"
-    typedarray "^0.0.6"
-
-concat-stream@~1.5.0, concat-stream@~1.5.1:
+concat-stream@^1.5.2, concat-stream@~1.5.0, concat-stream@~1.5.1:
   version "1.5.2"
   resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266"
   dependencies:
@@ -2329,7 +2321,7 @@ inflight@^1.0.4:
     once "^1.3.0"
     wrappy "1"
 
-inherits@2, [email protected], inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1:
+inherits@2, [email protected], inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
 
@@ -3577,7 +3569,7 @@ read-pkg@^1.0.0:
     normalize-package-data "^2.3.2"
     path-type "^1.0.0"
 
-readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6:
+readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.6:
   version "2.2.11"
   resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.11.tgz#0796b31f8d7688007ff0b93a8088d34aa17c0f72"
   dependencies:
@@ -4239,7 +4231,7 @@ type-is@~1.6.15:
     media-typer "0.3.0"
     mime-types "~2.1.15"
 
-typedarray@^0.0.6, typedarray@~0.0.5:
+typedarray@~0.0.5:
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
 


  [text/plain] 04-create-query-history-list.diff (43.9K, 6-04-create-query-history-list.diff)
  download | inline diff:
diff --git a/web/.eslintrc.js b/web/.eslintrc.js
index c60569db..98486dcd 100644
--- a/web/.eslintrc.js
+++ b/web/.eslintrc.js
@@ -5,7 +5,10 @@ module.exports = {
     'amd': true,
     'jasmine': true,
   },
-  'extends': 'eslint:recommended',
+  'extends': [
+    'eslint:recommended',
+    "plugin:react/recommended",
+  ],
   'parserOptions': {
     'ecmaFeatures': {
       'experimentalObjectRestSpread': true,
@@ -40,6 +43,6 @@ module.exports = {
     'comma-dangle': [
       'error',
       'always-multiline'
-    ]
+    ],
   }
 };
\ No newline at end of file
diff --git a/web/karma.conf.js b/web/karma.conf.js
index 5b0ab57b..a194bd16 100644
--- a/web/karma.conf.js
+++ b/web/karma.conf.js
@@ -27,7 +27,7 @@ module.exports = function (config) {
     // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
     preprocessors: {
       'regression/javascript/**/*.js': ['webpack'],
-      // 'regression/javascript/**/*.jsx': ['webpack'],
+      'regression/javascript/**/*.jsx': ['webpack'],
     },
 
     webpack: webpackConfig,
diff --git a/web/package.json b/web/package.json
index b5d2e0cb..e72138c2 100644
--- a/web/package.json
+++ b/web/package.json
@@ -5,9 +5,11 @@
     "babel-preset-es2015": "~6.24.0",
     "babel-preset-react": "~6.23.0",
     "enzyme": "~2.8.2",
+    "enzyme-matchers": "^3.1.0",
     "eslint": "^3.19.0",
     "eslint-plugin-react": "^6.10.3",
     "jasmine-core": "~2.5.2",
+    "jasmine-enzyme": "^3.1.0",
     "karma": "~1.5.0",
     "karma-babel-preprocessor": "^6.0.1",
     "karma-browserify": "~5.1.1",
@@ -28,7 +30,9 @@
     "babelify": "~7.3.0",
     "browserify": "~14.1.0",
     "exports-loader": "~0.6.4",
+    "immutability-helper": "^2.2.0",
     "imports-loader": "git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d",
+    "moment": "^2.18.1",
     "react": "~15.4.2",
     "react-dom": "~15.4.2",
     "requirejs": "~2.3.3",
diff --git a/web/pgadmin/feature_tests/query_tool_journey_test.py b/web/pgadmin/feature_tests/query_tool_journey_test.py
new file mode 100644
index 00000000..0766193e
--- /dev/null
+++ b/web/pgadmin/feature_tests/query_tool_journey_test.py
@@ -0,0 +1,111 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2017, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import pyperclip
+import time
+
+from selenium.webdriver import ActionChains
+
+from regression.python_test_utils import test_utils
+from regression.feature_utils.base_feature_test import BaseFeatureTest
+
+
+class QueryToolJourneyTest(BaseFeatureTest):
+    """
+    Tests the path through the query tool
+    """
+
+    scenarios = [
+        ("Tests the path through the query tool", dict())
+    ]
+
+    def before(self):
+        connection = test_utils.get_db_connection(self.server['db'],
+                                                  self.server['username'],
+                                                  self.server['db_password'],
+                                                  self.server['host'],
+                                                  self.server['port'])
+        test_utils.drop_database(connection, "acceptance_test_db")
+        test_utils.create_database(self.server, "acceptance_test_db")
+        test_utils.create_table(self.server, "acceptance_test_db", "test_table")
+        self.page.add_server(self.server)
+
+    def runTest(self):
+        self._navigate_to_query_tool()
+        self._execute_query("SELECT * FROM test_table ORDER BY value")
+
+        self._test_copies_rows()
+        self._test_copies_columns()
+        self._test_history_tab()
+
+    def _test_copies_rows(self):
+        pyperclip.copy("old clipboard contents")
+        time.sleep(5)
+        self.page.driver.switch_to.default_content()
+        self.page.driver.switch_to_frame(self.page.driver.find_element_by_tag_name("iframe"))
+        self.page.find_by_xpath("//*[contains(@class, 'slick-row')]/*[1]").click()
+        self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
+
+        self.assertEqual("'Some-Name','6','some info'",
+                         pyperclip.paste())
+
+    def _test_copies_columns(self):
+        pyperclip.copy("old clipboard contents")
+
+        self.page.driver.switch_to.default_content()
+        self.page.driver.switch_to_frame(self.page.driver.find_element_by_tag_name("iframe"))
+        self.page.find_by_xpath("//*[@data-test='output-column-header' and contains(., 'some_column')]").click()
+        self.page.find_by_xpath("//*[@id='btn-copy-row']").click()
+
+        self.assertTrue("'Some-Name'" in pyperclip.paste())
+        self.assertTrue("'Some-Other-Name'" in pyperclip.paste())
+        self.assertTrue("'Yet-Another-Name'" in pyperclip.paste())
+
+    def _test_history_tab(self):
+        self.__clear_query_tool()
+
+        editor_input = self.page.find_by_id("output-panel")
+        self.page.click_element(editor_input)
+        self._execute_query("SELECT * FROM shoes")
+
+        self.page.click_tab("History")
+        history_element = self.page.find_by_id("history_grid")
+        self.assertIn("SELECT * FROM test_table", history_element.text)
+        self.assertIn("SELECT * FROM shoes", history_element.text)
+
+    def __clear_query_tool(self):
+        self.page.click_element(self.page.find_by_xpath("//*[@id='btn-edit']"))
+        self.page.click_modal('Yes')
+
+    def _navigate_to_query_tool(self):
+        self.page.toggle_open_tree_item(self.server['name'])
+        self.page.toggle_open_tree_item('Databases')
+        self.page.toggle_open_tree_item('acceptance_test_db')
+        time.sleep(5)
+        self.page.find_by_partial_link_text("Tools").click()
+        self.page.find_by_partial_link_text("Query Tool").click()
+        self.page.click_tab('Query-1')
+        time.sleep(5)
+
+    def _execute_query(self, query):
+        ActionChains(self.page.driver).send_keys(query).perform()
+        self.page.driver.switch_to.default_content()
+        self.page.driver.switch_to_frame(self.page.driver.find_element_by_tag_name("iframe"))
+        self.page.find_by_id("btn-flash").click()
+
+    def after(self):
+        self.page.close_query_tool()
+        self.page.remove_server(self.server)
+
+        connection = test_utils.get_db_connection(self.server['db'],
+                                                  self.server['username'],
+                                                  self.server['db_password'],
+                                                  self.server['host'],
+                                                  self.server['port'])
+        test_utils.drop_database(connection, "acceptance_test_db")
diff --git a/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py b/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py
index 959b2c19..094dfed6 100644
--- a/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py
+++ b/web/pgadmin/feature_tests/xss_checks_pgadmin_debugger_test.py
@@ -83,7 +83,7 @@ class CheckDebuggerForXssFeatureTest(BaseFeatureTest):
 
         # If debugger plugin is not found
         if is_error and is_error == "Debugger Error":
-            self.page.click_modal_ok()
+            self.page.click_modal('OK')
             self.skipTest("Please make sure that debugger plugin is properly configured")
         else:
             time.sleep(2)
diff --git a/web/pgadmin/static/js/history/history_collection.js b/web/pgadmin/static/js/history/history_collection.js
new file mode 100644
index 00000000..f7b6acd7
--- /dev/null
+++ b/web/pgadmin/static/js/history/history_collection.js
@@ -0,0 +1,34 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+export default class HistoryCollection {
+
+  constructor(history_model) {
+    this.historyList = history_model;
+    this.onChange(() => {});
+  }
+
+  length() {
+    return this.historyList.length;
+  }
+
+  add(object) {
+    this.historyList.push(object);
+    this.onChangeHandler(this.historyList);
+  }
+
+  reset() {
+    this.historyList = [];
+    this.onChangeHandler(this.historyList);
+  }
+
+  onChange(onChangeHandler) {
+    this.onChangeHandler = onChangeHandler;
+  }
+}
\ No newline at end of file
diff --git a/web/pgadmin/static/js/history/index.js b/web/pgadmin/static/js/history/index.js
new file mode 100644
index 00000000..834878a8
--- /dev/null
+++ b/web/pgadmin/static/js/history/index.js
@@ -0,0 +1,14 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import historyCollection from './history_collection';
+
+export {
+  historyCollection,
+};
diff --git a/web/pgadmin/static/jsx/components.jsx b/web/pgadmin/static/jsx/components.jsx
index 5bcb5208..6ff34e4c 100644
--- a/web/pgadmin/static/jsx/components.jsx
+++ b/web/pgadmin/static/jsx/components.jsx
@@ -1,8 +1,10 @@
 
 import React from 'react';
 import {render} from 'react-dom';
+import QueryHistory from './history/query_history';
 
 export {
   render,
   React,
+  QueryHistory,
 };
\ No newline at end of file
diff --git a/web/pgadmin/static/jsx/history/query_history.jsx b/web/pgadmin/static/jsx/history/query_history.jsx
new file mode 100644
index 00000000..d36f5ce9
--- /dev/null
+++ b/web/pgadmin/static/jsx/history/query_history.jsx
@@ -0,0 +1,49 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import React from 'react';
+import QueryHistoryEntry from './query_history_entry';
+
+const liStyle = {
+  borderBottom: '1px solid #cccccc',
+};
+
+export default class QueryHistory extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      history: [],
+    };
+  }
+
+  componentWillMount() {
+    this.setState({history: this.props.historyCollection.historyList});
+    this.props.historyCollection.onChange((historyList) => this.setState({history: historyList}));
+  }
+
+  render() {
+    return <ul>
+      {_.chain(this.state.history)
+        .sortBy(historyEntry => historyEntry.start_time)
+        .reverse()
+        .map((entry, index) =>
+        <li key={index} style={liStyle}>
+          <QueryHistoryEntry historyEntry={entry}/>
+        </li>)
+        .value()
+      }
+    </ul>;
+  }
+}
+
+QueryHistory.propTypes = {
+  historyCollection: React.PropTypes.object.isRequired,
+};
\ No newline at end of file
diff --git a/web/pgadmin/static/jsx/history/query_history_entry.jsx b/web/pgadmin/static/jsx/history/query_history_entry.jsx
new file mode 100644
index 00000000..d66cb3a7
--- /dev/null
+++ b/web/pgadmin/static/jsx/history/query_history_entry.jsx
@@ -0,0 +1,93 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import React from 'react';
+import update from 'immutability-helper';
+import moment from 'moment';
+
+const outerDivStyle = {
+  paddingLeft: '10px',
+  fontFamily: 'monospace',
+  paddingRight: '20px',
+  fontSize: '14px',
+  backgroundColor: '#FFF',
+};
+const sqlStyle = {
+  textOverflow: 'ellipsis',
+  overflow: 'hidden',
+  whiteSpace: 'nowrap',
+  userSelect: 'auto',
+};
+const secondLineStyle = {
+  display: 'flex',
+  flexDirection: 'row',
+  justifyContent: 'space-between',
+  fontSize: '13px',
+  color: '#888888',
+};
+const timestampStyle = {
+  alignSelf: 'flex-start',
+};
+const rowsAffectedStyle = {
+  alignSelf: 'flex-end',
+};
+const errorMessageStyle = {
+  textOverflow: 'ellipsis',
+  overflow: 'hidden',
+  whiteSpace: 'nowrap',
+  userSelect: 'auto',
+  fontSize: '13px',
+  color: '#888888',
+};
+
+export default class QueryHistoryEntry extends React.Component {
+  formatDate(date) {
+    return (moment(date).format('MMM D YYYY [–] HH:mm:ss'));
+  }
+
+  render() {
+    return (
+      <div style={this.queryEntryBackgroundColor()}>
+        <div style={sqlStyle}>
+          {this.props.historyEntry.query}
+        </div>
+        <div style={secondLineStyle}>
+          <div style={timestampStyle}>
+            {this.formatDate(this.props.historyEntry.start_time)} /
+            total time: {this.props.historyEntry.total_time}
+          </div>
+          <div style={rowsAffectedStyle}>
+            {this.props.historyEntry.row_affected} rows affected
+          </div>
+        </div>
+        <div style={errorMessageStyle}>
+          {this.props.historyEntry.message}
+        </div>
+      </div>
+    );
+  }
+
+  queryEntryBackgroundColor() {
+    if (!this.props.historyEntry.status) {
+      return update(outerDivStyle, {$merge: {backgroundColor: '#F7D0D5'}});
+    }
+    return outerDivStyle;
+  }
+}
+
+QueryHistoryEntry.propTypes = {
+  historyEntry: React.PropTypes.shape({
+    query: React.PropTypes.string,
+    start_time: React.PropTypes.instanceOf(Date),
+    status: React.PropTypes.bool,
+    total_time: React.PropTypes.string,
+    row_affected: React.PropTypes.int,
+    message: React.PropTypes.string,
+  }),
+};
\ No newline at end of file
diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js b/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js
index da24f771..c6e02e6e 100644
--- a/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js
@@ -9,7 +9,10 @@ define([
     'sources/slickgrid/event_handlers/handle_query_output_keyboard_event',
     'sources/selection/xcell_selection_model',
     'sources/selection/set_staged_rows',
-    'sources/gettext', 'sources/sqleditor_utils',
+    'sources/gettext',
+    'sources/sqleditor_utils',
+    'sources/generated/history',
+    'sources/generated/reactComponents',
 
     'slickgrid', 'bootstrap', 'pgadmin.browser', 'wcdocker',
     'codemirror/mode/sql/sql', 'codemirror/addon/selection/mark-selection',
@@ -32,9 +35,9 @@ define([
     'slickgrid/plugins/slick.rowselectionmodel',
     'slickgrid/slick.grid'
 ], function(
-    $, _, S, alertify, pgAdmin, Backbone, Backgrid, CodeMirror, pgExplain, GridSelector,
-    ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
-    XCellSelectionModel, setStagedRows, gettext, SqlEditorUtils
+  $, _, S, alertify, pgAdmin, Backbone, Backgrid, CodeMirror,
+  pgExplain, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
+    XCellSelectionModel, setStagedRows, gettext, SqlEditorUtils, HistoryBundle, reactComponents
 ) {
     /* Return back, this has been called more than once */
     if (pgAdmin.SqlEditor)
@@ -864,147 +867,14 @@ define([
 
         // Remove any existing grid first
         if (self.history_grid) {
-            self.history_grid.remove();
+          self.history_grid.remove();
         }
 
-        var history_model = Backbone.Model.extend({
-          defaults: {
-            status: undefined,
-            start_time: undefined,
-            query: undefined,
-            row_affected: 0,
-            row_retrieved: 0,
-            total_time: undefined,
-            message: ''
-          }
-        });
-
-        var history_collection = self.history_collection = new (Backbone.Collection.extend({
-            model: history_model,
-            // comparator to sort the history in reverse order of the start_time
-            comparator: function(a, b) {
-              return -a.get('start_time').localeCompare(b.get('start_time'));
-            }
-        }));
-        var columns = [{
-            name: "status",
-            label: "",
-            cell: Backgrid.Cell.extend({
-              class: 'sql-status-cell',
-              render: function() {
-                this.$el.empty();
-                var $btn = $('<button></button>', {
-                  class: 'btn btn-circle'
-                }).appendTo(this.$el);
-                var $circleDiv = $('<i></i>', {class: 'fa'}).appendTo($btn);
-                if (this.model.get('status')) {
-                  $btn.addClass('btn-success');
-                  $circleDiv.addClass('fa-check');
-                } else {
-                  $btn.addClass('btn-danger');
-                  $circleDiv.addClass('fa-times');
-                }
-
-                return this;
-              },
-              editable: false
-            }),
-            editable: false
-          }, {
-            name: "start_time",
-            label: "Date",
-            cell: "string",
-            editable: false,
-            resizeable: true
-          }, {
-            name: "query",
-            label: "Query",
-            cell: "string",
-            editable: false,
-            resizeable: true
-          }, {
-            name: "row_affected",
-            label: "Rows affected",
-            cell: "integer",
-            editable: false,
-            resizeable: true
-          }, {
-            name: "total_time",
-            label: "Total Time",
-            cell: "string",
-            editable: false,
-            resizeable: true
-          }, {
-            name: "message",
-            label: "Message",
-            cell: "string",
-            editable: false,
-            resizeable: true
-        }];
-
-
-        // Create Collection of Backgrid columns
-        var columnsColl = new Backgrid.Columns(columns);
-        var $history_grid = self.$el.find('#history_grid');
-
-        var grid = self.history_grid = new Backgrid.Grid({
-            columns: columnsColl,
-            collection: history_collection,
-            className: "backgrid table-bordered presentation table backgrid-striped"
-        });
-
-        // Render the grid
-        $history_grid.append(grid.render().$el);
-
-        var sizeAbleCol = new Backgrid.Extension.SizeAbleColumns({
-          collection: history_collection,
-          columns: columnsColl,
-          grid: self.history_grid
-        });
-
-        $history_grid.find('thead').before(sizeAbleCol.render().el);
+        self.history_collection = new HistoryBundle.historyCollection([]);
 
-        // Add resize handlers
-        var sizeHandler = new Backgrid.Extension.SizeAbleColumnsHandlers({
-          sizeAbleColumns: sizeAbleCol,
-          grid: self.history_grid,
-          saveColumnWidth: true
-        });
-
-        // sizeHandler should render only when table grid loaded completely.
-        setTimeout(function() {
-          $history_grid.find('thead').before(sizeHandler.render().el);
-        }, 1000);
-
-        // re render sizeHandler whenever history panel tab becomes visible
-        self.history_panel.on(wcDocker.EVENT.VISIBILITY_CHANGED, function(ev) {
-          $history_grid.find('thead').before(sizeHandler.render().el);
-        });
-
-        // Initialized table width 0 still not calculated
-        var table_width = 0;
-        // Listen to resize events
-        columnsColl.on('resize',
-          function(columnModel, newWidth, oldWidth, offset) {
-            var $grid_el = $history_grid.find('table'),
-                tbl_orig_width = $grid_el.width(),
-                offset = oldWidth - newWidth,
-                tbl_new_width = tbl_orig_width - offset;
-
-            if (table_width == 0) {
-              table_width = tbl_orig_width
-            }
-            // Table new width cannot be less than original width
-            if (tbl_new_width >= table_width) {
-              $($grid_el).css('width', tbl_new_width + 'px');
-            }
-            else {
-              // reset if calculated tbl_new_width is less than original
-              // table width
-              tbl_new_width = table_width;
-              $($grid_el).css('width', tbl_new_width + 'px');
-            }
-        });
+        let queryHistoryElement = reactComponents.React.createElement(
+          reactComponents.QueryHistory, {historyCollection: self.history_collection});
+        reactComponents.render(queryHistoryElement, $('#history_grid')[0]);
       },
 
       // Callback function for Add New Row button click.
@@ -1307,7 +1177,7 @@ define([
         this._stopEventPropogation(ev);
         this._closeDropDown(ev);
         // ask for confirmation only if anything to clear
-        if(!self.history_collection.length) { return; }
+        if(!self.history_collection.length()) { return; }
 
         alertify.confirm(gettext("Clear history"),
           gettext("Are you sure you wish to clear the history?"),
@@ -2130,11 +2000,13 @@ define([
             $("#btn-flash").prop('disabled', false);
             self.trigger('pgadmin-sqleditor:loading-icon:hide');
             self.gridView.history_collection.add({
-              'status' : status, 'start_time': self.query_start_time.toString(),
-              'query': self.query, 'row_affected': self.rows_affected,
-              'total_time': self.total_time, 'message':msg
+              'status' : status,
+              'start_time': self.query_start_time,
+              'query': self.query,
+              'row_affected': self.rows_affected,
+              'total_time': self.total_time,
+              'message':msg,
             });
-            self.gridView.history_collection.sort();
           }
         },
 
@@ -2407,10 +2279,13 @@ define([
 
                 // Update the sql results in history tab
                 _.each(res.data.query_result, function(r) {
-                  self.gridView.history_collection.add(
-                    {'status' : r.status, 'start_time': self.query_start_time.toString(),
-                    'query': r.sql, 'row_affected': r.rows_affected,
-                    'total_time': self.total_time, 'message': r.result
+                  self.gridView.history_collection.add({
+                    'status': r.status,
+                    'start_time': self.query_start_time,
+                    'query': r.sql,
+                    'row_affected': r.rows_affected,
+                    'total_time': self.total_time,
+                    'message': r.result,
                   });
                 });
                 self.trigger('pgadmin-sqleditor:loading-icon:hide');
@@ -3356,7 +3231,7 @@ define([
 
                 var msg = e.responseText;
                 if (e.responseJSON != undefined &&
-                  e.responseJSON.errormsg != undefined)
+                    e.responseJSON.errormsg != undefined)
                   msg = e.responseJSON.errormsg;
 
                 alertify.alert('Get Object Name Error', msg);
diff --git a/web/regression/feature_utils/app_starter.py b/web/regression/feature_utils/app_starter.py
index 96cc516b..f40d6921 100644
--- a/web/regression/feature_utils/app_starter.py
+++ b/web/regression/feature_utils/app_starter.py
@@ -11,6 +11,7 @@ import subprocess
 import signal
 import random
 
+import time
 
 class AppStarter:
     """ Helper for starting the full pgadmin4 app and loading the page via
@@ -40,6 +41,7 @@ class AppStarter:
         )
 
         self.driver.set_window_size(1024, 1024)
+        time.sleep(10)
         self.driver.get(
             "http://" + self.app_config.DEFAULT_SERVER + ":" +
             random_server_port)
diff --git a/web/regression/feature_utils/pgadmin_page.py b/web/regression/feature_utils/pgadmin_page.py
index 46d50156..a1cb7147 100644
--- a/web/regression/feature_utils/pgadmin_page.py
+++ b/web/regression/feature_utils/pgadmin_page.py
@@ -33,15 +33,16 @@ class PgadminPage:
     def reset_layout(self):
         self.click_element(self.find_by_partial_link_text("File"))
         self.find_by_partial_link_text("Reset Layout").click()
-        self.click_modal_ok()
+        self.click_modal('OK')
         self.wait_for_reloading_indicator_to_disappear()
 
-    def click_modal_ok(self):
+    def click_modal(self, button_text):
         time.sleep(0.5)
         # Find active alertify dialog in case of multiple alertify dialog & click on that dialog
-        self.click_element(
-            self.find_by_xpath("//div[contains(@class, 'alertify') and not(contains(@class, 'ajs-hidden'))]//button[.='OK']")
-        )
+        modal_button = self.find_by_xpath(
+            "//div[contains(@class, 'alertify') and not(contains(@class, 'ajs-hidden'))]//button[.='%s']"
+            % button_text)
+        self.click_element(modal_button)
 
     def add_server(self, server_config):
         self.find_by_xpath("//*[@class='aciTreeText' and contains(.,'Servers')]").click()
@@ -78,10 +79,13 @@ class PgadminPage:
 
     def remove_server(self, server_config):
         self.driver.switch_to.default_content()
-        self.find_by_xpath("//*[@id='tree']//*[.='" + server_config['name'] + "' and @class='aciTreeItem']").click()
-        self.find_by_partial_link_text("Object").click()
-        self.find_by_partial_link_text("Delete/Drop").click()
-        self.click_modal_ok()
+        server_to_remove = self.find_by_xpath("//*[@id='tree']//*[.='" + server_config['name'] + "' and @class='aciTreeItem']")
+        self.click_element(server_to_remove)
+        object_menu_item = self.find_by_partial_link_text("Object")
+        self.click_element(object_menu_item)
+        delete_menu_item = self.find_by_partial_link_text("Delete/Drop")
+        self.click_element(delete_menu_item)
+        self.click_modal('OK')
 
     def select_tree_item(self, tree_item_text):
         self.find_by_xpath("//*[@id='tree']//*[.='" + tree_item_text + "' and @class='aciTreeItem']").click()
@@ -130,6 +134,7 @@ class PgadminPage:
         )
 
     def click_element(self, element):
+        # driver must be here to adhere to the method contract in selenium.webdriver.support.wait.WebDriverWait.until()
         def click_succeeded(driver):
             try:
                 element.click()
@@ -175,8 +180,9 @@ class PgadminPage:
         time.sleep(sleep_time)
 
     def click_tab(self, tab_name):
-        self.find_by_xpath("//*[contains(@class,'wcTabTop')]//*[contains(@class,'wcPanelTab') "
-                           "and contains(.,'" + tab_name + "')]").click()
+        tab = self.find_by_xpath("//*[contains(@class,'wcTabTop')]//*[contains(@class,'wcPanelTab') "
+                           "and contains(.,'" + tab_name + "')]")
+        self.click_element(tab)
 
     def wait_for_input_field_content(self, field_name, content):
         def input_field_has_content(driver):
diff --git a/web/regression/javascript/check_node_visiblity_spec.js b/web/regression/javascript/check_node_visibility_spec.js
similarity index 100%
rename from web/regression/javascript/check_node_visiblity_spec.js
rename to web/regression/javascript/check_node_visibility_spec.js
diff --git a/web/regression/javascript/history/history_collection_spec.js b/web/regression/javascript/history/history_collection_spec.js
new file mode 100644
index 00000000..e1baa554
--- /dev/null
+++ b/web/regression/javascript/history/history_collection_spec.js
@@ -0,0 +1,83 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import HistoryCollection from '../../../pgadmin/static/js/history/history_collection';
+
+describe('historyCollection', function () {
+  let historyCollection, historyModel, onChangeSpy;
+  beforeEach(() => {
+    historyModel = [{some: 'thing', someOther: ['array element']}];
+    historyCollection = new HistoryCollection(historyModel);
+    onChangeSpy = jasmine.createSpy('onChangeHandler');
+
+    historyCollection.onChange(onChangeSpy);
+  });
+
+  describe('length', function () {
+    it('returns 0 when underlying history model has no elements', function () {
+      historyCollection = new HistoryCollection([]);
+
+      expect(historyCollection.length()).toBe(0);
+    });
+
+    it('returns the length of the underlying history model', function () {
+      expect(historyCollection.length()).toBe(1);
+    });
+  });
+
+  describe('add', function () {
+    let expectedHistory;
+    beforeEach(() => {
+      historyCollection.add({some: 'new thing', someOther: ['value1', 'value2']});
+
+      expectedHistory = [
+        {some: 'thing', someOther: ['array element']},
+        {some: 'new thing', someOther: ['value1', 'value2']},
+      ];
+    });
+
+    it('adds a passed entry', function () {
+      expect(historyCollection.historyList).toEqual(expectedHistory);
+    });
+
+    it('calls the onChange function', function () {
+      expect(onChangeSpy).toHaveBeenCalledWith(expectedHistory);
+    });
+  });
+
+  describe('reset', function () {
+    beforeEach(() => {
+      historyCollection.reset();
+    });
+
+    it('drops the history', function () {
+      expect(historyCollection.historyList).toEqual([]);
+      expect(historyCollection.length()).toBe(0);
+    });
+
+    it('calls the onChange function', function () {
+      expect(onChangeSpy).toHaveBeenCalledWith([]);
+    });
+  });
+
+  describe('sort', function () {
+    it('doesn\'t sort');
+  });
+
+  describe('when instantiated', function () {
+    describe('from a history model', function () {
+      it('has the historyModel', () => {
+        let content = historyCollection.historyList;
+
+        expect(content).toEqual(historyModel);
+      });
+
+    });
+  });
+});
\ No newline at end of file
diff --git a/web/regression/javascript/history/query_history_entry_spec.jsx b/web/regression/javascript/history/query_history_entry_spec.jsx
new file mode 100644
index 00000000..c86a1cfc
--- /dev/null
+++ b/web/regression/javascript/history/query_history_entry_spec.jsx
@@ -0,0 +1,50 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import React from 'react';
+
+import QueryHistoryEntry from '../../../pgadmin/static/jsx/history/query_history_entry';
+
+import {mount} from 'enzyme';
+import jasmineEnzyme from 'jasmine-enzyme';
+
+describe('QueryHistoryEntry', () => {
+  let historyWrapper;
+  beforeEach(() => {
+    jasmineEnzyme();
+  });
+
+  describe('for a failed query', () => {
+    beforeEach(() => {
+      const historyEntry = {
+        query: 'second sql statement',
+        start_time: new Date(2016, 11, 11, 1, 33, 5, 99),
+        status: false,
+      };
+      historyWrapper = mount(<QueryHistoryEntry historyEntry={historyEntry}/>);
+    });
+    it('displays a pink background color', () => {
+      expect(historyWrapper.find('div').first()).toHaveStyle('backgroundColor', '#F7D0D5');
+    });
+  });
+
+  describe('for a successful query', () => {
+    beforeEach(() => {
+      const historyEntry = {
+        query: 'second sql statement',
+        start_time: new Date(2016, 11, 11, 1, 33, 5, 99),
+        status: true,
+      };
+      historyWrapper = mount(<QueryHistoryEntry historyEntry={historyEntry}/>);
+    });
+    it('does not display a pink background color', () => {
+      expect(historyWrapper.find('div').first()).toHaveStyle('backgroundColor', '#FFF');
+    });
+  });
+});
diff --git a/web/regression/javascript/history/query_history_spec.jsx b/web/regression/javascript/history/query_history_spec.jsx
new file mode 100644
index 00000000..e36988a8
--- /dev/null
+++ b/web/regression/javascript/history/query_history_spec.jsx
@@ -0,0 +1,103 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2017, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import React from 'react';
+import QueryHistory from '../../../pgadmin/static/jsx/history/query_history';
+import QueryHistoryEntry from '../../../pgadmin/static/jsx/history/query_history_entry';
+import HistoryCollection from '../../../pgadmin/static/js/history/history_collection';
+import jasmineEnzyme from 'jasmine-enzyme';
+
+import {mount, shallow} from 'enzyme';
+
+describe('QueryHistory', () => {
+  let historyWrapper;
+  beforeEach(() => {
+    jasmineEnzyme();
+    const historyCollection = new HistoryCollection([]);
+    historyWrapper = shallow(<QueryHistory historyCollection={historyCollection}/>);
+  });
+
+  describe('on construction', () => {
+    it('has no entries', (done) => {
+      let foundChildren = historyWrapper.find(QueryHistoryEntry);
+      expect(foundChildren.length).toBe(0);
+      done();
+    });
+  });
+
+  describe('when it has history', () => {
+    describe('when two SQL queries were executed', () => {
+      let foundChildren;
+
+      beforeEach(() => {
+        const historyObjects = [
+          {
+            query: 'second sql statement',
+            start_time: new Date(2016, 11, 11, 1, 33, 5, 99),
+            status: false,
+            row_affected: 1,
+            total_time: '234 msec',
+            message: 'some other message',
+          },
+          {
+            query: 'first sql statement',
+            start_time: new Date(2017, 5, 3, 14, 3, 15, 150),
+            status: true,
+            row_affected: 2,
+            total_time: '14 msec',
+            message: 'a very important message',
+          },
+        ];
+        const historyCollection = new HistoryCollection(historyObjects);
+
+        historyWrapper = mount(<QueryHistory historyCollection={historyCollection}/>);
+
+        foundChildren = historyWrapper.find(QueryHistoryEntry);
+      });
+
+      it('has two query history entries', () => {
+        expect(foundChildren.length).toBe(2);
+      });
+
+      it('displays the SQL of the queries in order', () => {
+        expect(foundChildren.at(0).text()).toContain('first sql statement');
+        expect(foundChildren.at(1).text()).toContain('second sql statement');
+      });
+
+      it('displays the formatted timestamp of the queries in chronological order by most recent first', () => {
+        expect(foundChildren.at(0).text()).toContain('Jun 3 2017 – 14:03:15');
+        expect(foundChildren.at(1).text()).toContain('Dec 11 2016 – 01:33:05');
+      });
+
+      it('displays the number of rows affected', () => {
+        expect(foundChildren.at(1).text()).toContain('1 rows affected');
+        expect(foundChildren.at(0).text()).toContain('2 rows affected');
+      });
+
+      it('displays the total time', () => {
+        expect(foundChildren.at(0).text()).toContain('total time: 14 msec');
+        expect(foundChildren.at(1).text()).toContain('total time: 234 msec');
+      });
+
+      it('displays the truncated message', () => {
+        expect(foundChildren.at(0).text()).toContain('a very important message');
+        expect(foundChildren.at(1).text()).toContain('some other message');
+      });
+
+      describe('when there are one failing and one successful query each', () => {
+        it('adds a white background color for the successful query', () => {
+          expect(foundChildren.at(0).find('div').first()).toHaveStyle('backgroundColor', '#FFF');
+        });
+        it('adds a red background color for the failed query', () => {
+          expect(foundChildren.at(1).find('div').first()).toHaveStyle('backgroundColor', '#F7D0D5');
+        });
+      });
+    });
+  });
+});
\ No newline at end of file
diff --git a/web/regression/python_test_utils/test_utils.py b/web/regression/python_test_utils/test_utils.py
index cf559c44..f3e7ed01 100644
--- a/web/regression/python_test_utils/test_utils.py
+++ b/web/regression/python_test_utils/test_utils.py
@@ -227,6 +227,7 @@ def create_constraint(
     except Exception:
         traceback.print_exc(file=sys.stderr)
 
+
 def create_debug_function(server, db_name, function_name="test_func"):
     try:
         connection = get_db_connection(db_name,
@@ -305,6 +306,7 @@ def drop_database(connection, database_name):
             connection.commit()
             connection.close()
 
+
 def drop_tablespace(connection):
     """This function used to drop the tablespace"""
     pg_cursor = connection.cursor()
diff --git a/web/webpack.config.js b/web/webpack.config.js
index 91586592..fc6d29e0 100644
--- a/web/webpack.config.js
+++ b/web/webpack.config.js
@@ -1,18 +1,21 @@
 /* eslint-env node */
 
 module.exports = {
-  context: __dirname + '/pgadmin/static/jsx',
-  entry: './components.jsx',
+  context: __dirname + '/pgadmin/static',
+  entry: {
+    reactComponents: './jsx/components.jsx',
+    history: './js/history/index.js',
+  },
   output: {
     libraryTarget: 'amd',
     path: __dirname + '/pgadmin/static/js/generated',
-    filename: 'reactComponents.js',
+    filename: '[name].js',
   },
 
   module: {
     rules: [{
       test: /\.jsx?$/,
-      exclude: /node_modules/,
+      exclude: [/node_modules/, /vendor/],
       use: {
         loader: 'babel-loader',
         options: {
diff --git a/web/webpack.test.config.js b/web/webpack.test.config.js
index b156f147..4909f4a7 100644
--- a/web/webpack.test.config.js
+++ b/web/webpack.test.config.js
@@ -22,7 +22,7 @@ module.exports = {
         use: {
           loader: 'babel-loader',
           options: {
-            presets: ['es2015'],
+            presets: ['es2015', 'react'],
           },
         },
       },
@@ -51,6 +51,7 @@ module.exports = {
   },
 
   resolve: {
+    extensions: ['.js', '.jsx'],
     alias: {
       'alertify': sourcesDir + '/vendor/alertifyjs/alertify',
       'jquery': sourcesDir + '/vendor/jquery/jquery-1.11.2',
@@ -66,4 +67,11 @@ module.exports = {
       'pgadmin': sourcesDir + '/js/pgadmin',
     },
   },
+  externals: {
+    'react/addons': true,
+    'react/lib/ReactContext': true,
+    'react/lib/ExecutionEnvironment': true,
+    'react-dom/test-utils': true,
+    'react-test-renderer/shallow': true,
+  },
 };
diff --git a/web/yarn.lock b/web/yarn.lock
index b04caa5c..ce72d41b 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -1371,6 +1371,12 @@ decamelize@^1.0.0, decamelize@^1.1.1:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
 
+deep-equal-ident@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/deep-equal-ident/-/deep-equal-ident-1.1.1.tgz#06f4b89e53710cd6cea4a7781c7a956642de8dc9"
+  dependencies:
+    lodash.isequal "^3.0"
+
 deep-extend@~0.4.0:
   version "0.4.2"
   resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
@@ -1608,6 +1614,12 @@ entities@^1.1.1, entities@~1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
 
+enzyme-matchers@^3.1.0, enzyme-matchers@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/enzyme-matchers/-/enzyme-matchers-3.2.0.tgz#4718779a3b9eb5e8ebad46804f8d3e66045d0181"
+  dependencies:
+    deep-equal-ident "^1.1.1"
+
 enzyme@~2.8.2:
   version "2.8.2"
   resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-2.8.2.tgz#6c8bcb05012abc4aa4bc3213fb23780b9b5b1714"
@@ -2299,6 +2311,12 @@ ignore@^3.2.0:
   version "3.3.3"
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d"
 
+immutability-helper@^2.2.0:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/immutability-helper/-/immutability-helper-2.2.2.tgz#e7e9da728b3de2fad34a216f4157b326dbccc892"
+  dependencies:
+    invariant "^2.2.0"
+
 "imports-loader@git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d":
   version "0.7.1"
   resolved "git+https://github.com/webpack-contrib/imports-loader.git#44d6f48463b256a17c1ba6fd9b5cc1449b4e379d"
@@ -2568,6 +2586,12 @@ jasmine-core@~2.5.2:
   version "2.5.2"
   resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.5.2.tgz#6f61bd79061e27f43e6f9355e44b3c6cab6ff297"
 
+jasmine-enzyme@^3.1.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/jasmine-enzyme/-/jasmine-enzyme-3.2.0.tgz#0eeb370d4fa965db03e04347ca9c4ed5a60fadc2"
+  dependencies:
+    enzyme-matchers "^3.2.0"
+
 jodid25519@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967"
@@ -2828,6 +2852,22 @@ loader-utils@^1.0.2:
     emojis-list "^2.0.0"
     json5 "^0.5.0"
 
+lodash._baseisequal@^3.0.0:
+  version "3.0.7"
+  resolved "https://registry.yarnpkg.com/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz#d8025f76339d29342767dcc887ce5cb95a5b51f1"
+  dependencies:
+    lodash.isarray "^3.0.0"
+    lodash.istypedarray "^3.0.0"
+    lodash.keys "^3.0.0"
+
+lodash._bindcallback@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
+
+lodash._getnative@^3.0.0:
+  version "3.9.1"
+  resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
+
 lodash.assignin@^4.0.9:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2"
@@ -2852,6 +2892,33 @@ lodash.foreach@^4.3.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
 
+lodash.isarguments@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
+
+lodash.isarray@^3.0.0:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
+
+lodash.isequal@^3.0:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-3.0.4.tgz#1c35eb3b6ef0cd1ff51743e3ea3cf7fdffdacb64"
+  dependencies:
+    lodash._baseisequal "^3.0.0"
+    lodash._bindcallback "^3.0.0"
+
+lodash.istypedarray@^3.0.0:
+  version "3.0.6"
+  resolved "https://registry.yarnpkg.com/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz#c9a477498607501d8e8494d283b87c39281cef62"
+
+lodash.keys@^3.0.0:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
+  dependencies:
+    lodash._getnative "^3.0.0"
+    lodash.isarguments "^3.0.0"
+    lodash.isarray "^3.0.0"
+
 lodash.map@^4.4.0:
   version "4.6.0"
   resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3"
@@ -3013,6 +3080,10 @@ module-deps@^4.0.8:
     through2 "^2.0.0"
     xtend "^4.0.0"
 
+moment@^2.18.1:
+  version "2.18.1"
+  resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f"
+
 [email protected]:
   version "0.7.1"
   resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
@@ -3499,10 +3570,10 @@ randomatic@^1.1.3:
     kind-of "^3.0.2"
 
 randombytes@^2.0.0, randombytes@^2.0.1:
-  version "2.0.4"
-  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.4.tgz#9551df208422c8f80eb58e2326dd0b840ff22efd"
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.5.tgz#dc009a246b8d09a177b4b7a0ae77bc570f4b1b79"
   dependencies:
-    safe-buffer "^5.0.1"
+    safe-buffer "^5.1.0"
 
 range-parser@^1.0.3, range-parser@^1.2.0:
   version "1.2.0"
@@ -3818,7 +3889,7 @@ rx-lite@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
 
-safe-buffer@^5.0.1:
+safe-buffer@^5.0.1, safe-buffer@^5.1.0:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a45e223"
 


view thread (45+ messages)  latest in thread

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
  Subject: Re: [pgAdmin4] [PATCH] History Tab rewrite in React
  In-Reply-To: <CAHowoHZFkDQrMO0-e2gx3ffo3kB56Qwb3jT2S1M7Sc8Bpo0Usw@mail.gmail.com>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox