浏览代码

HUE-9013 [editor] Improve result layout in notebook 2

This also moves the remaining global layout related functions into the new bindings
Johan Ahlen 6 年之前
父节点
当前提交
37a98fcb6a

+ 1344 - 1352
desktop/core/src/desktop/js/apps/notebook/app.js

@@ -26,1497 +26,1489 @@ import huePubSub from 'utils/huePubSub';
 import hueUtils from 'utils/hueUtils';
 import I18n from 'utils/i18n';
 import sqlWorkerHandler from 'sql/sqlWorkerHandler';
+import { initNotebook2 } from 'apps/notebook2/app';
 
-window.Clipboard = Clipboard;
+if (window.ENABLE_NOTEBOOK_2) {
+  initNotebook2();
+} else {
+  window.Clipboard = Clipboard;
 
-const HUE_PUB_SUB_EDITOR_ID =
-  window.location.pathname.indexOf('notebook') > -1 ? 'notebook' : 'editor';
+  const HUE_PUB_SUB_EDITOR_ID =
+    window.location.pathname.indexOf('notebook') > -1 ? 'notebook' : 'editor';
 
-huePubSub.subscribe('app.dom.loaded', app => {
-  if (app === 'editor' || app === 'notebook') {
-    window.MAIN_SCROLLABLE = '.page-content';
+  huePubSub.subscribe('app.dom.loaded', app => {
+    if (app === 'editor' || app === 'notebook') {
+      window.MAIN_SCROLLABLE = '.page-content';
 
-    let isLeftNavOpen = false;
-    huePubSub.subscribe(
-      'left.nav.open.toggle',
-      val => {
-        isLeftNavOpen = val;
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
+      let isLeftNavOpen = false;
+      huePubSub.subscribe(
+        'left.nav.open.toggle',
+        val => {
+          isLeftNavOpen = val;
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
 
-    huePubSub.subscribe(
-      'split.panel.resized',
-      () => {
-        huePubSub.publish('recalculate.name.description.width');
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    const showHoverMsg = e => {
-      let dt = null;
-      if (e) {
-        dt = e.dataTransfer;
-      }
-      if (
-        !isLeftNavOpen &&
-        (!dt ||
-          (dt.types &&
-            (dt.types.indexOf ? dt.types.indexOf('Files') !== -1 : dt.types.contains('Files'))))
-      ) {
+      huePubSub.subscribe(
+        'split.panel.resized',
+        () => {
+          huePubSub.publish('recalculate.name.description.width');
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      const showHoverMsg = e => {
+        let dt = null;
+        if (e) {
+          dt = e.dataTransfer;
+        }
+        if (
+          !isLeftNavOpen &&
+          (!dt ||
+            (dt.types &&
+              (dt.types.indexOf ? dt.types.indexOf('Files') !== -1 : dt.types.contains('Files'))))
+        ) {
+          $(window.EDITOR_BINDABLE_ELEMENT)
+            .find('.hoverMsg')
+            .removeClass('hide');
+        }
+      };
+
+      const hideHoverMsg = vm => {
+        if (vm.editorMode()) {
+          $(window.EDITOR_BINDABLE_ELEMENT)
+            .find('.hoverText')
+            .html(I18n('Drop a SQL file here'));
+        } else {
+          $(window.EDITOR_BINDABLE_ELEMENT)
+            .find('.hoverText')
+            .html(I18n('Drop iPython/Zeppelin notebooks here'));
+        }
         $(window.EDITOR_BINDABLE_ELEMENT)
           .find('.hoverMsg')
-          .removeClass('hide');
-      }
-    };
+          .addClass('hide');
+      };
 
-    const hideHoverMsg = vm => {
-      if (vm.editorMode()) {
-        $(window.EDITOR_BINDABLE_ELEMENT)
-          .find('.hoverText')
-          .html(I18n('Drop a SQL file here'));
-      } else {
-        $(window.EDITOR_BINDABLE_ELEMENT)
-          .find('.hoverText')
-          .html(I18n('Drop iPython/Zeppelin notebooks here'));
-      }
-      $(window.EDITOR_BINDABLE_ELEMENT)
-        .find('.hoverMsg')
-        .addClass('hide');
-    };
-
-    // TODO: Remove with ENABLE_NOTEBOOK_2
-    const createHueDatatable = (el, snippet, vm) => {
-      let DATATABLES_MAX_HEIGHT = 330;
-      let invisibleRows = 10;
-      if (snippet.result && snippet.result.data() && snippet.result.data().length) {
-        const cols = snippet.result.data()[0].length;
-        invisibleRows = cols > 200 ? 10 : cols > 30 ? 50 : 100;
-      }
-      const _dt = $(el).hueDataTable({
-        i18n: {
-          NO_RESULTS: I18n('No results found.'),
-          OF: I18n('of')
-        },
-        fnDrawCallback: function(oSettings) {
-          if (vm.editorMode()) {
-            $('#queryResults').removeAttr('style');
-            DATATABLES_MAX_HEIGHT =
-              $(window).height() -
+      const createHueDatatable = (el, snippet, vm) => {
+        let DATATABLES_MAX_HEIGHT = 330;
+        let invisibleRows = 10;
+        if (snippet.result && snippet.result.data() && snippet.result.data().length) {
+          const cols = snippet.result.data()[0].length;
+          invisibleRows = cols > 200 ? 10 : cols > 30 ? 50 : 100;
+        }
+        const _dt = $(el).hueDataTable({
+          i18n: {
+            NO_RESULTS: I18n('No results found.'),
+            OF: I18n('of')
+          },
+          fnDrawCallback: function(oSettings) {
+            if (vm.editorMode()) {
+              $('#queryResults').removeAttr('style');
+              DATATABLES_MAX_HEIGHT =
+                $(window).height() -
+                $(el)
+                  .parent()
+                  .offset().top -
+                40;
+              $(el)
+                .parents('.dataTables_wrapper')
+                .css('overflow-x', 'hidden');
+              $(el).jHueHorizontalScrollbar();
+              $(el)
+                .parents('.dataTables_wrapper')
+                .jHueScrollLeft();
+            } else if ($(el).data('fnDraws') === 1) {
               $(el)
-                .parent()
-                .offset().top -
-              40;
+                .parents('.dataTables_wrapper')
+                .jHueTableScroller({
+                  maxHeight: DATATABLES_MAX_HEIGHT,
+                  heightAfterCorrection: 0
+                });
+            }
+          },
+          scrollable:
+            vm.editorMode() && !vm.isPresentationMode()
+              ? window.MAIN_SCROLLABLE
+              : '.dataTables_wrapper',
+          contained: !vm.editorMode() || vm.isPresentationMode(),
+          forceInvisible: invisibleRows
+        });
+
+        window.setTimeout(() => {
+          if (vm.editorMode()) {
             $(el)
               .parents('.dataTables_wrapper')
               .css('overflow-x', 'hidden');
+            const bannerTopHeight = window.BANNER_TOP_HTML ? 30 : 2;
+            $(el).jHueTableExtender2({
+              mainScrollable: window.MAIN_SCROLLABLE,
+              fixedFirstColumn: vm.editorMode(),
+              stickToTopPosition: 48 + bannerTopHeight,
+              parentId: 'snippet_' + snippet.id(),
+              clonedContainerPosition: 'fixed',
+              app: 'editor'
+            });
             $(el).jHueHorizontalScrollbar();
-            $(el)
-              .parents('.dataTables_wrapper')
-              .jHueScrollLeft();
-          } else if ($(el).data('fnDraws') === 1) {
-            $(el)
-              .parents('.dataTables_wrapper')
-              .jHueTableScroller({
-                maxHeight: DATATABLES_MAX_HEIGHT,
-                heightAfterCorrection: 0
-              });
+          } else {
+            $(el).jHueTableExtender2({
+              mainScrollable: $(el).parents('.dataTables_wrapper')[0],
+              fixedFirstColumn: vm.editorMode(),
+              parentId: 'snippet_' + snippet.id(),
+              clonedContainerPosition: 'absolute',
+              app: 'editor'
+            });
           }
-        },
-        scrollable:
-          vm.editorMode() && !vm.isPresentationMode()
-            ? window.MAIN_SCROLLABLE
-            : '.dataTables_wrapper',
-        contained: !vm.editorMode() || vm.isPresentationMode(),
-        forceInvisible: invisibleRows
-      });
+        }, 0);
 
-      window.setTimeout(() => {
-        if (vm.editorMode()) {
-          $(el)
-            .parents('.dataTables_wrapper')
-            .css('overflow-x', 'hidden');
-          const bannerTopHeight = window.BANNER_TOP_HTML ? 30 : 2;
-          $(el).jHueTableExtender2({
-            mainScrollable: window.MAIN_SCROLLABLE,
-            fixedFirstColumn: vm.editorMode(),
-            stickToTopPosition: 48 + bannerTopHeight,
-            parentId: 'snippet_' + snippet.id(),
-            clonedContainerPosition: 'fixed',
-            app: 'editor'
-          });
-          $(el).jHueHorizontalScrollbar();
-        } else {
-          $(el).jHueTableExtender2({
-            mainScrollable: $(el).parents('.dataTables_wrapper')[0],
-            fixedFirstColumn: vm.editorMode(),
-            parentId: 'snippet_' + snippet.id(),
-            clonedContainerPosition: 'absolute',
-            app: 'editor'
-          });
-        }
-      }, 0);
-
-      return _dt;
-    };
-
-    // TODO: Remove with ENABLE_NOTEBOOK_2
-    const createDatatable = (el, snippet, vm) => {
-      const parent = $(el).parent();
-      // When executing few columns -> many columns -> few columns we have to clear the style
-      $(el).removeAttr('style');
-      if ($(el).hasClass('table-huedatatable')) {
-        $(el).removeClass('table-huedatatable');
-        if (parent.hasClass('dataTables_wrapper')) {
-          $(el).unwrap();
+        return _dt;
+      };
+
+      const createDatatable = (el, snippet, vm) => {
+        const parent = $(el).parent();
+        // When executing few columns -> many columns -> few columns we have to clear the style
+        $(el).removeAttr('style');
+        if ($(el).hasClass('table-huedatatable')) {
+          $(el).removeClass('table-huedatatable');
+          if (parent.hasClass('dataTables_wrapper')) {
+            $(el).unwrap();
+          }
         }
-      }
-      $(el).addClass('dt');
+        $(el).addClass('dt');
 
-      const _dt = createHueDatatable(el, snippet, vm);
+        const _dt = createHueDatatable(el, snippet, vm);
 
-      const dataTableEl = $(el).parents('.dataTables_wrapper');
+        const dataTableEl = $(el).parents('.dataTables_wrapper');
 
-      if (!vm.editorMode()) {
-        dataTableEl.bind('mousewheel DOMMouseScroll wheel', function(e) {
-          if (
-            $(el)
-              .closest('.results')
-              .css('overflow') === 'hidden'
-          ) {
-            return;
-          }
-          const _e = e.originalEvent,
-            _deltaX = _e.wheelDeltaX || -_e.deltaX,
-            _deltaY = _e.wheelDeltaY || -_e.deltaY;
-          this.scrollTop += -_deltaY / 2;
-          this.scrollLeft += -_deltaX / 2;
-
-          if (this.scrollTop === 0) {
-            $('body')[0].scrollTop += -_deltaY / 3;
-            $('html')[0].scrollTop += -_deltaY / 3; // for firefox
-          }
-          e.preventDefault();
-        });
-      }
+        if (!vm.editorMode()) {
+          dataTableEl.bind('mousewheel DOMMouseScroll wheel', function(e) {
+            if (
+              $(el)
+                .closest('.results')
+                .css('overflow') === 'hidden'
+            ) {
+              return;
+            }
+            const _e = e.originalEvent,
+              _deltaX = _e.wheelDeltaX || -_e.deltaX,
+              _deltaY = _e.wheelDeltaY || -_e.deltaY;
+            this.scrollTop += -_deltaY / 2;
+            this.scrollLeft += -_deltaX / 2;
+
+            if (this.scrollTop === 0) {
+              $('body')[0].scrollTop += -_deltaY / 3;
+              $('html')[0].scrollTop += -_deltaY / 3; // for firefox
+            }
+            e.preventDefault();
+          });
+        }
 
-      let _scrollTimeout = -1;
+        let _scrollTimeout = -1;
 
-      let scrollElement = dataTableEl;
-      if (vm.editorMode()) {
-        scrollElement = $(window.MAIN_SCROLLABLE);
-      }
+        let scrollElement = dataTableEl;
+        if (vm.editorMode()) {
+          scrollElement = $(window.MAIN_SCROLLABLE);
+        }
 
-      if (scrollElement.data('scrollFnDtCreation')) {
-        scrollElement.off('scroll', scrollElement.data('scrollFnDtCreation'));
-      }
+        if (scrollElement.data('scrollFnDtCreation')) {
+          scrollElement.off('scroll', scrollElement.data('scrollFnDtCreation'));
+        }
 
-      let resultFollowTimeout = -1;
-      const dataScroll = function() {
-        if (vm.editorMode()) {
-          const snippetEl = $('#snippet_' + snippet.id());
-          if (snippetEl.find('.dataTables_wrapper').length > 0 && snippet.showGrid()) {
-            window.clearTimeout(resultFollowTimeout);
-            resultFollowTimeout = window.setTimeout(() => {
-              const topCoord = vm.isPresentationMode() || vm.isResultFullScreenMode() ? 50 : 73;
-              let offsetTop = 0;
-              if (
-                snippetEl.find('.dataTables_wrapper').length > 0 &&
-                snippetEl.find('.dataTables_wrapper').offset()
-              ) {
-                offsetTop = (snippetEl.find('.dataTables_wrapper').offset().top - topCoord) * -1;
-              }
-              let margin = Math.max(offsetTop, 0);
-              if (window.BANNER_TOP_HTML) {
-                margin += 31;
-              }
-              if (snippet.isResultSettingsVisible()) {
-                snippetEl.find('.snippet-grid-settings').css({
-                  height:
-                    vm.isPresentationMode() || !vm.editorMode()
-                      ? '330px'
-                      : Math.max(
-                          100,
-                          Math.ceil(
-                            $(window).height() - Math.max($('#queryResults').offset().top, topCoord)
-                          )
-                        ) + 'px'
-                });
-                snippetEl.find('.result-settings').css({
-                  marginTop: margin
-                });
-              }
-              if (!window.ENABLE_NOTEBOOK_2) {
+        let resultFollowTimeout = -1;
+        const dataScroll = function() {
+          if (vm.editorMode()) {
+            const snippetEl = $('#snippet_' + snippet.id());
+            if (snippetEl.find('.dataTables_wrapper').length > 0 && snippet.showGrid()) {
+              window.clearTimeout(resultFollowTimeout);
+              resultFollowTimeout = window.setTimeout(() => {
+                const topCoord = vm.isPresentationMode() || vm.isResultFullScreenMode() ? 50 : 73;
+                let offsetTop = 0;
+                if (
+                  snippetEl.find('.dataTables_wrapper').length > 0 &&
+                  snippetEl.find('.dataTables_wrapper').offset()
+                ) {
+                  offsetTop = (snippetEl.find('.dataTables_wrapper').offset().top - topCoord) * -1;
+                }
+                let margin = Math.max(offsetTop, 0);
+                if (window.BANNER_TOP_HTML) {
+                  margin += 31;
+                }
+                if (snippet.isResultSettingsVisible()) {
+                  snippetEl.find('.snippet-grid-settings').css({
+                    height:
+                      vm.isPresentationMode() || !vm.editorMode()
+                        ? '330px'
+                        : Math.max(
+                            100,
+                            Math.ceil(
+                              $(window).height() -
+                                Math.max($('#queryResults').offset().top, topCoord)
+                            )
+                          ) + 'px'
+                  });
+                  snippetEl.find('.result-settings').css({
+                    marginTop: margin
+                  });
+                }
                 snippetEl.find('.snippet-actions').css({
                   marginTop: margin + 25
                 });
+              }, 100);
+            }
+          }
+          if (
+            !vm.editorMode() ||
+            (vm.editorMode() && snippet.currentQueryTab() === 'queryResults' && snippet.showGrid())
+          ) {
+            let _lastScrollPosition =
+              scrollElement.data('scrollPosition') != null
+                ? scrollElement.data('scrollPosition')
+                : 0;
+            window.clearTimeout(_scrollTimeout);
+            scrollElement.data('scrollPosition', scrollElement.scrollTop());
+            _scrollTimeout = window.setTimeout(() => {
+              if (vm.editorMode()) {
+                _lastScrollPosition--; //hack for forcing fetching
+              }
+              if (
+                _lastScrollPosition !== scrollElement.scrollTop() &&
+                scrollElement.scrollTop() + scrollElement.outerHeight() + 20 >=
+                  scrollElement[0].scrollHeight &&
+                _dt &&
+                snippet.result.hasMore()
+              ) {
+                huePubSub.publish('editor.snippet.result.gray', snippet);
+                snippet.fetchResult(100, false);
               }
             }, 100);
           }
-        }
-        if (
-          !vm.editorMode() ||
-          (vm.editorMode() && snippet.currentQueryTab() === 'queryResults' && snippet.showGrid())
-        ) {
-          let _lastScrollPosition =
-            scrollElement.data('scrollPosition') != null ? scrollElement.data('scrollPosition') : 0;
-          window.clearTimeout(_scrollTimeout);
-          scrollElement.data('scrollPosition', scrollElement.scrollTop());
-          _scrollTimeout = window.setTimeout(() => {
-            if (vm.editorMode()) {
-              _lastScrollPosition--; //hack for forcing fetching
-            }
-            if (
-              _lastScrollPosition !== scrollElement.scrollTop() &&
-              scrollElement.scrollTop() + scrollElement.outerHeight() + 20 >=
-                scrollElement[0].scrollHeight &&
-              _dt &&
-              snippet.result.hasMore()
-            ) {
-              huePubSub.publish('editor.snippet.result.gray', snippet);
-              snippet.fetchResult(100, false);
-            }
-          }, 100);
-        }
-      };
-      scrollElement.data('scrollFnDtCreation', dataScroll);
-      scrollElement.on('scroll', dataScroll);
-      snippet.isResultSettingsVisible.subscribe(newValue => {
-        if (newValue) {
-          dataScroll();
-        }
-      });
+        };
+        scrollElement.data('scrollFnDtCreation', dataScroll);
+        scrollElement.on('scroll', dataScroll);
+        snippet.isResultSettingsVisible.subscribe(newValue => {
+          if (newValue) {
+            dataScroll();
+          }
+        });
 
-      if (!window.ENABLE_NOTEBOOK_2) {
         huePubSub.subscribeOnce('chart.hard.reset', () => {
-          // hard reset the default opened chart once
+          // hard reset once the default opened chart
           const oldChartX = snippet.chartX();
           snippet.chartX(null);
           window.setTimeout(() => {
             snippet.chartX(oldChartX);
           }, 0);
         });
+
+        return _dt;
+      };
+
+      if (ko.options) {
+        ko.options.deferUpdates = true;
       }
 
-      return _dt;
-    };
+      let viewModel;
 
-    if (ko.options) {
-      ko.options.deferUpdates = true;
-    }
+      const hideFixedHeaders = function() {
+        $('.jHueTableExtenderClonedContainer').hide();
+        $('.jHueTableExtenderClonedContainerColumn').hide();
+        $('.jHueTableExtenderClonedContainerCell').hide();
+        $('.fixed-header-row').hide();
+        $('.fixed-first-cell').hide();
+        $('.fixed-first-column').hide();
+      };
 
-    let viewModel;
-
-    const hideFixedHeaders = function() {
-      $('.jHueTableExtenderClonedContainer').hide();
-      $('.jHueTableExtenderClonedContainerColumn').hide();
-      $('.jHueTableExtenderClonedContainerCell').hide();
-      $('.fixed-header-row').hide();
-      $('.fixed-first-cell').hide();
-      $('.fixed-first-column').hide();
-    };
-
-    window.hideFixedHeaders = hideFixedHeaders;
-
-    // TODO: Remove with ENABLE_NOTEBOOK_2
-    let redrawTimeout = -1;
-    const redrawFixedHeaders = function(timeout) {
-      const renderer = function() {
-        if (!viewModel.selectedNotebook()) {
-          return;
-        }
-        viewModel
-          .selectedNotebook()
-          .snippets()
-          .forEach(snippet => {
-            if (snippet.result.meta().length > 0) {
-              const tableExtender = $('#snippet_' + snippet.id() + ' .resultTable').data(
-                'plugin_jHueTableExtender2'
-              );
-              if (typeof tableExtender !== 'undefined') {
-                tableExtender.repositionHeader();
-                tableExtender.drawLockedRows();
+      window.hideFixedHeaders = hideFixedHeaders;
+
+      let redrawTimeout = -1;
+      const redrawFixedHeaders = function(timeout) {
+        const renderer = function() {
+          if (!viewModel.selectedNotebook()) {
+            return;
+          }
+          viewModel
+            .selectedNotebook()
+            .snippets()
+            .forEach(snippet => {
+              if (snippet.result.meta().length > 0) {
+                const tableExtender = $('#snippet_' + snippet.id() + ' .resultTable').data(
+                  'plugin_jHueTableExtender2'
+                );
+                if (typeof tableExtender !== 'undefined') {
+                  tableExtender.repositionHeader();
+                  tableExtender.drawLockedRows();
+                }
+                $(window.MAIN_SCROLLABLE).data('lastScroll', $(window.MAIN_SCROLLABLE).scrollTop());
+                $(window.MAIN_SCROLLABLE).trigger('scroll');
               }
-              $(window.MAIN_SCROLLABLE).data('lastScroll', $(window.MAIN_SCROLLABLE).scrollTop());
-              $(window.MAIN_SCROLLABLE).trigger('scroll');
+            });
+          $('.jHueTableExtenderClonedContainer').show();
+          $('.jHueTableExtenderClonedContainerColumn').show();
+          $('.jHueTableExtenderClonedContainerCell').show();
+          $('.fixed-header-row').show();
+          $('.fixed-first-cell').show();
+          $('.fixed-first-column').show();
+        };
+
+        if (timeout) {
+          window.clearTimeout(redrawTimeout);
+          redrawTimeout = window.setTimeout(renderer, timeout);
+        } else {
+          renderer();
+        }
+      };
+      window.redrawFixedHeaders = redrawFixedHeaders;
+
+      const replaceAce = content => {
+        const snip = viewModel.selectedNotebook().snippets()[0];
+        if (snip) {
+          snip.statement_raw(content);
+          snip.result.statements_count(1);
+          snip.ace().setValue(content, 1);
+          snip.result.statement_range({
+            start: {
+              row: 0,
+              column: 0
+            },
+            end: {
+              row: 0,
+              column: 0
             }
           });
-        $('.jHueTableExtenderClonedContainer').show();
-        $('.jHueTableExtenderClonedContainerColumn').show();
-        $('.jHueTableExtenderClonedContainerCell').show();
-        $('.fixed-header-row').show();
-        $('.fixed-first-cell').show();
-        $('.fixed-first-column').show();
+          snip.ace()._emit('focus');
+        }
+        hideHoverMsg(viewModel);
+        redrawFixedHeaders(200);
       };
+      window.replaceAce = replaceAce;
 
-      if (timeout) {
-        window.clearTimeout(redrawTimeout);
-        redrawTimeout = window.setTimeout(renderer, timeout);
-      } else {
-        renderer();
-      }
-    };
-    window.redrawFixedHeaders = redrawFixedHeaders;
-
-    const replaceAce = content => {
-      const snip = viewModel.selectedNotebook().snippets()[0];
-      if (snip) {
-        snip.statement_raw(content);
-        snip.result.statements_count(1);
-        snip.ace().setValue(content, 1);
-        snip.result.statement_range({
-          start: {
-            row: 0,
-            column: 0
-          },
-          end: {
-            row: 0,
-            column: 0
-          }
-        });
-        snip.ace()._emit('focus');
-      }
-      hideHoverMsg(viewModel);
-      redrawFixedHeaders(200);
-    };
-    window.replaceAce = replaceAce;
-
-    // Drag and drop iPython / Zeppelin notebooks
-    if (window.FileReader) {
-      let aceChecks = 0;
-
-      const addAce = (content, snippetType) => {
-        const snip = viewModel
-          .selectedNotebook()
-          .addSnippet({ type: snippetType, result: {} }, true);
-        snip.statement_raw(content);
-        aceChecks++;
-        snip.checkForAce = window.setInterval(() => {
-          if (snip.ace()) {
-            window.clearInterval(snip.checkForAce);
-            aceChecks--;
-            if (aceChecks === 0) {
-              hideHoverMsg(viewModel);
-              redrawFixedHeaders(200);
+      // Drag and drop iPython / Zeppelin notebooks
+      if (window.FileReader) {
+        let aceChecks = 0;
+
+        const addAce = (content, snippetType) => {
+          const snip = viewModel
+            .selectedNotebook()
+            .addSnippet({ type: snippetType, result: {} }, true);
+          snip.statement_raw(content);
+          aceChecks++;
+          snip.checkForAce = window.setInterval(() => {
+            if (snip.ace()) {
+              window.clearInterval(snip.checkForAce);
+              aceChecks--;
+              if (aceChecks === 0) {
+                hideHoverMsg(viewModel);
+                redrawFixedHeaders(200);
+              }
             }
-          }
-        }, 100);
-      };
+          }, 100);
+        };
 
-      const addMarkdown = content => {
-        const snip = viewModel
-          .selectedNotebook()
-          .addSnippet({ type: 'markdown', result: {} }, true);
-        snip.statement_raw(content);
-      };
+        const addMarkdown = content => {
+          const snip = viewModel
+            .selectedNotebook()
+            .addSnippet({ type: 'markdown', result: {} }, true);
+          snip.statement_raw(content);
+        };
 
-      const addPySpark = content => {
-        addAce(content, 'pyspark');
-      };
+        const addPySpark = content => {
+          addAce(content, 'pyspark');
+        };
 
-      const addSql = content => {
-        addAce(content, 'hive');
-      };
+        const addSql = content => {
+          addAce(content, 'hive');
+        };
 
-      const addScala = content => {
-        addAce(content, 'spark');
-      };
+        const addScala = content => {
+          addAce(content, 'spark');
+        };
 
-      const parseExternalJSON = raw => {
-        try {
-          if (viewModel.editorMode()) {
-            replaceAce(raw);
-          } else {
-            const loaded = typeof raw == 'string' ? JSON.parse(raw) : raw;
-            if (loaded.nbformat) {
-              //ipython
-              let cells = [];
-              if (loaded.nbformat === 3) {
-                cells = loaded.worksheets[0].cells;
-              } else if (loaded.nbformat === 4) {
-                cells = loaded.cells;
-              }
-              cells.forEach((cell, cellCnt) => {
-                window.setTimeout(() => {
-                  if (cell.cell_type === 'code') {
-                    if (loaded.nbformat === 3) {
-                      addPySpark($.isArray(cell.input) ? cell.input.join('') : cell.input);
-                    } else {
-                      addPySpark($.isArray(cell.source) ? cell.source.join('') : cell.source);
+        const parseExternalJSON = raw => {
+          try {
+            if (viewModel.editorMode()) {
+              replaceAce(raw);
+            } else {
+              const loaded = typeof raw == 'string' ? JSON.parse(raw) : raw;
+              if (loaded.nbformat) {
+                //ipython
+                let cells = [];
+                if (loaded.nbformat === 3) {
+                  cells = loaded.worksheets[0].cells;
+                } else if (loaded.nbformat === 4) {
+                  cells = loaded.cells;
+                }
+                cells.forEach((cell, cellCnt) => {
+                  window.setTimeout(() => {
+                    if (cell.cell_type === 'code') {
+                      if (loaded.nbformat === 3) {
+                        addPySpark($.isArray(cell.input) ? cell.input.join('') : cell.input);
+                      } else {
+                        addPySpark($.isArray(cell.source) ? cell.source.join('') : cell.source);
+                      }
                     }
-                  }
-                  if (cell.cell_type === 'heading') {
-                    let heading = $.isArray(cell.source) ? cell.source.join('') : cell.source;
-                    if (cell.level === 1) {
-                      heading += '\n====================';
-                    } else if (cell.level === 2) {
-                      heading += '\n--------------------';
-                    } else {
-                      heading = '### ' + heading;
+                    if (cell.cell_type === 'heading') {
+                      let heading = $.isArray(cell.source) ? cell.source.join('') : cell.source;
+                      if (cell.level === 1) {
+                        heading += '\n====================';
+                      } else if (cell.level === 2) {
+                        heading += '\n--------------------';
+                      } else {
+                        heading = '### ' + heading;
+                      }
+                      addMarkdown(heading);
                     }
-                    addMarkdown(heading);
-                  }
-                  if (cell.cell_type === 'markdown') {
-                    addMarkdown($.isArray(cell.source) ? cell.source.join('') : cell.source);
-                  }
-                  if (cellCnt === cells.length - 1 && aceChecks === 0) {
-                    hideHoverMsg(viewModel);
-                  }
-                }, 10);
-              });
-            }
-
-            if (loaded.paragraphs) {
-              //zeppelin
-              if (loaded.name) {
-                viewModel.selectedNotebook().name(loaded.name);
+                    if (cell.cell_type === 'markdown') {
+                      addMarkdown($.isArray(cell.source) ? cell.source.join('') : cell.source);
+                    }
+                    if (cellCnt === cells.length - 1 && aceChecks === 0) {
+                      hideHoverMsg(viewModel);
+                    }
+                  }, 10);
+                });
               }
-              loaded.paragraphs.forEach(paragraph => {
-                if (paragraph.text) {
-                  const content = paragraph.text.split('\n');
-                  if (content[0].indexOf('%md') > -1) {
-                    content.shift();
-                    addMarkdown(content.join('\n'));
-                  } else if (content[0].indexOf('%sql') > -1 || content[0].indexOf('%hive') > -1) {
-                    content.shift();
-                    addSql(content.join('\n'));
-                  } else if (content[0].indexOf('%pyspark') > -1) {
-                    content.shift();
-                    addPySpark(content.join('\n'));
-                  } else {
-                    if (content[0].indexOf('%spark') > -1) {
+
+              if (loaded.paragraphs) {
+                //zeppelin
+                if (loaded.name) {
+                  viewModel.selectedNotebook().name(loaded.name);
+                }
+                loaded.paragraphs.forEach(paragraph => {
+                  if (paragraph.text) {
+                    const content = paragraph.text.split('\n');
+                    if (content[0].indexOf('%md') > -1) {
+                      content.shift();
+                      addMarkdown(content.join('\n'));
+                    } else if (
+                      content[0].indexOf('%sql') > -1 ||
+                      content[0].indexOf('%hive') > -1
+                    ) {
                       content.shift();
+                      addSql(content.join('\n'));
+                    } else if (content[0].indexOf('%pyspark') > -1) {
+                      content.shift();
+                      addPySpark(content.join('\n'));
+                    } else {
+                      if (content[0].indexOf('%spark') > -1) {
+                        content.shift();
+                      }
+                      addScala(content.join('\n'));
                     }
-                    addScala(content.join('\n'));
                   }
-                }
-              });
+                });
+              }
             }
+          } catch (e) {
+            hideHoverMsg(viewModel);
+            replaceAce(raw);
           }
-        } catch (e) {
-          hideHoverMsg(viewModel);
-          replaceAce(raw);
-        }
-      };
-
-      const handleFileSelect = function(evt) {
-        evt.stopPropagation();
-        evt.preventDefault();
-        const dt = evt.dataTransfer;
-        const files = dt.files;
-        if (files.length > 0) {
-          showHoverMsg();
-        } else {
-          hideHoverMsg(viewModel);
-        }
-
-        for (let i = 0, f; (f = files[i]); i++) {
-          const reader = new FileReader();
-          reader.onload = (function(file) {
-            return function(e) {
-              $('.hoverText').html("<i class='fa fa-spinner fa-spin'></i>");
-              parseExternalJSON(e.target.result);
-            };
-          })(f);
-          reader.readAsText(f);
-        }
-      };
-
-      const handleDragOver = function(evt) {
-        evt.stopPropagation();
-        evt.preventDefault();
-        evt.dataTransfer.dropEffect = 'copy';
-      };
-
-      const dropZone = $(window.EDITOR_BINDABLE_ELEMENT)[0];
-      dropZone.addEventListener('dragenter', showHoverMsg, false);
-      dropZone.addEventListener('dragover', handleDragOver, false);
-      dropZone.addEventListener('drop', handleFileSelect, false);
-
-      let isDraggingOverText = false;
-
-      $(window.EDITOR_BINDABLE_ELEMENT)
-        .find('.hoverText')
-        .on('dragenter', e => {
-          e.preventDefault();
-          e.stopPropagation();
-          e.stopImmediatePropagation();
-          isDraggingOverText = true;
-        });
-
-      $(window.EDITOR_BINDABLE_ELEMENT)
-        .find('.hoverText')
-        .on('dragleave', e => {
-          e.preventDefault();
-          e.stopPropagation();
-          e.stopImmediatePropagation();
-          isDraggingOverText = false;
-        });
+        };
 
-      $(window.EDITOR_BINDABLE_ELEMENT)
-        .find('.hoverMsg')
-        .on('dragleave', e => {
-          if (!isDraggingOverText) {
+        const handleFileSelect = function(evt) {
+          evt.stopPropagation();
+          evt.preventDefault();
+          const dt = evt.dataTransfer;
+          const files = dt.files;
+          if (files.length > 0) {
+            showHoverMsg();
+          } else {
             hideHoverMsg(viewModel);
           }
-        });
-    }
 
-    if (window.EDITOR_ENABLE_QUERY_SCHEDULING) {
-      viewModel = new window.EditorViewModel(
-        window.EDITOR_ID,
-        window.NOTEBOOKS_JSON,
-        window.EDITOR_VIEW_MODEL_OPTIONS,
-        window.CoordinatorEditorViewModel,
-        window.RunningCoordinatorModel
-      );
-    } else {
-      viewModel = new window.EditorViewModel(
-        window.EDITOR_ID,
-        window.NOTEBOOKS_JSON,
-        window.EDITOR_VIEW_MODEL_OPTIONS
-      );
-    }
-    ko.applyBindings(viewModel, $(window.EDITOR_BINDABLE_ELEMENT)[0]);
-    viewModel.init();
+          for (let i = 0, f; (f = files[i]); i++) {
+            const reader = new FileReader();
+            reader.onload = (function(file) {
+              return function(e) {
+                $('.hoverText').html("<i class='fa fa-spinner fa-spin'></i>");
+                parseExternalJSON(e.target.result);
+              };
+            })(f);
+            reader.readAsText(f);
+          }
+        };
 
-    sqlWorkerHandler.registerWorkers();
+        const handleDragOver = function(evt) {
+          evt.stopPropagation();
+          evt.preventDefault();
+          evt.dataTransfer.dropEffect = 'copy';
+        };
 
-    viewModel.selectedNotebook.subscribe(newVal => {
-      huePubSub.publish('selected.notebook.changed', newVal);
-    });
+        const dropZone = $(window.EDITOR_BINDABLE_ELEMENT)[0];
+        dropZone.addEventListener('dragenter', showHoverMsg, false);
+        dropZone.addEventListener('dragover', handleDragOver, false);
+        dropZone.addEventListener('drop', handleFileSelect, false);
 
-    let wasResultFullScreenMode = false;
-    let isAssistAvailable = viewModel.assistAvailable();
-    let wasLeftPanelVisible = viewModel.isLeftPanelVisible();
-    let wasRightPanelVisible = viewModel.isRightPanelVisible();
+        let isDraggingOverText = false;
 
-    const exitPlayerMode = () => {
-      if (!wasResultFullScreenMode) {
-        viewModel.selectedNotebook().isPresentationMode(false);
-      } else {
-        viewModel.isResultFullScreenMode(false);
-      }
-      wasResultFullScreenMode = false;
-    };
-
-    viewModel.isResultFullScreenMode.subscribe(newValue => {
-      wasResultFullScreenMode = newValue;
-      huePubSub.publish('editor.presentation.operate.toggle', newValue);
-    });
-
-    // TODO: Remove with ENABLE_NOTEBOOK_2
-    viewModel.isLeftPanelVisible.subscribe(value => {
-      redrawFixedHeaders(200);
-    });
-
-    // Close the notebook snippets when leaving the page
-    window.onbeforeunload = function(e) {
-      if (!viewModel.selectedNotebook().avoidClosing) {
-        viewModel.selectedNotebook().close();
-      }
-    };
-    $(window).data('beforeunload', window.onbeforeunload);
+        $(window.EDITOR_BINDABLE_ELEMENT)
+          .find('.hoverText')
+          .on('dragenter', e => {
+            e.preventDefault();
+            e.stopPropagation();
+            e.stopImmediatePropagation();
+            isDraggingOverText = true;
+          });
 
-    $('.preview-sample').css('right', 10 + hueUtils.scrollbarWidth() + 'px');
+        $(window.EDITOR_BINDABLE_ELEMENT)
+          .find('.hoverText')
+          .on('dragleave', e => {
+            e.preventDefault();
+            e.stopPropagation();
+            e.stopImmediatePropagation();
+            isDraggingOverText = false;
+          });
 
-    const saveKeyHandler = () => {
-      if (viewModel.canSave()) {
-        viewModel.saveNotebook();
-      } else {
-        $('#saveAsModal' + window.EDITOR_SUFFIX).modal('show');
+        $(window.EDITOR_BINDABLE_ELEMENT)
+          .find('.hoverMsg')
+          .on('dragleave', e => {
+            if (!isDraggingOverText) {
+              hideHoverMsg(viewModel);
+            }
+          });
       }
-    };
 
-    const newKeyHandler = () => {
-      if (!viewModel.editorMode()) {
-        viewModel.selectedNotebook().newSnippet();
+      if (window.EDITOR_ENABLE_QUERY_SCHEDULING) {
+        viewModel = new window.EditorViewModel(
+          window.EDITOR_ID,
+          window.NOTEBOOKS_JSON,
+          window.EDITOR_VIEW_MODEL_OPTIONS,
+          window.CoordinatorEditorViewModel,
+          window.RunningCoordinatorModel
+        );
       } else {
-        viewModel.newNotebook(
-          viewModel.editorType(),
-          null,
-          viewModel.selectedNotebook()
-            ? viewModel
-                .selectedNotebook()
-                .snippets()[0]
-                .currentQueryTab()
-            : null
+        viewModel = new window.EditorViewModel(
+          window.EDITOR_ID,
+          window.NOTEBOOKS_JSON,
+          window.EDITOR_VIEW_MODEL_OPTIONS
         );
       }
-    };
+      ko.applyBindings(viewModel, $(window.EDITOR_BINDABLE_ELEMENT)[0]);
+      viewModel.init();
 
-    const initKeydownBindings = () => {
-      $(window).bind('keydown.editor', 'ctrl+s alt+s meta+s', e => {
-        e.preventDefault();
-        saveKeyHandler();
-        return false;
-      });
-      $(window).bind('keydown.editor', 'ctrl+shift+p alt+shift+p meta+shift+p', e => {
-        e.preventDefault();
-        huePubSub.publish('editor.presentation.toggle');
-        return false;
-      });
-      $(window).bind('keydown.editor', 'ctrl+e alt+e meta+e', e => {
-        e.preventDefault();
-        newKeyHandler();
-        return false;
-      });
-    };
+      sqlWorkerHandler.registerWorkers();
 
-    if (document.location.href.indexOf('editor') >= 0) {
-      initKeydownBindings();
-    }
+      viewModel.selectedNotebook.subscribe(newVal => {
+        huePubSub.publish('selected.notebook.changed', newVal);
+      });
 
-    $(document).bind('keyup', e => {
-      if (
-        e.keyCode === 191 &&
-        e.shiftKey &&
-        !$(e.target).is('input') &&
-        !$(e.target).is('textarea')
-      ) {
-        $('#helpModal' + window.EDITOR_SUFFIX).modal('show');
-      }
+      let wasResultFullScreenMode = false;
+      let isAssistAvailable = viewModel.assistAvailable();
+      let wasLeftPanelVisible = viewModel.isLeftPanelVisible();
+      let wasRightPanelVisible = viewModel.isRightPanelVisible();
 
-      if (
-        e.keyCode === 191 &&
-        !e.shiftKey &&
-        !$(e.target).is('input') &&
-        !$(e.target).is('textarea')
-      ) {
-        if (
-          viewModel.editorMode() &&
-          viewModel
-            .selectedNotebook()
-            .snippets()[0]
-            .currentQueryTab() === 'queryResults'
-        ) {
-          e.preventDefault();
-          const $t = $(
-            '#snippet_' +
-              viewModel
-                .selectedNotebook()
-                .snippets()[0]
-                .id()
-          ).find('.resultTable');
-          $t.hueDataTable().fnShowSearch();
-          return false;
+      const exitPlayerMode = () => {
+        if (!wasResultFullScreenMode) {
+          viewModel.selectedNotebook().isPresentationMode(false);
+        } else {
+          viewModel.isResultFullScreenMode(false);
         }
-      }
-    });
+        wasResultFullScreenMode = false;
+      };
 
-    let initialResizePosition = 100;
+      viewModel.isResultFullScreenMode.subscribe(newValue => {
+        wasResultFullScreenMode = newValue;
+        huePubSub.publish('editor.presentation.operate.toggle', newValue);
+      });
 
-    const draggableHelper = (el, e, ui, setSize) => {
-      const _snippet = ko.dataFor(el.parents('.snippet')[0]);
-      const _cm = $('#snippet_' + _snippet.id()).data('editor');
-      const _newSize = _snippet.aceSize() + (ui.offset.top - initialResizePosition);
-      _cm.setSize('99%', _newSize);
-      if (setSize) {
-        _snippet.aceSize(_newSize);
-      }
-    };
+      viewModel.isLeftPanelVisible.subscribe(value => {
+        redrawFixedHeaders(200);
+      });
 
-    const getDraggableOptions = minY => {
-      return {
-        axis: 'y',
-        start: function(e, ui) {
-          initialResizePosition = ui.offset.top;
-        },
-        drag: function(e, ui) {
-          draggableHelper($(this), e, ui);
-          $('.jHueTableExtenderClonedContainer').hide();
-          $('.jHueTableExtenderClonedContainerColumn').hide();
-          $('.jHueTableExtenderClonedContainerCell').hide();
-          $('.fixed-header-row').hide();
-          $('.fixed-first-cell').hide();
-          $('.fixed-first-column').hide();
-        },
-        stop: function(e, ui) {
-          $('.jHueTableExtenderClonedContainer').show();
-          $('.jHueTableExtenderClonedContainerColum').show();
-          $('.jHueTableExtenderClonedContainerCell').show();
-          $('.fixed-header-row').show();
-          $('.fixed-first-cell').show();
-          $('.fixed-first-column').show();
-          draggableHelper($(this), e, ui, true);
-          redrawFixedHeaders();
-          ui.helper.first().removeAttr('style');
-        },
-        containment: [0, minY, 4000, minY + 400]
+      // Close the notebook snippets when leaving the page
+      window.onbeforeunload = function(e) {
+        if (!viewModel.selectedNotebook().avoidClosing) {
+          viewModel.selectedNotebook().close();
+        }
       };
-    };
-
-    $('.resize-panel a').each(function() {
-      $(this).draggable(
-        getDraggableOptions(
-          $(this)
-            .parents('.snippet')
-            .offset().top + 128
-        )
-      );
-    });
-
-    // TODO: Remove with ENABLE_NOTEBOOK_2
-    const resetResultsResizer = snippet => {
-      const $snippet = $('#snippet_' + snippet.id());
-      $snippet
-        .find('.table-results .column-side')
-        .width(hueUtils.bootstrapRatios.span3() + '%')
-        .data('newWidth', hueUtils.bootstrapRatios.span3());
-      if (snippet.isResultSettingsVisible()) {
-        $snippet
-          .find('.table-results .grid-side')
-          .data('newWidth', hueUtils.bootstrapRatios.span9())
-          .width(hueUtils.bootstrapRatios.span9() + '%');
-      } else {
-        $snippet
-          .find('.table-results .grid-side')
-          .data('newWidth', 100)
-          .width('100%');
-      }
-      $snippet.find('.resize-bar').css('left', '');
-      try {
-        $snippet.find('.resize-bar').draggable('destroy');
-      } catch (e) {}
+      $(window).data('beforeunload', window.onbeforeunload);
 
-      let initialPosition = 0;
+      $('.preview-sample').css('right', 10 + hueUtils.scrollbarWidth() + 'px');
 
-      $snippet.find('.resize-bar').draggable({
-        axis: 'x',
-        containment: $snippet.find('.table-results'),
-        create: function() {
-          const $snip = $('#snippet_' + snippet.id());
-          initialPosition = $snip.find('.resize-bar').position().left;
-          $snip
-            .find('.table-results .column-side')
-            .data('newWidth', hueUtils.bootstrapRatios.span3());
-          $snip.find('.meta-filter').width($snip.find('.table-results .column-side').width() - 28);
-        },
-        drag: function(event, ui) {
-          const $snip = $('#snippet_' + snippet.id());
-          if (initialPosition === 0) {
-            initialPosition = $snip.find('.resize-bar').position().left;
-          }
-          ui.position.left = Math.max(150, ui.position.left);
-          const newSpan3Width =
-            (ui.position.left * hueUtils.bootstrapRatios.span3()) / initialPosition;
-          const newSpan9Width = 100 - newSpan3Width - hueUtils.bootstrapRatios.margin();
-          $snip
-            .find('.table-results .column-side')
-            .width(newSpan3Width + '%')
-            .data('newWidth', newSpan3Width);
-          $snip
-            .find('.table-results .grid-side')
-            .width(newSpan9Width + '%')
-            .data('newWidth', newSpan9Width);
-          $snip.find('.meta-filter').width($snip.find('.table-results .column-side').width() - 28);
-        },
-        stop: function() {
-          redrawFixedHeaders();
-          huePubSub.publish('resize.leaflet.map');
+      const saveKeyHandler = () => {
+        if (viewModel.canSave()) {
+          viewModel.saveNotebook();
+        } else {
+          $('#saveAsModal' + window.EDITOR_SUFFIX).modal('show');
         }
-      });
-    };
-
-    // TODO: Remove with ENABLE_NOTEBOOK_2
-    const resizeToggleResultSettings = (snippet, initial) => {
-      let _dtElement;
-      const $snip = $('#snippet_' + snippet.id());
-      if (snippet.showGrid()) {
-        _dtElement = $snip.find('.dataTables_wrapper');
-        const topCoord =
-          viewModel.isPresentationMode() || viewModel.isResultFullScreenMode()
-            ? window.BANNER_TOP_HTML
-              ? 31
-              : 1
-            : 73;
-        $snip.find('.snippet-grid-settings').css({
-          height:
-            viewModel.isPresentationMode() || !viewModel.editorMode()
-              ? '330px'
-              : Math.ceil(
-                  $(window).height() -
-                    Math.max(
-                      $('.result-settings').length > 0 ? $('.result-settings').offset().top : 0,
-                      topCoord
-                    )
-                ) + 'px'
-        });
-      } else {
-        _dtElement = $snip.find('.chart:visible');
-      }
-      if (_dtElement.length === 0) {
-        _dtElement = $snip.find('.table-results');
-      }
-      _dtElement
-        .parents('.snippet-body')
-        .find('.toggle-result-settings')
-        .css({
-          height: _dtElement.height() - 30 + 'px',
-          'line-height': _dtElement.height() - 30 + 'px'
+      };
+
+      const newKeyHandler = () => {
+        if (!viewModel.editorMode()) {
+          viewModel.selectedNotebook().newSnippet();
+        } else {
+          viewModel.newNotebook(
+            viewModel.editorType(),
+            null,
+            viewModel.selectedNotebook()
+              ? viewModel
+                  .selectedNotebook()
+                  .snippets()[0]
+                  .currentQueryTab()
+              : null
+          );
+        }
+      };
+
+      const initKeydownBindings = () => {
+        $(window).bind('keydown.editor', 'ctrl+s alt+s meta+s', e => {
+          e.preventDefault();
+          saveKeyHandler();
+          return false;
         });
-      if (initial) {
-        $snip.find('.result-settings').css({
-          marginTop: 0
+        $(window).bind('keydown.editor', 'ctrl+shift+p alt+shift+p meta+shift+p', e => {
+          e.preventDefault();
+          huePubSub.publish('editor.presentation.toggle');
+          return false;
         });
-        $snip.find('.snippet-actions').css({
-          marginTop: 0
+        $(window).bind('keydown.editor', 'ctrl+e alt+e meta+e', e => {
+          e.preventDefault();
+          newKeyHandler();
+          return false;
         });
-        huePubSub.publish('resize.leaflet.map');
-      }
-    };
-
-    const forceChartDraws = initial => {
-      if (viewModel.selectedNotebook()) {
-        viewModel
-          .selectedNotebook()
-          .snippets()
-          .forEach(snippet => {
-            if (snippet.result.data().length > 0) {
-              let _elCheckerInterval = -1;
-              const _el = $('#snippet_' + snippet.id());
-              _elCheckerInterval = window.setInterval(() => {
-                if (_el.find('.resultTable').length > 0) {
-                  try {
-                    resizeToggleResultSettings(snippet, initial);
-                    resetResultsResizer(snippet);
-                    $(document).trigger('forceChartDraw', snippet);
-                  } catch (e) {}
-                  window.clearInterval(_elCheckerInterval);
-                }
-              }, 200);
-            }
-          });
+      };
+
+      if (document.location.href.indexOf('editor') >= 0) {
+        initKeydownBindings();
       }
-    };
 
-    forceChartDraws(true);
+      $(document).bind('keyup', e => {
+        if (
+          e.keyCode === 191 &&
+          e.shiftKey &&
+          !$(e.target).is('input') &&
+          !$(e.target).is('textarea')
+        ) {
+          $('#helpModal' + window.EDITOR_SUFFIX).modal('show');
+        }
 
-    // ======== PubSub ========
+        if (
+          e.keyCode === 191 &&
+          !e.shiftKey &&
+          !$(e.target).is('input') &&
+          !$(e.target).is('textarea')
+        ) {
+          if (
+            viewModel.editorMode() &&
+            viewModel
+              .selectedNotebook()
+              .snippets()[0]
+              .currentQueryTab() === 'queryResults'
+          ) {
+            e.preventDefault();
+            const $t = $(
+              '#snippet_' +
+                viewModel
+                  .selectedNotebook()
+                  .snippets()[0]
+                  .id()
+            ).find('.resultTable');
+            $t.hueDataTable().fnShowSearch();
+            return false;
+          }
+        }
+      });
 
-    let splitDraggableTimeout = -1;
-    huePubSub.subscribe(
-      'split.draggable.position',
-      () => {
-        window.clearTimeout(splitDraggableTimeout);
-        splitDraggableTimeout = window.setTimeout(() => {
-          redrawFixedHeaders(100);
-        }, 200);
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    // TODO: Remove with ENABLE_NOTEBOOK_2?
-    huePubSub.subscribe(
-      'redraw.fixed.headers',
-      () => {
-        hideFixedHeaders();
-        redrawFixedHeaders(200);
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'app.gained.focus',
-      app => {
-        if (app === 'editor') {
-          huePubSub.publish('redraw.fixed.headers');
-          huePubSub.publish('hue.scrollleft.show');
-          huePubSub.publish('active.snippet.type.changed', {
-            type: viewModel.editorType(),
-            isSqlDialect: viewModel.getSnippetViewSettings(viewModel.editorType()).sqlDialect
-          });
+      let initialResizePosition = 100;
+
+      const draggableHelper = (el, e, ui, setSize) => {
+        const _snippet = ko.dataFor(el.parents('.snippet')[0]);
+        const _cm = $('#snippet_' + _snippet.id()).data('editor');
+        const _newSize = _snippet.aceSize() + (ui.offset.top - initialResizePosition);
+        _cm.setSize('99%', _newSize);
+        if (setSize) {
+          _snippet.aceSize(_newSize);
         }
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'current.query.tab.switched',
-      tab => {
-        if (tab !== 'queryResults') {
-          $('.hue-datatable-search').hide();
+      };
+
+      const getDraggableOptions = minY => {
+        return {
+          axis: 'y',
+          start: function(e, ui) {
+            initialResizePosition = ui.offset.top;
+          },
+          drag: function(e, ui) {
+            draggableHelper($(this), e, ui);
+            $('.jHueTableExtenderClonedContainer').hide();
+            $('.jHueTableExtenderClonedContainerColumn').hide();
+            $('.jHueTableExtenderClonedContainerCell').hide();
+            $('.fixed-header-row').hide();
+            $('.fixed-first-cell').hide();
+            $('.fixed-first-column').hide();
+          },
+          stop: function(e, ui) {
+            $('.jHueTableExtenderClonedContainer').show();
+            $('.jHueTableExtenderClonedContainerColum').show();
+            $('.jHueTableExtenderClonedContainerCell').show();
+            $('.fixed-header-row').show();
+            $('.fixed-first-cell').show();
+            $('.fixed-first-column').show();
+            draggableHelper($(this), e, ui, true);
+            redrawFixedHeaders();
+            ui.helper.first().removeAttr('style');
+          },
+          containment: [0, minY, 4000, minY + 400]
+        };
+      };
+
+      $('.resize-panel a').each(function() {
+        $(this).draggable(
+          getDraggableOptions(
+            $(this)
+              .parents('.snippet')
+              .offset().top + 128
+          )
+        );
+      });
+
+      const resetResultsResizer = snippet => {
+        const $snippet = $('#snippet_' + snippet.id());
+        $snippet
+          .find('.table-results .column-side')
+          .width(hueUtils.bootstrapRatios.span3() + '%')
+          .data('newWidth', hueUtils.bootstrapRatios.span3());
+        if (snippet.isResultSettingsVisible()) {
+          $snippet
+            .find('.table-results .grid-side')
+            .data('newWidth', hueUtils.bootstrapRatios.span9())
+            .width(hueUtils.bootstrapRatios.span9() + '%');
+        } else {
+          $snippet
+            .find('.table-results .grid-side')
+            .data('newWidth', 100)
+            .width('100%');
         }
-        if (tab === 'queryHistory') {
-          hueUtils.waitForRendered(
-            $('#queryHistory .history-table'),
-            el => {
-              return el.is(':visible');
-            },
-            () => {
-              viewModel.selectedNotebook().forceHistoryInitialHeight(true);
-              huePubSub.publish('editor.calculate.history.height');
+        $snippet.find('.resize-bar').css('left', '');
+        try {
+          $snippet.find('.resize-bar').draggable('destroy');
+        } catch (e) {}
+
+        let initialPosition = 0;
+
+        $snippet.find('.resize-bar').draggable({
+          axis: 'x',
+          containment: $snippet.find('.table-results'),
+          create: function() {
+            const $snip = $('#snippet_' + snippet.id());
+            initialPosition = $snip.find('.resize-bar').position().left;
+            $snip
+              .find('.table-results .column-side')
+              .data('newWidth', hueUtils.bootstrapRatios.span3());
+            $snip
+              .find('.meta-filter')
+              .width($snip.find('.table-results .column-side').width() - 28);
+          },
+          drag: function(event, ui) {
+            const $snip = $('#snippet_' + snippet.id());
+            if (initialPosition === 0) {
+              initialPosition = $snip.find('.resize-bar').position().left;
             }
-          );
+            ui.position.left = Math.max(150, ui.position.left);
+            const newSpan3Width =
+              (ui.position.left * hueUtils.bootstrapRatios.span3()) / initialPosition;
+            const newSpan9Width = 100 - newSpan3Width - hueUtils.bootstrapRatios.margin();
+            $snip
+              .find('.table-results .column-side')
+              .width(newSpan3Width + '%')
+              .data('newWidth', newSpan3Width);
+            $snip
+              .find('.table-results .grid-side')
+              .width(newSpan9Width + '%')
+              .data('newWidth', newSpan9Width);
+            $snip
+              .find('.meta-filter')
+              .width($snip.find('.table-results .column-side').width() - 28);
+          },
+          stop: function() {
+            redrawFixedHeaders();
+            huePubSub.publish('resize.leaflet.map');
+          }
+        });
+      };
+
+      const resizeToggleResultSettings = (snippet, initial) => {
+        let _dtElement;
+        const $snip = $('#snippet_' + snippet.id());
+        if (snippet.showGrid()) {
+          _dtElement = $snip.find('.dataTables_wrapper');
+          const topCoord =
+            viewModel.isPresentationMode() || viewModel.isResultFullScreenMode()
+              ? window.BANNER_TOP_HTML
+                ? 31
+                : 1
+              : 73;
+          $snip.find('.snippet-grid-settings').css({
+            height:
+              viewModel.isPresentationMode() || !viewModel.editorMode()
+                ? '330px'
+                : Math.ceil(
+                    $(window).height() -
+                      Math.max(
+                        $('.result-settings').length > 0 ? $('.result-settings').offset().top : 0,
+                        topCoord
+                      )
+                  ) + 'px'
+          });
+        } else {
+          _dtElement = $snip.find('.chart:visible');
         }
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'detach.scrolls',
-      snippet => {
-        let scrollElement = $('#snippet_' + snippet.id()).find('.dataTables_wrapper');
-        if (viewModel.editorMode()) {
-          scrollElement = $(window.MAIN_SCROLLABLE);
+        if (_dtElement.length === 0) {
+          _dtElement = $snip.find('.table-results');
         }
-        if (scrollElement.data('scrollFnDt')) {
-          scrollElement.off('scroll', scrollElement.data('scrollFnDt'));
+        _dtElement
+          .parents('.snippet-body')
+          .find('.toggle-result-settings')
+          .css({
+            height: _dtElement.height() - 30 + 'px',
+            'line-height': _dtElement.height() - 30 + 'px'
+          });
+        if (initial) {
+          $snip.find('.result-settings').css({
+            marginTop: 0
+          });
+          $snip.find('.snippet-actions').css({
+            marginTop: 0
+          });
+          huePubSub.publish('resize.leaflet.map');
         }
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
+      };
 
-    huePubSub.subscribe(
-      'editor.calculate.history.height',
-      () => {
-        if (
-          viewModel.editorMode() &&
-          (viewModel.selectedNotebook().historyInitialHeight() === 0 ||
-            viewModel.selectedNotebook().forceHistoryInitialHeight())
-        ) {
-          let h = $('#queryHistory .history-table').height();
-          if (h === 0) {
-            h = viewModel.selectedNotebook().history().length * 32;
+      const forceChartDraws = initial => {
+        if (viewModel.selectedNotebook()) {
+          viewModel
+            .selectedNotebook()
+            .snippets()
+            .forEach(snippet => {
+              if (snippet.result.data().length > 0) {
+                let _elCheckerInterval = -1;
+                const _el = $('#snippet_' + snippet.id());
+                _elCheckerInterval = window.setInterval(() => {
+                  if (_el.find('.resultTable').length > 0) {
+                    try {
+                      resizeToggleResultSettings(snippet, initial);
+                      resetResultsResizer(snippet);
+                      $(document).trigger('forceChartDraw', snippet);
+                    } catch (e) {}
+                    window.clearInterval(_elCheckerInterval);
+                  }
+                }, 200);
+              }
+            });
+        }
+      };
+
+      forceChartDraws(true);
+
+      // ======== PubSub ========
+
+      let splitDraggableTimeout = -1;
+      huePubSub.subscribe(
+        'split.draggable.position',
+        () => {
+          window.clearTimeout(splitDraggableTimeout);
+          splitDraggableTimeout = window.setTimeout(() => {
+            redrawFixedHeaders(100);
+          }, 200);
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'redraw.fixed.headers',
+        () => {
+          hideFixedHeaders();
+          redrawFixedHeaders(200);
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'app.gained.focus',
+        app => {
+          if (app === 'editor') {
+            huePubSub.publish('redraw.fixed.headers');
+            huePubSub.publish('hue.scrollleft.show');
+            huePubSub.publish('active.snippet.type.changed', {
+              type: viewModel.editorType(),
+              isSqlDialect: viewModel.getSnippetViewSettings(viewModel.editorType()).sqlDialect
+            });
+          }
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'current.query.tab.switched',
+        tab => {
+          if (tab !== 'queryResults') {
+            $('.hue-datatable-search').hide();
+          }
+          if (tab === 'queryHistory') {
+            hueUtils.waitForRendered(
+              $('#queryHistory .history-table'),
+              el => {
+                return el.is(':visible');
+              },
+              () => {
+                viewModel.selectedNotebook().forceHistoryInitialHeight(true);
+                huePubSub.publish('editor.calculate.history.height');
+              }
+            );
+          }
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'detach.scrolls',
+        snippet => {
+          let scrollElement = $('#snippet_' + snippet.id()).find('.dataTables_wrapper');
+          if (viewModel.editorMode()) {
+            scrollElement = $(window.MAIN_SCROLLABLE);
+          }
+          if (scrollElement.data('scrollFnDt')) {
+            scrollElement.off('scroll', scrollElement.data('scrollFnDt'));
           }
-          viewModel.selectedNotebook().historyInitialHeight(h + 80); // add pagination too
-          viewModel.selectedNotebook().forceHistoryInitialHeight(false);
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'editor.calculate.history.height',
+        () => {
+          if (
+            viewModel.editorMode() &&
+            (viewModel.selectedNotebook().historyInitialHeight() === 0 ||
+              viewModel.selectedNotebook().forceHistoryInitialHeight())
+          ) {
+            let h = $('#queryHistory .history-table').height();
+            if (h === 0) {
+              h = viewModel.selectedNotebook().history().length * 32;
+            }
+            viewModel.selectedNotebook().historyInitialHeight(h + 80); // add pagination too
+            viewModel.selectedNotebook().forceHistoryInitialHeight(false);
+          }
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe('editor.create.new', newKeyHandler, HUE_PUB_SUB_EDITOR_ID);
+
+      if (viewModel.isOptimizerEnabled()) {
+        if (window.OPTIMIZER_AUTO_UPLOAD_QUERIES) {
+          huePubSub.subscribe(
+            'editor.upload.query',
+            query_id => {
+              viewModel
+                .selectedNotebook()
+                .snippets()[0]
+                .uploadQuery(query_id);
+            },
+            HUE_PUB_SUB_EDITOR_ID
+          );
         }
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
 
-    huePubSub.subscribe('editor.create.new', newKeyHandler, HUE_PUB_SUB_EDITOR_ID);
+        if (window.OPTIMIZER_AUTO_UPLOAD_DDL) {
+          huePubSub.subscribe(
+            'editor.upload.table.stats',
+            options => {
+              viewModel
+                .selectedNotebook()
+                .snippets()[0]
+                .uploadTableStats(options);
+            },
+            HUE_PUB_SUB_EDITOR_ID
+          );
+        }
 
-    if (viewModel.isOptimizerEnabled()) {
-      if (window.OPTIMIZER_AUTO_UPLOAD_QUERIES) {
-        huePubSub.subscribe(
-          'editor.upload.query',
-          query_id => {
-            viewModel
-              .selectedNotebook()
-              .snippets()[0]
-              .uploadQuery(query_id);
-          },
-          HUE_PUB_SUB_EDITOR_ID
-        );
+        if (window.OPTIMIZER_QUERY_HISTORY_UPLOAD_LIMIT !== 0) {
+          huePubSub.subscribe(
+            'editor.upload.history',
+            () => {
+              viewModel
+                .selectedNotebook()
+                .snippets()[0]
+                .uploadQueryHistory(5);
+            },
+            HUE_PUB_SUB_EDITOR_ID
+          );
+        }
       }
 
-      if (window.OPTIMIZER_AUTO_UPLOAD_DDL) {
-        huePubSub.subscribe(
-          'editor.upload.table.stats',
-          options => {
-            viewModel
-              .selectedNotebook()
-              .snippets()[0]
-              .uploadTableStats(options);
-          },
-          HUE_PUB_SUB_EDITOR_ID
-        );
-      }
+      huePubSub.subscribe(
+        'get.selected.notebook',
+        () => {
+          huePubSub.publish('set.selected.notebook', viewModel.selectedNotebook());
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
 
-      if (window.OPTIMIZER_QUERY_HISTORY_UPLOAD_LIMIT !== 0) {
-        huePubSub.subscribe(
-          'editor.upload.history',
-          () => {
-            viewModel
-              .selectedNotebook()
-              .snippets()[0]
-              .uploadQueryHistory(5);
-          },
-          HUE_PUB_SUB_EDITOR_ID
-        );
-      }
-    }
+      huePubSub.subscribe(
+        'left.assist.show',
+        () => {
+          if (!viewModel.isLeftPanelVisible() && viewModel.assistAvailable()) {
+            viewModel.isLeftPanelVisible(true);
+          }
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
 
-    huePubSub.subscribe(
-      'get.selected.notebook',
-      () => {
-        huePubSub.publish('set.selected.notebook', viewModel.selectedNotebook());
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'left.assist.show',
-      () => {
-        if (!viewModel.isLeftPanelVisible() && viewModel.assistAvailable()) {
-          viewModel.isLeftPanelVisible(true);
-        }
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'assist.set.manual.visibility',
-      () => {
-        wasLeftPanelVisible = viewModel.isLeftPanelVisible();
-        wasRightPanelVisible = viewModel.isRightPanelVisible();
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'editor.presentation.operate.toggle',
-      value => {
-        viewModel.isEditing(!viewModel.isEditing());
-        if (value) {
-          $('.jHueNotify').remove();
-          isAssistAvailable = viewModel.assistAvailable();
+      huePubSub.subscribe(
+        'assist.set.manual.visibility',
+        () => {
           wasLeftPanelVisible = viewModel.isLeftPanelVisible();
           wasRightPanelVisible = viewModel.isRightPanelVisible();
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'editor.presentation.operate.toggle',
+        value => {
+          viewModel.isEditing(!viewModel.isEditing());
+          if (value) {
+            $('.jHueNotify').remove();
+            isAssistAvailable = viewModel.assistAvailable();
+            wasLeftPanelVisible = viewModel.isLeftPanelVisible();
+            wasRightPanelVisible = viewModel.isRightPanelVisible();
+
+            if (wasResultFullScreenMode) {
+              huePubSub.publish('both.assists.hide', true);
+            } else {
+              huePubSub.publish('right.assist.hide', true);
+            }
 
-          if (wasResultFullScreenMode) {
-            huePubSub.publish('both.assists.hide', true);
+            viewModel.assistWithoutStorage(true);
+            viewModel.assistAvailable(false);
+            viewModel.isLeftPanelVisible(true);
+            viewModel.isRightPanelVisible(false);
+            window.setTimeout(() => {
+              viewModel.assistWithoutStorage(false);
+            }, 0);
+            $('.navigator').hide();
+            $('.add-snippet').hide();
+
+            if (window.BANNER_TOP_HTML) {
+              $('.main-content').attr('style', 'top: 31px !important');
+            } else {
+              $('.main-content').css('top', '1px');
+            }
+            redrawFixedHeaders(200);
+            $(window).bind('keydown', 'esc', exitPlayerMode);
           } else {
-            huePubSub.publish('right.assist.hide', true);
+            hideFixedHeaders();
+            huePubSub.publish('both.assists.show', true);
+            viewModel.assistWithoutStorage(true);
+            viewModel.isLeftPanelVisible(wasLeftPanelVisible);
+            viewModel.isRightPanelVisible(wasRightPanelVisible);
+            viewModel.assistAvailable(isAssistAvailable);
+            window.setTimeout(() => {
+              viewModel.assistWithoutStorage(false);
+            }, 0);
+            $('.navigator').show();
+            $('.add-snippet').show();
+            if (window.BANNER_TOP_HTML) {
+              $('.main-content').css('top', '112px');
+            } else {
+              $('.main-content').css('top', '74px');
+            }
+            redrawFixedHeaders(200);
+            $(window).unbind('keydown', exitPlayerMode);
           }
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
 
-          viewModel.assistWithoutStorage(true);
-          viewModel.assistAvailable(false);
-          viewModel.isLeftPanelVisible(true);
-          viewModel.isRightPanelVisible(false);
-          window.setTimeout(() => {
-            viewModel.assistWithoutStorage(false);
-          }, 0);
-          $('.navigator').hide();
-          $('.add-snippet').hide();
+      huePubSub.subscribe(
+        'show.retry.modal',
+        data => {
+          $('#retryModal' + window.EDITOR_SUFFIX).modal('show');
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
 
-          if (window.BANNER_TOP_HTML) {
-            $('.main-content').attr('style', 'top: 31px !important');
-          } else {
-            $('.main-content').css('top', '1px');
+      huePubSub.subscribe(
+        'hide.retry.modal',
+        data => {
+          $('#retryModal' + window.EDITOR_SUFFIX).modal('hide');
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'open.link',
+        link => {
+          $(window).unbind('keydown.editor');
+          if (link.indexOf('editor') >= 0) {
+            initKeydownBindings();
           }
-          redrawFixedHeaders(200);
-          $(window).bind('keydown', 'esc', exitPlayerMode);
-        } else {
-          hideFixedHeaders();
-          huePubSub.publish('both.assists.show', true);
-          viewModel.assistWithoutStorage(true);
-          viewModel.isLeftPanelVisible(wasLeftPanelVisible);
-          viewModel.isRightPanelVisible(wasRightPanelVisible);
-          viewModel.assistAvailable(isAssistAvailable);
-          window.setTimeout(() => {
-            viewModel.assistWithoutStorage(false);
-          }, 0);
-          $('.navigator').show();
-          $('.add-snippet').show();
-          if (window.BANNER_TOP_HTML) {
-            $('.main-content').css('top', '112px');
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'editor.presentation.toggle',
+        () => {
+          viewModel.selectedNotebook().isPresentationMode(!viewModel.isPresentationMode());
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe('editor.save', saveKeyHandler, HUE_PUB_SUB_EDITOR_ID);
+
+      huePubSub.subscribe(
+        'editor.render.data',
+        options => {
+          const $snip = $('#snippet_' + options.snippet.id());
+          const _el = $snip.find('.resultTable');
+          if (options.data.length > 0) {
+            window.setTimeout(() => {
+              let _dt;
+              if (options.initial) {
+                options.snippet.result.meta.notifySubscribers();
+                $('#snippet_' + options.snippet.id())
+                  .find('select')
+                  .trigger('chosen:updated');
+                _dt = createDatatable(_el, options.snippet, viewModel);
+                resetResultsResizer(options.snippet);
+              } else {
+                _dt = _el.hueDataTable();
+              }
+              try {
+                _dt.fnAddData(options.data);
+              } catch (e) {}
+              const _dtElement = $snip.find('.dataTables_wrapper');
+              huePubSub.publish('editor.snippet.result.normal', options.snippet);
+              _dtElement.scrollTop(_dtElement.data('scrollPosition'));
+              redrawFixedHeaders();
+              resizeToggleResultSettings(options.snippet, options.initial);
+            }, 300);
           } else {
-            $('.main-content').css('top', '74px');
-          }
-          redrawFixedHeaders(200);
-          $(window).unbind('keydown', exitPlayerMode);
-        }
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'show.retry.modal',
-      data => {
-        $('#retryModal' + window.EDITOR_SUFFIX).modal('show');
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'hide.retry.modal',
-      data => {
-        $('#retryModal' + window.EDITOR_SUFFIX).modal('hide');
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'open.link',
-      link => {
-        $(window).unbind('keydown.editor');
-        if (link.indexOf('editor') >= 0) {
-          initKeydownBindings();
-        }
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'editor.presentation.toggle',
-      () => {
-        viewModel.selectedNotebook().isPresentationMode(!viewModel.isPresentationMode());
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe('editor.save', saveKeyHandler, HUE_PUB_SUB_EDITOR_ID);
-
-    // TODO: Remove with ENABLE_NOTEBOOK_2
-    huePubSub.subscribe(
-      'editor.render.data',
-      options => {
-        const $snip = $('#snippet_' + options.snippet.id());
-        const _el = $snip.find('.resultTable');
-        if (options.data.length > 0) {
-          window.setTimeout(() => {
-            let _dt;
-            if (options.initial) {
-              options.snippet.result.meta.notifySubscribers();
-              $('#snippet_' + options.snippet.id())
-                .find('select')
-                .trigger('chosen:updated');
-              _dt = createDatatable(_el, options.snippet, viewModel);
-              resetResultsResizer(options.snippet);
-            } else {
-              _dt = _el.hueDataTable();
-            }
-            try {
-              _dt.fnAddData(options.data);
-            } catch (e) {}
-            const _dtElement = $snip.find('.dataTables_wrapper');
             huePubSub.publish('editor.snippet.result.normal', options.snippet);
-            _dtElement.scrollTop(_dtElement.data('scrollPosition'));
-            redrawFixedHeaders();
-            resizeToggleResultSettings(options.snippet, options.initial);
-          }, 300);
-        } else {
-          huePubSub.publish('editor.snippet.result.normal', options.snippet);
-        }
-        $snip.find('select').trigger('chosen:updated');
-        $snip.find('.snippet-grid-settings').scrollLeft(0);
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'editor.redraw.data',
-      options => {
-        hueUtils.waitForRendered(
-          '#snippet_' + options.snippet.id() + ' .resultTable',
-          el => {
-            return el.is(':visible');
-          },
-          () => {
-            const $el = $('#snippet_' + options.snippet.id()).find('.resultTable');
-            const dt = createDatatable($el, options.snippet, viewModel);
-            dt.fnAddData(options.snippet.result.data());
           }
-        );
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    // TODO: Remove with ENABLE_NOTEBOOK_2
-    huePubSub.subscribe(
-      'editor.snippet.result.gray',
-      snippet => {
-        const $snippet = $('#snippet_' + snippet.id());
-        $snippet.find('.dataTables_wrapper .fixed-first-column').css({ opacity: '0' });
-        $snippet.find('.dataTables_wrapper .fixed-header-row').css({ opacity: '0' });
-        $snippet.find('.dataTables_wrapper .fixed-first-cell').css({ opacity: '0' });
-        $snippet.find('.dataTables_wrapper .resultTable').css({ opacity: '0.55' });
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    // TODO: Remove with ENABLE_NOTEBOOK_2
-    huePubSub.subscribe(
-      'editor.snippet.result.normal',
-      snippet => {
-        const $snippet = $('#snippet_' + snippet.id());
-        $snippet.find('.dataTables_wrapper .fixed-first-column').css({ opacity: '1' });
-        $snippet.find('.dataTables_wrapper .fixed-header-row').css({ opacity: '1' });
-        $snippet.find('.dataTables_wrapper .fixed-first-cell').css({ opacity: '1' });
-        $snippet.find('.dataTables_wrapper .resultTable').css({ opacity: '1' });
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'render.jqcron',
-      () => {
-        if (typeof window.renderJqCron !== 'undefined') {
-          window.renderJqCron();
-        }
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'submit.popup.return',
-      data => {
-        viewModel.selectedNotebook().viewSchedulerId(data.job_id);
-        $('.submit-modal-editor').modal('hide');
-        huePubSub.publish('show.jobs.panel', { id: data.job_id, interface: data.type });
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'jobbrowser.data',
-      jobs => {
-        const snippet = viewModel.selectedNotebook().snippets()[0];
-        if (!snippet || snippet.type() === 'impala') {
-          return;
-        }
-        if (jobs.length > 0) {
-          let progress = 0;
-          let parent;
-          jobs.forEach(job => {
-            const id = job.shortId || job.id;
-            const el = $('.jobs-overlay li:contains(' + id + ')');
-            if (!el.length) {
-              return;
-            }
-            const context = ko.contextFor(el[0]);
-            parent = context.$parent;
-            const _job = context.$data;
-            progress = parseInt(job.mapsPercentComplete);
-            if (isNaN(progress)) {
-              progress = parseInt(job.progress);
+          $snip.find('select').trigger('chosen:updated');
+          $snip.find('.snippet-grid-settings').scrollLeft(0);
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'editor.redraw.data',
+        options => {
+          hueUtils.waitForRendered(
+            '#snippet_' + options.snippet.id() + ' .resultTable',
+            el => {
+              return el.is(':visible');
+            },
+            () => {
+              const $el = $('#snippet_' + options.snippet.id()).find('.resultTable');
+              const dt = createDatatable($el, options.snippet, viewModel);
+              dt.fnAddData(options.snippet.result.data());
             }
-            if (!isNaN(progress)) {
-              _job.percentJob(progress);
-            } else {
-              progress = 0;
+          );
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'editor.snippet.result.gray',
+        snippet => {
+          const $snippet = $('#snippet_' + snippet.id());
+          $snippet.find('.dataTables_wrapper .fixed-first-column').css({ opacity: '0' });
+          $snippet.find('.dataTables_wrapper .fixed-header-row').css({ opacity: '0' });
+          $snippet.find('.dataTables_wrapper .fixed-first-cell').css({ opacity: '0' });
+          $snippet.find('.dataTables_wrapper .resultTable').css({ opacity: '0.55' });
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'editor.snippet.result.normal',
+        snippet => {
+          const $snippet = $('#snippet_' + snippet.id());
+          $snippet.find('.dataTables_wrapper .fixed-first-column').css({ opacity: '1' });
+          $snippet.find('.dataTables_wrapper .fixed-header-row').css({ opacity: '1' });
+          $snippet.find('.dataTables_wrapper .fixed-first-cell').css({ opacity: '1' });
+          $snippet.find('.dataTables_wrapper .resultTable').css({ opacity: '1' });
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'render.jqcron',
+        () => {
+          if (typeof window.renderJqCron !== 'undefined') {
+            window.renderJqCron();
+          }
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'submit.popup.return',
+        data => {
+          viewModel.selectedNotebook().viewSchedulerId(data.job_id);
+          $('.submit-modal-editor').modal('hide');
+          huePubSub.publish('show.jobs.panel', { id: data.job_id, interface: data.type });
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'jobbrowser.data',
+        jobs => {
+          const snippet = viewModel.selectedNotebook().snippets()[0];
+          if (!snippet || snippet.type() === 'impala') {
+            return;
+          }
+          if (jobs.length > 0) {
+            let progress = 0;
+            let parent;
+            jobs.forEach(job => {
+              const id = job.shortId || job.id;
+              const el = $('.jobs-overlay li:contains(' + id + ')');
+              if (!el.length) {
+                return;
+              }
+              const context = ko.contextFor(el[0]);
+              parent = context.$parent;
+              const _job = context.$data;
+              progress = parseInt(job.mapsPercentComplete);
+              if (isNaN(progress)) {
+                progress = parseInt(job.progress);
+              }
+              if (!isNaN(progress)) {
+                _job.percentJob(progress);
+              } else {
+                progress = 0;
+              }
+            });
+            if (parent && parent.jobs().length === 1) {
+              parent.progress(Math.max(progress, parent.progress()));
             }
-          });
-          if (parent && parent.jobs().length === 1) {
-            parent.progress(Math.max(progress, parent.progress()));
           }
-        }
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'editor.get.active.risks',
-      callback => {
-        const result = {
-          editor: undefined,
-          risks: {}
-        };
-        if (viewModel.selectedNotebook()) {
-          if (viewModel.selectedNotebook().snippets().length === 1) {
-            result.editor = viewModel
-              .selectedNotebook()
-              .snippets()[0]
-              .ace();
-            result.risks =
-              viewModel
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'editor.get.active.risks',
+        callback => {
+          const result = {
+            editor: undefined,
+            risks: {}
+          };
+          if (viewModel.selectedNotebook()) {
+            if (viewModel.selectedNotebook().snippets().length === 1) {
+              result.editor = viewModel
                 .selectedNotebook()
                 .snippets()[0]
-                .complexity() || {};
-          } else {
-            viewModel
-              .selectedNotebook()
-              .snippets()
-              .every(snippet => {
-                if (snippet.inFocus()) {
-                  result.editor = snippet.ace();
-                  result.risks = snippet.complexity() || {};
-                  return false;
+                .ace();
+              result.risks =
+                viewModel
+                  .selectedNotebook()
+                  .snippets()[0]
+                  .complexity() || {};
+            } else {
+              viewModel
+                .selectedNotebook()
+                .snippets()
+                .every(snippet => {
+                  if (snippet.inFocus()) {
+                    result.editor = snippet.ace();
+                    result.risks = snippet.complexity() || {};
+                    return false;
+                  }
+                  return true;
+                });
+            }
+          }
+          callback(result);
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'editor.grid.shown',
+        snippet => {
+          hueUtils.waitForRendered(
+            '#snippet_' + snippet.id() + ' .dataTables_wrapper',
+            el => {
+              return el.is(':visible');
+            },
+            () => {
+              resizeToggleResultSettings(snippet, true);
+              forceChartDraws();
+              $('#snippet_' + snippet.id())
+                .find('.snippet-grid-settings')
+                .scrollLeft(0);
+            }
+          );
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'editor.chart.shown',
+        snippet => {
+          resizeToggleResultSettings(snippet, true);
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'recalculate.name.description.width',
+        () => {
+          hueUtils.waitForRendered(
+            '.editorComponents .hue-title-bar .query-name',
+            el => {
+              return el.is(':visible');
+            },
+            () => {
+              let cumulativeWidth = 0;
+              $('.editorComponents .hue-title-bar ul li:not(.skip-width-calculation)').each(
+                function() {
+                  cumulativeWidth += $(this).outerWidth();
                 }
-                return true;
-              });
+              );
+              $('.notebook-name-desc').css(
+                'max-width',
+                ($('.editorComponents .hue-title-bar').width() -
+                  cumulativeWidth -
+                  $('.editorComponents .hue-title-bar .pull-right').width() -
+                  120) /
+                  2 +
+                  'px'
+              );
+            }
+          );
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      $(document).on('updateResultHeaders', e => {
+        hideFixedHeaders();
+        redrawFixedHeaders(200);
+      });
+
+      $(document).on('showAuthModal', (e, data) => {
+        viewModel.authSessionUsername(window.LOGGED_USERNAME);
+        viewModel.authSessionMessage(data['message']);
+        viewModel.authSessionPassword('');
+        viewModel.authSessionType(data['type']);
+        viewModel.authSessionCallback(data['callback']);
+        $('#authModal' + window.EDITOR_SUFFIX).modal('show');
+      });
+
+      $(document).on('hideHistoryModal', e => {
+        $('#clearHistoryModal' + window.EDITOR_SUFFIX).modal('hide');
+      });
+
+      $(document).on('toggleResultSettings', (e, snippet) => {
+        window.setTimeout(() => {
+          const $snip = $('#snippet_' + snippet.id());
+          $snip.find('.chart').trigger('forceUpdate');
+          $snip.find('.snippet-grid-settings').scrollLeft(0);
+          if (snippet.isResultSettingsVisible()) {
+            $snip
+              .find('.table-results .grid-side')
+              .width(
+                100 -
+                  $snip.find('.table-results .column-side').data('newWidth') -
+                  hueUtils.bootstrapRatios.margin() +
+                  '%'
+              );
+          } else {
+            $snip.find('.table-results .grid-side').width('100%');
           }
+          redrawFixedHeaders();
+          $(window).trigger('resize');
+        }, 10);
+      });
+
+      $(document).on('editorSizeChanged', () => {
+        window.setTimeout(forceChartDraws, 50);
+      });
+
+      $(document).on('redrawResults', () => {
+        window.setTimeout(forceChartDraws, 50);
+      });
+
+      $(document).on('executeStarted', (e, options) => {
+        const $snip = $('#snippet_' + options.snippet.id());
+        const $el = $snip.find('.resultTable');
+        if (options.vm.editorMode()) {
+          $('#queryResults').css({
+            height: $el.height() + 'px'
+          });
         }
-        callback(result);
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'editor.grid.shown',
-      snippet => {
-        hueUtils.waitForRendered(
-          '#snippet_' + snippet.id() + ' .dataTables_wrapper',
-          el => {
-            return el.is(':visible');
+        $el.data('scrollToCol', null);
+        $el.data('scrollToRow', null);
+        $snip.find('.progress-snippet').animate(
+          {
+            height: '3px'
           },
-          () => {
-            resizeToggleResultSettings(snippet, true);
-            forceChartDraws();
-            $('#snippet_' + snippet.id())
-              .find('.snippet-grid-settings')
-              .scrollLeft(0);
-          }
+          100
         );
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'editor.chart.shown',
-      snippet => {
-        resizeToggleResultSettings(snippet, true);
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    huePubSub.subscribe(
-      'recalculate.name.description.width',
-      () => {
-        hueUtils.waitForRendered(
-          '.editorComponents .hue-title-bar .query-name',
-          el => {
-            return el.is(':visible');
-          },
-          () => {
-            let cumulativeWidth = 0;
-            $('.editorComponents .hue-title-bar ul li:not(.skip-width-calculation)').each(
-              function() {
-                cumulativeWidth += $(this).outerWidth();
-              }
-            );
-            $('.notebook-name-desc').css(
-              'max-width',
-              ($('.editorComponents .hue-title-bar').width() -
-                cumulativeWidth -
-                $('.editorComponents .hue-title-bar .pull-right').width() -
-                120) /
-                2 +
-                'px'
-            );
+        if ($el.hasClass('dt')) {
+          $el.removeClass('dt');
+          $('#eT' + options.snippet.id() + 'jHueTableExtenderClonedContainer').remove();
+          $('#eT' + options.snippet.id() + 'jHueTableExtenderClonedContainerColumn').remove();
+          $('#eT' + options.snippet.id() + 'jHueTableExtenderClonedContainerCell').remove();
+          if ($el.hueDataTable()) {
+            $el.hueDataTable().fnDestroy();
           }
-        );
-      },
-      HUE_PUB_SUB_EDITOR_ID
-    );
-
-    $(document).on('updateResultHeaders', e => {
-      hideFixedHeaders();
-      redrawFixedHeaders(200);
-    });
-
-    $(document).on('showAuthModal', (e, data) => {
-      viewModel.authSessionUsername(window.LOGGED_USERNAME);
-      viewModel.authSessionMessage(data['message']);
-      viewModel.authSessionPassword('');
-      viewModel.authSessionType(data['type']);
-      viewModel.authSessionCallback(data['callback']);
-      $('#authModal' + window.EDITOR_SUFFIX).modal('show');
-    });
-
-    $(document).on('hideHistoryModal', e => {
-      $('#clearHistoryModal' + window.EDITOR_SUFFIX).modal('hide');
-    });
-
-    // TODO: Migrate to ko.resultChart.js for ENABLE_NOTEBOOK_2
-    $(document).on('toggleResultSettings', (e, snippet) => {
-      window.setTimeout(() => {
-        const $snip = $('#snippet_' + snippet.id());
-        $snip.find('.chart').trigger('forceUpdate');
-        $snip.find('.snippet-grid-settings').scrollLeft(0);
-        if (snippet.isResultSettingsVisible()) {
-          $snip
-            .find('.table-results .grid-side')
-            .width(
-              100 -
-                $snip.find('.table-results .column-side').data('newWidth') -
-                hueUtils.bootstrapRatios.margin() +
-                '%'
-            );
-        } else {
-          $snip.find('.table-results .grid-side').width('100%');
-        }
-        redrawFixedHeaders();
-        $(window).trigger('resize');
-      }, 10);
-    });
-
-    // TODO: Migrate to ko.resultChart.js for ENABLE_NOTEBOOK_2
-    $(document).on('editorSizeChanged', () => {
-      window.setTimeout(forceChartDraws, 50);
-    });
-
-    // TODO: Migrate to ko.resultChart.js for ENABLE_NOTEBOOK_2
-    $(document).on('redrawResults', () => {
-      window.setTimeout(forceChartDraws, 50);
-    });
-
-    // TODO: Migrate to ko.resultGrid.js for ENABLE_NOTEBOOK_2
-    $(document).on('executeStarted', (e, options) => {
-      const $snip = $('#snippet_' + options.snippet.id());
-      const $el = $snip.find('.resultTable');
-      if (options.vm.editorMode()) {
-        $('#queryResults').css({
-          height: $el.height() + 'px'
-        });
-      }
-      $el.data('scrollToCol', null);
-      $el.data('scrollToRow', null);
-      $snip.find('.progress-snippet').animate(
-        {
-          height: '3px'
-        },
-        100
-      );
-      if ($el.hasClass('dt')) {
-        $el.removeClass('dt');
-        $('#eT' + options.snippet.id() + 'jHueTableExtenderClonedContainer').remove();
-        $('#eT' + options.snippet.id() + 'jHueTableExtenderClonedContainerColumn').remove();
-        $('#eT' + options.snippet.id() + 'jHueTableExtenderClonedContainerCell').remove();
-        if ($el.hueDataTable()) {
-          $el.hueDataTable().fnDestroy();
+          $el.find('thead tr').empty();
+          $el.data('lockedRows', {});
         }
-        $el.find('thead tr').empty();
-        $el.data('lockedRows', {});
-      }
-    });
+      });
 
-    // TODO: Migrate to ko.resultGrid.js for ENABLE_NOTEBOOK_2
-    $(document).on('renderDataError', (e, options) => {
-      huePubSub.publish('editor.snippet.result.normal', options.snippet);
-    });
+      $(document).on('renderDataError', (e, options) => {
+        huePubSub.publish('editor.snippet.result.normal', options.snippet);
+      });
 
-    $(document).on('progress', (e, options) => {
-      if (options.data === 100) {
-        window.setTimeout(() => {
-          $('#snippet_' + options.snippet.id())
-            .find('.progress-snippet')
-            .animate(
-              {
-                height: '0'
-              },
-              100,
-              () => {
-                options.snippet.progress(0);
-                redrawFixedHeaders();
-              }
-            );
-        }, 2000);
-      }
-    });
+      $(document).on('progress', (e, options) => {
+        if (options.data === 100) {
+          window.setTimeout(() => {
+            $('#snippet_' + options.snippet.id())
+              .find('.progress-snippet')
+              .animate(
+                {
+                  height: '0'
+                },
+                100,
+                () => {
+                  options.snippet.progress(0);
+                  redrawFixedHeaders();
+                }
+              );
+          }, 2000);
+        }
+      });
 
-    // TODO: Migrate to ko.resultChart.js for ENABLE_NOTEBOOK_2
-    if (!window.ENABLE_NOTEBOOK_2) {
       $(document).on('forceChartDraw', (e, snippet) => {
         window.setTimeout(() => {
           snippet.chartX.notifySubscribers();
           snippet.chartX.valueHasMutated();
         }, 100);
       });
-    }
 
-    let hideTimeout = -1;
-    $(document).on('hideAutocomplete', () => {
-      window.clearTimeout(hideTimeout);
-      hideTimeout = window.setTimeout(() => {
-        const $aceAutocomplete = $('.ace_editor.ace_autocomplete');
-        if ($aceAutocomplete.is(':visible')) {
-          $aceAutocomplete.hide();
-        }
-      }, 100);
-    });
-
-    // TODO: Migrate to ko.resultChart.js for ENABLE_NOTEBOOK_2
-    let _resizeTimeout = -1;
-    $(window).on('resize', () => {
-      huePubSub.publish('recalculate.name.description.width');
-      window.clearTimeout(_resizeTimeout);
-      _resizeTimeout = window.setTimeout(() => {
-        forceChartDraws();
-      }, 200);
-    });
-  }
-});
+      let hideTimeout = -1;
+      $(document).on('hideAutocomplete', () => {
+        window.clearTimeout(hideTimeout);
+        hideTimeout = window.setTimeout(() => {
+          const $aceAutocomplete = $('.ace_editor.ace_autocomplete');
+          if ($aceAutocomplete.is(':visible')) {
+            $aceAutocomplete.hide();
+          }
+        }, 100);
+      });
+
+      let _resizeTimeout = -1;
+      $(window).on('resize', () => {
+        huePubSub.publish('recalculate.name.description.width');
+        window.clearTimeout(_resizeTimeout);
+        _resizeTimeout = window.setTimeout(() => {
+          forceChartDraws();
+        }, 200);
+      });
+    }
+  });
+}

+ 947 - 0
desktop/core/src/desktop/js/apps/notebook2/app.js

@@ -0,0 +1,947 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import ko from 'knockout';
+import $ from 'jquery';
+import Clipboard from 'clipboard';
+import 'jquery-mousewheel';
+import 'ext/bootstrap-datepicker.min';
+import 'ext/jquery.hotkeys';
+import 'jquery/plugins/jquery.hdfstree';
+
+import huePubSub from 'utils/huePubSub';
+import hueUtils from 'utils/hueUtils';
+import I18n from 'utils/i18n';
+import sqlWorkerHandler from 'sql/sqlWorkerHandler';
+
+import {
+  HIDE_FIXED_HEADERS_EVENT,
+  REDRAW_FIXED_HEADERS_EVENT,
+  SHOW_GRID_SEARCH_EVENT,
+  SHOW_NORMAL_RESULT_EVENT,
+  REDRAW_CHART_EVENT
+} from 'apps/notebook2/events';
+
+export const initNotebook2 = () => {
+  window.Clipboard = Clipboard;
+
+  const HUE_PUB_SUB_EDITOR_ID =
+    window.location.pathname.indexOf('notebook') > -1 ? 'notebook' : 'editor';
+
+  huePubSub.subscribe('app.dom.loaded', app => {
+    if (app === 'editor' || app === 'notebook') {
+      window.MAIN_SCROLLABLE = '.page-content';
+
+      let isLeftNavOpen = false;
+      huePubSub.subscribe(
+        'left.nav.open.toggle',
+        val => {
+          isLeftNavOpen = val;
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'split.panel.resized',
+        () => {
+          huePubSub.publish('recalculate.name.description.width');
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      const showHoverMsg = e => {
+        let dt = null;
+        if (e) {
+          dt = e.dataTransfer;
+        }
+        if (
+          !isLeftNavOpen &&
+          (!dt ||
+            (dt.types &&
+              (dt.types.indexOf ? dt.types.indexOf('Files') !== -1 : dt.types.contains('Files'))))
+        ) {
+          $(window.EDITOR_BINDABLE_ELEMENT)
+            .find('.hoverMsg')
+            .removeClass('hide');
+        }
+      };
+
+      const hideHoverMsg = vm => {
+        if (vm.editorMode()) {
+          $(window.EDITOR_BINDABLE_ELEMENT)
+            .find('.hoverText')
+            .html(I18n('Drop a SQL file here'));
+        } else {
+          $(window.EDITOR_BINDABLE_ELEMENT)
+            .find('.hoverText')
+            .html(I18n('Drop iPython/Zeppelin notebooks here'));
+        }
+        $(window.EDITOR_BINDABLE_ELEMENT)
+          .find('.hoverMsg')
+          .addClass('hide');
+      };
+
+      if (ko.options) {
+        ko.options.deferUpdates = true;
+      }
+
+      let viewModel;
+
+      const replaceAce = content => {
+        const snip = viewModel.selectedNotebook().snippets()[0];
+        if (snip) {
+          snip.statement_raw(content);
+          snip.result.statements_count(1);
+          snip.ace().setValue(content, 1);
+          snip.result.statement_range({
+            start: {
+              row: 0,
+              column: 0
+            },
+            end: {
+              row: 0,
+              column: 0
+            }
+          });
+          snip.ace()._emit('focus');
+        }
+        hideHoverMsg(viewModel);
+        huePubSub.publish(REDRAW_FIXED_HEADERS_EVENT);
+      };
+      window.replaceAce = replaceAce;
+
+      // Drag and drop iPython / Zeppelin notebooks
+      if (window.FileReader) {
+        let aceChecks = 0;
+
+        const addAce = (content, snippetType) => {
+          const snip = viewModel
+            .selectedNotebook()
+            .addSnippet({ type: snippetType, result: {} }, true);
+          snip.statement_raw(content);
+          aceChecks++;
+          snip.checkForAce = window.setInterval(() => {
+            if (snip.ace()) {
+              window.clearInterval(snip.checkForAce);
+              aceChecks--;
+              if (aceChecks === 0) {
+                hideHoverMsg(viewModel);
+                huePubSub.publish(REDRAW_FIXED_HEADERS_EVENT);
+              }
+            }
+          }, 100);
+        };
+
+        const addMarkdown = content => {
+          const snip = viewModel
+            .selectedNotebook()
+            .addSnippet({ type: 'markdown', result: {} }, true);
+          snip.statement_raw(content);
+        };
+
+        const addPySpark = content => {
+          addAce(content, 'pyspark');
+        };
+
+        const addSql = content => {
+          addAce(content, 'hive');
+        };
+
+        const addScala = content => {
+          addAce(content, 'spark');
+        };
+
+        const parseExternalJSON = raw => {
+          try {
+            if (viewModel.editorMode()) {
+              replaceAce(raw);
+            } else {
+              const loaded = typeof raw == 'string' ? JSON.parse(raw) : raw;
+              if (loaded.nbformat) {
+                //ipython
+                let cells = [];
+                if (loaded.nbformat === 3) {
+                  cells = loaded.worksheets[0].cells;
+                } else if (loaded.nbformat === 4) {
+                  cells = loaded.cells;
+                }
+                cells.forEach((cell, cellCnt) => {
+                  window.setTimeout(() => {
+                    if (cell.cell_type === 'code') {
+                      if (loaded.nbformat === 3) {
+                        addPySpark($.isArray(cell.input) ? cell.input.join('') : cell.input);
+                      } else {
+                        addPySpark($.isArray(cell.source) ? cell.source.join('') : cell.source);
+                      }
+                    }
+                    if (cell.cell_type === 'heading') {
+                      let heading = $.isArray(cell.source) ? cell.source.join('') : cell.source;
+                      if (cell.level === 1) {
+                        heading += '\n====================';
+                      } else if (cell.level === 2) {
+                        heading += '\n--------------------';
+                      } else {
+                        heading = '### ' + heading;
+                      }
+                      addMarkdown(heading);
+                    }
+                    if (cell.cell_type === 'markdown') {
+                      addMarkdown($.isArray(cell.source) ? cell.source.join('') : cell.source);
+                    }
+                    if (cellCnt === cells.length - 1 && aceChecks === 0) {
+                      hideHoverMsg(viewModel);
+                    }
+                  }, 10);
+                });
+              }
+
+              if (loaded.paragraphs) {
+                //zeppelin
+                if (loaded.name) {
+                  viewModel.selectedNotebook().name(loaded.name);
+                }
+                loaded.paragraphs.forEach(paragraph => {
+                  if (paragraph.text) {
+                    const content = paragraph.text.split('\n');
+                    if (content[0].indexOf('%md') > -1) {
+                      content.shift();
+                      addMarkdown(content.join('\n'));
+                    } else if (
+                      content[0].indexOf('%sql') > -1 ||
+                      content[0].indexOf('%hive') > -1
+                    ) {
+                      content.shift();
+                      addSql(content.join('\n'));
+                    } else if (content[0].indexOf('%pyspark') > -1) {
+                      content.shift();
+                      addPySpark(content.join('\n'));
+                    } else {
+                      if (content[0].indexOf('%spark') > -1) {
+                        content.shift();
+                      }
+                      addScala(content.join('\n'));
+                    }
+                  }
+                });
+              }
+            }
+          } catch (e) {
+            hideHoverMsg(viewModel);
+            replaceAce(raw);
+          }
+        };
+
+        const handleFileSelect = function(evt) {
+          evt.stopPropagation();
+          evt.preventDefault();
+          const dt = evt.dataTransfer;
+          const files = dt.files;
+          if (files.length > 0) {
+            showHoverMsg();
+          } else {
+            hideHoverMsg(viewModel);
+          }
+
+          for (let i = 0, f; (f = files[i]); i++) {
+            const reader = new FileReader();
+            reader.onload = (function(file) {
+              return function(e) {
+                $('.hoverText').html("<i class='fa fa-spinner fa-spin'></i>");
+                parseExternalJSON(e.target.result);
+              };
+            })(f);
+            reader.readAsText(f);
+          }
+        };
+
+        const handleDragOver = function(evt) {
+          evt.stopPropagation();
+          evt.preventDefault();
+          evt.dataTransfer.dropEffect = 'copy';
+        };
+
+        const dropZone = $(window.EDITOR_BINDABLE_ELEMENT)[0];
+        dropZone.addEventListener('dragenter', showHoverMsg, false);
+        dropZone.addEventListener('dragover', handleDragOver, false);
+        dropZone.addEventListener('drop', handleFileSelect, false);
+
+        let isDraggingOverText = false;
+
+        $(window.EDITOR_BINDABLE_ELEMENT)
+          .find('.hoverText')
+          .on('dragenter', e => {
+            e.preventDefault();
+            e.stopPropagation();
+            e.stopImmediatePropagation();
+            isDraggingOverText = true;
+          });
+
+        $(window.EDITOR_BINDABLE_ELEMENT)
+          .find('.hoverText')
+          .on('dragleave', e => {
+            e.preventDefault();
+            e.stopPropagation();
+            e.stopImmediatePropagation();
+            isDraggingOverText = false;
+          });
+
+        $(window.EDITOR_BINDABLE_ELEMENT)
+          .find('.hoverMsg')
+          .on('dragleave', e => {
+            if (!isDraggingOverText) {
+              hideHoverMsg(viewModel);
+            }
+          });
+      }
+
+      if (window.EDITOR_ENABLE_QUERY_SCHEDULING) {
+        viewModel = new window.EditorViewModel(
+          window.EDITOR_ID,
+          window.NOTEBOOKS_JSON,
+          window.EDITOR_VIEW_MODEL_OPTIONS,
+          window.CoordinatorEditorViewModel,
+          window.RunningCoordinatorModel
+        );
+      } else {
+        viewModel = new window.EditorViewModel(
+          window.EDITOR_ID,
+          window.NOTEBOOKS_JSON,
+          window.EDITOR_VIEW_MODEL_OPTIONS
+        );
+      }
+      ko.applyBindings(viewModel, $(window.EDITOR_BINDABLE_ELEMENT)[0]);
+      viewModel.init();
+
+      sqlWorkerHandler.registerWorkers();
+
+      viewModel.selectedNotebook.subscribe(newVal => {
+        huePubSub.publish('selected.notebook.changed', newVal);
+      });
+
+      let wasResultFullScreenMode = false;
+      let isAssistAvailable = viewModel.assistAvailable();
+      let wasLeftPanelVisible = viewModel.isLeftPanelVisible();
+      let wasRightPanelVisible = viewModel.isRightPanelVisible();
+
+      const exitPlayerMode = () => {
+        if (!wasResultFullScreenMode) {
+          viewModel.selectedNotebook().isPresentationMode(false);
+        } else {
+          viewModel.isResultFullScreenMode(false);
+        }
+        wasResultFullScreenMode = false;
+      };
+
+      viewModel.isResultFullScreenMode.subscribe(newValue => {
+        wasResultFullScreenMode = newValue;
+        huePubSub.publish('editor.presentation.operate.toggle', newValue);
+      });
+
+      viewModel.isLeftPanelVisible.subscribe(value => {
+        huePubSub.publish(REDRAW_FIXED_HEADERS_EVENT);
+      });
+
+      // Close the notebook snippets when leaving the page
+      window.onbeforeunload = function(e) {
+        if (!viewModel.selectedNotebook().avoidClosing) {
+          viewModel.selectedNotebook().close();
+        }
+      };
+      $(window).data('beforeunload', window.onbeforeunload);
+
+      $('.preview-sample').css('right', 10 + hueUtils.scrollbarWidth() + 'px');
+
+      const saveKeyHandler = () => {
+        if (viewModel.canSave()) {
+          viewModel.saveNotebook();
+        } else {
+          $('#saveAsModal' + window.EDITOR_SUFFIX).modal('show');
+        }
+      };
+
+      const newKeyHandler = () => {
+        if (!viewModel.editorMode()) {
+          viewModel.selectedNotebook().newSnippet();
+        } else {
+          viewModel.newNotebook(
+            viewModel.editorType(),
+            null,
+            viewModel.selectedNotebook()
+              ? viewModel
+                  .selectedNotebook()
+                  .snippets()[0]
+                  .currentQueryTab()
+              : null
+          );
+        }
+      };
+
+      const initKeydownBindings = () => {
+        $(window).bind('keydown.editor', 'ctrl+s alt+s meta+s', e => {
+          e.preventDefault();
+          saveKeyHandler();
+          return false;
+        });
+        $(window).bind('keydown.editor', 'ctrl+shift+p alt+shift+p meta+shift+p', e => {
+          e.preventDefault();
+          huePubSub.publish('editor.presentation.toggle');
+          return false;
+        });
+        $(window).bind('keydown.editor', 'ctrl+e alt+e meta+e', e => {
+          e.preventDefault();
+          newKeyHandler();
+          return false;
+        });
+      };
+
+      if (document.location.href.indexOf('editor') >= 0) {
+        initKeydownBindings();
+      }
+
+      $(document).bind('keyup', e => {
+        if (
+          e.keyCode === 191 &&
+          e.shiftKey &&
+          !$(e.target).is('input') &&
+          !$(e.target).is('textarea')
+        ) {
+          $('#helpModal' + window.EDITOR_SUFFIX).modal('show');
+        }
+
+        if (
+          e.keyCode === 191 &&
+          !e.shiftKey &&
+          !$(e.target).is('input') &&
+          !$(e.target).is('textarea')
+        ) {
+          if (
+            viewModel.editorMode() &&
+            viewModel
+              .selectedNotebook()
+              .snippets()[0]
+              .currentQueryTab() === 'queryResults'
+          ) {
+            e.preventDefault();
+            huePubSub.publish(SHOW_GRID_SEARCH_EVENT);
+            return false;
+          }
+        }
+      });
+
+      let initialResizePosition = 100;
+
+      const draggableHelper = (el, e, ui, setSize) => {
+        const _snippet = ko.dataFor(el.parents('.snippet')[0]);
+        const _cm = $('#snippet_' + _snippet.id()).data('editor');
+        const _newSize = _snippet.aceSize() + (ui.offset.top - initialResizePosition);
+        _cm.setSize('99%', _newSize);
+        if (setSize) {
+          _snippet.aceSize(_newSize);
+        }
+      };
+
+      const getDraggableOptions = minY => {
+        return {
+          axis: 'y',
+          start: function(e, ui) {
+            initialResizePosition = ui.offset.top;
+            huePubSub.publish(HIDE_FIXED_HEADERS_EVENT);
+          },
+          drag: function(e, ui) {
+            draggableHelper($(this), e, ui);
+          },
+          stop: function(e, ui) {
+            draggableHelper($(this), e, ui, true);
+            huePubSub.publish(REDRAW_FIXED_HEADERS_EVENT);
+            ui.helper.first().removeAttr('style');
+          },
+          containment: [0, minY, 4000, minY + 400]
+        };
+      };
+
+      $('.resize-panel a').each(function() {
+        $(this).draggable(
+          getDraggableOptions(
+            $(this)
+              .parents('.snippet')
+              .offset().top + 128
+          )
+        );
+      });
+
+      // ======== PubSub ========
+
+      let splitDraggableTimeout = -1;
+      huePubSub.subscribe(
+        'split.draggable.position',
+        () => {
+          window.clearTimeout(splitDraggableTimeout);
+          splitDraggableTimeout = window.setTimeout(() => {
+            huePubSub.publish(REDRAW_FIXED_HEADERS_EVENT);
+          }, 200);
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'app.gained.focus',
+        app => {
+          if (app === 'editor') {
+            huePubSub.publish(REDRAW_FIXED_HEADERS_EVENT);
+            huePubSub.publish('hue.scrollleft.show');
+            huePubSub.publish('active.snippet.type.changed', {
+              type: viewModel.editorType(),
+              isSqlDialect: viewModel.getSnippetViewSettings(viewModel.editorType()).sqlDialect
+            });
+          }
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'current.query.tab.switched',
+        tab => {
+          if (tab !== 'queryResults') {
+            $('.hue-datatable-search').hide();
+          }
+          if (tab === 'queryHistory') {
+            hueUtils.waitForRendered(
+              $('#queryHistory .history-table'),
+              el => {
+                return el.is(':visible');
+              },
+              () => {
+                viewModel.selectedNotebook().forceHistoryInitialHeight(true);
+                huePubSub.publish('editor.calculate.history.height');
+              }
+            );
+          }
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'editor.calculate.history.height',
+        () => {
+          if (
+            viewModel.editorMode() &&
+            (viewModel.selectedNotebook().historyInitialHeight() === 0 ||
+              viewModel.selectedNotebook().forceHistoryInitialHeight())
+          ) {
+            let h = $('#queryHistory .history-table').height();
+            if (h === 0) {
+              h = viewModel.selectedNotebook().history().length * 32;
+            }
+            viewModel.selectedNotebook().historyInitialHeight(h + 80); // add pagination too
+            viewModel.selectedNotebook().forceHistoryInitialHeight(false);
+          }
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe('editor.create.new', newKeyHandler, HUE_PUB_SUB_EDITOR_ID);
+
+      if (viewModel.isOptimizerEnabled()) {
+        if (window.OPTIMIZER_AUTO_UPLOAD_QUERIES) {
+          huePubSub.subscribe(
+            'editor.upload.query',
+            query_id => {
+              viewModel
+                .selectedNotebook()
+                .snippets()[0]
+                .uploadQuery(query_id);
+            },
+            HUE_PUB_SUB_EDITOR_ID
+          );
+        }
+
+        if (window.OPTIMIZER_AUTO_UPLOAD_DDL) {
+          huePubSub.subscribe(
+            'editor.upload.table.stats',
+            options => {
+              viewModel
+                .selectedNotebook()
+                .snippets()[0]
+                .uploadTableStats(options);
+            },
+            HUE_PUB_SUB_EDITOR_ID
+          );
+        }
+
+        if (window.OPTIMIZER_QUERY_HISTORY_UPLOAD_LIMIT !== 0) {
+          huePubSub.subscribe(
+            'editor.upload.history',
+            () => {
+              viewModel
+                .selectedNotebook()
+                .snippets()[0]
+                .uploadQueryHistory(5);
+            },
+            HUE_PUB_SUB_EDITOR_ID
+          );
+        }
+      }
+
+      huePubSub.subscribe(
+        'get.selected.notebook',
+        () => {
+          huePubSub.publish('set.selected.notebook', viewModel.selectedNotebook());
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'left.assist.show',
+        () => {
+          if (!viewModel.isLeftPanelVisible() && viewModel.assistAvailable()) {
+            viewModel.isLeftPanelVisible(true);
+          }
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'assist.set.manual.visibility',
+        () => {
+          wasLeftPanelVisible = viewModel.isLeftPanelVisible();
+          wasRightPanelVisible = viewModel.isRightPanelVisible();
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'editor.presentation.operate.toggle',
+        value => {
+          viewModel.isEditing(!viewModel.isEditing());
+          if (value) {
+            $('.jHueNotify').remove();
+            isAssistAvailable = viewModel.assistAvailable();
+            wasLeftPanelVisible = viewModel.isLeftPanelVisible();
+            wasRightPanelVisible = viewModel.isRightPanelVisible();
+
+            if (wasResultFullScreenMode) {
+              huePubSub.publish('both.assists.hide', true);
+            } else {
+              huePubSub.publish('right.assist.hide', true);
+            }
+
+            viewModel.assistWithoutStorage(true);
+            viewModel.assistAvailable(false);
+            viewModel.isLeftPanelVisible(true);
+            viewModel.isRightPanelVisible(false);
+            window.setTimeout(() => {
+              viewModel.assistWithoutStorage(false);
+            }, 0);
+            $('.navigator').hide();
+            $('.add-snippet').hide();
+
+            if (window.BANNER_TOP_HTML) {
+              $('.main-content').attr('style', 'top: 31px !important');
+            } else {
+              $('.main-content').css('top', '1px');
+            }
+            window.setTimeout(() => {
+              huePubSub.publish(REDRAW_FIXED_HEADERS_EVENT);
+            }, 200);
+            $(window).bind('keydown', 'esc', exitPlayerMode);
+          } else {
+            huePubSub.publish(HIDE_FIXED_HEADERS_EVENT);
+            huePubSub.publish('both.assists.show', true);
+            viewModel.assistWithoutStorage(true);
+            viewModel.isLeftPanelVisible(wasLeftPanelVisible);
+            viewModel.isRightPanelVisible(wasRightPanelVisible);
+            viewModel.assistAvailable(isAssistAvailable);
+            window.setTimeout(() => {
+              viewModel.assistWithoutStorage(false);
+            }, 0);
+            $('.navigator').show();
+            $('.add-snippet').show();
+            if (window.BANNER_TOP_HTML) {
+              $('.main-content').css('top', '112px');
+            } else {
+              $('.main-content').css('top', '74px');
+            }
+            window.setTimeout(() => {
+              huePubSub.publish(REDRAW_FIXED_HEADERS_EVENT);
+            }, 200);
+            $(window).unbind('keydown', exitPlayerMode);
+          }
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'show.retry.modal',
+        data => {
+          $('#retryModal' + window.EDITOR_SUFFIX).modal('show');
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'hide.retry.modal',
+        data => {
+          $('#retryModal' + window.EDITOR_SUFFIX).modal('hide');
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'open.link',
+        link => {
+          $(window).unbind('keydown.editor');
+          if (link.indexOf('editor') >= 0) {
+            initKeydownBindings();
+          }
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'editor.presentation.toggle',
+        () => {
+          viewModel.selectedNotebook().isPresentationMode(!viewModel.isPresentationMode());
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe('editor.save', saveKeyHandler, HUE_PUB_SUB_EDITOR_ID);
+
+      huePubSub.subscribe(
+        'render.jqcron',
+        () => {
+          if (typeof window.renderJqCron !== 'undefined') {
+            window.renderJqCron();
+          }
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'submit.popup.return',
+        data => {
+          viewModel.selectedNotebook().viewSchedulerId(data.job_id);
+          $('.submit-modal-editor').modal('hide');
+          huePubSub.publish('show.jobs.panel', { id: data.job_id, interface: data.type });
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'jobbrowser.data',
+        jobs => {
+          const snippet = viewModel.selectedNotebook().snippets()[0];
+          if (!snippet || snippet.type() === 'impala') {
+            return;
+          }
+          if (jobs.length > 0) {
+            let progress = 0;
+            let parent;
+            jobs.forEach(job => {
+              const id = job.shortId || job.id;
+              const el = $('.jobs-overlay li:contains(' + id + ')');
+              if (!el.length) {
+                return;
+              }
+              const context = ko.contextFor(el[0]);
+              parent = context.$parent;
+              const _job = context.$data;
+              progress = parseInt(job.mapsPercentComplete);
+              if (isNaN(progress)) {
+                progress = parseInt(job.progress);
+              }
+              if (!isNaN(progress)) {
+                _job.percentJob(progress);
+              } else {
+                progress = 0;
+              }
+            });
+            if (parent && parent.jobs().length === 1) {
+              parent.progress(Math.max(progress, parent.progress()));
+            }
+          }
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'editor.get.active.risks',
+        callback => {
+          const result = {
+            editor: undefined,
+            risks: {}
+          };
+          if (viewModel.selectedNotebook()) {
+            if (viewModel.selectedNotebook().snippets().length === 1) {
+              result.editor = viewModel
+                .selectedNotebook()
+                .snippets()[0]
+                .ace();
+              result.risks =
+                viewModel
+                  .selectedNotebook()
+                  .snippets()[0]
+                  .complexity() || {};
+            } else {
+              viewModel
+                .selectedNotebook()
+                .snippets()
+                .every(snippet => {
+                  if (snippet.inFocus()) {
+                    result.editor = snippet.ace();
+                    result.risks = snippet.complexity() || {};
+                    return false;
+                  }
+                  return true;
+                });
+            }
+          }
+          callback(result);
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      huePubSub.subscribe(
+        'recalculate.name.description.width',
+        () => {
+          hueUtils.waitForRendered(
+            '.editorComponents .hue-title-bar .query-name',
+            el => {
+              return el.is(':visible');
+            },
+            () => {
+              let cumulativeWidth = 0;
+              $('.editorComponents .hue-title-bar ul li:not(.skip-width-calculation)').each(
+                function() {
+                  cumulativeWidth += $(this).outerWidth();
+                }
+              );
+              $('.notebook-name-desc').css(
+                'max-width',
+                ($('.editorComponents .hue-title-bar').width() -
+                  cumulativeWidth -
+                  $('.editorComponents .hue-title-bar .pull-right').width() -
+                  120) /
+                  2 +
+                  'px'
+              );
+            }
+          );
+        },
+        HUE_PUB_SUB_EDITOR_ID
+      );
+
+      $(document).on('updateResultHeaders', e => {
+        huePubSub.publish(HIDE_FIXED_HEADERS_EVENT);
+        window.setTimeout(() => {
+          huePubSub.publish(REDRAW_FIXED_HEADERS_EVENT);
+        }, 200);
+      });
+
+      $(document).on('showAuthModal', (e, data) => {
+        viewModel.authSessionUsername(window.LOGGED_USERNAME);
+        viewModel.authSessionMessage(data['message']);
+        viewModel.authSessionPassword('');
+        viewModel.authSessionType(data['type']);
+        viewModel.authSessionCallback(data['callback']);
+        $('#authModal' + window.EDITOR_SUFFIX).modal('show');
+      });
+
+      $(document).on('hideHistoryModal', e => {
+        $('#clearHistoryModal' + window.EDITOR_SUFFIX).modal('hide');
+      });
+
+      $(document).on('editorSizeChanged', () => {
+        window.setTimeout(() => {
+          huePubSub.publish(REDRAW_CHART_EVENT);
+        }, 50);
+      });
+
+      $(document).on('redrawResults', () => {
+        window.setTimeout(() => {
+          huePubSub.publish(REDRAW_CHART_EVENT);
+        }, 50);
+      });
+
+      $(document).on('executeStarted', (e, options) => {
+        const $snip = $('#snippet_' + options.snippet.id());
+        const $el = $snip.find('.resultTable');
+        if (options.vm.editorMode()) {
+          $('#queryResults').css({
+            height: $el.height() + 'px'
+          });
+        }
+        $el.data('scrollToCol', null);
+        $el.data('scrollToRow', null);
+        $snip.find('.progress-snippet').animate(
+          {
+            height: '3px'
+          },
+          100
+        );
+        if ($el.hasClass('dt')) {
+          $el.removeClass('dt');
+          $('#eT' + options.snippet.id() + 'jHueTableExtenderClonedContainer').remove();
+          $('#eT' + options.snippet.id() + 'jHueTableExtenderClonedContainerColumn').remove();
+          $('#eT' + options.snippet.id() + 'jHueTableExtenderClonedContainerCell').remove();
+          if ($el.hueDataTable()) {
+            $el.hueDataTable().fnDestroy();
+          }
+          $el.find('thead tr').empty();
+          $el.data('lockedRows', {});
+        }
+      });
+
+      $(document).on('renderDataError', (e, options) => {
+        huePubSub.publish(SHOW_NORMAL_RESULT_EVENT);
+      });
+
+      $(document).on('progress', (e, options) => {
+        if (options.data === 100) {
+          window.setTimeout(() => {
+            $('#snippet_' + options.snippet.id())
+              .find('.progress-snippet')
+              .animate(
+                {
+                  height: '0'
+                },
+                100,
+                () => {
+                  options.snippet.progress(0);
+                  huePubSub.publish(REDRAW_FIXED_HEADERS_EVENT);
+                }
+              );
+          }, 2000);
+        }
+      });
+
+      let hideTimeout = -1;
+      $(document).on('hideAutocomplete', () => {
+        window.clearTimeout(hideTimeout);
+        hideTimeout = window.setTimeout(() => {
+          const $aceAutocomplete = $('.ace_editor.ace_autocomplete');
+          if ($aceAutocomplete.is(':visible')) {
+            $aceAutocomplete.hide();
+          }
+        }, 100);
+      });
+
+      $(window).on('resize', () => {
+        huePubSub.publish('recalculate.name.description.width');
+      });
+    }
+  });
+};

+ 3 - 3
desktop/core/src/desktop/js/apps/notebook2/components/ko.snippetExecuteActions.js

@@ -28,18 +28,18 @@ const TEMPLATE = `
 <div class="snippet-execute-actions" data-test="${NAME}">
   <div class="btn-group">
     <!-- ko if: status() !== '${EXECUTION_STATUS.running}' -->
-    <button class="btn btn-primary btn-mini btn-execute" data-test="execute" data-bind="click: execute"><i class="fa fa-play fa-fw"></i> ${I18n(
+    <button class="btn btn-primary btn-mini btn-execute disable-feedback" data-test="execute" data-bind="click: execute"><i class="fa fa-play fa-fw"></i> ${I18n(
       'Execute'
     )}</button>
     <!-- /ko -->
     <!-- ko if: status() === '${EXECUTION_STATUS.running}' -->
     <!-- ko ifnot: stopping -->
-    <button class="btn btn-primary btn-mini btn-execute" data-test="stop" data-bind="click: stop"><i class="fa fa-stop fa-fw"></i> ${I18n(
+    <button class="btn btn-primary btn-mini btn-execute disable-feedback" data-test="stop" data-bind="click: stop"><i class="fa fa-stop fa-fw"></i> ${I18n(
       'Stop'
     )}</button>
     <!-- /ko -->
     <!-- ko if: stopping -->
-    <button class="btn btn-primary btn-mini btn-execute disabled"><i class="fa fa-spinner fa-spin fa-fw"></i> ${I18n(
+    <button class="btn btn-primary btn-mini btn-execute disable-feedback disabled"><i class="fa fa-spinner fa-spin fa-fw"></i> ${I18n(
       'Stop'
     )}</button>
     <!-- /ko -->

+ 73 - 121
desktop/core/src/desktop/js/apps/notebook2/components/ko.snippetResults.js

@@ -18,10 +18,13 @@ import ko from 'knockout';
 
 import componentUtils from 'ko/components/componentUtils';
 import DisposableComponent from 'ko/components/DisposableComponent';
+import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
 
 import 'apps/notebook2/components/resultChart/ko.resultChart';
 import 'apps/notebook2/components/resultGrid/ko.resultGrid';
+import { REDRAW_FIXED_HEADERS_EVENT } from 'apps/notebook2/components/resultGrid/ko.resultGrid';
+import { REDRAW_CHART_EVENT } from 'apps/notebook2/components/resultChart/ko.resultChart';
 
 export const NAME = 'snippet-results';
 
@@ -39,97 +42,25 @@ const isStringColumn = type =>
 // prettier-ignore
 const TEMPLATE = `
 <div class="snippet-row">
-  <div class="result-left-bar">
-    <!-- ko if: type() === 'table' && hasSomeResults() -->
-    <div class="snippet-actions" style="opacity:1">
-      <div style="margin-top:25px;">
-        <a class="snippet-side-btn" href="javascript: void(0)" data-bind="
-            click: function() { showGrid(true); huePubSub.publish('redraw.fixed.headers'); huePubSub.publish('table.extender.redraw'); },
-            css: { 'active': showGrid }
-          " title="${ I18n('Grid') }">
-          <i class="fa fa-fw fa-th"></i>
-        </a>
-      </div>
-
-      <div class="dropdown">
-        <a class="snippet-side-btn" style="padding-right:0" href="javascript: void(0)" data-bind="css: { 'active': showChart }, click: function() { showChart(true); }" >
-          <i class="hcha fa-fw hcha-bar-chart" data-bind="visible: chartType() === window.HUE_CHARTS.TYPES.BARCHART" title="${ I18n('Bars') }"></i>
-          <i class="hcha fa-fw hcha-timeline-chart" data-bind="visible: chartType() === window.HUE_CHARTS.TYPES.TIMELINECHART" title="${ I18n('Time') }"></i>
-          <i class="hcha fa-fw hcha-pie-chart" data-bind="visible: chartType() === window.HUE_CHARTS.TYPES.PIECHART" title="${ I18n('Pie') }"></i>
-          <i class="fa fa-fw fa-dot-circle-o" data-bind="visible: chartType() === window.HUE_CHARTS.TYPES.SCATTERCHART" title="${ I18n('Scatter') }"></i>
-          <i class="fa fa-fw fa-map-marker" data-bind="visible: chartType() === window.HUE_CHARTS.TYPES.MAP" title="${ I18n('Marker Map') }"></i>
-          <i class="hcha fa-fw hcha-map-chart" data-bind="visible: chartType() === window.HUE_CHARTS.TYPES.GRADIENTMAP" title="${ I18n('Gradient Map') }"></i>
-        </a>
-        <a class="dropdown-toggle snippet-side-btn" style="padding:0" data-toggle="dropdown" href="javascript: void(0)" data-bind="css: { 'active': showChart }">
-          <i class="fa fa-caret-down"></i>
-        </a>
-
-        <ul class="dropdown-menu less-padding">
-          <li>
-            <a href="javascript:void(0)" data-bind="css: { 'active': chartType() === window.HUE_CHARTS.TYPES.BARCHART }, click: function() { showChart(true); chartType(window.HUE_CHARTS.TYPES.BARCHART); }">
-              <i class="hcha hcha-bar-chart"></i> ${ I18n('Bars') }
-            </a>
-          </li>
-          <li data-bind="visible: cleanedDateTimeMeta().length > 0">
-            <a href="javascript:void(0)" data-bind="css: { 'active': chartType() === window.HUE_CHARTS.TYPES.TIMELINECHART }, click: function() { showChart(true); chartType(window.HUE_CHARTS.TYPES.TIMELINECHART); }">
-              <i class="hcha hcha-timeline-chart"></i> ${ I18n('Time') }
-            </a>
-          </li>
-          <li>
-            <a href="javascript:void(0)" data-bind="css: { 'active': chartType() === window.HUE_CHARTS.TYPES.PIECHART }, click: function() { showChart(true); chartType(window.HUE_CHARTS.TYPES.PIECHART); }">
-              <i class="hcha hcha-pie-chart"></i> ${ I18n('Pie') }
-            </a>
-          </li>
-          <li>
-            <a href="javascript:void(0)" data-bind="css: { 'active': chartType() === window.HUE_CHARTS.TYPES.SCATTERCHART }, click: function() { showChart(true); chartType(window.HUE_CHARTS.TYPES.SCATTERCHART); }">
-              <i class="fa fa-fw fa-dot-circle-o chart-icon"></i> ${ I18n('Scatter') }
-            </a>
-          </li>
-          <li>
-            <a href="javascript:void(0)" data-bind="css: { 'active': chartType() === window.HUE_CHARTS.TYPES.MAP }, click: function() { showChart(true); chartType(window.HUE_CHARTS.TYPES.MAP); }">
-              <i class="fa fa-fw fa-map-marker chart-icon"></i> ${ I18n('Marker Map') }
-            </a>
-          </li>
-          <li>
-            <a href="javascript:void(0)" data-bind="css: {' active': chartType() === window.HUE_CHARTS.TYPES.GRADIENTMAP }, click: function() { showChart(true); chartType(window.HUE_CHARTS.TYPES.GRADIENTMAP); }">
-              <i class="hcha hcha-map-chart"></i> ${ I18n('Gradient Map') }
-            </a>
-          </li>
-        </ul>
-      </div>
-
-      <div>
-        <a class="snippet-side-btn" href="javascript:void(0)" data-bind="
-            toggle: isResultSettingsVisible,
-            publish: 'chart.hard.reset',
-            css: { 'blue' : isResultSettingsVisible }
-          " title="${ I18n('Columns') }">
-          <!-- ko if: isResultSettingsVisible() -->
-          <i class="fa fa-fw fa-chevron-left"></i>
-          <!-- /ko -->
-          <!-- ko ifnot: isResultSettingsVisible() -->
-          <i class="fa fa-fw fa-columns"></i>
-          <!-- /ko -->
-        </a>
-      </div>
-
-      <!-- ko if: false && window.ENABLE_DOWNLOAD -->
-        <div data-bind="
-            component: {
-              name: 'downloadSnippetResults',
-              params: {
-                gridSideBtn: false,
-                snippet: $data,
-                notebook: $parent 
-              } 
-            }
-          " style="display:inline-block;"></div>
-      <!-- /ko -->
+  <div class="result-actions">
+    <div class="btn-group">
+      <button class="btn btn-editor btn-mini disable-feedback" data-bind="toggle: showGrid, css: { 'active': showGrid }"><i class="fa fa-fw fa-th"></i> ${ I18n('Grid') }</button>
+      <button class="btn btn-editor btn-mini disable-feedback" data-bind="toggle: showChart, css: { 'active': showChart }"><i class="hcha fa-fw hcha-bar-chart"></i> ${ I18n('Chart') }</button>
+    </div>
+    <div class="btn-group pull-right">
+      <button class="btn btn-editor btn-mini disable-feedback" data-bind="toggle: isResultFullScreenMode">
+        <!-- ko if: isResultFullScreenMode -->
+        <i class="fa fa-compress"></i> ${ I18n('Collapse') }
+        <!-- /ko -->
+        <!-- ko ifnot: isResultFullScreenMode -->
+        <i class="fa fa-expand"></i> ${ I18n('Expand') }
+        <!-- /ko -->
+      </button>
     </div>
-    <!-- /ko -->
   </div>
+  
   <div class="result-body">
-    <div data-bind="visible: type() !== 'table'" style="display:none; max-height: 400px; margin: 10px 0; overflow-y: auto">
+    <div data-bind="visible: type() !== 'table'" style="display:none; margin: 10px 0; overflow-y: auto">
       <!-- ko if: data().length && data()[0][1] != "" -->
       <pre data-bind="text: data()[0][1]" class="no-margin-bottom"></pre>
       <!-- /ko -->
@@ -144,35 +75,37 @@ const TEMPLATE = `
       </ul>
       <!-- /ko -->
     </div>
-
-    <div class="table-results" data-bind="visible: type() === 'table'" style="display: none; max-height: 400px; min-height: 290px;">
-      <div data-bind="visible: showGrid" style="display: none;">
-        <!-- ko component: { name: 'result-grid', params: {
-          data: data,
-          editorMode: editorMode,
-          fetchResult: fetchResult,
-          hasMore: hasMore,
-          isPresentationMode: isPresentationMode,
-          isResultFullScreenMode: isResultFullScreenMode,
-          isResultSettingsVisible: isResultSettingsVisible,
-          meta: meta,
-          resultsKlass: resultsKlass,
-          status: status,
-        } } --><!-- /ko -->
+    <div class="table-results" data-bind="visible: type() === 'table'" style="display: none;">
+      <div data-bind="visible: showGrid" style="display: none; position: relative;">
+        <!-- ko component: { 
+          name: 'result-grid',
+          params: {
+            data: data,
+            editorMode: editorMode,
+            fetchResult: fetchResult,
+            hasMore: hasMore,
+            isPresentationMode: isPresentationMode,
+            isResultFullScreenMode: isResultFullScreenMode,
+            meta: meta,
+            resultsKlass: resultsKlass,
+            status: status
+          }
+        } --><!-- /ko -->
       </div>
-      <div data-bind="visible: showChart" style="display: none;">
-        <!-- ko component: { name: 'result-chart', params: {
-          chartType: chartType,
-          cleanedMeta: cleanedMeta,
-          cleanedDateTimeMeta: cleanedDateTimeMeta,
-          cleanedNumericMeta: cleanedNumericMeta,
-          cleanedStringMeta: cleanedStringMeta,
-          data: data,
-          id: id,
-          isResultSettingsVisible: isResultSettingsVisible,
-          meta: meta,
-          showChart: showChart
-        } } --><!-- /ko -->
+      <div data-bind="visible: showChart" style="display: none; position: relative;">
+        <!-- ko component: {
+          name: 'result-chart',
+          params: {
+            cleanedMeta: cleanedMeta,
+            cleanedDateTimeMeta: cleanedDateTimeMeta,
+            cleanedNumericMeta: cleanedNumericMeta,
+            cleanedStringMeta: cleanedStringMeta,
+            data: data,
+            id: id,
+            meta: meta,
+            showChart: showChart
+          }
+        } --><!-- /ko -->
       </div>
     </div>
   </div>
@@ -184,14 +117,9 @@ class SnippetResults extends DisposableComponent {
     super();
     this.element = element;
 
-    // For this and possibly chart and grid
-    this.chartType = params.chartType;
     this.type = params.type; // result
     this.hasSomeResults = params.hasSomeResults; // result
     this.images = params.images; // result
-    this.showGrid = params.showGrid;
-    this.showChart = params.showChart;
-    this.isResultSettingsVisible = params.isResultSettingsVisible;
     this.data = params.data; // result
     this.meta = params.meta; // result
 
@@ -207,6 +135,30 @@ class SnippetResults extends DisposableComponent {
     // Chart specific
     this.id = params.id;
 
+    this.showGrid = ko.observable(true); // TODO: Should be persisted
+    this.showChart = ko.observable(false); // TODO: Should be persisted
+
+    this.trackKoSub(
+      this.showChart.subscribe(val => {
+        if (val) {
+          this.showGrid(false);
+          huePubSub.publish(REDRAW_CHART_EVENT);
+          huePubSub.publish('editor.chart.shown', this);
+        }
+      })
+    );
+
+    this.trackKoSub(
+      this.showGrid.subscribe(val => {
+        if (val) {
+          this.showChart(false);
+          huePubSub.publish('editor.grid.shown', this);
+          huePubSub.publish(REDRAW_FIXED_HEADERS_EVENT);
+          huePubSub.publish('table.extender.redraw');
+        }
+      })
+    );
+
     this.cleanedMeta = ko.pureComputed(() => this.meta().filter(item => item.name !== ''));
 
     this.cleanedDateTimeMeta = ko.pureComputed(() =>

+ 143 - 47
desktop/core/src/desktop/js/apps/notebook2/components/resultChart/ko.resultChart.js

@@ -19,6 +19,7 @@ import ko from 'knockout';
 import componentUtils from 'ko/components/componentUtils';
 import DisposableComponent from 'ko/components/DisposableComponent';
 import hueAnalytics from 'utils/hueAnalytics';
+import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
 import {
   leafletMapChartTransformer,
@@ -28,6 +29,9 @@ import {
   scatterChartTransformer,
   timelineChartTransformer
 } from './chartTransformers';
+import $ from 'jquery';
+import { UUID } from 'utils/hueUtils';
+import { REDRAW_CHART_EVENT } from 'apps/notebook2/events';
 
 export const NAME = 'result-chart';
 
@@ -66,14 +70,74 @@ export const CHART_TIMELINE_TYPE = {
 
 // prettier-ignore
 const TEMPLATE = `
-<div>
-  <div class="column-side" style="position:relative; white-space: nowrap;" data-bind="
-      visible: isResultSettingsVisible,
-      css: { 'span3 result-settings': isResultSettingsVisible, 'hidden': ! isResultSettingsVisible() }
-    ">
+<div class="result-actions-append">
+  <div class="btn-group">
+    <button class="btn btn-mini btn-editor dropdown-toggle" data-toggle="dropdown">
+      <!-- ko if: isBarChart -->
+      <i class="hcha fa-fw hcha-bar-chart"></i> ${ I18n('Bar Chart') }
+      <!-- /ko -->
+      <!-- ko if: isTimelineChart -->
+      <i class="hcha fa-fw hcha-timeline-chart"></i> ${ I18n('Timeline Chart') }
+      <!-- /ko -->
+      <!-- ko if: isPieChart -->
+      <i class="hcha fa-fw hcha-pie-chart"></i> ${ I18n('Pie Chart') }
+      <!-- /ko -->
+      <!-- ko if: isScatterChart -->
+      <i class="fa fa-fw fa-dot-circle-o"></i> ${ I18n('Scatter Plot') }
+      <!-- /ko -->
+      <!-- ko if: isMapChart -->
+      <i class="fa fa-fw fa-map-marker"></i> ${ I18n('Marker Map') }
+      <!-- /ko -->
+      <!-- ko if: isGradientMapChart -->
+      <i class="hcha fa-fw hcha-map-chart"></i> ${ I18n('Gradient Map') }
+      <!-- /ko -->
+      <span class="caret"></span>
+    </button>
+    <ul class="dropdown-menu">
+      <li>
+        <a href="javascript:void(0);" data-bind="click: function() { chartType(window.HUE_CHARTS.TYPES.BARCHART); }">
+          <i class="hcha fa-fw hcha-bar-chart"></i> ${ I18n('Bar Chart') }
+        </a>
+      </li>
+      <li data-bind="if: cleanedDateTimeMeta().length">
+        <a href="javascript:void(0);" data-bind="click: function() { chartType(window.HUE_CHARTS.TYPES.TIMELINECHART); }">
+          <i class="hcha fa-fw hcha-timeline-chart"></i> ${ I18n('Timeline Chart') }
+        </a>
+      </li>
+      <li>
+        <a href="javascript:void(0);" data-bind="click: function() { chartType(window.HUE_CHARTS.TYPES.PIECHART); }">
+          <i class="hcha fa-fw hcha-pie-chart"></i> ${ I18n('Pie Chart') }
+        </a>
+      </li>
+      <li>
+        <a href="javascript:void(0);" data-bind="click: function() { chartType(window.HUE_CHARTS.TYPES.SCATTERCHART); }">
+          <i class="fa fa-fw fa-dot-circle-o"></i> ${ I18n('Scatter Plot') }
+        </a>
+      </li>
+      <li>
+        <a href="javascript:void(0);" data-bind="click: function() { chartType(window.HUE_CHARTS.TYPES.MAP); }">
+          <i class="fa fa-fw fa-map-marker"></i> ${ I18n('Marker Map') }
+        </a>
+      </li>
+      <li>
+        <a href="javascript:void(0);" data-bind="click: function() { chartType(window.HUE_CHARTS.TYPES.GRADIENTMAP); }">
+          <i class="hcha fa-fw hcha-map-chart"></i> ${ I18n('Gradient Map') }
+        </a>
+      </li>
+    </ul>
+  </div>
+  <div class="btn-group">
+    <button class="btn btn-editor btn-mini disable-feedback" data-bind="toggle: chartSettingsVisible, css: { 'active' : chartSettingsVisible }">
+      <i class="fa fa-cog"></i> ${ I18n('Settings') }
+    </button>
+  </div>
+</div>
+
+<div class="split-result-container">
+  <div class="result-settings-panel" style="display: none;" data-bind="visible: chartSettingsVisible">
     <div>
       <!-- ko if: chartType -->
-      <!-- ko if: isTimeLineChart() || isBarChart() -->
+      <!-- ko if: isTimelineChart() || isBarChart() -->
       <ul class="nav nav-list" style="border: none; background-color: #FFF">
         <li class="nav-header">${ I18n('type') }</li>
       </ul>
@@ -162,7 +226,7 @@ const TEMPLATE = `
 
       <div style="max-height: 220px" data-bind="
           delayedOverflow,
-          visible: ((isBarChart() && !chartXPivot()) || isLineChart() || isTimeLineChart())
+          visible: ((isBarChart() && !chartXPivot()) || isLineChart() || isTimelineChart())
         ">
         <ul class="unstyled" data-bind="foreach: cleanedNumericMeta" style="margin-bottom: 0">
           <li><label class="checkbox"><input type="checkbox" data-bind="checkedValue: name, checked: $parent.chartYMulti" /> <span data-bind="text: $data.name"></span></label></li>
@@ -376,41 +440,51 @@ const TEMPLATE = `
       <!-- /ko -->
       <!-- /ko -->
     </div>
-    <div class="resize-bar" style="top: 0; right: -10px; cursor: col-resize;"></div>
   </div>
-  <div class="grid-side" data-bind="css: { 'span9': isResultSettingsVisible, 'span12 nomargin': ! isResultSettingsVisible() }">
-    <div class="chart-container">
-      <h1 class="empty" data-bind="visible: !hasDataForChart()">${ I18n('Select the chart parameters on the left') }</h1>
-
-      <div data-bind="visible: hasDataForChart">
-        <!-- ko if: isPieChart -->
-        <div class="chart" data-bind="attr: { 'id': chartId }, pieChart: pieChartParams()" />
-        <!-- /ko -->
-
-        <!-- ko if: isBarChart -->
-        <div class="chart" data-bind="attr: { 'id': chartId }, barChart: barChartParams()" />
-        <!-- /ko -->
-
-        <!-- ko if: isLineChart -->
-        <div class="chart" data-bind="attr: { 'id': chartId }, lineChart: lineChartParams()" />
-        <!-- /ko -->
-
-        <!-- ko if: isTimeLineChart -->
-        <div class="chart" data-bind="attr:{ 'id': chartId }, timelineChart: timeLineChartParams()" />
-        <!-- /ko -->
-
-        <!-- ko if: isMapChart -->
-        <div class="chart" data-bind="attr:{ 'id': chartId }, leafletMapChart: leafletMapChartParams()" />
-        <!-- /ko -->
-
-        <!-- ko if: isGradientMapChart -->
-        <div class="chart" data-bind="attr:{ 'id': chartId }, mapChart: mapChartParams()" />
-        <!-- /ko -->
-
-        <!-- ko if: isScatterChart -->
-        <div class="chart" data-bind="attr:{ 'id': chartId }, scatterChart: scatterChartParams()" />
-        <!-- /ko -->
-      </div>
+  
+  <div class="split-result-resizer" style="display: none;" data-bind="
+      visible: chartSettingsVisible,
+      splitFlexDraggable : {
+        containerSelector: '.split-result-container',
+        sidePanelSelector: '.result-settings-panel',
+        sidePanelVisible: chartSettingsVisible,
+        orientation: 'left',
+        appName: 'result_chart',
+        onPosition: function() {  }
+      }
+    "><div class="resize-bar"></div></div>
+  
+  <div class="split-result-content chart-container">
+    <h1 class="empty" data-bind="visible: !hasDataForChart()" style="display:none">${ I18n('Select the chart parameters on the left') }</h1>
+
+    <div data-bind="visible: hasDataForChart" style="display:none">
+      <!-- ko if: isPieChart -->
+      <div class="chart" data-bind="attr: { 'id': chartId }, pieChart: pieChartParams()" />
+      <!-- /ko -->
+
+      <!-- ko if: isBarChart -->
+      <div class="chart" data-bind="attr: { 'id': chartId }, barChart: barChartParams()" />
+      <!-- /ko -->
+
+      <!-- ko if: isLineChart -->
+      <div class="chart" data-bind="attr: { 'id': chartId }, lineChart: lineChartParams()" />
+      <!-- /ko -->
+
+      <!-- ko if: isTimelineChart -->
+      <div class="chart" data-bind="attr:{ 'id': chartId }, timelineChart: timeLineChartParams()" />
+      <!-- /ko -->
+
+      <!-- ko if: isMapChart -->
+      <div class="chart" data-bind="attr:{ 'id': chartId }, leafletMapChart: leafletMapChartParams()" />
+      <!-- /ko -->
+
+      <!-- ko if: isGradientMapChart -->
+      <div class="chart" data-bind="attr:{ 'id': chartId }, mapChart: mapChartParams()" />
+      <!-- /ko -->
+
+      <!-- ko if: isScatterChart -->
+      <div class="chart" data-bind="attr:{ 'id': chartId }, scatterChart: scatterChartParams()" />
+      <!-- /ko -->
     </div>
   </div>
 </div>
@@ -422,14 +496,14 @@ class ResultChart extends DisposableComponent {
 
     this.data = params.data;
     this.id = params.id;
-    this.isResultSettingsVisible = params.isResultSettingsVisible;
-    this.chartType = params.chartType;
+    this.chartSettingsVisible = ko.observable(true);
 
     this.meta = params.meta;
     this.cleanedMeta = params.cleanedMeta;
     this.cleanedDateTimeMeta = params.cleanedDateTimeMeta;
     this.cleanedNumericMeta = params.cleanedNumericMeta;
     this.cleanedStringMeta = params.cleanedNumericMeta;
+    this.showChart = params.showChart;
 
     this.chartLimit = ko.observable().extend({ notify: 'always' }); // TODO: Should be persisted
     this.chartLimits = ko.observableArray([5, 10, 25, 50, 100]);
@@ -445,7 +519,7 @@ class ResultChart extends DisposableComponent {
     this.chartXPivot = ko.observable().extend({ notify: 'always' }); // TODO: Should be persisted
     this.chartYMulti = ko.observableArray(); // TODO: Should be persisted
     this.chartYSingle = ko.observable(); // TODO: Should be persisted
-    this.showChart = params.showChart;
+    this.chartType = ko.observable(window.HUE_CHARTS.TYPES.BARCHART); // TODO: Should be persisted
 
     this.chartId = ko.pureComputed(() => this.chartType() + '_' + this.id());
     this.isBarChart = ko.pureComputed(() => TYPES.BARCHART === this.chartType());
@@ -454,10 +528,10 @@ class ResultChart extends DisposableComponent {
     this.isScatterChart = ko.pureComputed(() => TYPES.SCATTERCHART === this.chartType());
     this.isGradientMapChart = ko.pureComputed(() => TYPES.GRADIENTMAP === this.chartType());
     this.isPieChart = ko.pureComputed(() => TYPES.PIECHART === this.chartType());
-    this.isTimeLineChart = ko.pureComputed(() => TYPES.TIMELINECHART === this.chartType());
+    this.isTimelineChart = ko.pureComputed(() => TYPES.TIMELINECHART === this.chartType());
 
     this.hasDataForChart = ko.pureComputed(() => {
-      if (this.isBarChart() || this.isLineChart() || this.isTimeLineChart()) {
+      if (this.isBarChart() || this.isLineChart() || this.isTimelineChart()) {
         return (
           typeof this.chartX() !== 'undefined' &&
           this.chartX() !== null &&
@@ -472,6 +546,8 @@ class ResultChart extends DisposableComponent {
       );
     });
 
+    this.trackKoSub(this.chartType.subscribe(this.redrawChart.bind(this)));
+
     this.trackKoSub(
       this.meta.subscribe(() => {
         if (this.chartX()) {
@@ -509,7 +585,7 @@ class ResultChart extends DisposableComponent {
       if (this.isBarChart() || this.isGradientMapChart()) {
         return this.cleanedMeta();
       }
-      if (this.isTimeLineChart()) {
+      if (this.isTimelineChart()) {
         return this.cleanedDateTimeMeta();
       }
       return this.cleanedNumericMeta();
@@ -581,6 +657,21 @@ class ResultChart extends DisposableComponent {
       size: this.chartScatterSize(),
       group: this.chartScatterGroup()
     }));
+
+    this.trackPubSub(huePubSub.subscribe(REDRAW_CHART_EVENT, this.redrawChart.bind(this)));
+
+    const resizeId = UUID();
+    let resizeTimeout = -1;
+    $(window).on('resize.' + resizeId, () => {
+      window.clearTimeout(resizeTimeout);
+      resizeTimeout = window.setTimeout(() => {
+        this.redrawChart();
+      }, 100);
+    });
+    this.disposals.push(() => {
+      window.clearTimeout(resizeTimeout);
+      $(window).off('resize.' + resizeId);
+    });
   }
 
   guessMetaField(originalField) {
@@ -673,6 +764,11 @@ class ResultChart extends DisposableComponent {
       }
     }
   }
+
+  redrawChart() {
+    this.chartX.notifySubscribers();
+    this.chartX.valueHasMutated();
+  }
 }
 
 componentUtils.registerComponent(NAME, ResultChart, TEMPLATE);

+ 119 - 111
desktop/core/src/desktop/js/apps/notebook2/components/resultGrid/ko.resultGrid.js

@@ -21,16 +21,45 @@ import componentUtils from 'ko/components/componentUtils';
 import DisposableComponent from 'ko/components/DisposableComponent';
 import I18n from 'utils/i18n';
 import huePubSub from 'utils/huePubSub';
-import { bootstrapRatios, defer, sleep } from 'utils/hueUtils';
+import { defer, sleep } from 'utils/hueUtils';
+import {
+  HIDE_FIXED_HEADERS_EVENT,
+  REDRAW_FIXED_HEADERS_EVENT,
+  SHOW_GRID_SEARCH_EVENT,
+  SHOW_NORMAL_RESULT_EVENT
+} from 'apps/notebook2/events';
 
 export const NAME = 'result-grid';
 
 // prettier-ignore
 const TEMPLATE = `
-<div>
-  <div class="column-side" style="position: relative; white-space: nowrap;" data-bind="
-      visible: isResultSettingsVisible,
-      css: { 'span3 result-settings': isResultSettingsVisible, 'hidden': !isResultSettingsVisible() }">
+<div class="result-actions-append">
+  <div class="btn-group">
+    <button class="btn btn-editor btn-mini disable-feedback" data-bind="click: showSearch.bind($data), css: { 'disabled': !data().length }">
+      <i class="fa fa-search"></i> ${ I18n('Search') }
+    </button>
+  </div>
+  <div class="btn-group">
+    <button class="btn btn-editor btn-mini disable-feedback" data-bind="toggle: columnsVisible, css: { 'active' : columnsVisible }">
+      <i class="fa fa-columns"></i> ${ I18n('Columns') }
+    </button>
+  </div>
+  <!-- ko if: false && window.ENABLE_DOWNLOAD -->
+    <div data-bind="
+        component: {
+          name: 'downloadSnippetResults',
+          params: {
+            gridSideBtn: false,
+            snippet: $data,
+            notebook: $parent 
+          } 
+        }
+      " style="display:inline-block;"></div>
+  <!-- /ko -->
+</div>
+
+<div class="split-result-container">
+  <div class="result-settings-panel" style="display: none;" data-bind="visible: columnsVisible">
     <div class="snippet-grid-settings" data-bind="delayedOverflow">
       <table class="table table-condensed margin-top-10 no-border">
         <thead>
@@ -86,43 +115,53 @@ const TEMPLATE = `
         <tr>
           <td colspan="3">
             <div class="margin-top-10 muted meta-noresults" data-bind="visible: !filteredMeta().length">
-              ${ I18n('No results found') }
+              ${ I18n('No columns found') }
             </div>
           </td>
         </tr>
         </tfoot>
       </table>
     </div>
-    <div class="resize-bar" style="top: 0; right: -10px; cursor: col-resize;"></div>
   </div>
-  <div class="grid-side" data-bind="css: { 'span9': isResultSettingsVisible, 'span12 nomargin': !isResultSettingsVisible() }">
-    <div data-bind="delayedOverflow: 'slow', css: resultsKlass">
-      <table class="table table-condensed resultTable">
-        <thead>
-        <tr data-bind="foreach: meta">
-          <th class="sorting" data-bind="
-              text: ($index() == 0 ? '&nbsp;' : $data.name),
-              css: typeof cssClass != 'undefined' ? cssClass : 'sort-string',
-              attr: { title: $data.type },
-              style: { 
-                'width': $index() == 0 ? '1%' : '',
-                'height': $index() == 0 ? '32px' : ''
-              },
-              click: function(obj, e) { $(e.target).parents('table').trigger('sort', obj); }
-            "></th>
-        </tr>
-        </thead>
-        <tbody>
-        </tbody>
-      </table>
-      <div style="display:none;" data-bind="
-          visible: status() == 'expired' && data() && data().length > 99,
-          css: resultsKlass
-        ">
-        <pre class="margin-top-10"><i class="fa fa-check muted"></i> ${ I18n("Results have expired, rerun the query if needed.") }</pre>
-      </div>
-      <span data-bind="template: { afterRender: resultTableRendered.bind($data) }"></span>
+
+  <div class="split-result-resizer" style="display: none;" data-bind="
+      visible: columnsVisible,
+      splitFlexDraggable : {
+        containerSelector: '.split-result-container',
+        sidePanelSelector: '.result-settings-panel',
+        sidePanelVisible: columnsVisible,
+        orientation: 'left',
+        appName: 'result_grid',
+        onPosition: function() { redrawFixedHeaders(); }
+      }
+    "><div class="resize-bar"></div></div>
+    
+  <div class="split-result-content" data-bind="delayedOverflow: 'slow', css: resultsKlass">
+    <table class="table table-condensed resultTable">
+      <thead>
+      <tr data-bind="foreach: meta">
+        <th class="sorting" data-bind="
+            text: ($index() == 0 ? '&nbsp;' : $data.name),
+            css: typeof cssClass != 'undefined' ? cssClass : 'sort-string',
+            attr: { title: $data.type },
+            style: { 
+              'width': $index() == 0 ? '1%' : '',
+              'height': $index() == 0 ? '32px' : ''
+            },
+            click: function(obj, e) { $(e.target).parents('table').trigger('sort', obj); }
+          "></th>
+      </tr>
+      </thead>
+      <tbody>
+      </tbody>
+    </table>
+    <div style="display:none;" data-bind="
+        visible: status() == 'expired' && data() && data().length > 99,
+        css: resultsKlass
+      ">
+      <pre class="margin-top-10"><i class="fa fa-check muted"></i> ${ I18n("Results have expired, rerun the query if needed.") }</pre>
     </div>
+    <span data-bind="template: { afterRender: resultTableRendered.bind($data) }"></span>
   </div>
 </div>
 `;
@@ -139,7 +178,7 @@ class ResultGrid extends DisposableComponent {
     this.fetchResult = params.fetchResult;
 
     this.status = params.status;
-    this.isResultSettingsVisible = params.isResultSettingsVisible;
+    this.columnsVisible = ko.observable(true);
     this.isMetaFilterVisible = ko.observable(false);
     this.metaFilter = ko.observable();
     this.resultsKlass = params.resultsKlass;
@@ -158,6 +197,12 @@ class ResultGrid extends DisposableComponent {
       });
     };
 
+    this.trackKoSub(
+      this.columnsVisible.subscribe(() => {
+        defer(this.redrawFixedHeaders.bind(this));
+      })
+    );
+
     this.trackKoSub(
       this.meta.subscribe(() => {
         adaptMeta();
@@ -386,7 +431,7 @@ class ResultGrid extends DisposableComponent {
           if (window.BANNER_TOP_HTML) {
             margin += 31;
           }
-          if (this.isResultSettingsVisible()) {
+          if (this.columnsVisible()) {
             $snippet.find('.snippet-grid-settings').css({
               height:
                 this.isPresentationMode() || !this.editorMode()
@@ -434,21 +479,26 @@ class ResultGrid extends DisposableComponent {
     });
 
     this.trackKoSub(
-      this.isResultSettingsVisible.subscribe(newValue => {
+      this.columnsVisible.subscribe(newValue => {
         if (newValue) {
           dataScroll();
         }
       })
     );
 
-    // huePubSub.subscribeOnce('chart.hard.reset', () => {
-    //   // hard reset once the default opened chart
-    //   const oldChartX = snippet.chartX();
-    //   snippet.chartX(null);
-    //   window.setTimeout(() => {
-    //     snippet.chartX(oldChartX);
-    //   }, 0);
-    // });
+    this.trackPubSub(
+      huePubSub.subscribe(REDRAW_FIXED_HEADERS_EVENT, this.redrawFixedHeaders.bind(this))
+    );
+
+    this.trackPubSub(huePubSub.subscribe(SHOW_GRID_SEARCH_EVENT, this.showSearch.bind(this)));
+
+    this.trackPubSub(
+      huePubSub.subscribe(HIDE_FIXED_HEADERS_EVENT, this.hideFixedHeaders.bind(this))
+    );
+
+    this.trackPubSub(
+      huePubSub.subscribe(SHOW_NORMAL_RESULT_EVENT, this.showNormalResult.bind(this))
+    );
 
     return this.hueDatatable;
   }
@@ -461,66 +511,24 @@ class ResultGrid extends DisposableComponent {
     return this.getSnippetElement().find('.resultTable');
   }
 
-  resetResultsResizer() {
+  hideFixedHeaders() {
     const $snippet = this.getSnippetElement();
-    $snippet
-      .find('.table-results .column-side')
-      .width(bootstrapRatios.span3() + '%')
-      .data('newWidth', bootstrapRatios.span3());
-    if (this.isResultSettingsVisible()) {
-      $snippet
-        .find('.table-results .grid-side')
-        .data('newWidth', bootstrapRatios.span9())
-        .width(bootstrapRatios.span9() + '%');
-    } else {
-      $snippet
-        .find('.table-results .grid-side')
-        .data('newWidth', 100)
-        .width('100%');
-    }
-    $snippet.find('.resize-bar').css('left', '');
-    try {
-      $snippet.find('.resize-bar').draggable('destroy');
-    } catch (e) {}
-
-    let initialPosition = 0;
-
-    $snippet.find('.resize-bar').draggable({
-      axis: 'x',
-      containment: $snippet.find('.table-results'),
-      create: () => {
-        const $snippet = this.getSnippetElement();
-        initialPosition = $snippet.find('.resize-bar').position().left;
-        $snippet.find('.table-results .column-side').data('newWidth', bootstrapRatios.span3());
-        $snippet
-          .find('.meta-filter')
-          .width($snippet.find('.table-results .column-side').width() - 28);
-      },
-      drag: (event, ui) => {
-        const $snippet = this.getSnippetElement();
-        if (initialPosition === 0) {
-          initialPosition = $snippet.find('.resize-bar').position().left;
-        }
-        ui.position.left = Math.max(150, ui.position.left);
-        const newSpan3Width = (ui.position.left * bootstrapRatios.span3()) / initialPosition;
-        const newSpan9Width = 100 - newSpan3Width - bootstrapRatios.margin();
-        $snippet
-          .find('.table-results .column-side')
-          .width(newSpan3Width + '%')
-          .data('newWidth', newSpan3Width);
-        $snippet
-          .find('.table-results .grid-side')
-          .width(newSpan9Width + '%')
-          .data('newWidth', newSpan9Width);
-        $snippet
-          .find('.meta-filter')
-          .width($snippet.find('.table-results .column-side').width() - 28);
-      },
-      stop: () => {
-        this.redrawFixedHeaders();
-        huePubSub.publish('resize.leaflet.map');
-      }
-    });
+    $snippet.find('.jHueTableExtenderClonedContainer').hide();
+    $snippet.find('.jHueTableExtenderClonedContainerColumn').hide();
+    $snippet.find('.jHueTableExtenderClonedContainerCell').hide();
+    $snippet.find('.fixed-header-row').hide();
+    $snippet.find('.fixed-first-cell').hide();
+    $snippet.find('.fixed-first-column').hide();
+  }
+
+  showFixedHeaders() {
+    const $snippet = this.getSnippetElement();
+    $snippet.find('.jHueTableExtenderClonedContainer').show();
+    $snippet.find('.jHueTableExtenderClonedContainerColumn').show();
+    $snippet.find('.jHueTableExtenderClonedContainerCell').show();
+    $snippet.find('.fixed-header-row').show();
+    $snippet.find('.fixed-first-cell').show();
+    $snippet.find('.fixed-first-column').show();
   }
 
   redrawFixedHeaders() {
@@ -534,12 +542,7 @@ class ResultGrid extends DisposableComponent {
       $(window.MAIN_SCROLLABLE).data('lastScroll', $(window.MAIN_SCROLLABLE).scrollTop());
       $(window.MAIN_SCROLLABLE).trigger('scroll');
     }
-    $snippet.find('.jHueTableExtenderClonedContainer').show();
-    $snippet.find('.jHueTableExtenderClonedContainerColumn').show();
-    $snippet.find('.jHueTableExtenderClonedContainerCell').show();
-    $snippet.find('.fixed-header-row').show();
-    $snippet.find('.fixed-first-cell').show();
-    $snippet.find('.fixed-first-column').show();
+    this.showFixedHeaders();
   }
 
   resizeToggleResultSettings(initial) {
@@ -598,7 +601,6 @@ class ResultGrid extends DisposableComponent {
         this.meta.notifySubscribers();
         $snippet.find('select').trigger('chosen:updated');
         dataTable = this.createDatatable();
-        this.resetResultsResizer();
         $resultTable.data('rendered', true);
       } else {
         dataTable = $resultTable.hueDataTable();
@@ -662,6 +664,12 @@ class ResultGrid extends DisposableComponent {
     $wrapper.find('.resultTable').css({ opacity: '1' });
   }
 
+  showSearch() {
+    if (this.hueDatatable) {
+      this.hueDatatable.fnShowSearch();
+    }
+  }
+
   toggleAllResultColumns(linkElement) {
     this.hueDatatable.fnToggleAllCols(linkElement.checked);
     this.hueDatatable.fnDraw();

+ 5 - 0
desktop/core/src/desktop/js/apps/notebook2/events.js

@@ -0,0 +1,5 @@
+export const REDRAW_CHART_EVENT = 'result.chart.redraw';
+export const HIDE_FIXED_HEADERS_EVENT = 'result.grid.hide.fixed.headers';
+export const REDRAW_FIXED_HEADERS_EVENT = 'result.grid.redraw.fixed.headers';
+export const SHOW_GRID_SEARCH_EVENT = 'result.grid.show.search';
+export const SHOW_NORMAL_RESULT_EVENT = 'result.grid.show.normal.results';

+ 4 - 5
desktop/core/src/desktop/js/apps/notebook2/notebookSerde.js

@@ -67,7 +67,6 @@ export const notebookToJSON = notebook =>
       aceCursorPosition: snippet.aceCursorPosition(),
       aceSize: snippet.aceSize(),
       associatedDocumentUuid: snippet.associatedDocumentUuid(),
-      chartData: snippet.chartData(),
       // chartLimit: snippet.chartLimit(), // TODO: Move somewhere else
       // chartMapHeat: snippet.chartMapHeat(), // TODO: Move somewhere else
       // chartMapLabel: snippet.chartMapLabel(), // TODO: Move somewhere else
@@ -87,7 +86,7 @@ export const notebookToJSON = notebook =>
       //   }
       // );
       //
-      chartType: snippet.chartType(),
+      // chartType: snippet.chartType(), // TODO: Move somewhere else
       // chartX: snippet.chartX(), // TODO: Move somewhere else
       // chartXPivot: snippet.chartXPivot(), // TODO: Move somewhere else
       // chartYMulti: snippet.chartYMulti(), // TODO: Move somewhere else
@@ -97,7 +96,7 @@ export const notebookToJSON = notebook =>
       database: snippet.database(),
       id: snippet.id(),
       is_redacted: snippet.is_redacted(),
-      isResultSettingsVisible: snippet.isResultSettingsVisible(),
+      //isResultSettingsVisible: snippet.isResultSettingsVisible(), // TODO: Move somewhere else
       lastAceSelectionRowOffset: snippet.lastAceSelectionRowOffset(),
       lastExecuted: snippet.lastExecuted(),
       name: snippet.name(),
@@ -107,8 +106,8 @@ export const notebookToJSON = notebook =>
       // result: ...,
       settingsVisible: snippet.settingsVisible(),
       // schedulerViewModel: ?
-      showChart: snippet.showChart(),
-      showGrid: snippet.showGrid(),
+      // showChart: snippet.showChart(), // TODO: Move somewhere else
+      // showGrid: snippet.showGrid(), // TODO: Move somewhere else
       showLogs: snippet.showLogs(),
       statement_raw: snippet.statement_raw(),
       statementPath: snippet.statementPath(),

+ 3 - 45
desktop/core/src/desktop/js/apps/notebook2/snippet.js

@@ -35,6 +35,7 @@ import Result from 'apps/notebook2/result';
 import sessionManager from 'apps/notebook2/execution/sessionManager';
 import SqlExecutable from 'apps/notebook2/execution/sqlExecutable';
 import { notebookToContextJSON, snippetToContextJSON } from 'apps/notebook2/notebookSerde';
+import { REDRAW_FIXED_HEADERS_EVENT } from 'apps/notebook2/components/resultGrid/ko.resultGrid';
 
 // TODO: Remove. Temporary here for debug
 window.SqlExecutable = SqlExecutable;
@@ -303,7 +304,7 @@ export default class Snippet {
 
     this.lastQueriesPage = 1;
     this.currentQueryTab.subscribe(newValue => {
-      huePubSub.publish('redraw.fixed.headers');
+      huePubSub.publish(REDRAW_FIXED_HEADERS_EVENT);
       huePubSub.publish('current.query.tab.switched', newValue);
       if (
         newValue === 'savedQueries' &&
@@ -754,11 +755,6 @@ export default class Snippet {
     if (!this.result.hasSomeResults()) {
       this.currentQueryTab('queryHistory');
     }
-    this.showGrid = ko.observable(snippet.showGrid !== false);
-    this.showChart = ko.observable(!!snippet.showChart);
-    if (!this.showGrid() && !this.showChart()) {
-      this.showGrid(true);
-    }
     let defaultShowLogs = true;
     if (this.parentVm.editorMode() && $.totalStorage('hue.editor.showLogs')) {
       defaultShowLogs = $.totalStorage('hue.editor.showLogs');
@@ -774,23 +770,8 @@ export default class Snippet {
       $(document).trigger('progress', { data: val, snippet: this });
     });
 
-    this.showGrid.subscribe(val => {
-      if (val) {
-        this.showChart(false);
-        huePubSub.publish('editor.grid.shown', this);
-      }
-    });
-
-    this.showChart.subscribe(val => {
-      if (val) {
-        this.showGrid(false);
-        this.isResultSettingsVisible(true);
-        $(document).trigger('forceChartDraw', this);
-        huePubSub.publish('editor.chart.shown', this);
-      }
-    });
     this.showLogs.subscribe(val => {
-      huePubSub.publish('redraw.fixed.headers');
+      huePubSub.publish(REDRAW_FIXED_HEADERS_EVENT);
       if (val) {
         this.getLogs();
       }
@@ -807,22 +788,6 @@ export default class Snippet {
 
     this.is_redacted = ko.observable(!!snippet.is_redacted);
 
-    this.chartType = ko.observable(snippet.chartType || window.HUE_CHARTS.TYPES.BARCHART);
-
-    this.chartData = ko.observableArray(snippet.chartData || []);
-
-    this.chartType.subscribe(() => {
-      $(document).trigger('forceChartDraw', this);
-    });
-
-    this.previousChartOptions = {};
-
-    this.isResultSettingsVisible = ko.observable(!!snippet.isResultSettingsVisible);
-
-    this.isResultSettingsVisible.subscribe(() => {
-      $(document).trigger('toggleResultSettings', this);
-    });
-
     this.settingsVisible = ko.observable(!!snippet.settingsVisible);
     this.saveResultsModalVisible = ko.observable(false);
 
@@ -1030,9 +995,6 @@ export default class Snippet {
 
     this.longOperationTimeout = -1;
 
-    this.lastExecutedStatements = undefined;
-    this.lastExecutedSelectionRange = undefined;
-
     this.lastCompatibilityRequest = undefined;
 
     this.isFetchingData = false;
@@ -1941,10 +1903,6 @@ export default class Snippet {
     this.showLongOperationWarning(false);
   }
 
-  toggleResultSettings() {
-    this.isResultSettingsVisible(!this.isResultSettingsVisible());
-  }
-
   uploadQuery(query_id) {
     $.post('/metadata/api/optimizer/upload/query', {
       query_id: query_id,

+ 9 - 6
desktop/core/src/desktop/js/ko/bindings/ko.splitFlexDraggable.js

@@ -35,20 +35,23 @@ ko.bindingHandlers.splitFlexDraggable = {
 
     const onPosition = options.onPosition || function() {};
 
+    let containerWidth = $container.width();
     $sidePanel.css('flex-basis', sidePanelWidth + 'px');
     $resizer.draggable({
       axis: 'x',
       containment: $container,
       start: function() {
         sidePanelWidth = $sidePanel.width();
+        containerWidth = $container.width();
       },
       drag: function(event, ui) {
-        if (isLeft) {
-          $sidePanel.css('flex-basis', Math.max(sidePanelWidth + ui.position.left, 200) + 'px');
-        } else {
-          $sidePanel.css('flex-basis', Math.max(sidePanelWidth - ui.position.left, 200) + 'px');
-        }
-        onPosition();
+        const flexBasis =
+          Math.min(
+            Math.max(sidePanelWidth + (isLeft ? ui.position.left : -ui.position.left), 200),
+            containerWidth
+          ) + 'px';
+        $sidePanel.css('flex-basis', flexBasis);
+        onPosition(flexBasis);
         ui.position.left = 0;
       },
       stop: function() {

+ 6 - 0
desktop/core/src/desktop/js/ko/components/DisposableComponent.js

@@ -25,6 +25,12 @@ export default class DisposableComponent {
     });
   }
 
+  trackPubSub(pubSub) {
+    this.disposals.push(() => {
+      pubSub.remove();
+    });
+  }
+
   addDisposalCallback(callback) {
     this.disposals.push(callback);
   }

文件差异内容过多而无法显示
+ 0 - 0
desktop/libs/notebook/src/notebook/static/notebook/css/notebook2.css


+ 79 - 52
desktop/libs/notebook/src/notebook/static/notebook/less/notebook2.less

@@ -248,27 +248,90 @@
     margin-top: 10px !important;
   }
 
-  .result-left-bar {
-    position: absolute;
-    display: inline-block;
-    height: auto;
-    left: 0;
-    top: 0;
-    bottom: 0;
-    width: 35px;
+  .result-actions {
+    padding: 5px;
+    height: 30px;
   }
 
-  .result-body {
-    margin-left: 35px;
+  .result-actions .btn-group,
+  .result-body .result-actions-append .btn-group {
+    .btn-editor:first-of-type {
+      border-top-left-radius: 2px;
+      border-bottom-left-radius: 2px;
+    }
+
+    .btn-editor:last-of-type {
+      border-top-right-radius: 2px;
+      border-bottom-right-radius: 2px;
+    }
   }
 
-  .result-settings {
-    .nav-list .nav-header {
-      margin-bottom: -3px;
-      padding-left: 3px;
+  .result-body {
+    padding: 0 5px;
+
+    .result-actions-append {
+      position: absolute;
+      left: 150px;
+      top: -35px;
     }
-    .checkbox {
-      padding-top: 0;
+
+    .result-settings {
+      .nav-list .nav-header {
+        margin-bottom: -3px;
+        padding-left: 3px;
+      }
+
+      .checkbox {
+        padding-top: 0;
+      }
+    }
+
+    .split-result-container {
+      .display-flex();
+
+      position: relative;
+      width: 100%;
+
+      .result-settings-panel {
+        .flex(0 0 200px);
+
+        min-width: 200px;
+      }
+
+      .split-result-resizer {
+        .flex(0 0 3px);
+
+        cursor: ew-resize;
+      }
+
+      .split-result-content {
+        .flex(1);
+
+        .resultTable th {
+          color: @cui-white;
+        }
+
+        .dataTables_wrapper .resultTable th {
+          color: @cui-gray-800;
+        }
+
+        .results-images {
+          border-left: 5px solid @cui-default-border-color;
+          background-color: @cui-gray-050;
+          padding: 5px;
+        }
+
+        .table-results {
+          position: relative;
+          width: 100%;
+          min-height: 290px;
+
+          table > thead > tr > td:first-child,
+          table > tbody > tr > td:first-child {
+            background-color: @cui-gray-050;
+          }
+        }
+      }
     }
   }
 
@@ -578,23 +641,6 @@
     color: @cui-gray-300;
   }
 
-  .toggle-result-settings {
-    float: left;
-    padding: 3px;
-    margin-top: 31px;
-    width: 10px;
-    text-align: center;
-    cursor: pointer;
-  }
-
-  .toggle-result-settings .close {
-    margin-right: 2px;
-  }
-
-  .toggle-result-settings .open {
-    margin-left: 2px;
-  }
-
   .chart-icon {
     width: 15px;
     text-align: center;
@@ -781,13 +827,6 @@
     filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='@cui-pink-050', endColorstr='#00ffffff', GradientType=1)
   }
 
-
-  .results-images {
-    border-left: 5px solid @cui-default-border-color;
-    background-color: @cui-gray-050;
-    padding: 5px;
-  }
-
   .alert-notebook, .alert-gradient {
     border: none;
     margin-top: -3px;
@@ -870,14 +909,6 @@
     color: @cui-gray-500;
   }
 
-  .resultTable th {
-    color: @cui-white;
-  }
-
-  .dataTables_wrapper .resultTable th {
-    color: @cui-gray-800;
-  }
-
   .navbar .editor-nav > li > a {
     max-width: 600px;
   }
@@ -971,10 +1002,6 @@
     color: @cui-orange-200;
   }
 
-  .table-results table > thead > tr > td:first-child, .table-results table > tbody > tr > td:first-child {
-    background-color: @cui-gray-050;
-  }
-
   #helpModal th:first-child {
     width: 25%;
   }

+ 31 - 26
desktop/libs/notebook/src/notebook/templates/editor_components2.mako

@@ -508,7 +508,22 @@
         <pre data-bind="visible: result.logs() && result.logs().length > 0, text: result.logs, logScroller: result.logs, logScrollerVisibilityEvent: showLogs" class="logs logs-bigger logs-populated"></pre>
       </span>
       </div>
-      <div class="snippet-log-resizer" data-bind="visible: result.logs().length > 0, logResizer: {parent: '.snippet-log-container', target: '.logs-populated', mainScrollable: MAIN_SCROLLABLE, onStart: hideFixedHeaders, onResize: function(){ hideFixedHeaders(); redrawFixedHeaders(500); }, minHeight: 50}">
+      <div class="snippet-log-resizer" data-bind="
+          visible: result.logs().length > 0,
+          logResizer: {
+            parent: '.snippet-log-container',
+            target: '.logs-populated',
+            mainScrollable: MAIN_SCROLLABLE,
+            onStart: function () { huePubSub.publish('result.grid.hide.fixed.headers') },
+            onResize: function() {
+              huePubSub.publish('result.grid.hide.fixed.headers');
+              window.setTimeout(function () {
+                huePubSub.publish('result.grid.redraw.fixed.headers');
+              }, 500);
+            },
+            minHeight: 50
+          }
+        ">
         <i class="fa fa-ellipsis-h"></i>
       </div>
     </div>
@@ -575,19 +590,6 @@
               (<span data-bind="text: result.rows().toLocaleString() + (type() == 'impala' && result.rows() == 1024 ? '+' : '')" title="${ _('Number of rows') }"></span>)
               <!-- /ko -->
             </a>
-            <div style="margin-left: -15px;" class="inline-block">
-              <!-- ko if: showGrid -->
-              <div class="inline-block inactive-action pointer visible-on-hover" title="${_('Search the results')}" data-bind="click: function(data, e){ $(e.target).parents('.snippet').find('.resultTable').hueDataTable().fnShowSearch() }">
-                <i class="snippet-icon fa fa-search"></i>
-              </div>
-              <!-- /ko -->
-              <div class="inline-block inactive-action pointer visible-on-hover" title="${_('Expand results')}" rel="tooltip" data-bind="css: { 'margin-left-10': !showGrid()}, visible: !$root.isResultFullScreenMode(), click: function(){ $root.isResultFullScreenMode(true); }">
-                <i class="snippet-icon fa fa-expand"></i>
-              </div>
-              <div class="inline-block inactive-action pointer visible-on-hover" title="${_('Collapse results')}" rel="tooltip" data-bind="visible: $root.isResultFullScreenMode(), click: function(){ $root.isResultFullScreenMode(false); }">
-                <i class="snippet-icon fa fa-compress"></i>
-              </div>
-            </div>
           </li>
           <!-- ko if: result.explanation().length > 0 -->
           <li data-bind="click: function(){ currentQueryTab('queryExplain'); }, css: {'active': currentQueryTab() == 'queryExplain'}"><a class="inactive-action" href="#queryExplain" data-toggle="tab">${_('Explain')}</a></li>
@@ -733,7 +735,6 @@
           <div class="tab-pane" id="queryResults" data-bind="css: {'active': currentQueryTab() == 'queryResults'}">
             <!-- ko if: ['text', 'jar', 'py', 'markdown'].indexOf(type()) === -1 -->
               <!-- ko component: { name: 'snippet-results', params: {
-                chartType: chartType,
                 data: result.data,
                 editorMode: parentVm.editorMode,
                 fetchResult: result.fetchMoreRows,
@@ -743,12 +744,8 @@
                 images: result.images,
                 isPresentationMode: parentNotebook.isPresentationMode,
                 isResultFullScreenMode: parentVm.isResultFullScreenMode,
-                isResultSettingsVisible: isResultSettingsVisible,
-                isResultSettingsVisible: isResultSettingsVisible,
                 meta: result.meta,
                 resultsKlass: resultsKlass,
-                showChart: showChart,
-                showGrid: showGrid,
                 status: status,
                 type: result.type,
               }} --><!-- /ko -->
@@ -892,7 +889,6 @@
             <!-- /ko -->
             <!-- ko if: !$root.editorMode() && ['text', 'jar', 'java', 'distcp', 'shell', 'mapreduce', 'py', 'markdown'].indexOf(type()) === -1 -->
               <!-- ko component: { name: 'snippet-results', params: {
-                chartType: chartType,
                 data: result.data,
                 editorMode: parentVm.editorMode,
                 fetchResult: result.fetchMoreRows,
@@ -902,12 +898,8 @@
                 images: result.images,
                 isPresentationMode: parentNotebook.isPresentationMode,
                 isResultFullScreenMode: parentVm.isResultFullScreenMode,
-                isResultSettingsVisible: isResultSettingsVisible,
-                isResultSettingsVisible: isResultSettingsVisible,
                 meta: result.meta,
                 resultsKlass: resultsKlass,
-                showChart: showChart,
-                showGrid: showGrid,
                 status: status,
                 type: result.type,
               }} --><!-- /ko -->
@@ -1327,7 +1319,14 @@
 
   <script type="text/html" id="snippet-execution-status${ suffix }">
     <div class="snippet-execution-status" data-bind="clickForAceFocus: ace">
-      <a class="inactive-action pull-left snippet-logs-btn" href="javascript:void(0)" data-bind="visible: status() === 'running' && errors().length == 0, click: function() { hideFixedHeaders(); $data.showLogs(!$data.showLogs());}, css: {'blue': $data.showLogs}" title="${ _('Toggle Logs') }"><i class="fa fa-fw" data-bind="css: { 'fa-caret-right': !$data.showLogs(), 'fa-caret-down': $data.showLogs() }"></i></a>
+      <a class="inactive-action pull-left snippet-logs-btn" href="javascript:void(0)" data-bind="
+          visible: status() === 'running' && errors().length == 0,
+          click: function() {
+            huePubSub.publish('result.grid.hide.fixed.headers');
+            $data.showLogs(!$data.showLogs());
+          },
+          css: {'blue': $data.showLogs}
+        " title="${ _('Toggle Logs') }"><i class="fa fa-fw" data-bind="css: { 'fa-caret-right': !$data.showLogs(), 'fa-caret-down': $data.showLogs() }"></i></a>
       <div class="snippet-progress-container" data-bind="visible: status() != 'canceled' && status() != 'with-optimizer-report'">
         <!-- ko component: { name: 'executable-progress-bar', params: { snippet: $data } } --><!-- /ko -->
       </div>
@@ -1364,7 +1363,13 @@
   </script>
 
   <script type="text/html" id="snippet-code-resizer${ suffix }">
-    <div class="snippet-code-resizer" data-bind="aceResizer : { snippet: $data, target: '.ace-container-resizable', onStart: hideFixedHeaders, onStop: redrawFixedHeaders }">
+    <div class="snippet-code-resizer" data-bind="
+        aceResizer : {
+          snippet: $data,
+          target: '.ace-container-resizable',
+          onStart: function () { huePubSub.publish('result.grid.hide.fixed.headers') },
+          onStop: function () { huePubSub.publish('result.grid.redraw.fixed.headers') }
+        }">
       <i class="fa fa-ellipsis-h"></i>
     </div>
   </script>

部分文件因为文件数量过多而无法显示