superset-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From b...@apache.org
Subject [incubator-superset] branch lyftga updated: Fetch charts with GET to benefit from browser cache and conditional requests (#7032)
Date Wed, 03 Apr 2019 19:11:25 GMT
This is an automated email from the ASF dual-hosted git repository.

beto pushed a commit to branch lyftga
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/lyftga by this push:
     new 538776b  Fetch charts with GET to benefit from browser cache and conditional requests (#7032)
538776b is described below

commit 538776b47025f8ff97f293f943736cb3633b2702
Author: Beto Dealmeida <roberto@dealmeida.net>
AuthorDate: Wed Apr 3 12:11:08 2019 -0700

    Fetch charts with GET to benefit from browser cache and conditional requests (#7032)
    
    * Sparkline dates aren't formatting in Time Series Table (#6976)
    
    * Exclude venv for python linter to ignore
    
    * Fix NaN error
    
    * Fix the white background shown in SQL editor on drag (#7021)
    
    This PR sets the background-color css property on `.ace_scroller` instead of `.ace_content` to prevent the white background shown during resizing of the SQL editor before drag ends.
    
    * Show tooltip with time frame (#6979)
    
    * Fix time filter control (#6978)
    
    * Enhancement of query context and object. (#6962)
    
    * added more functionalities for query context and object.
    
    * fixed cache logic
    
    * added default value for groupby
    
    * updated comments and removed print
    
    (cherry picked from commit d5b9795f87f79fa2c41e144ffc00fd9586be7657)
    
    * [fix] /superset/slice/id url is too long (#6989)
    
    
    (cherry picked from commit 6a4d507ab607b01ed324cb3341b71c6fb2cb5c97)
    
    * [WIP] fix user specified JSON metadata not updating dashboard on refresh (#7027)
    
    
    (cherry picked from commit cc58f0e661044e95c7c86d0da8d77a0a6640efe7)
    
    * feat: add ability to change font size in big number (#7003)
    
    * Add ability to change font sizes in Big Number
    
    * rename big number to header
    
    * Add comment to clarify font size values
    
    * Allow LIMIT to be specified in parameters (#7052)
    
    * [fix] Cursor jumping when editing chart and dashboard titles (#7038)
    
    
    (cherry picked from commit fc1770f7b79a4d8815b646b46390fabf190c3815)
    
    * Changing time table viz to pass formatTime a date (#7020)
    
    (cherry picked from commit 7f3c145b1f5a4e2d8b95982119503e98772e2c47)
    
    * [db-engine-spec] Aligning Hive/Presto partition logic (#7007)
    
    
    (cherry picked from commit 05be86611785fef2904992e4e7d31dce23f1c51b)
    
    * [fix] explore chart from dashboard missed slice title (#7046)
    
    
    (cherry picked from commit a6d48d4052839286aec725d51303b3b2bf6e8dd4)
    
    * fix inaccurate data calculation with adata rolling and contribution (#7035)
    
    
    (cherry picked from commit 0782e831cd37f665a2838119d87c433269f1b36b)
    
    * Adding warning message for sqllab save query (#7028)
    
    
    (cherry picked from commit ead3d48133e7e1ab8b91d51e561544a544b4eaad)
    
    * [datasource] Ensuring consistent behavior of datasource editing/saving. (#7037)
    
    * Update datasource.py
    
    * Update datasource.py
    
    (cherry picked from commit c771625f1068d3a7f41e6bced14b0cbdbf9962cc)
    
    * [csv-upload] Fixing message encoding (#6971)
    
    
    (cherry picked from commit 48431ab5b9375a94c5262a0336d9c69e5f01a3ac)
    
    * [sql-parse] Fixing LIMIT exceptions (#6963)
    
    
    (cherry picked from commit 3e076cb60b385e675ed1c9a8053493375e43370b)
    
    * Adding custom control overrides (#6956)
    
    * Adding extraOverrides to line chart
    
    * Updating extraOverrides to fit with more cases
    
    * Moving extraOverrides to index.js
    
    * Removing webpack-merge in package.json
    
    * Fixing metrics control clearing metric
    
    (cherry picked from commit e6194051f486e42922dc4e34a861f4490c1062fc)
    
    * [sqlparse] Fixing table name extraction for ill-defined query (#7029)
    
    
    (cherry picked from commit 07c340cf8203f13222f16efad1e55e202deb1865)
    
    * [missing values] Removing replacing missing values (#4905)
    
    
    (cherry picked from commit 61add606ca16a6ba981ccde864b121f5464b697a)
    
    * [SQL Lab] Improved query and results tabs rendering reliability (#7082)
    
    closes #7080
    
    (cherry picked from commit 9b58e9f4920ef424e5b545dcbb4726e22bed5982)
    
    * Fix filter_box migration PR #6523 (#7066)
    
    * Fix filter_box migration PR #6523
    
    * Fix druid-related bug
    
    (cherry picked from commit b210742ad24d01ca05bc58ca3342c90e301fe073)
    
    * SQL editor layout makeover (#7102)
    
    This PR includes the following layout and css tweaks:
    - Using flex to layout the north and south sub panes of query pane so resizing works properly in both Chrome and Firefox
    - Removal of necessary wrapper divs and tweaking of css in sql lab so we can scroll to the bottom of both the table list and the results pane
    - Make sql lab's content not overflow vertically and layout the query result area to eliminate double scroll bars
    - css tweaks on the basic.html page so the loading animation appears in the center of the page across the board
    
    (cherry picked from commit 71f1bbd2ec59b99d6ba6d9a4a2f9cfceaf922b80)
    
    * [forms] Fix handling of NULLs
    
    (cherry picked from commit e83a07d3dfda350cc44041cb6cbaec4510887902)
    
    * handle null column_name in sqla and druid models
    
    (cherry picked from commit 2ff721ae072b8d69c5cabddc3e1a388a596b1b6f)
    
    * Use metric name instead of metric in filter box (#7106)
    
    
    (cherry picked from commit 003364e74ea70cad1a4a6e784933fe8bef4c78ec)
    
    * Bump python lib croniter to an existing version (#7132)
    
    Package maintainers should really never delete packages, but it appears
    this happened with croniter and resulted in breaking our builds.
    
    This PR bumps to a more recent existing version of the library
    
    (cherry picked from commit 215ed392a11598eac228f57341dbfd232cf770e3)
    
    * Revert PR #6933 (#7162)
    
    * Add decorator for etag cache
    
    * Fetch charts with GET
    
    * Small fixes
    
    * Fix typo
    
    * Compute correct cache key; fix logging
    
    * Check perms on cached response
    
    * Revert change
    
    * If perms fail, return naked response
    
    * Fix lint
    
    * Compute cache key from all form data
    
    * Pass extra_filters in GET request
    
    * Fix pylint
    
    * Fix flake8
    
    * Use ETags even if no cache is set
    
    * Handle adhoc filters
    
    * Raise in debug mode
    
    * Rename actions
    
    * Fix integration tests
    
    * Do POST request on new charts
    
    * Set extra/adhoc filters only in GET requests
    
    * Raise if check_perms fails
    
    * Refactor auth
    
    * Fix flake8
    
    * Fix js unit tests
    
    * Fix js unit tests that fail in lyftga
    
    * Fix js
    
    * Sparkline dates aren't formatting in Time Series Table (#6976)
    
    * Exclude venv for python linter to ignore
    
    * Fix NaN error
    
    * Changing time table viz to pass formatTime a date (#7020)
    
    (cherry picked from commit 7f3c145b1f5a4e2d8b95982119503e98772e2c47)
    
    * SQL editor layout makeover (#7102)
    
    This PR includes the following layout and css tweaks:
    - Using flex to layout the north and south sub panes of query pane so resizing works properly in both Chrome and Firefox
    - Removal of necessary wrapper divs and tweaking of css in sql lab so we can scroll to the bottom of both the table list and the results pane
    - Make sql lab's content not overflow vertically and layout the query result area to eliminate double scroll bars
    - css tweaks on the basic.html page so the loading animation appears in the center of the page across the board
    
    (cherry picked from commit 71f1bbd2ec59b99d6ba6d9a4a2f9cfceaf922b80)
    
    * Add decorator for etag cache
    
    * Fetch charts with GET
    
    * Small fixes
    
    * Fix typo
    
    * Compute correct cache key; fix logging
    
    * Check perms on cached response
    
    * Revert change
    
    * If perms fail, return naked response
    
    * Fix lint
    
    * Compute cache key from all form data
    
    * Pass extra_filters in GET request
    
    * Fix pylint
    
    * Fix flake8
    
    * Use ETags even if no cache is set
    
    * Handle adhoc filters
    
    * Raise in debug mode
    
    * Rename actions
    
    * Fix integration tests
    
    * Do POST request on new charts
    
    * Set extra/adhoc filters only in GET requests
    
    * Raise if check_perms fails
    
    * Refactor auth
    
    * Fix flake8
    
    * Fix js unit tests
    
    * Fix js unit tests that fail in lyftga
    
    * Fix js
    
    * Fix bad merge
    
    * Use far future when max_age=0
---
 .../cypress/integration/dashboard/controls.js      |   2 +-
 .../cypress/integration/dashboard/edit_mode.js     |   2 +-
 .../assets/cypress/integration/dashboard/filter.js |   2 +-
 .../assets/cypress/integration/dashboard/load.js   |   2 +-
 .../assets/cypress/integration/dashboard/save.js   |   2 +-
 .../cypress/integration/explore/control.test.js    |  36 +-
 .../cypress/integration/explore/link.test.js       |   9 +-
 superset/assets/package-lock.json                  | 362 ++++++++++-----------
 .../spec/javascripts/chart/chartActions_spec.js    |  14 +-
 .../dashboard/components/Dashboard_spec.jsx        |  10 +-
 superset/assets/src/chart/Chart.jsx                |  23 +-
 superset/assets/src/chart/chartAction.js           |  36 +-
 .../assets/src/dashboard/components/Dashboard.jsx  |   4 +-
 .../assets/src/dashboard/containers/Dashboard.jsx  |   4 +-
 .../src/explore/components/ExploreChartHeader.jsx  |   6 +-
 .../explore/components/ExploreViewContainer.jsx    |   5 +-
 superset/assets/src/explore/exploreUtils.js        |  15 +-
 superset/utils/decorators.py                       |  70 ++++
 superset/views/core.py                             | 200 ++++--------
 superset/views/utils.py                            | 118 ++++++-
 20 files changed, 552 insertions(+), 370 deletions(-)

diff --git a/superset/assets/cypress/integration/dashboard/controls.js b/superset/assets/cypress/integration/dashboard/controls.js
index fb103bd..3e218d2 100644
--- a/superset/assets/cypress/integration/dashboard/controls.js
+++ b/superset/assets/cypress/integration/dashboard/controls.js
@@ -39,7 +39,7 @@ export default () => describe('top-level controls', () => {
         .forEach((id) => {
           const sliceRequest = `getJson_${id}`;
           sliceRequests.push(`@${sliceRequest}`);
-          cy.route('POST', `/superset/explore_json/?form_data={"slice_id":${id}}`).as(sliceRequest);
+          cy.route('GET', `/superset/explore_json/?form_data={"slice_id":${id}}`).as(sliceRequest);
 
           const forceRefresh = `getJson_${id}_force`;
           forceRefreshRequests.push(`@${forceRefresh}`);
diff --git a/superset/assets/cypress/integration/dashboard/edit_mode.js b/superset/assets/cypress/integration/dashboard/edit_mode.js
index e58e7df..d9395d2 100644
--- a/superset/assets/cypress/integration/dashboard/edit_mode.js
+++ b/superset/assets/cypress/integration/dashboard/edit_mode.js
@@ -29,7 +29,7 @@ export default () => describe('edit mode', () => {
       const dashboard = bootstrapData.dashboard_data;
       const boxplotChartId = dashboard.slices.find(slice => (slice.form_data.viz_type === 'box_plot')).slice_id;
       const boxplotRequest = `/superset/explore_json/?form_data={"slice_id":${boxplotChartId}}`;
-      cy.route('POST', boxplotRequest).as('boxplotRequest');
+      cy.route('GET', boxplotRequest).as('boxplotRequest');
     });
 
     cy.get('.dashboard-header').contains('Edit dashboard').click();
diff --git a/superset/assets/cypress/integration/dashboard/filter.js b/superset/assets/cypress/integration/dashboard/filter.js
index 157cbe8..6ec1c92 100644
--- a/superset/assets/cypress/integration/dashboard/filter.js
+++ b/superset/assets/cypress/integration/dashboard/filter.js
@@ -40,7 +40,7 @@ export default () => describe('dashboard filter', () => {
     const aliases = [];
 
     const filterRoute = `/superset/explore_json/?form_data={"slice_id":${filterId}}`;
-    cy.route('POST', filterRoute).as('fetchFilter');
+    cy.route('GET', filterRoute).as('fetchFilter');
     cy.wait('@fetchFilter');
     sliceIds
       .filter(id => (parseInt(id, 10) !== filterId))
diff --git a/superset/assets/cypress/integration/dashboard/load.js b/superset/assets/cypress/integration/dashboard/load.js
index 30c9d32..0dbe1ff 100644
--- a/superset/assets/cypress/integration/dashboard/load.js
+++ b/superset/assets/cypress/integration/dashboard/load.js
@@ -34,7 +34,7 @@ export default () => describe('load', () => {
       // then define routes and create alias for each requests
       slices.forEach((slice) => {
         const alias = `getJson_${slice.slice_id}`;
-        cy.route('POST', `/superset/explore_json/?form_data={"slice_id":${slice.slice_id}}`).as(alias);
+        cy.route('GET', `/superset/explore_json/?form_data={"slice_id":${slice.slice_id}}`).as(alias);
         aliases.push(`@${alias}`);
       });
     });
diff --git a/superset/assets/cypress/integration/dashboard/save.js b/superset/assets/cypress/integration/dashboard/save.js
index 1c673b0..d144a71 100644
--- a/superset/assets/cypress/integration/dashboard/save.js
+++ b/superset/assets/cypress/integration/dashboard/save.js
@@ -57,7 +57,7 @@ export default () => describe('save', () => {
 
     // should have box_plot chart
     const boxplotRequest = `/superset/explore_json/?form_data={"slice_id":${boxplotChartId}}`;
-    cy.route('POST', boxplotRequest).as('boxplotRequest');
+    cy.route('GET', boxplotRequest).as('boxplotRequest');
     cy.wait('@boxplotRequest');
     cy.get('.grid-container .box_plot').should('be.exist');
 
diff --git a/superset/assets/cypress/integration/explore/control.test.js b/superset/assets/cypress/integration/explore/control.test.js
index ba4636c..711ab78 100644
--- a/superset/assets/cypress/integration/explore/control.test.js
+++ b/superset/assets/cypress/integration/explore/control.test.js
@@ -26,7 +26,8 @@ describe('Groupby', () => {
     cy.server();
     cy.login();
 
-    cy.route('POST', '/superset/explore_json/**').as('getJson');
+    cy.route('GET', '/superset/explore_json/**').as('getJson');
+    cy.route('POST', '/superset/explore_json/**').as('postJson');
     cy.visitChartByName('Num Births Trend');
     cy.verifySliceSuccess({ waitAlias: '@getJson' });
 
@@ -36,7 +37,7 @@ describe('Groupby', () => {
       cy.get('.VirtualizedSelectFocusedOption').click();
     });
     cy.get('button.query').click();
-    cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
+    cy.verifySliceSuccess({ waitAlias: '@postJson', chartSelector: 'svg' });
   });
 });
 
@@ -44,7 +45,8 @@ describe('AdhocMetrics', () => {
   beforeEach(() => {
     cy.login();
     cy.server();
-    cy.route('POST', '/superset/explore_json/**').as('getJson');
+    cy.route('GET', '/superset/explore_json/**').as('getJson');
+    cy.route('POST', '/superset/explore_json/**').as('postJson');
   });
 
   it('Clear metric and set simple adhoc metric', () => {
@@ -74,7 +76,7 @@ describe('AdhocMetrics', () => {
 
     cy.get('button.query').click();
     cy.verifySliceSuccess({
-      waitAlias: '@getJson',
+      waitAlias: '@postJson',
       querySubstring: metricName,
       chartSelector: 'svg',
     });
@@ -105,7 +107,7 @@ describe('AdhocMetrics', () => {
 
     cy.get('button.query').click();
     cy.verifySliceSuccess({
-      waitAlias: '@getJson',
+      waitAlias: '@postJson',
       querySubstring: metric,
       chartSelector: 'svg',
     });
@@ -137,7 +139,7 @@ describe('AdhocMetrics', () => {
 
     cy.get('button.query').click();
     cy.verifySliceSuccess({
-      waitAlias: '@getJson',
+      waitAlias: '@postJson',
       chartSelector: 'svg',
     });
   });
@@ -147,7 +149,8 @@ describe('AdhocFilters', () => {
   beforeEach(() => {
     cy.login();
     cy.server();
-    cy.route('POST', '/superset/explore_json/**').as('getJson');
+    cy.route('GET', '/superset/explore_json/**').as('getJson');
+    cy.route('POST', '/superset/explore_json/**').as('postJson');
   });
 
   it('Set simple adhoc filter', () => {
@@ -177,7 +180,7 @@ describe('AdhocFilters', () => {
 
     cy.get('button.query').click();
     cy.verifySliceSuccess({
-      waitAlias: '@getJson',
+      waitAlias: '@postJson',
       chartSelector: 'svg',
     });
   });
@@ -206,7 +209,7 @@ describe('AdhocFilters', () => {
 
     cy.get('button.query').click();
     cy.verifySliceSuccess({
-      waitAlias: '@getJson',
+      waitAlias: '@postJson',
       chartSelector: 'svg',
     });
   });
@@ -217,7 +220,8 @@ describe('Advanced analytics', () => {
   beforeEach(() => {
     cy.login();
     cy.server();
-    cy.route('POST', '/superset/explore_json/**').as('getJson');
+    cy.route('GET', '/superset/explore_json/**').as('getJson');
+    cy.route('POST', '/superset/explore_json/**').as('postJson');
   });
 
   it('Create custom time compare', () => {
@@ -240,7 +244,7 @@ describe('Advanced analytics', () => {
     });
 
     cy.get('button.query').click();
-    cy.wait('@getJson');
+    cy.wait('@postJson');
     cy.reload();
     cy.verifySliceSuccess({
       waitAlias: '@getJson',
@@ -257,7 +261,8 @@ describe('Annotations', () => {
   beforeEach(() => {
     cy.login();
     cy.server();
-    cy.route('POST', '/superset/explore_json/**').as('getJson');
+    cy.route('GET', '/superset/explore_json/**').as('getJson');
+    cy.route('POST', '/superset/explore_json/**').as('postJson');
   });
 
   it('Create formula annotation y-axis goal line', () => {
@@ -280,7 +285,7 @@ describe('Annotations', () => {
 
     cy.get('button.query').click();
     cy.verifySliceSuccess({
-      waitAlias: '@getJson',
+      waitAlias: '@postJson',
       chartSelector: 'svg',
     });
 
@@ -292,7 +297,8 @@ describe('Time range filter', () => {
   beforeEach(() => {
     cy.login();
     cy.server();
-    cy.route('POST', '/superset/explore_json/**').as('getJson');
+    cy.route('GET', '/superset/explore_json/**').as('getJson');
+    cy.route('POST', '/superset/explore_json/**').as('postJson');
   });
 
   it('Defaults to the correct tab for time_range params', () => {
@@ -304,7 +310,7 @@ describe('Time range filter', () => {
     };
 
     cy.visitChartByParams(JSON.stringify(formData));
-    cy.verifySliceSuccess({ waitAlias: '@getJson' });
+    cy.verifySliceSuccess({ waitAlias: '@postJson' });
 
     cy.get('[data-test=time_range]').within(() => {
       cy.get('span.label').click();
diff --git a/superset/assets/cypress/integration/explore/link.test.js b/superset/assets/cypress/integration/explore/link.test.js
index 024612f..36b56ce 100644
--- a/superset/assets/cypress/integration/explore/link.test.js
+++ b/superset/assets/cypress/integration/explore/link.test.js
@@ -26,7 +26,8 @@ describe('Test explore links', () => {
   beforeEach(() => {
     cy.login();
     cy.server();
-    cy.route('POST', '/superset/explore_json/**').as('getJson');
+    cy.route('GET', '/superset/explore_json/**').as('getJson');
+    cy.route('POST', '/superset/explore_json/**').as('postJson');
   });
 
   it('Open and close view query modal', () => {
@@ -35,7 +36,7 @@ describe('Test explore links', () => {
 
     cy.get('button#query').click();
     cy.get('span').contains('View query').parent().click();
-    cy.wait('@getJson').then(() => {
+    cy.wait('@postJson').then(() => {
       cy.get('code');
     });
     cy.get('.modal-header').within(() => {
@@ -83,7 +84,7 @@ describe('Test explore links', () => {
     const newChartName = 'Test chart';
 
     cy.visitChartByParams(JSON.stringify(formData));
-    cy.verifySliceSuccess({ waitAlias: '@getJson' });
+    cy.verifySliceSuccess({ waitAlias: '@postJson' });
     cy.url().then((url) => {
       cy.get('button[data-target="#save_modal"]').click();
       cy.get('.modal-content').within(() => {
@@ -109,7 +110,7 @@ describe('Test explore links', () => {
     cy.get('.modal-content').within(() => {
       cy.get('button#btn_modal_save').click();
     });
-    cy.verifySliceSuccess({ waitAlias: '@getJson' });
+    cy.verifySliceSuccess({ waitAlias: '@postJson' });
     cy.request(`/chart/api/read?_flt_3_slice_name=${chartName}`).then((response) => {
       cy.request('DELETE', `/chart/api/delete/${response.body.pks[0]}`);
     });
diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json
index 35b3c3a..8eeca9b 100644
--- a/superset/assets/package-lock.json
+++ b/superset/assets/package-lock.json
@@ -1368,7 +1368,7 @@
     },
     "@data-ui/event-flow": {
       "version": "0.0.54",
-      "resolved": "https://registry.npmjs.org/@data-ui/event-flow/-/event-flow-0.0.54.tgz",
+      "resolved": "http://registry.npmjs.org/@data-ui/event-flow/-/event-flow-0.0.54.tgz",
       "integrity": "sha1-uwPh/StWNCSGVbjfnTxsOKdH5l4=",
       "requires": {
         "@data-ui/forms": "0.0.50",
@@ -1659,7 +1659,7 @@
     },
     "@data-ui/radial-chart": {
       "version": "0.0.54",
-      "resolved": "https://registry.npmjs.org/@data-ui/radial-chart/-/radial-chart-0.0.54.tgz",
+      "resolved": "http://registry.npmjs.org/@data-ui/radial-chart/-/radial-chart-0.0.54.tgz",
       "integrity": "sha1-DSiwdoHZtgJ9msI7cpJBgn1RMAE=",
       "requires": {
         "@data-ui/shared": "0.0.54",
@@ -1674,7 +1674,7 @@
     },
     "@data-ui/shared": {
       "version": "0.0.54",
-      "resolved": "https://registry.npmjs.org/@data-ui/shared/-/shared-0.0.54.tgz",
+      "resolved": "http://registry.npmjs.org/@data-ui/shared/-/shared-0.0.54.tgz",
       "integrity": "sha1-L7DW3ukNrCC/jzwpE8aFCoIj1Zs=",
       "requires": {
         "@data-ui/theme": "0.0.48",
@@ -1747,7 +1747,7 @@
     },
     "@data-ui/sparkline": {
       "version": "0.0.54",
-      "resolved": "https://registry.npmjs.org/@data-ui/sparkline/-/sparkline-0.0.54.tgz",
+      "resolved": "http://registry.npmjs.org/@data-ui/sparkline/-/sparkline-0.0.54.tgz",
       "integrity": "sha1-zj0WbZ4LI5oLoC84lMuejIQXHO8=",
       "requires": {
         "@data-ui/shared": "0.0.54",
@@ -2201,7 +2201,7 @@
     },
     "@sinonjs/formatio": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz",
+      "resolved": "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz",
       "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==",
       "dev": true,
       "requires": {
@@ -3307,7 +3307,7 @@
     },
     "acorn-jsx": {
       "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
+      "resolved": "http://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
       "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
       "dev": true,
       "requires": {
@@ -3316,7 +3316,7 @@
       "dependencies": {
         "acorn": {
           "version": "3.3.0",
-          "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
+          "resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
           "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
           "dev": true
         }
@@ -3751,7 +3751,7 @@
     },
     "array-equal": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
+      "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
       "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
       "dev": true
     },
@@ -3874,7 +3874,7 @@
         },
         "util": {
           "version": "0.10.3",
-          "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+          "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz",
           "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
           "dev": true,
           "requires": {
@@ -4364,7 +4364,7 @@
     },
     "babel-plugin-syntax-dynamic-import": {
       "version": "6.18.0",
-      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz",
+      "resolved": "http://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz",
       "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=",
       "dev": true
     },
@@ -4648,7 +4648,7 @@
     },
     "brace": {
       "version": "0.11.1",
-      "resolved": "https://registry.npmjs.org/brace/-/brace-0.11.1.tgz",
+      "resolved": "http://registry.npmjs.org/brace/-/brace-0.11.1.tgz",
       "integrity": "sha1-SJb8ydVE7vRfS7dmDbMg07N5/lg="
     },
     "brace-expansion": {
@@ -4680,7 +4680,7 @@
     },
     "brfs": {
       "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz",
+      "resolved": "http://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz",
       "integrity": "sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==",
       "requires": {
         "quote-stream": "^1.0.1",
@@ -4712,7 +4712,7 @@
       "dependencies": {
         "resolve": {
           "version": "1.1.7",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+          "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
           "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
           "dev": true
         }
@@ -4720,7 +4720,7 @@
     },
     "browserify-aes": {
       "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+      "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
       "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
       "dev": true,
       "requires": {
@@ -4757,7 +4757,7 @@
     },
     "browserify-rsa": {
       "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+      "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
       "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
       "dev": true,
       "requires": {
@@ -4809,7 +4809,7 @@
     },
     "buffer": {
       "version": "4.9.1",
-      "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
+      "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
       "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
       "dev": true,
       "requires": {
@@ -4942,7 +4942,7 @@
       "dependencies": {
         "callsites": {
           "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
+          "resolved": "http://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
           "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
           "dev": true
         }
@@ -5261,7 +5261,7 @@
     },
     "clean-webpack-plugin": {
       "version": "0.1.19",
-      "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-0.1.19.tgz",
+      "resolved": "http://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-0.1.19.tgz",
       "integrity": "sha512-M1Li5yLHECcN2MahoreuODul5LkjohJGFxLPTjl3j1ttKrF5rgjZET1SJduuqxLAuT1gAPOdkhg03qcaaU1KeA==",
       "dev": true,
       "requires": {
@@ -5672,7 +5672,7 @@
     },
     "create-hash": {
       "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+      "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
       "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
       "dev": true,
       "requires": {
@@ -5685,7 +5685,7 @@
     },
     "create-hmac": {
       "version": "1.1.7",
-      "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+      "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
       "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
       "dev": true,
       "requires": {
@@ -5746,7 +5746,7 @@
     },
     "css-color-names": {
       "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
+      "resolved": "http://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
       "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=",
       "dev": true
     },
@@ -5810,7 +5810,7 @@
     },
     "css-in-js-utils": {
       "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz",
+      "resolved": "http://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz",
       "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==",
       "requires": {
         "hyphenate-style-name": "^1.0.2",
@@ -5870,7 +5870,7 @@
     },
     "css-select": {
       "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
+      "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
       "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
       "dev": true,
       "requires": {
@@ -6258,7 +6258,7 @@
     },
     "d3-geo-projection": {
       "version": "0.2.16",
-      "resolved": "https://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-0.2.16.tgz",
+      "resolved": "http://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-0.2.16.tgz",
       "integrity": "sha1-SZTs0QM92xUztsTFUoocgdzClCc=",
       "requires": {
         "brfs": "^1.3.0"
@@ -6676,7 +6676,7 @@
     },
     "diffie-hellman": {
       "version": "5.0.3",
-      "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+      "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
       "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
       "dev": true,
       "requires": {
@@ -6706,7 +6706,7 @@
     },
     "dnd-core": {
       "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-2.6.0.tgz",
+      "resolved": "http://registry.npmjs.org/dnd-core/-/dnd-core-2.6.0.tgz",
       "integrity": "sha1-ErrWbVh0LG5ffPKUP7aFlED4CcQ=",
       "requires": {
         "asap": "^2.0.6",
@@ -6836,7 +6836,7 @@
     },
     "duplexer": {
       "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
+      "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
       "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
       "dev": true
     },
@@ -7078,7 +7078,7 @@
     },
     "es6-promise": {
       "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
+      "resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
       "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM="
     },
     "es6bindall": {
@@ -7125,7 +7125,7 @@
     },
     "eslint": {
       "version": "4.19.1",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz",
+      "resolved": "http://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz",
       "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==",
       "dev": true,
       "requires": {
@@ -7229,7 +7229,7 @@
         },
         "fast-deep-equal": {
           "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
+          "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
           "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
           "dev": true
         },
@@ -7403,7 +7403,7 @@
       "dependencies": {
         "doctrine": {
           "version": "1.5.0",
-          "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
+          "resolved": "http://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
           "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
           "dev": true,
           "requires": {
@@ -7413,7 +7413,7 @@
         },
         "load-json-file": {
           "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+          "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
           "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
           "dev": true,
           "requires": {
@@ -7551,7 +7551,7 @@
     },
     "espree": {
       "version": "3.5.4",
-      "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
+      "resolved": "http://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
       "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
       "dev": true,
       "requires": {
@@ -7731,7 +7731,7 @@
       "dependencies": {
         "source-map": {
           "version": "0.5.0",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz",
+          "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz",
           "integrity": "sha1-D+llA6yGpa213mP05BKuSHLNvoY=",
           "dev": true
         }
@@ -7777,7 +7777,7 @@
       "dependencies": {
         "array-flatten": {
           "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+          "resolved": "http://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
           "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
           "dev": true
         },
@@ -7805,7 +7805,7 @@
     },
     "external-editor": {
       "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
+      "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
       "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
       "dev": true,
       "requires": {
@@ -8027,7 +8027,7 @@
     },
     "file-loader": {
       "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz",
+      "resolved": "http://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz",
       "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==",
       "dev": true,
       "requires": {
@@ -8037,7 +8037,7 @@
     },
     "file-type": {
       "version": "3.9.0",
-      "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
+      "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
       "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek="
     },
     "fileset": {
@@ -8070,7 +8070,7 @@
     },
     "finalhandler": {
       "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
+      "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
       "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
       "dev": true,
       "requires": {
@@ -8676,27 +8676,27 @@
       "dependencies": {
         "abbrev": {
           "version": "1.1.1",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
           "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
           "dev": true,
           "optional": true
         },
         "ansi-regex": {
           "version": "2.1.1",
-          "resolved": false,
-          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
-          "dev": true
+          "bundled": true,
+          "dev": true,
+          "optional": true
         },
         "aproba": {
           "version": "1.2.0",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
           "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
           "dev": true,
           "optional": true
         },
         "are-we-there-yet": {
           "version": "1.1.4",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
           "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
           "dev": true,
           "optional": true,
@@ -8707,15 +8707,15 @@
         },
         "balanced-match": {
           "version": "1.0.0",
-          "resolved": false,
-          "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
-          "dev": true
+          "bundled": true,
+          "dev": true,
+          "optional": true
         },
         "brace-expansion": {
           "version": "1.1.11",
-          "resolved": false,
-          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+          "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "balanced-match": "^1.0.0",
             "concat-map": "0.0.1"
@@ -8723,39 +8723,39 @@
         },
         "chownr": {
           "version": "1.0.1",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz",
           "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=",
           "dev": true,
           "optional": true
         },
         "code-point-at": {
           "version": "1.1.0",
-          "resolved": false,
-          "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
-          "dev": true
+          "bundled": true,
+          "dev": true,
+          "optional": true
         },
         "concat-map": {
           "version": "0.0.1",
-          "resolved": false,
-          "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-          "dev": true
+          "bundled": true,
+          "dev": true,
+          "optional": true
         },
         "console-control-strings": {
           "version": "1.1.0",
-          "resolved": false,
-          "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
-          "dev": true
+          "bundled": true,
+          "dev": true,
+          "optional": true
         },
         "core-util-is": {
           "version": "1.0.2",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
           "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
           "dev": true,
           "optional": true
         },
         "debug": {
           "version": "2.6.9",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
           "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
           "dev": true,
           "optional": true,
@@ -8765,28 +8765,28 @@
         },
         "deep-extend": {
           "version": "0.5.1",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz",
           "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==",
           "dev": true,
           "optional": true
         },
         "delegates": {
           "version": "1.0.0",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
           "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
           "dev": true,
           "optional": true
         },
         "detect-libc": {
           "version": "1.0.3",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
           "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
           "dev": true,
           "optional": true
         },
         "fs-minipass": {
           "version": "1.2.5",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz",
           "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
           "dev": true,
           "optional": true,
@@ -8796,14 +8796,14 @@
         },
         "fs.realpath": {
           "version": "1.0.0",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
           "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
           "dev": true,
           "optional": true
         },
         "gauge": {
           "version": "2.7.4",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
           "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
           "dev": true,
           "optional": true,
@@ -8820,7 +8820,7 @@
         },
         "glob": {
           "version": "7.1.2",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
           "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
           "dev": true,
           "optional": true,
@@ -8835,14 +8835,14 @@
         },
         "has-unicode": {
           "version": "2.0.1",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
           "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
           "dev": true,
           "optional": true
         },
         "iconv-lite": {
           "version": "0.4.21",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz",
           "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==",
           "dev": true,
           "optional": true,
@@ -8852,7 +8852,7 @@
         },
         "ignore-walk": {
           "version": "3.0.1",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz",
           "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
           "dev": true,
           "optional": true,
@@ -8862,7 +8862,7 @@
         },
         "inflight": {
           "version": "1.0.6",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
           "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
           "dev": true,
           "optional": true,
@@ -8873,53 +8873,53 @@
         },
         "inherits": {
           "version": "2.0.3",
-          "resolved": false,
-          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
-          "dev": true
+          "bundled": true,
+          "dev": true,
+          "optional": true
         },
         "ini": {
           "version": "1.3.5",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
           "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
           "dev": true,
           "optional": true
         },
         "is-fullwidth-code-point": {
           "version": "1.0.0",
-          "resolved": false,
-          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+          "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "number-is-nan": "^1.0.0"
           }
         },
         "isarray": {
           "version": "1.0.0",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
           "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
           "dev": true,
           "optional": true
         },
         "minimatch": {
           "version": "3.0.4",
-          "resolved": false,
-          "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+          "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "brace-expansion": "^1.1.7"
           }
         },
         "minimist": {
           "version": "0.0.8",
-          "resolved": false,
-          "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
-          "dev": true
+          "bundled": true,
+          "dev": true,
+          "optional": true
         },
         "minipass": {
           "version": "2.2.4",
-          "resolved": false,
-          "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==",
+          "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "safe-buffer": "^5.1.1",
             "yallist": "^3.0.0"
@@ -8927,7 +8927,7 @@
         },
         "minizlib": {
           "version": "1.1.0",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz",
           "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==",
           "dev": true,
           "optional": true,
@@ -8937,23 +8937,23 @@
         },
         "mkdirp": {
           "version": "0.5.1",
-          "resolved": false,
-          "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+          "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "minimist": "0.0.8"
           }
         },
         "ms": {
           "version": "2.0.0",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
           "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
           "dev": true,
           "optional": true
         },
         "needle": {
           "version": "2.2.0",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.0.tgz",
           "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==",
           "dev": true,
           "optional": true,
@@ -8965,7 +8965,7 @@
         },
         "node-pre-gyp": {
           "version": "0.10.0",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.0.tgz",
           "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==",
           "dev": true,
           "optional": true,
@@ -8984,7 +8984,7 @@
         },
         "nopt": {
           "version": "4.0.1",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
           "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
           "dev": true,
           "optional": true,
@@ -8995,14 +8995,14 @@
         },
         "npm-bundled": {
           "version": "1.0.3",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.3.tgz",
           "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==",
           "dev": true,
           "optional": true
         },
         "npm-packlist": {
           "version": "1.1.10",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.10.tgz",
           "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==",
           "dev": true,
           "optional": true,
@@ -9013,7 +9013,7 @@
         },
         "npmlog": {
           "version": "4.1.2",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
           "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
           "dev": true,
           "optional": true,
@@ -9026,43 +9026,43 @@
         },
         "number-is-nan": {
           "version": "1.0.1",
-          "resolved": false,
-          "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
-          "dev": true
+          "bundled": true,
+          "dev": true,
+          "optional": true
         },
         "object-assign": {
           "version": "4.1.1",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
           "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
           "dev": true,
           "optional": true
         },
         "once": {
           "version": "1.4.0",
-          "resolved": false,
-          "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+          "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "wrappy": "1"
           }
         },
         "os-homedir": {
           "version": "1.0.2",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
           "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
           "dev": true,
           "optional": true
         },
         "os-tmpdir": {
           "version": "1.0.2",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
           "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
           "dev": true,
           "optional": true
         },
         "osenv": {
           "version": "0.1.5",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
           "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
           "dev": true,
           "optional": true,
@@ -9073,21 +9073,21 @@
         },
         "path-is-absolute": {
           "version": "1.0.1",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
           "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
           "dev": true,
           "optional": true
         },
         "process-nextick-args": {
           "version": "2.0.0",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
           "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
           "dev": true,
           "optional": true
         },
         "rc": {
           "version": "1.2.7",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.7.tgz",
           "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==",
           "dev": true,
           "optional": true,
@@ -9100,7 +9100,7 @@
           "dependencies": {
             "minimist": {
               "version": "1.2.0",
-              "resolved": false,
+              "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
               "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
               "dev": true,
               "optional": true
@@ -9109,7 +9109,7 @@
         },
         "readable-stream": {
           "version": "2.3.6",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
           "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
           "dev": true,
           "optional": true,
@@ -9125,7 +9125,7 @@
         },
         "rimraf": {
           "version": "2.6.2",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
           "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
           "dev": true,
           "optional": true,
@@ -9135,50 +9135,50 @@
         },
         "safe-buffer": {
           "version": "5.1.1",
-          "resolved": false,
-          "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
-          "dev": true
+          "bundled": true,
+          "dev": true,
+          "optional": true
         },
         "safer-buffer": {
           "version": "2.1.2",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
           "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
           "dev": true,
           "optional": true
         },
         "sax": {
           "version": "1.2.4",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
           "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
           "dev": true,
           "optional": true
         },
         "semver": {
           "version": "5.5.0",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
           "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
           "dev": true,
           "optional": true
         },
         "set-blocking": {
           "version": "2.0.0",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
           "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
           "dev": true,
           "optional": true
         },
         "signal-exit": {
           "version": "3.0.2",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
           "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
           "dev": true,
           "optional": true
         },
         "string-width": {
           "version": "1.0.2",
-          "resolved": false,
-          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+          "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "code-point-at": "^1.0.0",
             "is-fullwidth-code-point": "^1.0.0",
@@ -9187,7 +9187,7 @@
         },
         "string_decoder": {
           "version": "1.1.1",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
           "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
           "dev": true,
           "optional": true,
@@ -9197,23 +9197,23 @@
         },
         "strip-ansi": {
           "version": "3.0.1",
-          "resolved": false,
-          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+          "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "ansi-regex": "^2.0.0"
           }
         },
         "strip-json-comments": {
           "version": "2.0.1",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
           "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
           "dev": true,
           "optional": true
         },
         "tar": {
           "version": "4.4.1",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.1.tgz",
           "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==",
           "dev": true,
           "optional": true,
@@ -9229,14 +9229,14 @@
         },
         "util-deprecate": {
           "version": "1.0.2",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
           "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
           "dev": true,
           "optional": true
         },
         "wide-align": {
           "version": "1.1.2",
-          "resolved": false,
+          "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
           "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
           "dev": true,
           "optional": true,
@@ -9246,15 +9246,15 @@
         },
         "wrappy": {
           "version": "1.0.2",
-          "resolved": false,
-          "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-          "dev": true
+          "bundled": true,
+          "dev": true,
+          "optional": true
         },
         "yallist": {
           "version": "3.0.2",
-          "resolved": false,
-          "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=",
-          "dev": true
+          "bundled": true,
+          "dev": true,
+          "optional": true
         }
       }
     },
@@ -9363,7 +9363,7 @@
     },
     "gettext-parser": {
       "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-1.1.0.tgz",
+      "resolved": "http://registry.npmjs.org/gettext-parser/-/gettext-parser-1.1.0.tgz",
       "integrity": "sha1-LFpmONiTk0ubVQN9CtgstwBLJnk=",
       "dev": true,
       "requires": {
@@ -9455,7 +9455,7 @@
     },
     "globby": {
       "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+      "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
       "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
       "dev": true,
       "requires": {
@@ -9544,7 +9544,7 @@
         },
         "minimist": {
           "version": "0.0.10",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+          "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
           "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
           "dev": true
         },
@@ -9732,7 +9732,7 @@
     },
     "hoist-non-react-statics": {
       "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
+      "resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
       "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs="
     },
     "homedir-polyfill": {
@@ -9836,7 +9836,7 @@
     },
     "http-errors": {
       "version": "1.6.3",
-      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+      "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
       "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
       "dev": true,
       "requires": {
@@ -10584,7 +10584,7 @@
     },
     "is-accessor-descriptor": {
       "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+      "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
       "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
       "dev": true,
       "requires": {
@@ -10672,7 +10672,7 @@
     },
     "is-data-descriptor": {
       "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+      "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
       "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
       "dev": true,
       "requires": {
@@ -10777,7 +10777,7 @@
     },
     "is-obj": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+      "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
       "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
       "dev": true
     },
@@ -12643,7 +12643,7 @@
     },
     "magic-string": {
       "version": "0.22.5",
-      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz",
+      "resolved": "http://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz",
       "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==",
       "requires": {
         "vlq": "^0.2.2"
@@ -12766,7 +12766,7 @@
         },
         "minimist": {
           "version": "0.0.8",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+          "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
           "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
         },
         "quickselect": {
@@ -12824,7 +12824,7 @@
     },
     "mathjs": {
       "version": "3.20.2",
-      "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-3.20.2.tgz",
+      "resolved": "http://registry.npmjs.org/mathjs/-/mathjs-3.20.2.tgz",
       "integrity": "sha512-3f6/+uf1cUtIz1rYFz775wekl/UEDSQ3mU6xdxW7qzpvvhc2v28i3UtLsGTRB+u8OqDWoSX6Dz8gehaGFs6tCA==",
       "requires": {
         "complex.js": "2.0.4",
@@ -12874,7 +12874,7 @@
     },
     "media-typer": {
       "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
       "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
       "dev": true
     },
@@ -13074,7 +13074,7 @@
     },
     "minimist": {
       "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+      "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
       "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
     },
     "mississippi": {
@@ -13126,7 +13126,7 @@
     },
     "mkdirp": {
       "version": "0.5.1",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+      "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
       "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
       "requires": {
         "minimist": "0.0.8"
@@ -13134,7 +13134,7 @@
       "dependencies": {
         "minimist": {
           "version": "0.0.8",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+          "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
           "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
         }
       }
@@ -13801,7 +13801,7 @@
     },
     "os-tmpdir": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+      "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
       "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
       "dev": true
     },
@@ -13988,7 +13988,7 @@
     },
     "path-browserify": {
       "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
+      "resolved": "http://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
       "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=",
       "dev": true
     },
@@ -14006,7 +14006,7 @@
     },
     "path-is-absolute": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
       "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
     },
     "path-is-inside": {
@@ -14147,7 +14147,7 @@
     },
     "po2json": {
       "version": "0.4.5",
-      "resolved": "https://registry.npmjs.org/po2json/-/po2json-0.4.5.tgz",
+      "resolved": "http://registry.npmjs.org/po2json/-/po2json-0.4.5.tgz",
       "integrity": "sha1-R7spUtoy1Yob4vJWpZjuvAt0URg=",
       "dev": true,
       "requires": {
@@ -14209,7 +14209,7 @@
       "dependencies": {
         "async": {
           "version": "1.5.2",
-          "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+          "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz",
           "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
           "dev": true
         }
@@ -16377,7 +16377,7 @@
     },
     "react-ace": {
       "version": "5.10.0",
-      "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-5.10.0.tgz",
+      "resolved": "http://registry.npmjs.org/react-ace/-/react-ace-5.10.0.tgz",
       "integrity": "sha512-aEK/XZCowP8IXq91e2DYqOtGhabk1bbjt+fyeW0UBcIkzDzP/RX/MeJKeyW7wsZcwElACVwyy9nnwXBTqgky3A==",
       "requires": {
         "brace": "^0.11.0",
@@ -16456,7 +16456,7 @@
     },
     "react-dnd": {
       "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-2.6.0.tgz",
+      "resolved": "http://registry.npmjs.org/react-dnd/-/react-dnd-2.6.0.tgz",
       "integrity": "sha1-f6JWds+CfViokSk+PBq1naACVFo=",
       "requires": {
         "disposables": "^1.0.1",
@@ -16476,7 +16476,7 @@
     },
     "react-dnd-html5-backend": {
       "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-2.6.0.tgz",
+      "resolved": "http://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-2.6.0.tgz",
       "integrity": "sha1-WQzRzKeEQbsnTt1XH+9MCxbdz44=",
       "requires": {
         "lodash": "^4.2.0"
@@ -16924,7 +16924,7 @@
     },
     "readable-stream": {
       "version": "2.3.6",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+      "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
       "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
       "requires": {
         "core-util-is": "~1.0.0",
@@ -17275,7 +17275,7 @@
     },
     "reduce-css-calc": {
       "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz",
+      "resolved": "http://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz",
       "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=",
       "requires": {
         "balanced-match": "^0.4.2",
@@ -17398,7 +17398,7 @@
     },
     "regexpp": {
       "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz",
+      "resolved": "http://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz",
       "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==",
       "dev": true
     },
@@ -17430,7 +17430,7 @@
       "dependencies": {
         "jsesc": {
           "version": "0.5.0",
-          "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+          "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
           "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
           "dev": true
         }
@@ -17546,7 +17546,7 @@
     },
     "require-uncached": {
       "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
+      "resolved": "http://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
       "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
       "dev": true,
       "requires": {
@@ -17629,7 +17629,7 @@
     },
     "rgba-regex": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz",
+      "resolved": "http://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz",
       "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=",
       "dev": true
     },
@@ -17713,7 +17713,7 @@
     },
     "safe-regex": {
       "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+      "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
       "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
       "dev": true,
       "requires": {
@@ -17891,7 +17891,7 @@
     },
     "sha.js": {
       "version": "2.4.11",
-      "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+      "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
       "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
       "dev": true,
       "requires": {
@@ -17926,7 +17926,7 @@
         },
         "iconv-lite": {
           "version": "0.2.11",
-          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz",
+          "resolved": "http://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz",
           "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg="
         }
       }
@@ -17946,7 +17946,7 @@
       "dependencies": {
         "minimist": {
           "version": "0.0.5",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz",
+          "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz",
           "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY="
         }
       }
@@ -18005,7 +18005,7 @@
     },
     "sinon": {
       "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz",
+      "resolved": "http://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz",
       "integrity": "sha512-trdx+mB0VBBgoYucy6a9L7/jfQOmvGeaKZT4OOJ+lPAtI8623xyGr8wLiE4eojzBS8G9yXbhx42GHUOVLr4X2w==",
       "dev": true,
       "requires": {
@@ -18397,7 +18397,7 @@
     },
     "split": {
       "version": "0.2.10",
-      "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz",
+      "resolved": "http://registry.npmjs.org/split/-/split-0.2.10.tgz",
       "integrity": "sha1-Zwl8YB1pfOE2j0GPBs0gHPBSGlc=",
       "requires": {
         "through": "2"
@@ -18440,7 +18440,7 @@
     },
     "sprintf-js": {
       "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
       "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
       "dev": true
     },
@@ -18692,7 +18692,7 @@
     },
     "string_decoder": {
       "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
       "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
       "requires": {
         "safe-buffer": "~5.1.0"
@@ -18700,7 +18700,7 @@
     },
     "strip-ansi": {
       "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+      "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
       "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
       "dev": true,
       "requires": {
@@ -18715,7 +18715,7 @@
     },
     "strip-eof": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+      "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
       "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
       "dev": true
     },
@@ -18960,7 +18960,7 @@
         },
         "fast-deep-equal": {
           "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
+          "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
           "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
           "dev": true
         },
@@ -19017,7 +19017,7 @@
     },
     "tapable": {
       "version": "0.1.10",
-      "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz",
+      "resolved": "http://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz",
       "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=",
       "dev": true
     },
@@ -19205,7 +19205,7 @@
     },
     "through": {
       "version": "2.3.8",
-      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
       "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
     },
     "through2": {
@@ -19901,7 +19901,7 @@
     },
     "tty-browserify": {
       "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+      "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
       "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
       "dev": true
     },
@@ -20434,7 +20434,7 @@
     },
     "vm-browserify": {
       "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
+      "resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
       "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=",
       "dev": true,
       "requires": {
@@ -22016,7 +22016,7 @@
     },
     "wrap-ansi": {
       "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+      "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
       "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
       "dev": true,
       "requires": {
@@ -22101,7 +22101,7 @@
     },
     "xmlbuilder": {
       "version": "9.0.7",
-      "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
+      "resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
       "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
     },
     "xregexp": {
diff --git a/superset/assets/spec/javascripts/chart/chartActions_spec.js b/superset/assets/spec/javascripts/chart/chartActions_spec.js
index 09f618c..a4a8326 100644
--- a/superset/assets/spec/javascripts/chart/chartActions_spec.js
+++ b/superset/assets/spec/javascripts/chart/chartActions_spec.js
@@ -51,7 +51,7 @@ describe('chart actions', () => {
   });
 
   it('should dispatch CHART_UPDATE_STARTED action before the query', () => {
-    const actionThunk = actions.runQuery({});
+    const actionThunk = actions.postChartFormData({});
 
     return actionThunk(dispatch).then(() => {
       // chart update, trigger query, update form data, success
@@ -64,7 +64,7 @@ describe('chart actions', () => {
   });
 
   it('should dispatch TRIGGER_QUERY action with the query', () => {
-    const actionThunk = actions.runQuery({});
+    const actionThunk = actions.postChartFormData({});
     return actionThunk(dispatch).then(() => {
       // chart update, trigger query, update form data, success
       expect(dispatch.callCount).toBe(5);
@@ -76,7 +76,7 @@ describe('chart actions', () => {
   });
 
   it('should dispatch UPDATE_QUERY_FORM_DATA action with the query', () => {
-    const actionThunk = actions.runQuery({});
+    const actionThunk = actions.postChartFormData({});
     return actionThunk(dispatch).then(() => {
       // chart update, trigger query, update form data, success
       expect(dispatch.callCount).toBe(5);
@@ -88,7 +88,7 @@ describe('chart actions', () => {
   });
 
   it('should dispatch logEvent async action', () => {
-    const actionThunk = actions.runQuery({});
+    const actionThunk = actions.postChartFormData({});
     return actionThunk(dispatch).then(() => {
       // chart update, trigger query, update form data, success
       expect(dispatch.callCount).toBe(5);
@@ -104,7 +104,7 @@ describe('chart actions', () => {
   });
 
   it('should dispatch CHART_UPDATE_SUCCEEDED action upon success', () => {
-    const actionThunk = actions.runQuery({});
+    const actionThunk = actions.postChartFormData({});
     return actionThunk(dispatch).then(() => {
       // chart update, trigger query, update form data, success
       expect(dispatch.callCount).toBe(5);
@@ -120,7 +120,7 @@ describe('chart actions', () => {
     fetchMock.post(MOCK_URL, () => unresolvingPromise, { overwriteRoutes: true });
 
     const timeoutInSec = 1 / 1000;
-    const actionThunk = actions.runQuery({}, false, timeoutInSec);
+    const actionThunk = actions.postChartFormData({}, false, timeoutInSec);
 
     return actionThunk(dispatch).then(() => {
       // chart update, trigger query, update form data, fail
@@ -136,7 +136,7 @@ describe('chart actions', () => {
     fetchMock.post(MOCK_URL, { throws: { statusText: 'misc error' } }, { overwriteRoutes: true });
 
     const timeoutInSec = 1 / 1000;
-    const actionThunk = actions.runQuery({}, false, timeoutInSec);
+    const actionThunk = actions.postChartFormData({}, false, timeoutInSec);
 
     return actionThunk(dispatch).then(() => {
       // chart update, trigger query, update form data, fail
diff --git a/superset/assets/spec/javascripts/dashboard/components/Dashboard_spec.jsx b/superset/assets/spec/javascripts/dashboard/components/Dashboard_spec.jsx
index 523b835..de637cd 100644
--- a/superset/assets/spec/javascripts/dashboard/components/Dashboard_spec.jsx
+++ b/superset/assets/spec/javascripts/dashboard/components/Dashboard_spec.jsx
@@ -39,7 +39,7 @@ describe('Dashboard', () => {
     actions: {
       addSliceToDashboard() {},
       removeSliceFromDashboard() {},
-      runQuery() {},
+      postChartFormData() {},
       logEvent() {},
     },
     initMessages: [],
@@ -82,15 +82,15 @@ describe('Dashboard', () => {
       },
     };
 
-    it('should call runQuery for all non-exempt slices', () => {
+    it('should call postChartFormData for all non-exempt slices', () => {
       const wrapper = setup({ charts: overrideCharts, slices: overrideSlices });
-      const spy = sinon.spy(props.actions, 'runQuery');
+      const spy = sinon.spy(props.actions, 'postChartFormData');
       wrapper.instance().refreshExcept('1001');
       spy.restore();
       expect(spy.callCount).toBe(Object.keys(overrideCharts).length - 1);
     });
 
-    it('should not call runQuery for filter_immune_slices', () => {
+    it('should not call postChartFormData for filter_immune_slices', () => {
       const wrapper = setup({
         charts: overrideCharts,
         dashboardInfo: {
@@ -103,7 +103,7 @@ describe('Dashboard', () => {
           },
         },
       });
-      const spy = sinon.spy(props.actions, 'runQuery');
+      const spy = sinon.spy(props.actions, 'postChartFormData');
       wrapper.instance().refreshExcept();
       spy.restore();
       expect(spy.callCount).toBe(0);
diff --git a/superset/assets/src/chart/Chart.jsx b/superset/assets/src/chart/Chart.jsx
index bc19d63..8cb6153 100644
--- a/superset/assets/src/chart/Chart.jsx
+++ b/superset/assets/src/chart/Chart.jsx
@@ -68,12 +68,23 @@ class Chart extends React.PureComponent {
   }
   componentDidMount() {
     if (this.props.triggerQuery) {
-      this.props.actions.runQuery(
-        this.props.formData,
-        false,
-        this.props.timeout,
-        this.props.chartId,
-      );
+      if (this.props.chartId > 0) {
+        // Load saved chart with a GET request
+        this.props.actions.getSavedChart(
+          this.props.formData,
+          false,
+          this.props.timeout,
+          this.props.chartId,
+        );
+      } else {
+        // Create chart with POST request
+        this.props.actions.postChartFormData(
+          this.props.formData,
+          false,
+          this.props.timeout,
+          this.props.chartId,
+        );
+      }
     }
   }
 
diff --git a/superset/assets/src/chart/chartAction.js b/superset/assets/src/chart/chartAction.js
index 93433c8..3909dad 100644
--- a/superset/assets/src/chart/chartAction.js
+++ b/superset/assets/src/chart/chartAction.js
@@ -165,14 +165,14 @@ export function addChart(chart, key) {
   return { type: ADD_CHART, chart, key };
 }
 
-export const RUN_QUERY = 'RUN_QUERY';
-export function runQuery(formData, force = false, timeout = 60, key) {
+export function exploreJSON(formData, force = false, timeout = 60, key, method) {
   return (dispatch) => {
     const { url, payload } = getExploreUrlAndPayload({
       formData,
       endpointType: 'json',
       force,
       allowDomainSharding: true,
+      method,
     });
     const logStart = Logger.getTimestamp();
     const controller = new AbortController();
@@ -193,7 +193,9 @@ export function runQuery(formData, force = false, timeout = 60, key) {
         credentials: 'include',
       };
     }
-    const queryPromise = SupersetClient.post(querySettings)
+
+    const clientMethod = method === 'GET' ? SupersetClient.get : SupersetClient.post;
+    const queryPromise = clientMethod(querySettings)
       .then(({ json }) => {
         dispatch(logEvent(LOG_ACTIONS_LOAD_CHART, {
           slice_id: key,
@@ -246,6 +248,32 @@ export function runQuery(formData, force = false, timeout = 60, key) {
   };
 }
 
+export const GET_SAVED_CHART = 'GET_SAVED_CHART';
+export function getSavedChart(formData, force = false, timeout = 60, key) {
+  /*
+   * Perform a GET request to `/explore_json`.
+   *
+   * This will return the payload of a saved chart, optionally filtered by
+   * ad-hoc or extra filters from dashboards. Eg:
+   *
+   *  GET  /explore_json?{"chart_id":1}
+   *  GET  /explore_json?{"chart_id":1,"extra_filters":"..."}
+   *
+   */
+  return exploreJSON(formData, force, timeout, key, 'GET');
+}
+
+export const POST_CHART_FORM_DATA = 'POST_CHART_FORM_DATA';
+export function postChartFormData(formData, force = false, timeout = 60, key) {
+  /*
+   * Perform a POST request to `/explore_json`.
+   *
+   * This will post the form data to the endpoint, returning a new chart.
+   *
+   */
+  return exploreJSON(formData, force, timeout, key, 'POST');
+}
+
 export function redirectSQLLab(formData) {
   return (dispatch) => {
     const { url } = getExploreUrlAndPayload({ formData, endpointType: 'query' });
@@ -272,6 +300,6 @@ export function refreshChart(chart, force, timeout) {
     if (!chart.latestQueryFormData || Object.keys(chart.latestQueryFormData).length === 0) {
       return;
     }
-    dispatch(runQuery(chart.latestQueryFormData, force, timeout, chart.id));
+    dispatch(postChartFormData(chart.latestQueryFormData, force, timeout, chart.id));
   };
 }
diff --git a/superset/assets/src/dashboard/components/Dashboard.jsx b/superset/assets/src/dashboard/components/Dashboard.jsx
index dc1f054..32cc376 100644
--- a/superset/assets/src/dashboard/components/Dashboard.jsx
+++ b/superset/assets/src/dashboard/components/Dashboard.jsx
@@ -40,7 +40,7 @@ const propTypes = {
   actions: PropTypes.shape({
     addSliceToDashboard: PropTypes.func.isRequired,
     removeSliceFromDashboard: PropTypes.func.isRequired,
-    runQuery: PropTypes.func.isRequired,
+    postChartFormData: PropTypes.func.isRequired,
     logEvent: PropTypes.func.isRequired,
   }).isRequired,
   dashboardInfo: dashboardInfoPropShape.isRequired,
@@ -156,7 +156,7 @@ class Dashboard extends React.PureComponent {
           sliceId: chart.id,
         });
 
-        this.props.actions.runQuery(
+        this.props.actions.postChartFormData(
           updatedFormData,
           false,
           this.props.timeout,
diff --git a/superset/assets/src/dashboard/containers/Dashboard.jsx b/superset/assets/src/dashboard/containers/Dashboard.jsx
index 865ec40..e5cf4fb 100644
--- a/superset/assets/src/dashboard/containers/Dashboard.jsx
+++ b/superset/assets/src/dashboard/containers/Dashboard.jsx
@@ -25,7 +25,7 @@ import {
   addSliceToDashboard,
   removeSliceFromDashboard,
 } from '../actions/dashboardState';
-import { runQuery } from '../../chart/chartAction';
+import { postChartFormData } from '../../chart/chartAction';
 import { logEvent } from '../../logger/actions';
 import getLoadStatsPerTopLevelComponent from '../util/logging/getLoadStatsPerTopLevelComponent';
 
@@ -64,7 +64,7 @@ function mapDispatchToProps(dispatch) {
       {
         addSliceToDashboard,
         removeSliceFromDashboard,
-        runQuery,
+        postChartFormData,
         logEvent,
       },
       dispatch,
diff --git a/superset/assets/src/explore/components/ExploreChartHeader.jsx b/superset/assets/src/explore/components/ExploreChartHeader.jsx
index d2f3b98..31c74ad 100644
--- a/superset/assets/src/explore/components/ExploreChartHeader.jsx
+++ b/superset/assets/src/explore/components/ExploreChartHeader.jsx
@@ -50,8 +50,8 @@ const propTypes = {
 };
 
 class ExploreChartHeader extends React.PureComponent {
-  runQuery() {
-    this.props.actions.runQuery(this.props.form_data, true,
+  postChartFormData() {
+    this.props.actions.postChartFormData(this.props.form_data, true,
       this.props.timeout, this.props.chart.id);
   }
 
@@ -142,7 +142,7 @@ class ExploreChartHeader extends React.PureComponent {
             />}
           {chartSucceeded && queryResponse && queryResponse.is_cached &&
             <CachedLabel
-              onClick={this.runQuery.bind(this)}
+              onClick={this.postChartFormData.bind(this)}
               cachedTimestamp={queryResponse.cached_dttm}
             />}
           <Timer
diff --git a/superset/assets/src/explore/components/ExploreViewContainer.jsx b/superset/assets/src/explore/components/ExploreViewContainer.jsx
index 9ef7e9e..91777e4 100644
--- a/superset/assets/src/explore/components/ExploreViewContainer.jsx
+++ b/superset/assets/src/explore/components/ExploreViewContainer.jsx
@@ -215,7 +215,7 @@ class ExploreViewContainer extends React.Component {
 
   triggerQueryIfNeeded() {
     if (this.props.chart.triggerQuery && !this.hasErrors()) {
-      this.props.actions.runQuery(
+      this.props.actions.postChartFormData(
         this.props.form_data,
         false,
         this.props.timeout,
@@ -255,7 +255,8 @@ class ExploreViewContainer extends React.Component {
     const formData = history.state;
     if (formData && Object.keys(formData).length) {
       this.props.actions.setExploreControls(formData);
-      this.props.actions.runQuery(formData, false, this.props.timeout, this.props.chart.id);
+      this.props.actions.postChartFormData(
+        formData, false, this.props.timeout, this.props.chart.id);
     }
   }
 
diff --git a/superset/assets/src/explore/exploreUtils.js b/superset/assets/src/explore/exploreUtils.js
index 48bb60a..4785933 100644
--- a/superset/assets/src/explore/exploreUtils.js
+++ b/superset/assets/src/explore/exploreUtils.js
@@ -95,6 +95,7 @@ export function getExploreUrlAndPayload({
   curUrl = null,
   requestParams = {},
   allowDomainSharding = false,
+  method = 'POST',
 }) {
   if (!formData.datasource) {
     return null;
@@ -118,8 +119,18 @@ export function getExploreUrlAndPayload({
 
   // Building the querystring (search) part of the URI
   const search = uri.search(true);
-  if (formData.slice_id) {
-    search.form_data = safeStringify({ slice_id: formData.slice_id });
+  const { slice_id, extra_filters, adhoc_filters } = formData;
+  if (slice_id) {
+    const form_data = { slice_id };
+    if (method === 'GET') {
+      if (extra_filters && extra_filters.length) {
+        form_data.extra_filters = extra_filters;
+      }
+      if (adhoc_filters && adhoc_filters.length) {
+        form_data.adhoc_filters = adhoc_filters;
+      }
+    }
+    search.form_data = safeStringify(form_data);
   }
   if (force) {
     search.force = 'true';
diff --git a/superset/utils/decorators.py b/superset/utils/decorators.py
index b75b883..23b15a5 100644
--- a/superset/utils/decorators.py
+++ b/superset/utils/decorators.py
@@ -14,11 +14,23 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+from datetime import datetime, timedelta
+from functools import wraps
+import logging
+
 from contextlib2 import contextmanager
+from flask import request
 
+from superset import app, cache
 from superset.utils.dates import now_as_float
 
 
+# If a user sets `max_age` to 0, for long the browser should cache the
+# resource? Flask-Caching will cache forever, but for the HTTP header we need
+# to specify a "far future" date.
+FAR_FUTURE = 365 * 24 * 60 * 60  # 1 year in seconds
+
+
 @contextmanager
 def stats_timing(stats_key, stats_logger):
     """Provide a transactional scope around a series of operations."""
@@ -29,3 +41,61 @@ def stats_timing(stats_key, stats_logger):
         raise e
     finally:
         stats_logger.timing(stats_key, now_as_float() - start_ts)
+
+
+def etag_cache(max_age, check_perms=bool):
+    """
+    A decorator for caching views and handling etag conditional requests.
+
+    The decorator caches the response, returning headers for etag and last
+    modified. If the client makes a request that matches, the server will
+    return a "304 Not Mofified" status.
+
+    If no cache is set, the decorator will still set the ETag header, and
+    handle conditional requests.
+
+    """
+    def decorator(f):
+        @wraps(f)
+        def wrapper(*args, **kwargs):
+            # check if the user can access the resource
+            check_perms(*args, **kwargs)
+
+            try:
+                # build the cache key from the function arguments and any other
+                # additional GET arguments (like `form_data`, eg).
+                key_args = list(args)
+                key_kwargs = kwargs.copy()
+                key_kwargs.update(request.args)
+                cache_key = wrapper.make_cache_key(f, *key_args, **key_kwargs)
+                response = cache.get(cache_key)
+            except Exception:  # pylint: disable=broad-except
+                if app.debug:
+                    raise
+                logging.exception('Exception possibly due to cache backend.')
+                response = None
+
+            if response is None or request.method == 'POST':
+                response = f(*args, **kwargs)
+                response.cache_control.public = True
+                response.last_modified = datetime.utcnow()
+                expiration = max_age if max_age != 0 else FAR_FUTURE
+                response.expires = response.last_modified + timedelta(seconds=expiration)
+                response.add_etag()
+                try:
+                    cache.set(cache_key, response, timeout=max_age)
+                except Exception:  # pylint: disable=broad-except
+                    logging.exception('Exception possibly due to cache backend.')
+
+            return response.make_conditional(request)
+
+        if cache:
+            wrapper.uncached = f
+            wrapper.cache_timeout = max_age
+            wrapper.make_cache_key = \
+                cache._memoize_make_cache_key(  # pylint: disable=protected-access
+                    make_name=None, timeout=max_age)
+
+        return wrapper
+
+    return decorator
diff --git a/superset/views/core.py b/superset/views/core.py
index ad75c3b..61fde62 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -50,7 +50,6 @@ from superset.connectors.sqla.models import AnnotationDatasource, SqlaTable
 from superset.exceptions import SupersetException
 from superset.forms import CsvToDatabaseForm
 from superset.jinja_context import get_template_processor
-from superset.legacy import cast_form_data, update_time_range
 import superset.models.core as models
 from superset.models.sql_lab import Query
 from superset.models.user_attributes import UserAttribute
@@ -58,6 +57,7 @@ from superset.sql_parse import ParsedQuery
 from superset.utils import core as utils
 from superset.utils import dashboard_import_export
 from superset.utils.dates import now_as_float
+from superset.utils.decorators import etag_cache
 from .base import (
     api, BaseSupersetView,
     check_ownership,
@@ -65,9 +65,10 @@ from .base import (
     get_error_msg, handle_api_exception, json_error_response, json_success,
     SupersetFilter, SupersetModelView, YamlExportMixin,
 )
-from .utils import bootstrap_user_data
+from .utils import bootstrap_user_data, get_datasource_info, get_form_data, get_viz
 
 config = app.config
+CACHE_DEFAULT_TIMEOUT = config.get('CACHE_DEFAULT_TIMEOUT', 0)
 stats_logger = config.get('STATS_LOGGER')
 log_this = models.Log.log_this
 DAR = models.DatasourceAccessRequest
@@ -100,6 +101,46 @@ def is_owner(obj, user):
     return obj and user in obj.owners
 
 
+def check_datasource_perms(self, datasource_type=None, datasource_id=None):
+    """
+    Check if user can access a cached response from explore_json.
+
+    This function takes `self` since it must have the same signature as the
+    the decorated method.
+
+    """
+    form_data = get_form_data()[0]
+    datasource_id, datasource_type = get_datasource_info(
+        datasource_id, datasource_type, form_data)
+    viz_obj = get_viz(
+        datasource_type=datasource_type,
+        datasource_id=datasource_id,
+        form_data=form_data,
+        force=False,
+    )
+    security_manager.assert_datasource_permission(viz_obj.datasource)
+
+
+def check_slice_perms(self, slice_id):
+    """
+    Check if user can access a cached response from slice_json.
+
+    This function takes `self` since it must have the same signature as the
+    the decorated method.
+
+    """
+    form_data, slc = get_form_data(slice_id, use_slice_data=True)
+    datasource_type = slc.datasource.type
+    datasource_id = slc.datasource.id
+    viz_obj = get_viz(
+        datasource_type=datasource_type,
+        datasource_id=datasource_id,
+        form_data=form_data,
+        force=False,
+    )
+    security_manager.assert_datasource_permission(viz_obj.datasource)
+
+
 class DatabaseFilter(SupersetFilter):
     def apply(self, query, func):  # noqa
         if security_manager.all_database_access():
@@ -1012,89 +1053,10 @@ class Superset(BaseSupersetView):
         session.commit()
         return redirect('/accessrequestsmodelview/list/')
 
-    def get_form_data(self, slice_id=None, use_slice_data=False):
-        form_data = {}
-        post_data = request.form.get('form_data')
-        request_args_data = request.args.get('form_data')
-        # Supporting POST
-        if post_data:
-            form_data.update(json.loads(post_data))
-        # request params can overwrite post body
-        if request_args_data:
-            form_data.update(json.loads(request_args_data))
-
-        url_id = request.args.get('r')
-        if url_id:
-            saved_url = db.session.query(models.Url).filter_by(id=url_id).first()
-            if saved_url:
-                url_str = parse.unquote_plus(
-                    saved_url.url.split('?')[1][10:], encoding='utf-8', errors=None)
-                url_form_data = json.loads(url_str)
-                # allow form_date in request override saved url
-                url_form_data.update(form_data)
-                form_data = url_form_data
-
-        if request.args.get('viz_type'):
-            # Converting old URLs
-            form_data = cast_form_data(form_data)
-
-        form_data = {
-            k: v
-            for k, v in form_data.items()
-            if k not in FORM_DATA_KEY_BLACKLIST
-        }
-
-        # When a slice_id is present, load from DB and override
-        # the form_data from the DB with the other form_data provided
-        slice_id = form_data.get('slice_id') or slice_id
-        slc = None
-
-        # Check if form data only contains slice_id
-        contains_only_slc_id = not any(key != 'slice_id' for key in form_data)
-
-        # Include the slice_form_data if request from explore or slice calls
-        # or if form_data only contains slice_id
-        if slice_id and (use_slice_data or contains_only_slc_id):
-            slc = db.session.query(models.Slice).filter_by(id=slice_id).one_or_none()
-            if slc:
-                slice_form_data = slc.form_data.copy()
-                slice_form_data.update(form_data)
-                form_data = slice_form_data
-
-        update_time_range(form_data)
-
-        return form_data, slc
-
-    def get_viz(
-            self,
-            slice_id=None,
-            form_data=None,
-            datasource_type=None,
-            datasource_id=None,
-            force=False,
-    ):
-        if slice_id:
-            slc = (
-                db.session.query(models.Slice)
-                .filter_by(id=slice_id)
-                .one()
-            )
-            return slc.get_viz()
-        else:
-            viz_type = form_data.get('viz_type', 'table')
-            datasource = ConnectorRegistry.get_datasource(
-                datasource_type, datasource_id, db.session)
-            viz_obj = viz.viz_types[viz_type](
-                datasource,
-                form_data=form_data,
-                force=force,
-            )
-            return viz_obj
-
     @has_access
     @expose('/slice/<slice_id>/')
     def slice(self, slice_id):
-        form_data, slc = self.get_form_data(slice_id, use_slice_data=True)
+        form_data, slc = get_form_data(slice_id, use_slice_data=True)
         if not slc:
             abort(404)
         endpoint = '/superset/explore/?form_data={}'.format(
@@ -1138,18 +1100,7 @@ class Superset(BaseSupersetView):
         })
 
     def generate_json(
-            self, datasource_type, datasource_id, form_data,
-            csv=False, query=False, force=False, results=False,
-            samples=False,
-    ):
-        viz_obj = self.get_viz(
-            datasource_type=datasource_type,
-            datasource_id=datasource_id,
-            form_data=form_data,
-            force=force,
-        )
-        security_manager.assert_datasource_permission(viz_obj.datasource)
-
+            self, viz_obj, csv=False, query=False, results=False, samples=False):
         if csv:
             return CsvResponse(
                 viz_obj.get_csv(),
@@ -1173,21 +1124,25 @@ class Superset(BaseSupersetView):
     @api
     @has_access_api
     @expose('/slice_json/<slice_id>')
+    @etag_cache(CACHE_DEFAULT_TIMEOUT, check_perms=check_slice_perms)
     def slice_json(self, slice_id):
-        form_data, slc = self.get_form_data(slice_id, use_slice_data=True)
+        form_data, slc = get_form_data(slice_id, use_slice_data=True)
         datasource_type = slc.datasource.type
         datasource_id = slc.datasource.id
-
-        return self.generate_json(datasource_type=datasource_type,
-                                  datasource_id=datasource_id,
-                                  form_data=form_data)
+        viz_obj = get_viz(
+            datasource_type=datasource_type,
+            datasource_id=datasource_id,
+            form_data=form_data,
+            force=False,
+        )
+        return self.generate_json(viz_obj)
 
     @log_this
     @api
     @has_access_api
     @expose('/annotation_json/<layer_id>')
     def annotation_json(self, layer_id):
-        form_data = self.get_form_data()[0]
+        form_data = get_form_data()[0]
         form_data['layer_id'] = layer_id
         form_data['filters'] = [{'col': 'layer_id',
                                  'op': '==',
@@ -1207,6 +1162,7 @@ class Superset(BaseSupersetView):
     @handle_api_exception
     @expose('/explore_json/<datasource_type>/<datasource_id>/', methods=['GET', 'POST'])
     @expose('/explore_json/', methods=['GET', 'POST'])
+    @etag_cache(CACHE_DEFAULT_TIMEOUT, check_perms=check_datasource_perms)
     def explore_json(self, datasource_type=None, datasource_id=None):
         """Serves all request that GET or POST form_data
 
@@ -1223,18 +1179,21 @@ class Superset(BaseSupersetView):
         samples = request.args.get('samples') == 'true'
         force = request.args.get('force') == 'true'
 
-        form_data = self.get_form_data()[0]
-        datasource_id, datasource_type = self.datasource_info(
+        form_data = get_form_data()[0]
+        datasource_id, datasource_type = get_datasource_info(
             datasource_id, datasource_type, form_data)
-
-        return self.generate_json(
+        viz_obj = get_viz(
             datasource_type=datasource_type,
             datasource_id=datasource_id,
             form_data=form_data,
+            force=force,
+        )
+
+        return self.generate_json(
+            viz_obj,
             csv=csv,
             query=query,
             results=results,
-            force=force,
             samples=samples,
         )
 
@@ -1260,34 +1219,15 @@ class Superset(BaseSupersetView):
             datasource_id=datasource_id,
             **request.args))
 
-    @staticmethod
-    def datasource_info(datasource_id, datasource_type, form_data):
-        """Compatibility layer for handling of datasource info
-
-        datasource_id & datasource_type used to be passed in the URL
-        directory, now they should come as part of the form_data,
-        This function allows supporting both without duplicating code"""
-        datasource = form_data.get('datasource', '')
-        if '__' in datasource:
-            datasource_id, datasource_type = datasource.split('__')
-            # The case where the datasource has been deleted
-            datasource_id = None if datasource_id == 'None' else datasource_id
-
-        if not datasource_id:
-            raise Exception(
-                'The datasource associated with this chart no longer exists')
-        datasource_id = int(datasource_id)
-        return datasource_id, datasource_type
-
     @log_this
     @has_access
     @expose('/explore/<datasource_type>/<datasource_id>/', methods=['GET', 'POST'])
     @expose('/explore/', methods=['GET', 'POST'])
     def explore(self, datasource_type=None, datasource_id=None):
         user_id = g.user.get_id() if g.user else None
-        form_data, slc = self.get_form_data(use_slice_data=True)
+        form_data, slc = get_form_data(use_slice_data=True)
 
-        datasource_id, datasource_type = self.datasource_info(
+        datasource_id, datasource_type = get_datasource_info(
             datasource_id, datasource_type, form_data)
 
         error_redirect = '/chart/list/'
@@ -1413,7 +1353,7 @@ class Superset(BaseSupersetView):
         """Save or overwrite a slice"""
         slice_name = args.get('slice_name')
         action = args.get('action')
-        form_data, unused_slc = self.get_form_data()
+        form_data = get_form_data()[0]
 
         if action in ('saveas'):
             if 'slice_id' in form_data:
@@ -2105,8 +2045,8 @@ class Superset(BaseSupersetView):
 
         for slc in slices:
             try:
-                form_data = self.get_form_data(slc.id, use_slice_data=True)[0]
-                obj = self.get_viz(
+                form_data = get_form_data(slc.id, use_slice_data=True)[0]
+                obj = get_viz(
                     datasource_type=slc.datasource.type,
                     datasource_id=slc.datasource.id,
                     form_data=form_data,
@@ -2874,7 +2814,7 @@ class Superset(BaseSupersetView):
         This method exposes an API endpoint to
         get the database query string for this slice
         """
-        viz_obj = self.get_viz(slice_id)
+        viz_obj = get_viz(slice_id)
         security_manager.assert_datasource_permission(viz_obj.datasource)
         return self.get_query_string_response(viz_obj)
 
diff --git a/superset/views/utils.py b/superset/views/utils.py
index eb68316..4318141 100644
--- a/superset/views/utils.py
+++ b/superset/views/utils.py
@@ -16,11 +16,25 @@
 # under the License.
 # pylint: disable=C,R,W
 from collections import defaultdict
+from urllib import parse
 
-from flask import g
+from flask import g, request
 from flask_appbuilder.security.sqla import models as ab_models
+import simplejson as json
 
-from superset import db
+from superset import app, db, viz
+from superset.connectors.connector_registry import ConnectorRegistry
+from superset.legacy import cast_form_data, update_time_range
+import superset.models.core as models
+
+
+FORM_DATA_KEY_BLACKLIST = []
+if not app.config.get('ENABLE_JAVASCRIPT_CONTROLS'):
+    FORM_DATA_KEY_BLACKLIST = [
+        'js_tooltip',
+        'js_onclick_href',
+        'js_data_mutator',
+    ]
 
 
 def bootstrap_user_data(username=None, include_perms=False):
@@ -74,3 +88,103 @@ def get_permissions(user):
         ]
 
     return roles, permissions
+
+
+def get_viz(
+        slice_id=None,
+        form_data=None,
+        datasource_type=None,
+        datasource_id=None,
+        force=False,
+):
+    if slice_id:
+        slc = (
+            db.session.query(models.Slice)
+            .filter_by(id=slice_id)
+            .one()
+        )
+        return slc.get_viz()
+    else:
+        viz_type = form_data.get('viz_type', 'table')
+        datasource = ConnectorRegistry.get_datasource(
+            datasource_type, datasource_id, db.session)
+        viz_obj = viz.viz_types[viz_type](
+            datasource,
+            form_data=form_data,
+            force=force,
+        )
+        return viz_obj
+
+
+def get_form_data(slice_id=None, use_slice_data=False):
+    form_data = {}
+    post_data = request.form.get('form_data')
+    request_args_data = request.args.get('form_data')
+    # Supporting POST
+    if post_data:
+        form_data.update(json.loads(post_data))
+    # request params can overwrite post body
+    if request_args_data:
+        form_data.update(json.loads(request_args_data))
+
+    url_id = request.args.get('r')
+    if url_id:
+        saved_url = db.session.query(models.Url).filter_by(id=url_id).first()
+        if saved_url:
+            url_str = parse.unquote_plus(
+                saved_url.url.split('?')[1][10:], encoding='utf-8', errors=None)
+            url_form_data = json.loads(url_str)
+            # allow form_date in request override saved url
+            url_form_data.update(form_data)
+            form_data = url_form_data
+
+    if request.args.get('viz_type'):
+        # Converting old URLs
+        form_data = cast_form_data(form_data)
+
+    form_data = {
+        k: v
+        for k, v in form_data.items()
+        if k not in FORM_DATA_KEY_BLACKLIST
+    }
+
+    # When a slice_id is present, load from DB and override
+    # the form_data from the DB with the other form_data provided
+    slice_id = form_data.get('slice_id') or slice_id
+    slc = None
+
+    # Check if form data only contains slice_id and additional filters
+    valid_keys = ['slice_id', 'extra_filters', 'adhoc_filters']
+    valid_slice_id = all(key in valid_keys for key in form_data)
+
+    # Include the slice_form_data if request from explore or slice calls
+    # or if form_data only contains slice_id and additional filters
+    if slice_id and (use_slice_data or valid_slice_id):
+        slc = db.session.query(models.Slice).filter_by(id=slice_id).one_or_none()
+        if slc:
+            slice_form_data = slc.form_data.copy()
+            slice_form_data.update(form_data)
+            form_data = slice_form_data
+
+    update_time_range(form_data)
+
+    return form_data, slc
+
+
+def get_datasource_info(datasource_id, datasource_type, form_data):
+    """Compatibility layer for handling of datasource info
+
+    datasource_id & datasource_type used to be passed in the URL
+    directory, now they should come as part of the form_data,
+    This function allows supporting both without duplicating code"""
+    datasource = form_data.get('datasource', '')
+    if '__' in datasource:
+        datasource_id, datasource_type = datasource.split('__')
+        # The case where the datasource has been deleted
+        datasource_id = None if datasource_id == 'None' else datasource_id
+
+    if not datasource_id:
+        raise Exception(
+            'The datasource associated with this chart no longer exists')
+    datasource_id = int(datasource_id)
+    return datasource_id, datasource_type


Mime
View raw message