You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by mo...@apache.org on 2017/01/04 02:37:49 UTC

[2/2] zeppelin git commit: [ZEPPELIN-1722] Modernize visualization/transformation using ES6 class and module syntax

[ZEPPELIN-1722] Modernize visualization/transformation using ES6 class and module syntax

### What is this PR for?
Modernize visualization/transformation using ES6 class and module syntax.
And remove global variable 'zeppelin'

### What type of PR is it?
Refactoring

### Todos
* [x] - Use ES6 class and module syntax

### What is the Jira issue?
https://issues.apache.org/jira/browse/ZEPPELIN-1722

### Questions:
* Does the licenses files need update? no
* Is there breaking changes for older versions? no
* Does this needs documentation? no

Author: Lee moon soo <mo...@apache.org>

Closes #1815 from Leemoonsoo/ZEPPELIN-1722 and squashes the following commits:

f945e7e [Lee moon soo] make _renderSetting function
aa33a04 [Lee moon soo] Use => and remove self refernece
59d1a6d [Lee moon soo] use class and import/export syntax for transformations(tabledata) and visualizations


Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/d60dd6fd
Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/d60dd6fd
Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/d60dd6fd

Branch: refs/heads/master
Commit: d60dd6fd7924a5c883328c4464fa77e92cc36611
Parents: 90decd2
Author: Lee moon soo <mo...@apache.org>
Authored: Sun Jan 1 07:03:37 2017 -0800
Committer: Lee moon soo <mo...@apache.org>
Committed: Tue Jan 3 18:37:40 2017 -0800

----------------------------------------------------------------------
 .../src/app/handsontable/handsonHelper.js       | 291 +++++-----
 .../paragraph/result/result.controller.js       |  21 +-
 .../src/app/tabledata/columnselector.js         |  96 ++--
 zeppelin-web/src/app/tabledata/passthrough.js   |  24 +-
 zeppelin-web/src/app/tabledata/pivot.js         | 416 +++++++-------
 zeppelin-web/src/app/tabledata/tabledata.js     |  96 ++--
 .../src/app/tabledata/transformation.js         | 150 ++---
 .../builtins/visualization-areachart.js         | 102 ++--
 .../builtins/visualization-barchart.js          |  94 ++--
 .../builtins/visualization-linechart.js         | 166 +++---
 .../builtins/visualization-nvd3chart.js         | 408 +++++++-------
 .../builtins/visualization-piechart.js          |  92 +--
 .../builtins/visualization-scatterchart.js      | 562 ++++++++++---------
 .../builtins/visualization-table.js             |  79 +--
 .../src/app/visualization/visualization.js      | 297 +++++-----
 zeppelin-web/src/app/zeppelin.js                |  21 -
 zeppelin-web/test/spec/tabledata/tabledata.js   |   6 +-
 zeppelin-web/webpack.config.js                  |   4 -
 18 files changed, 1453 insertions(+), 1472 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d60dd6fd/zeppelin-web/src/app/handsontable/handsonHelper.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/handsontable/handsonHelper.js b/zeppelin-web/src/app/handsontable/handsonHelper.js
index 7434e6f..bacb298 100644
--- a/zeppelin-web/src/app/handsontable/handsonHelper.js
+++ b/zeppelin-web/src/app/handsontable/handsonHelper.js
@@ -12,186 +12,187 @@
  * limitations under the License.
  */
 
-import zeppelin from '../zeppelin';
-
 /**
  * HandsonHelper class
  */
-zeppelin.HandsonHelper = function(columns, rows, comment) {
-  this.columns = columns || [];
-  this.rows = rows || [];
-  this.comment = comment || '';
-};
-
-zeppelin.HandsonHelper.prototype.getHandsonTableConfig = function(columns, columnNames, resultRows) {
-  return {
-    colHeaders: columnNames,
-    data: resultRows,
-    rowHeaders: false,
-    stretchH: 'all',
-    sortIndicator: true,
-    columns: columns,
-    columnSorting: true,
-    contextMenu: false,
-    manualColumnResize: true,
-    manualRowResize: true,
-    readOnly: true,
-    readOnlyCellClassName: '',
-    fillHandle: false,
-    fragmentSelection: true,
-    disableVisualSelection: true,
-    cells: function(ro, co, pro) {
-      var cellProperties = {};
-      var colType = columns[co].type;
-      cellProperties.renderer = function(instance, td, row, col, prop, value, cellProperties) {
-        _cellRenderer(instance, td, row, col, prop, value, cellProperties, colType);
-      };
-      return cellProperties;
-    },
-    afterGetColHeader: function(col, TH) {
-      var instance = this;
-      var menu = _buildDropDownMenu(columns[col].type);
-      var button = _buildTypeSwitchButton();
-
-      _addButtonMenuEvent(button, menu);
-
-      Handsontable.Dom.addEvent(menu, 'click', function(event) {
-        if (event.target.nodeName === 'LI') {
-          _setColumnType(columns, event.target.data.colType, instance, col);
+export default class HandsonHelper {
+  constructor(columns, rows, comment) {
+    this.columns = columns || [];
+    this.rows = rows || [];
+    this.comment = comment || '';
+  };
+
+  getHandsonTableConfig(columns, columnNames, resultRows) {
+    var self = this;
+    return {
+      colHeaders: columnNames,
+      data: resultRows,
+      rowHeaders: false,
+      stretchH: 'all',
+      sortIndicator: true,
+      columns: columns,
+      columnSorting: true,
+      contextMenu: false,
+      manualColumnResize: true,
+      manualRowResize: true,
+      readOnly: true,
+      readOnlyCellClassName: '',
+      fillHandle: false,
+      fragmentSelection: true,
+      disableVisualSelection: true,
+      cells: function(ro, co, pro) {
+        var cellProperties = {};
+        var colType = columns[co].type;
+        cellProperties.renderer = function(instance, td, row, col, prop, value, cellProperties) {
+          self._cellRenderer(instance, td, row, col, prop, value, cellProperties, colType);
+        };
+        return cellProperties;
+      },
+      afterGetColHeader: function(col, TH) {
+        var instance = this;
+        var menu = self._buildDropDownMenu(columns[col].type);
+        var button = self._buildTypeSwitchButton();
+
+        self._addButtonMenuEvent(button, menu);
+
+        Handsontable.Dom.addEvent(menu, 'click', function(event) {
+          if (event.target.nodeName === 'LI') {
+            self._setColumnType(columns, event.target.data.colType, instance, col);
+          }
+        });
+        if (TH.firstChild.lastChild.nodeName === 'BUTTON') {
+          TH.firstChild.removeChild(TH.firstChild.lastChild);
         }
-      });
-      if (TH.firstChild.lastChild.nodeName === 'BUTTON') {
-        TH.firstChild.removeChild(TH.firstChild.lastChild);
+        TH.firstChild.appendChild(button);
+        TH.style['white-space'] = 'normal';
       }
-      TH.firstChild.appendChild(button);
-      TH.style['white-space'] = 'normal';
-    }
+    };
   };
-};
 
-/*
-** Private Service Functions
-*/
+  /*
+  ** Private Service Functions
+  */
 
-function _addButtonMenuEvent(button, menu) {
-  Handsontable.Dom.addEvent(button, 'click', function(event) {
-    var changeTypeMenu;
-    var position;
-    var removeMenu;
+  _addButtonMenuEvent(button, menu) {
+    Handsontable.Dom.addEvent(button, 'click', function(event) {
+      var changeTypeMenu;
+      var position;
+      var removeMenu;
 
-    document.body.appendChild(menu);
+      document.body.appendChild(menu);
 
-    event.preventDefault();
-    event.stopImmediatePropagation();
+      event.preventDefault();
+      event.stopImmediatePropagation();
 
-    changeTypeMenu = document.querySelectorAll('.changeTypeMenu');
+      changeTypeMenu = document.querySelectorAll('.changeTypeMenu');
 
-    for (var i = 0, len = changeTypeMenu.length; i < len; i++) {
-      changeTypeMenu[i].style.display = 'none';
-    }
-    menu.style.display = 'block';
-    position = button.getBoundingClientRect();
+      for (var i = 0, len = changeTypeMenu.length; i < len; i++) {
+        changeTypeMenu[i].style.display = 'none';
+      }
+      menu.style.display = 'block';
+      position = button.getBoundingClientRect();
 
-    menu.style.top = (position.top + (window.scrollY || window.pageYOffset)) + 2 + 'px';
-    menu.style.left = (position.left) + 'px';
+      menu.style.top = (position.top + (window.scrollY || window.pageYOffset)) + 2 + 'px';
+      menu.style.left = (position.left) + 'px';
 
-    removeMenu = function(event) {
-      if (menu.parentNode) {
-        menu.parentNode.removeChild(menu);
-      }
-    };
-    Handsontable.Dom.removeEvent(document, 'click', removeMenu);
-    Handsontable.Dom.addEvent(document, 'click', removeMenu);
-  });
-}
+      removeMenu = function(event) {
+        if (menu.parentNode) {
+          menu.parentNode.removeChild(menu);
+        }
+      };
+      Handsontable.Dom.removeEvent(document, 'click', removeMenu);
+      Handsontable.Dom.addEvent(document, 'click', removeMenu);
+    });
+  }
 
-function _buildDropDownMenu(activeCellType) {
-  var menu = document.createElement('UL');
-  var types = ['text', 'numeric', 'date'];
-  var item;
+  _buildDropDownMenu(activeCellType) {
+    var menu = document.createElement('UL');
+    var types = ['text', 'numeric', 'date'];
+    var item;
 
-  menu.className = 'changeTypeMenu';
+    menu.className = 'changeTypeMenu';
 
-  for (var i = 0, len = types.length; i < len; i++) {
-    item = document.createElement('LI');
-    if ('innerText' in item) {
-      item.innerText = types[i];
-    } else {
-      item.textContent = types[i];
-    }
+    for (var i = 0, len = types.length; i < len; i++) {
+      item = document.createElement('LI');
+      if ('innerText' in item) {
+        item.innerText = types[i];
+      } else {
+        item.textContent = types[i];
+      }
 
-    item.data = {'colType': types[i]};
+      item.data = {'colType': types[i]};
 
-    if (activeCellType === types[i]) {
-      item.className = 'active';
+      if (activeCellType === types[i]) {
+        item.className = 'active';
+      }
+      menu.appendChild(item);
     }
-    menu.appendChild(item);
-  }
 
-  return menu;
-}
+    return menu;
+  }
 
-function _buildTypeSwitchButton() {
-  var button = document.createElement('BUTTON');
+  _buildTypeSwitchButton() {
+    var button = document.createElement('BUTTON');
 
-  button.innerHTML = '\u25BC';
-  button.className = 'changeType';
+    button.innerHTML = '\u25BC';
+    button.className = 'changeType';
 
-  return button;
-}
+    return button;
+  }
 
-function _isNumeric(value) {
-  if (!isNaN(value)) {
-    if (value.length !== 0) {
-      if (Number(value) <= Number.MAX_SAFE_INTEGER && Number(value) >= Number.MIN_SAFE_INTEGER) {
-        return true;
+  _isNumeric(value) {
+    if (!isNaN(value)) {
+      if (value.length !== 0) {
+        if (Number(value) <= Number.MAX_SAFE_INTEGER && Number(value) >= Number.MIN_SAFE_INTEGER) {
+          return true;
+        }
       }
     }
+    return false;
   }
-  return false;
-}
 
-function _cellRenderer(instance, td, row, col, prop, value, cellProperties, colType) {
-  if (colType === 'numeric' && _isNumeric(value)) {
-    cellProperties.format = '0,0.[00000]';
-    td.style.textAlign = 'left';
-    Handsontable.renderers.NumericRenderer.apply(this, arguments);
-  } else if (value.length > '%html'.length && '%html ' === value.substring(0, '%html '.length)) {
-    td.innerHTML = value.substring('%html'.length);
-  } else {
-    Handsontable.renderers.TextRenderer.apply(this, arguments);
+  _cellRenderer(instance, td, row, col, prop, value, cellProperties, colType) {
+    if (colType === 'numeric' && this._isNumeric(value)) {
+      cellProperties.format = '0,0.[00000]';
+      td.style.textAlign = 'left';
+      Handsontable.renderers.NumericRenderer.apply(this, arguments);
+    } else if (value.length > '%html'.length && '%html ' === value.substring(0, '%html '.length)) {
+      td.innerHTML = value.substring('%html'.length);
+    } else {
+      Handsontable.renderers.TextRenderer.apply(this, arguments);
+    }
   }
-}
 
-function _dateValidator(value, callback) {
-  var d = moment(value);
-  return callback(d.isValid());
-}
+  _dateValidator(value, callback) {
+    var d = moment(value);
+    return callback(d.isValid());
+  }
 
-function _numericValidator(value, callback) {
-  return callback(_isNumeric(value));
-}
+  _numericValidator(value, callback) {
+    return callback(this._isNumeric(value));
+  }
 
-function _setColumnType(columns, type, instance, col) {
-  columns[col].type = type;
-  _setColumnValidator(columns, col);
-  instance.updateSettings({columns: columns});
-  instance.validateCells(null);
-  if (_isColumnSorted(instance, col)) {
-    instance.sort(col, instance.sortOrder);
+  _setColumnType(columns, type, instance, col) {
+    columns[col].type = type;
+    this._setColumnValidator(columns, col);
+    instance.updateSettings({columns: columns});
+    instance.validateCells(null);
+    if (this._isColumnSorted(instance, col)) {
+      instance.sort(col, instance.sortOrder);
+    }
   }
-}
 
-function _isColumnSorted(instance, col) {
-  return instance.sortingEnabled && instance.sortColumn === col;
-}
+  _isColumnSorted(instance, col) {
+    return instance.sortingEnabled && instance.sortColumn === col;
+  }
 
-function _setColumnValidator(columns, col) {
-  if (columns[col].type === 'numeric') {
-    columns[col].validator = _numericValidator;
-  } else if (columns[col].type === 'date') {
-    columns[col].validator = _dateValidator;
-  } else {
-    columns[col].validator = null;
+  _setColumnValidator(columns, col) {
+    if (columns[col].type === 'numeric') {
+      columns[col].validator = this._numericValidator;
+    } else if (columns[col].type === 'date') {
+      columns[col].validator = this._dateValidator;
+    } else {
+      columns[col].validator = null;
+    }
   }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d60dd6fd/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js b/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js
index 77f2e28..c5360fe 100644
--- a/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js
+++ b/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js
@@ -12,7 +12,13 @@
  * limitations under the License.
  */
 
-import zeppelin from '../../../zeppelin';
+import TableData from '../../../tabledata/tabledata';
+import TableVisualization from '../../../visualization/builtins/visualization-table';
+import BarchartVisualization from '../../../visualization/builtins/visualization-barchart';
+import PiechartVisualization from '../../../visualization/builtins/visualization-piechart';
+import AreachartVisualization from '../../../visualization/builtins/visualization-areachart';
+import LinechartVisualization from '../../../visualization/builtins/visualization-linechart';
+import ScatterchartVisualization from '../../../visualization/builtins/visualization-scatterchart';
 
 (function() {
 
@@ -86,27 +92,27 @@ import zeppelin from '../../../zeppelin';
      */
     var builtInVisualizations = {
       'table': {
-        class: zeppelin.TableVisualization,
+        class: TableVisualization,
         instance: undefined   // created from setGraphMode()
       },
       'multiBarChart': {
-        class: zeppelin.BarchartVisualization,
+        class: BarchartVisualization,
         instance: undefined
       },
       'pieChart': {
-        class: zeppelin.PiechartVisualization,
+        class: PiechartVisualization,
         instance: undefined
       },
       'stackedAreaChart': {
-        class: zeppelin.AreachartVisualization,
+        class: AreachartVisualization,
         instance: undefined
       },
       'lineChart': {
-        class: zeppelin.LinechartVisualization,
+        class: LinechartVisualization,
         instance: undefined
       },
       'scatterChart': {
-        class: zeppelin.ScatterchartVisualization,
+        class: ScatterchartVisualization,
         instance: undefined
       }
     };
@@ -220,7 +226,6 @@ import zeppelin from '../../../zeppelin';
       enableHelium = (index === paragraphRef.results.msg.length - 1);
 
       if ($scope.type === 'TABLE') {
-        var TableData = zeppelin.TableData;
         tableData = new TableData();
         tableData.loadParagraphResult({type: $scope.type, msg: data});
         $scope.tableDataColumns = tableData.columns;

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d60dd6fd/zeppelin-web/src/app/tabledata/columnselector.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/tabledata/columnselector.js b/zeppelin-web/src/app/tabledata/columnselector.js
index 1cb2445..4b9180a 100644
--- a/zeppelin-web/src/app/tabledata/columnselector.js
+++ b/zeppelin-web/src/app/tabledata/columnselector.js
@@ -12,7 +12,7 @@
  * limitations under the License.
  */
 
-import zeppelin from '../zeppelin';
+import Transformation from './transformation';
 
 /**
  * select columns
@@ -25,58 +25,58 @@ import zeppelin from '../zeppelin';
  *     ...
  *   ]
  */
-zeppelin.ColumnselectorTransformation = function(config, columnSelectorProp) {
-  zeppelin.Transformation.call(this, config);
-  this.props = columnSelectorProp;
-};
-
-zeppelin.ColumnselectorTransformation.prototype = Object.create(zeppelin.Transformation.prototype);
+export default class ColumnselectorTransformation extends Transformation {
+  constructor(config, columnSelectorProp) {
+    super(config);
+    this.props = columnSelectorProp;
+  };
 
-zeppelin.ColumnselectorTransformation.prototype.getSetting = function() {
-  var self = this;
-  var configObj = self.config;
-  return {
-    template: 'app/tabledata/columnselector_settings.html',
-    scope: {
-      config: self.config,
-      props: self.props,
-      tableDataColumns: self.tableDataColumns,
-      save: function() {
-        self.emitConfig(configObj);
-      },
-      remove: function(selectorName) {
-        configObj[selectorName] = null;
-        self.emitConfig(configObj);
+  getSetting() {
+    var self = this;
+    var configObj = self.config;
+    return {
+      template: 'app/tabledata/columnselector_settings.html',
+      scope: {
+        config: self.config,
+        props: self.props,
+        tableDataColumns: self.tableDataColumns,
+        save: function() {
+          self.emitConfig(configObj);
+        },
+        remove: function(selectorName) {
+          configObj[selectorName] = null;
+          self.emitConfig(configObj);
+        }
       }
-    }
+    };
   };
-};
 
-/**
- * Method will be invoked when tableData or config changes
- */
-zeppelin.ColumnselectorTransformation.prototype.transform = function(tableData) {
-  this.tableDataColumns = tableData.columns;
-  this.removeUnknown();
-  return tableData;
-};
+  /**
+   * Method will be invoked when tableData or config changes
+   */
+  transform(tableData) {
+    this.tableDataColumns = tableData.columns;
+    this.removeUnknown();
+    return tableData;
+  };
 
-zeppelin.ColumnselectorTransformation.prototype.removeUnknown = function() {
-  var fields = this.config;
-  for (var f in fields) {
-    if (fields[f]) {
-      var found = false;
-      for (var i = 0; i < this.tableDataColumns.length; i++) {
-        var a = fields[f];
-        var b = this.tableDataColumns[i];
-        if (a.index === b.index && a.name === b.name) {
-          found = true;
-          break;
+  removeUnknown() {
+    var fields = this.config;
+    for (var f in fields) {
+      if (fields[f]) {
+        var found = false;
+        for (var i = 0; i < this.tableDataColumns.length; i++) {
+          var a = fields[f];
+          var b = this.tableDataColumns[i];
+          if (a.index === b.index && a.name === b.name) {
+            found = true;
+            break;
+          }
+        }
+        if (!found && (fields[f] instanceof Object) && !(fields[f] instanceof Array)) {
+          fields[f] = null;
         }
-      }
-      if (!found && (fields[f] instanceof Object) && !(fields[f] instanceof Array)) {
-        fields[f] = null;
       }
     }
-  }
-};
+  };
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d60dd6fd/zeppelin-web/src/app/tabledata/passthrough.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/tabledata/passthrough.js b/zeppelin-web/src/app/tabledata/passthrough.js
index a5579b4..b2d6ec4 100644
--- a/zeppelin-web/src/app/tabledata/passthrough.js
+++ b/zeppelin-web/src/app/tabledata/passthrough.js
@@ -12,20 +12,20 @@
  * limitations under the License.
  */
 
-import zeppelin from '../zeppelin';
+import Transformation from './transformation';
 
 /**
  * passthough the data
  */
-zeppelin.PassthroughTransformation = function(config) {
-  zeppelin.Transformation.call(this, config);
-};
+export default class PassthroughTransformation extends Transformation {
+  constructor(config) {
+    super(config);
+  };
 
-zeppelin.PassthroughTransformation.prototype = Object.create(zeppelin.Transformation.prototype);
-
-/**
- * Method will be invoked when tableData or config changes
- */
-zeppelin.PassthroughTransformation.prototype.transform = function(tableData) {
-  return tableData;
-};
+  /**
+   * Method will be invoked when tableData or config changes
+   */
+  transform(tableData) {
+    return tableData;
+  };
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d60dd6fd/zeppelin-web/src/app/tabledata/pivot.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/tabledata/pivot.js b/zeppelin-web/src/app/tabledata/pivot.js
index 2fc9799..366efee 100644
--- a/zeppelin-web/src/app/tabledata/pivot.js
+++ b/zeppelin-web/src/app/tabledata/pivot.js
@@ -12,251 +12,251 @@
  * limitations under the License.
  */
 
-import zeppelin from '../zeppelin';
+import Transformation from './transformation';
 
 /**
  * pivot table data and return d3 chart data
  */
-zeppelin.PivotTransformation = function(config) {
-  zeppelin.Transformation.call(this, config);
-};
-
-zeppelin.PivotTransformation.prototype = Object.create(zeppelin.Transformation.prototype);
+export default class PivotTransformation extends Transformation {
+  constructor(config) {
+    super(config);
+  };
 
-zeppelin.PivotTransformation.prototype.getSetting = function() {
-  var self = this;
+  getSetting() {
+    var self = this;
 
-  var configObj = self.config;
-  console.log('getSetting', configObj);
-  return {
-    template: 'app/tabledata/pivot_settings.html',
-    scope: {
-      config: configObj.common.pivot,
-      tableDataColumns: self.tableDataColumns,
-      save: function() {
-        self.emitConfig(configObj);
-      },
-      removeKey: function(idx) {
-        configObj.common.pivot.keys.splice(idx, 1);
-        self.emitConfig(configObj);
-      },
-      removeGroup: function(idx) {
-        configObj.common.pivot.groups.splice(idx, 1);
-        self.emitConfig(configObj);
-      },
-      removeValue: function(idx) {
-        configObj.common.pivot.values.splice(idx, 1);
-        self.emitConfig(configObj);
-      },
-      setValueAggr: function(idx, aggr) {
-        configObj.common.pivot.values[idx].aggr = aggr;
-        self.emitConfig(configObj);
+    var configObj = self.config;
+    console.log('getSetting', configObj);
+    return {
+      template: 'app/tabledata/pivot_settings.html',
+      scope: {
+        config: configObj.common.pivot,
+        tableDataColumns: self.tableDataColumns,
+        save: function() {
+          self.emitConfig(configObj);
+        },
+        removeKey: function(idx) {
+          configObj.common.pivot.keys.splice(idx, 1);
+          self.emitConfig(configObj);
+        },
+        removeGroup: function(idx) {
+          configObj.common.pivot.groups.splice(idx, 1);
+          self.emitConfig(configObj);
+        },
+        removeValue: function(idx) {
+          configObj.common.pivot.values.splice(idx, 1);
+          self.emitConfig(configObj);
+        },
+        setValueAggr: function(idx, aggr) {
+          configObj.common.pivot.values[idx].aggr = aggr;
+          self.emitConfig(configObj);
+        }
       }
-    }
+    };
   };
-};
-
-/**
- * Method will be invoked when tableData or config changes
- */
-zeppelin.PivotTransformation.prototype.transform = function(tableData) {
-  this.tableDataColumns = tableData.columns;
-  this.config.common = this.config.common || {};
-  this.config.common.pivot = this.config.common.pivot || {};
-  var config = this.config.common.pivot;
-  var firstTime = (!config.keys && !config.groups && !config.values);
 
-  config.keys = config.keys || [];
-  config.groups = config.groups || [];
-  config.values = config.values || [];
+  /**
+   * Method will be invoked when tableData or config changes
+   */
+  transform(tableData) {
+    this.tableDataColumns = tableData.columns;
+    this.config.common = this.config.common || {};
+    this.config.common.pivot = this.config.common.pivot || {};
+    var config = this.config.common.pivot;
+    var firstTime = (!config.keys && !config.groups && !config.values);
 
-  this.removeUnknown();
-  if (firstTime) {
-    this.selectDefault();
-  }
-  return this.pivot(
-    tableData,
-    config.keys,
-    config.groups,
-    config.values);
-};
+    config.keys = config.keys || [];
+    config.groups = config.groups || [];
+    config.values = config.values || [];
 
-zeppelin.PivotTransformation.prototype.removeUnknown = function() {
-  var config = this.config.common.pivot;
-  var tableDataColumns = this.tableDataColumns;
-  var unique = function(list) {
-    for (var i = 0; i < list.length; i++) {
-      for (var j = i + 1; j < list.length; j++) {
-        if (angular.equals(list[i], list[j])) {
-          list.splice(j, 1);
-        }
-      }
+    this.removeUnknown();
+    if (firstTime) {
+      this.selectDefault();
     }
+    return this.pivot(
+      tableData,
+      config.keys,
+      config.groups,
+      config.values);
   };
 
-  var removeUnknown = function(list) {
-    for (var i = 0; i < list.length; i++) {
-      // remove non existing column
-      var found = false;
-      for (var j = 0; j < tableDataColumns.length; j++) {
-        var a = list[i];
-        var b = tableDataColumns[j];
-        if (a.index === b.index && a.name === b.name) {
-          found = true;
-          break;
+  removeUnknown() {
+    var config = this.config.common.pivot;
+    var tableDataColumns = this.tableDataColumns;
+    var unique = function(list) {
+      for (var i = 0; i < list.length; i++) {
+        for (var j = i + 1; j < list.length; j++) {
+          if (angular.equals(list[i], list[j])) {
+            list.splice(j, 1);
+          }
         }
       }
-      if (!found) {
-        list.splice(i, 1);
-      }
-    }
-  };
+    };
 
-  unique(config.keys);
-  removeUnknown(config.keys);
-  unique(config.groups);
-  removeUnknown(config.groups);
-  removeUnknown(config.values);
-};
+    var removeUnknown = function(list) {
+      for (var i = 0; i < list.length; i++) {
+        // remove non existing column
+        var found = false;
+        for (var j = 0; j < tableDataColumns.length; j++) {
+          var a = list[i];
+          var b = tableDataColumns[j];
+          if (a.index === b.index && a.name === b.name) {
+            found = true;
+            break;
+          }
+        }
+        if (!found) {
+          list.splice(i, 1);
+        }
+      }
+    };
 
-zeppelin.PivotTransformation.prototype.selectDefault = function() {
-  var config = this.config.common.pivot;
-  if (config.keys.length === 0 &&
-      config.groups.length === 0 &&
-      config.values.length === 0) {
-    if (config.keys.length === 0 && this.tableDataColumns.length > 0) {
-      config.keys.push(this.tableDataColumns[0]);
-    }
+    unique(config.keys);
+    removeUnknown(config.keys);
+    unique(config.groups);
+    removeUnknown(config.groups);
+    removeUnknown(config.values);
+  };
 
-    if (config.values.length === 0 && this.tableDataColumns.length > 1) {
-      config.values.push(this.tableDataColumns[1]);
-    }
-  }
-};
+  selectDefault() {
+    var config = this.config.common.pivot;
+    if (config.keys.length === 0 &&
+        config.groups.length === 0 &&
+        config.values.length === 0) {
+      if (config.keys.length === 0 && this.tableDataColumns.length > 0) {
+        config.keys.push(this.tableDataColumns[0]);
+      }
 
-zeppelin.PivotTransformation.prototype.pivot = function(data, keys, groups, values) {
-  var aggrFunc = {
-    sum: function(a, b) {
-      var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
-      var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
-      return varA + varB;
-    },
-    count: function(a, b) {
-      var varA = (a !== undefined) ? parseInt(a) : 0;
-      var varB = (b !== undefined) ? 1 : 0;
-      return varA + varB;
-    },
-    min: function(a, b) {
-      var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
-      var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
-      return Math.min(varA,varB);
-    },
-    max: function(a, b) {
-      var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
-      var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
-      return Math.max(varA,varB);
-    },
-    avg: function(a, b, c) {
-      var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
-      var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
-      return varA + varB;
+      if (config.values.length === 0 && this.tableDataColumns.length > 1) {
+        config.values.push(this.tableDataColumns[1]);
+      }
     }
   };
 
-  var aggrFuncDiv = {
-    sum: false,
-    count: false,
-    min: false,
-    max: false,
-    avg: true
-  };
+  pivot(data, keys, groups, values) {
+    var aggrFunc = {
+      sum: function(a, b) {
+        var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
+        var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
+        return varA + varB;
+      },
+      count: function(a, b) {
+        var varA = (a !== undefined) ? parseInt(a) : 0;
+        var varB = (b !== undefined) ? 1 : 0;
+        return varA + varB;
+      },
+      min: function(a, b) {
+        var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
+        var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
+        return Math.min(varA,varB);
+      },
+      max: function(a, b) {
+        var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
+        var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
+        return Math.max(varA,varB);
+      },
+      avg: function(a, b, c) {
+        var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
+        var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
+        return varA + varB;
+      }
+    };
 
-  var schema = {};
-  var rows = {};
+    var aggrFuncDiv = {
+      sum: false,
+      count: false,
+      min: false,
+      max: false,
+      avg: true
+    };
 
-  for (var i = 0; i < data.rows.length; i++) {
-    var row = data.rows[i];
-    var s = schema;
-    var p = rows;
+    var schema = {};
+    var rows = {};
 
-    for (var k = 0; k < keys.length; k++) {
-      var key = keys[k];
+    for (var i = 0; i < data.rows.length; i++) {
+      var row = data.rows[i];
+      var s = schema;
+      var p = rows;
 
-      // add key to schema
-      if (!s[key.name]) {
-        s[key.name] = {
-          order: k,
-          index: key.index,
-          type: 'key',
-          children: {}
-        };
-      }
-      s = s[key.name].children;
+      for (var k = 0; k < keys.length; k++) {
+        var key = keys[k];
 
-      // add key to row
-      var keyKey = row[key.index];
-      if (!p[keyKey]) {
-        p[keyKey] = {};
+        // add key to schema
+        if (!s[key.name]) {
+          s[key.name] = {
+            order: k,
+            index: key.index,
+            type: 'key',
+            children: {}
+          };
+        }
+        s = s[key.name].children;
+
+        // add key to row
+        var keyKey = row[key.index];
+        if (!p[keyKey]) {
+          p[keyKey] = {};
+        }
+        p = p[keyKey];
       }
-      p = p[keyKey];
-    }
 
-    for (var g = 0; g < groups.length; g++) {
-      var group = groups[g];
-      var groupKey = row[group.index];
+      for (var g = 0; g < groups.length; g++) {
+        var group = groups[g];
+        var groupKey = row[group.index];
 
-      // add group to schema
-      if (!s[groupKey]) {
-        s[groupKey] = {
-          order: g,
-          index: group.index,
-          type: 'group',
-          children: {}
-        };
-      }
-      s = s[groupKey].children;
+        // add group to schema
+        if (!s[groupKey]) {
+          s[groupKey] = {
+            order: g,
+            index: group.index,
+            type: 'group',
+            children: {}
+          };
+        }
+        s = s[groupKey].children;
 
-      // add key to row
-      if (!p[groupKey]) {
-        p[groupKey] = {};
+        // add key to row
+        if (!p[groupKey]) {
+          p[groupKey] = {};
+        }
+        p = p[groupKey];
       }
-      p = p[groupKey];
-    }
 
-    for (var v = 0; v < values.length; v++) {
-      var value = values[v];
-      var valueKey = value.name + '(' + value.aggr + ')';
+      for (var v = 0; v < values.length; v++) {
+        var value = values[v];
+        var valueKey = value.name + '(' + value.aggr + ')';
 
-      // add value to schema
-      if (!s[valueKey]) {
-        s[valueKey] = {
-          type: 'value',
-          order: v,
-          index: value.index
-        };
-      }
+        // add value to schema
+        if (!s[valueKey]) {
+          s[valueKey] = {
+            type: 'value',
+            order: v,
+            index: value.index
+          };
+        }
 
-      // add value to row
-      if (!p[valueKey]) {
-        p[valueKey] = {
-          value: (value.aggr !== 'count') ? row[value.index] : 1,
-          count: 1
-        };
-      } else {
-        p[valueKey] = {
-          value: aggrFunc[value.aggr](p[valueKey].value, row[value.index], p[valueKey].count + 1),
-          count: (aggrFuncDiv[value.aggr]) ?  p[valueKey].count + 1 : p[valueKey].count
-        };
+        // add value to row
+        if (!p[valueKey]) {
+          p[valueKey] = {
+            value: (value.aggr !== 'count') ? row[value.index] : 1,
+            count: 1
+          };
+        } else {
+          p[valueKey] = {
+            value: aggrFunc[value.aggr](p[valueKey].value, row[value.index], p[valueKey].count + 1),
+            count: (aggrFuncDiv[value.aggr]) ?  p[valueKey].count + 1 : p[valueKey].count
+          };
+        }
       }
     }
-  }
 
-  //console.log('schema=%o, rows=%o', schema, rows);
-  return {
-    keys: keys,
-    groups: groups,
-    values: values,
-    schema: schema,
-    rows: rows
+    //console.log('schema=%o, rows=%o', schema, rows);
+    return {
+      keys: keys,
+      groups: groups,
+      values: values,
+      schema: schema,
+      rows: rows
+    };
   };
-};
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d60dd6fd/zeppelin-web/src/app/tabledata/tabledata.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/tabledata/tabledata.js b/zeppelin-web/src/app/tabledata/tabledata.js
index 550cf1d..69fd5dc 100644
--- a/zeppelin-web/src/app/tabledata/tabledata.js
+++ b/zeppelin-web/src/app/tabledata/tabledata.js
@@ -12,61 +12,61 @@
  * limitations under the License.
  */
 
-import zeppelin from '../zeppelin';
-
 /**
  * Create table data object from paragraph table type result
  */
-zeppelin.TableData = function(columns, rows, comment) {
-  this.columns = columns || [];
-  this.rows = rows || [];
-  this.comment = comment || '';
-};
+export default class TableData {
+  constructor(columns, rows, comment) {
+    this.columns = columns || [];
+    this.rows = rows || [];
+    this.comment = comment || '';
+  };
 
-zeppelin.TableData.prototype.loadParagraphResult = function(paragraphResult) {
-  if (!paragraphResult || paragraphResult.type !== 'TABLE') {
-    console.log('Can not load paragraph result');
-    return;
-  }
+  loadParagraphResult(paragraphResult) {
+    if (!paragraphResult || paragraphResult.type !== 'TABLE') {
+      console.log('Can not load paragraph result');
+      return;
+    }
 
-  var columnNames = [];
-  var rows = [];
-  var array = [];
-  var textRows = paragraphResult.msg.split('\n');
-  var comment = '';
-  var commentRow = false;
+    var columnNames = [];
+    var rows = [];
+    var array = [];
+    var textRows = paragraphResult.msg.split('\n');
+    var comment = '';
+    var commentRow = false;
 
-  for (var i = 0; i < textRows.length; i++) {
-    var textRow = textRows[i];
-    if (commentRow) {
-      comment += textRow;
-      continue;
-    }
+    for (var i = 0; i < textRows.length; i++) {
+      var textRow = textRows[i];
+      if (commentRow) {
+        comment += textRow;
+        continue;
+      }
 
-    if (textRow === '') {
-      if (rows.length > 0) {
-        commentRow = true;
+      if (textRow === '') {
+        if (rows.length > 0) {
+          commentRow = true;
+        }
+        continue;
       }
-      continue;
-    }
-    var textCols = textRow.split('\t');
-    var cols = [];
-    var cols2 = [];
-    for (var j = 0; j < textCols.length; j++) {
-      var col = textCols[j];
-      if (i === 0) {
-        columnNames.push({name: col, index: j, aggr: 'sum'});
-      } else {
-        cols.push(col);
-        cols2.push({key: (columnNames[i]) ? columnNames[i].name : undefined, value: col});
+      var textCols = textRow.split('\t');
+      var cols = [];
+      var cols2 = [];
+      for (var j = 0; j < textCols.length; j++) {
+        var col = textCols[j];
+        if (i === 0) {
+          columnNames.push({name: col, index: j, aggr: 'sum'});
+        } else {
+          cols.push(col);
+          cols2.push({key: (columnNames[i]) ? columnNames[i].name : undefined, value: col});
+        }
+      }
+      if (i !== 0) {
+        rows.push(cols);
+        array.push(cols2);
       }
     }
-    if (i !== 0) {
-      rows.push(cols);
-      array.push(cols2);
-    }
-  }
-  this.comment = comment;
-  this.columns = columnNames;
-  this.rows = rows;
-};
+    this.comment = comment;
+    this.columns = columnNames;
+    this.rows = rows;
+  };
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d60dd6fd/zeppelin-web/src/app/tabledata/transformation.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/tabledata/transformation.js b/zeppelin-web/src/app/tabledata/transformation.js
index 4d85c6b..bdd620b 100644
--- a/zeppelin-web/src/app/tabledata/transformation.js
+++ b/zeppelin-web/src/app/tabledata/transformation.js
@@ -12,92 +12,92 @@
  * limitations under the License.
  */
 
-import zeppelin from '../zeppelin';
-
 /**
  * Base class for visualization
  */
-zeppelin.Transformation = function(config) {
-  this.config = config;
-  this._emitter;
-};
-
-/**
- * return {
- *   template : angular template string or url (url should end with .html),
- *   scope : an object to bind to template scope
- * }
- */
-zeppelin.Transformation.prototype.getSetting = function() {
-  // override this
-};
+export default class Transformation {
+  constructor(config) {
+    this.config = config;
+    this._emitter;
+  };
 
-/**
- * Method will be invoked when tableData or config changes
- */
-zeppelin.Transformation.prototype.transform = function(tableData) {
-  // override this
-};
+  /**
+   * return {
+   *   template : angular template string or url (url should end with .html),
+   *   scope : an object to bind to template scope
+   * }
+   */
+  getSetting() {
+    // override this
+  };
 
-/**
- * render setting
- */
-zeppelin.Transformation.prototype.renderSetting = function(targetEl) {
-  var setting = this.getSetting();
-  if (!setting) {
-    return;
-  }
+  /**
+   * Method will be invoked when tableData or config changes
+   */
+  transform(tableData) {
+    // override this
+  };
 
-  // already readered
-  if (this._scope) {
-    var self = this;
-    this._scope.$apply(function() {
-      for (var k in setting.scope) {
-        self._scope[k] = setting.scope[k];
-      }
+  /**
+   * render setting
+   */
+  renderSetting(targetEl) {
+    var setting = this.getSetting();
+    if (!setting) {
+      return;
+    }
 
-      for (var k in self._prevSettingScope) {
-        if (!setting.scope[k]) {
+    // already readered
+    if (this._scope) {
+      var self = this;
+      this._scope.$apply(function() {
+        for (var k in setting.scope) {
           self._scope[k] = setting.scope[k];
         }
-      }
-    });
-    return;
-  } else {
-    this._prevSettingScope = setting.scope;
-  }
 
-  var scope = this._createNewScope();
-  for (var k in setting.scope) {
-    scope[k] = setting.scope[k];
-  }
-  var template = setting.template;
+        for (var k in self._prevSettingScope) {
+          if (!setting.scope[k]) {
+            self._scope[k] = setting.scope[k];
+          }
+        }
+      });
+      return;
+    } else {
+      this._prevSettingScope = setting.scope;
+    }
+
+    var scope = this._createNewScope();
+    for (var k in setting.scope) {
+      scope[k] = setting.scope[k];
+    }
+    var template = setting.template;
 
-  if (template.split('\n').length === 1 &&
-      template.endsWith('.html')) { // template is url
-    var self = this;
-    this._templateRequest(template).then(function(t) {
-      self._render(targetEl, t, scope);
-    });
-  } else {
-    this._render(targetEl, template, scope);
-  }
-};
+    if (template.split('\n').length === 1 &&
+        template.endsWith('.html')) { // template is url
+      var self = this;
+      this._templateRequest(template).then(function(t) {
+        self._render(targetEl, t, scope);
+      });
+    } else {
+      this._render(targetEl, template, scope);
+    }
+  };
 
-zeppelin.Transformation.prototype._render = function(targetEl, template, scope) {
-  this._targetEl = targetEl;
-  targetEl.html(template);
-  this._compile(targetEl.contents())(scope);
-  this._scope = scope;
-};
+  _render(targetEl, template, scope) {
+    this._targetEl = targetEl;
+    targetEl.html(template);
+    this._compile(targetEl.contents())(scope);
+    this._scope = scope;
+  };
 
-zeppelin.Transformation.prototype.setConfig = function(config) {
-  this.config = config;
-};
+  setConfig(config) {
+    this.config = config;
+  };
 
-/**
- * Emit config. config will sent to server and saved.
- */
-zeppelin.Transformation.prototype.emitConfig = function(config) {
-  this._emitter(config);
-};
+  /**
+   * Emit config. config will sent to server and saved.
+   */
+  emitConfig(config) {
+    this._emitter(config);
+  };
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d60dd6fd/zeppelin-web/src/app/visualization/builtins/visualization-areachart.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/visualization/builtins/visualization-areachart.js b/zeppelin-web/src/app/visualization/builtins/visualization-areachart.js
index 6b9f95c..719f3e3 100644
--- a/zeppelin-web/src/app/visualization/builtins/visualization-areachart.js
+++ b/zeppelin-web/src/app/visualization/builtins/visualization-areachart.js
@@ -12,68 +12,68 @@
  * limitations under the License.
  */
 
-import zeppelin from '../../zeppelin';
+import Nvd3ChartVisualization from './visualization-nvd3chart';
+import PivotTransformation from '../../tabledata/pivot';
 
 /**
  * Visualize data in area chart
  */
-zeppelin.AreachartVisualization = function(targetEl, config) {
-  zeppelin.Nvd3ChartVisualization.call(this, targetEl, config);
+export default class AreachartVisualization extends Nvd3ChartVisualization {
+  constructor(targetEl, config) {
+    super(targetEl, config);
 
-  var PivotTransformation = zeppelin.PivotTransformation;
-  this.pivot = new PivotTransformation(config);
-  this.xLables = [];
-};
+    this.pivot = new PivotTransformation(config);
+    this.xLables = [];
+  };
 
-zeppelin.AreachartVisualization.prototype = Object.create(zeppelin.Nvd3ChartVisualization.prototype);
+  type() {
+    return 'stackedAreaChart';
+  };
 
-zeppelin.AreachartVisualization.prototype.type = function() {
-  return 'stackedAreaChart';
-};
+  getTransformation() {
+    return this.pivot;
+  };
 
-zeppelin.AreachartVisualization.prototype.getTransformation = function() {
-  return this.pivot;
-};
+  render(pivot) {
+    var d3Data = this.d3DataFromPivot(
+      pivot.schema,
+      pivot.rows,
+      pivot.keys,
+      pivot.groups,
+      pivot.values,
+      false,
+      true,
+      false);
 
-zeppelin.AreachartVisualization.prototype.render = function(pivot) {
-  var d3Data = this.d3DataFromPivot(
-    pivot.schema,
-    pivot.rows,
-    pivot.keys,
-    pivot.groups,
-    pivot.values,
-    false,
-    true,
-    false);
+    this.xLabels = d3Data.xLabels;
+    super.render(d3Data);
+  };
 
-  this.xLabels = d3Data.xLabels;
-  zeppelin.Nvd3ChartVisualization.prototype.render.call(this, d3Data);
-};
+  /**
+   * Set new config
+   */
+  setConfig(config) {
+    super.setConfig(config);
+    this.pivot.setConfig(config);
+  };
 
-/**
- * Set new config
- */
-zeppelin.AreachartVisualization.prototype.setConfig = function(config) {
-  zeppelin.Nvd3ChartVisualization.prototype.setConfig.call(this, config);
-  this.pivot.setConfig(config);
-};
-
-zeppelin.AreachartVisualization.prototype.configureChart = function(chart) {
-  var self = this;
-  chart.xAxis.tickFormat(function(d) {return self.xAxisTickFormat(d, self.xLabels);});
-  chart.yAxisTickFormat(function(d) {return self.yAxisTickFormat(d);});
-  chart.yAxis.axisLabelDistance(50);
-  chart.useInteractiveGuideline(true); // for better UX and performance issue. (https://github.com/novus/nvd3/issues/691)
+  configureChart(chart) {
+    var self = this;
+    chart.xAxis.tickFormat(function(d) {return self.xAxisTickFormat(d, self.xLabels);});
+    chart.yAxisTickFormat(function(d) {return self.yAxisTickFormat(d);});
+    chart.yAxis.axisLabelDistance(50);
+    chart.useInteractiveGuideline(true); // for better UX and performance issue. (https://github.com/novus/nvd3/issues/691)
 
-  this.chart.style(this.config.style || 'stack');
+    this.chart.style(this.config.style || 'stack');
 
-  var self = this;
-  this.chart.dispatch.on('stateChange', function(s) {
-    self.config.style = s.style;
+    var self = this;
+    this.chart.dispatch.on('stateChange', function(s) {
+      self.config.style = s.style;
 
-    // give some time to animation finish
-    setTimeout(function() {
-      self.emitConfig(self.config);
-    }, 500);
-  });
-};
+      // give some time to animation finish
+      setTimeout(function() {
+        self.emitConfig(self.config);
+      }, 500);
+    });
+  };
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d60dd6fd/zeppelin-web/src/app/visualization/builtins/visualization-barchart.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/visualization/builtins/visualization-barchart.js b/zeppelin-web/src/app/visualization/builtins/visualization-barchart.js
index 9d151ec..a0ac573 100644
--- a/zeppelin-web/src/app/visualization/builtins/visualization-barchart.js
+++ b/zeppelin-web/src/app/visualization/builtins/visualization-barchart.js
@@ -12,64 +12,64 @@
  * limitations under the License.
  */
 
-import zeppelin from '../../zeppelin';
+import Nvd3ChartVisualization from './visualization-nvd3chart';
+import PivotTransformation from '../../tabledata/pivot';
 
 /**
  * Visualize data in bar char
  */
-zeppelin.BarchartVisualization = function(targetEl, config) {
-  zeppelin.Nvd3ChartVisualization.call(this, targetEl, config);
+export default class BarchartVisualization extends Nvd3ChartVisualization {
+  constructor(targetEl, config) {
+    super(targetEl, config);
 
-  var PivotTransformation = zeppelin.PivotTransformation;
-  this.pivot = new PivotTransformation(config);
-};
+    this.pivot = new PivotTransformation(config);
+  };
 
-zeppelin.BarchartVisualization.prototype = Object.create(zeppelin.Nvd3ChartVisualization.prototype);
+  type() {
+    return 'multiBarChart';
+  };
 
-zeppelin.BarchartVisualization.prototype.type = function() {
-  return 'multiBarChart';
-};
+  getTransformation() {
+    return this.pivot;
+  };
 
-zeppelin.BarchartVisualization.prototype.getTransformation = function() {
-  return this.pivot;
-};
+  render(pivot) {
+    var d3Data = this.d3DataFromPivot(
+      pivot.schema,
+      pivot.rows,
+      pivot.keys,
+      pivot.groups,
+      pivot.values,
+      true,
+      false,
+      true);
 
-zeppelin.BarchartVisualization.prototype.render = function(pivot) {
-  var d3Data = this.d3DataFromPivot(
-    pivot.schema,
-    pivot.rows,
-    pivot.keys,
-    pivot.groups,
-    pivot.values,
-    true,
-    false,
-    true);
+    super.render(d3Data);
+  };
 
-  zeppelin.Nvd3ChartVisualization.prototype.render.call(this, d3Data);
-};
+  /**
+   * Set new config
+   */
+  setConfig(config) {
+    super.setConfig(config);
+    this.pivot.setConfig(config);
+  };
 
-/**
- * Set new config
- */
-zeppelin.BarchartVisualization.prototype.setConfig = function(config) {
-  zeppelin.Nvd3ChartVisualization.prototype.setConfig.call(this, config);
-  this.pivot.setConfig(config);
-};
-
-zeppelin.BarchartVisualization.prototype.configureChart = function(chart) {
-  var self = this;
-  chart.yAxis.axisLabelDistance(50);
-  chart.yAxis.tickFormat(function(d) {return self.yAxisTickFormat(d);});
+  configureChart(chart) {
+    var self = this;
+    chart.yAxis.axisLabelDistance(50);
+    chart.yAxis.tickFormat(function(d) {return self.yAxisTickFormat(d);});
 
-  this.chart.stacked(this.config.stacked);
+    this.chart.stacked(this.config.stacked);
 
-  var self = this;
-  this.chart.dispatch.on('stateChange', function(s) {
-    self.config.stacked = s.stacked;
+    var self = this;
+    this.chart.dispatch.on('stateChange', function(s) {
+      self.config.stacked = s.stacked;
 
-    // give some time to animation finish
-    setTimeout(function() {
-      self.emitConfig(self.config);
-    }, 500);
-  });
-};
+      // give some time to animation finish
+      setTimeout(function() {
+        self.emitConfig(self.config);
+      }, 500);
+    });
+  };
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d60dd6fd/zeppelin-web/src/app/visualization/builtins/visualization-linechart.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/visualization/builtins/visualization-linechart.js b/zeppelin-web/src/app/visualization/builtins/visualization-linechart.js
index 1dc5a42..aab51f8 100644
--- a/zeppelin-web/src/app/visualization/builtins/visualization-linechart.js
+++ b/zeppelin-web/src/app/visualization/builtins/visualization-linechart.js
@@ -12,103 +12,103 @@
  * limitations under the License.
  */
 
-import zeppelin from '../../zeppelin';
+import Nvd3ChartVisualization from './visualization-nvd3chart';
+import PivotTransformation from '../../tabledata/pivot';
 
 /**
  * Visualize data in line chart
  */
-zeppelin.LinechartVisualization = function(targetEl, config) {
-  zeppelin.Nvd3ChartVisualization.call(this, targetEl, config);
+export default class LinechartVisualization extends Nvd3ChartVisualization {
+  constructor(targetEl, config) {
+    super(targetEl, config);
 
-  var PivotTransformation = zeppelin.PivotTransformation;
-  this.pivot = new PivotTransformation(config);
-  this.xLables = [];
-};
-
-zeppelin.LinechartVisualization.prototype = Object.create(zeppelin.Nvd3ChartVisualization.prototype);
+    this.pivot = new PivotTransformation(config);
+    this.xLables = [];
+  };
 
-zeppelin.LinechartVisualization.prototype.type = function() {
-  if (this.config.lineWithFocus) {
-    return 'lineWithFocusChart';
-  } else {
-    return 'lineChart';
-  }
-};
+  type() {
+    if (this.config.lineWithFocus) {
+      return 'lineWithFocusChart';
+    } else {
+      return 'lineChart';
+    }
+  };
 
-zeppelin.LinechartVisualization.prototype.getTransformation = function() {
-  return this.pivot;
-};
+  getTransformation() {
+    return this.pivot;
+  };
 
-zeppelin.LinechartVisualization.prototype.render = function(pivot) {
-  var d3Data = this.d3DataFromPivot(
-    pivot.schema,
-    pivot.rows,
-    pivot.keys,
-    pivot.groups,
-    pivot.values,
-    false,
-    true,
-    false);
+  render(pivot) {
+    var d3Data = this.d3DataFromPivot(
+      pivot.schema,
+      pivot.rows,
+      pivot.keys,
+      pivot.groups,
+      pivot.values,
+      false,
+      true,
+      false);
 
-  this.xLabels = d3Data.xLabels;
-  zeppelin.Nvd3ChartVisualization.prototype.render.call(this, d3Data);
-};
+    this.xLabels = d3Data.xLabels;
+    super.render(d3Data);
+  };
 
-/**
- * Set new config
- */
-zeppelin.LinechartVisualization.prototype.setConfig = function(config) {
-  zeppelin.Nvd3ChartVisualization.prototype.setConfig.call(this, config);
-  this.pivot.setConfig(config);
+  /**
+   * Set new config
+   */
+  setConfig(config) {
+    super.setConfig(config);
+    this.pivot.setConfig(config);
 
-  // change mode
-  if (this.currentMode !== config.lineWithFocus) {
-    zeppelin.Nvd3ChartVisualization.prototype.destroy.call(this);
-    this.currentMode = config.lineWithFocus;
-  }
-};
+    // change mode
+    if (this.currentMode !== config.lineWithFocus) {
+      super.destroy();
+      this.currentMode = config.lineWithFocus;
+    }
+  };
 
-zeppelin.LinechartVisualization.prototype.configureChart = function(chart) {
-  var self = this;
-  chart.xAxis.tickFormat(function(d) {return self.xAxisTickFormat(d, self.xLabels);});
-  chart.yAxis.tickFormat(function(d) {return self.yAxisTickFormat(d, self.xLabels);});
-  chart.yAxis.axisLabelDistance(50);
-  if (chart.useInteractiveGuideline) {   // lineWithFocusChart hasn't got useInteractiveGuideline
-    chart.useInteractiveGuideline(true); // for better UX and performance issue. (https://github.com/novus/nvd3/issues/691)
-  }
-  if (this.config.forceY) {
-    chart.forceY([0]); // force y-axis minimum to 0 for line chart.
-  } else {
-    chart.forceY([]);
-  }
-};
+  configureChart(chart) {
+    var self = this;
+    chart.xAxis.tickFormat(function(d) {return self.xAxisTickFormat(d, self.xLabels);});
+    chart.yAxis.tickFormat(function(d) {return self.yAxisTickFormat(d, self.xLabels);});
+    chart.yAxis.axisLabelDistance(50);
+    if (chart.useInteractiveGuideline) {   // lineWithFocusChart hasn't got useInteractiveGuideline
+      chart.useInteractiveGuideline(true); // for better UX and performance issue. (https://github.com/novus/nvd3/issues/691)
+    }
+    if (this.config.forceY) {
+      chart.forceY([0]); // force y-axis minimum to 0 for line chart.
+    } else {
+      chart.forceY([]);
+    }
+  };
 
-zeppelin.LinechartVisualization.prototype.getSetting = function(chart) {
-  var self = this;
-  var configObj = self.config;
+  getSetting(chart) {
+    var self = this;
+    var configObj = self.config;
 
-  return {
-    template: `<div>
-      <label>
-        <input type="checkbox"
-             ng-model="config.forceY"
-             ng-click="save()" />
-        force Y to 0
-      </label>
-      <br/>
+    return {
+      template: `<div>
+        <label>
+          <input type="checkbox"
+               ng-model="config.forceY"
+               ng-click="save()" />
+          force Y to 0
+        </label>
+        <br/>
 
-      <label>
-        <input type="checkbox"
-             ng-model="config.lineWithFocus"
-             ng-click="save()" />
-        show line chart with focus
-      </label>
-    </div>`,
-    scope: {
-      config: configObj,
-      save: function() {
-        self.emitConfig(configObj);
+        <label>
+          <input type="checkbox"
+               ng-model="config.lineWithFocus"
+               ng-click="save()" />
+          show line chart with focus
+        </label>
+      </div>`,
+      scope: {
+        config: configObj,
+        save: function() {
+          self.emitConfig(configObj);
+        }
       }
-    }
+    };
   };
-};
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d60dd6fd/zeppelin-web/src/app/visualization/builtins/visualization-nvd3chart.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/visualization/builtins/visualization-nvd3chart.js b/zeppelin-web/src/app/visualization/builtins/visualization-nvd3chart.js
index cf04215..61de1d9 100644
--- a/zeppelin-web/src/app/visualization/builtins/visualization-nvd3chart.js
+++ b/zeppelin-web/src/app/visualization/builtins/visualization-nvd3chart.js
@@ -12,246 +12,246 @@
  * limitations under the License.
  */
 
-import zeppelin from '../../zeppelin';
+import Visualization from '../visualization';
 
 /**
  * Visualize data in table format
  */
-zeppelin.Nvd3ChartVisualization = function(targetEl, config) {
-  zeppelin.Visualization.call(this, targetEl, config);
-  this.targetEl.append('<svg></svg>');
-};
-
-zeppelin.Nvd3ChartVisualization.prototype = Object.create(zeppelin.Visualization.prototype);
-
-zeppelin.Visualization.prototype.refresh = function() {
-  if (this.chart) {
-    this.chart.update();
-  }
-};
-
-zeppelin.Nvd3ChartVisualization.prototype.render = function(data) {
-  var type = this.type();
-  var d3g = data.d3g;
-
-  if (!this.chart) {
-    this.chart = nv.models[type]();
-  }
-
-  this.configureChart(this.chart);
-
-  var animationDuration = 300;
-  var numberOfDataThreshold = 150;
-  var height = this.targetEl.height();
-
-  // turn off animation when dataset is too large. (for performance issue)
-  // still, since dataset is large, the chart content sequentially appears like animated.
-  try {
-    if (d3g[0].values.length > numberOfDataThreshold) {
-      animationDuration = 0;
-    }
-  } catch (ignoreErr) {
-  }
-
-  d3.select('#' + this.targetEl[0].id + ' svg')
-    .attr('height', height)
-    .datum(d3g)
-    .transition()
-    .duration(animationDuration)
-    .call(this.chart);
-  d3.select('#' + this.targetEl[0].id + ' svg').style.height = height + 'px';
-};
-
-zeppelin.Nvd3ChartVisualization.prototype.type = function() {
-  // override this and return chart type
-};
-
-zeppelin.Nvd3ChartVisualization.prototype.configureChart = function(chart) {
-  // override this to configure chart
-};
-
-zeppelin.Nvd3ChartVisualization.prototype.groupedThousandsWith3DigitsFormatter = function(x) {
-  return d3.format(',')(d3.round(x, 3));
-};
-
-zeppelin.Nvd3ChartVisualization.prototype.customAbbrevFormatter = function(x) {
-  var s = d3.format('.3s')(x);
-  switch (s[s.length - 1]) {
-    case 'G': return s.slice(0, -1) + 'B';
-  }
-  return s;
-};
-
-zeppelin.Nvd3ChartVisualization.prototype.xAxisTickFormat = function(d, xLabels) {
-  if (xLabels[d] && (isNaN(parseFloat(xLabels[d])) || !isFinite(xLabels[d]))) { // to handle string type xlabel
-    return xLabels[d];
-  } else {
-    return d;
-  }
-};
-
-zeppelin.Nvd3ChartVisualization.prototype.yAxisTickFormat = function(d) {
-  if (Math.abs(d) >= Math.pow(10,6)) {
-    return this.customAbbrevFormatter(d);
-  }
-  return this.groupedThousandsWith3DigitsFormatter(d);
-};
-
-zeppelin.Nvd3ChartVisualization.prototype.d3DataFromPivot = function(
-  schema, rows, keys, groups, values, allowTextXAxis, fillMissingValues, multiBarChart) {
-  // construct table data
-  var d3g = [];
-
-  var concat = function(o, n) {
-    if (!o) {
-      return n;
-    } else {
-      return o + '.' + n;
-    }
+export default class Nvd3ChartVisualization extends Visualization {
+  constructor(targetEl, config) {
+    super(targetEl, config);
+    this.targetEl.append('<svg></svg>');
   };
 
-  var getSchemaUnderKey = function(key, s) {
-    for (var c in key.children) {
-      s[c] = {};
-      getSchemaUnderKey(key.children[c], s[c]);
+  refresh() {
+    if (this.chart) {
+      this.chart.update();
     }
   };
 
-  var traverse = function(sKey, s, rKey, r, func, rowName, rowValue, colName) {
-    //console.log("TRAVERSE sKey=%o, s=%o, rKey=%o, r=%o, rowName=%o, rowValue=%o, colName=%o", sKey, s, rKey, r, rowName, rowValue, colName);
-
-    if (s.type === 'key') {
-      rowName = concat(rowName, sKey);
-      rowValue = concat(rowValue, rKey);
-    } else if (s.type === 'group') {
-      colName = concat(colName, rKey);
-    } else if (s.type === 'value' && sKey === rKey || valueOnly) {
-      colName = concat(colName, rKey);
-      func(rowName, rowValue, colName, r);
+  render(data) {
+    var type = this.type();
+    var d3g = data.d3g;
+
+    if (!this.chart) {
+      this.chart = nv.models[type]();
     }
 
-    for (var c in s.children) {
-      if (fillMissingValues && s.children[c].type === 'group' && r[c] === undefined) {
-        var cs = {};
-        getSchemaUnderKey(s.children[c], cs);
-        traverse(c, s.children[c], c, cs, func, rowName, rowValue, colName);
-        continue;
-      }
+    this.configureChart(this.chart);
 
-      for (var j in r) {
-        if (s.children[c].type === 'key' || c === j) {
-          traverse(c, s.children[c], j, r[j], func, rowName, rowValue, colName);
-        }
+    var animationDuration = 300;
+    var numberOfDataThreshold = 150;
+    var height = this.targetEl.height();
+
+    // turn off animation when dataset is too large. (for performance issue)
+    // still, since dataset is large, the chart content sequentially appears like animated.
+    try {
+      if (d3g[0].values.length > numberOfDataThreshold) {
+        animationDuration = 0;
       }
+    } catch (ignoreErr) {
+    }
+
+    d3.select('#' + this.targetEl[0].id + ' svg')
+      .attr('height', height)
+      .datum(d3g)
+      .transition()
+      .duration(animationDuration)
+      .call(this.chart);
+    d3.select('#' + this.targetEl[0].id + ' svg').style.height = height + 'px';
+  };
+
+  type() {
+    // override this and return chart type
+  };
+
+  configureChart(chart) {
+    // override this to configure chart
+  };
+
+  groupedThousandsWith3DigitsFormatter(x) {
+    return d3.format(',')(d3.round(x, 3));
+  };
+
+  customAbbrevFormatter(x) {
+    var s = d3.format('.3s')(x);
+    switch (s[s.length - 1]) {
+      case 'G': return s.slice(0, -1) + 'B';
     }
+    return s;
   };
 
-  var valueOnly = (keys.length === 0 && groups.length === 0 && values.length > 0);
-  var noKey = (keys.length === 0);
-  var isMultiBarChart = multiBarChart;
+  xAxisTickFormat(d, xLabels) {
+    if (xLabels[d] && (isNaN(parseFloat(xLabels[d])) || !isFinite(xLabels[d]))) { // to handle string type xlabel
+      return xLabels[d];
+    } else {
+      return d;
+    }
+  };
 
-  var sKey = Object.keys(schema)[0];
+  yAxisTickFormat(d) {
+    if (Math.abs(d) >= Math.pow(10,6)) {
+      return this.customAbbrevFormatter(d);
+    }
+    return this.groupedThousandsWith3DigitsFormatter(d);
+  };
 
-  var rowNameIndex = {};
-  var rowIdx = 0;
-  var colNameIndex = {};
-  var colIdx = 0;
-  var rowIndexValue = {};
+  d3DataFromPivot(
+    schema, rows, keys, groups, values, allowTextXAxis, fillMissingValues, multiBarChart) {
+    // construct table data
+    var d3g = [];
 
-  for (var k in rows) {
-    traverse(sKey, schema[sKey], k, rows[k], function(rowName, rowValue, colName, value) {
-      //console.log("RowName=%o, row=%o, col=%o, value=%o", rowName, rowValue, colName, value);
-      if (rowNameIndex[rowValue] === undefined) {
-        rowIndexValue[rowIdx] = rowValue;
-        rowNameIndex[rowValue] = rowIdx++;
+    var concat = function(o, n) {
+      if (!o) {
+        return n;
+      } else {
+        return o + '.' + n;
       }
+    };
 
-      if (colNameIndex[colName] === undefined) {
-        colNameIndex[colName] = colIdx++;
+    var getSchemaUnderKey = function(key, s) {
+      for (var c in key.children) {
+        s[c] = {};
+        getSchemaUnderKey(key.children[c], s[c]);
       }
-      var i = colNameIndex[colName];
-      if (noKey && isMultiBarChart) {
-        i = 0;
+    };
+
+    var traverse = function(sKey, s, rKey, r, func, rowName, rowValue, colName) {
+      //console.log("TRAVERSE sKey=%o, s=%o, rKey=%o, r=%o, rowName=%o, rowValue=%o, colName=%o", sKey, s, rKey, r, rowName, rowValue, colName);
+
+      if (s.type === 'key') {
+        rowName = concat(rowName, sKey);
+        rowValue = concat(rowValue, rKey);
+      } else if (s.type === 'group') {
+        colName = concat(colName, rKey);
+      } else if (s.type === 'value' && sKey === rKey || valueOnly) {
+        colName = concat(colName, rKey);
+        func(rowName, rowValue, colName, r);
       }
 
-      if (!d3g[i]) {
-        d3g[i] = {
-          values: [],
-          key: (noKey && isMultiBarChart) ? 'values' : colName
-        };
-      }
+      for (var c in s.children) {
+        if (fillMissingValues && s.children[c].type === 'group' && r[c] === undefined) {
+          var cs = {};
+          getSchemaUnderKey(s.children[c], cs);
+          traverse(c, s.children[c], c, cs, func, rowName, rowValue, colName);
+          continue;
+        }
 
-      var xVar = isNaN(rowValue) ? ((allowTextXAxis) ? rowValue : rowNameIndex[rowValue]) : parseFloat(rowValue);
-      var yVar = 0;
-      if (xVar === undefined) { xVar = colName; }
-      if (value !== undefined) {
-        yVar = isNaN(value.value) ? 0 : parseFloat(value.value) / parseFloat(value.count);
+        for (var j in r) {
+          if (s.children[c].type === 'key' || c === j) {
+            traverse(c, s.children[c], j, r[j], func, rowName, rowValue, colName);
+          }
+        }
       }
-      d3g[i].values.push({
-        x: xVar,
-        y: yVar
-      });
-    });
-  }
-
-  // clear aggregation name, if possible
-  var namesWithoutAggr = {};
-  var colName;
-  var withoutAggr;
-  // TODO - This part could use som refactoring - Weird if/else with similar actions and variable names
-  for (colName in colNameIndex) {
-    withoutAggr = colName.substring(0, colName.lastIndexOf('('));
-    if (!namesWithoutAggr[withoutAggr]) {
-      namesWithoutAggr[withoutAggr] = 1;
-    } else {
-      namesWithoutAggr[withoutAggr]++;
-    }
-  }
+    };
+
+    var valueOnly = (keys.length === 0 && groups.length === 0 && values.length > 0);
+    var noKey = (keys.length === 0);
+    var isMultiBarChart = multiBarChart;
+
+    var sKey = Object.keys(schema)[0];
+
+    var rowNameIndex = {};
+    var rowIdx = 0;
+    var colNameIndex = {};
+    var colIdx = 0;
+    var rowIndexValue = {};
+
+    for (var k in rows) {
+      traverse(sKey, schema[sKey], k, rows[k], function(rowName, rowValue, colName, value) {
+        //console.log("RowName=%o, row=%o, col=%o, value=%o", rowName, rowValue, colName, value);
+        if (rowNameIndex[rowValue] === undefined) {
+          rowIndexValue[rowIdx] = rowValue;
+          rowNameIndex[rowValue] = rowIdx++;
+        }
 
-  if (valueOnly) {
-    for (var valueIndex = 0; valueIndex < d3g[0].values.length; valueIndex++) {
-      colName = d3g[0].values[valueIndex].x;
-      if (!colName) {
-        continue;
-      }
+        if (colNameIndex[colName] === undefined) {
+          colNameIndex[colName] = colIdx++;
+        }
+        var i = colNameIndex[colName];
+        if (noKey && isMultiBarChart) {
+          i = 0;
+        }
 
-      withoutAggr = colName.substring(0, colName.lastIndexOf('('));
-      if (namesWithoutAggr[withoutAggr] <= 1) {
-        d3g[0].values[valueIndex].x = withoutAggr;
-      }
+        if (!d3g[i]) {
+          d3g[i] = {
+            values: [],
+            key: (noKey && isMultiBarChart) ? 'values' : colName
+          };
+        }
+
+        var xVar = isNaN(rowValue) ? ((allowTextXAxis) ? rowValue : rowNameIndex[rowValue]) : parseFloat(rowValue);
+        var yVar = 0;
+        if (xVar === undefined) { xVar = colName; }
+        if (value !== undefined) {
+          yVar = isNaN(value.value) ? 0 : parseFloat(value.value) / parseFloat(value.count);
+        }
+        d3g[i].values.push({
+          x: xVar,
+          y: yVar
+        });
+      });
     }
-  } else {
-    for (var d3gIndex = 0; d3gIndex < d3g.length; d3gIndex++) {
-      colName = d3g[d3gIndex].key;
+
+    // clear aggregation name, if possible
+    var namesWithoutAggr = {};
+    var colName;
+    var withoutAggr;
+    // TODO - This part could use som refactoring - Weird if/else with similar actions and variable names
+    for (colName in colNameIndex) {
       withoutAggr = colName.substring(0, colName.lastIndexOf('('));
-      if (namesWithoutAggr[withoutAggr] <= 1) {
-        d3g[d3gIndex].key = withoutAggr;
+      if (!namesWithoutAggr[withoutAggr]) {
+        namesWithoutAggr[withoutAggr] = 1;
+      } else {
+        namesWithoutAggr[withoutAggr]++;
       }
     }
 
-    // use group name instead of group.value as a column name, if there're only one group and one value selected.
-    if (groups.length === 1 && values.length === 1) {
-      for (d3gIndex = 0; d3gIndex < d3g.length; d3gIndex++) {
+    if (valueOnly) {
+      for (var valueIndex = 0; valueIndex < d3g[0].values.length; valueIndex++) {
+        colName = d3g[0].values[valueIndex].x;
+        if (!colName) {
+          continue;
+        }
+
+        withoutAggr = colName.substring(0, colName.lastIndexOf('('));
+        if (namesWithoutAggr[withoutAggr] <= 1) {
+          d3g[0].values[valueIndex].x = withoutAggr;
+        }
+      }
+    } else {
+      for (var d3gIndex = 0; d3gIndex < d3g.length; d3gIndex++) {
         colName = d3g[d3gIndex].key;
-        colName = colName.split('.').slice(0, -1).join('.');
-        d3g[d3gIndex].key = colName;
+        withoutAggr = colName.substring(0, colName.lastIndexOf('('));
+        if (namesWithoutAggr[withoutAggr] <= 1) {
+          d3g[d3gIndex].key = withoutAggr;
+        }
+      }
+
+      // use group name instead of group.value as a column name, if there're only one group and one value selected.
+      if (groups.length === 1 && values.length === 1) {
+        for (d3gIndex = 0; d3gIndex < d3g.length; d3gIndex++) {
+          colName = d3g[d3gIndex].key;
+          colName = colName.split('.').slice(0, -1).join('.');
+          d3g[d3gIndex].key = colName;
+        }
       }
     }
-  }
 
-  return {
-    xLabels: rowIndexValue,
-    d3g: d3g
+    return {
+      xLabels: rowIndexValue,
+      d3g: d3g
+    };
   };
-};
 
-/**
- * method will be invoked when visualization need to be destroyed.
- * Don't need to destroy this.targetEl.
- */
-zeppelin.Visualization.prototype.destroy = function() {
-  if (this.chart) {
-    d3.selectAll('#' + this.targetEl[0].id + ' svg > *').remove();
-    this.chart = undefined;
-  }
-};
+  /**
+   * method will be invoked when visualization need to be destroyed.
+   * Don't need to destroy this.targetEl.
+   */
+  destroy() {
+    if (this.chart) {
+      d3.selectAll('#' + this.targetEl[0].id + ' svg > *').remove();
+      this.chart = undefined;
+    }
+  };
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/d60dd6fd/zeppelin-web/src/app/visualization/builtins/visualization-piechart.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/visualization/builtins/visualization-piechart.js b/zeppelin-web/src/app/visualization/builtins/visualization-piechart.js
index a2a1fcf..fdc67b1 100644
--- a/zeppelin-web/src/app/visualization/builtins/visualization-piechart.js
+++ b/zeppelin-web/src/app/visualization/builtins/visualization-piechart.js
@@ -12,61 +12,61 @@
  * limitations under the License.
  */
 
-import zeppelin from '../../zeppelin';
+import Nvd3ChartVisualization from './visualization-nvd3chart';
+import PivotTransformation from '../../tabledata/pivot';
 
 /**
  * Visualize data in pie chart
  */
-zeppelin.PiechartVisualization = function(targetEl, config) {
-  zeppelin.Nvd3ChartVisualization.call(this, targetEl, config);
+export default class PiechartVisualization extends Nvd3ChartVisualization {
+  constructor(targetEl, config) {
+    super(targetEl, config);
 
-  var PivotTransformation = zeppelin.PivotTransformation;
-  this.pivot = new PivotTransformation(config);
-};
+    this.pivot = new PivotTransformation(config);
+  };
 
-zeppelin.PiechartVisualization.prototype = Object.create(zeppelin.Nvd3ChartVisualization.prototype);
+  type() {
+    return 'pieChart';
+  };
 
-zeppelin.PiechartVisualization.prototype.type = function() {
-  return 'pieChart';
-};
+  getTransformation() {
+    return this.pivot;
+  };
 
-zeppelin.PiechartVisualization.prototype.getTransformation = function() {
-  return this.pivot;
-};
+  render(pivot) {
+    var d3Data = this.d3DataFromPivot(
+      pivot.schema,
+      pivot.rows,
+      pivot.keys,
+      pivot.groups,
+      pivot.values,
+      true,
+      false,
+      false);
 
-zeppelin.PiechartVisualization.prototype.render = function(pivot) {
-  var d3Data = this.d3DataFromPivot(
-    pivot.schema,
-    pivot.rows,
-    pivot.keys,
-    pivot.groups,
-    pivot.values,
-    true,
-    false,
-    false);
-
-  var d = d3Data.d3g;
-  var d3g = [];
-  if (d.length > 0) {
-    for (var i = 0; i < d[0].values.length ; i++) {
-      var e = d[0].values[i];
-      d3g.push({
-        label: e.x,
-        value: e.y
-      });
+    var d = d3Data.d3g;
+    var d3g = [];
+    if (d.length > 0) {
+      for (var i = 0; i < d[0].values.length ; i++) {
+        var e = d[0].values[i];
+        d3g.push({
+          label: e.x,
+          value: e.y
+        });
+      }
     }
-  }
-  zeppelin.Nvd3ChartVisualization.prototype.render.call(this, {d3g: d3g});
-};
+    super.render({d3g: d3g});
+  };
 
-/**
- * Set new config
- */
-zeppelin.PiechartVisualization.prototype.setConfig = function(config) {
-  zeppelin.Nvd3ChartVisualization.prototype.setConfig.call(this, config);
-  this.pivot.setConfig(config);
-};
+  /**
+   * Set new config
+   */
+  setConfig(config) {
+    super.setConfig(config);
+    this.pivot.setConfig(config);
+  };
 
-zeppelin.PiechartVisualization.prototype.configureChart = function(chart) {
-  chart.x(function(d) { return d.label;}).y(function(d) { return d.value;});
-};
+  configureChart(chart) {
+    chart.x(function(d) { return d.label;}).y(function(d) { return d.value;});
+  };
+}