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/14 18:27:25 UTC

[2/2] zeppelin git commit: [ZEPPELIN-1619] Load js package as a plugin visualization

[ZEPPELIN-1619] Load js package as a plugin visualization

### What is this PR for?
Current helium plugin application api (experimental) requires create library in java class, and need to create both backend / frontend code in the package. Which is good if your plugin requires both frontend and backend code running.

However, when user just want to make new visualization which completely runs on front-end side in javascript, creating helium application in java project and taking care of backend code can be bit of overhead and barrier for javascript developers.

This PR adds capability to load pure javascript package as a visualization.

### how it works

1. create (copy, download) 'helium package json' file into `ZEPPELIN_HOME/helium` directory.
  The json file point visualization js package in npm repository or local file system in `artifact` field.
  `type` field in the json file need to be `VISUALIZATION`

Here's an example (zeppelin-examples/zeppelin-example-horizontalbar/zeppelin-example-horizontalbar.json)
```
{
  "type" : "VISUALIZATION",
  "name" : "zeppelin_horizontalbar",
  "description" : "Horizontal Bar chart (example)",
  "artifact" : "./zeppelin-examples/zeppelin-example-horizontalbar",
  "icon" : "<i class='fa fa-bar-chart rotate90flipX'></i>"
}
```

2. Go to helium GUI menu. (e.g. http://localhost:8080/#/helium).
  The menu will list all available packages.
<img width="796" alt="writing_visualization_helium_menu" src="https://cloud.githubusercontent.com/assets/1540981/21749660/0f401c10-d558-11e6-9961-b6d0a9c023d8.png">

3. click 'enable' in any package want to use.
Once a visualization package is enabled, `HeliumVisualizationFactory` will collect all enabled visualizations and create js bundle on the fly.

4. js bundle will be loaded on notebook and additional visualization becomes available
![image](https://cloud.githubusercontent.com/assets/1540981/21749729/709b2b3e-d559-11e6-8318-7f2871e7c39a.png)

### Programming API to create new plugin visualization.

Simply extends [visualization.js](https://github.com/apache/zeppelin/blob/master/zeppelin-web/src/app/visualization/visualization.js) and overrides some methods, such as

```
  /**
   * get transformation
   */
  getTransformation() {
    // override this
  };

  /**
   * Method will be invoked when data or configuration changed
   */
  render(tableData) {
    // override this
  };

  /**
   * Refresh visualization.
   */
  refresh() {
    // override this
  };

  /**
   * method will be invoked when visualization need to be destroyed.
   * Don't need to destroy this.targetEl.
   */
  destroy() {
    // 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
  };
```

This is exactly the same api that built-in visualization uses.

an example implementation included `zeppelin-examples/zeppelin-example-horizontalbar/horizontalbar.js`.
Actually [all built-in visualizations](https://github.com/apache/zeppelin/tree/master/zeppelin-web/src/app/visualization/builtins) are example

### Packaging and publishing visualization

Each visualization will need `package.json` file (e.g. `zeppelin-examples/zeppelin-example-horizontalbar/package.json`) to be packaged.
Package can be published in npm repository or package can be deployed to the local filesystem.

`zeppelin-examples/zeppelin-example-horizontalbar/` is an example package that is deployed in the local filesystem

### Development mode

First, locally install and enable your development package by setting `artifact` field to the development directory.
Then run zeppelin-web in visualization development mode with following command
```
cd zeppelin-web
npm run visdev
```
When you have change in your local development package, just reload your notebook. Then Zeppelin will automatically rebuild / reload the package.

Any feedback would be appreciated!

### What type of PR is it?
Feature

### Todos
* [x] - Load plugin visualization js package on runtime
* [x] - Make the feature works in zeppelin Binary package
* [x] - Show loading indicator while 'enable' / 'disable' package
* [x] - Add document
* [x] - Add license of new dependency
* [x] - Development mode
* [x] - Propagate error to front-end
* [x] - Display multiple versions of a package.

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

### How should this be tested?
Build Zeppelin with `-Pexamples` flag. That'll install example visualization package `horizontalbar`.
You'll able to select `horizontalbar` along with other built-in visualizations
![image](https://cloud.githubusercontent.com/assets/1540981/21655057/27d61740-d26d-11e6-88f2-02c653e102c6.png)

To test npm online package install capability,  Place [zeppelin-bubble.json](https://github.com/Leemoonsoo/zeppelin-bubble/blob/master/zeppelin-bubble.json) in hour local registry (`ZEPPELIN_HOME/helium`) and enable it in Helium gui menu.
And then zeppelin will download package from npm repository and load.
![bubblechart](https://cloud.githubusercontent.com/assets/1540981/21749717/280aa430-d559-11e6-9209-889a4f86d7e2.gif)

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

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

Closes #1842 from Leemoonsoo/ZEPPELIN-1619-rebased and squashes the following commits:

7c49bbb [Lee moon soo] Let Zeppelin continue to bootstrap on offline
816bdec [Lee moon soo] Display license of package when enabling
28fb37d [Lee moon soo] beautifulize helium menu
295768e [Lee moon soo] fix drag and drop visualization reorder
bb304db [Lee moon soo] Sort version in decreasing order
e7f18f1 [Lee moon soo] fix english in docs and labels
c7b187f [Lee moon soo] Merge branch 'master' into ZEPPELIN-1619-rebased
4c87983 [Lee moon soo] Merge remote-tracking branch 'apache-github/master' into ZEPPELIN-1619-rebased
ecd925b [Lee moon soo] Merge remote-tracking branch 'apache-github/master' into ZEPPELIN-1619-rebased
a92cadd [Lee moon soo] Use minifiable syntax
cec534c [Lee moon soo] Reduce log message
f373f1d [Lee moon soo] Ignore removed package
e18d9a4 [Lee moon soo] Ability to customize order of visualization package display
cd74396 [Lee moon soo] Add rest api doc
9de5d6d [Lee moon soo] exclude .npmignore and package.json from zeppelin-web rat check
08abded [Lee moon soo] exclude package.json from rat check
661c26b [Lee moon soo] update screenshot and keep experimental tag only in docs
4515805 [Lee moon soo] Display multiple versions of a package
408c512 [Lee moon soo] Make unittest test bundling with proper vis package on npm registry
fb7a147 [Lee moon soo] display svg icon
47de6d9 [Lee moon soo] Propagate bundle error to the front-end
0fe5e00 [Lee moon soo] visualization development mode
022e8f6 [Lee moon soo] exclude zeppelin-examples/zeppelin-example-horizontalbar/package.json file from rat check
2ef3b69 [Lee moon soo] Add new dependency license
f943d33 [Lee moon soo] Add doc
f494dbd [Lee moon soo] package npm dependency module in binary package
b655fa6 [Lee moon soo] use any version of dependency in example. so zeppelin version bumpup doesn't need to take care of them
2aec52d [Lee moon soo] show loading indicator while enable/disable package
6c380f6 [Lee moon soo] refactor code to fix HeliumTest
e142336 [Lee moon soo] update unittest
7d5e0ae [Lee moon soo] Resolve dependency conflict
c50a524 [Lee moon soo] Add conf/helium.json in .gitignore
1c7b73a [Lee moon soo] add result.css
d2823ad [Lee moon soo] load visualization and tabledata module from source instead npm if accessible
4e1b061 [Lee moon soo] Convert horizontalbar to VISUALIZATION example
a5a935b [Lee moon soo] connect visualization factory with restapi
4b21252 [Lee moon soo] initial implementation of helium menu
0c4da2e [Lee moon soo] pass bundled visualization to result.controller.js
f5ce99e [Lee moon soo] import helium service js
1663582 [Lee moon soo] initial implementation of helium menu
74d52d4 [Lee moon soo] bundle visualization package from npm repository on the fly


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

Branch: refs/heads/master
Commit: 300f7532342d1ea47b85d3b777a8797a3e2248d4
Parents: 00742ff
Author: Lee moon soo <mo...@apache.org>
Authored: Thu Jan 12 10:58:06 2017 -0800
Committer: Lee moon soo <mo...@apache.org>
Committed: Sat Jan 14 10:27:17 2017 -0800

----------------------------------------------------------------------
 .gitignore                                      |   1 +
 .travis.yml                                     |   3 +-
 docs/_includes/themes/zeppelin/_navigation.html |   4 +-
 .../docs-img/writing_visualization_example.png  | Bin 0 -> 80870 bytes
 .../writing_visualization_helium_menu.png       | Bin 0 -> 177591 bytes
 .../development/writingzeppelinvisualization.md | 212 +++++++++++
 docs/rest-api/rest-helium.md                    | 378 +++++++++++++++++++
 pom.xml                                         |   3 +-
 .../src/assemble/distribution.xml               |   8 +
 zeppelin-distribution/src/bin_license/LICENSE   |   2 +
 .../zeppelin-example-clock.json                 |   1 +
 .../horizontalbar.js                            |  77 ++++
 .../zeppelin-example-horizontalbar/package.json |  12 +
 .../app/horizontalbar/HorizontalBar.java        |  85 -----
 .../example/app/horizontalbar/horizontalbar.js  |  56 ---
 .../horizontalbar/horizontalbar_mockdata.txt    |  10 -
 .../zeppelin-example-horizontalbar.json         |  11 +-
 .../apache/zeppelin/helium/HeliumPackage.java   |  14 +-
 .../zeppelin/helium/ApplicationLoaderTest.java  |   4 +-
 zeppelin-server/pom.xml                         |   5 +-
 .../org/apache/zeppelin/rest/HeliumRestApi.java |  95 ++++-
 .../apache/zeppelin/server/ZeppelinServer.java  |  47 ++-
 zeppelin-web/.eslintrc                          |   3 +-
 zeppelin-web/package.json                       |   2 +
 zeppelin-web/pom.xml                            |   3 +-
 zeppelin-web/src/app/app.js                     |  22 +-
 .../src/app/helium/helium.controller.js         | 219 +++++++++++
 zeppelin-web/src/app/helium/helium.css          | 107 ++++++
 zeppelin-web/src/app/helium/helium.html         |  86 +++++
 .../paragraph/result/result-chart-selector.html |   5 +-
 .../paragraph/result/result.controller.js       |  37 +-
 .../app/notebook/paragraph/result/result.css    |  24 +-
 zeppelin-web/src/app/tabledata/.npmignore       |   1 +
 zeppelin-web/src/app/tabledata/package.json     |  15 +
 zeppelin-web/src/app/visualization/.npmignore   |   1 +
 zeppelin-web/src/app/visualization/package.json |  16 +
 .../src/components/helium/helium.service.js     |  62 +++
 zeppelin-web/src/components/navbar/navbar.html  |   1 +
 zeppelin-web/src/index.html                     |   1 +
 zeppelin-web/src/index.js                       |   2 +
 zeppelin-web/webpack.config.js                  |   9 +-
 zeppelin-zengine/pom.xml                        |  22 +-
 .../java/org/apache/zeppelin/helium/Helium.java | 221 ++++++++++-
 .../org/apache/zeppelin/helium/HeliumConf.java  |  42 ++-
 .../helium/HeliumPackageSearchResult.java       |   8 +-
 .../helium/HeliumVisualizationFactory.java      | 351 +++++++++++++++++
 .../org/apache/zeppelin/helium/NpmPackage.java  |  28 ++
 .../apache/zeppelin/helium/WebpackResult.java   |  25 ++
 .../src/main/resources/helium/package.json      |  15 +
 .../src/main/resources/helium/webpack.config.js |  34 ++
 .../helium/HeliumApplicationFactoryTest.java    |  12 +-
 .../helium/HeliumLocalRegistryTest.java         |   4 +-
 .../org/apache/zeppelin/helium/HeliumTest.java  |  22 +-
 .../helium/HeliumVisualizationFactoryTest.java  | 157 ++++++++
 .../src/test/resources/helium/vis1/package.json |  12 +
 .../src/test/resources/helium/vis1/vis1.js      |  37 ++
 .../src/test/resources/helium/vis2/package.json |  12 +
 .../src/test/resources/helium/vis2/vis2.js      |  38 ++
 58 files changed, 2453 insertions(+), 231 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 2947228..5b638fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@ conf/interpreter.json
 conf/notebook-authorization.json
 conf/shiro.ini
 conf/credentials.json
+conf/helium.json
 
 # other generated files
 spark/dependency-reduced-pom.xml

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/.travis.yml
----------------------------------------------------------------------
diff --git a/.travis.yml b/.travis.yml
index 1e046c4..48e8aa7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -106,8 +106,7 @@ after_success:
 
 after_failure:
   - echo "Travis exited with ${TRAVIS_TEST_RESULT}"
-  - cat target/rat.txt
-  - cat zeppelin-server/target/rat.txt
+  - find . -name rat.txt | xargs cat
   - cat zeppelin-distribution/target/zeppelin-*-SNAPSHOT/zeppelin-*-SNAPSHOT/logs/zeppelin*.log
   - cat zeppelin-distribution/target/zeppelin-*-SNAPSHOT/zeppelin-*-SNAPSHOT/logs/zeppelin*.out
   - cat zeppelin-web/npm-debug.log

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/docs/_includes/themes/zeppelin/_navigation.html
----------------------------------------------------------------------
diff --git a/docs/_includes/themes/zeppelin/_navigation.html b/docs/_includes/themes/zeppelin/_navigation.html
index 42b38f4..b13ef68 100644
--- a/docs/_includes/themes/zeppelin/_navigation.html
+++ b/docs/_includes/themes/zeppelin/_navigation.html
@@ -103,6 +103,7 @@
                 <li><a href="{{BASE_PATH}}/rest-api/rest-notebook.html">Notebook API</a></li>
                 <li><a href="{{BASE_PATH}}/rest-api/rest-configuration.html">Configuration API</a></li>
                 <li><a href="{{BASE_PATH}}/rest-api/rest-credential.html">Credential API</a></li>
+                <li><a href="{{BASE_PATH}}/rest-api/rest-helium.html">Helium API</a></li>
                 <li role="separator" class="divider"></li>
                 <li class="title"><span><b>Security</b><span></li>
                 <li><a href="{{BASE_PATH}}/security/shiroauthentication.html">Shiro Authentication</a></li>                
@@ -118,7 +119,8 @@
                 <li role="separator" class="divider"></li>
                 <li class="title"><span><b>Contibute</b><span></li>
                 <li><a href="{{BASE_PATH}}/development/writingzeppelininterpreter.html">Writing Zeppelin Interpreter</a></li>
-                <li><a href="{{BASE_PATH}}/development/writingzeppelinapplication.html">Writing Zeppelin Application (Experimental)</a></li>                
+                <li><a href="{{BASE_PATH}}/development/writingzeppelinvisualization.html">Writing Zeppelin Visualization (Experimental)</a></li>
+                <li><a href="{{BASE_PATH}}/development/writingzeppelinapplication.html">Writing Zeppelin Application (Experimental)</a></li>
                 <li><a href="{{BASE_PATH}}/development/howtocontribute.html">How to contribute (code)</a></li>
                 <li><a href="{{BASE_PATH}}/development/howtocontributewebsite.html">How to contribute (website)</a></li>
               </ul>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/docs/assets/themes/zeppelin/img/docs-img/writing_visualization_example.png
----------------------------------------------------------------------
diff --git a/docs/assets/themes/zeppelin/img/docs-img/writing_visualization_example.png b/docs/assets/themes/zeppelin/img/docs-img/writing_visualization_example.png
new file mode 100644
index 0000000..219d7c8
Binary files /dev/null and b/docs/assets/themes/zeppelin/img/docs-img/writing_visualization_example.png differ

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/docs/assets/themes/zeppelin/img/docs-img/writing_visualization_helium_menu.png
----------------------------------------------------------------------
diff --git a/docs/assets/themes/zeppelin/img/docs-img/writing_visualization_helium_menu.png b/docs/assets/themes/zeppelin/img/docs-img/writing_visualization_helium_menu.png
new file mode 100644
index 0000000..7e1ce20
Binary files /dev/null and b/docs/assets/themes/zeppelin/img/docs-img/writing_visualization_helium_menu.png differ

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/docs/development/writingzeppelinvisualization.md
----------------------------------------------------------------------
diff --git a/docs/development/writingzeppelinvisualization.md b/docs/development/writingzeppelinvisualization.md
new file mode 100644
index 0000000..d7c2268
--- /dev/null
+++ b/docs/development/writingzeppelinvisualization.md
@@ -0,0 +1,212 @@
+---
+layout: page
+title: "Writing a new Visualization(Experimental)"
+description: "Apache Zeppelin Application is a package that runs on Interpreter process and displays it's output inside of the notebook. Make your own Application in Apache Zeppelin is quite easy."
+group: development
+---
+<!--
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+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.
+-->
+{% include JB/setup %}
+
+# Writing a new Visualization (Experimental)
+
+<div id="toc"></div>
+
+## What is Apache Zeppelin Visualization
+
+Apache Zeppelin Visualization is a pluggable package that can be loaded/unloaded on runtime through Helium framework in Zeppelin. A Visualization is a javascript npm package and user can use them just like any other built-in visualization in notebook.
+
+
+## How it works
+
+
+#### 1. Load Helium package files from registry
+Zeppelin needs to know what Visualization packages are available. Zeppelin searches _Helium package file_ from local registry (by default helium/ directory) by default.
+_Helium package file_ provides informations like name, artifact, and so on. It's similar to _package.json_ in npm package.
+
+Here's an example `helium/zeppelin-example-horizontalbar.json`
+
+```
+{
+  "type" : "VISUALIZATION",
+  "name" : "zeppelin_horizontalbar",
+  "description" : "Horizontal Bar chart (example)",
+  "artifact" : "./zeppelin-examples/zeppelin-example-horizontalbar",
+  "license" : "Apache-2.0",
+  "icon" : "<i class='fa fa-bar-chart rotate90flipX'></i>"
+}
+```
+
+Check [Create helium package file](#3-create-helium-package-file) section to learn about it.
+
+
+#### 2. Enable packages
+Once Zeppelin loads _Helium package files_ from local registry, available packages are displayed in Helium menu.
+
+Click 'enable' button.
+
+<img class="img-responsive" style="width:70%" src="/assets/themes/zeppelin/img/docs-img/writing_visualization_helium_menu.png" />
+
+
+#### 3. Create and load visualization bundle on the fly
+
+Once a Visualization package is enabled, [HeliumVisualizationFactory](https://github.com/apache/zeppelin/blob/master/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumVisualizationPackage.java) creates a js bundle. The js bundle is served by `helium/visualization/load` rest api endpoint.
+
+
+#### 4. Run visualization
+
+Zeppelin shows additional button for loaded Visualizations.
+User can use just like any other built-in visualizations.
+
+<img class="img-responsive" style="width:70%" src="/assets/themes/zeppelin/img/docs-img/writing_visualization_example.png" />
+
+
+
+## Write new Visualization
+
+#### 1. Create a npm package
+
+Create a [package.json](https://docs.npmjs.com/files/package.json) in your new Visualization directory. Normally, you can add any dependencies in package.json however Zeppelin Visualization package only allows two dependencies: [zeppelin-vis](https://github.com/apache/zeppelin/tree/master/zeppelin-web/src/app/visualization) and [zeppelin-tabledata](https://github.com/apache/zeppelin/tree/master/zeppelin-web/src/app/tabledata).
+
+Here's an example
+
+```
+{
+  "name": "zeppelin_horizontalbar",
+  "description" : "Horizontal Bar chart",
+  "version": "1.0.0",
+  "main": "horizontalbar",
+  "author": "",
+  "license": "Apache-2.0",
+  "dependencies": {
+    "zeppelin-tabledata": "*",
+    "zeppelin-vis": "*"
+  }
+}
+```
+
+#### 2. Create your own visualization
+
+To create your own visualization, you need to create a js file and import [Visualization](https://github.com/apache/zeppelin/blob/master/zeppelin-web/src/app/visualization/visualization.js) class from [zeppelin-vis](https://github.com/apache/zeppelin/tree/master/zeppelin-web/src/app/visualization) package and extend the class. [zeppelin-tabledata](https://github.com/apache/zeppelin/tree/master/zeppelin-web/src/app/tabledata) package provides some useful transformations, like pivot, you can use in your visualization. (you can create your own transformation, too).
+
+[Visualization](https://github.com/apache/zeppelin/blob/master/zeppelin-web/src/app/visualization/visualization.js) class, there're several methods that you need to override and implement. Here's simple visualization that just prints `Hello world`.
+
+```
+import Visualization from 'zeppelin-vis'
+import PassthroughTransformation from 'zeppelin-tabledata/passthrough'
+
+export default class helloworld extends Visualization {
+  constructor(targetEl, config) {
+    super(targetEl, config)
+    this.passthrough = new PassthroughTransformation(config);
+  }
+
+  render(tableData) {
+    this.targetEl.html('Hello world!')
+  }
+
+  getTransformation() {
+    return this.passthrough
+  }
+}
+```
+
+To learn more about `Visualization` class, check [visualization.js](https://github.com/apache/zeppelin/blob/master/zeppelin-web/src/app/visualization/visualization.js).
+
+You can check complete visualization package example [here](https://github.com/apache/zeppelin/tree/master/zeppelin-examples/zeppelin-example-horizontalbar).
+
+Zeppelin's built-in visualization uses the same API, so you can check [built-in visualizations](https://github.com/apache/zeppelin/tree/master/zeppelin-web/src/app/visualization/builtins) as additional examples.
+
+
+#### 3. Create __Helium package file__
+
+__Helium Package file__ is a json file that provides information about the application.
+Json file contains the following information
+
+```
+{
+  "type" : "VISUALIZATION",
+  "name" : "zeppelin_horizontalbar",
+  "description" : "Horizontal Bar chart (example)",
+  "license" : "Apache-2.0",
+  "artifact" : "./zeppelin-examples/zeppelin-example-horizontalbar",
+  "icon" : "<i class='fa fa-bar-chart rotate90flipX'></i>"
+}
+```
+
+##### type
+
+When you're creating a visualization, 'type' should be 'VISUALIZATION'.
+Check [application](./writingzeppelinapplication.html) type if you're interested in the other types of package.
+
+##### name
+
+Name of visualization. Should be unique. Allows `[A-Za-z90-9_]`.
+
+
+##### description
+
+A short description about visualization.
+
+##### artifact
+
+Location of the visualization npm package. Support npm package with version or local filesystem path.
+
+e.g.
+
+When artifact exists in npm repository
+
+```
+artifact: "my-visualiztion@1.0.0"
+```
+
+
+When artifact exists in local file system
+
+```
+artifact: "/path/to/my/visualization"
+```
+
+##### license
+
+License information.
+
+e.g.
+
+```
+license: "Apache-2.0"
+```
+
+##### icon
+
+Icon to be used in visualization select button. String in this field will be rendered as a HTML tag.
+
+e.g.
+
+```
+icon: "<i class='fa fa-coffee'></i>"
+```
+
+
+#### 4. Run in dev mode
+
+Place your __Helium package file__ in local registry (ZEPPELIN_HOME/helium).
+Run Zeppelin. And then run zeppelin-web in visualization dev mode.
+
+```
+cd zeppelin-web
+yarn run visdev
+```
+
+You can browse localhost:9000. Everytime refresh your browser, Zeppelin will rebuild your visualization and reload changes.

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/docs/rest-api/rest-helium.md
----------------------------------------------------------------------
diff --git a/docs/rest-api/rest-helium.md b/docs/rest-api/rest-helium.md
new file mode 100644
index 0000000..8d2ff4d
--- /dev/null
+++ b/docs/rest-api/rest-helium.md
@@ -0,0 +1,378 @@
+---
+layout: page
+title: "Apache Zeppelin Helium REST API"
+description: "This page contains Apache Zeppelin Helium REST API information."
+group: rest-api
+---
+<!--
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+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.
+-->
+{% include JB/setup %}
+
+# Apache Zeppelin Helium REST API
+
+<div id="toc"></div>
+
+## Overview
+Apache Zeppelin provides several REST APIs for interaction and remote activation of zeppelin functionality.
+All REST APIs are available starting with the following endpoint `http://[zeppelin-server]:[zeppelin-port]/api`. 
+Note that Apache Zeppelin REST APIs receive or return JSON objects, it is recommended for you to install some JSON viewers such as [JSONView](https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc).
+
+If you work with Apache Zeppelin and find a need for an additional REST API, please [file an issue or send us an email](http://zeppelin.apache.org/community.html).
+
+## Helium REST API List
+
+### List of all available helium packages
+
+  <table class="table-configuration">
+    <col width="200">
+    <tr>
+      <td>Description</td>
+      <td>This ```GET``` method returns all the available helium packages in configured registries.</td>
+    </tr>
+    <tr>
+      <td>URL</td>
+      <td>```http://[zeppelin-server]:[zeppelin-port]/api/helium/all```</td>
+    </tr>
+    <tr>
+      <td>Success code</td>
+      <td>200</td>
+    </tr>
+    <tr>
+      <td>Fail code</td>
+      <td> 500 </td>
+    </tr>
+    <tr>
+      <td>Sample JSON response</td>
+      <td>
+        <pre>
+{
+  "status": "OK",
+  "message": "",
+  "body": {
+    "zeppelin.clock": [
+      {
+        "registry": "local",
+        "pkg": {
+          "type": "APPLICATION",
+          "name": "zeppelin.clock",
+          "description": "Clock (example)",
+          "artifact": "zeppelin-examples\/zeppelin-example-clock\/target\/zeppelin-example-clock-0.7.0-SNAPSHOT.jar",
+          "className": "org.apache.zeppelin.example.app.clock.Clock",
+          "resources": [
+            [
+              ":java.util.Date"
+            ]
+          ],
+          "icon": "icon"
+        },
+        "enabled": false
+      }
+    ],
+    "zeppelin-bubblechart": [
+      {
+        "registry": "local",
+        "pkg": {
+          "type": "VISUALIZATION",
+          "name": "zeppelin-bubblechart",
+          "description": "Animated bubble chart",
+          "artifact": ".\/..\/helium\/zeppelin-bubble",
+          "icon": "icon"
+        },
+        "enabled": true
+      },
+      {
+        "registry": "local",
+        "pkg": {
+          "type": "VISUALIZATION",
+          "name": "zeppelin-bubblechart",
+          "description": "Animated bubble chart",
+          "artifact": "zeppelin-bubblechart@0.0.2",
+          "icon": "icon"
+        },
+        "enabled": false
+      }
+    ],
+    "zeppelin_horizontalbar": [
+      {
+        "registry": "local",
+        "pkg": {
+          "type": "VISUALIZATION",
+          "name": "zeppelin_horizontalbar",
+          "description": "Horizontal Bar chart (example)",
+          "artifact": ".\/zeppelin-examples\/zeppelin-example-horizontalbar",
+          "icon": "icon"
+        },
+        "enabled": true
+      }
+    ]
+  }
+}
+        </pre>
+      </td>
+    </tr>
+  </table>
+
+<br/>
+### Suggest Helium application
+
+  <table class="table-configuration">
+    <col width="200">
+    <tr>
+      <td>Description</td>
+      <td>This ```GET``` method returns suggested helium application for the paragraph.</td>
+    </tr>
+    <tr>
+      <td>URL</td>
+      <td>```http://[zeppelin-server]:[zeppelin-port]/api/helium/suggest/[Note ID]/[Paragraph ID]```</td>
+    </tr>
+    <tr>
+      <td>Success code</td>
+      <td>200</td>
+    </tr>
+    <tr>
+      <td>Fail code</td>
+      <td>
+        404 on note or paragraph not exists <br />
+        500
+      </td>
+    </tr>
+    <tr>
+      <td>Sample JSON response</td>
+      <td>
+        <pre>
+{
+  "status": "OK",
+  "message": "",
+  "body": {
+    "available": [
+      {
+        "registry": "local",
+        "pkg": {
+          "type": "APPLICATION",
+          "name": "zeppelin.clock",
+          "description": "Clock (example)",
+          "artifact": "zeppelin-examples\/zeppelin-example-clock\/target\/zeppelin-example-clock-0.7.0-SNAPSHOT.jar",
+          "className": "org.apache.zeppelin.example.app.clock.Clock",
+          "resources": [
+            [
+              ":java.util.Date"
+            ]
+          ],
+          "icon": "icon"
+        },
+        "enabled": true
+      }
+    ]
+  }
+}
+        </pre>
+      </td>
+    </tr>
+  </table>
+  
+<br/>
+### Load helium Application on a paragraph
+
+  <table class="table-configuration">
+    <col width="200">
+    <tr>
+      <td>Description</td>
+      <td>This ```GET``` method returns a helium Application id on success.</td>
+    </tr>
+    <tr>
+      <td>URL</td>
+      <td>```http://[zeppelin-server]:[zeppelin-port]/api/helium/load/[Note ID]/[Paragraph ID]```</td>
+    </tr>
+    <tr>
+      <td>Success code</td>
+      <td>200</td>
+    </tr>
+    <tr>
+      <td>Fail code</td>
+      <td>
+          404 on note or paragraph not exists <br/>
+          500 for any other errors
+      </td>
+    </tr>
+    <tr>
+      <td>Sample JSON response</td>
+      <td>
+        <pre>
+{
+  "status": "OK",
+  "message": "",
+  "body": "app_2C5FYRZ1E-20170108-040449_2068241472zeppelin_clock"
+}
+        </pre>
+      </td>
+    </tr>
+  </table>
+
+<br/>
+### Load bundled visualization script
+
+  <table class="table-configuration">
+    <col width="200">
+    <tr>
+      <td>Description</td>
+      <td>This ```GET``` method returns bundled helium visualization javascript. When refresh=true (optional) is provided, Zeppelin rebuild bundle. otherwise, provided from cache</td>
+    </tr>
+    <tr>
+      <td>URL</td>
+      <td>```http://[zeppelin-server]:[zeppelin-port]/api/helium/visualizations/load[?refresh=true]```</td>
+    </tr>
+    <tr>
+      <td>Success code</td>
+      <td>200 reponse body is executable javascript</td>
+    </tr>
+    <tr>
+      <td>Fail code</td>
+      <td>
+          200 reponse body is error message string starts with ERROR:<br/>
+      </td>
+    </tr>
+  </table>
+
+<br/>
+### Enable package
+  <table class="table-configuration">
+    <col width="200">
+    <tr>
+      <td>Description</td>
+      <td>This ```POST``` method enables a helium package. Needs artifact name in input payload</td>
+    </tr>
+    <tr>
+      <td>URL</td>
+      <td>```http://[zeppelin-server]:[zeppelin-port]/api/helium/enable/[Package Name]```</td>
+    </tr>
+    <tr>
+      <td>Success code</td>
+      <td>200</td>
+    </tr>
+    <tr>
+      <td>Fail code</td>
+      <td> 500 </td>
+    </tr>
+    <tr>
+      <td>Sample input</td>
+      <td>
+        <pre>
+zeppelin-examples/zeppelin-example-clock/target/zeppelin-example-clock-0.7.0-SNAPSHOT.jar
+        </pre>
+      </td>
+    </tr>
+    <tr>
+      <td>Sample JSON response</td>
+      <td>
+        <pre>
+{"status":"OK"}
+        </pre>
+      </td>
+    </tr>
+  </table>
+
+<br/>
+### Disable package
+
+  <table class="table-configuration">
+    <col width="200">
+    <tr>
+      <td>Description</td>
+      <td>This ```POST``` method disables a helium package.</td>
+    </tr>
+    <tr>
+      <td>URL</td>
+      <td>```http://[zeppelin-server]:[zeppelin-port]/api/helium/disable/[Package Name]```</td>
+    </tr>
+    <tr>
+      <td>Success code</td>
+      <td>200</td>
+    </tr>
+    <tr>
+      <td> Fail code</td>
+      <td> 500 </td>
+    </tr>
+    <tr>
+      <td>Sample JSON response</td>
+      <td>
+        <code>{"status":"OK"}</code>
+      </td>
+    </tr>
+  </table>
+<br />
+
+### Get visualization display order
+
+  <table class="table-configuration">
+    <col width="200">
+    <tr>
+      <td>Description</td>
+      <td>This ```GET``` method returns display order of enabled visualization packages.</td>
+    </tr>
+    <tr>
+      <td>URL</td>
+      <td>```http://[zeppelin-server]:[zeppelin-port]/api/helium/visualizationOrder```</td>
+    </tr>
+    <tr>
+      <td>Success code</td>
+      <td>200</td>
+    </tr>
+    <tr>
+      <td> Fail code</td>
+      <td> 500 </td>
+    </tr>
+    <tr>
+      <td>Sample JSON response</td>
+      <td>
+        <code>{"status":"OK","body":["zeppelin_horizontalbar","zeppelin-bubblechart"]}</code>
+      </td>
+    </tr>
+  </table>
+
+
+<br />
+
+### Set visualization display order
+
+  <table class="table-configuration">
+    <col width="200">
+    <tr>
+      <td>Description</td>
+      <td>This ```POST``` method sets visualization packages display order.</td>
+    </tr>
+    <tr>
+      <td>URL</td>
+      <td>```http://[zeppelin-server]:[zeppelin-port]/api/helium/visualizationOrder```</td>
+    </tr>
+    <tr>
+      <td>Success code</td>
+      <td>200</td>
+    </tr>
+    <tr>
+      <td> Fail code</td>
+      <td> 500 </td>
+    </tr>
+    <tr>
+      <td>Sample JSON input</td>
+      <td>
+        <code>["zeppelin-bubblechart", "zeppelin_horizontalbar"]</code>
+      </td>
+    </tr>
+    <tr>
+      <td>Sample JSON response</td>
+      <td>
+        <code>{"status":"OK"}</code>
+      </td>
+    </tr>
+  </table>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index dcd359b..481015c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -940,7 +940,8 @@
               <exclude>docs/_site/**</exclude>
               <exclude>docs/Gemfile.lock</exclude>
 
-              <exclude>**/horizontalbar_mockdata.txt</exclude>
+              <!-- package.json -->
+              <exclude>**/package.json</exclude>
 
               <!-- compiled R packages (binaries) -->
               <exclude>R/lib/**</exclude>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-distribution/src/assemble/distribution.xml
----------------------------------------------------------------------
diff --git a/zeppelin-distribution/src/assemble/distribution.xml b/zeppelin-distribution/src/assemble/distribution.xml
index 371aed3..e8188e8 100644
--- a/zeppelin-distribution/src/assemble/distribution.xml
+++ b/zeppelin-distribution/src/assemble/distribution.xml
@@ -95,5 +95,13 @@
       <outputDirectory>/lib/interpreter</outputDirectory>
       <directory>../zeppelin-interpreter/target/lib</directory>
     </fileSet>
+    <fileSet>
+      <outputDirectory>/lib/node_modules/zeppelin-vis</outputDirectory>
+      <directory>../zeppelin-web/src/app/visualization</directory>
+    </fileSet>
+    <fileSet>
+      <outputDirectory>/lib/node_modules/zeppelin-tabledata</outputDirectory>
+      <directory>../zeppelin-web/src/app/tabledata</directory>
+    </fileSet>
   </fileSets>
 </assembly>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-distribution/src/bin_license/LICENSE
----------------------------------------------------------------------
diff --git a/zeppelin-distribution/src/bin_license/LICENSE b/zeppelin-distribution/src/bin_license/LICENSE
index 7cce062..1197ea7 100644
--- a/zeppelin-distribution/src/bin_license/LICENSE
+++ b/zeppelin-distribution/src/bin_license/LICENSE
@@ -214,6 +214,8 @@ The following components are provided under Apache License.
     (Apache 2.0) Maven Wagon HTTP Shared 1.0 (org.apache.maven.wagon:wagon-http-shared:1.0 - https://mvnrepository.com/artifact/org.apache.maven.wagon/wagon-http-shared/1.0)
     (Apache 2.0) Commons HTTP Client 3.1 (commons-httpclient:commons-httpclient:3.1 - https://mvnrepository.com/artifact/commons-httpclient/commons-httpclient/3.1)
     (Apache 2.0) Scalatest 2.2.4 (org.scalatest:scalatest_2.10:2.2.4 - https://github.com/scalatest/scalatest)
+    (Apache 2.0) frontend-maven-plugin 1.3 (com.github.eirslett:frontend-maven-plugin:1.3 - https://github.com/eirslett/frontend-maven-plugin/blob/frontend-plugins-1.3/LICENSE
+    (Apache 2.0) frontend-plugin-core 1.3 (com.github.eirslett:frontend-plugin-core) - https://github.com/eirslett/frontend-maven-plugin/blob/frontend-plugins-1.3/LICENSE
 
 ========================================================================
 MIT licenses

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-examples/zeppelin-example-clock/zeppelin-example-clock.json
----------------------------------------------------------------------
diff --git a/zeppelin-examples/zeppelin-example-clock/zeppelin-example-clock.json b/zeppelin-examples/zeppelin-example-clock/zeppelin-example-clock.json
index a7526f5..3e48697 100644
--- a/zeppelin-examples/zeppelin-example-clock/zeppelin-example-clock.json
+++ b/zeppelin-examples/zeppelin-example-clock/zeppelin-example-clock.json
@@ -21,5 +21,6 @@
   "artifact" : "zeppelin-examples/zeppelin-example-clock/target/zeppelin-example-clock-0.7.0-SNAPSHOT.jar",
   "className" : "org.apache.zeppelin.example.app.clock.Clock",
   "resources" : [[":java.util.Date"]],
+  "license" : "Apache-2.0",
   "icon" : '<i class="fa fa-clock-o"></i>'
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-examples/zeppelin-example-horizontalbar/horizontalbar.js
----------------------------------------------------------------------
diff --git a/zeppelin-examples/zeppelin-example-horizontalbar/horizontalbar.js b/zeppelin-examples/zeppelin-example-horizontalbar/horizontalbar.js
new file mode 100644
index 0000000..d574a89
--- /dev/null
+++ b/zeppelin-examples/zeppelin-example-horizontalbar/horizontalbar.js
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 Nvd3ChartVisualization from 'zeppelin-vis/builtins/visualization-nvd3chart';
+import PivotTransformation from 'zeppelin-tabledata/pivot';
+
+/**
+ * Base class for visualization
+ */
+export default class horizontalbar extends Nvd3ChartVisualization {
+  constructor(targetEl, config) {
+    super(targetEl, config)
+    this.pivot = new PivotTransformation(config);
+  }
+
+  type() {
+    return 'multiBarHorizontalChart';
+  };
+
+  render(pivot) {
+    var d3Data = this.d3DataFromPivot(
+      pivot.schema,
+      pivot.rows,
+      pivot.keys,
+      pivot.groups,
+      pivot.values,
+      true,
+      false,
+      true);
+
+    super.render(d3Data);
+  }
+
+  getTransformation() {
+    return this.pivot;
+  }
+  
+  /**
+   * Set new config
+   */
+  setConfig(config) {
+    super.setConfig(config);
+    this.pivot.setConfig(config);
+  };
+
+  configureChart(chart) {
+    var self = this;
+    chart.yAxis.axisLabelDistance(50);
+    chart.yAxis.tickFormat(function(d) {return self.yAxisTickFormat(d);});
+
+    this.chart.stacked(this.config.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);
+    });
+  };
+}
+

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-examples/zeppelin-example-horizontalbar/package.json
----------------------------------------------------------------------
diff --git a/zeppelin-examples/zeppelin-example-horizontalbar/package.json b/zeppelin-examples/zeppelin-example-horizontalbar/package.json
new file mode 100644
index 0000000..60121d6
--- /dev/null
+++ b/zeppelin-examples/zeppelin-example-horizontalbar/package.json
@@ -0,0 +1,12 @@
+{
+  "name": "zeppelin_horizontalbar",
+  "description" : "Horizontal Bar chart (example)",
+  "version": "1.0.0",
+  "main": "horizontalbar",
+  "author": "",
+  "license": "Apache-2.0",
+  "dependencies": {
+    "zeppelin-tabledata": "*",
+    "zeppelin-vis": "*"
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-examples/zeppelin-example-horizontalbar/src/main/java/org/apache/zeppelin/example/app/horizontalbar/HorizontalBar.java
----------------------------------------------------------------------
diff --git a/zeppelin-examples/zeppelin-example-horizontalbar/src/main/java/org/apache/zeppelin/example/app/horizontalbar/HorizontalBar.java b/zeppelin-examples/zeppelin-example-horizontalbar/src/main/java/org/apache/zeppelin/example/app/horizontalbar/HorizontalBar.java
deleted file mode 100644
index 6637821..0000000
--- a/zeppelin-examples/zeppelin-example-horizontalbar/src/main/java/org/apache/zeppelin/example/app/horizontalbar/HorizontalBar.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF 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.
- */
-package org.apache.zeppelin.example.app.horizontalbar;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.zeppelin.helium.Application;
-import org.apache.zeppelin.helium.ApplicationContext;
-import org.apache.zeppelin.helium.ApplicationException;
-import org.apache.zeppelin.helium.ZeppelinApplicationDevServer;
-import org.apache.zeppelin.interpreter.InterpreterResult;
-import org.apache.zeppelin.resource.*;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Random;
-
-/**
- * Basic example application.
- * TableData for input
- */
-public class HorizontalBar extends Application {
-  private final Logger logger = LoggerFactory.getLogger(HorizontalBar.class);
-
-  InterpreterResult result;
-
-  public HorizontalBar(ApplicationContext context) {
-    super(context);
-  }
-
-  @Override
-  public void run(ResourceSet resources) throws ApplicationException, IOException {
-    // Get data from resource args
-    result = (InterpreterResult) resources.get(0).get();
-
-    // create element
-    println(String.format(
-        "<div id=\"horizontalbar_%s\" style=\"height:400px\"><svg></svg></div>",
-        context().getApplicationInstanceId()));
-    // write js
-    printResourceAsJavascript("example/app/horizontalbar/horizontalbar.js");
-  }
-
-  @Override
-  public void unload() throws ApplicationException {
-  }
-
-  /**
-   * Development mode
-   */
-  public static void main(String[] args) throws Exception {
-    LocalResourcePool pool = new LocalResourcePool("dev");
-    InputStream ins = ClassLoader.getSystemResourceAsStream(
-        "example/app/horizontalbar/horizontalbar_mockdata.txt");
-    InterpreterResult result = new InterpreterResult(
-        InterpreterResult.Code.SUCCESS,
-        InterpreterResult.Type.TABLE,
-        IOUtils.toString(ins));
-    pool.put(WellKnownResourceName.ZeppelinTableResult.name(), result);
-
-    ZeppelinApplicationDevServer devServer = new ZeppelinApplicationDevServer(
-        HorizontalBar.class.getName(),
-        pool.getAll());
-
-    devServer.start();
-    devServer.join();
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-examples/zeppelin-example-horizontalbar/src/main/resources/example/app/horizontalbar/horizontalbar.js
----------------------------------------------------------------------
diff --git a/zeppelin-examples/zeppelin-example-horizontalbar/src/main/resources/example/app/horizontalbar/horizontalbar.js b/zeppelin-examples/zeppelin-example-horizontalbar/src/main/resources/example/app/horizontalbar/horizontalbar.js
deleted file mode 100644
index fac2c8e..0000000
--- a/zeppelin-examples/zeppelin-example-horizontalbar/src/main/resources/example/app/horizontalbar/horizontalbar.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF 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.
- */
-
-var data = [];
-_.forEach($z.result.columnNames, function(col, series) {
-   if (series == 0) return;
-   var values = _.map($z.result.rows, function(row) {
-       return {
-           label: row[0],
-           value : parseFloat(row[series])
-       }
-   });
-
-   data.push({
-       key : col.name,
-       values : values
-   })
-});
-
-nv.addGraph(function() {
-    var chart = nv.models.multiBarHorizontalChart()
-        .x(function(d) { return d.label })
-        .y(function(d) { return d.value })
-        .margin({top: 30, right: 20, bottom: 50, left: 175})
-        .showValues(true)           //Show bar value next to each bar.
-        .tooltips(true)             //Show tooltips on hover.
-        .showControls(true);        //Allow user to switch between "Grouped" and "Stacked" mode.
-
-    chart.yAxis
-        .tickFormat(d3.format(',.2f'));
-
-    d3.select('#horizontalbar_' + $z.id + ' svg')
-        .datum(data)
-        .call(chart);
-
-    nv.utils.windowResize(chart.update);
-
-    return chart;
-});
-
-
-

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-examples/zeppelin-example-horizontalbar/src/main/resources/example/app/horizontalbar/horizontalbar_mockdata.txt
----------------------------------------------------------------------
diff --git a/zeppelin-examples/zeppelin-example-horizontalbar/src/main/resources/example/app/horizontalbar/horizontalbar_mockdata.txt b/zeppelin-examples/zeppelin-example-horizontalbar/src/main/resources/example/app/horizontalbar/horizontalbar_mockdata.txt
deleted file mode 100644
index adf322d..0000000
--- a/zeppelin-examples/zeppelin-example-horizontalbar/src/main/resources/example/app/horizontalbar/horizontalbar_mockdata.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Label	Series1	Series2
-GroupA	-1.8746444827653	25.307646510375
-GroupB	-8.0961543492239	16.756779544553
-GroupC	-0.57072943117674	18.451534877007
-GroupD	-2.4174010336624	8.6142352811805
-GroupE	-0.72009071426284	7.8082472075876
-GroupF	-0.77154485523777	5.259101026956
-GroupG	-0.90152097798131	0.30947953487127
-GroupH	-0.91445417330854	0
-GroupI	-0.055746319141851	0

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-examples/zeppelin-example-horizontalbar/zeppelin-example-horizontalbar.json
----------------------------------------------------------------------
diff --git a/zeppelin-examples/zeppelin-example-horizontalbar/zeppelin-example-horizontalbar.json b/zeppelin-examples/zeppelin-example-horizontalbar/zeppelin-example-horizontalbar.json
index da57d06..c8ac28c 100644
--- a/zeppelin-examples/zeppelin-example-horizontalbar/zeppelin-example-horizontalbar.json
+++ b/zeppelin-examples/zeppelin-example-horizontalbar/zeppelin-example-horizontalbar.json
@@ -15,11 +15,10 @@
  * limitations under the License.
  */
 {
-  "type" : "APPLICATION",
-  "name" : "zeppelin.horizontalbar",
+  "type" : "VISUALIZATION",
+  "name" : "zeppelin_horizontalbar",
   "description" : "Horizontal Bar chart (example)",
-  "artifact" : "zeppelin-examples/zeppelin-example-horizontalbar/target/zeppelin-example-horizontalbar-0.7.0-SNAPSHOT.jar",
-  "className" : "org.apache.zeppelin.example.app.horizontalbar.HorizontalBar",
-  "resources" : [[":org.apache.zeppelin.interpreter.InterpreterResult"]],
-  "icon" : '<i class="fa fa-bar-chart" style="-webkit-transform: rotate(90deg) scaleX(-1);-moz-transform: rotate(90deg) scaleX(-1);-ms-transform: rotate(90deg) scaleX(-1);"></i>'
+  "artifact" : "./zeppelin-examples/zeppelin-example-horizontalbar",
+  "license" : "Apache-2.0",
+  "icon" : "<i class='fa fa-bar-chart rotate90flipX'></i>"
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/HeliumPackage.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/HeliumPackage.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/HeliumPackage.java
index 1352642..84a2ab3 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/HeliumPackage.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/HeliumPackage.java
@@ -30,14 +30,17 @@ public class HeliumPackage {
   private String className;      // entry point
   private String [][] resources; // resource classnames that requires
                                  // [[ .. and .. and .. ] or [ .. and .. and ..] ..]
+  private String license;
   private String icon;
+
   /**
    * Type of package
    */
   public static enum Type {
     INTERPRETER,
     NOTEBOOK_REPO,
-    APPLICATION
+    APPLICATION,
+    VISUALIZATION
   }
 
   public HeliumPackage(Type type,
@@ -45,13 +48,17 @@ public class HeliumPackage {
                        String description,
                        String artifact,
                        String className,
-                       String[][] resources) {
+                       String[][] resources,
+                       String license,
+                       String icon) {
     this.type = type;
     this.name = name;
     this.description = description;
     this.artifact = artifact;
     this.className = className;
     this.resources = resources;
+    this.license = license;
+    this.icon = icon;
   }
 
   @Override
@@ -93,6 +100,9 @@ public class HeliumPackage {
     return resources;
   }
 
+  public String getLicense() {
+    return license;
+  }
   public String getIcon() {
     return icon;
   }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-interpreter/src/test/java/org/apache/zeppelin/helium/ApplicationLoaderTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/helium/ApplicationLoaderTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/helium/ApplicationLoaderTest.java
index c92699c..3924e28 100644
--- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/helium/ApplicationLoaderTest.java
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/helium/ApplicationLoaderTest.java
@@ -79,7 +79,9 @@ public class ApplicationLoaderTest {
         "desc1",
         artifact,
         className,
-        new String[][]{{}});
+        new String[][]{{}},
+        "license",
+        "icon");
     return app1;
   }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-server/pom.xml
----------------------------------------------------------------------
diff --git a/zeppelin-server/pom.xml b/zeppelin-server/pom.xml
index ea51fa7..6503e66 100644
--- a/zeppelin-server/pom.xml
+++ b/zeppelin-server/pom.xml
@@ -279,7 +279,10 @@
           <groupId>com.jcraft</groupId>
           <artifactId>jsch</artifactId>
         </exclusion>
-
+        <exclusion>
+          <groupId>org.apache.commons</groupId>
+          <artifactId>commons-compress</artifactId>
+        </exclusion>
       </exclusions>
     </dependency>
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-server/src/main/java/org/apache/zeppelin/rest/HeliumRestApi.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/HeliumRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/HeliumRestApi.java
index 062f5b9..953188b 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/HeliumRestApi.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/HeliumRestApi.java
@@ -17,9 +17,11 @@
 
 package org.apache.zeppelin.rest;
 
+import com.github.eirslett.maven.plugins.frontend.lib.TaskRunnerException;
 import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import org.apache.commons.io.FileUtils;
 import org.apache.zeppelin.helium.Helium;
-import org.apache.zeppelin.helium.HeliumApplicationFactory;
 import org.apache.zeppelin.helium.HeliumPackage;
 import org.apache.zeppelin.notebook.Note;
 import org.apache.zeppelin.notebook.Notebook;
@@ -30,6 +32,9 @@ import org.slf4j.LoggerFactory;
 
 import javax.ws.rs.*;
 import javax.ws.rs.core.Response;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
 
 /**
  * Helium Rest Api
@@ -40,18 +45,14 @@ public class HeliumRestApi {
   Logger logger = LoggerFactory.getLogger(HeliumRestApi.class);
 
   private Helium helium;
-  private HeliumApplicationFactory applicationFactory;
   private Notebook notebook;
   private Gson gson = new Gson();
 
   public HeliumRestApi() {
   }
 
-  public HeliumRestApi(Helium helium,
-                       HeliumApplicationFactory heliumApplicationFactory,
-                       Notebook notebook) {
+  public HeliumRestApi(Helium helium, Notebook notebook) {
     this.helium  = helium;
-    this.applicationFactory = heliumApplicationFactory;
     this.notebook = notebook;
   }
 
@@ -101,8 +102,88 @@ public class HeliumRestApi {
     }
     HeliumPackage pkg = gson.fromJson(heliumPackage, HeliumPackage.class);
 
-    String appId = applicationFactory.loadAndRun(pkg, paragraph);
+    String appId = helium.getApplicationFactory().loadAndRun(pkg, paragraph);
     return new JsonResponse(Response.Status.OK, "", appId).build();
   }
 
+  @GET
+  @Path("visualizations/load")
+  @Produces("text/javascript")
+  public Response visualizationLoad(@QueryParam("refresh") String refresh) {
+    try {
+      File bundle;
+      if (refresh != null && refresh.equals("true")) {
+        bundle = helium.recreateVisualizationBundle();
+      } else {
+        bundle = helium.getVisualizationFactory().getCurrentBundle();
+      }
+
+      if (bundle == null) {
+        return Response.ok().build();
+      } else {
+        String visBundle = FileUtils.readFileToString(bundle);
+        return Response.ok(visBundle).build();
+      }
+    } catch (Exception e) {
+      logger.error(e.getMessage(), e);
+
+      // returning error will prevent zeppelin front-end render any notebook.
+      // visualization load fail doesn't need to block notebook rendering work.
+      // so it's better return ok instead of any error.
+      return Response.ok("ERROR: " + e.getMessage()).build();
+    }
+  }
+
+  @POST
+  @Path("enable/{packageName}")
+  public Response enablePackage(@PathParam("packageName") String packageName,
+                                String artifact) {
+    try {
+      helium.enable(packageName, artifact);
+      return new JsonResponse(Response.Status.OK).build();
+    } catch (IOException e) {
+      logger.error(e.getMessage(), e);
+      return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()).build();
+    } catch (TaskRunnerException e) {
+      logger.error(e.getMessage(), e);
+      return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()).build();
+    }
+  }
+
+  @POST
+  @Path("disable/{packageName}")
+  public Response enablePackage(@PathParam("packageName") String packageName) {
+    try {
+      helium.disable(packageName);
+      return new JsonResponse(Response.Status.OK).build();
+    } catch (IOException e) {
+      logger.error(e.getMessage(), e);
+      return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()).build();
+    } catch (TaskRunnerException e) {
+      logger.error(e.getMessage(), e);
+      return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()).build();
+    }
+  }
+
+  @GET
+  @Path("visualizationOrder")
+  public Response getVisualizationPackageOrder() {
+    List<String> order = helium.getVisualizationPackageOrder();
+    return new JsonResponse(Response.Status.OK, order).build();
+  }
+
+  @POST
+  @Path("visualizationOrder")
+  public Response setVisualizationPackageOrder(String orderedPackageNameList) {
+    List<String> orderedList = gson.fromJson(
+        orderedPackageNameList, new TypeToken<List<String>>(){}.getType());
+
+    try {
+      helium.setVisualizationPackageOrder(orderedList);
+    } catch (IOException | TaskRunnerException e) {
+      logger.error(e.getMessage(), e);
+      return new JsonResponse(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()).build();
+    }
+    return new JsonResponse(Response.Status.OK).build();
+  }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
index 342d5f9..b173d04 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
@@ -35,6 +35,7 @@ import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
 import org.apache.zeppelin.dep.DependencyResolver;
 import org.apache.zeppelin.helium.Helium;
 import org.apache.zeppelin.helium.HeliumApplicationFactory;
+import org.apache.zeppelin.helium.HeliumVisualizationFactory;
 import org.apache.zeppelin.interpreter.InterpreterFactory;
 import org.apache.zeppelin.notebook.Notebook;
 import org.apache.zeppelin.notebook.NotebookAuthorization;
@@ -82,7 +83,6 @@ public class ZeppelinServer extends Application {
   public static Server jettyWebServer;
   public static NotebookServer notebookWsServer;
   public static Helium helium;
-  public static HeliumApplicationFactory heliumApplicationFactory;
 
   private SchedulerFactory schedulerFactory;
   private InterpreterFactory replFactory;
@@ -98,8 +98,39 @@ public class ZeppelinServer extends Application {
     this.depResolver = new DependencyResolver(
         conf.getString(ConfVars.ZEPPELIN_INTERPRETER_LOCALREPO));
 
-    this.helium = new Helium(conf.getHeliumConfPath(), conf.getHeliumDefaultLocalRegistryPath());
-    this.heliumApplicationFactory = new HeliumApplicationFactory();
+    HeliumApplicationFactory heliumApplicationFactory = new HeliumApplicationFactory();
+    HeliumVisualizationFactory heliumVisualizationFactory;
+
+    if (isBinaryPackage(conf)) {
+      /* In binary package, zeppelin-web/src/app/visualization and zeppelin-web/src/app/tabledata
+       * are copied to lib/node_modules/zeppelin-vis, lib/node_modules/zeppelin-tabledata directory.
+       * Check zeppelin/zeppelin-distribution/src/assemble/distribution.xml to see how they're
+       * packaged into binary package.
+       */
+      heliumVisualizationFactory = new HeliumVisualizationFactory(
+          new File(conf.getRelativeDir(ConfVars.ZEPPELIN_DEP_LOCALREPO)),
+          new File(conf.getRelativeDir("lib/node_modules/zeppelin-tabledata")),
+          new File(conf.getRelativeDir("lib/node_modules/zeppelin-vis")));
+    } else {
+      heliumVisualizationFactory = new HeliumVisualizationFactory(
+          new File(conf.getRelativeDir(ConfVars.ZEPPELIN_DEP_LOCALREPO)),
+          new File(conf.getRelativeDir("zeppelin-web/src/app/tabledata")),
+          new File(conf.getRelativeDir("zeppelin-web/src/app/visualization")));
+    }
+
+    this.helium = new Helium(
+        conf.getHeliumConfPath(),
+        conf.getHeliumDefaultLocalRegistryPath(),
+        heliumVisualizationFactory,
+        heliumApplicationFactory);
+
+    // create visualization bundle
+    try {
+      heliumVisualizationFactory.bundle(helium.getVisualizationPackagesToBundle());
+    } catch (Exception e) {
+      LOG.error(e.getMessage(), e);
+    }
+
     this.schedulerFactory = new SchedulerFactory();
     this.replFactory = new InterpreterFactory(conf, notebookWsServer,
         notebookWsServer, heliumApplicationFactory, depResolver, SecurityUtils.isAuthenticated());
@@ -333,7 +364,7 @@ public class ZeppelinServer extends Application {
     NotebookRepoRestApi notebookRepoApi = new NotebookRepoRestApi(notebookRepo, notebookWsServer);
     singletons.add(notebookRepoApi);
 
-    HeliumRestApi heliumApi = new HeliumRestApi(helium, heliumApplicationFactory, notebook);
+    HeliumRestApi heliumApi = new HeliumRestApi(helium, notebook);
     singletons.add(heliumApi);
 
     InterpreterRestApi interpreterApi = new InterpreterRestApi(replFactory);
@@ -353,5 +384,13 @@ public class ZeppelinServer extends Application {
 
     return singletons;
   }
+
+  /**
+   * Check if it is source build or binary package
+   * @return
+   */
+  private static boolean isBinaryPackage(ZeppelinConfiguration conf) {
+    return !new File(conf.getRelativeDir("zeppelin-web")).isDirectory();
+  }
 }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-web/.eslintrc
----------------------------------------------------------------------
diff --git a/zeppelin-web/.eslintrc b/zeppelin-web/.eslintrc
index ca8cd07..b4d3909 100644
--- a/zeppelin-web/.eslintrc
+++ b/zeppelin-web/.eslintrc
@@ -26,7 +26,8 @@
     "BootstrapDialog": false,
     "Handsontable": false,
     "moment": false,
-    "zeppelin" : false
+    "zeppelin" : false,
+    "process": false
   },
   "rules": {
     "no-bitwise": 2,

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-web/package.json
----------------------------------------------------------------------
diff --git a/zeppelin-web/package.json b/zeppelin-web/package.json
index 49bc046..e25f0c8 100644
--- a/zeppelin-web/package.json
+++ b/zeppelin-web/package.json
@@ -12,8 +12,10 @@
     "build": "grunt pre-webpack-dist && webpack && grunt post-webpack-dist",
     "predev": "grunt pre-webpack-dev",
     "dev:server": "webpack-dev-server --hot",
+    "visdev:server": "HELIUM_VIS_DEV=true webpack-dev-server --hot",
     "dev:watch": "grunt watch-webpack-dev",
     "dev": "npm-run-all --parallel dev:server dev:watch",
+    "visdev": "npm-run-all --parallel visdev:server dev:watch",
     "pretest": "npm install karma-phantomjs-launcher",
     "test": "karma start test/karma.conf.js"
   },

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-web/pom.xml
----------------------------------------------------------------------
diff --git a/zeppelin-web/pom.xml b/zeppelin-web/pom.xml
index fc13e53..f5fd73e 100644
--- a/zeppelin-web/pom.xml
+++ b/zeppelin-web/pom.xml
@@ -92,7 +92,8 @@
             <exclude>src/fonts/source-code-pro*</exclude>
             <exclude>src/fonts/google-fonts.css</exclude>
             <exclude>bower.json</exclude>
-            <exclude>package.json</exclude>
+            <exclude>**/package.json</exclude>
+            <exclude>**/.npmignore</exclude>
             <exclude>yarn.lock</exclude>
             <exclude>*.md</exclude>
           </excludes>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-web/src/app/app.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/app.js b/zeppelin-web/src/app/app.js
index fcfed28..f68b3da 100644
--- a/zeppelin-web/src/app/app.js
+++ b/zeppelin-web/src/app/app.js
@@ -46,25 +46,35 @@ var zeppelinWebApp = angular.module('zeppelinWebApp', [
     // withCredentials when running locally via grunt
     $httpProvider.defaults.withCredentials = true;
 
+    var visBundleLoad = {
+      load: ['heliumService', function(heliumService) {
+        return heliumService.load;
+      }]
+    };
+
     $routeProvider
       .when('/', {
         templateUrl: 'app/home/home.html'
       })
       .when('/notebook/:noteId', {
         templateUrl: 'app/notebook/notebook.html',
-        controller: 'NotebookCtrl'
+        controller: 'NotebookCtrl',
+        resolve: visBundleLoad
       })
       .when('/notebook/:noteId/paragraph?=:paragraphId', {
         templateUrl: 'app/notebook/notebook.html',
-        controller: 'NotebookCtrl'
+        controller: 'NotebookCtrl',
+        resolve: visBundleLoad
       })
       .when('/notebook/:noteId/paragraph/:paragraphId?', {
         templateUrl: 'app/notebook/notebook.html',
-        controller: 'NotebookCtrl'
+        controller: 'NotebookCtrl',
+        resolve: visBundleLoad
       })
       .when('/notebook/:noteId/revision/:revisionId', {
         templateUrl: 'app/notebook/notebook.html',
-        controller: 'NotebookCtrl'
+        controller: 'NotebookCtrl',
+        resolve: visBundleLoad
       })
       .when('/jobmanager', {
         templateUrl: 'app/jobmanager/jobmanager.html',
@@ -83,6 +93,10 @@ var zeppelinWebApp = angular.module('zeppelinWebApp', [
         templateUrl: 'app/credential/credential.html',
         controller: 'CredentialCtrl'
       })
+      .when('/helium', {
+        templateUrl: 'app/helium/helium.html',
+        controller: 'HeliumCtrl'
+      })    
       .when('/configuration', {
         templateUrl: 'app/configuration/configuration.html',
         controller: 'ConfigurationCtrl'

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-web/src/app/helium/helium.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/helium/helium.controller.js b/zeppelin-web/src/app/helium/helium.controller.js
new file mode 100644
index 0000000..a344e80
--- /dev/null
+++ b/zeppelin-web/src/app/helium/helium.controller.js
@@ -0,0 +1,219 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     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.
+ */
+
+(function() {
+
+  angular.module('zeppelinWebApp').controller('HeliumCtrl', HeliumCtrl);
+
+  HeliumCtrl.$inject = ['$scope', '$rootScope', '$sce', 'baseUrlSrv', 'ngToast', 'heliumService'];
+
+  function HeliumCtrl($scope, $rootScope, $sce, baseUrlSrv, ngToast, heliumService) {
+    $scope.packageInfos = {};
+    $scope.defaultVersions = {};
+    $scope.showVersions = {};
+    $scope.visualizationOrder = [];
+    $scope.visualizationOrderChanged = false;
+
+    var buildDefaultVersionListToDisplay = function(packageInfos) {
+      var defaultVersions = {};
+      // show enabled version if any version of package is enabled
+      for (var name in packageInfos) {
+        var pkgs = packageInfos[name];
+        for (var pkgIdx in pkgs) {
+          var pkg = pkgs[pkgIdx];
+          pkg.pkg.icon = $sce.trustAsHtml(pkg.pkg.icon);
+          if (pkg.enabled) {
+            defaultVersions[name] = pkg;
+            pkgs.splice(pkgIdx, 1);
+            break;
+          }
+        }
+
+        // show first available version if package is not enabled
+        if (!defaultVersions[name]) {
+          defaultVersions[name] = pkgs[0];
+          pkgs.splice(0, 1);
+        }
+      }
+      $scope.defaultVersions = defaultVersions;
+    };
+
+    var getAllPackageInfo = function() {
+      heliumService.getAllPackageInfo().
+        success(function(data, status) {
+          $scope.packageInfos = data.body;
+          buildDefaultVersionListToDisplay($scope.packageInfos);
+        }).
+        error(function(data, status) {
+          console.log('Can not load package info %o %o', status, data);
+        });
+    };
+
+    var getVisualizationOrder = function() {
+      heliumService.getVisualizationOrder().
+        success(function(data, status) {
+          $scope.visualizationOrder = data.body;
+        }).
+        error(function(data, status) {
+          console.log('Can not get visualization order %o %o', status, data);
+        });
+    };
+
+    $scope.visualizationOrderListeners = {
+      accept: function(sourceItemHandleScope, destSortableScope) {return true;},
+      itemMoved: function(event) {},
+      orderChanged: function(event) {
+        $scope.visualizationOrderChanged = true;
+      }
+    };
+
+    var init = function() {
+      getAllPackageInfo();
+      getVisualizationOrder();
+      $scope.visualizationOrderChanged = false;
+    };
+
+    init();
+
+    $scope.saveVisualizationOrder = function() {
+      var confirm = BootstrapDialog.confirm({
+        closable: false,
+        closeByBackdrop: false,
+        closeByKeyboard: false,
+        title: '',
+        message: 'Save changes?',
+        callback: function(result) {
+          if (result) {
+            confirm.$modalFooter.find('button').addClass('disabled');
+            confirm.$modalFooter.find('button:contains("OK")')
+              .html('<i class="fa fa-circle-o-notch fa-spin"></i> Enabling');
+            heliumService.setVisualizationOrder($scope.visualizationOrder).
+              success(function(data, status) {
+                init();
+                confirm.close();
+              }).
+              error(function(data, status) {
+                confirm.close();
+                console.log('Failed to save order');
+                BootstrapDialog.show({
+                  title: 'Error on saving order ',
+                  message: data.message
+                });
+              });
+            return false;
+          }
+        }
+      });
+    }
+
+    var getLicense = function(name, artifact) {
+      var pkg = _.filter($scope.defaultVersions[name], function(p) {
+        return p.artifact === artifact;
+      });
+
+      var license;
+      if (pkg.length === 0) {
+        pkg = _.filter($scope.packageInfos[name], function(p) {
+          return p.pkg.artifact === artifact;
+        });
+
+        if (pkg.length > 0) {
+          license  = pkg[0].pkg.license;
+        }
+      } else {
+        license = pkg[0].license;
+      }
+
+      if (!license) {
+        license = 'Unknown';
+      }
+      return license;
+    }
+
+    $scope.enable = function(name, artifact) {
+      var license = getLicense(name, artifact);
+
+      var confirm = BootstrapDialog.confirm({
+        closable: false,
+        closeByBackdrop: false,
+        closeByKeyboard: false,
+        title: '',
+        message: 'Do you want to enable ' + name + '?' +
+          '<div style="color:gray">' + artifact + '</div>' +
+          '<div style="border-top: 1px solid #efefef; margin-top: 10px; padding-top: 5px;">License</div>' +
+          '<div style="color:gray">' + license + '</div>',
+        callback: function(result) {
+          if (result) {
+            confirm.$modalFooter.find('button').addClass('disabled');
+            confirm.$modalFooter.find('button:contains("OK")')
+              .html('<i class="fa fa-circle-o-notch fa-spin"></i> Enabling');
+            heliumService.enable(name, artifact).
+              success(function(data, status) {
+                init();
+                confirm.close();
+              }).
+              error(function(data, status) {
+                confirm.close();
+                console.log('Failed to enable package %o %o. %o', name, artifact, data);
+                BootstrapDialog.show({
+                  title: 'Error on enabling ' + name,
+                  message: data.message
+                });
+              });
+            return false;
+          }
+        }
+      });
+    };
+
+    $scope.disable = function(name) {
+      var confirm = BootstrapDialog.confirm({
+        closable: false,
+        closeByBackdrop: false,
+        closeByKeyboard: false,
+        title: '',
+        message: 'Do you want to disable ' + name + '?',
+        callback: function(result) {
+          if (result) {
+            confirm.$modalFooter.find('button').addClass('disabled');
+            confirm.$modalFooter.find('button:contains("OK")')
+              .html('<i class="fa fa-circle-o-notch fa-spin"></i> Disabling');
+            heliumService.disable(name).
+              success(function(data, status) {
+                init();
+                confirm.close();
+              }).
+              error(function(data, status) {
+                confirm.close();
+                console.log('Failed to disable package %o. %o', name, data);
+                BootstrapDialog.show({
+                  title: 'Error on disabling ' + name,
+                  message: data.message
+                });
+              });
+            return false;
+          }
+        }
+      });
+    };
+
+    $scope.toggleVersions = function(pkgName) {
+      if ($scope.showVersions[pkgName]) {
+        $scope.showVersions[pkgName] = false;
+      } else {
+        $scope.showVersions[pkgName] = true;
+      }
+    };
+  }
+})();

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-web/src/app/helium/helium.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/helium/helium.css b/zeppelin-web/src/app/helium/helium.css
new file mode 100644
index 0000000..f17d6bd
--- /dev/null
+++ b/zeppelin-web/src/app/helium/helium.css
@@ -0,0 +1,107 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     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.
+ */
+
+.heliumPackageContainer {
+  padding-bottom: 0px;
+  margin-bottom: 0px;
+}
+
+.heliumPackageList {
+  min-height: 25px;
+  margin-bottom: 15px;
+  border-bottom: 1px solid #EFEFEF;
+  padding-bottom: 13px;
+}
+
+.heliumPackageList:last-child {
+  border-bottom: none;
+}
+
+.heliumPackageList .heliumPackageHead {
+  height: 30px;
+}
+
+.heliumPackageList .heliumPackageHead .btn {
+  margin-top: 5px;
+}
+
+.heliumPackageList .heliumPackageIcon {
+  float: left;
+  width: 28px;
+  height: 22px;
+  padding: 5px 2px 0px 2px;
+}
+
+.heliumPackageList .heliumPackageName {
+  font-size: 20px;
+  font-weight: bold;
+  color: #3071a9;
+  float: left;
+  margin-top: 0;
+}
+
+.heliumPackageList .heliumPackageName span {
+  font-size: 10px;
+  color: #AAAAAA;
+}
+
+
+.heliumPackageList .heliumPackageDisabledArtifact {
+  color:gray;
+}
+
+.heliumPackageList .heliumPackageEnabledArtifact {
+  color:#444444;
+}
+
+.heliumPackageList .heliumPackageEnabledArtifact span,
+.heliumPackageList .heliumPackageDisabledArtifact span {
+  margin-left:3px;
+  cursor:pointer;
+  text-decoration:
+  underline;color:#3071a9
+}
+
+.heliumPackageList .heliumPackageDescription {
+  margin-top: 10px;
+}
+
+.heliumVisualizationOrder {
+  display: inline-block;
+}
+
+.heliumVisualizationOrder .as-sortable-item,
+.heliumVisualizationOrder .as-sortable-placeholder {
+  display: inline-block;
+  float: left;
+}
+
+.sortable-row .as-sortable-item-handle {
+  width: 35px;
+  height: 30px;
+}
+
+.sortable-row .as-sortable-item svg {
+  width: 100%;
+  height: 100%;
+}
+
+.heliumVisualizationOrder .saveLink {
+  margin-left:10px;
+  margin-top:5px;
+  cursor:pointer;
+  text-decoration:
+  underline;color:#3071a9;
+  display: inline-block;
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-web/src/app/helium/helium.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/helium/helium.html b/zeppelin-web/src/app/helium/helium.html
new file mode 100644
index 0000000..546995c
--- /dev/null
+++ b/zeppelin-web/src/app/helium/helium.html
@@ -0,0 +1,86 @@
+<!--
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    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.
+-->
+<div class="interpreterHead">
+  <div class="header">
+    <div class="row">
+      <div class="col-md-12">
+        <h3 class="new_h3">
+          Helium
+        </h3>
+      </div>
+    </div>
+    <div ng-show="visualizationOrder.length > 1"
+         class="row heliumVisualizationOrder">
+      <div style="margin:0 0 5px 15px">Visualization package display order (drag and drop to reorder)</div>
+      <div class="col-md-12 sortable-row btn-group"
+           as-sortable="visualizationOrderListeners"
+           data-ng-model="visualizationOrder">
+        <div class="btn-group" data-ng-repeat="pkgName in visualizationOrder"
+             as-sortable-item>
+          <div class="btn btn-default btn-sm"
+               ng-bind-html='defaultVersions[pkgName].pkg.icon'
+               as-sortable-item-handle>
+          </div>
+        </div>
+        <span class="saveLink"
+           ng-show="visualizationOrderChanged"
+           ng-click="saveVisualizationOrder()">
+          save
+        </span>
+      </div>
+    </div>
+  </div>
+</div>
+
+<div class="box width-full heliumPackageContainer">
+  <div class="row heliumPackageList"
+       ng-repeat="(pkgName, pkgInfo) in defaultVersions">
+    <div class="col-md-12">
+      <div class="heliumPackageHead">
+        <div class="heliumPackageIcon"
+             ng-bind-html=pkgInfo.pkg.icon></div>
+        <div class="heliumPackageName">{{pkgName}} <span>{{pkgInfo.pkg.type}}</span></div>
+        <div ng-show="!pkgInfo.enabled"
+             ng-click="enable(pkgName, pkgInfo.pkg.artifact)"
+             class="btn btn-success btn-xs"
+             style="float:right">Enable</div>
+        <div ng-show="pkgInfo.enabled"
+             ng-click="disable(pkgName)"
+             class="btn btn-info btn-xs"
+             style="float:right">Disable</div>
+      </div>
+      <div ng-class="{heliumPackageDisabledArtifact: !pkgInfo.enabled, heliumPackageEnabledArtifact: pkgInfo.enabled}">
+        {{pkgInfo.pkg.artifact}}
+        <span ng-show="packageInfos[pkgName].length > 0"
+              ng-click="toggleVersions(pkgName)">
+          versions
+        </span>
+      </div>
+      <ul class="heliumPackageVersions"
+           ng-show="showVersions[pkgName]">
+        <li class="heliumPackageDisabledArtifact"
+             ng-repeat="pkg in packageInfos[pkgName]">
+          {{pkg.pkg.artifact}} -
+          <span ng-click="enable(pkgName, pkg.pkg.artifact)"
+                style="margin-left:3px;cursor:pointer;text-decoration: underline;color:#3071a9">
+            enable
+          </span>
+        </li>
+      </ul>
+      <div class="heliumPackageDescription">
+        {{pkgInfo.pkg.description}}
+      </div>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-web/src/app/notebook/paragraph/result/result-chart-selector.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/result/result-chart-selector.html b/zeppelin-web/src/app/notebook/paragraph/result/result-chart-selector.html
index b48e0d7..0d2afb7 100644
--- a/zeppelin-web/src/app/notebook/paragraph/result/result-chart-selector.html
+++ b/zeppelin-web/src/app/notebook/paragraph/result/result-chart-selector.html
@@ -14,13 +14,14 @@ limitations under the License.
 
 <div id="{{id}}_switch"
      ng-if="(type == 'TABLE' || apps.length > 0 || suggestion.available && suggestion.available.length > 0) && !asIframe && !viewOnly"
-     class="chart-selector">
+     class="result-chart-selector">
 
   <div ng-if="type == 'TABLE'" class="btn-group">
     <button type="button" class="btn btn-default btn-sm"
             ng-repeat="viz in builtInTableDataVisualizationList track by $index"
             ng-class="{'active' : viz.id == graphMode && !config.helium.activeApp}"
-            ng-click="switchViz(viz.id)"><i ng-class="viz.icon"></i>
+            ng-click="switchViz(viz.id)"
+            ng-bind-html="viz.icon">
     </button>
   </div>
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/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 e793199..6d56fe4 100644
--- a/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js
+++ b/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js
@@ -34,16 +34,18 @@ ResultCtrl.$inject = [
   '$http',
   '$q',
   '$templateRequest',
+  '$sce',
   'websocketMsgSrv',
   'baseUrlSrv',
   'ngToast',
   'saveAsService',
-  'noteVarShareService'
+  'noteVarShareService',
+  'heliumService'
 ];
 
 function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location,
-                    $timeout, $compile, $http, $q, $templateRequest, websocketMsgSrv,
-                    baseUrlSrv, ngToast, saveAsService, noteVarShareService) {
+                    $timeout, $compile, $http, $q, $templateRequest, $sce, websocketMsgSrv,
+                    baseUrlSrv, ngToast, saveAsService, noteVarShareService, heliumService) {
 
   /**
    * Built-in visualizations
@@ -52,36 +54,36 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
     {
       id: 'table',   // paragraph.config.graph.mode
       name: 'Table', // human readable name. tooltip
-      icon: 'fa fa-table'
+      icon: '<i class="fa fa-table"></i>'
     },
     {
       id: 'multiBarChart',
       name: 'Bar Chart',
-      icon: 'fa fa-bar-chart',
+      icon: '<i class="fa fa-bar-chart"></i>',
       transformation: 'pivot'
     },
     {
       id: 'pieChart',
       name: 'Pie Chart',
-      icon: 'fa fa-pie-chart',
+      icon: '<i class="fa fa-pie-chart"></i>',
       transformation: 'pivot'
     },
     {
       id: 'stackedAreaChart',
       name: 'Area Chart',
-      icon: 'fa fa-area-chart',
+      icon: '<i class="fa fa-area-chart"></i>',
       transformation: 'pivot'
     },
     {
       id: 'lineChart',
       name: 'Line Chart',
-      icon: 'fa fa-line-chart',
+      icon: '<i class="fa fa-line-chart"></i>',
       transformation: 'pivot'
     },
     {
       id: 'scatterChart',
       name: 'Scatter Chart',
-      icon: 'cf cf-scatter-chart'
+      icon: '<i class="cf cf-scatter-chart"></i>'
     }
   ];
 
@@ -150,6 +152,21 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
 
   $scope.init = function(result, config, paragraph, index) {
     console.log('result controller init %o %o %o', result, config, index);
+
+    // register helium plugin vis
+    var heliumVis = heliumService.get();
+    console.log('Helium visualizations %o', heliumVis);
+    heliumVis.forEach(function(vis) {
+      $scope.builtInTableDataVisualizationList.push({
+        id: vis.id,
+        name: vis.name,
+        icon: $sce.trustAsHtml(vis.icon)
+      });
+      builtInVisualizations[vis.id] = {
+        class: vis.class
+      };
+    });
+    
     updateData(result, config, paragraph, index);
     renderResult($scope.type);
   };
@@ -421,7 +438,7 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
                 builtInViz.instance.resize();
               });
             } catch (err) {
-              console.log('Graph drawing error %o', err);
+              console.error('Graph drawing error %o', err);
             }
           } else {
             $timeout(retryRenderer, 10);

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-web/src/app/notebook/paragraph/result/result.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/result/result.css b/zeppelin-web/src/app/notebook/paragraph/result/result.css
index 91338ba..905b88c 100644
--- a/zeppelin-web/src/app/notebook/paragraph/result/result.css
+++ b/zeppelin-web/src/app/notebook/paragraph/result/result.css
@@ -12,9 +12,31 @@
  * limitations under the License.
  */
 
-.chart-selector {
+.result-chart-selector {
     margin-bottom: 10px; 
     position: relative; 
     display: inline-block; 
     vertical-align: middle;
 }
+
+.result-chart-selector button {
+  width: 35px;
+  height: 30px;
+}
+
+.result-chart-selector button svg {
+  width: 100%;
+  height: 100%;
+}
+
+.rotate90 {
+  -webkit-transform: rotate(90deg);
+  -moz-transform: rotate(90deg);
+  -ms-transform: rotate(90deg);
+}
+
+.rotate90flipX {
+  -webkit-transform: rotate(90deg) scaleX(-1);
+  -moz-transform: rotate(90deg) scaleX(-1);
+  -ms-transform: rotate(90deg) scaleX(-1);
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-web/src/app/tabledata/.npmignore
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/tabledata/.npmignore b/zeppelin-web/src/app/tabledata/.npmignore
new file mode 100644
index 0000000..0b84df0
--- /dev/null
+++ b/zeppelin-web/src/app/tabledata/.npmignore
@@ -0,0 +1 @@
+*.html
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-web/src/app/tabledata/package.json
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/tabledata/package.json b/zeppelin-web/src/app/tabledata/package.json
new file mode 100644
index 0000000..e92abb2
--- /dev/null
+++ b/zeppelin-web/src/app/tabledata/package.json
@@ -0,0 +1,15 @@
+{
+  "name": "zeppelin-tabledata",
+  "description": "tabledata api",
+  "version": "0.7.0-SNAPSHOT",
+  "main": "tabledata",
+  "dependencies": {
+    "json3": "~3.3.1",
+    "lodash": "~3.9.3"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/apache/zeppelin"
+  },
+  "license": "Apache-2.0"
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/300f7532/zeppelin-web/src/app/visualization/.npmignore
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/visualization/.npmignore b/zeppelin-web/src/app/visualization/.npmignore
new file mode 100644
index 0000000..2d19fc7
--- /dev/null
+++ b/zeppelin-web/src/app/visualization/.npmignore
@@ -0,0 +1 @@
+*.html