You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by al...@apache.org on 2015/04/17 21:10:30 UTC

[1/5] ambari git commit: AMBARI-10528. Hive View: Visual Explain, Error handling and bugfixes (alexantonenko)

Repository: ambari
Updated Branches:
  refs/heads/trunk 2917d0ded -> 672eee34d


http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/view.xml
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/view.xml b/contrib/views/hive/src/main/resources/view.xml
index ac97d58..9c8b103 100644
--- a/contrib/views/hive/src/main/resources/view.xml
+++ b/contrib/views/hive/src/main/resources/view.xml
@@ -17,10 +17,12 @@
 <view>
     <name>HIVE</name>
     <label>Hive</label>
-    <version>0.1.0</version>
+    <version>0.2.0</version>
 
     <min-ambari-version>1.7.*</min-ambari-version>
 
+    <validator-class>org.apache.ambari.view.hive.PropertyValidator</validator-class>
+
     <!-- HDFS Configs -->
     <parameter>
         <name>webhdfs.url</name>
@@ -159,10 +161,6 @@
 
     <persistence>
         <entity>
-            <class>org.apache.ambari.view.hive.persistence.DataStoreStorage$SmokeTestEntity</class>
-            <id-property>id</id-property>
-        </entity>
-        <entity>
             <class>org.apache.ambari.view.hive.resources.jobs.viewJobs.JobImpl</class>
             <id-property>id</id-property>
         </entity>

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/ATSParserTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/ATSParserTest.java b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/ATSParserTest.java
index d2ff02c..c5cc52b 100644
--- a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/ATSParserTest.java
+++ b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/ATSParserTest.java
@@ -70,6 +70,26 @@ public class ATSParserTest {
   protected static class ATSRequestsDelegateStub implements ATSRequestsDelegate {
 
     @Override
+    public String hiveQueryIdDirectUrl(String entity) {
+      return null;
+    }
+
+    @Override
+    public String hiveQueryIdOperationIdUrl(String operationId) {
+      return null;
+    }
+
+    @Override
+    public String tezDagDirectUrl(String entity) {
+      return null;
+    }
+
+    @Override
+    public String tezDagNameUrl(String name) {
+      return null;
+    }
+
+    @Override
     public JSONObject hiveQueryIdList(String username) {
       return (JSONObject) JSONValue.parse(
           "{ \"entities\" : [ { \"domain\" : \"DEFAULT\",\n" +
@@ -409,4 +429,4 @@ public class ATSParserTest {
       );
     }
   }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/AggregatorTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/AggregatorTest.java b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/AggregatorTest.java
index 50642ac..10d052a 100644
--- a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/AggregatorTest.java
+++ b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/AggregatorTest.java
@@ -393,4 +393,4 @@ public class AggregatorTest {
       this.hiveQueryIds = hiveQueryIds;
     }
   }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index c703bcf..eb9b587 100644
--- a/pom.xml
+++ b/pom.xml
@@ -300,6 +300,7 @@
             <exclude>contrib/ambari-scom/metrics-sink/target/**</exclude>
             <exclude>contrib/views/*/target/**</exclude>
             <exclude>contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/**</exclude>
+            <exclude>contrib/views/hive/src/main/resources/ui/hive-web/.bowerrc</exclude>
             <exclude>contrib/views/files/src/main/resources/ui/bower_components/**</exclude>
             <exclude>contrib/views/files/src/main/resources/ui/node/**</exclude>
             <exclude>contrib/views/files/src/main/resources/ui/node_modules/**</exclude>


[3/5] ambari git commit: AMBARI-10528. Hive View: Visual Explain, Error handling and bugfixes (alexantonenko)

Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/alert-message-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/alert-message-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/alert-message-widget.hbs
index 7106f0a..0901b30 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/alert-message-widget.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/alert-message-widget.hbs
@@ -18,11 +18,11 @@
 
 <div {{bind-attr class=":alert :alert-dismissible message.typeClass"}}>
   <button type="button" class="close" data-dismiss="alert" aria-hidden="true" {{action "remove"}}>&times;</button>
-  <strong>{{tb-helper message.title}}</strong>
+  <strong {{action 'toggleMessage'}}>{{tb-helper message.title}}</strong>
 
   {{#if message.isExpanded}}
     <div class="alert-message">
       {{message.content}}
     </div>
   {{/if}}
-</div>
\ No newline at end of file
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/collapsible-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/collapsible-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/collapsible-widget.hbs
index f6af59b..230ec3b 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/collapsible-widget.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/collapsible-widget.hbs
@@ -18,7 +18,12 @@
 
 <div>
   <a {{action "toggle"}} {{bind-attr class=":fa iconClass"}}> {{heading}}</a>
+  <div class="pull-right">
+    {{#each control in controls}}
+      <a {{action 'sendControlAction' control.action}} {{bind-attr class=":fa control.icon" title="control.tooltip"}}></a>
+    {{/each}}
+  </div>
 </div>
 {{#if isExpanded}}
   {{yield}}
-{{/if}}
\ No newline at end of file
+{{/if}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/notify-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/notify-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/notify-widget.hbs
new file mode 100644
index 0000000..5f905a8
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/notify-widget.hbs
@@ -0,0 +1,21 @@
+{{!
+* 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.
+}}
+
+{{#each notification in notifications}}
+  {{view "notification" notification=notification}}
+{{/each}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/panel-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/panel-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/panel-widget.hbs
index 45c4edf..1e29033 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/panel-widget.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/panel-widget.hbs
@@ -16,7 +16,7 @@
 * limitations under the License.
 }}
 
-<div class="panel panel-default" {{bind-attr class=classNames}}>
+<div {{bind-attr class=":panel :panel-default classNames"}}>
   {{#if heading}}
     <div class="panel-heading">
       {{#if menuItems}}
@@ -34,6 +34,14 @@
               </ul>
           </div>
       {{/if}}
+
+      {{#if iconActions}}
+        {{#each iconAction in iconActions}}
+        <i {{action "sendMenuItemAction" iconAction.action}}
+          {{bind-attr class=":pull-right :panel-action-icon :fa iconAction.icon"}}></i>
+        {{/each}}
+      {{/if}}
+
       <strong>{{heading}}</strong>
       {{#if isLoading}}
         <div class="spinner small pull-right"></div>
@@ -43,4 +51,4 @@
   <div class="panel-body">
     {{yield}}
   </div>
-</div>
\ No newline at end of file
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/query-tabs.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/query-tabs.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/query-tabs.hbs
new file mode 100644
index 0000000..f131367
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/query-tabs.hbs
@@ -0,0 +1,29 @@
+{{!
+* 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.
+}}
+
+{{#each tab in tabs}}
+    <span {{action tab.action tab}} {{bind-attr class=":query-menu-tab tabClassNames tab.iconClass tab.active:active"}}>
+      {{#if tab.badge}}
+        <span class="badge">{{tab.badge}}</span>
+      {{/if}}
+
+      {{#if tab.text}}
+        {{tab.text}}
+      {{/if}}
+    </span>
+{{/each}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tabs-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tabs-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tabs-widget.hbs
index e5f313a..5f9c18c 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tabs-widget.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tabs-widget.hbs
@@ -23,6 +23,7 @@
         {{#link-to tab.path tab.id tagName="li"}}
           <a>
             {{tab.name}}
+            {{#if tab.isDirty}}*{{/if}}
             {{#if view.removeEnabled}}
               <i class="fa fa-remove" {{action 'remove' tab}}></i>
             {{/if}}
@@ -37,4 +38,4 @@
   {{/each}}
 </ul>
 
-{{yield}}
\ No newline at end of file
+{{yield}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-search-results.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-search-results.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-search-results.hbs
index 354d7bd..007d9ca 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-search-results.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-search-results.hbs
@@ -44,5 +44,7 @@
     </div>
   </div>
 {{else}}
-  <h4>{{t "labels.noTablesMatches"}} "{{tablesSearchTerm}}"</h4>
+  <div class="alert alert-warning database-explorer-alert" role="alert">
+    {{t "labels.noTablesMatch"}} <strong>&quot;{{tablesSearchTerm}}&quot;</strong>
+  </div>
 {{/if}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-tree.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-tree.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-tree.hbs
index 5da9760..bdac484 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-tree.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-tree.hbs
@@ -18,11 +18,11 @@
 
 <div class="databases">
   {{#each database in content}}
-    {{#collapsible-widget heading=database.name isExpanded=database.isExpanded iconClass="fa-database" expanded="getTables"}}
+    {{#collapsible-widget heading=database.name isExpanded=database.isExpanded iconClass="fa-database" expanded="getTables" toggledParam=database}}
       {{#if database.isExpanded}}
         <div class="tables">
           {{#each table in database.visibleTables}}
-            {{#collapsible-widget heading=table.name isExpanded=table.isExpanded toggledParam=database iconClass="fa-th" expanded="getColumns"}}
+            {{#collapsible-widget heading=table.name isExpanded=table.isExpanded toggledParam=database iconClass="fa-table" expanded="getColumns" controls=tableControls}}
               {{#if table.isExpanded}}
                 <div class="columns">
                   {{#each column in table.visibleColumns}}
@@ -45,4 +45,4 @@
       {{/if}}
     {{/collapsible-widget}}
   {{/each}}
-</div>
\ No newline at end of file
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases.hbs
index a0ce19f..392b8f3 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases.hbs
@@ -16,7 +16,7 @@
 * limitations under the License.
 }}
 
-{{#panel-widget headingTranslation="titles.database" isLoading=isLoading classNames="database-explorer"}}
+{{#panel-widget headingTranslation="titles.database" isLoading=isLoading classNames="database-explorer" iconActions=panelIconActions}}
   {{#if model}}
 
     {{typeahead-widget
@@ -51,4 +51,4 @@
       {{partial selectedTab.view}}
     {{/tabs-widget}}
   {{/if}}
-{{/panel-widget}}
\ No newline at end of file
+{{/panel-widget}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index.hbs
index 8621baa..1551072 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index.hbs
@@ -23,22 +23,22 @@
     </aside>
 
     <div class="col-md-9 col-xs-12 query-container">
-      {{render 'alerts'}}
 
       {{#panel-widget headingTranslation="titles.query.editor" classNames="query-editor-panel"}}
         {{render 'open-queries'}}
 
         <div class="toolbox">
-          <button type="button" class="btn btn-sm btn-success execute-query"
-                  {{bind-attr class="canExecute::disabled"}}
+          <button type="button"
+                  {{bind-attr class=":btn :btn-sm :btn-success :execute-query canExecute::disabled"}}
                   {{action "executeQuery"}}>
             {{t "buttons.execute"}}
           </button>
-          <button type="button" class="btn btn-sm btn-default"
-                  {{bind-attr class="canExecute::disabled"}}
+          <button type="button"
+                  {{bind-attr class=":btn :btn-sm :btn-default canExecute::disabled"}}
                   {{action "explainQuery"}}>
             {{t "buttons.explain"}}
           </button>
+
           <button type="button" class="btn btn-sm btn-default save-query-as" {{action "saveQuery"}}>{{t "buttons.saveAs"}}</button>
 
           {{render 'insert-udfs'}}
@@ -76,13 +76,7 @@
     </div>
   </div>
 
-  {{#if tezUI.showOverlay}}
-    {{render 'tez-ui'}}
-  {{/if}}
-
-  {{#if visualExplain.showOverlay}}
-    {{render 'visual-explain'}}
-  {{/if}}
+  {{outlet 'overlay'}}
 
   <div class="query-menu">
     {{#popover-widget classNames="fa fa-info-circle queries-icon" titleTranslation="popover.queryEditorHelp.title" }}
@@ -93,10 +87,6 @@
       </ul>
     {{/popover-widget}}
 
-    <span {{bind-attr class="settings.showOverlay:active :fa :fa-gear :queries-icon"}} {{action 'toggleOverlay' 'settings'}}></span>
-
-    <span {{bind-attr class="visualExplain.showOverlay:active :fa :fa-bar-chart :queries-icon"}} {{action 'toggleOverlay' 'visualExplain'}}></span>
-
-    <span {{bind-attr class="tezUI.showOverlay:active shouldShowTez::hide :queries-icon :text-icon"}} {{action 'toggleOverlay' 'tezUI'}}>TEZ</span>
+    {{query-tabs}}
   </div>
 </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/results.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/results.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/results.hbs
index ed6fba6..06c21e4 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/results.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/results.hbs
@@ -20,13 +20,13 @@
   <table class="table table-expandable">
     <thead>
       <tr>
-        {{#each column in results.schema}}
+        {{#each column in formattedResults.schema}}
           <th> {{column.name}} </th>
         {{/each}}
       </tr>
     </thead>
     <tbody>
-      {{#each row in results.rows}}
+      {{#each row in formattedResults.rows}}
         <tr>
           {{#each item in row}}
             <td>{{item}}</td>
@@ -44,4 +44,4 @@
   </div>
 {{else}}
   {{error}}
-{{/if}}
\ No newline at end of file
+{{/if}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/insert-udfs.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/insert-udfs.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/insert-udfs.hbs
index 0ea2c99..0911835 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/insert-udfs.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/insert-udfs.hbs
@@ -16,29 +16,31 @@
 * limitations under the License.
 }}
 
-<div class="dropdown">
-  <a role="button" data-toggle="dropdown" class="btn btn-default btn-sm" data-target="#">
-    {{t "placeholders.select.udfs"}}
-    <span class="caret"></span>
-  </a>
-  <ul class="dropdown-menu pull-right" role="menu" aria-labelledby="dropdownMenu">
-    {{#each item in this}}
-      <li class="dropdown dropdown-submenu">
-        {{#if item.file}}
-          <a tabindex="-1">{{item.file.name}}</a>
-        {{else}}
-          <a tabindex="-1">{{tb-helper item.name}}</a>
-        {{/if}}
-        <ul class="dropdown-menu">
-          {{#each udf in item.udfs}}
-            <li>
-              {{#no-bubbling click="insertUdf" data=udf tagName="a"}}
-                {{udf.name}}
-              {{/no-bubbling}}
-            </li>
-          {{/each}}
-        </ul>
-      </li>
-    {{/each}}
-  </ul>
-</div>
\ No newline at end of file
+{{#if this.length}}
+  <div class="dropdown">
+    <a role="button" data-toggle="dropdown" class="btn btn-default btn-sm" data-target="#">
+      {{t "placeholders.select.udfs"}}
+      <span class="caret"></span>
+    </a>
+    <ul class="dropdown-menu pull-right" role="menu" aria-labelledby="dropdownMenu">
+      {{#each item in this}}
+        <li class="dropdown dropdown-submenu">
+          {{#if item.file}}
+            <a tabindex="-1">{{item.file.name}}</a>
+          {{else}}
+            <a tabindex="-1">{{tb-helper item.name}}</a>
+          {{/if}}
+          <ul class="dropdown-menu">
+            {{#each udf in item.udfs}}
+              <li>
+                {{#no-bubbling click="insertUdf" data=udf tagName="a"}}
+                  {{udf.name}}
+                {{/no-bubbling}}
+              </li>
+            {{/each}}
+          </ul>
+        </li>
+      {{/each}}
+    </ul>
+  </div>
+{{/if}}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/message.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/message.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/message.hbs
new file mode 100644
index 0000000..ad7fb9a
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/message.hbs
@@ -0,0 +1,36 @@
+{{!
+* 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.
+}}
+
+<div {{bind-attr class=":alert :notification view.typeClass"}}>
+  <button type="button" class="close" {{action "close" target="view"}}><span aria-hidden="true">&times;</span></button>
+  <i {{bind-attr class=":fa view.typeIcon"}}></i>
+
+  {{#if view.notification.body}}
+    <a {{action "expand" target="view"}}>
+      {{view.notification.message}}
+    </a>
+  {{else}}
+      {{view.notification.message}}
+  {{/if}}
+
+  {{#if view.isExpanded}}
+  <pre class="message-body">
+    {{preformatted-string view.notification.body}}
+  </pre>
+  {{/if}}
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/messages.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/messages.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/messages.hbs
new file mode 100644
index 0000000..7c494b6
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/messages.hbs
@@ -0,0 +1,30 @@
+{{!
+* 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.
+}}
+
+<div class="editor-overlay messages-container">
+  <h3>Messages
+    {{#if messages.length}}
+      <button class="btn btn-danger btn-xs" {{action 'removeAllMessages'}}><i class="fa fa-minus"></i> Clear All</button>
+    {{/if}}
+  </h3>
+
+
+  {{#each message in messages}}
+    {{view 'message' notification=message}}
+  {{/each}}
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-delete.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-delete.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-delete.hbs
index a870a0e..032c144 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-delete.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-delete.hbs
@@ -17,5 +17,5 @@
 }}
 
 {{#modal-widget heading=heading close="close" ok="delete"}}
-  {{text}}
+  {{tb-helper text}}
 {{/modal-widget}}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-save-query.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-save-query.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-save-query.hbs
new file mode 100644
index 0000000..6853550
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-save-query.hbs
@@ -0,0 +1,24 @@
+{{!
+* 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.
+}}
+
+{{#modal-widget heading=heading close="close" ok="save"}}
+  {{input type="text" class="form-control" value=text }}
+  {{#if showMessage}}
+    <span class="label label-warning">{{tb-helper message}}</span>
+  {{/if}}
+{{/modal-widget}}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/notification.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/notification.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/notification.hbs
new file mode 100644
index 0000000..35c2fe1
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/notification.hbs
@@ -0,0 +1,23 @@
+{{!
+* 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.
+}}
+
+<div {{bind-attr class=":alert :notification view.typeClass"}}>
+  <button type="button" class="close" {{action "close" target="view"}}><span aria-hidden="true">&times;</span></button>
+  <i {{bind-attr class=":fa view.typeIcon"}}></i>
+  {{view.notification.message}}
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/open-queries.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/open-queries.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/open-queries.hbs
index ad1d9bd..fdf5597 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/open-queries.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/open-queries.hbs
@@ -17,7 +17,7 @@
 }}
 
 {{#tabs-widget tabs=queryTabs removeClicked="removeQueryTab" canRemove=true}}
-  {{render 'settings'}}
+  {{outlet 'overlay'}}
   {{query-editor tables=selectedTables query=currentQuery.fileContent editor=view.editor highlightedText=highlightedText
                  columnsNeeded="getColumnsForAutocomplete"}}
 {{/tabs-widget}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/settings.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/settings.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/settings.hbs
index 791b7d0..98f0b54 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/settings.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/settings.hbs
@@ -16,44 +16,51 @@
 * limitations under the License.
 }}
 
-{{#if showOverlay}}
-  <div class="settings-container fadeIn">
-    <h3> Settings
-      <button class="btn btn-success btn-xs" {{action 'add'}}><i class="fa fa-plus"></i> Add</button>
-    </h3>
+<div class="editor-overlay settings-container fadeIn">
+  <h3> Settings
+    <button class="btn btn-success btn-xs" {{action 'add'}}><i class="fa fa-plus"></i> Add</button>
+    {{#if currentSettings.settings}}
+      <button class="btn btn-danger btn-xs" {{action 'removeAll'}}><i class="fa fa-minus"></i> Remove All</button>
+    {{/if}}
 
-    {{#each setting in currentSettings.settings}}
-      <div class="setting col-md-6 col-sm-12">
-        <form>
-          <div class="form-group">
-            <div class="input-group">
-              <div class="input-group-addon">
-                {{typeahead-widget
-                    content=predefinedSettings
-                    optionLabelPath="name"
-                    optionValuePath="name"
-                    selection=setting.key
-                    create="addKey"
-                }}
-              </div>
-              <div {{bind-attr class=":input-group-addon setting.valid::has-error"}}>
 
-                {{#if setting.key.values}}
-                  {{select-widget items=setting.key.values
-                                  labelPath="value"
-                                  selectedValue=setting.selection
-                                  defaultLabelTranslation="placeholders.select.value"
-                  }}
-                {{else}}
-                  {{input class="input-sm form-control" placeholderTranslation="placeholders.select.value" value=setting.selection.value}}
-                {{/if}}
+    {{#if canInvalidateSession}}
+      <button class="btn btn-danger btn-xs pull-right" {{action 'invalidateSession'}}><i class="fa fa-times"></i> Invalidate Session</button>
+    {{/if}}
+  </h3>
+
+  {{#each setting in currentSettings.settings}}
+    <div class="setting col-md-6 col-sm-12">
+      <form>
+        <div class="form-group">
+          <div class="input-group">
+            <div class="input-group-addon">
+              {{typeahead-widget
+                  options=predefinedSettings
+                  excluded=selectedSettings
+                  optionLabelPath="name"
+                  optionValuePath="name"
+                  selection=setting.key
+                  create="addKey"
+              }}
+            </div>
+            <div {{bind-attr class=":input-group-addon setting.valid::has-error"}}>
+
+              {{#if setting.key.values}}
+                {{select-widget items=setting.key.values
+                                labelPath="value"
+                                selectedValue=setting.selection
+                                defaultLabelTranslation="placeholders.select.value"
+                }}
+              {{else}}
+                {{input class="input-sm form-control" placeholderTranslation="placeholders.select.value" value=setting.selection.value}}
+              {{/if}}
 
-                <span class="fa fa-times-circle remove pull-right" {{action 'remove' setting}}></span>
-              </div>
+              <span class="fa fa-times-circle remove pull-right" {{action 'remove' setting}}></span>
             </div>
           </div>
-        </form>
-      </div>
-    {{/each}}
-  </div>
-{{/if}}
+        </div>
+      </form>
+    </div>
+  {{/each}}
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/tez-ui.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/tez-ui.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/tez-ui.hbs
index 4bee4d6..4bc0c0e 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/tez-ui.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/tez-ui.hbs
@@ -23,6 +23,8 @@
     {{else}}
       {{#if error}}
         <div class="alert alert-danger" role="alert"><strong>{{tb-helper error}}</strong></div>
+      {{else}}
+        <div class="alert alert-danger" role="alert"><strong>{{tb-helper 'tez.errors.no.dag'}}</strong></div>
       {{/if}}
     {{/if}}
   {{/panel-widget}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/visual-explain.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/visual-explain.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/visual-explain.hbs
index a823ef2..a831b8c 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/visual-explain.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/visual-explain.hbs
@@ -18,5 +18,63 @@
 
 <div id="visual-explain">
   {{#panel-widget headingTranslation="titles.query.visualExplain"}}
+
+  {{#each edge in view.edges}}
+    <div class="edge">
+      <div class="edge-path" {{bind-attr style="edge.style"}}>
+        {{edge.type}}
+      </div>
+     {{!--  <div class="edge-arrow" ></div> --}}
+    </div>
+  {{/each}}
+
+  <div class="nodes">
+    {{#each group in view.verticesGroups}}
+      <div class="node-container">
+        {{#if group.contents}}
+          {{#each node in group.contents}}
+            <div {{bind-attr class="node.isTableNode:table-node node.isOutputNode:output-node :node" title="node.id"}}>
+              {{#if node.isTableNode}}
+                <p><strong>{{t 'labels.table'}}</strong></p>
+                {{node.label}}
+              {{else}}
+                {{#if node.isOutputNode}}
+                  {{node.label}}
+                {{else}}
+                  <div class="node-heading">
+                    <strong>{{node.label}}</strong>
+                  </div>
+                  <div class="node-content">
+                    {{#each section in node.contents}}
+                      <p>
+                        {{#popover-widget classNames="fa fa-info-circle" titleTranslation="popover.visualExplain.statistics" }}
+                          {{section.statistics}}
+                        {{/popover-widget}}
+                        <strong>
+                          {{section.index}}. {{section.title}}
+                        </strong>
+                        {{section.value}}
+                      </p>
+
+                      {{#each field in section.fields}}
+                        {{#if field.value}}
+                          <p>{{field.label}} {{field.value}}</p>
+                        {{/if}}
+                      {{/each}}
+                    {{/each}}
+                  </div>
+                {{/if}}
+              {{/if}}
+            </div>
+          {{/each}}
+        {{else}}
+          <div class="node" {{bind-attr title="group.label"}}>
+            {{group.label}}
+          </div>
+        {{/if}}
+      </div>
+    {{/each}}
+  </div>
+
   {{/panel-widget}}
 </div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/constants.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/constants.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/constants.js
index 888275d..b099ce6 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/constants.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/constants.js
@@ -55,6 +55,7 @@ export default Ember.Object.create({
     udfInsertPrefix: 'create temporary function ',
     fileInsertPrefix: 'add jar ',
     explainPrefix: 'EXPLAIN ',
+    explainFormattedPrefix: 'EXPLAIN FORMATTED ',
     insertUdfs: 'insert-udfs',
     job: 'job',
     jobs: 'jobs',
@@ -180,7 +181,7 @@ export default Ember.Object.create({
 
   //this can be replaced by a string.format implementation
   adapter: {
-    version: '0.0.1',
+    version: '0.2.0',
     instance: 'Hive',
     apiPrefix: '/api/v1/views/HIVE/versions/',
     instancePrefix: '/instances/',
@@ -189,5 +190,25 @@ export default Ember.Object.create({
 
   settings: {
     executionEngine: 'hive.execution.engine'
+  },
+  sampleDataQuery: 'SELECT * FROM %@ LIMIT 100;',
+
+  notify: {
+    ERROR:  {
+      typeClass : 'alert-danger',
+      typeIcon  : 'fa-exclamation-triangle'
+    },
+    WARN: {
+      typeClass : 'alert-warning',
+      typeIcon  : 'fa-times-circle'
+    },
+    SUCCESS: {
+      typeClass : 'alert-success',
+      typeIcon  : 'fa-check'
+    },
+    INFO: {
+      typeClass : 'alert-info',
+      typeIcon  : 'fa-info'
+    }
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/dag-rules.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/dag-rules.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/dag-rules.js
new file mode 100644
index 0000000..f4b7f5d
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/dag-rules.js
@@ -0,0 +1,141 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.ArrayProxy.create({
+  content: Ember.A(
+    [
+      {
+        targetOperator: 'TableScan',
+        targetProperty: 'alias:',
+        label: 'Table Scan:',
+
+        fields: [
+          {
+            label: 'filterExpr:',
+            targetProperty: 'filterExpr:'
+          }
+        ]
+      },
+      {
+        targetOperator: 'Filter Operator',
+        targetProperty: 'predicate:',
+        label: 'Filter:',
+
+        fields: []
+      },
+      {
+        targetOperator: 'Map Join Operator',
+        label: 'Map Join',
+
+        fields: []
+      },
+      {
+        targetOperator: 'Merge Join Operator',
+        label: 'Merge Join',
+
+        fields: []
+      },
+      {
+        targetOperator: 'Select Operator',
+        label: 'Select',
+
+        fields: []
+      },
+      {
+        targetOperator: 'Reduce Output Operator',
+        label: 'Reduce',
+
+        fields: [
+          {
+            label: 'Partition columns:',
+            targetProperty: 'Map-reduce partition columns:'
+          },
+          {
+            label: 'Key expressions:',
+            targetProperty: 'key expressions:'
+          },
+          {
+            label: 'Sort order:',
+            targetProperty: 'sort order:'
+          }
+        ]
+      },
+      {
+        targetOperator: 'File Output Operator',
+        label: 'File Output Operator',
+
+        fields: []
+      },
+      {
+        targetOperator: 'Group By Operator',
+        label: 'Group By:',
+
+        fields: [
+          {
+            label: 'Aggregations:',
+            targetProperties: 'aggregations:'
+          },
+          {
+            label: 'Keys:',
+            targetProperty: 'keys:'
+          }
+        ]
+      },
+      {
+        targetOperator: 'Limit',
+        targetProperty: 'Number of rows:',
+        label: 'Limit:',
+
+        fields: []
+      },
+      {
+        targetOperator: 'Extract',
+        label: 'Extract',
+
+        fields: []
+      },
+      {
+        targetOperator: 'PTF Operator',
+        label: 'Partition Table Function',
+
+        fields: []
+      },
+      {
+        targetOperator: 'Dynamic Partitioning Event Operator',
+        labelel: 'Dynamic Partitioning Event',
+
+        fields: [
+          {
+            label: 'Target column:',
+            targetProperty: 'Target column:'
+          },
+          {
+            label: 'Target Vertex:',
+            targetProperty: 'Target Vertex:'
+          },
+          {
+            label: 'Partition key expr:',
+            targetProperty: 'Partition key expr:'
+          }
+        ]
+      }
+    ]
+  )
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/views/message.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/views/message.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/message.js
new file mode 100644
index 0000000..dd32a15
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/message.js
@@ -0,0 +1,36 @@
+  /**
+ * 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 Ember from 'ember';
+import NotificationView from 'hive/views/notification';
+
+export default NotificationView.extend({
+  templateName : 'message',
+  removeLater  : Ember.K,
+  isExpanded  : false,
+  removeMessage: 'removeMessage',
+
+  actions: {
+    expand: function() {
+      this.toggleProperty('isExpanded');
+    },
+
+    close: function() {
+      this.get('controller').send('removeMessage', this.get('notification'));
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/views/notification.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/views/notification.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/notification.js
new file mode 100644
index 0000000..291bfeb
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/notification.js
@@ -0,0 +1,51 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.View.extend({
+  closeAfter         : 500000,
+  isHovering         : false,
+  templateName       : 'notification',
+  removeNotification : 'removeNotification',
+
+  setup: function() {
+    this.set('typeClass', this.get('notification.type.typeClass'));
+    this.set('typeIcon', this.get('notification.type.typeIcon'));
+  }.on('init'),
+
+  removeLater: function() {
+    Ember.run.later(this, function() {
+      if (this.get('isHovering')) {
+        this.removeLater();
+      } else if (this.element) {
+        this.send('close');
+      }
+    }, this.get('closeAfter'));
+  }.on('didInsertElement'),
+
+  mouseEnter: function() { this.set('isHovering', true);  },
+  mouseLeave: function() { this.set('isHovering', false); },
+
+  actions: {
+    close: function() {
+      this.remove();
+      this.get('parentView').send('removeNotification', this.get('notification'));
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/views/visual-explain.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/views/visual-explain.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/visual-explain.js
index 4a887e2..07baedb 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/views/visual-explain.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/visual-explain.js
@@ -17,13 +17,28 @@
  */
 
 import Ember from 'ember';
+import dagRules from '../utils/dag-rules';
 
 export default Ember.View.extend({
+  willInsertElement: function () {
+    this.set('verticesGroups', []);
+    this.set('edges', []);
+    this.set('graph', new dagre.graphlib.Graph());
+
+    if (this.get('controller.json')) {
+      this.renderDag();
+    }
+  },
+
   didInsertElement: function () {
+    this._super();
+
     var target = this.$('#visual-explain');
 
     target.css('min-height', $('.main-content').height());
     target.animate({ width: $('.main-content').width() }, 'fast');
+
+    Ember.run.scheduleOnce('afterRender', this, this.afterRenderEvent);
   },
 
   willDestroyElement: function () {
@@ -31,5 +46,391 @@ export default Ember.View.extend({
 
     target.css('min-height', 0);
     target.css('width', 0);
+  },
+
+  getOffset: function (el) {
+    var _x = 0;
+    var _y = 0;
+    var _w = el.offsetWidth|0;
+    var _h = el.offsetHeight|0;
+    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
+        _x += el.offsetLeft - el.scrollLeft;
+        _y += el.offsetTop - el.scrollTop;
+        el = el.offsetParent;
+    }
+    return { top: _y, left: _x, width: _w, height: _h };
+  },
+
+  addEdge: function (div1, div2, thickness, type) {
+    var off1 = this.getOffset(div1);
+    var off2 = this.getOffset(div2);
+    // bottom right
+    var x1 = off1.left + off1.width / 2;
+    var y1 = off1.top + off1.height;
+    // top right
+    var x2 = off2.left + off2.width / 2;
+    var y2 = off2.top;
+    // distance
+    var length = Math.sqrt(((x2-x1) * (x2-x1)) + ((y2-y1) * (y2-y1)));
+    // center
+    var cx = ((x1 + x2) / 2) - (length / 2);
+    var cy = ((y1 + y2) / 2) - (thickness / 2) - 73;
+    // angle
+    var angle = Math.round(Math.atan2((y1-y2), (x1-x2)) * (180 / Math.PI));
+
+    if (angle < -90) {
+      angle = 180 + angle;
+    }
+
+    var style = "left: %@px; top: %@px; width: %@px;" +
+                "-moz-transform:rotate(%@4deg);" +
+                "-webkit-transform:rotate(%@4deg);" +
+                "-ms-transform:rotate(%@4deg);" +
+                "-transform:rotate(%@4deg);";
+
+    style = style.fmt(cx, cy, length, angle);
+
+    var edgeType;
+
+    if (type) {
+      if (type === 'BROADCAST_EDGE') {
+        edgeType = 'BROADCAST';
+      } else {
+        edgeType = 'SHUFFLE';
+      }
+    }
+
+    this.get('edges').pushObject({
+      style: style,
+      type: edgeType
+    });
+  },
+
+  afterRenderEvent : function () {
+    var g = this.get('graph');
+    var self = this;
+
+    //draw edges after the slide aniamtion for the visual explain container is done
+    Ember.run.later(function () {
+      g.edges().forEach(function (value) {
+        var edge = g.edge(value);
+        var v = value.v;
+
+        var firstNode = self.$("[title='" + value.v + "']")[0];
+        var secondNode = self.$("[title='" + value.w + "']")[0];
+
+        self.addEdge(firstNode, secondNode, 2, g.edge(value).type);
+      });
+    }, 300);
+  },
+
+  getNodeContents: function (operator, contents, table, vertex) {
+    var currentTable = table,
+      contents = contents || [],
+      nodeName,
+      node,
+      ruleNode,
+      nodeLabelValue,
+      self = this;
+
+    if (operator.constructor === Array) {
+      operator.forEach(function (childOperator) {
+        self.getNodeContents(childOperator, contents, currentTable, vertex);
+      });
+
+      return contents;
+    } else {
+      nodeName = Object.getOwnPropertyNames(operator)[0];
+      node = operator[nodeName];
+      ruleNode = dagRules.findBy('targetOperator', nodeName);
+
+      if (ruleNode) {
+        if (nodeName.indexOf('Map Join') > -1) {
+          nodeLabelValue = this.handleMapJoinNode(node, currentTable);
+          currentTable = null;
+        } else if (nodeName.indexOf('Merge Join') > -1) {
+          nodeLabelValue = this.handleMergeJoinNode(node, vertex);
+        } else {
+          nodeLabelValue = node[ruleNode.targetProperty];
+        }
+
+        contents.pushObject({
+          title: ruleNode.label,
+          statistics: node["Statistics:"],
+          index: contents.length + 1,
+          value: nodeLabelValue,
+          fields: ruleNode.fields.map(function (field) {
+            var value = node[field.targetProperty || field.targetProperties];
+
+            return {
+              label: field.label,
+              value: value
+            }
+          })
+        });
+
+        if (node.children) {
+          return this.getNodeContents(node.children, contents, currentTable, vertex);
+        } else {
+          return contents;
+        }
+      } else {
+        return contents;
+      }
+    }
+  },
+
+  handleMapJoinNode: function (node, table) {
+    var rows = table || "<rows from above>";
+    var firstTable = node["input vertices:"][0] || rows;
+    var secondTable = node["input vertices:"][1] || rows;
+
+    var joinString = node["condition map:"][0][""];
+    joinString = joinString.replace("0", firstTable);
+    joinString = joinString.replace("1", secondTable);
+    joinString += " on ";
+    joinString += node["keys:"][0] + "=";
+    joinString += node["keys:"][1];
+
+    return joinString;
+  },
+
+  handleMergeJoinNode: function (node, vertex) {
+    var graphData = this.get('controller.json')['STAGE PLANS']['Stage-1']['Tez'];
+    var edges = graphData['Edges:'];
+    var index = 0;
+    var joinString = node["condition map:"][0][""];
+
+    edges[vertex].toArray().forEach(function (edge) {
+      if (edge.type === "SIMPLE_EDGE") {
+        joinString.replace(String(index), edge.parent);
+        index++;
+      }
+    });
+
+    return joinString;
+  },
+
+  //sets operator nodes
+  setNodes: function (vertices) {
+    var g = this.get('graph');
+    var self = this;
+
+    vertices.forEach(function (vertex) {
+      var contents = [];
+      var operator;
+      var currentTable;
+
+      if (vertex.name.indexOf('Map') > -1) {
+        operator = vertex.value['Map Operator Tree:'][0];
+        currentTable = operator["TableScan"]["alias:"];
+      } else if (vertex.name.indexOf('Reducer') > -1) {
+        operator = vertex.value['Reduce Operator Tree:'];
+      }
+
+      // else if (vertex.name.indexOf('Union') > -1) {
+      //   g.setNode(vertex, {
+      //     id: vertex.name,
+      //     label: vertex.name
+      //   });
+      // }
+
+      if (operator) {
+        contents = self.getNodeContents(operator, null, currentTable, vertex.name);
+
+        g.setNode(vertex.name, {
+          contents: contents,
+          id: vertex.name,
+          label: vertex.name
+        });
+      }
+    });
+
+    return this;
+  },
+
+  //sets edges between operator nodes
+  setEdges: function (edges) {
+    var i;
+    var g = this.get('graph');
+    var invalidEdges = [];
+    var edgesToBeRemoved = [];
+    var isValidEdgeType = function (type) {
+      return type === "SIMPLE_EDGE" ||
+             type === "BROADCAST_EDGE";
+    };
+
+    edges.forEach(function (edge) {
+      var parent;
+      var type;
+
+      if (edge.value.constructor === Array) {
+        edge.value.forEach(function (childEdge) {
+          parent = childEdge.parent;
+          type = childEdge.type;
+
+          if (isValidEdgeType(type)) {
+            g.setEdge(parent, edge.name);
+            g.edge({v: parent, w: edge.name}).type = type;
+          } else {
+            invalidEdges.pushObject({
+              vertex: edge.name,
+              edge: childEdge
+            });
+          }
+        });
+      } else {
+        parent = edge.value.parent;
+        type = edge.value.type;
+
+        if (isValidEdgeType(type)) {
+          g.setEdge(parent, edge.name);
+          g.edge({v: parent, w: edge.name}).type = type;
+        } else {
+          invalidEdges.pushObject({
+            vertex: edge.name,
+            edge: edge.name
+          });
+        }
+      }
+    });
+
+    invalidEdges.forEach(function (invalidEdge) {
+      var targetEdge = g.edges().find(function (graphEdge) {
+        return graphEdge.v === invalidEdge.edge.parent ||
+               graphEdge.w === invalidEdge.edge.parent;
+      });
+
+      var targetVertex;
+
+      if (targetEdge) {
+        edgesToBeRemoved.pushObject(targetEdge);
+
+        if (targetEdge.v === invalidEdge.edge.parent) {
+          targetVertex = targetEdge.w;
+        } else {
+          targetVertex = targetEdge.v;
+        }
+
+        parent = invalidEdge.vertex;
+
+        g.setEdge({v: parent, w: targetVertex});
+        g.setEdge({v: parent, w: targetVertex}).type = "BROADCAST_EDGE";
+      }
+    });
+
+    edgesToBeRemoved.uniq().forEach(function (edge) {
+      g.removeEdge(edge.v, edge.w, edge.name);
+    });
+
+    return this;
+  },
+
+  //sets nodes for tables and their edges
+  setTableNodesAndEdges: function (vertices) {
+    var g = this.get('graph');
+
+    vertices.forEach(function (vertex) {
+      var operator;
+      var table;
+      var id;
+
+      if (vertex.name.indexOf('Map') > -1) {
+        operator = vertex.value['Map Operator Tree:'][0];
+        for (var node in operator) {
+          table = operator[node]['alias:'];
+
+          //create unique identifier by using table + map pairs so that we have
+          //different nodes for the same table if it's a table connected to multiple Map operators
+          id = table + ' for ' + vertex.name;
+
+          g.setNode(id, { id: id, label: table, isTableNode: true });
+          g.setEdge(id, vertex.name);
+        }
+      }
+    });
+
+    return this;
+  },
+
+  createNodeGroups: function () {
+    var groupedNodes = [];
+    var g = this.get('graph');
+    var lastRowNode;
+    var fileOutputOperator;
+
+    g.nodes().forEach(function (value) {
+      var node = g.node(value);
+
+      if (node) {
+        var existentRow = groupedNodes.findBy('topOffset', node.y);
+
+        if (!existentRow) {
+           groupedNodes.pushObject({
+              topOffset: node.y,
+              contents: [ node ]
+           });
+        } else {
+          existentRow.contents.pushObject(node);
+        }
+      }
+    });
+
+    groupedNodes = groupedNodes.sortBy('topOffset');
+    groupedNodes.forEach(function (group) {
+      group.contents = group.contents.sortBy('x');
+    });
+
+    lastRowNode = groupedNodes.get('lastObject.contents.lastObject');
+    fileOutputOperator = lastRowNode.contents.get('lastObject');
+
+    g.setNode(fileOutputOperator.title, { id: fileOutputOperator.title, label: fileOutputOperator.title, isOutputNode: true });
+    g.setEdge(fileOutputOperator.title, lastRowNode.id);
+
+    groupedNodes.pushObject({
+      contents: [ g.node(fileOutputOperator.title) ]
+    });
+
+    lastRowNode.contents.removeObject(fileOutputOperator);
+
+    this.set('verticesGroups', groupedNodes);
+  },
+
+  renderDag: function () {
+    var convert = function (inputObj) {
+      var array = [];
+
+      for (var key in inputObj) {
+        if (inputObj.hasOwnProperty(key)) {
+          array.pushObject({
+            name: key,
+            value: inputObj[key]
+          });
+        }
+      }
+
+      return array;
+    };
+
+    // Create a new directed graph
+    var g = this.get('graph');
+
+    var graphData = this.get('controller.json')['STAGE PLANS']['Stage-1']['Tez'];
+    var vertices = convert(graphData['Vertices:']);
+    var edges = convert(graphData['Edges:']);
+
+    // Set an object for the graph label
+    g.setGraph({});
+
+    // Default to assigning a new object as a label for each new edge.
+    g.setDefaultEdgeLabel(function () { return {}; });
+
+    this.setNodes(vertices)
+        .setEdges(edges)
+        .setTableNodesAndEdges(vertices);
+
+    dagre.layout(g);
+
+    this.createNodeGroups();
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/bower.json
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/bower.json b/contrib/views/hive/src/main/resources/ui/hive-web/bower.json
index 161a626..24e8821 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/bower.json
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/bower.json
@@ -1,27 +1,25 @@
 {
   "name": "hive",
   "dependencies": {
-    "handlebars": "2.0.0",
     "jquery": "^1.11.1",
-    "ember": "1.9.0",
-    "ember-data": "1.0.0-beta.14.1",
-    "ember-resolver": "~0.1.7",
-    "loader.js": "stefanpenner/loader.js#1.0.1",
+    "ember": "1.10.0",
+    "ember-data": "1.0.0-beta.16.1",
+    "ember-resolver": "~0.1.12",
+    "loader.js": "stefanpenner/loader.js#3.2.0",
     "ember-cli-shims": "stefanpenner/ember-cli-shims#0.0.3",
-    "ember-cli-test-loader": "rwjblue/ember-cli-test-loader#0.0.4",
+    "ember-cli-test-loader": "rwjblue/ember-cli-test-loader#0.1.3",
     "ember-load-initializers": "stefanpenner/ember-load-initializers#0.0.2",
     "ember-qunit": "0.2.8",
     "ember-qunit-notifications": "0.0.7",
-    "qunit": "~1.15.0",
+    "qunit": "~1.17.1",
     "bootstrap": "~3.2.0",
-    "ember-i18n": "~2.9.0",
+    "ember-i18n": "~3.0.0",
     "blanket": "~1.1.5",
     "jquery-ui": "~1.11.2",
     "selectize": "~0.12.0",
     "pretender": "0.1.0"
   },
   "resolutions": {
-    "handlebars": "2.0.0",
-    "ember": "1.9.0"
+    "ember": "1.10.0"
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/package.json
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/package.json b/contrib/views/hive/src/main/resources/ui/hive-web/package.json
index c2523b1..95f6897 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/package.json
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/package.json
@@ -22,24 +22,24 @@
   "devDependencies": {
     "body-parser": "^1.2.0",
     "bower": ">= 1.3.12",
-    "broccoli-asset-rev": "0.3.1",
-    "broccoli-sass": "^0.3.2",
-    "ember-cli": "0.1.15",
-    "ember-cli-blanket": "^0.2.2",
+    "broccoli-asset-rev": "^2.0.0",
+    "broccoli-sass": "0.6.3",
+    "ember-cli": "0.2.2",
+    "ember-cli-blanket": "^0.4.0",
     "ember-cli-content-security-policy": "0.3.0",
     "ember-cli-font-awesome": "0.0.4",
-    "ember-cli-htmlbars": "^0.5.3",
+    "ember-cli-htmlbars": "0.7.4",
     "ember-cli-ic-ajax": "0.1.1",
     "ember-cli-inject-live-reload": "^1.3.0",
     "ember-cli-jquery-ui": "0.0.12",
     "ember-cli-moment": "0.0.1",
     "ember-cli-pretender": "^0.3.1",
-    "ember-cli-qunit": "0.3.7",
+    "ember-cli-qunit": "0.3.9",
     "ember-cli-selectize": "0.0.19",
-    "ember-data": "1.0.0-beta.14.1",
+    "ember-data": "1.0.0-beta.16.1",
     "ember-dynamic-component": "0.0.1",
     "ember-export-application-global": "^1.0.0",
     "express": "^4.8.5",
-    "glob": "4.4.0"
+    "ember-cli-uglify": "1.0.1"
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/testem.json
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/testem.json b/contrib/views/hive/src/main/resources/ui/hive-web/testem.json
index eff93f9..5a8d375 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/testem.json
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/testem.json
@@ -1,6 +1,6 @@
 {
   "framework": "qunit",
-  "test_page": "tests/index.html",
+  "test_page": "tests/index.html?hidepassed&nocontainer",
   "launch_in_ci": [
     "PhantomJS"
   ],

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/api-mock.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/api-mock.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/api-mock.js
index 46a73f9..8567f30 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/api-mock.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/api-mock.js
@@ -86,7 +86,7 @@ export default function() {
         "fileResources":[],
         "statusDir":"job1",
         "id":1,
-        "title":"New Query",
+        "title":"Worksheet",
         "duration":2,
         "forcedContent":"",
         "owner":"admin",

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/index.html
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/index.html b/contrib/views/hive/src/main/resources/ui/hive-web/tests/index.html
index 6adbc89..b8928ff 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/index.html
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/index.html
@@ -46,6 +46,9 @@
         zoom: 50%;
       }
     </style>
+
+    {{content-for 'head-footer'}}
+    {{content-for 'test-head-footer'}}
   </head>
   <body>
     <div id="qunit"></div>
@@ -60,5 +63,8 @@
     <script src="assets/blanket-loader.js"></script>
     <script src="testem.js"></script>
     <script src="assets/test-loader.js"></script>
+
+    {{content-for 'body-footer'}}
+    {{content-for 'test-body-footer'}}
   </body>
 </html>

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/database-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/database-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/database-test.js
index 5be4fdc..0a6b12c 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/database-test.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/database-test.js
@@ -58,7 +58,7 @@ test('Expanding a database will retrieve the first page of tables for that datab
     click(targetDB);
 
     andThen(function () {
-      equal(find('.fa-th').length, 3);
+      equal(find('.fa-table').length, 3);
     });
   });
 });
@@ -74,7 +74,7 @@ test('Expanding a table will retrieve the first page of columns for that table.'
     click(targetDB);
 
     andThen(function () {
-      var targetTable = find('.fa-th').first();
+      var targetTable = find('.fa-table').first();
 
       click(targetTable);
 
@@ -102,4 +102,4 @@ test('Searching for a table will display table results and column search field',
       equal(find('.nav-tabs li').length, 2, 'Results tab has been redendered.');
     });
   });
-});
\ No newline at end of file
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/test-helper.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/test-helper.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/test-helper.js
index 53d9f9b..96975ee 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/test-helper.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/test-helper.js
@@ -22,9 +22,3 @@ import {
 } from 'ember-qunit';
 
 setResolver(resolver);
-
-document.write('<div id="ember-testing-container"><div id="ember-testing"></div></div>');
-
-QUnit.config.urlConfig.push({ id: 'nocontainer', label: 'Hide container'});
-var containerVisibility = QUnit.urlParams.nocontainer ? 'hidden' : 'visible';
-document.getElementById('ember-testing-container').style.visibility = containerVisibility;

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/alert-message-widget-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/alert-message-widget-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/alert-message-widget-test.js
index c7deabb..8f0f245 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/alert-message-widget-test.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/alert-message-widget-test.js
@@ -33,19 +33,19 @@ test('isExpanded is toggled on click', function() {
   });
 
   Ember.run(function() {
-    component.click();
+    component.send('toggleMessage');
   });
 
   equal(component.get('message.isExpanded'), true, 'isExpanded is set to true');
 
   Ember.run(function() {
-    component.click();
+    component.send('toggleMessage');
   });
 
   equal(component.get('message.isExpanded'), false, 'isExpanded is set to false');
 });
 
-test('removeLater should be called on click', function() {
+test('removeLater should be called when the message is toggled', function() {
   expect(1);
 
   var message = Ember.Object.create({ isExpanded: false});
@@ -63,11 +63,11 @@ test('removeLater should be called on click', function() {
   });
 
   Ember.run(function() {
-    component.click();
+    component.send('toggleMessage');
   });
 
   Ember.run(function() {
-    component.click();
+    component.send('toggleMessage');
   });
 });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/index-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/index-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/index-test.js
index 291cc7e..7d7b531 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/index-test.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/index-test.js
@@ -26,7 +26,9 @@ moduleFor('controller:index', 'IndexController', {
           'controller:index/history-query/results',
           'controller:index/history-query/explain',
           'controller:settings',
-          'adapter:database', 'controller:tables', 'controller:columns']
+          'adapter:database', 'controller:tables', 'controller:columns',
+          'controller:visual-explain', 'controller:tez-ui'
+        ]
 });
 
 test('when initialized, controller sets the queryProcessTabs.', function () {
@@ -106,14 +108,19 @@ test('bindQueryParams replaces same param multiple times', function() {
 test('parseQueryParams sets queryParams when query changes', function() {
   expect(3);
 
-  var controller = this.subject();
 
-  var query = "select $what from $where";
+  var query = Ember.Object.create({
+    id: 1,
+    fileContent: "select $what from $where"
+  });
+
+  var controller = this.subject({
+    model: query
+  });
 
   Ember.run(function() {
-    controller.set('openQueries.currentQuery', {
-        'fileContent': query
-    });
+    controller.set('openQueries.queryTabs', [query]);
+    controller.set('openQueries.currentQuery', query);
   });
 
   equal(controller.get('queryParams.length'), 2, '2 queryParams parsed');

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/queries-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/queries-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/queries-test.js
index d14f2cc..2578c33 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/queries-test.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/queries-test.js
@@ -20,7 +20,10 @@ import Ember from 'ember';
 import { moduleFor, test } from 'ember-qunit';
 
 moduleFor('controller:queries', 'QueriesController', {
-  needs: [ 'controller:history' ]
+  needs: [
+    'controller:history',
+    'controller:open-queries'
+  ]
 });
 
 test('controller is initialized', function() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/settings-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/settings-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/settings-test.js
index ef1b3d8..0101ceb 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/settings-test.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/settings-test.js
@@ -29,7 +29,12 @@ moduleFor('controller:settings', 'SettingsController', {
     'controller:index/history-query/explain',
     'controller:columns',
     'controller:udfs',
-    'controller:index/history-query/logs'
+    'controller:index/history-query/logs',
+    'controller:visual-explain',
+    'controller:tez-ui',
+    'controller:tables',
+    'adapter:database',
+    'adapter:application'
   ]
 });
 
@@ -48,6 +53,7 @@ test('can add a setting', function() {
 test('hasSettings return true if there are settings', function() {
   var controller = this.subject();
 
+  controller.get('currentSettings');
   ok(!controller.hasSettings(null), 'No settings => return false');
 
   Ember.run(function() {
@@ -81,12 +87,21 @@ test('validate', function() {
     predefinedSettings: predefinedSettings
   });
 
+  controller.set('openQueries.update', function () {
+    var defer = Ember.RSVP.defer();
+    defer.resolve();
+
+    return defer.promise;
+  });
+
   var settings = [
     Ember.Object.create({key: { name: 'some.key' }, value: 'value'}),
     Ember.Object.create({key: { name: 'some.key' }, value: '123'})
   ];
 
   Ember.run(function() {
+    controller.set('index.model', Ember.Object.create({ id: 1 }));
+    controller.get('currentSettings');
     controller.setSettingForQuery(1, settings);
   });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/tez-ui-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/tez-ui-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/tez-ui-test.js
index f2755c5..e04abd5 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/tez-ui-test.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/tez-ui-test.js
@@ -23,7 +23,7 @@ import {
 
 moduleFor('controller:tez-ui', 'TezUiController', {
   // Specify the other units that are required for this test.
-  // needs: ['controller:foo']
+  needs: ['controller:index']
 });
 
 // Replace this with your real tests.


[4/5] ambari git commit: AMBARI-10528. Hive View: Visual Explain, Error handling and bugfixes (alexantonenko)

Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/.travis.yml
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/.travis.yml b/contrib/views/hive/src/main/resources/ui/hive-web/.travis.yml
new file mode 100644
index 0000000..5d96e28
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/.travis.yml
@@ -0,0 +1,38 @@
+# 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.
+
+---
+language: node_js
+node_js:
+  - "0.12"
+
+sudo: false
+
+cache:
+  directories:
+    - node_modules
+
+before_install:
+  - "npm config set spin false"
+  - "npm install -g npm@^2"
+
+install:
+  - npm install -g bower
+  - npm install
+  - bower install
+
+script:
+  - npm test

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/Brocfile.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/Brocfile.js b/contrib/views/hive/src/main/resources/ui/hive-web/Brocfile.js
index db65f1f..8a367c9 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/Brocfile.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/Brocfile.js
@@ -25,9 +25,13 @@ var app = new EmberApp({
     //valid values are `default`, `bootstrap2`, `bootstrap3` or false
     'theme': 'bootstrap3'
   },
+  vendorFiles: {
+    'handlebars.js': null
+  },
   hinting: false
 });
 
+app.import('bower_components/ember/ember-template-compiler.js');
 app.import('bower_components/bootstrap/dist/js/bootstrap.js');
 app.import('bower_components/bootstrap/dist/css/bootstrap.css');
 app.import('bower_components/bootstrap/dist/css/bootstrap.css.map', {
@@ -42,5 +46,6 @@ app.import('vendor/codemirror/sql-hint.js');
 app.import('vendor/codemirror/show-hint.js');
 app.import('vendor/codemirror/codemirror.css');
 app.import('vendor/codemirror/show-hint.css');
+app.import('vendor/dagre.min.js');
 
 module.exports = app.toTree();

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/components/alert-message-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/alert-message-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/alert-message-widget.js
index fd8c1d7..df44c37 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/alert-message-widget.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/alert-message-widget.js
@@ -19,17 +19,17 @@
 import Ember from 'ember';
 
 export default Ember.Component.extend({
-  click: function () {
-    this.toggleProperty('message.isExpanded');
-
-    if (!this.get('message.isExpanded')) {
-      this.sendAction('removeLater', this.get('message'));
-    }
-  },
-
   actions: {
     remove: function () {
       this.sendAction('removeMessage', this.get('message'));
+    },
+
+    toggleMessage: function() {
+      this.toggleProperty('message.isExpanded');
+
+      if (!this.get('message.isExpanded')) {
+        this.sendAction('removeLater', this.get('message'));
+      }
     }
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/components/collapsible-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/collapsible-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/collapsible-widget.js
index 4425b59..f2081f2 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/collapsible-widget.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/collapsible-widget.js
@@ -28,6 +28,11 @@ export default Ember.Component.extend({
       if (this.get('isExpanded')) {
         this.sendAction('expanded', this.get('heading'), this.get('toggledParam'));
       }
+    },
+
+    sendControlAction: function(action) {
+      this.set('controlAction', action);
+      this.sendAction('controlAction', this.get('heading'), this.get('toggledParam'));
     }
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/components/modal-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/modal-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/modal-widget.js
index b9179bb..790e84b 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/modal-widget.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/modal-widget.js
@@ -25,10 +25,32 @@ export default Ember.Component.extend(Ember.I18n.TranslateableProperties, {
     }.bind(this));
   }.on('didInsertElement'),
 
+  keyPress: function(e) {
+    Ember.run.debounce(this, function() {
+      if (e.which === 13) {
+        this.send('ok');
+      } else if (e.which === 27) {
+        this.send('close');
+      }
+    }, 200)
+  },
+
+  setupEvents: function() {
+    this.$(document).on('keyup', Ember.$.proxy(this.keyPress, this));
+  }.on('didInsertElement'),
+
+  destroyEvents: function() {
+    this.$(document).off('keyup', Ember.$.proxy(this.keyPress, this));
+  }.on('willDestroyElement'),
+
   actions: {
     ok: function () {
       this.$('.modal').modal('hide');
       this.sendAction('ok');
+    },
+    close: function () {
+      this.$('.modal').modal('hide');
+      this.sendAction('close');
     }
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/components/notify-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/notify-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/notify-widget.js
new file mode 100644
index 0000000..5a37f13
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/notify-widget.js
@@ -0,0 +1,32 @@
+/**
+* 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 Ember from 'ember';
+
+
+export default Ember.Component.extend({
+  tagName: 'notifications',
+  classNames: ['notifications-container'],
+  notifications : Ember.computed.alias('notify.notifications'),
+
+  actions: {
+    removeNotification: function(notification) {
+      this.notify.removeNotification(notification);
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/components/number-range-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/number-range-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/number-range-widget.js
index ad98beb..4ab1dba 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/number-range-widget.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/number-range-widget.js
@@ -24,27 +24,25 @@ export default Ember.Component.extend({
 
     var slider;
 
-    this.$(function() {
-      if (!self.get('numberRange.from') && !self.get('numberRange.to')) {
-        self.get('numberRange').set('from', self.get('numberRange.min'));
-        self.get('numberRange').set('to', self.get('numberRange.max'));
-      }
+    if (!self.get('numberRange.from') && !self.get('numberRange.to')) {
+      self.get('numberRange').set('from', self.get('numberRange.min'));
+      self.get('numberRange').set('to', self.get('numberRange.max'));
+    }
 
-      slider = self.$( ".slider" ).slider({
-        range: true,
-        min: self.get('numberRange.min'),
-        max: self.get('numberRange.max'),
-        units: self.get('numberRange.units'),
-        values: [ self.get('numberRange.from'), self.get('numberRange.to') ],
-        slide: function (event, ui) {
-          self.set('numberRange.from', ui.values[0]);
-          self.set('numberRange.to', ui.values[1]);
-        },
+    slider = self.$( ".slider" ).slider({
+      range: true,
+      min: self.get('numberRange.min'),
+      max: self.get('numberRange.max'),
+      units: self.get('numberRange.units'),
+      values: [ self.get('numberRange.from'), self.get('numberRange.to') ],
+      slide: function (event, ui) {
+        self.set('numberRange.from', ui.values[0]);
+        self.set('numberRange.to', ui.values[1]);
+      },
 
-        change: function () {
-          self.sendAction('rangeChanged', self.get('numberRange'));
-        }
-      });
+      change: function () {
+        self.sendAction('rangeChanged', self.get('numberRange'));
+      }
     });
 
     this.set('slider', slider);

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/components/query-tabs.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/query-tabs.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/query-tabs.js
new file mode 100644
index 0000000..0ef6768
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/query-tabs.js
@@ -0,0 +1,121 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.Component.extend({
+  tabClassNames : "fa queries-icon query-context-tab",
+  openOverlayAction   : 'openOverlay',
+  closeOverlayAction  : 'closeOverlay',
+
+  tabs: [
+    Ember.Object.create({
+      iconClass: 'fa-code',
+      action: 'setDefaultActive'
+    }),
+    Ember.Object.create({
+      iconClass: 'fa-gear',
+      action: 'toggleOverlay',
+      template: 'settings',
+      outlet: 'overlay',
+      into: 'open-queries'
+    }),
+    Ember.Object.create({
+      iconClass: 'fa-bar-chart',
+      action: 'toggleOverlay',
+      template: 'visual-explain',
+      outlet: 'overlay',
+      into: 'index'
+    }),
+    Ember.Object.create({
+      iconClass: 'text-icon',
+      text: 'TEZ',
+      action: 'toggleOverlay',
+      template: 'tez-ui',
+      outlet: 'overlay',
+      into: 'index'
+    }),
+    Ember.Object.create({
+      iconClass: 'fa-envelope',
+      action: 'toggleOverlay',
+      template: 'messages',
+      outlet: 'overlay',
+      into: 'open-queries',
+      badgeProperty: 'count'
+    })
+  ],
+
+  setDefaultTab: function() {
+    var defaultTab = this.get('tabs.firstObject');
+
+    defaultTab.set('active', true);
+    this.set('default', defaultTab);
+    this.set('active', defaultTab);
+  }.on('init'),
+
+  setupTabsBadges: function() {
+    var tabs = this.get('tabs');
+    var self = this;
+
+    tabs.map(function(tab) {
+      if (tab.get('badgeProperty')) {
+        var controller = self.container.lookup('controller:' + tab.get('template'));
+        tab.set('controller', controller);
+
+        Ember.oneWay(tab, 'badge', 'controller.count');
+      }
+    });
+  }.on('init'),
+
+  closeActiveOverlay: function() {
+    this.sendAction('closeOverlayAction', this.get('active'));
+  },
+
+  openOverlay: function(tab) {
+    this.closeActiveOverlay();
+    this.set('active.active', false);
+    tab.set('active', true);
+    this.set('active', tab);
+    this.sendAction('openOverlayAction', tab);
+  },
+
+  setDefaultActive: function() {
+    var active     = this.get('active');
+    var defaultTab = this.get('default');
+
+    if (active !== defaultTab) {
+      this.closeActiveOverlay();
+      defaultTab.set('active', true);
+      active.set('active', false);
+      this.set('active', defaultTab);
+    }
+  },
+
+  actions: {
+    toggleOverlay: function(tab) {
+      if (tab !== this.get('default') && tab.get('active')) {
+        this.setDefaultActive();
+      } else {
+        this.openOverlay(tab);
+      }
+    },
+
+    setDefaultActive: function() {
+      this.setDefaultActive();
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/components/typeahead-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/typeahead-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/typeahead-widget.js
index 34c1f4b..51f09be 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/typeahead-widget.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/typeahead-widget.js
@@ -23,15 +23,60 @@ export default Typeahead.extend(Ember.I18n.TranslateableProperties, {
   didInsertElement: function() {
     this._super();
 
-    if(!this.get('selection') && this.get('content.firstObject')) {
+    if (!this.get('selection') && this.get('content.firstObject')) {
       this.set('selection', this.get('content.firstObject'));
     }
 
     this.selectize.on('dropdown_close', Ember.$.proxy(this.onClose, this));
   },
 
+  removeExcludedObserver: function() {
+    var self    = this;
+    var options = this.get('content');
+
+    if (!options) {
+      options = this.removeExcluded(true);
+      this.set('content', options);
+    } else {
+      this.removeExcluded();
+    }
+  }.observes('excluded.@each.key').on('init'),
+
+  removeExcluded: function(shouldReturn) {
+    var self            = this;
+    var excluded        = this.get('excluded') || [];
+    var options         = this.get('options');
+    var selection       = this.get('selection');
+    var objectToModify  = this.get('content');
+    var objectsToRemove = [];
+    var objectsToAdd    = [];
+
+    if (!options) {
+      return;
+    }
+
+    if (shouldReturn) {
+      objectToModify = Ember.copy(options);
+    }
+
+    if (options) {
+      options.forEach(function(option, index) {
+        if (excluded.contains(option) && option !== selection) {
+          objectsToRemove.push(option);
+        } else if (!objectToModify.contains(option)) {
+          objectsToAdd.push(option);
+        }
+      });
+    }
+
+    objectToModify.removeObjects(objectsToRemove);
+    objectToModify.pushObjects(objectsToAdd);
+
+    return objectToModify;
+  },
+
   onClose: function() {
-    if(!this.get('selection') && this.get('prevSelection')) {
+    if (!this.get('selection') && this.get('prevSelection')) {
       this.set('selection', this.get('prevSelection'));
     }
   },

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/alerts.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/alerts.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/alerts.js
deleted file mode 100644
index be70566..0000000
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/alerts.js
+++ /dev/null
@@ -1,47 +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.
- */
-
-import Ember from 'ember';
-
-export default Ember.ArrayController.extend({
-  pushObject: function (object) {
-    object.typeClass = 'alert-' + object.type;
-    this._super(object);
-    this.removeLater(object);
-  },
-
-  removeLater: function (object) {
-    var self = this;
-
-    Ember.run.later(function() {
-      if (!object.isExpanded) {
-        self.removeObject(object);
-      }
-    }, 5000);
-  },
-
-  actions: {
-    remove: function (message) {
-      this.removeObject(message);
-    },
-
-    removeLater: function (message) {
-      this.removeLater(message);
-    }
-  }
-});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/databases.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/databases.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/databases.js
index 6a259ba..0b103cd 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/databases.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/databases.js
@@ -30,13 +30,13 @@ export default Ember.ArrayController.extend({
   dbTables: Ember.computed.alias('controllers.' + constants.namingConventions.tables),
   dbColumns: Ember.computed.alias('controllers.' + constants.namingConventions.columns),
 
-  _handleTablesError: function (err) {
-    this.send('addAlert', constants.alerts.error, err.responseText, "alerts.errors.get.tables");
+  _handleTablesError: function (error) {
+    this.notify.error(error.responseJSON.message, error.responseJSON.trace);
     this.set('isLoading', false);
   },
 
-  _handleColumnsError: function (err) {
-    this.send('addAlert', constants.alerts.error, err.responseText, "alerts.errors.get.columns");
+  _handleColumnsError: function (error) {
+    this.notify.error(error.responseJSON.message, error.responseJSON.trace);
     this.set('isLoading', false);
   },
 
@@ -191,7 +191,52 @@ export default Ember.ArrayController.extend({
     return defer.promise;
   },
 
+  tableControls: Ember.A([
+    Ember.Object.create({
+      icon: 'fa-list',
+      action: 'loadSampleData',
+      tooltip: Ember.I18n.t('tooltips.loadSample')
+    })
+  ]),
+
+  panelIconActions: function () {
+    return [
+      Ember.Object.create({
+        icon: 'fa-refresh',
+        action: 'refreshDatabaseExplorer'
+      })
+    ];
+  }.property(),
+
   actions: {
+    refreshDatabaseExplorer: function() {
+      var self = this;
+      var selectedDatabase = this.get('selectedDatabase');
+
+      this.store.unloadAll('database');
+      this.store.fetchAll('database').then(function() {
+        var database = self.get('model').findBy('id', selectedDatabase.get('id'));
+        self.set('selectedDatabase', database);
+      }).catch(function(response) {
+        self.notify.error(response.responseJSON.message, response.responseJSON.trace);
+      });
+    },
+
+    loadSampleData: function(tableName, database) {
+      var self = this;
+      this.send('addQuery', Ember.I18n.t('titles.tableSample', { tableName: tableName }));
+
+      Ember.run.later(function() {
+        var query = constants.sampleDataQuery.fmt(tableName);
+
+        self.set('selectedDatabase', database);
+        self.get('openQueries.currentQuery')
+          .set('fileContent', query);
+
+        self.send('executeQuery');
+      });
+    },
+
     getTables: function (dbName) {
       var database = this.findBy('name', dbName),
           tables = database.tables,
@@ -257,6 +302,7 @@ export default Ember.ArrayController.extend({
           resultsTab = this.get('tabs').findBy('view', constants.namingConventions.databaseSearch),
           tableSearchResults = this.get('tableSearchResults');
 
+      this.set('tablesSearchTerm', searchTerm);
       resultsTab.set('visible', true);
       this.set('selectedTab', resultsTab);
       this.set('columnSearchTerm', '');
@@ -351,4 +397,4 @@ export default Ember.ArrayController.extend({
       });
     }
   }
-});
\ No newline at end of file
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index.js
index 954b42b..df2d088 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index.js
@@ -42,10 +42,19 @@ export default Ember.Controller.extend({
   visualExplain: Ember.computed.alias('controllers.' + constants.namingConventions.visualExplain),
   tezUI: Ember.computed.alias('controllers.' + constants.namingConventions.tezUI),
 
+  isQueryTabActive: function() {
+    return !this.get('tezUI.showOverlay') && !this.get('visualExplain.showOverlay') && !this.get('settings.showOverlay');
+  }.property('tezUI.showOverlay', 'visualExplain.showOverlay', 'settings.showOverlay'),
+
   shouldShowTez: function() {
     return this.get('model.dagId') && this.get('tezUI.isTezViewAvailable');
   }.property('model.dagId', 'tezUI.isTezViewAvailable'),
 
+  shouldShowVisualExplain: function () {
+    return this.get('openQueries.currentQuery.fileContent');
+  }.property('openQueries.currentQuery.fileContent'),
+
+
   canExecute: function () {
     var isModelRunning = this.get('model.isRunning');
     var hasParams = this.get('queryParams.length');
@@ -84,11 +93,12 @@ export default Ember.Controller.extend({
     currentParams.setObjects(updatedParams);
   }.observes('openQueries.currentQuery.fileContent'),
 
-  _executeQuery: function (shouldExplain) {
+  _executeQuery: function (shouldExplain, shouldGetVisualExplain) {
     var queryId,
         query,
         finalQuery,
         job,
+        defer = Ember.RSVP.defer(),
         originalModel = this.get('model');
 
     job = this.store.createRecord(constants.namingConventions.job, {
@@ -110,15 +120,55 @@ export default Ember.Controller.extend({
 
     query = this.get('openQueries').getQueryForModel(originalModel);
 
-    finalQuery = this.buildQuery(query, shouldExplain);
+    query = this.buildQuery(query, shouldExplain, shouldGetVisualExplain);
+
+    // for now we won't support multiple queries
+    // buildQuery will return false it multiple queries
+    // are selected
+    if (!query) {
+      originalModel.set('isRunning', false);
+      defer.reject({
+        responseJSON: {
+          message: 'Running multiple queries is not supported.'
+        }
+      });
+
+      return defer.promise;
+    }
+
+    finalQuery = query;
     finalQuery = this.bindQueryParams(finalQuery);
     finalQuery = this.prependQuerySettings(finalQuery);
 
     job.set('forcedContent', finalQuery);
 
+    if (shouldGetVisualExplain) {
+      return this.getVisualExplainJson(job, originalModel);
+    }
+
     return this.saveQuery(job, originalModel);
   },
 
+  getVisualExplainJson: function (job, originalModel) {
+    var self = this;
+    var defer = Ember.RSVP.defer();
+
+    job.save().then(function () {
+      self.get('results').getResultsJson(job).then(function (json) {
+        defer.resolve(json);
+        originalModel.set('isRunning', undefined);
+      }, function (err) {
+        defer.reject(err);
+        originalModel.set('isRunning', undefined);
+      });
+    }, function (err) {
+      defer.reject(err);
+        originalModel.set('isRunning', undefined);
+    });
+
+    return defer.promise;
+  },
+
   saveQuery: function (job, originalModel) {
     var defer = Ember.RSVP.defer(),
         self = this,
@@ -158,35 +208,41 @@ export default Ember.Controller.extend({
     return query;
   },
 
-  buildQuery: function (query, shouldExplain) {
+  buildQuery: function (query, shouldExplain, shouldGetVisualExplain) {
     var selections = this.get('openQueries.highlightedText'),
         isQuerySelected = selections && selections[0] !== "",
-        queryComponents = this.extractComponents(query.get('fileContent')),
+        queryContent = query ? query.get('fileContent') : '',
+        queryComponents = this.extractComponents(queryContent),
         finalQuery = '',
-        queries;
+        queries = null;
 
     if (isQuerySelected) {
-      queries = selections.map(function (s) {
-        return s.replace(";", "");
-      });
-    } else {
-      queries = queryComponents.queryString.split(';');
-      queries = queries.filter(Boolean);
+      queryComponents.queryString = selections.join('');
+    }
+
+    queries = queryComponents.queryString.split(';');
+    queries = queries.map(function(s) {
+      return s.trim();
+    });
+    queries = queries.filter(Boolean);
+
+    // return false if multiple queries are selected
+    // @FIXME: Remove this to support multiple queries
+    if (queries.length > 1) {
+      return false;
     }
 
     queries = queries.map(function (query) {
       if (shouldExplain) {
-        if (query.indexOf(constants.namingConventions.explainPrefix) === -1) {
+        query = query.replace(/explain|formatted/gi, '').trim();
+
+        if (shouldGetVisualExplain) {
+          return constants.namingConventions.explainFormattedPrefix + query;
+        } else {
           return constants.namingConventions.explainPrefix + query;
         }
-
-        return query;
       } else {
-        if (query.indexOf(constants.namingConventions.explainPrefix) > -1) {
-          return query.replace(constants.namingConventions.explainPrefix, '');
-        }
-
-        return query;
+        return query.replace(/explain|formatted/gi, '').trim();
       }
     });
 
@@ -199,6 +255,7 @@ export default Ember.Controller.extend({
     }
 
     finalQuery += queries.join(";");
+    finalQuery += ";";
     return finalQuery;
   },
 
@@ -278,6 +335,10 @@ export default Ember.Controller.extend({
     });
   }.observes('content'),
 
+  selectedDatabaseChanged: function() {
+    this.set('content.dataBase', this.get('databases.selectedDatabase.name'));
+  }.observes('databases.selectedDatabase'),
+
   csvUrl: function () {
     if (this.get('content.constructor.typeKey') !== constants.namingConventions.job) {
       return;
@@ -309,7 +370,7 @@ export default Ember.Controller.extend({
         items.push(
           Ember.Object.create({
             title: Ember.I18n.t('buttons.saveCsv'),
-            href: this.get('csvUrl')
+            action: 'downloadAsCSV'
           })
         );
       }
@@ -350,7 +411,7 @@ export default Ember.Controller.extend({
     }).then(function (response) {
       self.pollSaveToHDFS(response);
     }, function (response) {
-      self.send('addAlert', constants.alerts.error, response.message, "alerts.errors.save.results");
+      self.notify.error(response.responseJSON.message, response.responseJSON.trace);
     });
   },
 
@@ -367,7 +428,7 @@ export default Ember.Controller.extend({
           self.set('content.isRunning', false);
         }
       }, function (response) {
-        self.send('addAlert', constants.alerts.error, response.message, "alerts.errors.save.results");
+        self.notify.error(response.responseJSON.message, response.responseJSON.trace);
       });
     }, 2000);
   },
@@ -386,6 +447,25 @@ export default Ember.Controller.extend({
       this.saveToHDFS();
     },
 
+    downloadAsCSV: function() {
+      var self = this,
+          defer = Ember.RSVP.defer();
+
+      this.send('openModal', 'modal-save', {
+        heading: "modals.download.csv",
+        text: this.get('content.title'),
+        defer: defer
+      });
+
+      defer.promise.then(function (text) {
+        // download file ...
+        var urlString = "%@/?fileName=%@.csv";
+        var url = self.get('csvUrl');
+        url = urlString.fmt(url, text);
+        window.open(url);
+      });
+    },
+
     insertUdf: function (item) {
       var query = this.get('openQueries').getQueryForModel(this.get('model'));
 
@@ -418,17 +498,16 @@ export default Ember.Controller.extend({
     addQuery: (function () {
       var idCounter = 0;
 
-      return function () {
+      return function (workSheetName) {
         var model = this.store.createRecord(constants.namingConventions.savedQuery, {
           dataBase: this.get('databases.selectedDatabase.name'),
-          title: 'New Query',
-          type: constants.namingConventions.savedQuery,
+          title: workSheetName ? workSheetName : Ember.I18n.t('titles.query.tab'),
           queryFile: '',
           id: 'fixture_' + idCounter
         });
 
-        if (idCounter) {
-          model.set('title', model.get('title') + ' (' + idCounter + ')')
+        if (idCounter && !workSheetName) {
+          model.set('title', model.get('title') + ' (' + idCounter + ')');
         }
 
         idCounter++;
@@ -438,26 +517,34 @@ export default Ember.Controller.extend({
     }()),
 
     saveQuery: function () {
+      //case 1. Save a new query from a new query tab -> route changes to new id
+      //case 2. Save a new query from an existing query tab -> route changes to new id
+      //case 3. Save a new query from a job tab -> route doesn't change
+      //case 4. Update an existing query tab. -> route doesn't change
+
       var self = this,
-          wasNew = this.get('model.isNew'),
           defer = Ember.RSVP.defer();
 
       this.set('model.dataBase', this.get('databases.selectedDatabase.name'));
 
-      this.send('openModal', 'modal-save', {
-        heading: "modals.save.heading",
+      this.send('openModal', 'modal-save-query', {
+        heading: 'modals.save.heading',
+        message: 'modals.save.overwrite',
         text: this.get('content.title'),
+        content: this.get('content'),
         defer: defer
       });
 
-      defer.promise.then(function (text) {
-        self.get('content').set('title', text);
-
-        self.get('openQueries').save(self.get('content')).then(function () {
-          if (wasNew) {
-            self.transitionToRoute(constants.namingConventions.subroutes.savedQuery, self.get('model.id'));
-          }
-        });
+      defer.promise.then(function (result) {
+        if (result.get('overwrite')) {
+          self.get('openQueries').save(self.get('content'), null, true, result.get('text'));
+        } else {
+          self.get('openQueries').save(self.get('content'), null, false, result.get('text')).then(function (newId) {
+            if (self.get('model.constructor.typeKey') !== constants.namingConventions.job) {
+              self.transitionToRoute(constants.namingConventions.subroutes.savedQuery, newId);
+            }
+          });
+        }
       });
     },
 
@@ -476,7 +563,8 @@ export default Ember.Controller.extend({
 
         self.transitionToRoute(constants.namingConventions.subroutes.historyQuery, job.get('id'));
       }, function (err) {
-        self.send('addAlert', constants.alerts.error, err.responseText, "alerts.errors.save.query");
+        var errorBody = err.responseJSON.trace ? err.responseJSON.trace : false;
+        self.notify.error(err.responseJSON.message, errorBody);
       });
     },
 
@@ -488,11 +576,13 @@ export default Ember.Controller.extend({
 
         self.transitionToRoute(constants.namingConventions.subroutes.historyQuery, job.get('id'));
       }, function (err) {
-        self.send('addAlert', constants.alerts.error, err.responseText, "alerts.errors.save.query");
+        this.notify.error(err.responseJSON.message, err.responseJSON.trace);
       });
     },
 
     toggleOverlay: function (targetController) {
+      var self = this;
+
       if (this.get('visualExplain.showOverlay') && targetController !== 'visualExplain') {
         this.set('visualExplain.showOverlay', false);
       } else if (this.get('tezUI.showOverlay') && targetController !== 'tezUI') {
@@ -501,12 +591,28 @@ export default Ember.Controller.extend({
         this.set('settings.showOverlay', false);
       }
 
+      if (!targetController) {
+        return;
+      }
+
       if (targetController !== 'settings') {
         //set content for visual explain and tez ui.
         this.set(targetController + '.content', this.get('content'));
       }
 
-      this.toggleProperty(targetController + '.showOverlay');
+      if (targetController === 'visualExplain' && !this.get(targetController + '.showOverlay')) {
+        this._executeQuery(true, true).then(function (json) {
+          //this condition should be changed once we change the way of retrieving this json
+          if (json['STAGE PLANS']['Stage-1']) {
+            self.set(targetController + '.json', json);
+            self.toggleProperty(targetController + '.showOverlay');
+          }
+        }, function (err) {
+          self.notify.error(err.responseJSON.message, err.responseJSON.trace);
+        });
+      } else {
+        this.toggleProperty(targetController + '.showOverlay');
+      }
     }
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js
index dc75280..e61df51 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js
@@ -39,30 +39,44 @@ export default Ember.ObjectController.extend({
     if (cachedExplain) {
       this.formatExplainResults(cachedExplain);
     } else {
-      this.getExplain();
+      this.getExplain(true);
     }
   }.observes('content'),
 
-  getExplain: function () {
+  getExplain: function (firstPage, rows) {
     var self = this;
     var url = this.container.lookup('adapter:application').buildURL();
-    url += '/' + constants.namingConventions.jobs + '/' + this.get('content.id') + '/results?first=true';
+    url += '/' + constants.namingConventions.jobs + '/' + this.get('content.id') + '/results';
+
+    if (firstPage) {
+      url += '?first=true';
+    }
 
     Ember.$.getJSON(url).then(function (data) {
-      var explainSet = self.get('cachedExplains').pushObject(Ember.Object.create({
-        id: self.get('content.id'),
-        explain: data
-      }));
+      var explainSet;
+
+      //if rows from a previous page read exist, prepend them
+      if (rows) {
+        data.rows.unshiftObjects(rows);
+      }
 
-      self.set('content.explain', explainSet);
+      if (!data.hasNext) {
+        explainSet = self.get('cachedExplains').pushObject(Ember.Object.create({
+          id: self.get('content.id'),
+          explain: data
+        }));
 
-      self.formatExplainResults(explainSet);
+        self.set('content.explain', explainSet);
+
+        self.formatExplainResults(explainSet);
+      } else {
+        self.getExplain(false, data.rows);
+      }
     });
   },
 
   formatExplainResults: function (explainSet) {
     var formatted = [],
-        orderedNodes = [],
         currentNode,
         currentNodeWhitespace,
         previousNode,
@@ -116,4 +130,4 @@ export default Ember.ObjectController.extend({
 
     this.set('formattedExplain', formatted);
   }
-});
\ No newline at end of file
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js
index 02edc86..59c0892 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js
@@ -28,13 +28,22 @@ export default Ember.ObjectController.extend({
   reloadJobLogs: function (job) {
     var self = this,
         defer = Ember.RSVP.defer(),
-        handleError = function (err) {
-          self.send('addAlert', constants.namingConventions.alerts.error, err.responseText);
+        handleError = function (error) {
+          job.set('isRunning', false);
+
+          if (typeof error === "string") {
+            self.notify.error(error);
+          } else {
+            self.notify.error(error.responseJSON.message, error.responseJSON.trace);
+          }
           defer.reject();
         };
 
     job.reload().then(function () {
-      self.get('files').reload(job.get('logFile')).then(function (file) {
+      if (utils.insensitiveCompare(job.get('status'), constants.statuses.error)) {
+        handleError(job.get('statusMessage'));
+      } else {
+        self.get('files').reload(job.get('logFile')).then(function (file) {
         var fileContent = file.get('fileContent');
 
         if (fileContent) {
@@ -42,9 +51,10 @@ export default Ember.ObjectController.extend({
         }
 
         defer.resolve();
-      },function (err) {
-        handleError(err);
-      });
+        },function (err) {
+          handleError(err);
+        });
+      }
     }, function (err) {
       handleError(err);
     });

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js
index 7977541..9a50f27 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js
@@ -22,6 +22,25 @@ import utils from 'hive/utils/functions';
 
 export default Ember.ObjectController.extend({
   cachedResults: [],
+  formattedResults: [],
+
+  processResults: function() {
+    var results = this.get('results');
+
+    if (!results || !results.schema || !results.rows) {
+      return;
+    }
+
+    var schema = results.schema.map(function(column) {
+      return {
+        name: column[0],
+        type: column[1],
+        index: column[2]
+      }
+    });
+
+    this.set('formattedResults', { schema: schema, rows: results.rows });
+  }.observes('results'),
 
   keepAlive: function (job) {
     Ember.run.later(this, function () {
@@ -73,6 +92,20 @@ export default Ember.ObjectController.extend({
     return this.cachedResults.findBy('id', this.get('content.id')).results.indexOf(this.get('results')) <= 0;
   }.property('results'),
 
+  getResultsJson: function (job) {
+    var defer = Ember.RSVP.defer();
+    var url = this.container.lookup('adapter:application').buildURL();
+    url += '/' + constants.namingConventions.jobs + '/' + job.get('id') + '/results?first=true';
+
+    Ember.$.getJSON(url).then(function (results) {
+      defer.resolve(JSON.parse(results.rows[0][0]));
+    }, function (err) {
+      defer.reject(err);
+    });
+
+    return defer.promise;
+  },
+
   actions: {
     getNextPage: function (firstPage, job) {
       var self = this;
@@ -115,6 +148,7 @@ export default Ember.ObjectController.extend({
         if (firstPage) {
           self.keepAlive(job || self.get('content'));
         }
+
       }, function (err) {
         self.set('error', err.responseText);
       });

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/messages.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/messages.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/messages.js
new file mode 100644
index 0000000..6de1c64
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/messages.js
@@ -0,0 +1,33 @@
+/**
+* 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 Ember from 'ember';
+
+export default Ember.Controller.extend({
+  messages: Ember.computed.alias('notify.messages'),
+  count: Ember.computed.alias('messages.length'),
+
+  actions: {
+    removeMessage: function(message) {
+      this.notify.removeMessage(message);
+    },
+
+    removeAllMessages: function() {
+      this.notify.removeAllMessages();
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/modal-save-query.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/modal-save-query.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/modal-save-query.js
new file mode 100644
index 0000000..d878bc7
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/modal-save-query.js
@@ -0,0 +1,42 @@
+/**
+ * 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 Ember from 'ember';
+import ModalSave from '../controllers/modal-save';
+import constants from '../utils/constants';
+
+export default ModalSave.extend({
+  showMessage: function () {
+    var content = this.get('content');
+
+    return !content.get('isNew') &&
+            content.get('title') === this.get('text') &&
+            content.get('constructor.typeKey') !== constants.namingConventions.job;
+  }.property('content.isNew', 'text'),
+
+  actions: {
+    save: function () {
+      this.send('closeModal');
+
+      this.defer.resolve(Ember.Object.create({
+        text: this.get('text'),
+        overwrite: this.get('showMessage')
+      }));
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/open-queries.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/open-queries.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/open-queries.js
index 2abfff6..a0c033e 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/open-queries.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/open-queries.js
@@ -154,7 +154,7 @@ export default Ember.ArrayController.extend({
     return defer.promise;
   },
 
-  save: function (model, query) {
+  save: function (model, query, isUpdating, newTitle) {
     var tab = this.getTabForModel(model),
         self = this,
         wasNew,
@@ -168,6 +168,7 @@ export default Ember.ArrayController.extend({
 
     if (model.get('isNew')) {
       wasNew = true;
+      model.set('title', newTitle);
       model.set('id', null);
     }
 
@@ -175,16 +176,30 @@ export default Ember.ArrayController.extend({
     if (model.get('constructor.typeKey') === constants.namingConventions.job) {
       model = this.store.createRecord(constants.namingConventions.savedQuery, {
         dataBase: this.get('databases.selectedDatabase.name'),
-        title: model.get('title'),
+        title: newTitle,
         queryFile: model.get('queryFile'),
         owner: model.get('owner')
       });
+    } else {
+      tab.set('name', newTitle);
+    }
+
+    //if saving a new query from an existing one create a new record and save it
+    if (!isUpdating && !model.get('isNew') && model.get('constructor.typeKey') !== constants.namingConventions.job) {
+      model = this.store.createRecord(constants.namingConventions.savedQuery, {
+        dataBase: this.get('databases.selectedDatabase.name'),
+        title: newTitle,
+        owner: model.get('owner')
+      });
+
+      wasNew = true;
     }
 
     model.save().then(function (updatedModel) {
-      tab.set('name', updatedModel.get('title'));
       jobModel.set('queryId', updatedModel.get('id'));
 
+      tab.set('isDirty', false);
+
       var content = self.get('index').prependQuerySettings(query.get('fileContent'));
       //update query tab path with saved model id if its a new record
       if (wasNew) {
@@ -198,7 +213,7 @@ export default Ember.ArrayController.extend({
             self.pushObject(updatedFile);
             self.set('currentQuery', updatedFile);
 
-            defer.resolve();
+            defer.resolve(updatedModel.get('id'));
           }, function (err) {
             defer.reject(err);
           });
@@ -209,7 +224,7 @@ export default Ember.ArrayController.extend({
         query.set('fileContent', content);
         query.save().then(function () {
           self.toggleProperty('tabUpdated');
-          defer.resolve();
+          defer.resolve(updatedModel.get('id'));
         }, function (err) {
           defer.reject(err);
         });
@@ -259,34 +274,83 @@ export default Ember.ArrayController.extend({
          hasSettings;
   },
 
+  isDirty: function(model) {
+    var query = this.getQueryForModel(model);
+
+    if (model.get('isNew') && !query.get('fileContent')) {
+      return false;
+    }
+
+    if (query && query.get('isDirty')) {
+      return true;
+    }
+
+    return !!(!model.get('queryId') && model.get('isDirty'));
+
+
+  },
+
+  updatedDeletedQueryTab: function (model) {
+    var tab = this.getTabForModel(model);
+
+    if (tab) {
+      this.closeTab(tab);
+    }
+  },
+
+  dirtyObserver: function () {
+    var tab;
+    var model = this.get('index.model');
+
+    if (model) {
+      tab = this.getTabForModel(model);
+
+      if (tab) {
+        tab.set('isDirty', this.isDirty(model));
+      }
+    }
+  }.observes('currentQuery.isDirty', 'currentQuery.fileContent'),
+
+  closeTab: function (tab, goToNextTab) {
+    var remainingTabs = this.get('queryTabs').without(tab);
+
+    this.set('queryTabs', remainingTabs);
+
+    //remove cached results set
+    if (tab.type === constants.namingConventions.job) {
+      this.get('jobResults').clearCachedResultsSet(tab.id);
+      this.get('jobExplain').clearCachedExplainSet(tab.id);
+    }
+
+    if (goToNextTab) {
+      this.navigateToLastTab();
+    }
+  },
+
+  navigateToLastTab: function () {
+    var lastTab = this.get('queryTabs.lastObject');
+
+    if (lastTab) {
+      if (lastTab.type === constants.namingConventions.job) {
+        this.transitionToRoute(constants.namingConventions.subroutes.historyQuery, lastTab.id);
+      } else {
+        this.transitionToRoute(constants.namingConventions.subroutes.savedQuery, lastTab.id);
+      }
+    } else {
+      this.get('index').send('addQuery');
+    }
+  },
+
   actions: {
     removeQueryTab: function (tab) {
       var self = this,
-          defer,
-          remainingTabs = this.get('queryTabs').without(tab),
-          lastTab = remainingTabs.get('lastObject'),
-          closeTab = function () {
-            self.set('queryTabs', remainingTabs);
-
-            //remove cached results set
-            if (tab.type === constants.namingConventions.job) {
-              self.get('jobResults').clearCachedResultsSet(tab.id);
-              self.get('jobExplain').clearCachedExplainSet(tab.id);
-            }
-
-            if (lastTab.type === constants.namingConventions.job) {
-              self.transitionToRoute(constants.namingConventions.subroutes.historyQuery, lastTab.id);
-            } else {
-              self.transitionToRoute(constants.namingConventions.subroutes.savedQuery, lastTab.id);
-            }
-          };
+          defer;
 
       this.store.find(tab.type, tab.id).then(function (model) {
         var query = self.getQueryForModel(model);
 
-        if ((model.get('isNew') && !query.get('fileContent')) ||
-            (!model.get('isNew') && !query.get('isDirty'))) {
-          closeTab();
+        if (!self.isDirty(model)) {
+          self.closeTab(tab, true);
         } else {
           defer = Ember.RSVP.defer();
           self.send('openModal',
@@ -300,11 +364,11 @@ export default Ember.ArrayController.extend({
           defer.promise.then(function (text) {
             model.set('title', text);
             self.save(model, query).then(function () {
-              closeTab();
+              self.closeTab(tab, true);
             });
           }, function () {
             model.rollback();
-            closeTab();
+            self.closeTab(tab, true);
           });
         }
       });

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/queries.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/queries.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/queries.js
index 0195dc2..aec2273 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/queries.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/queries.js
@@ -21,7 +21,11 @@ import FilterableMixin from 'hive/mixins/filterable';
 import constants from 'hive/utils/constants';
 
 export default Ember.ArrayController.extend(FilterableMixin, {
-  needs: [ constants.namingConventions.routes.history ],
+  needs: [ constants.namingConventions.routes.history,
+           constants.namingConventions.openQueries ],
+
+  history: Ember.computed.alias('controllers.' + constants.namingConventions.routes.history),
+  openQueries: Ember.computed.alias('controllers.' + constants.namingConventions.openQueries),
 
   sortAscending: true,
   sortProperties: [],
@@ -68,9 +72,11 @@ export default Ember.ArrayController.extend(FilterableMixin, {
 
   actions: {
     executeAction: function (action, savedQuery) {
+      var self = this;
+
       switch (action) {
         case "buttons.history":
-          this.get('controllers.' + constants.namingConventions.routes.history).filterBy('queryId', savedQuery.get('id'), true);
+          this.get('history').filterBy('queryId', savedQuery.get('id'), true);
           this.transitionToRoute(constants.namingConventions.routes.history);
           break;
         case "buttons.delete":
@@ -85,6 +91,7 @@ export default Ember.ArrayController.extend(FilterableMixin, {
 
           defer.promise.then(function () {
             savedQuery.destroyRecord();
+            self.get('openQueries').updatedDeletedQueryTab(savedQuery);
           });
 
           break;

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/settings.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/settings.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/settings.js
index 6ac0828..ddc5b1e 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/settings.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/settings.js
@@ -27,9 +27,22 @@ export default Ember.ArrayController.extend({
 
   index: Ember.computed.alias('controllers.' + constants.namingConventions.index),
   openQueries: Ember.computed.alias('controllers.' + constants.namingConventions.openQueries),
+  sessionTag: Ember.computed.alias('index.model.sessionTag'),
+  sessionActive: Ember.computed.alias('index.model.sessionActive'),
+
+  canInvalidateSession: Ember.computed.and('sessionTag', 'sessionActive'),
 
   predefinedSettings: constants.hiveParameters,
 
+  selectedSettings: function() {
+    var predefined = this.get('predefinedSettings');
+    var current = this.get('currentSettings.settings');
+
+    return predefined.filter(function(setting) {
+      return current.findBy('key.name', setting.name);
+    });
+  }.property('currentSettings.settings.@each.key'),
+
   currentSettings: function () {
     var currentId = this.get('index.model.id');
     var targetSettings = this.findBy('id', currentId);
@@ -150,9 +163,14 @@ export default Ember.ArrayController.extend({
         return;
       }
 
+      if (!predefined.validate) {
+        setting.set('valid', true);
+        return;
+      }
+
       setting.set('valid', false);
     });
-  }.observes('currentSettings.[]', 'currentSettings.settings.@each.value', 'currentSettings.settings.@each.key'),
+  }.observes('currentSettings.[]', 'currentSettings.settings.[]', 'currentSettings.settings.@each.value', 'currentSettings.settings.@each.key'),
 
   currentSettingsAreValid: function() {
     var currentSettings = this.get('currentSettings.settings');
@@ -161,6 +179,24 @@ export default Ember.ArrayController.extend({
     return invalid.length ? false : true;
   }.property('currentSettings.settings.@each.value', 'currentSettings.settings.@each.key'),
 
+  loadSessionStatus: function() {
+    var model         = this.get('index.model');
+    var sessionActive = this.get('sessionActive');
+    var sessionTag    = this.get('sessionTag');
+    var adapter       = this.container.lookup('adapter:application');
+    var url           = adapter.buildURL() + '/jobs/sessions/' + sessionTag;
+
+    if (sessionTag && sessionActive === undefined) {
+      adapter.ajax(url, 'GET')
+        .then(function(response) {
+          model.set('sessionActive', response.session.actual);
+        })
+        .catch(function() {
+          model.set('sessionActive', false);
+        });
+    }
+  }.observes('index.model', 'index.model.status'),
+
   actions: {
     add: function () {
       var currentId = this.get('index.model.id'),
@@ -185,6 +221,31 @@ export default Ember.ArrayController.extend({
       });
 
       this.get('currentSettings.settings').findBy('key', null).set('key', newKey);
+    },
+
+    removeAll: function() {
+      var currentId = this.get('index.model.id'),
+          querySettings = this.findBy('id', currentId);
+
+      querySettings.set('settings', []);
+    },
+
+    invalidateSession: function() {
+      var self       = this;
+      var sessionTag = this.get('sessionTag');
+      var adapter    = this.container.lookup('adapter:application');
+      var url        = adapter.buildURL() + '/jobs/sessions/' + sessionTag;
+      var model = this.get('index.model');
+
+      // @TODO: Split this into then/catch once the BE is fixed
+      adapter.ajax(url, 'DELETE').catch(function(response) {
+        if ([200, 404].contains(response.status)) {
+          model.set('sessionActive', false);
+          self.notify.success('alerts.success.sessions.deleted');
+        } else {
+          self.notify.error(response.responseJSON.message, response.responseJSON.trace);
+        }
+      });
     }
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udf.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udf.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udf.js
index 657743c..3f8d3ed 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udf.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udf.js
@@ -50,8 +50,8 @@ export default Ember.ObjectController.extend({
           this.send('openModal',
                     'modal-delete',
                      {
-                        heading: Ember.I18n.translations.modals.delete.heading,
-                        text: Ember.I18n.translations.modals.delete.message,
+                        heading: 'modals.delete.heading',
+                        text: 'modals.delete.message',
                         defer: defer
                      });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/all-uppercase.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/all-uppercase.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/all-uppercase.js
index e5ea321..f068ed0 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/all-uppercase.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/all-uppercase.js
@@ -19,7 +19,7 @@
 import Ember from 'ember';
 
 export function allUppercase(input) {
-  return input.toUpperCase();
+  return input ? input.toUpperCase() : input;
 };
 
 export default Ember.Handlebars.makeBoundHelper(allUppercase);

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/preformatted-string.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/preformatted-string.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/preformatted-string.js
new file mode 100644
index 0000000..bb3d006
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/preformatted-string.js
@@ -0,0 +1,28 @@
+/**
+* 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 Ember from 'ember';
+
+export function preformattedString(string) {
+  string = string.replace(/\\n/g, '&#10;'); // newline
+  string = string.replace(/\\t/g, '&#09;'); // tabs
+  string = string.replace(/^\s+|\s+$/g, ''); // trim
+
+  return new Ember.Handlebars.SafeString(string);
+}
+
+export default Ember.Handlebars.makeBoundHelper(preformattedString);

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/index.html
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/index.html b/contrib/views/hive/src/main/resources/ui/hive-web/app/index.html
index 4f609bd..2cbf9f0 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/index.html
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/index.html
@@ -28,11 +28,15 @@
 
     <link rel="stylesheet" href="assets/vendor.css">
     <link rel="stylesheet" href="assets/hive.css">
+
+    {{content-for 'head-footer'}}
   </head>
   <body>
     {{content-for 'body'}}
 
     <script src="assets/vendor.js"></script>
     <script src="assets/hive.js"></script>
+
+    {{content-for 'body-footer'}}
   </body>
 </html>

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/i18n.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/i18n.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/i18n.js
index 8d60248..b637c5e 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/i18n.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/i18n.js
@@ -28,9 +28,13 @@ export default {
     Ember.I18n.translations = TRANSLATIONS;
     Ember.TextField.reopen(Ember.I18n.TranslateableAttributes);
   }
-};
+}
 
 TRANSLATIONS = {
+  tooltips: {
+    refresh: 'Refresh database',
+    loadSample: 'Load sample data'
+  },
   alerts: {
     errors: {
       save: {
@@ -40,6 +44,17 @@ TRANSLATIONS = {
       get: {
         tables: 'Error when trying to retrieve the tables for the selected database',
         columns: 'Error when trying to retrieve the table columns.'
+      },
+      sessions: {
+        delete: 'Error invalidating sessions'
+      },
+      job: {
+        status: "An error occured while processing the job."
+      }
+    },
+    success: {
+      sessions: {
+        deleted: 'Session invalidated'
       }
     }
   },
@@ -53,7 +68,12 @@ TRANSLATIONS = {
     save: {
       heading: 'Saving item',
       saveBeforeCloseHeading: "Save item before closing?",
-      message: 'Enter name:'
+      message: 'Enter name:',
+      overwrite: 'Saving will overwrite previously saved query'
+    },
+
+    download: {
+      csv: 'Download results as CSV'
     }
   },
   titles: {
@@ -62,13 +82,15 @@ TRANSLATIONS = {
     results: 'Search Results',
     settings: 'Database Settings',
     query: {
+      tab: 'Worksheet',
       editor: 'Query Editor',
       process: 'Query Process Results',
       parameters: 'Parameters',
       visualExplain: 'Visual Explain',
       tez: 'TEZ'
     },
-    download: 'Save results...'
+    download: 'Save results...',
+    tableSample: '{{tableName}} sample'
   },
   placeholders: {
     search: {
@@ -130,7 +152,7 @@ TRANSLATIONS = {
     explain: 'Explain',
     saveAs: 'Save as...',
     save: 'Save',
-    newQuery: 'New Query',
+    newQuery: 'New Worksheet',
     newUdf: 'New UDF',
     history: 'History',
     ok: 'OK',
@@ -147,9 +169,13 @@ TRANSLATIONS = {
     runOnTez: 'Run on Tez'
   },
   labels: {
-    noTablesMatches: 'No tables matches for'
+    noTablesMatch: 'No tables match',
+    table: 'Table '
   },
   popover: {
+    visualExplain: {
+      statistics: "Statistics"
+    },
     queryEditorHelp: {
       title: "Did you know?",
       content: {
@@ -163,7 +189,10 @@ TRANSLATIONS = {
   tez: {
     errors: {
       'not.deployed': "Tez View isn't deployed.",
-      'no.instance': "No instance of Tez View found."
+      'no.instance': "No instance of Tez View found.",
+      'no.dag': "No DAG available"
     }
-  }
+  },
+
+  generalError: 'Unexpected error'
 };

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/notify.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/notify.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/notify.js
new file mode 100644
index 0000000..be9c359
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/notify.js
@@ -0,0 +1,26 @@
+/**
+* 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.
+*/
+export default {
+  name: 'notify',
+  initialize: function(container, app) {
+    app.inject('route', 'notify', 'service:notify');
+    app.inject('controller', 'notify', 'service:notify');
+    app.inject('component', 'notify', 'service:notify');
+    app.inject('views', 'notify', 'service:notify');
+  }
+};

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file.js
index f8f1b9b..c13d4e1 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file.js
@@ -21,6 +21,6 @@ import DS from 'ember-data';
 export default DS.Model.extend({
   fileContent: DS.attr(),
   hasNext: DS.attr(),
-  page: DS.attr,
+  page: DS.attr('number'),
   pageCount: DS.attr()
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/models/job.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/models/job.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/job.js
index 06bdc2e..472f824 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/models/job.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/job.js
@@ -26,12 +26,17 @@ export default DS.Model.extend({
   dataBase: DS.attr('string'),
   duration: DS.attr(),
   status: DS.attr('string'),
+  statusMessage: DS.attr('string'),
   dateSubmitted: DS.attr('date'),
   forcedContent: DS.attr('string'),
   logFile: DS.attr('string'),
   dagName:  DS.attr('string'),
   dagId: DS.attr('string'),
   sessionTag: DS.attr('string'),
+  page: DS.attr(),
+  statusDir: DS.attr('string'),
+  applicationId: DS.attr(),
+  confFile: DS.attr('string'),
 
   dateSubmittedTimestamp: function () {
     var date = this.get('dateSubmitted');

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/application.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/application.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/application.js
index 2f9a5ae..bf413a3 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/application.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/application.js
@@ -33,6 +33,8 @@ export default Ember.Route.extend({
   actions: {
     openModal: function (modalTemplate, options) {
       this.controllerFor(modalTemplate).setProperties({
+        content: options.content || {},
+        message: options.message,
         heading: options.heading,
         text: options.text,
         defer: options.defer
@@ -51,11 +53,16 @@ export default Ember.Route.extend({
       });
     },
 
-    addAlert: function (type, message, title) {
-      this.controllerFor(constants.namingConventions.alerts).pushObject({
-        type: type,
-        title: title,
-        content: message
+    openOverlay: function(overlay) {
+      return this.render(overlay.template, {
+        outlet: overlay.outlet,
+        into: overlay.into
+      });
+    },
+    closeOverlay: function(overlay) {
+      return this.disconnectOutlet({
+        outlet: overlay.outlet,
+        parentView: overlay.into
       });
     }
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/index.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/index.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/index.js
index f2be946..120a102 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/index.js
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/index.js
@@ -23,14 +23,14 @@ export default Ember.Route.extend({
   beforeModel: function () {
     var model = this.controllerFor(constants.namingConventions.routes.index).get('model');
 
-    if (model) {
+    if (model && !model.get('isDeleted')) {
       if (model.get('constructor.typeKey') === constants.namingConventions.job) {
         this.transitionTo(constants.namingConventions.subroutes.historyQuery, model);
       } else {
         this.transitionTo(constants.namingConventions.subroutes.savedQuery, model);
       }
     } else {
-      this.controllerFor(constants.namingConventions.routes.index).send('addQuery');
+      this.controllerFor(constants.namingConventions.openQueries).navigateToLastTab();
     }
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/services/notify.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/services/notify.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/services/notify.js
new file mode 100644
index 0000000..fbd50cd
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/services/notify.js
@@ -0,0 +1,88 @@
+/**
+* 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.Service.extend({
+  types: constants.notify,
+
+  messages      : Ember.ArrayProxy.create({ content : [] }),
+  notifications : Ember.ArrayProxy.create({ content : [] }),
+
+  add: function(type, message, body) {
+    var formattedBody = this.formatMessageBody(body);
+
+    var notification = Ember.Object.create({
+      type    : type,
+      message : message,
+      body    : formattedBody
+    });
+
+    this.messages.pushObject(notification);
+    this.notifications.pushObject(notification);
+  },
+
+  info: function(message, body) {
+    this.add(this.types.INFO, message, body);
+  },
+
+  warn: function(message, body) {
+    this.add(this.types.WARN, message, body);
+  },
+
+  error: function(message, body) {
+    this.add(this.types.ERROR, message, body);
+  },
+
+  success: function(message, body) {
+    this.add(this.types.SUCCESS, message, body);
+  },
+
+  formatMessageBody: function(body) {
+    if (!body) {
+      return;
+    }
+
+    if (typeof body === "string") {
+      return body;
+    }
+
+    if (typeof body === "object") {
+      var formattedBody = "";
+      for (var key in body) {
+        formattedBody += "\n\n%@:\n%@".fmt(key, body[key]);
+      }
+
+      return formattedBody;
+    }
+  },
+
+  removeMessage: function(message) {
+    this.messages.removeObject(message);
+    this.notifications.removeObject(message);
+  },
+
+  removeNotification: function(notification) {
+    this.notifications.removeObject(notification);
+  },
+
+  removeAllMessages: function() {
+    this.messages.removeAt(0, this.messages.get('length'));
+  }
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/app.scss
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/app.scss b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/app.scss
index 3f49713..7fdf096 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/app.scss
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/app.scss
@@ -16,11 +16,15 @@
  * limitations under the License.
  */
 
+@import 'vars';
 @import 'dropdown-submenu';
+@import 'mixins';
+@import 'notifications';
+@import 'query-tabs';
 
-$panel-background: #f5f5f5;
-$placeholder-color: #aaa;
-$border-color: #ddd;
+a {
+  word-wrap: break-word;
+}
 
 @-webkit-keyframes fadeIn {
   0% {opacity: 0;}
@@ -60,6 +64,10 @@ $border-color: #ddd;
   display: flex;
 }
 
+#visual-explain {
+  white-space: nowrap;
+}
+
 #visual-explain, #tez-ui {
   position: absolute;
   left: 0;
@@ -68,13 +76,6 @@ $border-color: #ddd;
   background: white;
 }
 
-#alerts-container {
-  position: absolute;
-  left: 15px;
-  right: 15px;
-  z-index: 1100;
-}
-
 aside  hr {
   margin: 10px 0;
 }
@@ -181,21 +182,10 @@ dropdown .fa-remove {
 }
 
 .main-content {
+  width: 90%;
   flex-grow: 1;
 }
 
-.query-menu {
-  margin-top: 57px;
-
-  span, popover {
-    cursor: pointer;
-    overflow: hidden;
-    display: block;
-    border-bottom: 1px solid $border-color;
-    padding: 10px;
-  }
-}
-
 .queries-icon {
   font-size: 20px;
 
@@ -209,11 +199,23 @@ dropdown .fa-remove {
   }
 }
 
+.query-context-tab {
+  background: #f1f1f1;
+
+  &.active {
+    background: white;
+  }
+}
+
 .alert {
   margin-bottom: 5px;
   padding-bottom: 10px;
   padding-top: 10px;
 
+  strong {
+    text-decoration: underline;
+  }
+
   .alert-message {
     max-height: 250px;
     overflow-y: auto;
@@ -317,22 +319,8 @@ body {
 }
 
 .settings-container {
-  width: 100%;
-  overflow-y: scroll;
-  height: calc(100% - 41px);
-  top: 41px;
-  left: 0;
-  background-color: #fff;
-  position: absolute;
-  padding: 0 15px;
-  z-index: 1000;
-
-  border: 1px solid $border-color;
-  -webkit-animation-duration: .5s;
-          animation-duration: .5s;
-  -webkit-animation-fill-mode: both;
-          animation-fill-mode: both;
 }
+
 .settings-container .close-settings {
   float: right;
   font-size: 18px;
@@ -377,3 +365,77 @@ tree-view ul li {
   height: 822px;
   border: none;
 }
+
+.edge {
+  text-align: center;
+  font-size: 10px;
+  font-weight: 800;
+
+  .edge-path {
+    height: 2px;
+    background-color: #dedede;
+    position: absolute;
+  }
+
+  .edge-arrow {
+    position: absolute;
+    width: 0;
+    height: 0;
+    border-top: 5px solid transparent;
+    border-bottom: 5px solid transparent;
+    border-right: 10px solid black;
+  }
+}
+
+.nodes {
+  width: 100%;
+  position: relative;
+
+  .node-container {
+    text-align: center;
+
+    .node {
+      border: 1px solid #bbb;
+      background: #fefefe;
+      font-size: 12px;
+      box-sizing: border-box;
+      text-align: left;
+      max-width: 200px;
+      margin: 0 25px 100px 0;
+      display: inline-block;
+      vertical-align: top;
+
+      @include box-shadow(1px, 1px, 15px, #888888);
+
+      &.table-node, &.output-node {
+        background-color: ghostwhite;
+        color: gray;
+        padding: 5px;
+        text-align: center;
+        min-width: 100px;
+        line-height: 8px;
+        vertical-align: bottom;
+        margin-bottom: 50px;
+      }
+
+      .node-heading {
+        padding: 5px;
+        text-align: center;
+        background-color: lightslategrey;
+        color: white;
+      }
+
+      .node-content {
+        max-height: 300px;
+        white-space: normal;
+        padding: 5px;
+        overflow-y: auto;
+        overflow-x: hidden;
+
+        .fa {
+          color: green;
+        }
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/mixins.scss
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/mixins.scss b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/mixins.scss
new file mode 100644
index 0000000..95e4ae8
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/mixins.scss
@@ -0,0 +1,23 @@
+/**
+ * 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.
+ */
+
+@mixin box-shadow($horizontal, $vertical, $blur, $color) {
+  -webkit-box-shadow: $horizontal $vertical $blur $color;
+     -moz-box-shadow: $horizontal $vertical $blur $color;
+          box-shadow: $horizontal $vertical $blur $color;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/notifications.scss
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/notifications.scss b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/notifications.scss
new file mode 100644
index 0000000..c676e4e
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/notifications.scss
@@ -0,0 +1,36 @@
+/**
+* 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.
+*/
+.notifications-container {
+  position: absolute;
+  top: 4px;
+  right: 20px;
+  width: 600px;
+  z-index: 9999;
+}
+
+.notification > .fa {
+  width: 15px;
+  text-align: center;
+  margin-right: 10px;
+}
+
+.notifications-container .notification {
+  word-wrap: break-word;
+  max-height: 200px;
+  overflow-x: auto;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/query-tabs.scss
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/query-tabs.scss b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/query-tabs.scss
new file mode 100644
index 0000000..d23a751
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/query-tabs.scss
@@ -0,0 +1,68 @@
+/**
+* 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.
+*/
+.query-menu {
+  margin-top: 57px;
+
+  span, popover {
+    cursor: pointer;
+    display: block;
+    border-bottom: 1px solid $border-color;
+    padding: 10px;
+  }
+}
+
+.fa.panel-action-icon {
+  line-height: 22px;
+  font-size: 16px;
+}
+
+.editor-overlay {
+  width: 100%;
+  overflow-y: scroll;
+  height: calc(100% - 41px);
+  top: 41px;
+  left: 0;
+  background-color: #fff;
+  position: absolute;
+  padding: 0 15px;
+  z-index: 1000;
+
+  border: 1px solid $border-color;
+  -webkit-animation-duration: .5s;
+          animation-duration: .5s;
+  -webkit-animation-fill-mode: both;
+          animation-fill-mode: both;
+}
+
+.message-body {
+  margin-top: 10px;
+}
+
+.query-menu-tab {
+  position: relative;
+}
+.query-menu-tab .badge {
+  position: absolute;
+  top: -4px;
+  left: -4px;
+  background-color: red;
+  color: #fff;
+  padding: 2px 4px;
+  font-weight: bold;
+  z-index: 9999;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/vars.scss
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/vars.scss b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/vars.scss
new file mode 100644
index 0000000..eec2277
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/vars.scss
@@ -0,0 +1,20 @@
+/**
+* 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.
+*/
+$panel-background: #f5f5f5;
+$placeholder-color: #aaa;
+$border-color: #ddd;

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/alerts.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/alerts.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/alerts.hbs
deleted file mode 100644
index 0fbf34a..0000000
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/alerts.hbs
+++ /dev/null
@@ -1,23 +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.
-}}
-
-<div id="alerts-container">
-  {{#each alert in this}}
-    {{alert-message-widget message=alert removeMessage="remove" removeLater="removeLater"}}
-  {{/each}}
-</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/application.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/application.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/application.hbs
index 055a87b..99662dd 100644
--- a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/application.hbs
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/application.hbs
@@ -16,10 +16,11 @@
 * limitations under the License.
 }}
 
+{{notify-widget}}
 {{render 'navbar'}}
 
 <div id="content">
   {{outlet}}
 
   {{outlet "modal"}}
-</div>
\ No newline at end of file
+</div>


[5/5] ambari git commit: AMBARI-10528. Hive View: Visual Explain, Error handling and bugfixes (alexantonenko)

Posted by al...@apache.org.
AMBARI-10528. Hive View: Visual Explain, Error handling and bugfixes (alexantonenko)


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

Branch: refs/heads/trunk
Commit: 672eee34dce8200f7375f164c307d864b4f5ffd1
Parents: 2917d0d
Author: Alex Antonenko <hi...@gmail.com>
Authored: Fri Apr 17 18:39:50 2015 +0300
Committer: Alex Antonenko <hi...@gmail.com>
Committed: Fri Apr 17 22:10:24 2015 +0300

----------------------------------------------------------------------
 contrib/views/hive/pom.xml                      |  11 +-
 .../ambari/view/hive/PropertyValidator.java     | 109 +++++
 .../ambari/view/hive/client/Connection.java     |  44 +-
 .../view/hive/client/ConnectionFactory.java     |   7 +-
 .../apache/ambari/view/hive/client/Cursor.java  |  22 +-
 .../ambari/view/hive/client/DDLDelegator.java   |   2 +-
 .../hive/client/HiveErrorStatusException.java   |   2 +-
 .../apache/ambari/view/hive/client/Utils.java   |   2 +-
 .../view/hive/persistence/DataStoreStorage.java | 114 +++---
 .../persistence/InstanceKeyValueStorage.java    |   5 -
 .../hive/persistence/LocalKeyValueStorage.java  |   5 -
 .../ambari/view/hive/persistence/Storage.java   |   9 -
 .../hive/resources/CRUDResourceManager.java     |   2 +-
 .../view/hive/resources/files/FileService.java  |  36 +-
 .../view/hive/resources/jobs/Aggregator.java    |   3 +-
 .../resources/jobs/ConnectionController.java    |  15 +-
 .../view/hive/resources/jobs/JobService.java    |  74 +++-
 .../jobs/NoOperationStatusSetException.java     |   5 +-
 .../jobs/OperationHandleController.java         |  45 ++-
 .../jobs/OperationHandleResourceManager.java    |   2 +-
 .../jobs/ResultsPaginationController.java       |  24 ++
 .../resources/jobs/StoredOperationHandle.java   |   2 +-
 .../hive/resources/jobs/atsJobs/ATSParser.java  |   1 +
 .../jobs/atsJobs/ATSRequestsDelegate.java       |   8 +
 .../jobs/atsJobs/ATSRequestsDelegateImpl.java   |  24 +-
 .../resources/jobs/atsJobs/HiveQueryId.java     |   2 +
 .../view/hive/resources/jobs/viewJobs/Job.java  |   8 +
 .../resources/jobs/viewJobs/JobController.java  |   1 +
 .../jobs/viewJobs/JobControllerImpl.java        |  39 +-
 .../hive/resources/jobs/viewJobs/JobImpl.java   |  28 +-
 .../ambari/view/hive/resources/udfs/UDF.java    |   6 +-
 .../apache/ambari/view/hive/utils/HdfsApi.java  |   6 +-
 .../apache/ambari/view/hive/utils/HdfsUtil.java |   8 +-
 .../utils/HiveClientFormattedException.java     |  26 ++
 .../src/main/resources/ui/hive-web/.bowerrc     |   4 +
 .../main/resources/ui/hive-web/.editorconfig    |  34 ++
 .../src/main/resources/ui/hive-web/.ember-cli   |  27 ++
 .../src/main/resources/ui/hive-web/.travis.yml  |  38 ++
 .../src/main/resources/ui/hive-web/Brocfile.js  |   5 +
 .../app/components/alert-message-widget.js      |  16 +-
 .../app/components/collapsible-widget.js        |   5 +
 .../ui/hive-web/app/components/modal-widget.js  |  22 +
 .../ui/hive-web/app/components/notify-widget.js |  32 ++
 .../app/components/number-range-widget.js       |  36 +-
 .../ui/hive-web/app/components/query-tabs.js    | 121 ++++++
 .../hive-web/app/components/typeahead-widget.js |  49 ++-
 .../ui/hive-web/app/controllers/alerts.js       |  47 ---
 .../ui/hive-web/app/controllers/databases.js    |  56 ++-
 .../ui/hive-web/app/controllers/index.js        | 188 +++++++--
 .../controllers/index/history-query/explain.js  |  36 +-
 .../app/controllers/index/history-query/logs.js |  22 +-
 .../controllers/index/history-query/results.js  |  34 ++
 .../ui/hive-web/app/controllers/messages.js     |  33 ++
 .../app/controllers/modal-save-query.js         |  42 ++
 .../ui/hive-web/app/controllers/open-queries.js | 120 ++++--
 .../ui/hive-web/app/controllers/queries.js      |  11 +-
 .../ui/hive-web/app/controllers/settings.js     |  63 ++-
 .../ui/hive-web/app/controllers/udf.js          |   4 +-
 .../ui/hive-web/app/helpers/all-uppercase.js    |   2 +-
 .../hive-web/app/helpers/preformatted-string.js |  28 ++
 .../main/resources/ui/hive-web/app/index.html   |   4 +
 .../ui/hive-web/app/initializers/i18n.js        |  43 +-
 .../ui/hive-web/app/initializers/notify.js      |  26 ++
 .../resources/ui/hive-web/app/models/file.js    |   2 +-
 .../resources/ui/hive-web/app/models/job.js     |   5 +
 .../ui/hive-web/app/routes/application.js       |  17 +-
 .../ui/hive-web/app/routes/index/index.js       |   4 +-
 .../ui/hive-web/app/services/notify.js          |  88 ++++
 .../resources/ui/hive-web/app/styles/app.scss   | 136 +++++--
 .../ui/hive-web/app/styles/mixins.scss          |  23 ++
 .../ui/hive-web/app/styles/notifications.scss   |  36 ++
 .../ui/hive-web/app/styles/query-tabs.scss      |  68 ++++
 .../resources/ui/hive-web/app/styles/vars.scss  |  20 +
 .../ui/hive-web/app/templates/alerts.hbs        |  23 --
 .../ui/hive-web/app/templates/application.hbs   |   3 +-
 .../components/alert-message-widget.hbs         |   4 +-
 .../templates/components/collapsible-widget.hbs |   7 +-
 .../app/templates/components/notify-widget.hbs  |  21 +
 .../app/templates/components/panel-widget.hbs   |  12 +-
 .../app/templates/components/query-tabs.hbs     |  29 ++
 .../app/templates/components/tabs-widget.hbs    |   3 +-
 .../app/templates/databases-search-results.hbs  |   4 +-
 .../hive-web/app/templates/databases-tree.hbs   |   6 +-
 .../ui/hive-web/app/templates/databases.hbs     |   4 +-
 .../ui/hive-web/app/templates/index.hbs         |  24 +-
 .../templates/index/history-query/results.hbs   |   6 +-
 .../ui/hive-web/app/templates/insert-udfs.hbs   |  54 +--
 .../ui/hive-web/app/templates/message.hbs       |  36 ++
 .../ui/hive-web/app/templates/messages.hbs      |  30 ++
 .../ui/hive-web/app/templates/modal-delete.hbs  |   2 +-
 .../hive-web/app/templates/modal-save-query.hbs |  24 ++
 .../ui/hive-web/app/templates/notification.hbs  |  23 ++
 .../ui/hive-web/app/templates/open-queries.hbs  |   2 +-
 .../ui/hive-web/app/templates/settings.hbs      |  79 ++--
 .../ui/hive-web/app/templates/tez-ui.hbs        |   2 +
 .../hive-web/app/templates/visual-explain.hbs   |  58 +++
 .../ui/hive-web/app/utils/constants.js          |  23 +-
 .../ui/hive-web/app/utils/dag-rules.js          | 141 +++++++
 .../resources/ui/hive-web/app/views/message.js  |  36 ++
 .../ui/hive-web/app/views/notification.js       |  51 +++
 .../ui/hive-web/app/views/visual-explain.js     | 401 +++++++++++++++++++
 .../src/main/resources/ui/hive-web/bower.json   |  18 +-
 .../src/main/resources/ui/hive-web/package.json |  16 +-
 .../src/main/resources/ui/hive-web/testem.json  |   2 +-
 .../ui/hive-web/tests/helpers/api-mock.js       |   2 +-
 .../main/resources/ui/hive-web/tests/index.html |   6 +
 .../hive-web/tests/integration/database-test.js |   6 +-
 .../resources/ui/hive-web/tests/test-helper.js  |   6 -
 .../components/alert-message-widget-test.js     |  10 +-
 .../tests/unit/controllers/index-test.js        |  19 +-
 .../tests/unit/controllers/queries-test.js      |   5 +-
 .../tests/unit/controllers/settings-test.js     |  17 +-
 .../tests/unit/controllers/tez-ui-test.js       |   2 +-
 .../resources/ui/hive-web/vendor/dagre.min.js   |  27 ++
 contrib/views/hive/src/main/resources/view.xml  |   8 +-
 .../view/hive/resources/jobs/ATSParserTest.java |  22 +-
 .../hive/resources/jobs/AggregatorTest.java     |   2 +-
 pom.xml                                         |   1 +
 118 files changed, 2938 insertions(+), 583 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/pom.xml
----------------------------------------------------------------------
diff --git a/contrib/views/hive/pom.xml b/contrib/views/hive/pom.xml
index e381719..81bf144 100644
--- a/contrib/views/hive/pom.xml
+++ b/contrib/views/hive/pom.xml
@@ -19,7 +19,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.apache.ambari.contrib.views</groupId>
   <artifactId>hive</artifactId>
-  <version>0.1.0-SNAPSHOT</version>
+  <version>0.2.0-SNAPSHOT</version>
   <name>Hive</name>
 
   <parent>
@@ -30,6 +30,11 @@
 
   <dependencies>
     <dependency>
+      <groupId>com.jayway.jsonpath</groupId>
+      <artifactId>json-path</artifactId>
+      <version>2.0.0</version>
+    </dependency>
+      <dependency>
       <groupId>com.google.inject</groupId>
       <artifactId>guice</artifactId>
     </dependency>
@@ -195,8 +200,8 @@
         <artifactId>frontend-maven-plugin</artifactId>
         <version>0.0.14</version>
         <configuration>
-          <nodeVersion>v0.10.32</nodeVersion>
-          <npmVersion>1.4.3</npmVersion>
+          <nodeVersion>v0.12.2</nodeVersion>
+          <npmVersion>1.4.8</npmVersion>
           <workingDirectory>src/main/resources/ui/hive-web/</workingDirectory>
         </configuration>
         <executions>

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/PropertyValidator.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/PropertyValidator.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/PropertyValidator.java
new file mode 100644
index 0000000..61efa49
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/PropertyValidator.java
@@ -0,0 +1,109 @@
+/**
+ * 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.ambari.view.hive;
+
+import org.apache.ambari.view.ViewInstanceDefinition;
+import org.apache.ambari.view.validation.ValidationResult;
+import org.apache.ambari.view.validation.Validator;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public class PropertyValidator implements Validator {
+
+  public static final String WEBHDFS_URL = "webhdfs.url";
+  public static final String HIVE_PORT = "hive.port";
+  public static final String YARN_ATS_URL = "yarn.ats.url";
+  public static final String HIVE_AUTH = "hive.auth";
+
+  @Override
+  public ValidationResult validateInstance(ViewInstanceDefinition viewInstanceDefinition, ValidationContext validationContext) {
+    return null;
+  }
+
+  @Override
+  public ValidationResult validateProperty(String property, ViewInstanceDefinition viewInstanceDefinition, ValidationContext validationContext) {
+    if (property.equals(WEBHDFS_URL)) {
+      String webhdfsUrl = viewInstanceDefinition.getPropertyMap().get(WEBHDFS_URL);
+      if (validateURL(webhdfsUrl)) return new InvalidPropertyValidationResult(false, "Must be valid URL");
+    }
+
+    if (property.equals(HIVE_PORT)) {
+      String hivePort = viewInstanceDefinition.getPropertyMap().get(HIVE_PORT);
+      try {
+        int port = Integer.valueOf(hivePort);
+        if (port < 1 || port > 65535) {
+          return new InvalidPropertyValidationResult(false, "Must be from 1 to 65535");
+        }
+      } catch (NumberFormatException e) {
+        return new InvalidPropertyValidationResult(false, "Must be integer");
+      }
+    }
+
+    if (property.equals(YARN_ATS_URL)) {
+      String atsUrl = viewInstanceDefinition.getPropertyMap().get(YARN_ATS_URL);
+      if (validateURL(atsUrl)) return new InvalidPropertyValidationResult(false, "Must be valid URL");
+    }
+
+    if (property.equals(HIVE_AUTH)) {
+      String auth = viewInstanceDefinition.getPropertyMap().get(HIVE_AUTH);
+
+      if (auth != null && !auth.isEmpty()) {
+        for(String param : auth.split(";")) {
+          String[] keyvalue = param.split("=");
+          if (keyvalue.length != 2) {
+            return new InvalidPropertyValidationResult(false, "Can not parse authentication param " + param + " in " + auth);
+          }
+        }
+      }
+    }
+
+    return ValidationResult.SUCCESS;
+  }
+
+  public boolean validateURL(String webhdfsUrl) {
+    try {
+      new URI(webhdfsUrl);
+    } catch (URISyntaxException e) {
+      return true;
+    }
+    return false;
+  }
+
+  public static class InvalidPropertyValidationResult implements ValidationResult {
+    private boolean valid;
+    private String detail;
+
+    public InvalidPropertyValidationResult(boolean valid, String detail) {
+      this.valid = valid;
+      this.detail = detail;
+    }
+
+    @Override
+    public boolean isValid() {
+      return valid;
+    }
+
+    @Override
+    public String getDetail() {
+      return detail;
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Connection.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Connection.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Connection.java
index 70b5047..cf1a998 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Connection.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Connection.java
@@ -82,7 +82,7 @@ public class Connection {
       transport.open();
       client = new TCLIService.Client(new TBinaryProtocol(transport));
     } catch (TTransportException e) {
-      throw new HiveClientException("Could not establish connecton to "
+      throw new HiveClientException("H020 Could not establish connecton to "
           + host + ":" + port + ": " + e.toString(), e);
     }
     LOG.info("Hive connection opened");
@@ -109,7 +109,7 @@ public class Connection {
             try {
               saslQOP = SaslQOP.fromString(authParams.get(Utils.HiveAuthenticationParams.AUTH_QOP));
             } catch (IllegalArgumentException e) {
-              throw new HiveClientException("Invalid " + Utils.HiveAuthenticationParams.AUTH_QOP +
+              throw new HiveClientException("H040 Invalid " + Utils.HiveAuthenticationParams.AUTH_QOP +
                   " parameter. " + e.getMessage(), e);
             }
           }
@@ -162,7 +162,7 @@ public class Connection {
         return HiveAuthFactory.getSocketTransport(host, port, 10000);
       }
     } catch (SaslException e) {
-      throw new HiveClientException("Could not create secure connection to "
+      throw new HiveClientException("H040 Could not create secure connection to "
           + host + ": " + e.getMessage(), e);
     }
     return transport;
@@ -181,7 +181,7 @@ public class Connection {
         tokenStr = ShimLoader.getHadoopShims().
             getTokenStrForm(HiveAuthFactory.HS2_CLIENT_TOKEN);
       } catch (IOException e) {
-        throw new HiveClientException("Error reading token ", e);
+        throw new HiveClientException("H050 Error reading token", e);
       }
     }
     return tokenStr;
@@ -205,12 +205,12 @@ public class Connection {
         try {
           return client.OpenSession(openReq);
         } catch (TException e) {
-          throw new HiveClientException("Unable to open Hive session", e);
+          throw new HiveClientException("H060 Unable to open Hive session", e);
         }
 
       }
     }.call();
-    Utils.verifySuccess(openResp.getStatus(), "Unable to open Hive session");
+    Utils.verifySuccess(openResp.getStatus(), "H070 Unable to open Hive session");
 
     if (protocol == null)
       protocol = openResp.getServerProtocolVersion();
@@ -231,7 +231,7 @@ public class Connection {
   public TSessionHandle getSessionByTag(String tag) throws HiveClientException {
     TSessionHandle sessionHandle = sessHandles.get(tag);
     if (sessionHandle == null) {
-      throw new HiveClientException("Session with provided tag not found", null);
+      throw new HiveClientException("E030 Session with provided tag not found", null);
     }
     return sessionHandle;
   }
@@ -244,15 +244,21 @@ public class Connection {
     }
   }
 
+  public void invalidateSessionByTag(String tag) throws HiveClientException {
+    TSessionHandle sessionHandle = getSessionByTag(tag);
+    closeSession(sessionHandle);
+    sessHandles.remove(tag);
+  }
+
   private synchronized void closeSession(TSessionHandle sessHandle) throws HiveClientException {
     if (sessHandle == null) return;
     TCloseSessionReq closeReq = new TCloseSessionReq(sessHandle);
     TCloseSessionResp closeResp = null;
     try {
       closeResp = client.CloseSession(closeReq);
-      Utils.verifySuccess(closeResp.getStatus(), "Unable to close Hive session");
+      Utils.verifySuccess(closeResp.getStatus(), "H080 Unable to close Hive session");
     } catch (TException e) {
-      throw new HiveClientException("Unable to close Hive session", e);
+      throw new HiveClientException("H090 Unable to close Hive session", e);
     }
     LOG.info("Hive session closed");
   }
@@ -314,18 +320,18 @@ public class Connection {
           try {
             return client.ExecuteStatement(execReq);
           } catch (TException e) {
-            throw new HiveClientException("Unable to submit statement " + cmd, e);
+            throw new HiveClientException("H100 Unable to submit statement " + cmd, e);
           }
 
         }
       }.call();
 
-      Utils.verifySuccess(execResp.getStatus(), "Unable to submit statement " + cmd);
+      Utils.verifySuccess(execResp.getStatus(), "H110 Unable to submit statement");
       //TODO: check if status have results
       handle = execResp.getOperationHandle();
     }
     if (handle == null) {
-      throw new HiveClientException("Empty command given", null);
+      throw new HiveClientException("H120 Empty command given", null);
     }
     return handle;
   }
@@ -373,19 +379,11 @@ public class Connection {
         try {
           return client.GetOperationStatus(statusReq);
         } catch (TException e) {
-          throw new HiveClientException("Unable to fetch operation status", e);
+          throw new HiveClientException("H130 Unable to fetch operation status", e);
         }
 
       }
     }.call();
-//    transportLock.lock();
-//    try {
-//      return client.GetOperationStatus(statusReq);
-//    } catch (TException e) {
-//      throw new HiveClientException("Unable to fetch operation status", e);
-//    } finally {
-//      transportLock.unlock();
-//    }
   }
 
   /**
@@ -400,11 +398,11 @@ public class Connection {
         try {
           return client.CancelOperation(cancelReq);
         } catch (TException e) {
-          throw new HiveClientException("Unable to cancel operation", null);
+          throw new HiveClientException("H140 Unable to cancel operation", null);
         }
       }
     }.call();
-    Utils.verifySuccess(cancelResp.getStatus(), "Unable to cancel operation");
+    Utils.verifySuccess(cancelResp.getStatus(), "H150 Unable to cancel operation");
   }
 
   public int getPort() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ConnectionFactory.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ConnectionFactory.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ConnectionFactory.java
index 6886f57..98256eb 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ConnectionFactory.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ConnectionFactory.java
@@ -19,6 +19,7 @@
 package org.apache.ambari.view.hive.client;
 
 import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.utils.HiveClientFormattedException;
 import org.apache.ambari.view.hive.utils.ServiceFormattedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -41,7 +42,7 @@ public class ConnectionFactory implements IConnectionFactory {
       return new Connection(getHiveHost(), Integer.valueOf(getHivePort()),
           getHiveAuthParams(), context.getUsername());
     } catch (HiveClientException e) {
-      throw new ServiceFormattedException("Couldn't open connection to Hive: " + e.toString(), e);
+      throw new HiveClientFormattedException(e);
     }
   }
 
@@ -62,8 +63,8 @@ public class ConnectionFactory implements IConnectionFactory {
     for(String param : auth.split(";")) {
       String[] keyvalue = param.split("=");
       if (keyvalue.length != 2) {
-        LOG.error("Can not parse authentication param " + param + " in " + auth);
-        continue;
+        //Should never happen because validator already checked this
+        throw new ServiceFormattedException("H010 Can not parse authentication param " + param + " in " + auth);
       }
       params.put(keyvalue[0], keyvalue[1]);
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Cursor.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Cursor.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Cursor.java
index 84987f5..16fdf36 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Cursor.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Cursor.java
@@ -21,6 +21,7 @@ package org.apache.ambari.view.hive.client;
 import static org.apache.hive.service.cli.thrift.TCLIServiceConstants.TYPE_NAMES;
 
 import org.apache.ambari.view.hive.utils.BadRequestFormattedException;
+import org.apache.ambari.view.hive.utils.HiveClientFormattedException;
 import org.apache.hive.service.cli.RowSet;
 import org.apache.hive.service.cli.RowSetFactory;
 import org.apache.hive.service.cli.thrift.*;
@@ -73,12 +74,12 @@ public class Cursor implements Iterator<Row>, Iterable<Row> {
         try {
           return client.FetchResults(fetchReq);
         } catch (TException e) {
-          throw new HiveClientException("Unable to fetch results", e);
+          throw new HiveClientException("H160 Unable to fetch results", e);
         }
 
       }
     }.call();
-    Utils.verifySuccess(fetchResp.getStatus(), "Unable to fetch results");
+    Utils.verifySuccess(fetchResp.getStatus(), "H170 Unable to fetch results");
     TRowSet results = fetchResp.getResults();
     fetched = RowSetFactory.create(results, connection.getProtocol());
     fetchedIterator = fetched.iterator();
@@ -90,7 +91,6 @@ public class Cursor implements Iterator<Row>, Iterable<Row> {
 
   public ArrayList<ColumnDescription> getSchema() throws HiveClientException {
     if (this.schema == null) {
-      // TODO: extract all HiveCall inline classes to separate files
       TGetResultSetMetadataResp fetchResp = new HiveCall<TGetResultSetMetadataResp>(connection) {
         @Override
         public TGetResultSetMetadataResp body() throws HiveClientException {
@@ -99,12 +99,12 @@ public class Cursor implements Iterator<Row>, Iterable<Row> {
           try {
             return client.GetResultSetMetadata(fetchReq);
           } catch (TException e) {
-            throw new HiveClientException("Unable to fetch results metadata", e);
+            throw new HiveClientException("H180 Unable to fetch results metadata", e);
           }
 
         }
       }.call();
-      Utils.verifySuccess(fetchResp.getStatus(), "Unable to fetch results metadata");
+      Utils.verifySuccess(fetchResp.getStatus(), "H190 Unable to fetch results metadata");
       TTableSchema schema = fetchResp.getSchema();
 
       List<TColumnDesc> thriftColumns = schema.getColumns();
@@ -168,7 +168,7 @@ public class Cursor implements Iterator<Row>, Iterable<Row> {
       try {
         fetchNextBlock();
       } catch (HiveClientException e) {
-        throw new HiveClientRuntimeException(e.getMessage(), e);
+        throw new HiveClientFormattedException(e);
       }
     }
   }
@@ -209,6 +209,16 @@ public class Cursor implements Iterator<Row>, Iterable<Row> {
     return read;
   }
 
+  public Row getHeadersRow() throws HiveClientException {
+    ArrayList<ColumnDescription> schema = getSchema();
+
+    Object[] row = new Object[schema.size()];
+    for (ColumnDescription columnDescription : schema) {
+      row[columnDescription.getPosition()-1] = columnDescription.getName();
+    }
+    return new Row(row, selectedColumns);
+  }
+
   public int readRaw(ArrayList<Object[]> rows, int count) {
     int read = 0;
     while(read < count && hasNext()) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/DDLDelegator.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/DDLDelegator.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/DDLDelegator.java
index 8326e50..eda12ed 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/DDLDelegator.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/DDLDelegator.java
@@ -129,7 +129,7 @@ public class DDLDelegator {
         try {
           return connection.getClient().GetColumns(req);
         } catch (TException e) {
-          throw new HiveClientException("Unable to get table columns", e);
+          throw new HiveClientException("H200 Unable to get table columns", e);
         }
       }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveErrorStatusException.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveErrorStatusException.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveErrorStatusException.java
index 7adbe23..1b306dc 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveErrorStatusException.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveErrorStatusException.java
@@ -25,6 +25,6 @@ import org.apache.hive.service.cli.thrift.TStatusCode;
  */
 public class HiveErrorStatusException extends HiveClientException {
   public HiveErrorStatusException(TStatusCode statusCode, String comment) {
-    super(String.format("Failed with status %s: %s", statusCode, comment), null);
+    super(String.format("%s [%s]", comment, statusCode), null);
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Utils.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Utils.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Utils.java
index fd1ecb5..e0dd438 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Utils.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Utils.java
@@ -26,7 +26,7 @@ public class Utils {
     if (status.getStatusCode() != TStatusCode.SUCCESS_STATUS &&
         status.getStatusCode() != TStatusCode.SUCCESS_WITH_INFO_STATUS) {
       String message = (status.getErrorMessage() != null) ? status.getErrorMessage() : "";
-      throw new HiveErrorStatusException(status.getStatusCode(), message + ": " + comment);
+      throw new HiveErrorStatusException(status.getStatusCode(), comment + ". " + message);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/DataStoreStorage.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/DataStoreStorage.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/DataStoreStorage.java
index b4bc415..1e8f07f 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/DataStoreStorage.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/DataStoreStorage.java
@@ -25,10 +25,14 @@ import org.apache.ambari.view.hive.persistence.utils.Indexed;
 import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
 import org.apache.ambari.view.hive.persistence.utils.OnlyOwnersFilteringStrategy;
 import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+import org.apache.commons.beanutils.BeanUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.ws.rs.WebApplicationException;
+import java.beans.Transient;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
@@ -52,14 +56,52 @@ public class DataStoreStorage implements Storage {
 
   @Override
   public synchronized void store(Class model, Indexed obj) {
+    assignId(model, obj);
+
+    Indexed newBean;
     try {
-      if (obj.getId() == null) {
-        String id = nextIdForEntity(context, model);
-        obj.setId(id);
-      }
-      context.getDataStore().store(obj);
+      newBean = (Indexed) BeanUtils.cloneBean(obj);
+    } catch (IllegalAccessException e) {
+      throw new ServiceFormattedException("S010 Data storage error", e);
+    } catch (InstantiationException e) {
+      throw new ServiceFormattedException("S010 Data storage error", e);
+    } catch (InvocationTargetException e) {
+      throw new ServiceFormattedException("S010 Data storage error", e);
+    } catch (NoSuchMethodException e) {
+      throw new ServiceFormattedException("S010 Data storage error", e);
+    }
+    preprocessEntity(newBean);
+
+    try {
+      context.getDataStore().store(newBean);
     } catch (PersistenceException e) {
-      throw new ServiceFormattedException("Error while saving object to DataStorage", e);
+      throw new ServiceFormattedException("S020 Data storage error", e);
+    }
+  }
+
+  public void assignId(Class model, Indexed obj) {
+    if (obj.getId() == null) {
+      String id = nextIdForEntity(context, model);
+      obj.setId(id);
+    }
+  }
+
+  private void preprocessEntity(Indexed obj) {
+    cleanTransientFields(obj);
+  }
+
+  private void cleanTransientFields(Indexed obj) {
+    for (Method m : obj.getClass().getMethods()) {
+      Transient aTransient = m.getAnnotation(Transient.class);
+      if (aTransient != null && m.getName().startsWith("set")) {
+        try {
+          m.invoke(obj, new Object[]{ null });
+        } catch (IllegalAccessException e) {
+          throw new ServiceFormattedException("S030 Data storage error", e);
+        } catch (InvocationTargetException e) {
+          throw new ServiceFormattedException("S030 Data storage error", e);
+        }
+      }
     }
   }
 
@@ -87,7 +129,7 @@ public class DataStoreStorage implements Storage {
         throw new ItemNotFound();
       }
     } catch (PersistenceException e) {
-      throw new ServiceFormattedException("Error while finding object in DataStorage", e);
+      throw new ServiceFormattedException("S040 Data storage error", e);
     }
   }
 
@@ -96,27 +138,16 @@ public class DataStoreStorage implements Storage {
     LinkedList<T> list = new LinkedList<T>();
     LOG.debug(String.format("Loading all %s-s", model.getName()));
     try {
-      //TODO: use WHERE statement instead of this ugly filter
       for(T item: context.getDataStore().findAll(model, filter.whereStatement())) {
         list.add(item);
       }
     } catch (PersistenceException e) {
-      throw new ServiceFormattedException("Error while finding all objects in DataStorage", e);
+      throw new ServiceFormattedException("S050 Data storage error", e);
     }
     return list;
   }
 
   @Override
-  public <T extends Indexed> List<T> loadWhere(Class<T> model, String where) {
-    LOG.debug(String.format("Loading all %s-s", model.getName()));
-    try {
-      return new ArrayList<T>(context.getDataStore().findAll(model, where));
-    } catch (PersistenceException e) {
-      throw new ServiceFormattedException("Error while finding objects in DataStorage; where = " + where, e);
-    }
-  }
-
-  @Override
   public synchronized <T extends Indexed> List<T> loadAll(Class<T> model) {
     return loadAll(model, new OnlyOwnersFilteringStrategy(this.context.getUsername()));
   }
@@ -128,7 +159,7 @@ public class DataStoreStorage implements Storage {
     try {
       context.getDataStore().remove(obj);
     } catch (PersistenceException e) {
-      throw new ServiceFormattedException("Error while removing object from DataStorage", e);
+      throw new ServiceFormattedException("S060 Data storage error", e);
     }
   }
 
@@ -137,48 +168,7 @@ public class DataStoreStorage implements Storage {
     try {
       return context.getDataStore().find(model, id) != null;
     } catch (PersistenceException e) {
-      throw new ServiceFormattedException("Error while finding object in DataStorage", e);
-    }
-  }
-
-  public static void storageSmokeTest(ViewContext context) {
-    try {
-      SmokeTestEntity entity = new SmokeTestEntity();
-      entity.setData("42");
-      DataStoreStorage storage = new DataStoreStorage(context);
-      storage.store(SmokeTestEntity.class, entity);
-
-      if (entity.getId() == null) throw new ServiceFormattedException("Ambari Views instance data DB doesn't work properly (auto increment id doesn't work)", null);
-      Object id = entity.getId();
-      SmokeTestEntity entity2 = storage.load(SmokeTestEntity.class, id);
-      boolean status = entity2.getData().compareTo("42") == 0;
-      storage.delete(SmokeTestEntity.class, id);
-      if (!status) throw new ServiceFormattedException("Ambari Views instance data DB doesn't work properly", null);
-    } catch (WebApplicationException ex) {
-      throw ex;
-    } catch (Exception ex) {
-      throw new ServiceFormattedException(ex.getMessage(), ex);
-    }
-  }
-
-  public static class SmokeTestEntity implements Indexed {
-    private String id = null;
-    private String data = null;
-
-    public String getId() {
-      return id;
-    }
-
-    public void setId(String id) {
-      this.id = id;
-    }
-
-    public String getData() {
-      return data;
-    }
-
-    public void setData(String data) {
-      this.data = data;
+      throw new ServiceFormattedException("S070 Data storage error", e);
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/InstanceKeyValueStorage.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/InstanceKeyValueStorage.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/InstanceKeyValueStorage.java
index 932b58e..98703fa 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/InstanceKeyValueStorage.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/InstanceKeyValueStorage.java
@@ -132,9 +132,4 @@ public class InstanceKeyValueStorage extends KeyValueStorage {
       throw new ServiceFormattedException(ex.getMessage(), ex);
     }
   }
-
-  @Override
-  public <T extends Indexed> List<T> loadWhere(Class<T> model, String where) {
-    throw new NotImplementedException();
-  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/LocalKeyValueStorage.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/LocalKeyValueStorage.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/LocalKeyValueStorage.java
index 674029a..24ed335 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/LocalKeyValueStorage.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/LocalKeyValueStorage.java
@@ -70,9 +70,4 @@ public class LocalKeyValueStorage extends KeyValueStorage {
     }
     return config;
   }
-
-  @Override
-  public <T extends Indexed> List<T> loadWhere(Class<T> model, String where) {
-    throw new NotImplementedException();
-  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/Storage.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/Storage.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/Storage.java
index 188282e..a34f566 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/Storage.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/Storage.java
@@ -55,15 +55,6 @@ public interface Storage {
   /**
    * Load all objects of given bean class
    * @param model bean class
-   * @param where filtering strategy (where clause)
-   * @param <T> bean class
-   * @return list of filtered objects
-   */
-  <T extends Indexed> List<T> loadWhere(Class<T> model, String where);
-
-  /**
-   * Load all objects of given bean class
-   * @param model bean class
    * @param <T> bean class
    * @return list of all objects
    */

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/CRUDResourceManager.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/CRUDResourceManager.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/CRUDResourceManager.java
index 891b526..c7167a8 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/CRUDResourceManager.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/CRUDResourceManager.java
@@ -124,7 +124,7 @@ abstract public class CRUDResourceManager<T extends Indexed> implements IResourc
     try {
       delete(object.getId());
     } catch (ItemNotFound itemNotFound) {
-      throw new ServiceFormattedException("Error in creation, during clean up: " + itemNotFound.toString(), itemNotFound);
+      throw new ServiceFormattedException("E040 Item not found", itemNotFound);
     }
     throw e;
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileService.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileService.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileService.java
index 3f5b3b8..8bde44c 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileService.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileService.java
@@ -19,11 +19,13 @@
 package org.apache.ambari.view.hive.resources.files;
 
 import com.google.inject.Inject;
+import com.jayway.jsonpath.JsonPath;
 import org.apache.ambari.view.ViewContext;
 import org.apache.ambari.view.ViewResourceHandler;
 import org.apache.ambari.view.hive.BaseService;
 import org.apache.ambari.view.hive.utils.*;
 import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.IOUtils;
 import org.apache.hadoop.fs.FSDataOutputStream;
 import org.apache.hadoop.fs.FileAlreadyExistsException;
 import org.json.simple.JSONObject;
@@ -38,6 +40,9 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.HashMap;
 
 /**
  * File access resource
@@ -53,6 +58,7 @@ import java.io.IOException;
  */
 public class FileService extends BaseService {
   public static final String FAKE_FILE = "fakefile://";
+  public static final String JSON_PATH_FILE = "jsonpath:";
 
   @Inject
   ViewResourceHandler handler;
@@ -78,10 +84,17 @@ public class FileService extends BaseService {
         if (page > 1)
           throw new IllegalArgumentException("There's only one page in fake files");
 
-        String content = filePath.substring(FAKE_FILE.length());
+        String encodedContent = filePath.substring(FAKE_FILE.length());
+        String content = new String(Base64.decodeBase64(encodedContent));
 
         fillFakeFileObject(filePath, file, content);
-      } else {
+      } else if (filePath.startsWith(JSON_PATH_FILE)) {
+        if (page > 1)
+          throw new IllegalArgumentException("There's only one page in fake files");
+
+        String content = getJsonPathContentByUrl(filePath);
+        fillFakeFileObject(filePath, file, content);
+      } else  {
         FilePaginator paginator = new FilePaginator(filePath, getSharedObjectsFactory().getHdfsApi());
 
         fillRealFileObject(filePath, page, file, paginator);
@@ -101,6 +114,19 @@ public class FileService extends BaseService {
     }
   }
 
+  protected String getJsonPathContentByUrl(String filePath) throws IOException {
+    URL url = new URL(filePath.substring(JSON_PATH_FILE.length()));
+
+    InputStream responseInputStream = context.getURLStreamProvider().readFrom(url.toString(), "GET",
+        null, new HashMap<String, String>());
+    String response = IOUtils.toString(responseInputStream);
+
+    for (String ref : url.getRef().split("!")) {
+      response = JsonPath.read(response, ref);
+    }
+    return response;
+  }
+
   public void fillRealFileObject(String filePath, Long page, FileResource file, FilePaginator paginator) throws IOException, InterruptedException {
     file.setFilePath(filePath);
     file.setFileContent(paginator.readPage(page));
@@ -109,9 +135,7 @@ public class FileService extends BaseService {
     file.setPageCount(paginator.pageCount());
   }
 
-  public void fillFakeFileObject(String filePath, FileResource file, String encodedContent) {
-    String content = new String(Base64.decodeBase64(encodedContent));
-
+  public void fillFakeFileObject(String filePath, FileResource file, String content) {
     file.setFilePath(filePath);
     file.setFileContent(content);
     file.setHasNext(false);
@@ -176,7 +200,7 @@ public class FileService extends BaseService {
         }
         output.close();
       } catch (FileAlreadyExistsException ex) {
-        throw new ServiceFormattedException(ex.getMessage(), ex, 400);
+        throw new ServiceFormattedException("F020 File already exists", ex, 400);
       }
       response.setHeader("Location",
           String.format("%s/%s", ui.getAbsolutePath().toString(), request.file.getFilePath()));

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/Aggregator.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/Aggregator.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/Aggregator.java
index e5fae95..687298e 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/Aggregator.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/Aggregator.java
@@ -23,6 +23,7 @@ import org.apache.ambari.view.hive.persistence.utils.Indexed;
 import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
 import org.apache.ambari.view.hive.persistence.utils.OnlyOwnersFilteringStrategy;
 import org.apache.ambari.view.hive.resources.IResourceManager;
+import org.apache.ambari.view.hive.resources.files.FileService;
 import org.apache.ambari.view.hive.resources.jobs.atsJobs.HiveQueryId;
 import org.apache.ambari.view.hive.resources.jobs.atsJobs.IATSParser;
 import org.apache.ambari.view.hive.resources.jobs.atsJobs.TezDagId;
@@ -192,7 +193,7 @@ public class Aggregator {
     String query = atsHiveQuery.query;
     atsJob.setTitle(query.substring(0, (query.length() > 42)?42:query.length()));
 
-    atsJob.setQueryFile("fakefile://" + Base64.encodeBase64URLSafeString(query.getBytes()));  // fake queryFile
+    atsJob.setQueryFile(FileService.JSON_PATH_FILE + atsHiveQuery.url + "#otherinfo.QUERY!queryText");
     return atsJob;
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ConnectionController.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ConnectionController.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ConnectionController.java
index c52d7b5..f481993 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ConnectionController.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ConnectionController.java
@@ -20,6 +20,7 @@ package org.apache.ambari.view.hive.resources.jobs;
 
 import org.apache.ambari.view.hive.client.Connection;
 import org.apache.ambari.view.hive.client.HiveClientException;
+import org.apache.ambari.view.hive.utils.HiveClientFormattedException;
 import org.apache.ambari.view.hive.utils.ServiceFormattedException;
 import org.apache.commons.codec.binary.Hex;
 import org.apache.hive.service.cli.thrift.TOperationHandle;
@@ -35,12 +36,8 @@ public class ConnectionController {
     this.operationHandleControllerFactory = operationHandleControllerFactory;
   }
 
-  public TSessionHandle getSessionByTag(String tag) {
-    try {
-      return connection.getSessionByTag(tag);
-    } catch (HiveClientException e) {
-      throw new ServiceFormattedException(e.toString(), e);
-    }
+  public TSessionHandle getSessionByTag(String tag) throws HiveClientException {
+    return connection.getSessionByTag(tag);
   }
 
   public String openSession() {
@@ -48,7 +45,7 @@ public class ConnectionController {
       TSessionHandle sessionHandle = connection.openSession();
       return getTagBySession(sessionHandle);
     } catch (HiveClientException e) {
-      throw new ServiceFormattedException(e.toString(), e);
+      throw new HiveClientFormattedException(e);
     }
   }
 
@@ -60,7 +57,7 @@ public class ConnectionController {
     try {
       connection.executeSync(session, "use " + database + ";");
     } catch (HiveClientException e) {
-      throw new ServiceFormattedException(e.toString(), e);
+      throw new HiveClientFormattedException(e);
     }
   }
 
@@ -69,7 +66,7 @@ public class ConnectionController {
     try {
       operationHandle = connection.executeAsync(session, cmd);
     } catch (HiveClientException e) {
-      throw new ServiceFormattedException(e.toString(), e);
+      throw new HiveClientFormattedException(e);
     }
     return operationHandleControllerFactory.createControllerForHandle(operationHandle);
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobService.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobService.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobService.java
index 1a6f7bf..3c1895b 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobService.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobService.java
@@ -22,7 +22,9 @@ import com.google.inject.Inject;
 import org.apache.ambari.view.ViewResourceHandler;
 import org.apache.ambari.view.hive.BaseService;
 import org.apache.ambari.view.hive.backgroundjobs.BackgroundJobController;
+import org.apache.ambari.view.hive.client.Connection;
 import org.apache.ambari.view.hive.client.Cursor;
+import org.apache.ambari.view.hive.client.HiveClientException;
 import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
 import org.apache.ambari.view.hive.resources.jobs.atsJobs.ATSRequestsDelegate;
 import org.apache.ambari.view.hive.resources.jobs.atsJobs.ATSRequestsDelegateImpl;
@@ -125,7 +127,7 @@ public class JobService extends BaseService {
     try {
       mergedJob = getAggregator().readATSJob(hiveJob);
     } catch (ItemNotFound itemNotFound) {
-      throw new ServiceFormattedException("Job not found", itemNotFound);
+      throw new ServiceFormattedException("E010 Job not found", itemNotFound);
     }
     Map createdJobMap = PropertyUtils.describe(mergedJob);
     createdJobMap.remove("class"); // no need to show Bean class on client
@@ -143,6 +145,7 @@ public class JobService extends BaseService {
   @Produces("text/csv")
   public Response getResultsCSV(@PathParam("jobId") String jobId,
                                 @Context HttpServletResponse response,
+                                @QueryParam("fileName") String fileName,
                                 @QueryParam("columns") final String requestedColumns) {
     try {
       JobController jobController = getResourceManager().readController(jobId);
@@ -155,6 +158,13 @@ public class JobService extends BaseService {
           Writer writer = new BufferedWriter(new OutputStreamWriter(os));
           CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT);
           try {
+
+            try {
+              csvPrinter.printRecord(resultSet.getHeadersRow().getRow());
+            } catch (HiveClientException e) {
+              LOG.error("Error on reading results header", e);
+            }
+
             while (resultSet.hasNext()) {
               csvPrinter.printRecord(resultSet.next().getRow());
               writer.flush();
@@ -165,7 +175,13 @@ public class JobService extends BaseService {
         }
       };
 
-      return Response.ok(stream).build();
+      if (fileName == null || fileName.isEmpty()) {
+        fileName = "results.csv";
+      }
+
+      return Response.ok(stream).
+          header("Content-Disposition", String.format("attachment; filename=\"%s\"", fileName)).
+          build();
     } catch (WebApplicationException ex) {
       throw ex;
     } catch (ItemNotFound itemNotFound) {
@@ -216,11 +232,11 @@ public class JobService extends BaseService {
               stream.close();
 
             } catch (IOException e) {
-              throw new ServiceFormattedException("Could not write CSV to HDFS for job#" + jobController.getJob().getId(), e);
+              throw new ServiceFormattedException("F010 Could not write CSV to HDFS for job#" + jobController.getJob().getId(), e);
             } catch (InterruptedException e) {
-              throw new ServiceFormattedException("Could not write CSV to HDFS for job#" + jobController.getJob().getId(), e);
+              throw new ServiceFormattedException("F010 Could not write CSV to HDFS for job#" + jobController.getJob().getId(), e);
             } catch (ItemNotFound itemNotFound) {
-              throw new NotFoundFormattedException("Job results are expired", itemNotFound);
+              throw new NotFoundFormattedException("E020 Job results are expired", itemNotFound);
             }
 
           }
@@ -261,6 +277,8 @@ public class JobService extends BaseService {
                              @QueryParam("columns") final String requestedColumns) {
     try {
       final JobController jobController = getResourceManager().readController(jobId);
+      if (!jobController.hasResults())
+        return ResultsPaginationController.emptyResponse().build();
 
       return ResultsPaginationController.getInstance(context)
            .request(jobId, searchId, true, fromBeginning, count,
@@ -385,6 +403,52 @@ public class JobService extends BaseService {
   }
 
   /**
+   * Invalidate session
+   */
+  @DELETE
+  @Path("sessions/{sessionTag}")
+  public Response invalidateSession(@PathParam("sessionTag") String sessionTag) {
+    try {
+      Connection connection = getSharedObjectsFactory().getHiveConnection();
+      connection.invalidateSessionByTag(sessionTag);
+      return Response.ok().build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
+  }
+
+  /**
+   * Session status
+   */
+  @GET
+  @Path("sessions/{sessionTag}")
+  @Produces(MediaType.APPLICATION_JSON)
+  public Response sessionStatus(@PathParam("sessionTag") String sessionTag) {
+    try {
+      Connection connection = getSharedObjectsFactory().getHiveConnection();
+
+      JSONObject session = new JSONObject();
+      session.put("sessionTag", sessionTag);
+      try {
+        connection.getSessionByTag(sessionTag);
+        session.put("actual", true);
+      } catch (HiveClientException ex) {
+        session.put("actual", false);
+      }
+
+      JSONObject status = new JSONObject();
+      status.put("session", session);
+      return Response.ok(status).build();
+    } catch (WebApplicationException ex) {
+      throw ex;
+    } catch (Exception ex) {
+      throw new ServiceFormattedException(ex.getMessage(), ex);
+    }
+  }
+
+  /**
    * Wrapper object for json mapping
    */
   public static class JobRequest {

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/NoOperationStatusSetException.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/NoOperationStatusSetException.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/NoOperationStatusSetException.java
index 1c1ce91..020c63e 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/NoOperationStatusSetException.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/NoOperationStatusSetException.java
@@ -19,8 +19,5 @@
 package org.apache.ambari.view.hive.resources.jobs;
 
 
-public class NoOperationStatusSetException extends Throwable {
-  public NoOperationStatusSetException(String s) {
-    super(s);
-  }
+public class NoOperationStatusSetException extends Exception {
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleController.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleController.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleController.java
index e146d55..34fc2d9 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleController.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleController.java
@@ -23,7 +23,7 @@ import org.apache.ambari.view.hive.client.Cursor;
 import org.apache.ambari.view.hive.client.HiveClientException;
 import org.apache.ambari.view.hive.client.IConnectionFactory;
 import org.apache.ambari.view.hive.resources.jobs.viewJobs.Job;
-import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+import org.apache.ambari.view.hive.utils.HiveClientFormattedException;
 import org.apache.hive.service.cli.thrift.TGetOperationStatusResp;
 import org.apache.hive.service.cli.thrift.TOperationHandle;
 import org.slf4j.Logger;
@@ -51,49 +51,54 @@ public class OperationHandleController {
     this.operationHandle = storedOperationHandle;
   }
 
-  public String getOperationStatus() throws NoOperationStatusSetException, HiveClientException {
+  public OperationStatus getOperationStatus() throws NoOperationStatusSetException, HiveClientException {
     TGetOperationStatusResp statusResp = connectionsFabric.getHiveConnection().getOperationStatus(operationHandle);
+
     if (!statusResp.isSetOperationState()) {
-      throw new NoOperationStatusSetException("Operation state is not set");
+      throw new NoOperationStatusSetException();
     }
 
-    String status;
+    OperationStatus opStatus = new OperationStatus();
+    opStatus.sqlState = statusResp.getSqlState();
+    opStatus.message = statusResp.getErrorMessage();
+
     switch (statusResp.getOperationState()) {
       case INITIALIZED_STATE:
-        status = Job.JOB_STATE_INITIALIZED;
+        opStatus.status = Job.JOB_STATE_INITIALIZED;
         break;
       case RUNNING_STATE:
-        status = Job.JOB_STATE_RUNNING;
+        opStatus.status = Job.JOB_STATE_RUNNING;
         break;
       case FINISHED_STATE:
-        status = Job.JOB_STATE_FINISHED;
+        opStatus.status = Job.JOB_STATE_FINISHED;
         break;
       case CANCELED_STATE:
-        status = Job.JOB_STATE_CANCELED;
+        opStatus.status = Job.JOB_STATE_CANCELED;
         break;
       case CLOSED_STATE:
-        status = Job.JOB_STATE_CLOSED;
+        opStatus.status = Job.JOB_STATE_CLOSED;
         break;
       case ERROR_STATE:
-        status = Job.JOB_STATE_ERROR;
+        opStatus.status = Job.JOB_STATE_ERROR;
         break;
       case UKNOWN_STATE:
-        status = Job.JOB_STATE_UNKNOWN;
+        opStatus.status = Job.JOB_STATE_UNKNOWN;
         break;
       case PENDING_STATE:
-        status = Job.JOB_STATE_PENDING;
+        opStatus.status = Job.JOB_STATE_PENDING;
         break;
       default:
-        throw new NoOperationStatusSetException("Unknown status " + statusResp.getOperationState());
+        throw new NoOperationStatusSetException();
     }
-    return status;
+
+    return opStatus;
   }
 
   public void cancel() {
     try {
       connectionsFabric.getHiveConnection().cancelOperation(operationHandle);
     } catch (HiveClientException e) {
-      throw new ServiceFormattedException("Cancel failed: " + e.toString(), e);
+      throw new HiveClientFormattedException(e);
     }
   }
 
@@ -108,4 +113,14 @@ public class OperationHandleController {
   public Cursor getResults() {
     return connectionsFabric.getHiveConnection().getResults(operationHandle);
   }
+
+  public boolean hasResults() {
+    return operationHandle.isHasResultSet();
+  }
+
+  public static class OperationStatus {
+    public String status;
+    public String sqlState;
+    public String message;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleResourceManager.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleResourceManager.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleResourceManager.java
index bab30a0..9e49dd1 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleResourceManager.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleResourceManager.java
@@ -65,7 +65,7 @@ public class OperationHandleResourceManager extends SharedCRUDResourceManager<St
       try {
         update(handle, jobRelatedHandles.get(0).getId());
       } catch (ItemNotFound itemNotFound) {
-        throw new ServiceFormattedException("Error when updating operation handle: " + itemNotFound.toString(), itemNotFound);
+        throw new ServiceFormattedException("E050 Error when updating operation handle: " + itemNotFound.toString(), itemNotFound);
       }
     } else {
       create(handle);

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ResultsPaginationController.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ResultsPaginationController.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ResultsPaginationController.java
index c152b03..8305708 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ResultsPaginationController.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ResultsPaginationController.java
@@ -23,6 +23,7 @@ import org.apache.ambari.view.ViewContext;
 import org.apache.ambari.view.hive.client.ColumnDescription;
 import org.apache.ambari.view.hive.client.HiveClientException;
 import org.apache.ambari.view.hive.client.Cursor;
+import org.apache.ambari.view.hive.utils.HiveClientFormattedException;
 import org.apache.ambari.view.hive.utils.ServiceFormattedException;
 import org.apache.commons.collections4.map.PassiveExpiringMap;
 
@@ -96,6 +97,8 @@ public class ResultsPaginationController {
       Cursor resultSet = null;
       try {
         resultSet = makeResultsSet.call();
+      } catch (HiveClientException ex) {
+        throw new HiveClientFormattedException(ex);
       } catch (Exception ex) {
         throw new ServiceFormattedException(ex.getMessage(), ex);
       }
@@ -127,6 +130,18 @@ public class ResultsPaginationController {
     resultsResponse.setHasNext(resultSet.hasNext());
 //      resultsResponse.setSize(resultSet.size());
     resultsResponse.setOffset(resultSet.getOffset());
+    resultsResponse.setHasResults(true);
+    return Response.ok(resultsResponse);
+  }
+
+  public static Response.ResponseBuilder emptyResponse() {
+    ResultsResponse resultsResponse = new ResultsResponse();
+    resultsResponse.setSchema(new ArrayList<ColumnDescription>());
+    resultsResponse.setRows(new ArrayList<Object[]>());
+    resultsResponse.setReadCount(0);
+    resultsResponse.setHasNext(false);
+    resultsResponse.setOffset(0);
+    resultsResponse.setHasResults(false);
     return Response.ok(resultsResponse);
   }
 
@@ -136,6 +151,7 @@ public class ResultsPaginationController {
     private int readCount;
     private boolean hasNext;
     private long offset;
+    private boolean hasResults;
 
     public void setSchema(ArrayList<ColumnDescription> schema) {
       this.schema = schema;
@@ -176,5 +192,13 @@ public class ResultsPaginationController {
     public void setOffset(long offset) {
       this.offset = offset;
     }
+
+    public boolean getHasResults() {
+      return hasResults;
+    }
+
+    public void setHasResults(boolean hasResults) {
+      this.hasResults = hasResults;
+    }
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/StoredOperationHandle.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/StoredOperationHandle.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/StoredOperationHandle.java
index 1d3f6e0..a1d87ad 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/StoredOperationHandle.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/StoredOperationHandle.java
@@ -80,7 +80,7 @@ public class StoredOperationHandle implements Indexed {
       identifier.setGuid(Hex.decodeHex(getGuid().toCharArray()));
       identifier.setSecret(Hex.decodeHex(getSecret().toCharArray()));
     } catch (DecoderException e) {
-      throw new ServiceFormattedException("Wrong identifer of OperationHandle is stored in DB");
+      throw new ServiceFormattedException("E060 Wrong identifier of OperationHandle is stored in DB");
     }
     handle.setOperationId(identifier);
     return handle;

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSParser.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSParser.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSParser.java
index 106babd..a6446ff 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSParser.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSParser.java
@@ -91,6 +91,7 @@ public class ATSParser implements IATSParser {
     HiveQueryId parsedJob = new HiveQueryId();
 
     parsedJob.entity = (String) job.get("entity");
+    parsedJob.url = delegate.hiveQueryIdDirectUrl((String) job.get("entity"));
     parsedJob.starttime = ((Long) job.get("starttime")) / MillisInSecond;
 
     JSONObject primaryfilters = (JSONObject) job.get("primaryfilters");

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegate.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegate.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegate.java
index 3aa07d4..f9b19bf 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegate.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegate.java
@@ -21,6 +21,14 @@ package org.apache.ambari.view.hive.resources.jobs.atsJobs;
 import org.json.simple.JSONObject;
 
 public interface ATSRequestsDelegate {
+  String hiveQueryIdDirectUrl(String entity);
+
+  String hiveQueryIdOperationIdUrl(String operationId);
+
+  String tezDagDirectUrl(String entity);
+
+  String tezDagNameUrl(String name);
+
   JSONObject hiveQueryIdList(String username);
 
   JSONObject hiveQueryIdByOperationId(String operationId);

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegateImpl.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegateImpl.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegateImpl.java
index 047bd63..6ac5ef8 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegateImpl.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/ATSRequestsDelegateImpl.java
@@ -43,6 +43,26 @@ public class ATSRequestsDelegateImpl implements ATSRequestsDelegate {
   }
 
   @Override
+  public String hiveQueryIdDirectUrl(String entity) {
+    return atsUrl + "/ws/v1/timeline/HIVE_QUERY_ID/" + entity;
+  }
+
+  @Override
+  public String hiveQueryIdOperationIdUrl(String operationId) {
+    return atsUrl + "/ws/v1/timeline/HIVE_QUERY_ID?primaryFilter=operationid:" + operationId;
+  }
+
+  @Override
+  public String tezDagDirectUrl(String entity) {
+    return atsUrl + "/ws/v1/timeline/TEZ_DAG_ID/" + entity;
+  }
+
+  @Override
+  public String tezDagNameUrl(String name) {
+    return atsUrl + "/ws/v1/timeline/TEZ_DAG_ID?primaryFilter=dagName:" + name;
+  }
+
+  @Override
   public JSONObject hiveQueryIdList(String username) {
     String hiveQueriesListUrl = atsUrl + "/ws/v1/timeline/HIVE_QUERY_ID?primaryFilter=requestuser:" + username;
     String response = readFromWithDefault(hiveQueriesListUrl, "{ \"entities\" : [  ] }");
@@ -51,14 +71,14 @@ public class ATSRequestsDelegateImpl implements ATSRequestsDelegate {
 
   @Override
   public JSONObject hiveQueryIdByOperationId(String operationId) {
-    String hiveQueriesListUrl = atsUrl + "/ws/v1/timeline/HIVE_QUERY_ID?primaryFilter=operationid:" + operationId;
+    String hiveQueriesListUrl = hiveQueryIdOperationIdUrl(operationId);
     String response = readFromWithDefault(hiveQueriesListUrl, "{ \"entities\" : [  ] }");
     return (JSONObject) JSONValue.parse(response);
   }
 
   @Override
   public JSONObject tezDagByName(String name) {
-    String tezDagUrl = atsUrl + "/ws/v1/timeline/TEZ_DAG_ID?primaryFilter=dagName:" + name;
+    String tezDagUrl = tezDagNameUrl(name);
     String response = readFromWithDefault(tezDagUrl, EMPTY_ENTITIES_JSON);
     return (JSONObject) JSONValue.parse(response);
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/HiveQueryId.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/HiveQueryId.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/HiveQueryId.java
index edb726b..af54dbb 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/HiveQueryId.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/atsJobs/HiveQueryId.java
@@ -23,6 +23,8 @@ import org.json.simple.JSONObject;
 import java.util.List;
 
 public class HiveQueryId {
+  public String url;
+
   public String entity;
   public String query;
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/Job.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/Job.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/Job.java
index 65c36a7..dc4dfa8 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/Job.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/Job.java
@@ -104,4 +104,12 @@ public interface Job extends Serializable,Indexed,PersonalResource {
   String getSessionTag();
 
   void setSessionTag(String sessionTag);
+
+  String getSqlState();
+
+  void setSqlState(String sqlState);
+
+  String getStatusMessage();
+
+  void setStatusMessage(String message);
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobController.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobController.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobController.java
index 339e194..a25f7d4 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobController.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobController.java
@@ -35,6 +35,7 @@ public interface JobController {
   Job getJobPOJO();
 
   Cursor getResults() throws ItemNotFound;
+  boolean hasResults() throws ItemNotFound;
 
   void afterCreation();
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobControllerImpl.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobControllerImpl.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobControllerImpl.java
index c6c1c78..eb2e141 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobControllerImpl.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobControllerImpl.java
@@ -77,9 +77,9 @@ public class JobControllerImpl implements JobController, ModifyNotificationDeleg
     try {
       query = paginator.readPage(0);  //warning - reading only 0 page restricts size of query to 1MB
     } catch (IOException e) {
-      throw new ServiceFormattedException("Error when reading file: " + e.toString(), e);
+      throw new ServiceFormattedException("F030 Error when reading file " + job.getQueryFile(), e);
     } catch (InterruptedException e) {
-      throw new ServiceFormattedException("Error when reading file: " + e.toString(), e);
+      throw new ServiceFormattedException("F030 Error when reading file " + job.getQueryFile(), e);
     }
     return query;
   }
@@ -109,12 +109,19 @@ public class JobControllerImpl implements JobController, ModifyNotificationDeleg
   }
 
   private TSessionHandle getSession() {
-    if (job.getSessionTag() != null) {
-      return hiveConnection.getSessionByTag(getJob().getSessionTag());
-    } else {
-      String tag = hiveConnection.openSession();
-      job.setSessionTag(tag);
+    try {
+      if (job.getSessionTag() != null)
+        return hiveConnection.getSessionByTag(getJob().getSessionTag());
+    } catch (HiveClientException ignore) {
+      LOG.debug("Stale sessionTag was provided, new session will be opened");
+    }
+
+    String tag = hiveConnection.openSession();
+    job.setSessionTag(tag);
+    try {
       return hiveConnection.getSessionByTag(tag);
+    } catch (HiveClientException e) {
+      throw new HiveClientFormattedException(e);
     }
   }
 
@@ -136,8 +143,10 @@ public class JobControllerImpl implements JobController, ModifyNotificationDeleg
     try {
 
       OperationHandleController handle = opHandleControllerFactory.getHandleForJob(job);
-      String status = handle.getOperationStatus();
-      job.setStatus(status);
+      OperationHandleController.OperationStatus status = handle.getOperationStatus();
+      job.setStatus(status.status);
+      job.setStatusMessage(status.message);
+      job.setSqlState(status.sqlState);
       LOG.debug("Status of job#" + job.getId() + " is " + job.getStatus());
 
     } catch (NoOperationStatusSetException e) {
@@ -148,7 +157,7 @@ public class JobControllerImpl implements JobController, ModifyNotificationDeleg
       job.setStatus(Job.JOB_STATE_UNKNOWN);
 
     } catch (HiveClientException e) {
-      throw new ServiceFormattedException("Could not fetch job status " + job.getId(), e);
+      throw new HiveClientFormattedException(e);
 
     } catch (ItemNotFound itemNotFound) {
       LOG.debug("No TOperationHandle for job#" + job.getId() + ", can't update status");
@@ -213,6 +222,12 @@ public class JobControllerImpl implements JobController, ModifyNotificationDeleg
   }
 
   @Override
+  public boolean hasResults() throws ItemNotFound {
+    OperationHandleController handle = opHandleControllerFactory.getHandleForJob(job);
+    return handle.hasResults();
+  }
+
+  @Override
   public void afterCreation() {
     setupStatusDirIfNotPresent();
     setupQueryFileIfNotPresent();
@@ -311,9 +326,9 @@ public class JobControllerImpl implements JobController, ModifyNotificationDeleg
       }
 
     } catch (IOException e) {
-      throw new ServiceFormattedException("Error in creation: " + e.toString(), e);
+      throw new ServiceFormattedException("F040 Error when creating file " + jobQueryFilePath, e);
     } catch (InterruptedException e) {
-      throw new ServiceFormattedException("Error in creation: " + e.toString(), e);
+      throw new ServiceFormattedException("F040 Error when creating file " + jobQueryFilePath, e);
     }
     job.setQueryFile(jobQueryFilePath);
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobImpl.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobImpl.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobImpl.java
index d0d74a2..dc3f624 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobImpl.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/viewJobs/JobImpl.java
@@ -20,6 +20,7 @@ package org.apache.ambari.view.hive.resources.jobs.viewJobs;
 
 import org.apache.commons.beanutils.PropertyUtils;
 
+import java.beans.Transient;
 import java.lang.reflect.InvocationTargetException;
 import java.util.Map;
 
@@ -32,11 +33,14 @@ public class JobImpl implements Job {
   private String statusDir = null;
   private Long dateSubmitted = 0L;
   private Long duration = 0L;
-  private String status = JOB_STATE_UNKNOWN;
   private String forcedContent = null;
   private String dataBase = null;
   private String queryId = null;
 
+  private String status = JOB_STATE_UNKNOWN;
+  private String statusMessage = null;
+  private String sqlState = null;
+
   private String applicationId;
   private String dagId;
   private String dagName;
@@ -148,11 +152,13 @@ public class JobImpl implements Job {
   }
 
   @Override
+  @Transient
   public String getForcedContent() {
     return forcedContent;
   }
 
   @Override
+  @Transient
   public void setForcedContent(String forcedContent) {
     this.forcedContent = forcedContent;
   }
@@ -246,4 +252,24 @@ public class JobImpl implements Job {
   public void setSessionTag(String sessionTag) {
     this.sessionTag = sessionTag;
   }
+
+  @Override
+  public String getStatusMessage() {
+    return statusMessage;
+  }
+
+  @Override
+  public void setStatusMessage(String statusMessage) {
+    this.statusMessage = statusMessage;
+  }
+
+  @Override
+  public String getSqlState() {
+    return sqlState;
+  }
+
+  @Override
+  public void setSqlState(String sqlState) {
+    this.sqlState = sqlState;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDF.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDF.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDF.java
index 2dafcf4..77c37b5 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDF.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDF.java
@@ -31,7 +31,7 @@ import java.util.Map;
 public class UDF implements Serializable, PersonalResource {
   private String name;
   private String classname;
-  private Integer fileResource;
+  private String fileResource;
 
   private String id;
   private String owner;
@@ -77,11 +77,11 @@ public class UDF implements Serializable, PersonalResource {
     this.classname = classname;
   }
 
-  public Integer getFileResource() {
+  public String getFileResource() {
     return fileResource;
   }
 
-  public void setFileResource(Integer fileResource) {
+  public void setFileResource(String fileResource) {
     this.fileResource = fileResource;
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsApi.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsApi.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsApi.java
index e5e3593..844bc80 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsApi.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsApi.java
@@ -253,7 +253,7 @@ public class HdfsApi {
       }
     });
     if (!result) {
-      throw new ServiceFormattedException("Can't copy source file from " + src + " to " + dest);
+      throw new ServiceFormattedException("F050 Can't copy source file from " + src + " to " + dest);
     }
   }
 
@@ -341,11 +341,11 @@ public class HdfsApi {
       api = new HdfsApi(defaultFS, getHdfsUsername(context), getHdfsAuthParams(context));
       LOG.info("HdfsApi connected OK");
     } catch (IOException e) {
-      String message = "HdfsApi IO error: " + e.getMessage();
+      String message = "F060 Couldn't open connection to HDFS";
       LOG.error(message);
       throw new ServiceFormattedException(message, e);
     } catch (InterruptedException e) {
-      String message = "HdfsApi Interrupted error: " + e.getMessage();
+      String message = "F060 Couldn't open connection to HDFS";
       LOG.error(message);
       throw new ServiceFormattedException(message, e);
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsUtil.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsUtil.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsUtil.java
index 3120958..7386ee4 100644
--- a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsUtil.java
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsUtil.java
@@ -43,9 +43,9 @@ public class HdfsUtil {
         stream.close();
       }
     } catch (IOException e) {
-      throw new ServiceFormattedException("Could not write file " + filePath, e);
+      throw new ServiceFormattedException("F070 Could not write file " + filePath, e);
     } catch (InterruptedException e) {
-      throw new ServiceFormattedException("Could not write file " + filePath, e);
+      throw new ServiceFormattedException("F070 Could not write file " + filePath, e);
     }
   }
 
@@ -74,9 +74,9 @@ public class HdfsUtil {
         triesCount += 1;
       } while (!isUnallocatedFilenameFound);
     } catch (IOException e) {
-      throw new ServiceFormattedException("Error in creation: " + e.toString(), e);
+      throw new ServiceFormattedException("F080 Error in creation " + fullPathAndFilename + "...", e);
     } catch (InterruptedException e) {
-      throw new ServiceFormattedException("Error in creation: " + e.toString(), e);
+      throw new ServiceFormattedException("F080 Error in creation " + fullPathAndFilename + "...", e);
     }
 
     return newFilePath;

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HiveClientFormattedException.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HiveClientFormattedException.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HiveClientFormattedException.java
new file mode 100644
index 0000000..a602d22
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HiveClientFormattedException.java
@@ -0,0 +1,26 @@
+/**
+ * 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.ambari.view.hive.utils;
+
+public class HiveClientFormattedException extends ServiceFormattedException {
+
+  public HiveClientFormattedException(Throwable exception) {
+    super(exception.getMessage(), exception);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/.bowerrc
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/.bowerrc b/contrib/views/hive/src/main/resources/ui/hive-web/.bowerrc
new file mode 100644
index 0000000..959e169
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/.bowerrc
@@ -0,0 +1,4 @@
+{
+  "directory": "bower_components",
+  "analytics": false
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/.editorconfig
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/.editorconfig b/contrib/views/hive/src/main/resources/ui/hive-web/.editorconfig
new file mode 100644
index 0000000..47c5438
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/.editorconfig
@@ -0,0 +1,34 @@
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+
+[*]
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+indent_style = space
+indent_size = 2
+
+[*.js]
+indent_style = space
+indent_size = 2
+
+[*.hbs]
+insert_final_newline = false
+indent_style = space
+indent_size = 2
+
+[*.css]
+indent_style = space
+indent_size = 2
+
+[*.html]
+indent_style = space
+indent_size = 2
+
+[*.{diff,md}]
+trim_trailing_whitespace = false

http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/.ember-cli
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/.ember-cli b/contrib/views/hive/src/main/resources/ui/hive-web/.ember-cli
new file mode 100644
index 0000000..3da2738
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/.ember-cli
@@ -0,0 +1,27 @@
+/**
+ * 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.
+ */
+
+{
+  /**
+    Ember CLI sends analytics information by default. The data is completely
+    anonymous, but there are times when you might want to disable this behavior.
+
+    Setting `disableAnalytics` to true will prevent any data from being sent.
+  */
+  "disableAnalytics": true
+}


[2/5] ambari git commit: AMBARI-10528. Hive View: Visual Explain, Error handling and bugfixes (alexantonenko)

Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/672eee34/contrib/views/hive/src/main/resources/ui/hive-web/vendor/dagre.min.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/vendor/dagre.min.js b/contrib/views/hive/src/main/resources/ui/hive-web/vendor/dagre.min.js
new file mode 100644
index 0000000..10a0471
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/vendor/dagre.min.js
@@ -0,0 +1,27 @@
+/**
+ * @license
+ * Copyright (c) 2012-2014 Chris Pettitt
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){(function(global){global.dagre=require("./index");global.dagre.graphlib=require("graphlib")}).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"./index":2,graphlib:29}],2:[function(require,module,exports){module.exports={layout:require("./lib/layout"),debug:require("./lib/debug"),util:{time:require("./lib/util").time,notime:require("./lib/util").notime},version:require("./lib/version")}},{"./lib/debug":6,"./lib/layout":8,"./lib/util":27,"./lib/v
 ersion":28}],3:[function(require,module,exports){"use strict";var _=require("lodash"),greedyFAS=require("./greedy-fas");module.exports={run:run,undo:undo};function run(g){var fas=g.graph().acyclicer==="greedy"?greedyFAS(g,weightFn(g)):dfsFAS(g);_.each(fas,function(e){var label=g.edge(e);g.removeEdge(e);label.forwardName=e.name;label.reversed=true;g.setEdge(e.w,e.v,label,_.uniqueId("rev"))});function weightFn(g){return function(e){return g.edge(e).weight}}}function dfsFAS(g){var fas=[],stack={},visited={};function dfs(v){if(_.has(visited,v)){return}visited[v]=true;stack[v]=true;_.each(g.outEdges(v),function(e){if(_.has(stack,e.w)){fas.push(e)}else{dfs(e.w)}});delete stack[v]}_.each(g.nodes(),dfs);return fas}function undo(g){_.each(g.edges(),function(e){var label=g.edge(e);if(label.reversed){g.removeEdge(e);var forwardName=label.forwardName;delete label.reversed;delete label.forwardName;g.setEdge(e.w,e.v,label,forwardName)}})}},{"./greedy-fas":7,lodash:48}],4:[function(require,module,
 exports){var _=require("lodash"),util=require("./util");module.exports=addBorderSegments;function addBorderSegments(g){function dfs(v){var children=g.children(v),node=g.node(v);if(children.length){_.each(children,dfs)}if(_.has(node,"minRank")){node.borderLeft=[];node.borderRight=[];for(var rank=node.minRank,maxRank=node.maxRank+1;rank<maxRank;++rank){addBorderNode(g,"borderLeft","_bl",v,node,rank);addBorderNode(g,"borderRight","_br",v,node,rank)}}}_.each(g.children(),dfs)}function addBorderNode(g,prop,prefix,sg,sgNode,rank){var label={width:0,height:0,rank:rank},prev=sgNode[prop][rank-1],curr=util.addDummyNode(g,"border",label,prefix);sgNode[prop][rank]=curr;g.setParent(curr,sg);if(prev){g.setEdge(prev,curr,{weight:1})}}},{"./util":27,lodash:48}],5:[function(require,module,exports){module.exports=List;function List(){var sentinel={};sentinel._next=sentinel._prev=sentinel;this._sentinel=sentinel}List.prototype.dequeue=function(){var sentinel=this._sentinel,entry=sentinel._prev;if(ent
 ry!==sentinel){unlink(entry);return entry}};List.prototype.enqueue=function(entry){var sentinel=this._sentinel;if(entry._prev&&entry._next){unlink(entry)}entry._next=sentinel._next;sentinel._next._prev=entry;sentinel._next=entry;entry._prev=sentinel};List.prototype.toString=function(){var strs=[],sentinel=this._sentinel,curr=sentinel._prev;while(curr!==sentinel){strs.push(JSON.stringify(curr,filterOutLinks));curr=curr._prev}return"["+strs.join(", ")+"]"};function unlink(entry){entry._prev._next=entry._next;entry._next._prev=entry._prev;delete entry._next;delete entry._prev}function filterOutLinks(k,v){if(k!=="_next"&&k!=="_prev"){return v}}},{}],6:[function(require,module,exports){var _=require("lodash"),util=require("./util"),Graph=require("graphlib").Graph;module.exports={debugOrdering:debugOrdering};function debugOrdering(g){var layerMatrix=util.buildLayerMatrix(g);var h=new Graph({compound:true,multigraph:true}).setGraph({});_.each(g.nodes(),function(v){h.setNode(v,{label:v});h.
 setParent(v,"layer"+g.node(v).rank)});_.each(g.edges(),function(e){h.setEdge(e.v,e.w,{},e.name)});_.each(layerMatrix,function(layer,i){var layerV="layer"+i;h.setNode(layerV,{rank:"same"});_.reduce(layer,function(u,v){h.setEdge(u,v,{style:"invis"});return v})});return h}},{"./util":27,graphlib:29,lodash:48}],7:[function(require,module,exports){var _=require("lodash"),Graph=require("graphlib").Graph,List=require("./data/list");module.exports=greedyFAS;var DEFAULT_WEIGHT_FN=_.constant(1);function greedyFAS(g,weightFn){if(g.nodeCount()<=1){return[]}var state=buildState(g,weightFn||DEFAULT_WEIGHT_FN);var results=doGreedyFAS(state.graph,state.buckets,state.zeroIdx);return _.flatten(_.map(results,function(e){return g.outEdges(e.v,e.w)}),true)}function doGreedyFAS(g,buckets,zeroIdx){var results=[],sources=buckets[buckets.length-1],sinks=buckets[0];var entry;while(g.nodeCount()){while(entry=sinks.dequeue()){removeNode(g,buckets,zeroIdx,entry)}while(entry=sources.dequeue()){removeNode(g,bucke
 ts,zeroIdx,entry)}if(g.nodeCount()){for(var i=buckets.length-2;i>0;--i){entry=buckets[i].dequeue();if(entry){results=results.concat(removeNode(g,buckets,zeroIdx,entry,true));break}}}}return results}function removeNode(g,buckets,zeroIdx,entry,collectPredecessors){var results=collectPredecessors?[]:undefined;_.each(g.inEdges(entry.v),function(edge){var weight=g.edge(edge),uEntry=g.node(edge.v);if(collectPredecessors){results.push({v:edge.v,w:edge.w})}uEntry.out-=weight;assignBucket(buckets,zeroIdx,uEntry)});_.each(g.outEdges(entry.v),function(edge){var weight=g.edge(edge),w=edge.w,wEntry=g.node(w);wEntry.in-=weight;assignBucket(buckets,zeroIdx,wEntry)});g.removeNode(entry.v);return results}function buildState(g,weightFn){var fasGraph=new Graph,maxIn=0,maxOut=0;_.each(g.nodes(),function(v){fasGraph.setNode(v,{v:v,"in":0,out:0})});_.each(g.edges(),function(e){var prevWeight=fasGraph.edge(e.v,e.w)||0,weight=weightFn(e),edgeWeight=prevWeight+weight;fasGraph.setEdge(e.v,e.w,edgeWeight);max
 Out=Math.max(maxOut,fasGraph.node(e.v).out+=weight);maxIn=Math.max(maxIn,fasGraph.node(e.w).in+=weight)});var buckets=_.range(maxOut+maxIn+3).map(function(){return new List});var zeroIdx=maxIn+1;_.each(fasGraph.nodes(),function(v){assignBucket(buckets,zeroIdx,fasGraph.node(v))});return{graph:fasGraph,buckets:buckets,zeroIdx:zeroIdx}}function assignBucket(buckets,zeroIdx,entry){if(!entry.out){buckets[0].enqueue(entry)}else if(!entry.in){buckets[buckets.length-1].enqueue(entry)}else{buckets[entry.out-entry.in+zeroIdx].enqueue(entry)}}},{"./data/list":5,graphlib:29,lodash:48}],8:[function(require,module,exports){"use strict";var _=require("lodash"),acyclic=require("./acyclic"),normalize=require("./normalize"),rank=require("./rank"),normalizeRanks=require("./util").normalizeRanks,parentDummyChains=require("./parent-dummy-chains"),removeEmptyRanks=require("./util").removeEmptyRanks,nestingGraph=require("./nesting-graph"),addBorderSegments=require("./add-border-segments"),order=require(".
 /order"),position=require("./position"),util=require("./util"),Graph=require("graphlib").Graph;module.exports=layout;function layout(g,opts){var time=opts&&opts.debugTiming?util.time:util.notime;time("layout",function(){var layoutGraph=time("  buildLayoutGraph",function(){return buildLayoutGraph(g)});time("  runLayout",function(){runLayout(layoutGraph,time)});time("  updateInputGraph",function(){updateInputGraph(g,layoutGraph)})})}function runLayout(g,time){time("    makeSpaceForEdgeLabels",function(){makeSpaceForEdgeLabels(g)});time("    removeSelfEdges",function(){removeSelfEdges(g)});time("    acyclic",function(){acyclic.run(g)});time("    nestingGraph.run",function(){nestingGraph.run(g)});time("    rank",function(){rank(util.asNonCompoundGraph(g))});time("    injectEdgeLabelProxies",function(){injectEdgeLabelProxies(g)});time("    removeEmptyRanks",function(){removeEmptyRanks(g)});time("    nestingGraph.cleanup",function(){nestingGraph.cleanup(g)});time("    normalizeRanks",func
 tion(){normalizeRanks(g)});time("    assignRankMinMax",function(){assignRankMinMax(g)});time("    removeEdgeLabelProxies",function(){removeEdgeLabelProxies(g)});time("    normalize.run",function(){normalize.run(g)});time("    parentDummyChains",function(){parentDummyChains(g)});time("    addBorderSegments",function(){addBorderSegments(g)});time("    order",function(){order(g)});time("    insertSelfEdges",function(){insertSelfEdges(g)});time("    position",function(){position(g)});time("    positionSelfEdges",function(){positionSelfEdges(g)});time("    setGraphDimensions",function(){setGraphDimensions(g)});time("    removeBorderNodes",function(){removeBorderNodes(g)});time("    normalize.undo",function(){normalize.undo(g)});time("    assignNodeIntersects",function(){assignNodeIntersects(g)});time("    reversePoints",function(){reversePointsForReversedEdges(g)});time("    acyclic.undo",function(){acyclic.undo(g)})}function updateInputGraph(inputGraph,layoutGraph){_.each(inputGraph.nod
 es(),function(v){var inputLabel=inputGraph.node(v),layoutLabel=layoutGraph.node(v);if(inputLabel){inputLabel.x=layoutLabel.x;inputLabel.y=layoutLabel.y;if(layoutGraph.children(v).length){inputLabel.width=layoutLabel.width;inputLabel.height=layoutLabel.height}}});_.each(inputGraph.edges(),function(e){var inputLabel=inputGraph.edge(e),layoutLabel=layoutGraph.edge(e);inputLabel.points=layoutLabel.points;if(_.has(layoutLabel,"x")){inputLabel.x=layoutLabel.x;inputLabel.y=layoutLabel.y}});inputGraph.graph().width=layoutGraph.graph().width;inputGraph.graph().height=layoutGraph.graph().height}var graphNumAttrs=["nodesep","edgesep","ranksep","marginx","marginy"],graphDefaults={ranksep:50,edgesep:10,nodesep:50},graphAttrs=["acyclicer","ranker","rankdir","align"],nodeNumAttrs=["width","height"],nodeDefaults={width:0,height:0},edgeNumAttrs=["minlen","weight","width","height"],edgeDefaults={minlen:1,weight:1,width:0,height:0};function buildLayoutGraph(inputGraph){var g=new Graph({multigraph:true
 ,compound:true});g.setGraph(_.merge({},graphDefaults,selectNumberAttrs(inputGraph.graph(),graphNumAttrs),_.pick(inputGraph.graph(),graphAttrs)));_.each(inputGraph.nodes(),function(v){var label=inputGraph.node(v);g.setNode(v,_.defaults(selectNumberAttrs(label,nodeNumAttrs),nodeDefaults));g.setParent(v,inputGraph.parent(v))});_.each(inputGraph.edges(),function(e){var label=inputGraph.edge(e);g.setEdge(e,_.defaults(selectNumberAttrs(label,edgeNumAttrs),edgeDefaults))});return g}function makeSpaceForEdgeLabels(g){var graphLabel=g.graph();graphLabel.ranksep/=2;_.each(g.edges(),function(e){g.edge(e).minlen*=2})}function injectEdgeLabelProxies(g){_.each(g.edges(),function(e){var edge=g.edge(e);if(edge.width&&edge.height){var v=g.node(e.v),w=g.node(e.w),label={rank:(w.rank-v.rank)/2+v.rank,e:e};util.addDummyNode(g,"edge-proxy",label,"_ep")}})}function assignRankMinMax(g){var maxRank=0;_.each(g.nodes(),function(v){var node=g.node(v);if(node.borderTop){node.minRank=g.node(node.borderTop).rank
 ;node.maxRank=g.node(node.borderBottom).rank;maxRank=_.max(maxRank,node.maxRank)}});g.graph().maxRank=maxRank}function removeEdgeLabelProxies(g){_.each(g.nodes(),function(v){var node=g.node(v);if(node.dummy==="edge-proxy"){g.edge(node.e).labelRank=node.rank;g.removeNode(v)}})}function setGraphDimensions(g){var width=0,height=0,graph=g.graph();_.each(g.nodes(),function(v){var node=g.node(v);width=Math.max(width,node.x+node.width/2);height=Math.max(height,node.y+node.height/2)});graph.width=width;graph.height=height}function assignNodeIntersects(g){_.each(g.edges(),function(e){var edge=g.edge(e),nodeV=g.node(e.v),nodeW=g.node(e.w),p1,p2;if(!edge.points){edge.points=[];p1=nodeW;p2=nodeV}else{p1=edge.points[0];p2=edge.points[edge.points.length-1]}edge.points.unshift(util.intersectRect(nodeV,p1));edge.points.push(util.intersectRect(nodeW,p2))})}function reversePointsForReversedEdges(g){_.each(g.edges(),function(e){var edge=g.edge(e);if(edge.reversed){edge.points.reverse()}})}function rem
 oveBorderNodes(g){var rankdir=(g.graph().rankdir||"tb").toLowerCase();_.each(g.nodes(),function(v){if(g.children(v).length){var node=g.node(v),t=g.node(node.borderTop),b=g.node(node.borderBottom),l=g.node(_.last(node.borderLeft)),r=g.node(_.last(node.borderRight)),tmp;if(rankdir==="bt"||rankdir==="rl"){tmp=t;t=b;b=tmp}if(rankdir==="lr"||rankdir==="rl"){tmp=t;t=l;l=tmp;tmp=b;b=r;r=tmp}node.width=r.x-l.x;node.height=b.y-t.y;node.x=l.x+node.width/2;node.y=t.y+node.height/2}});_.each(g.nodes(),function(v){if(g.node(v).dummy==="border"){g.removeNode(v)}})}function removeSelfEdges(g){_.each(g.edges(),function(e){if(e.v===e.w){var node=g.node(e.v);if(!node.selfEdges){node.selfEdges=[]}node.selfEdges.push({e:e,label:g.edge(e)});g.removeEdge(e)}})}function insertSelfEdges(g){var layers=util.buildLayerMatrix(g);_.each(layers,function(layer){var orderShift=0;_.each(layer,function(v,i){var node=g.node(v);node.order=i+orderShift;_.each(node.selfEdges,function(selfEdge){util.addDummyNode(g,"selfe
 dge",{width:selfEdge.label.width,height:selfEdge.label.height,rank:node.rank,order:i+ ++orderShift,e:selfEdge.e,label:selfEdge.label},"_se")});delete node.selfEdges})})}function positionSelfEdges(g){_.each(g.nodes(),function(v){var node=g.node(v);if(node.dummy==="selfedge"){var selfNode=g.node(node.e.v),x=selfNode.x+selfNode.width/2,y=selfNode.y,dx=node.x-x,dy=selfNode.height/2;g.setEdge(node.e,node.label);g.removeNode(v);node.label.points=[{x:x+2*dx/3,y:y-dy},{x:x+5*dx/6,y:y-dy},{x:x+dx,y:y},{x:x+5*dx/6,y:y+dy},{x:x+2*dx/3,y:y+dy}];node.label.x=node.x;node.label.y=node.y}})}function selectNumberAttrs(obj,attrs){return _.mapValues(_.pick(obj,attrs),Number)}},{"./acyclic":3,"./add-border-segments":4,"./nesting-graph":9,"./normalize":10,"./order":15,"./parent-dummy-chains":20,"./position":22,"./rank":24,"./util":27,graphlib:29,lodash:48}],9:[function(require,module,exports){var _=require("lodash"),util=require("./util");module.exports={run:run,cleanup:cleanup};function run(g){var root
 =util.addDummyNode(g,"root",{},"_root"),depths=treeDepths(g),height=_.max(depths)-1,nodeSep=2*height+1;g.graph().nestingRoot=root;_.each(g.edges(),function(e){g.edge(e).minlen*=nodeSep});var weight=sumWeights(g)+1;_.each(g.children(),function(child){dfs(g,root,nodeSep,weight,height,depths,child)});g.graph().nodeRankFactor=nodeSep}function dfs(g,root,nodeSep,weight,height,depths,v){var children=g.children(v);if(!children.length){if(v!==root){g.setEdge(root,v,{weight:0,minlen:nodeSep})}return}var top=util.addBorderNode(g,"_bt"),bottom=util.addBorderNode(g,"_bb"),label=g.node(v);g.setParent(top,v);label.borderTop=top;g.setParent(bottom,v);label.borderBottom=bottom;_.each(children,function(child){dfs(g,root,nodeSep,weight,height,depths,child);var childNode=g.node(child),childTop=childNode.borderTop?childNode.borderTop:child,childBottom=childNode.borderBottom?childNode.borderBottom:child,thisWeight=childNode.borderTop?weight:2*weight,minlen=childTop!==childBottom?1:height-depths[v]+1;g.s
 etEdge(top,childTop,{weight:thisWeight,minlen:minlen,nestingEdge:true});g.setEdge(childBottom,bottom,{weight:thisWeight,minlen:minlen,nestingEdge:true})});if(!g.parent(v)){g.setEdge(root,top,{weight:0,minlen:height+depths[v]})}}function treeDepths(g){var depths={};function dfs(v,depth){var children=g.children(v);if(children&&children.length){_.each(children,function(child){dfs(child,depth+1)})}depths[v]=depth}_.each(g.children(),function(v){dfs(v,1)});return depths}function sumWeights(g){return _.reduce(g.edges(),function(acc,e){return acc+g.edge(e).weight},0)}function cleanup(g){var graphLabel=g.graph();g.removeNode(graphLabel.nestingRoot);delete graphLabel.nestingRoot;_.each(g.edges(),function(e){var edge=g.edge(e);if(edge.nestingEdge){g.removeEdge(e)}})}},{"./util":27,lodash:48}],10:[function(require,module,exports){"use strict";var _=require("lodash"),util=require("./util");module.exports={run:run,undo:undo};function run(g){g.graph().dummyChains=[];_.each(g.edges(),function(edge
 ){normalizeEdge(g,edge)})}function normalizeEdge(g,e){var v=e.v,vRank=g.node(v).rank,w=e.w,wRank=g.node(w).rank,name=e.name,edgeLabel=g.edge(e),labelRank=edgeLabel.labelRank;if(wRank===vRank+1)return;g.removeEdge(e);var dummy,attrs,i;for(i=0,++vRank;vRank<wRank;++i,++vRank){edgeLabel.points=[];attrs={width:0,height:0,edgeLabel:edgeLabel,edgeObj:e,rank:vRank};dummy=util.addDummyNode(g,"edge",attrs,"_d");if(vRank===labelRank){attrs.width=edgeLabel.width;attrs.height=edgeLabel.height;attrs.dummy="edge-label"}g.setEdge(v,dummy,{weight:edgeLabel.weight},name);if(i===0){g.graph().dummyChains.push(dummy)}v=dummy}g.setEdge(v,w,{weight:edgeLabel.weight},name)}function undo(g){_.each(g.graph().dummyChains,function(v){var node=g.node(v),origLabel=node.edgeLabel,w;g.setEdge(node.edgeObj,origLabel);while(node.dummy){w=g.successors(v)[0];g.removeNode(v);origLabel.points.push({x:node.x,y:node.y});if(node.dummy==="edge-label"){origLabel.x=node.x;origLabel.y=node.y}v=w;node=g.node(v)}})}},{"./util":
 27,lodash:48}],11:[function(require,module,exports){var _=require("lodash");module.exports=addSubgraphConstraints;function addSubgraphConstraints(g,cg,vs){var prev={},rootPrev;_.each(vs,function(v){var child=g.parent(v),parent,prevChild;while(child){parent=g.parent(child);if(parent){prevChild=prev[parent];prev[parent]=child}else{prevChild=rootPrev;rootPrev=child}if(prevChild&&prevChild!==child){cg.setEdge(prevChild,child);return}child=parent}})}},{lodash:48}],12:[function(require,module,exports){var _=require("lodash");module.exports=barycenter;function barycenter(g,movable){return _.map(movable,function(v){var inV=g.inEdges(v);if(!inV.length){return{v:v}}else{var result=_.reduce(inV,function(acc,e){var edge=g.edge(e),nodeU=g.node(e.v);return{sum:acc.sum+edge.weight*nodeU.order,weight:acc.weight+edge.weight}},{sum:0,weight:0});return{v:v,barycenter:result.sum/result.weight,weight:result.weight}}})}},{lodash:48}],13:[function(require,module,exports){var _=require("lodash"),Graph=requ
 ire("graphlib").Graph;module.exports=buildLayerGraph;function buildLayerGraph(g,rank,relationship){var root=createRootNode(g),result=new Graph({compound:true}).setGraph({root:root}).setDefaultNodeLabel(function(v){return g.node(v)});_.each(g.nodes(),function(v){var node=g.node(v),parent=g.parent(v);if(node.rank===rank||node.minRank<=rank&&rank<=node.maxRank){result.setNode(v);result.setParent(v,parent||root);_.each(g[relationship](v),function(e){var u=e.v===v?e.w:e.v,edge=result.edge(u,v),weight=!_.isUndefined(edge)?edge.weight:0;result.setEdge(u,v,{weight:g.edge(e).weight+weight})});if(_.has(node,"minRank")){result.setNode(v,{borderLeft:node.borderLeft[rank],borderRight:node.borderRight[rank]})}}});return result}function createRootNode(g){var v;while(g.hasNode(v=_.uniqueId("_root")));return v}},{graphlib:29,lodash:48}],14:[function(require,module,exports){"use strict";var _=require("lodash");module.exports=crossCount;function crossCount(g,layering){var cc=0;for(var i=1;i<layering.l
 ength;++i){cc+=twoLayerCrossCount(g,layering[i-1],layering[i])}return cc}function twoLayerCrossCount(g,northLayer,southLayer){var southPos=_.zipObject(southLayer,_.map(southLayer,function(v,i){return i}));var southEntries=_.flatten(_.map(northLayer,function(v){return _.chain(g.outEdges(v)).map(function(e){return{pos:southPos[e.w],weight:g.edge(e).weight}}).sortBy("pos").value()}),true);var firstIndex=1;while(firstIndex<southLayer.length)firstIndex<<=1;var treeSize=2*firstIndex-1;firstIndex-=1;var tree=_.map(new Array(treeSize),function(){return 0});var cc=0;_.each(southEntries.forEach(function(entry){var index=entry.pos+firstIndex;tree[index]+=entry.weight;var weightSum=0;while(index>0){if(index%2){weightSum+=tree[index+1]}index=index-1>>1;tree[index]+=entry.weight}cc+=entry.weight*weightSum}));return cc}},{lodash:48}],15:[function(require,module,exports){"use strict";var _=require("lodash"),initOrder=require("./init-order"),crossCount=require("./cross-count"),sortSubgraph=require("
 ./sort-subgraph"),buildLayerGraph=require("./build-layer-graph"),addSubgraphConstraints=require("./add-subgraph-constraints"),Graph=require("graphlib").Graph,util=require("../util");module.exports=order;function order(g){var maxRank=util.maxRank(g),downLayerGraphs=buildLayerGraphs(g,_.range(1,maxRank+1),"inEdges"),upLayerGraphs=buildLayerGraphs(g,_.range(maxRank-1,-1,-1),"outEdges");var layering=initOrder(g);assignOrder(g,layering);var bestCC=Number.POSITIVE_INFINITY,best;for(var i=0,lastBest=0;lastBest<4;++i,++lastBest){sweepLayerGraphs(i%2?downLayerGraphs:upLayerGraphs,i%4>=2);layering=util.buildLayerMatrix(g);var cc=crossCount(g,layering);if(cc<bestCC){lastBest=0;best=_.cloneDeep(layering);bestCC=cc}}assignOrder(g,best)}function buildLayerGraphs(g,ranks,relationship){return _.map(ranks,function(rank){return buildLayerGraph(g,rank,relationship)})}function sweepLayerGraphs(layerGraphs,biasRight){var cg=new Graph;_.each(layerGraphs,function(lg){var root=lg.graph().root;var sorted=so
 rtSubgraph(lg,root,cg,biasRight);_.each(sorted.vs,function(v,i){lg.node(v).order=i});addSubgraphConstraints(lg,cg,sorted.vs)})}function assignOrder(g,layering){_.each(layering,function(layer){_.each(layer,function(v,i){g.node(v).order=i})})}},{"../util":27,"./add-subgraph-constraints":11,"./build-layer-graph":13,"./cross-count":14,"./init-order":16,"./sort-subgraph":18,graphlib:29,lodash:48}],16:[function(require,module,exports){"use strict";var _=require("lodash");module.exports=initOrder;function initOrder(g){var visited={},simpleNodes=_.filter(g.nodes(),function(v){return!g.children(v).length}),maxRank=_.max(_.map(simpleNodes,function(v){return g.node(v).rank})),layers=_.map(_.range(maxRank+1),function(){return[]});function dfs(v){if(_.has(visited,v))return;visited[v]=true;var node=g.node(v);layers[node.rank].push(v);_.each(g.successors(v),dfs)}var orderedVs=_.sortBy(simpleNodes,function(v){return g.node(v).rank});_.each(orderedVs,dfs);return layers}},{lodash:48}],17:[function(re
 quire,module,exports){"use strict";var _=require("lodash");module.exports=resolveConflicts;function resolveConflicts(entries,cg){var mappedEntries={};_.each(entries,function(entry,i){var tmp=mappedEntries[entry.v]={indegree:0,"in":[],out:[],vs:[entry.v],i:i};if(!_.isUndefined(entry.barycenter)){tmp.barycenter=entry.barycenter;tmp.weight=entry.weight}});_.each(cg.edges(),function(e){var entryV=mappedEntries[e.v],entryW=mappedEntries[e.w];if(!_.isUndefined(entryV)&&!_.isUndefined(entryW)){entryW.indegree++;entryV.out.push(mappedEntries[e.w])}});var sourceSet=_.filter(mappedEntries,function(entry){return!entry.indegree});return doResolveConflicts(sourceSet)}function doResolveConflicts(sourceSet){var entries=[];function handleIn(vEntry){return function(uEntry){if(uEntry.merged){return}if(_.isUndefined(uEntry.barycenter)||_.isUndefined(vEntry.barycenter)||uEntry.barycenter>=vEntry.barycenter){mergeEntries(vEntry,uEntry)}}}function handleOut(vEntry){return function(wEntry){wEntry.in.push(
 vEntry);if(--wEntry.indegree===0){sourceSet.push(wEntry)}}}while(sourceSet.length){var entry=sourceSet.pop();entries.push(entry);_.each(entry.in.reverse(),handleIn(entry));_.each(entry.out,handleOut(entry))}return _.chain(entries).filter(function(entry){return!entry.merged}).map(function(entry){return _.pick(entry,["vs","i","barycenter","weight"])}).value()}function mergeEntries(target,source){var sum=0,weight=0;if(target.weight){sum+=target.barycenter*target.weight;weight+=target.weight}if(source.weight){sum+=source.barycenter*source.weight;weight+=source.weight}target.vs=source.vs.concat(target.vs);target.barycenter=sum/weight;target.weight=weight;target.i=Math.min(source.i,target.i);source.merged=true}},{lodash:48}],18:[function(require,module,exports){var _=require("lodash"),barycenter=require("./barycenter"),resolveConflicts=require("./resolve-conflicts"),sort=require("./sort");module.exports=sortSubgraph;function sortSubgraph(g,v,cg,biasRight){var movable=g.children(v),node=g.
 node(v),bl=node?node.borderLeft:undefined,br=node?node.borderRight:undefined,subgraphs={};if(bl){movable=_.filter(movable,function(w){return w!==bl&&w!==br})}var barycenters=barycenter(g,movable);_.each(barycenters,function(entry){if(g.children(entry.v).length){var subgraphResult=sortSubgraph(g,entry.v,cg,biasRight);subgraphs[entry.v]=subgraphResult;if(_.has(subgraphResult,"barycenter")){mergeBarycenters(entry,subgraphResult)}}});var entries=resolveConflicts(barycenters,cg);expandSubgraphs(entries,subgraphs);var result=sort(entries,biasRight);if(bl){result.vs=_.flatten([bl,result.vs,br],true);if(g.predecessors(bl).length){var blPred=g.node(g.predecessors(bl)[0]),brPred=g.node(g.predecessors(br)[0]);if(!_.has(result,"barycenter")){result.barycenter=0;result.weight=0}result.barycenter=(result.barycenter*result.weight+blPred.order+brPred.order)/(result.weight+2);result.weight+=2}}return result}function expandSubgraphs(entries,subgraphs){_.each(entries,function(entry){entry.vs=_.flatten
 (entry.vs.map(function(v){if(subgraphs[v]){return subgraphs[v].vs}return v}),true)})}function mergeBarycenters(target,other){if(!_.isUndefined(target.barycenter)){target.barycenter=(target.barycenter*target.weight+other.barycenter*other.weight)/(target.weight+other.weight);target.weight+=other.weight}else{target.barycenter=other.barycenter;target.weight=other.weight}}},{"./barycenter":12,"./resolve-conflicts":17,"./sort":19,lodash:48}],19:[function(require,module,exports){var _=require("lodash"),util=require("../util");module.exports=sort;function sort(entries,biasRight){var parts=util.partition(entries,function(entry){return _.has(entry,"barycenter")});var sortable=parts.lhs,unsortable=_.sortBy(parts.rhs,function(entry){return-entry.i}),vs=[],sum=0,weight=0,vsIndex=0;sortable.sort(compareWithBias(!!biasRight));vsIndex=consumeUnsortable(vs,unsortable,vsIndex);_.each(sortable,function(entry){vsIndex+=entry.vs.length;vs.push(entry.vs);sum+=entry.barycenter*entry.weight;weight+=entry.w
 eight;vsIndex=consumeUnsortable(vs,unsortable,vsIndex)});var result={vs:_.flatten(vs,true)};if(weight){result.barycenter=sum/weight;result.weight=weight}return result}function consumeUnsortable(vs,unsortable,index){var last;while(unsortable.length&&(last=_.last(unsortable)).i<=index){unsortable.pop();vs.push(last.vs);index++}return index}function compareWithBias(bias){return function(entryV,entryW){if(entryV.barycenter<entryW.barycenter){return-1}else if(entryV.barycenter>entryW.barycenter){return 1}return!bias?entryV.i-entryW.i:entryW.i-entryV.i}}},{"../util":27,lodash:48}],20:[function(require,module,exports){var _=require("lodash");module.exports=parentDummyChains;function parentDummyChains(g){var postorderNums=postorder(g);_.each(g.graph().dummyChains,function(v){var node=g.node(v),edgeObj=node.edgeObj,pathData=findPath(g,postorderNums,edgeObj.v,edgeObj.w),path=pathData.path,lca=pathData.lca,pathIdx=0,pathV=path[pathIdx],ascending=true;while(v!==edgeObj.w){node=g.node(v);if(asce
 nding){while((pathV=path[pathIdx])!==lca&&g.node(pathV).maxRank<node.rank){pathIdx++}if(pathV===lca){ascending=false}}if(!ascending){while(pathIdx<path.length-1&&g.node(pathV=path[pathIdx+1]).minRank<=node.rank){pathIdx++}pathV=path[pathIdx]}g.setParent(v,pathV);v=g.successors(v)[0]}})}function findPath(g,postorderNums,v,w){var vPath=[],wPath=[],low=Math.min(postorderNums[v].low,postorderNums[w].low),lim=Math.max(postorderNums[v].lim,postorderNums[w].lim),parent,lca;parent=v;do{parent=g.parent(parent);vPath.push(parent)}while(parent&&(postorderNums[parent].low>low||lim>postorderNums[parent].lim));lca=parent;parent=w;while((parent=g.parent(parent))!==lca){wPath.push(parent)}return{path:vPath.concat(wPath.reverse()),lca:lca}}function postorder(g){var result={},lim=0;function dfs(v){var low=lim;_.each(g.children(v),dfs);result[v]={low:low,lim:lim++}}_.each(g.children(),dfs);return result}},{lodash:48}],21:[function(require,module,exports){"use strict";var _=require("lodash"),util=requi
 re("../util");module.exports={positionX:positionX,findType1Conflicts:findType1Conflicts,findType2Conflicts:findType2Conflicts,addConflict:addConflict,hasConflict:hasConflict,verticalAlignment:verticalAlignment,horizontalCompaction:horizontalCompaction,alignCoordinates:alignCoordinates,findSmallestWidthAlignment:findSmallestWidthAlignment,balance:balance};function findType1Conflicts(g,layering){var conflicts={};function visitLayer(prevLayer,layer){var k0=0,scanPos=0,prevLayerLength=prevLayer.length,lastNode=_.last(layer);_.each(layer,function(v,i){var w=findOtherInnerSegmentNode(g,v),k1=w?g.node(w).order:prevLayerLength;if(w||v===lastNode){_.each(layer.slice(scanPos,i+1),function(scanNode){_.each(g.predecessors(scanNode),function(u){var uLabel=g.node(u),uPos=uLabel.order;if((uPos<k0||k1<uPos)&&!(uLabel.dummy&&g.node(scanNode).dummy)){addConflict(conflicts,u,scanNode)}})});scanPos=i+1;k0=k1}});return layer}_.reduce(layering,visitLayer);return conflicts}function findType2Conflicts(g,la
 yering){var conflicts={};function scan(south,southPos,southEnd,prevNorthBorder,nextNorthBorder){var v;_.each(_.range(southPos,southEnd),function(i){v=south[i];if(g.node(v).dummy){_.each(g.predecessors(v),function(u){var uNode=g.node(u);if(uNode.dummy&&(uNode.order<prevNorthBorder||uNode.order>nextNorthBorder)){addConflict(conflicts,u,v)}})}})}function visitLayer(north,south){var prevNorthPos=-1,nextNorthPos,southPos=0;_.each(south,function(v,southLookahead){if(g.node(v).dummy==="border"){var predecessors=g.predecessors(v);if(predecessors.length){nextNorthPos=g.node(predecessors[0]).order;scan(south,southPos,southLookahead,prevNorthPos,nextNorthPos);southPos=southLookahead;prevNorthPos=nextNorthPos}}scan(south,southPos,south.length,nextNorthPos,north.length)});return south}_.reduce(layering,visitLayer);return conflicts}function findOtherInnerSegmentNode(g,v){if(g.node(v).dummy){return _.find(g.predecessors(v),function(u){return g.node(u).dummy})}}function addConflict(conflicts,v,w){i
 f(v>w){var tmp=v;v=w;w=tmp}var conflictsV=conflicts[v];if(!conflictsV){conflicts[v]=conflictsV={}}conflictsV[w]=true}function hasConflict(conflicts,v,w){if(v>w){var tmp=v;v=w;w=tmp}return _.has(conflicts[v],w)}function verticalAlignment(g,layering,conflicts,neighborFn){var root={},align={},pos={};_.each(layering,function(layer){_.each(layer,function(v,order){root[v]=v;align[v]=v;pos[v]=order})});_.each(layering,function(layer){var prevIdx=-1;_.each(layer,function(v){var ws=neighborFn(v);if(ws.length){ws=_.sortBy(ws,function(w){return pos[w]});var mp=(ws.length-1)/2;for(var i=Math.floor(mp),il=Math.ceil(mp);i<=il;++i){var w=ws[i];if(align[v]===v&&prevIdx<pos[w]&&!hasConflict(conflicts,v,w)){align[w]=v;align[v]=root[v]=root[w];prevIdx=pos[w]}}}})});return{root:root,align:align}}function horizontalCompaction(g,layering,root,align){var shift={},sink={},xs={},pred={},graphLabel=g.graph(),sepFn=sep(graphLabel.nodesep,graphLabel.edgesep);_.each(layering,function(layer){_.each(layer,functio
 n(v,order){sink[v]=v;shift[v]=Number.POSITIVE_INFINITY;pred[v]=layer[order-1]})});_.each(g.nodes(),function(v){if(root[v]===v){placeBlock(g,layering,sepFn,root,align,shift,sink,pred,xs,v)}});_.each(layering,function(layer){_.each(layer,function(v){xs[v]=xs[root[v]];if(v===root[v]&&shift[sink[root[v]]]<Number.POSITIVE_INFINITY){xs[v]+=shift[sink[root[v]]]}})});return xs}function placeBlock(g,layering,sepFn,root,align,shift,sink,pred,xs,v){if(_.has(xs,v))return;xs[v]=0;var w=v,u;do{if(pred[w]){u=root[pred[w]];placeBlock(g,layering,sepFn,root,align,shift,sink,pred,xs,u);if(sink[v]===v){sink[v]=sink[u]}var delta=sepFn(g,w,pred[w]);if(sink[v]!==sink[u]){shift[sink[u]]=Math.min(shift[sink[u]],xs[v]-xs[u]-delta)}else{xs[v]=Math.max(xs[v],xs[u]+delta)}}w=align[w]}while(w!==v)}function findSmallestWidthAlignment(g,xss){return _.min(xss,function(xs){var min=_.min(xs,function(x,v){return x-width(g,v)/2}),max=_.max(xs,function(x,v){return x+width(g,v)/2});return max-min})}function alignCoordina
 tes(xss,alignTo){var alignToMin=_.min(alignTo),alignToMax=_.max(alignTo);_.each(["u","d"],function(vert){_.each(["l","r"],function(horiz){var alignment=vert+horiz,xs=xss[alignment],delta;if(xs===alignTo)return;delta=horiz==="l"?alignToMin-_.min(xs):alignToMax-_.max(xs);if(delta){xss[alignment]=_.mapValues(xs,function(x){return x+delta})}})})}function balance(xss,align){return _.mapValues(xss.ul,function(ignore,v){if(align){return xss[align.toLowerCase()][v]}else{var xs=_.sortBy(_.pluck(xss,v));return(xs[1]+xs[2])/2}})}function positionX(g){var layering=util.buildLayerMatrix(g),conflicts=_.merge(findType1Conflicts(g,layering),findType2Conflicts(g,layering));
+var xss={},adjustedLayering;_.each(["u","d"],function(vert){adjustedLayering=vert==="u"?layering:_.values(layering).reverse();_.each(["l","r"],function(horiz){if(horiz==="r"){adjustedLayering=_.map(adjustedLayering,function(inner){return _.values(inner).reverse()})}var neighborFn=vert==="u"?g.predecessors.bind(g):g.successors.bind(g);var align=verticalAlignment(g,adjustedLayering,conflicts,neighborFn);var xs=horizontalCompaction(g,adjustedLayering,align.root,align.align);if(horiz==="r"){xs=_.mapValues(xs,function(x){return-x})}xss[vert+horiz]=xs})});var smallestWidth=findSmallestWidthAlignment(g,xss);alignCoordinates(xss,smallestWidth);return balance(xss,g.graph().align)}function sep(nodeSep,edgeSep){return function(g,v,u){var vLabel=g.node(v),uLabel=g.node(u),sums=uLabel.width+(uLabel.dummy?edgeSep:nodeSep)+(vLabel.dummy?edgeSep:nodeSep)+vLabel.width;return sums/2}}function width(g,v){return g.node(v).width}},{"../util":27,lodash:48}],22:[function(require,module,exports){"use stric
 t";var _=require("lodash"),util=require("../util"),positionX=require("./bk").positionX;module.exports=position;function position(g){g=util.asNonCompoundGraph(g);var rankDir=(g.graph().rankdir||"tb").toLowerCase();if(rankDir==="lr"||rankDir==="rl"){swapWidthHeight(g)}positionY(g);_.each(positionX(g),function(x,v){g.node(v).x=x});if(rankDir==="bt"||rankDir==="rl"){reverseY(g)}if(rankDir==="lr"||rankDir==="rl"){swapXY(g);swapWidthHeight(g)}translate(g)}function positionY(g){var layering=util.buildLayerMatrix(g),rankSep=g.graph().ranksep,prevY=0;_.each(layering,function(layer){var maxHeight=_.max(_.map(layer,function(v){return g.node(v).height}));_.each(layer,function(v){g.node(v).y=prevY+maxHeight/2});prevY+=maxHeight+rankSep})}function translate(g){var minX=Number.POSITIVE_INFINITY,maxX=0,minY=Number.POSITIVE_INFINITY,maxY=0,graphLabel=g.graph(),marginX=graphLabel.marginx||0,marginY=graphLabel.marginy||0;_.each(g.nodes(),function(v){var node=g.node(v),x=node.x,y=node.y,w=node.width,h=
 node.height;minX=Math.min(minX,x-w/2);maxX=Math.max(maxX,x+w/2);minY=Math.min(minY,y-h/2);maxY=Math.max(maxY,y+h/2)});minX-=marginX;minY-=marginY;_.each(g.nodes(),function(v){var node=g.node(v);node.x-=minX;node.y-=minY});graphLabel.width=maxX-minX+marginX;graphLabel.height=maxY-minY+marginY}function reverseY(g){_.each(g.nodes(),function(v){var node=g.node(v);node.y=-node.y})}function swapWidthHeight(g){_.each(g.nodes(),function(v){var node=g.node(v),width=node.width,height=node.height;node.width=height;node.height=width})}function swapXY(g){_.each(g.nodes(),function(v){var node=g.node(v),x=node.x,y=node.y;node.x=y;node.y=x})}},{"../util":27,"./bk":21,lodash:48}],23:[function(require,module,exports){"use strict";var _=require("lodash"),Graph=require("graphlib").Graph,slack=require("./util").slack;module.exports=feasibleTree;function feasibleTree(g){var t=new Graph({directed:false});var start=g.nodes()[0],size=g.nodeCount();t.setNode(start,{});var edge,delta;while(tightTree(t,g)<size
 ){edge=findMinSlackEdge(t,g);delta=t.hasNode(edge.v)?slack(g,edge):-slack(g,edge);shiftRanks(t,g,delta)}return t}function tightTree(t,g){function dfs(v){_.each(g.nodeEdges(v),function(e){var edgeV=e.v,w=v===edgeV?e.w:edgeV;if(!t.hasNode(w)&&!slack(g,e)){t.setNode(w,{});t.setEdge(v,w,{});dfs(w)}})}_.each(t.nodes(),dfs);return t.nodeCount()}function findMinSlackEdge(t,g){return _.min(g.edges(),function(e){if(t.hasNode(e.v)!==t.hasNode(e.w)){return slack(g,e)}})}function shiftRanks(t,g,delta){_.each(t.nodes(),function(v){g.node(v).rank+=delta})}},{"./util":26,graphlib:29,lodash:48}],24:[function(require,module,exports){"use strict";var rankUtil=require("./util"),longestPath=rankUtil.longestPath,feasibleTree=require("./feasible-tree"),networkSimplex=require("./network-simplex");module.exports=rank;function rank(g){switch(g.graph().ranker){case"network-simplex":networkSimplexRanker(g);break;case"tight-tree":tightTreeRanker(g);break;case"longest-path":longestPathRanker(g);break;default:ne
 tworkSimplexRanker(g)}}var longestPathRanker=longestPath;function tightTreeRanker(g){longestPath(g);feasibleTree(g)}function networkSimplexRanker(g){networkSimplex(g)}},{"./feasible-tree":23,"./network-simplex":25,"./util":26}],25:[function(require,module,exports){"use strict";var _=require("lodash"),feasibleTree=require("./feasible-tree"),slack=require("./util").slack,initRank=require("./util").longestPath,preorder=require("graphlib").alg.preorder,postorder=require("graphlib").alg.postorder,simplify=require("../util").simplify;module.exports=networkSimplex;networkSimplex.initLowLimValues=initLowLimValues;networkSimplex.initCutValues=initCutValues;networkSimplex.calcCutValue=calcCutValue;networkSimplex.leaveEdge=leaveEdge;networkSimplex.enterEdge=enterEdge;networkSimplex.exchangeEdges=exchangeEdges;function networkSimplex(g){g=simplify(g);initRank(g);var t=feasibleTree(g);initLowLimValues(t);initCutValues(t,g);var e,f;while(e=leaveEdge(t)){f=enterEdge(t,g,e);exchangeEdges(t,g,e,f)}}
 function initCutValues(t,g){var vs=postorder(t,t.nodes());vs=vs.slice(0,vs.length-1);_.each(vs,function(v){assignCutValue(t,g,v)})}function assignCutValue(t,g,child){var childLab=t.node(child),parent=childLab.parent;t.edge(child,parent).cutvalue=calcCutValue(t,g,child)}function calcCutValue(t,g,child){var childLab=t.node(child),parent=childLab.parent,childIsTail=true,graphEdge=g.edge(child,parent),cutValue=0;if(!graphEdge){childIsTail=false;graphEdge=g.edge(parent,child)}cutValue=graphEdge.weight;_.each(g.nodeEdges(child),function(e){var isOutEdge=e.v===child,other=isOutEdge?e.w:e.v;if(other!==parent){var pointsToHead=isOutEdge===childIsTail,otherWeight=g.edge(e).weight;cutValue+=pointsToHead?otherWeight:-otherWeight;if(isTreeEdge(t,child,other)){var otherCutValue=t.edge(child,other).cutvalue;cutValue+=pointsToHead?-otherCutValue:otherCutValue}}});return cutValue}function initLowLimValues(tree,root){if(arguments.length<2){root=tree.nodes()[0]}dfsAssignLowLim(tree,{},1,root)}function
  dfsAssignLowLim(tree,visited,nextLim,v,parent){var low=nextLim,label=tree.node(v);visited[v]=true;_.each(tree.neighbors(v),function(w){if(!_.has(visited,w)){nextLim=dfsAssignLowLim(tree,visited,nextLim,w,v)}});label.low=low;label.lim=nextLim++;if(parent){label.parent=parent}else{delete label.parent}return nextLim}function leaveEdge(tree){return _.find(tree.edges(),function(e){return tree.edge(e).cutvalue<0})}function enterEdge(t,g,edge){var v=edge.v,w=edge.w;if(!g.hasEdge(v,w)){v=edge.w;w=edge.v}var vLabel=t.node(v),wLabel=t.node(w),tailLabel=vLabel,flip=false;if(vLabel.lim>wLabel.lim){tailLabel=wLabel;flip=true}var candidates=_.filter(g.edges(),function(edge){return flip===isDescendant(t,t.node(edge.v),tailLabel)&&flip!==isDescendant(t,t.node(edge.w),tailLabel)});return _.min(candidates,function(edge){return slack(g,edge)})}function exchangeEdges(t,g,e,f){var v=e.v,w=e.w;t.removeEdge(v,w);t.setEdge(f.v,f.w,{});initLowLimValues(t);initCutValues(t,g);updateRanks(t,g)}function update
 Ranks(t,g){var root=_.find(t.nodes(),function(v){return!g.node(v).parent}),vs=preorder(t,root);vs=vs.slice(1);_.each(vs,function(v){var parent=t.node(v).parent,edge=g.edge(v,parent),flipped=false;if(!edge){edge=g.edge(parent,v);flipped=true}g.node(v).rank=g.node(parent).rank+(flipped?edge.minlen:-edge.minlen)})}function isTreeEdge(tree,u,v){return tree.hasEdge(u,v)}function isDescendant(tree,vLabel,rootLabel){return rootLabel.low<=vLabel.lim&&vLabel.lim<=rootLabel.lim}},{"../util":27,"./feasible-tree":23,"./util":26,graphlib:29,lodash:48}],26:[function(require,module,exports){"use strict";var _=require("lodash");module.exports={longestPath:longestPath,slack:slack};function longestPath(g){var visited={};function dfs(v){var label=g.node(v);if(_.has(visited,v)){return label.rank}visited[v]=true;var rank=_.min(_.map(g.outEdges(v),function(e){return dfs(e.w)-g.edge(e).minlen}));if(rank===Number.POSITIVE_INFINITY){rank=0}return label.rank=rank}_.each(g.sources(),dfs)}function slack(g,e){r
 eturn g.node(e.w).rank-g.node(e.v).rank-g.edge(e).minlen}},{lodash:48}],27:[function(require,module,exports){"use strict";var _=require("lodash"),Graph=require("graphlib").Graph;module.exports={addDummyNode:addDummyNode,simplify:simplify,asNonCompoundGraph:asNonCompoundGraph,successorWeights:successorWeights,predecessorWeights:predecessorWeights,intersectRect:intersectRect,buildLayerMatrix:buildLayerMatrix,normalizeRanks:normalizeRanks,removeEmptyRanks:removeEmptyRanks,addBorderNode:addBorderNode,maxRank:maxRank,partition:partition,time:time,notime:notime};function addDummyNode(g,type,attrs,name){var v;do{v=_.uniqueId(name)}while(g.hasNode(v));attrs.dummy=type;g.setNode(v,attrs);return v}function simplify(g){var simplified=(new Graph).setGraph(g.graph());_.each(g.nodes(),function(v){simplified.setNode(v,g.node(v))});_.each(g.edges(),function(e){var simpleLabel=simplified.edge(e.v,e.w)||{weight:0,minlen:1},label=g.edge(e);simplified.setEdge(e.v,e.w,{weight:simpleLabel.weight+label.we
 ight,minlen:Math.max(simpleLabel.minlen,label.minlen)})});return simplified}function asNonCompoundGraph(g){var simplified=new Graph({multigraph:g.isMultigraph()}).setGraph(g.graph());_.each(g.nodes(),function(v){if(!g.children(v).length){simplified.setNode(v,g.node(v))}});_.each(g.edges(),function(e){simplified.setEdge(e,g.edge(e))});return simplified}function successorWeights(g){var weightMap=_.map(g.nodes(),function(v){var sucs={};_.each(g.outEdges(v),function(e){sucs[e.w]=(sucs[e.w]||0)+g.edge(e).weight});return sucs});return _.zipObject(g.nodes(),weightMap)}function predecessorWeights(g){var weightMap=_.map(g.nodes(),function(v){var preds={};_.each(g.inEdges(v),function(e){preds[e.v]=(preds[e.v]||0)+g.edge(e).weight});return preds});return _.zipObject(g.nodes(),weightMap)}function intersectRect(rect,point){var x=rect.x;var y=rect.y;var dx=point.x-x;var dy=point.y-y;var w=rect.width/2;var h=rect.height/2;if(!dx&&!dy){throw new Error("Not possible to find intersection inside of th
 e rectangle")}var sx,sy;if(Math.abs(dy)*w>Math.abs(dx)*h){if(dy<0){h=-h}sx=h*dx/dy;sy=h}else{if(dx<0){w=-w}sx=w;sy=w*dy/dx}return{x:x+sx,y:y+sy}}function buildLayerMatrix(g){var layering=_.map(_.range(maxRank(g)+1),function(){return[]});_.each(g.nodes(),function(v){var node=g.node(v),rank=node.rank;if(!_.isUndefined(rank)){layering[rank][node.order]=v}});return layering}function normalizeRanks(g){var min=_.min(_.map(g.nodes(),function(v){return g.node(v).rank}));_.each(g.nodes(),function(v){var node=g.node(v);if(_.has(node,"rank")){node.rank-=min}})}function removeEmptyRanks(g){var offset=_.min(_.map(g.nodes(),function(v){return g.node(v).rank}));var layers=[];_.each(g.nodes(),function(v){var rank=g.node(v).rank-offset;if(!_.has(layers,rank)){layers[rank]=[]}layers[rank].push(v)});var delta=0,nodeRankFactor=g.graph().nodeRankFactor;_.each(layers,function(vs,i){if(_.isUndefined(vs)&&i%nodeRankFactor!==0){--delta}else if(delta){_.each(vs,function(v){g.node(v).rank+=delta})}})}function
  addBorderNode(g,prefix,rank,order){var node={width:0,height:0};if(arguments.length>=4){node.rank=rank;node.order=order}return addDummyNode(g,"border",node,prefix)}function maxRank(g){return _.max(_.map(g.nodes(),function(v){var rank=g.node(v).rank;if(!_.isUndefined(rank)){return rank}}))}function partition(collection,fn){var result={lhs:[],rhs:[]};_.each(collection,function(value){if(fn(value)){result.lhs.push(value)}else{result.rhs.push(value)}});return result}function time(name,fn){var start=_.now();try{return fn()}finally{console.log(name+" time: "+(_.now()-start)+"ms")}}function notime(name,fn){return fn()}},{graphlib:29,lodash:48}],28:[function(require,module,exports){module.exports="0.5.1"},{}],29:[function(require,module,exports){var _=require("lodash");module.exports=_.clone(require("./lib"));module.exports.json=require("./lib/json");module.exports.alg=require("./lib/alg")},{"./lib":45,"./lib/alg":36,"./lib/json":46,lodash:48}],30:[function(require,module,exports){var _=req
 uire("lodash");module.exports=components;function components(g){var visited={},cmpts=[],cmpt;function dfs(v){if(_.has(visited,v))return;visited[v]=true;cmpt.push(v);_.each(g.successors(v),dfs);_.each(g.predecessors(v),dfs)}_.each(g.nodes(),function(v){cmpt=[];dfs(v);if(cmpt.length){cmpts.push(cmpt)}});return cmpts}},{lodash:48}],31:[function(require,module,exports){var _=require("lodash");module.exports=dfs;function dfs(g,vs,order){if(!_.isArray(vs)){vs=[vs]}var acc=[],visited={};_.each(vs,function(v){if(!g.hasNode(v)){throw new Error("Graph does not have node: "+v)}doDfs(g,v,order==="post",visited,acc)});return acc}function doDfs(g,v,postorder,visited,acc){if(!_.has(visited,v)){visited[v]=true;if(!postorder){acc.push(v)}_.each(g.neighbors(v),function(w){doDfs(g,w,postorder,visited,acc)});if(postorder){acc.push(v)}}}},{lodash:48}],32:[function(require,module,exports){var dijkstra=require("./dijkstra"),_=require("lodash");module.exports=dijkstraAll;function dijkstraAll(g,weightFunc,e
 dgeFunc){return _.transform(g.nodes(),function(acc,v){acc[v]=dijkstra(g,v,weightFunc,edgeFunc)},{})}},{"./dijkstra":33,lodash:48}],33:[function(require,module,exports){var _=require("lodash"),PriorityQueue=require("../data/priority-queue");module.exports=dijkstra;var DEFAULT_WEIGHT_FUNC=_.constant(1);function dijkstra(g,source,weightFn,edgeFn){return runDijkstra(g,String(source),weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runDijkstra(g,source,weightFn,edgeFn){var results={},pq=new PriorityQueue,v,vEntry;var updateNeighbors=function(edge){var w=edge.v!==v?edge.v:edge.w,wEntry=results[w],weight=weightFn(edge),distance=vEntry.distance+weight;if(weight<0){throw new Error("dijkstra does not allow negative edge weights. "+"Bad edge: "+edge+" Weight: "+weight)}if(distance<wEntry.distance){wEntry.distance=distance;wEntry.predecessor=v;pq.decrease(w,distance)}};g.nodes().forEach(function(v){var distance=v===source?0:Number.POSITIVE_INFINITY;results[v]={d
 istance:distance};pq.add(v,distance)});while(pq.size()>0){v=pq.removeMin();vEntry=results[v];if(vEntry.distance===Number.POSITIVE_INFINITY){break}edgeFn(v).forEach(updateNeighbors)}return results}},{"../data/priority-queue":43,lodash:48}],34:[function(require,module,exports){var _=require("lodash"),tarjan=require("./tarjan");module.exports=findCycles;function findCycles(g){return _.filter(tarjan(g),function(cmpt){return cmpt.length>1})}},{"./tarjan":41,lodash:48}],35:[function(require,module,exports){var _=require("lodash");module.exports=floydWarshall;var DEFAULT_WEIGHT_FUNC=_.constant(1);function floydWarshall(g,weightFn,edgeFn){return runFloydWarshall(g,weightFn||DEFAULT_WEIGHT_FUNC,edgeFn||function(v){return g.outEdges(v)})}function runFloydWarshall(g,weightFn,edgeFn){var results={},nodes=g.nodes();nodes.forEach(function(v){results[v]={};results[v][v]={distance:0};nodes.forEach(function(w){if(v!==w){results[v][w]={distance:Number.POSITIVE_INFINITY}}});edgeFn(v).forEach(function(
 edge){var w=edge.v===v?edge.w:edge.v,d=weightFn(edge);results[v][w]={distance:d,predecessor:v}})});nodes.forEach(function(k){var rowK=results[k];nodes.forEach(function(i){var rowI=results[i];nodes.forEach(function(j){var ik=rowI[k];var kj=rowK[j];var ij=rowI[j];var altDistance=ik.distance+kj.distance;if(altDistance<ij.distance){ij.distance=altDistance;ij.predecessor=kj.predecessor}})})});return results}},{lodash:48}],36:[function(require,module,exports){module.exports={components:require("./components"),dijkstra:require("./dijkstra"),dijkstraAll:require("./dijkstra-all"),findCycles:require("./find-cycles"),floydWarshall:require("./floyd-warshall"),isAcyclic:require("./is-acyclic"),postorder:require("./postorder"),preorder:require("./preorder"),prim:require("./prim"),tarjan:require("./tarjan"),topsort:require("./topsort")}},{"./components":30,"./dijkstra":33,"./dijkstra-all":32,"./find-cycles":34,"./floyd-warshall":35,"./is-acyclic":37,"./postorder":38,"./preorder":39,"./prim":40,"./
 tarjan":41,"./topsort":42}],37:[function(require,module,exports){var topsort=require("./topsort");module.exports=isAcyclic;function isAcyclic(g){try{topsort(g)}catch(e){if(e instanceof topsort.CycleException){return false}throw e}return true}},{"./topsort":42}],38:[function(require,module,exports){var dfs=require("./dfs");module.exports=postorder;function postorder(g,vs){return dfs(g,vs,"post")}},{"./dfs":31}],39:[function(require,module,exports){var dfs=require("./dfs");module.exports=preorder;function preorder(g,vs){return dfs(g,vs,"pre")}},{"./dfs":31}],40:[function(require,module,exports){var _=require("lodash"),Graph=require("../graph"),PriorityQueue=require("../data/priority-queue");module.exports=prim;function prim(g,weightFunc){var result=new Graph,parents={},pq=new PriorityQueue,v;function updateNeighbors(edge){var w=edge.v===v?edge.w:edge.v,pri=pq.priority(w);if(pri!==undefined){var edgeWeight=weightFunc(edge);if(edgeWeight<pri){parents[w]=v;pq.decrease(w,edgeWeight)}}}if(
 g.nodeCount()===0){return result}_.each(g.nodes(),function(v){pq.add(v,Number.POSITIVE_INFINITY);result.setNode(v)});pq.decrease(g.nodes()[0],0);var init=false;while(pq.size()>0){v=pq.removeMin();if(_.has(parents,v)){result.setEdge(v,parents[v])}else if(init){throw new Error("Input graph is not connected: "+g)}else{init=true}g.nodeEdges(v).forEach(updateNeighbors)}return result}},{"../data/priority-queue":43,"../graph":44,lodash:48}],41:[function(require,module,exports){var _=require("lodash");module.exports=tarjan;function tarjan(g){var index=0,stack=[],visited={},results=[];function dfs(v){var entry=visited[v]={onStack:true,lowlink:index,index:index++};stack.push(v);g.successors(v).forEach(function(w){if(!_.has(visited,w)){dfs(w);entry.lowlink=Math.min(entry.lowlink,visited[w].lowlink)}else if(visited[w].onStack){entry.lowlink=Math.min(entry.lowlink,visited[w].index)}});if(entry.lowlink===entry.index){var cmpt=[],w;do{w=stack.pop();visited[w].onStack=false;cmpt.push(w)}while(v!==w
 );results.push(cmpt)}}g.nodes().forEach(function(v){if(!_.has(visited,v)){dfs(v)}});return results}},{lodash:48}],42:[function(require,module,exports){var _=require("lodash");module.exports=topsort;topsort.CycleException=CycleException;function topsort(g){var visited={},stack={},results=[];function visit(node){if(_.has(stack,node)){throw new CycleException}if(!_.has(visited,node)){stack[node]=true;visited[node]=true;_.each(g.predecessors(node),visit);delete stack[node];results.push(node)}}_.each(g.sinks(),visit);if(_.size(visited)!==g.nodeCount()){throw new CycleException}return results}function CycleException(){}},{lodash:48}],43:[function(require,module,exports){var _=require("lodash");module.exports=PriorityQueue;function PriorityQueue(){this._arr=[];this._keyIndices={}}PriorityQueue.prototype.size=function(){return this._arr.length};PriorityQueue.prototype.keys=function(){return this._arr.map(function(x){return x.key})};PriorityQueue.prototype.has=function(key){return _.has(this
 ._keyIndices,key)};PriorityQueue.prototype.priority=function(key){var index=this._keyIndices[key];if(index!==undefined){return this._arr[index].priority}};PriorityQueue.prototype.min=function(){if(this.size()===0){throw new Error("Queue underflow")}return this._arr[0].key};PriorityQueue.prototype.add=function(key,priority){var keyIndices=this._keyIndices;key=String(key);if(!_.has(keyIndices,key)){var arr=this._arr;var index=arr.length;keyIndices[key]=index;arr.push({key:key,priority:priority});this._decrease(index);return true}return false};PriorityQueue.prototype.removeMin=function(){this._swap(0,this._arr.length-1);var min=this._arr.pop();delete this._keyIndices[min.key];this._heapify(0);return min.key};PriorityQueue.prototype.decrease=function(key,priority){var index=this._keyIndices[key];if(priority>this._arr[index].priority){throw new Error("New priority is greater than current priority. "+"Key: "+key+" Old: "+this._arr[index].priority+" New: "+priority)}this._arr[index].priori
 ty=priority;this._decrease(index)};PriorityQueue.prototype._heapify=function(i){var arr=this._arr;var l=2*i,r=l+1,largest=i;if(l<arr.length){largest=arr[l].priority<arr[largest].priority?l:largest;if(r<arr.length){largest=arr[r].priority<arr[largest].priority?r:largest}if(largest!==i){this._swap(i,largest);this._heapify(largest)}}};PriorityQueue.prototype._decrease=function(index){var arr=this._arr;var priority=arr[index].priority;var parent;while(index!==0){parent=index>>1;if(arr[parent].priority<priority){break}this._swap(index,parent);index=parent}};PriorityQueue.prototype._swap=function(i,j){var arr=this._arr;var keyIndices=this._keyIndices;var origArrI=arr[i];var origArrJ=arr[j];arr[i]=origArrJ;arr[j]=origArrI;keyIndices[origArrJ.key]=i;keyIndices[origArrI.key]=j}},{lodash:48}],44:[function(require,module,exports){"use strict";var _=require("lodash");module.exports=Graph;var DEFAULT_EDGE_NAME="\x00",GRAPH_NODE="\x00",EDGE_KEY_DELIM="";function Graph(opts){this._isDirected=_.ha
 s(opts,"directed")?opts.directed:true;this._isMultigraph=_.has(opts,"multigraph")?opts.multigraph:false;this._isCompound=_.has(opts,"compound")?opts.compound:false;this._label=undefined;this._defaultNodeLabelFn=_.constant(undefined);this._defaultEdgeLabelFn=_.constant(undefined);this._nodes={};if(this._isCompound){this._parent={};this._children={};this._children[GRAPH_NODE]={}}this._in={};this._preds={};this._out={};this._sucs={};this._edgeObjs={};this._edgeLabels={}}Graph.prototype._nodeCount=0;Graph.prototype._edgeCount=0;Graph.prototype.isDirected=function(){return this._isDirected};Graph.prototype.isMultigraph=function(){return this._isMultigraph};Graph.prototype.isCompound=function(){return this._isCompound};Graph.prototype.setGraph=function(label){this._label=label;return this};Graph.prototype.graph=function(){return this._label};Graph.prototype.setDefaultNodeLabel=function(newDefault){if(!_.isFunction(newDefault)){newDefault=_.constant(newDefault)}this._defaultNodeLabelFn=new
 Default;return this};Graph.prototype.nodeCount=function(){return this._nodeCount};Graph.prototype.nodes=function(){return _.keys(this._nodes)};Graph.prototype.sources=function(){return _.filter(this.nodes(),function(v){return _.isEmpty(this._in[v])},this)};Graph.prototype.sinks=function(){return _.filter(this.nodes(),function(v){return _.isEmpty(this._out[v])},this)};Graph.prototype.setNodes=function(vs,value){var args=arguments;_.each(vs,function(v){if(args.length>1){this.setNode(v,value)}else{this.setNode(v)}},this);return this};Graph.prototype.setNode=function(v,value){if(_.has(this._nodes,v)){if(arguments.length>1){this._nodes[v]=value}return this}this._nodes[v]=arguments.length>1?value:this._defaultNodeLabelFn(v);if(this._isCompound){this._parent[v]=GRAPH_NODE;this._children[v]={};this._children[GRAPH_NODE][v]=true}this._in[v]={};this._preds[v]={};this._out[v]={};this._sucs[v]={};++this._nodeCount;return this};Graph.prototype.node=function(v){return this._nodes[v]};Graph.protot
 ype.hasNode=function(v){return _.has(this._nodes,v)};Graph.prototype.removeNode=function(v){var self=this;if(_.has(this._nodes,v)){var removeEdge=function(e){self.removeEdge(self._edgeObjs[e])};delete this._nodes[v];if(this._isCompound){this._removeFromParentsChildList(v);delete this._parent[v];_.each(this.children(v),function(child){this.setParent(child)},this);delete this._children[v]}_.each(_.keys(this._in[v]),removeEdge);delete this._in[v];delete this._preds[v];_.each(_.keys(this._out[v]),removeEdge);delete this._out[v];delete this._sucs[v];--this._nodeCount}return this};Graph.prototype.setParent=function(v,parent){if(!this._isCompound){throw new Error("Cannot set parent in a non-compound graph")}if(_.isUndefined(parent)){parent=GRAPH_NODE}else{for(var ancestor=parent;!_.isUndefined(ancestor);ancestor=this.parent(ancestor)){if(ancestor===v){throw new Error("Setting "+parent+" as parent of "+v+" would create create a cycle")}}this.setNode(parent)}this.setNode(v);this._removeFromP
 arentsChildList(v);this._parent[v]=parent;this._children[parent][v]=true;return this};Graph.prototype._removeFromParentsChildList=function(v){delete this._children[this._parent[v]][v]};Graph.prototype.parent=function(v){if(this._isCompound){var parent=this._parent[v];if(parent!==GRAPH_NODE){return parent}}};Graph.prototype.children=function(v){if(_.isUndefined(v)){v=GRAPH_NODE}if(this._isCompound){var children=this._children[v];if(children){return _.keys(children)}}else if(v===GRAPH_NODE){return this.nodes()}else if(this.hasNode(v)){return[]}};Graph.prototype.predecessors=function(v){var predsV=this._preds[v];if(predsV){return _.keys(predsV)}};Graph.prototype.successors=function(v){var sucsV=this._sucs[v];if(sucsV){return _.keys(sucsV)}};Graph.prototype.neighbors=function(v){var preds=this.predecessors(v);if(preds){return _.union(preds,this.successors(v))}};Graph.prototype.setDefaultEdgeLabel=function(newDefault){if(!_.isFunction(newDefault)){newDefault=_.constant(newDefault)}this._
 defaultEdgeLabelFn=newDefault;return this};Graph.prototype.edgeCount=function(){return this._edgeCount};Graph.prototype.edges=function(){return _.values(this._edgeObjs)};Graph.prototype.setPath=function(vs,value){var self=this,args=arguments;_.reduce(vs,function(v,w){if(args.length>1){self.setEdge(v,w,value)}else{self.setEdge(v,w)}return w});return this};Graph.prototype.setEdge=function(v,w,value,name){var valueSpecified=arguments.length>2;if(_.isPlainObject(arguments[0])){v=arguments[0].v;w=arguments[0].w;name=arguments[0].name;if(arguments.length===2){value=arguments[1];valueSpecified=true}}var e=edgeArgsToId(this._isDirected,v,w,name);if(_.has(this._edgeLabels,e)){if(valueSpecified){this._edgeLabels[e]=value}return this}if(!_.isUndefined(name)&&!this._isMultigraph){throw new Error("Cannot set a named edge when isMultigraph = false")}this.setNode(v);this.setNode(w);this._edgeLabels[e]=valueSpecified?value:this._defaultEdgeLabelFn(v,w,name);var edgeObj=edgeArgsToObj(this._isDirecte
 d,v,w,name);v=edgeObj.v;w=edgeObj.w;Object.freeze(edgeObj);this._edgeObjs[e]=edgeObj;incrementOrInitEntry(this._preds[w],v);incrementOrInitEntry(this._sucs[v],w);this._in[w][e]=edgeObj;this._out[v][e]=edgeObj;this._edgeCount++;return this};Graph.prototype.edge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);return this._edgeLabels[e]};Graph.prototype.hasEdge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name);return _.has(this._edgeLabels,e)};Graph.prototype.removeEdge=function(v,w,name){var e=arguments.length===1?edgeObjToId(this._isDirected,arguments[0]):edgeArgsToId(this._isDirected,v,w,name),edge=this._edgeObjs[e];if(edge){v=edge.v;w=edge.w;delete this._edgeLabels[e];delete this._edgeObjs[e];decrementOrRemoveEntry(this._preds[w],v);decrementOrRemoveEntry(this._sucs[v],w);delete this._in[w][e];delete this._out[v][e];this._ed
 geCount--}return this};Graph.prototype.inEdges=function(v,u){var inV=this._in[v];if(inV){var edges=_.values(inV);if(!u){return edges}return _.filter(edges,function(edge){return edge.v===u})}};Graph.prototype.outEdges=function(v,w){var outV=this._out[v];if(outV){var edges=_.values(outV);if(!w){return edges}return _.filter(edges,function(edge){return edge.w===w})}};Graph.prototype.nodeEdges=function(v,w){var inEdges=this.inEdges(v,w);if(inEdges){return inEdges.concat(this.outEdges(v,w))}};function incrementOrInitEntry(map,k){if(_.has(map,k)){map[k]++}else{map[k]=1}}function decrementOrRemoveEntry(map,k){if(!--map[k]){delete map[k]}}function edgeArgsToId(isDirected,v,w,name){if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}return v+EDGE_KEY_DELIM+w+EDGE_KEY_DELIM+(_.isUndefined(name)?DEFAULT_EDGE_NAME:name)}function edgeArgsToObj(isDirected,v,w,name){if(!isDirected&&v>w){var tmp=v;v=w;w=tmp}var edgeObj={v:v,w:w};if(name){edgeObj.name=name}return edgeObj}function edgeObjToId(isDirected,edgeObj)
 {return edgeArgsToId(isDirected,edgeObj.v,edgeObj.w,edgeObj.name)}},{lodash:48}],45:[function(require,module,exports){module.exports={Graph:require("./graph"),version:require("./version")}},{"./graph":44,"./version":47}],46:[function(require,module,exports){var _=require("lodash"),Graph=require("./graph");module.exports={write:write,read:read};function write(g){var json={options:{directed:g.isDirected(),multigraph:g.isMultigraph(),compound:g.isCompound()},nodes:writeNodes(g),edges:writeEdges(g)};if(!_.isUndefined(g.graph())){json.value=_.clone(g.graph())}return json}function writeNodes(g){return _.map(g.nodes(),function(v){var nodeValue=g.node(v),parent=g.parent(v),node={v:v};if(!_.isUndefined(nodeValue)){node.value=nodeValue}if(!_.isUndefined(parent)){node.parent=parent}return node})}function writeEdges(g){return _.map(g.edges(),function(e){var edgeValue=g.edge(e),edge={v:e.v,w:e.w};if(!_.isUndefined(e.name)){edge.name=e.name}if(!_.isUndefined(edgeValue)){edge.value=edgeValue}retur
 n edge})}function read(json){var g=new Graph(json.options).setGraph(json.value);_.each(json.nodes,function(entry){g.setNode(entry.v,entry.value);if(entry.parent){g.setParent(entry.v,entry.parent)}});_.each(json.edges,function(entry){g.setEdge({v:entry.v,w:entry.w,name:entry.name},entry.value)});return g}},{"./graph":44,lodash:48}],47:[function(require,module,exports){module.exports="0.8.0"},{}],48:[function(require,module,exports){(function(global){(function(){var undefined;var arrayPool=[],objectPool=[];var idCounter=0;var keyPrefix=+new Date+"";var largeArraySize=75;var maxPoolSize=40;var whitespace="   \f "+"\n\r\u2028\u2029"+" ᠎              ";var reEmptyStringLeading=/\b__p \+= '';/g,reEmptyStringMiddle=/\b(__p \+=) '' \+/g,reEmptyStringTrailing=/(__e\(.*?\)|\b__t\)) \+\n'';/g;var reEsTemplate=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;var reFlags=/\w*$/;var reFuncName=/^\s*function[ \n\r\t]+\w/;var reInterpolate=/<%=([\s\S]+?)%>/g;var reLeadingSpaces
 AndZeros=RegExp("^["+whitespace+"]*0+(?=.$)");var reNoMatch=/($^)/;var reThis=/\bthis\b/;var reUnescapedString=/['\n\r\t\u2028\u2029\\]/g;var contextProps=["Array","Boolean","Date","Function","Math","Number","Object","RegExp","String","_","attachEvent","clearTimeout","isFinite","isNaN","parseInt","setTimeout"];var templateCounter=0;var argsClass="[object Arguments]",arrayClass="[object Array]",boolClass="[object Boolean]",dateClass="[object Date]",funcClass="[object Function]",numberClass="[object Number]",objectClass="[object Object]",regexpClass="[object RegExp]",stringClass="[object String]";var cloneableClasses={};cloneableClasses[funcClass]=false;cloneableClasses[argsClass]=cloneableClasses[arrayClass]=cloneableClasses[boolClass]=cloneableClasses[dateClass]=cloneableClasses[numberClass]=cloneableClasses[objectClass]=cloneableClasses[regexpClass]=cloneableClasses[stringClass]=true;var debounceOptions={leading:false,maxWait:0,trailing:false};var descriptor={configurable:false,enu
 merable:false,value:null,writable:false};var objectTypes={"boolean":false,"function":true,object:true,number:false,string:false,undefined:false};var stringEscapes={"\\":"\\","'":"'","\n":"n","\r":"r","  ":"t","\u2028":"u2028","\u2029":"u2029"};var root=objectTypes[typeof window]&&window||this;var freeExports=objectTypes[typeof exports]&&exports&&!exports.nodeType&&exports;var freeModule=objectTypes[typeof module]&&module&&!module.nodeType&&module;var moduleExports=freeModule&&freeModule.exports===freeExports&&freeExports;var freeGlobal=objectTypes[typeof global]&&global;if(freeGlobal&&(freeGlobal.global===freeGlobal||freeGlobal.window===freeGlobal)){root=freeGlobal}function baseIndexOf(array,value,fromIndex){var index=(fromIndex||0)-1,length=array?array.length:0;while(++index<length){if(array[index]===value){return index}}return-1}function cacheIndexOf(cache,value){var type=typeof value;cache=cache.cache;if(type=="boolean"||value==null){return cache[value]?0:-1}if(type!="number"&&ty
 pe!="string"){type="object"}var key=type=="number"?value:keyPrefix+value;cache=(cache=cache[type])&&cache[key];return type=="object"?cache&&baseIndexOf(cache,value)>-1?0:-1:cache?0:-1
+}function cachePush(value){var cache=this.cache,type=typeof value;if(type=="boolean"||value==null){cache[value]=true}else{if(type!="number"&&type!="string"){type="object"}var key=type=="number"?value:keyPrefix+value,typeCache=cache[type]||(cache[type]={});if(type=="object"){(typeCache[key]||(typeCache[key]=[])).push(value)}else{typeCache[key]=true}}}function charAtCallback(value){return value.charCodeAt(0)}function compareAscending(a,b){var ac=a.criteria,bc=b.criteria,index=-1,length=ac.length;while(++index<length){var value=ac[index],other=bc[index];if(value!==other){if(value>other||typeof value=="undefined"){return 1}if(value<other||typeof other=="undefined"){return-1}}}return a.index-b.index}function createCache(array){var index=-1,length=array.length,first=array[0],mid=array[length/2|0],last=array[length-1];if(first&&typeof first=="object"&&mid&&typeof mid=="object"&&last&&typeof last=="object"){return false}var cache=getObject();cache["false"]=cache["null"]=cache["true"]=cache[
 "undefined"]=false;var result=getObject();result.array=array;result.cache=cache;result.push=cachePush;while(++index<length){result.push(array[index])}return result}function escapeStringChar(match){return"\\"+stringEscapes[match]}function getArray(){return arrayPool.pop()||[]}function getObject(){return objectPool.pop()||{array:null,cache:null,criteria:null,"false":false,index:0,"null":false,number:null,object:null,push:null,string:null,"true":false,undefined:false,value:null}}function releaseArray(array){array.length=0;if(arrayPool.length<maxPoolSize){arrayPool.push(array)}}function releaseObject(object){var cache=object.cache;if(cache){releaseObject(cache)}object.array=object.cache=object.criteria=object.object=object.number=object.string=object.value=null;if(objectPool.length<maxPoolSize){objectPool.push(object)}}function slice(array,start,end){start||(start=0);if(typeof end=="undefined"){end=array?array.length:0}var index=-1,length=end-start||0,result=Array(length<0?0:length);whi
 le(++index<length){result[index]=array[start+index]}return result}function runInContext(context){context=context?_.defaults(root.Object(),context,_.pick(root,contextProps)):root;var Array=context.Array,Boolean=context.Boolean,Date=context.Date,Function=context.Function,Math=context.Math,Number=context.Number,Object=context.Object,RegExp=context.RegExp,String=context.String,TypeError=context.TypeError;var arrayRef=[];var objectProto=Object.prototype;var oldDash=context._;var toString=objectProto.toString;var reNative=RegExp("^"+String(toString).replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/toString| for [^\]]+/g,".*?")+"$");var ceil=Math.ceil,clearTimeout=context.clearTimeout,floor=Math.floor,fnToString=Function.prototype.toString,getPrototypeOf=isNative(getPrototypeOf=Object.getPrototypeOf)&&getPrototypeOf,hasOwnProperty=objectProto.hasOwnProperty,push=arrayRef.push,setTimeout=context.setTimeout,splice=arrayRef.splice,unshift=arrayRef.unshift;var defineProperty=function(){try{var o
 ={},func=isNative(func=Object.defineProperty)&&func,result=func(o,o,o)&&func}catch(e){}return result}();var nativeCreate=isNative(nativeCreate=Object.create)&&nativeCreate,nativeIsArray=isNative(nativeIsArray=Array.isArray)&&nativeIsArray,nativeIsFinite=context.isFinite,nativeIsNaN=context.isNaN,nativeKeys=isNative(nativeKeys=Object.keys)&&nativeKeys,nativeMax=Math.max,nativeMin=Math.min,nativeParseInt=context.parseInt,nativeRandom=Math.random;var ctorByClass={};ctorByClass[arrayClass]=Array;ctorByClass[boolClass]=Boolean;ctorByClass[dateClass]=Date;ctorByClass[funcClass]=Function;ctorByClass[objectClass]=Object;ctorByClass[numberClass]=Number;ctorByClass[regexpClass]=RegExp;ctorByClass[stringClass]=String;function lodash(value){return value&&typeof value=="object"&&!isArray(value)&&hasOwnProperty.call(value,"__wrapped__")?value:new lodashWrapper(value)}function lodashWrapper(value,chainAll){this.__chain__=!!chainAll;this.__wrapped__=value}lodashWrapper.prototype=lodash.prototype;va
 r support=lodash.support={};support.funcDecomp=!isNative(context.WinRTError)&&reThis.test(runInContext);support.funcNames=typeof Function.name=="string";lodash.templateSettings={escape:/<%-([\s\S]+?)%>/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:reInterpolate,variable:"",imports:{_:lodash}};function baseBind(bindData){var func=bindData[0],partialArgs=bindData[2],thisArg=bindData[4];function bound(){if(partialArgs){var args=slice(partialArgs);push.apply(args,arguments)}if(this instanceof bound){var thisBinding=baseCreate(func.prototype),result=func.apply(thisBinding,args||arguments);return isObject(result)?result:thisBinding}return func.apply(thisArg,args||arguments)}setBindData(bound,bindData);return bound}function baseClone(value,isDeep,callback,stackA,stackB){if(callback){var result=callback(value);if(typeof result!="undefined"){return result}}var isObj=isObject(value);if(isObj){var className=toString.call(value);if(!cloneableClasses[className]){return value}var ctor=ctorByClass[clas
 sName];switch(className){case boolClass:case dateClass:return new ctor(+value);case numberClass:case stringClass:return new ctor(value);case regexpClass:result=ctor(value.source,reFlags.exec(value));result.lastIndex=value.lastIndex;return result}}else{return value}var isArr=isArray(value);if(isDeep){var initedStack=!stackA;stackA||(stackA=getArray());stackB||(stackB=getArray());var length=stackA.length;while(length--){if(stackA[length]==value){return stackB[length]}}result=isArr?ctor(value.length):{}}else{result=isArr?slice(value):assign({},value)}if(isArr){if(hasOwnProperty.call(value,"index")){result.index=value.index}if(hasOwnProperty.call(value,"input")){result.input=value.input}}if(!isDeep){return result}stackA.push(value);stackB.push(result);(isArr?forEach:forOwn)(value,function(objValue,key){result[key]=baseClone(objValue,isDeep,callback,stackA,stackB)});if(initedStack){releaseArray(stackA);releaseArray(stackB)}return result}function baseCreate(prototype,properties){return is
 Object(prototype)?nativeCreate(prototype):{}}if(!nativeCreate){baseCreate=function(){function Object(){}return function(prototype){if(isObject(prototype)){Object.prototype=prototype;var result=new Object;Object.prototype=null}return result||context.Object()}}()}function baseCreateCallback(func,thisArg,argCount){if(typeof func!="function"){return identity}if(typeof thisArg=="undefined"||!("prototype"in func)){return func}var bindData=func.__bindData__;if(typeof bindData=="undefined"){if(support.funcNames){bindData=!func.name}bindData=bindData||!support.funcDecomp;if(!bindData){var source=fnToString.call(func);if(!support.funcNames){bindData=!reFuncName.test(source)}if(!bindData){bindData=reThis.test(source);setBindData(func,bindData)}}}if(bindData===false||bindData!==true&&bindData[1]&1){return func}switch(argCount){case 1:return function(value){return func.call(thisArg,value)};case 2:return function(a,b){return func.call(thisArg,a,b)};case 3:return function(value,index,collection){r
 eturn func.call(thisArg,value,index,collection)};case 4:return function(accumulator,value,index,collection){return func.call(thisArg,accumulator,value,index,collection)}}return bind(func,thisArg)}function baseCreateWrapper(bindData){var func=bindData[0],bitmask=bindData[1],partialArgs=bindData[2],partialRightArgs=bindData[3],thisArg=bindData[4],arity=bindData[5];var isBind=bitmask&1,isBindKey=bitmask&2,isCurry=bitmask&4,isCurryBound=bitmask&8,key=func;function bound(){var thisBinding=isBind?thisArg:this;if(partialArgs){var args=slice(partialArgs);push.apply(args,arguments)}if(partialRightArgs||isCurry){args||(args=slice(arguments));if(partialRightArgs){push.apply(args,partialRightArgs)}if(isCurry&&args.length<arity){bitmask|=16&~32;return baseCreateWrapper([func,isCurryBound?bitmask:bitmask&~3,args,null,thisArg,arity])}}args||(args=arguments);if(isBindKey){func=thisBinding[key]}if(this instanceof bound){thisBinding=baseCreate(func.prototype);var result=func.apply(thisBinding,args);r
 eturn isObject(result)?result:thisBinding}return func.apply(thisBinding,args)}setBindData(bound,bindData);return bound}function baseDifference(array,values){var index=-1,indexOf=getIndexOf(),length=array?array.length:0,isLarge=length>=largeArraySize&&indexOf===baseIndexOf,result=[];if(isLarge){var cache=createCache(values);if(cache){indexOf=cacheIndexOf;values=cache}else{isLarge=false}}while(++index<length){var value=array[index];if(indexOf(values,value)<0){result.push(value)}}if(isLarge){releaseObject(values)}return result}function baseFlatten(array,isShallow,isStrict,fromIndex){var index=(fromIndex||0)-1,length=array?array.length:0,result=[];while(++index<length){var value=array[index];if(value&&typeof value=="object"&&typeof value.length=="number"&&(isArray(value)||isArguments(value))){if(!isShallow){value=baseFlatten(value,isShallow,isStrict)}var valIndex=-1,valLength=value.length,resIndex=result.length;result.length+=valLength;while(++valIndex<valLength){result[resIndex++]=valu
 e[valIndex]}}else if(!isStrict){result.push(value)}}return result}function baseIsEqual(a,b,callback,isWhere,stackA,stackB){if(callback){var result=callback(a,b);if(typeof result!="undefined"){return!!result}}if(a===b){return a!==0||1/a==1/b}var type=typeof a,otherType=typeof b;if(a===a&&!(a&&objectTypes[type])&&!(b&&objectTypes[otherType])){return false}if(a==null||b==null){return a===b}var className=toString.call(a),otherClass=toString.call(b);if(className==argsClass){className=objectClass}if(otherClass==argsClass){otherClass=objectClass}if(className!=otherClass){return false}switch(className){case boolClass:case dateClass:return+a==+b;case numberClass:return a!=+a?b!=+b:a==0?1/a==1/b:a==+b;case regexpClass:case stringClass:return a==String(b)}var isArr=className==arrayClass;if(!isArr){var aWrapped=hasOwnProperty.call(a,"__wrapped__"),bWrapped=hasOwnProperty.call(b,"__wrapped__");if(aWrapped||bWrapped){return baseIsEqual(aWrapped?a.__wrapped__:a,bWrapped?b.__wrapped__:b,callback,is
 Where,stackA,stackB)}if(className!=objectClass){return false}var ctorA=a.constructor,ctorB=b.constructor;if(ctorA!=ctorB&&!(isFunction(ctorA)&&ctorA instanceof ctorA&&isFunction(ctorB)&&ctorB instanceof ctorB)&&("constructor"in a&&"constructor"in b)){return false}}var initedStack=!stackA;stackA||(stackA=getArray());stackB||(stackB=getArray());var length=stackA.length;while(length--){if(stackA[length]==a){return stackB[length]==b}}var size=0;result=true;stackA.push(a);stackB.push(b);if(isArr){length=a.length;size=b.length;result=size==length;if(result||isWhere){while(size--){var index=length,value=b[size];if(isWhere){while(index--){if(result=baseIsEqual(a[index],value,callback,isWhere,stackA,stackB)){break}}}else if(!(result=baseIsEqual(a[size],value,callback,isWhere,stackA,stackB))){break}}}}else{forIn(b,function(value,key,b){if(hasOwnProperty.call(b,key)){size++;return result=hasOwnProperty.call(a,key)&&baseIsEqual(a[key],value,callback,isWhere,stackA,stackB)}});if(result&&!isWhere
 ){forIn(a,function(value,key,a){if(hasOwnProperty.call(a,key)){return result=--size>-1}})}}stackA.pop();stackB.pop();if(initedStack){releaseArray(stackA);releaseArray(stackB)}return result}function baseMerge(object,source,callback,stackA,stackB){(isArray(source)?forEach:forOwn)(source,function(source,key){var found,isArr,result=source,value=object[key];if(source&&((isArr=isArray(source))||isPlainObject(source))){var stackLength=stackA.length;while(stackLength--){if(found=stackA[stackLength]==source){value=stackB[stackLength];break}}if(!found){var isShallow;if(callback){result=callback(value,source);if(isShallow=typeof result!="undefined"){value=result}}if(!isShallow){value=isArr?isArray(value)?value:[]:isPlainObject(value)?value:{}}stackA.push(source);stackB.push(value);if(!isShallow){baseMerge(value,source,callback,stackA,stackB)}}}else{if(callback){result=callback(value,source);if(typeof result=="undefined"){result=source}}if(typeof result!="undefined"){value=result}}object[key]=v
 alue})}function baseRandom(min,max){return min+floor(nativeRandom()*(max-min+1))}function baseUniq(array,isSorted,callback){var index=-1,indexOf=getIndexOf(),length=array?array.length:0,result=[];var isLarge=!isSorted&&length>=largeArraySize&&indexOf===baseIndexOf,seen=callback||isLarge?getArray():result;if(isLarge){var cache=createCache(seen);indexOf=cacheIndexOf;seen=cache}while(++index<length){var value=array[index],computed=callback?callback(value,index,array):value;if(isSorted?!index||seen[seen.length-1]!==computed:indexOf(seen,computed)<0){if(callback||isLarge){seen.push(computed)}result.push(value)}}if(isLarge){releaseArray(seen.array);releaseObject(seen)}else if(callback){releaseArray(seen)}return result}function createAggregator(setter){return function(collection,callback,thisArg){var result={};callback=lodash.createCallback(callback,thisArg,3);var index=-1,length=collection?collection.length:0;if(typeof length=="number"){while(++index<length){var value=collection[index];se
 tter(result,value,callback(value,index,collection),collection)}}else{forOwn(collection,function(value,key,collection){setter(result,value,callback(value,key,collection),collection)})}return result}}function createWrapper(func,bitmask,partialArgs,partialRightArgs,thisArg,arity){var isBind=bitmask&1,isBindKey=bitmask&2,isCurry=bitmask&4,isCurryBound=bitmask&8,isPartial=bitmask&16,isPartialRight=bitmask&32;if(!isBindKey&&!isFunction(func)){throw new TypeError}if(isPartial&&!partialArgs.length){bitmask&=~16;isPartial=partialArgs=false}if(isPartialRight&&!partialRightArgs.length){bitmask&=~32;isPartialRight=partialRightArgs=false}var bindData=func&&func.__bindData__;if(bindData&&bindData!==true){bindData=slice(bindData);if(bindData[2]){bindData[2]=slice(bindData[2])}if(bindData[3]){bindData[3]=slice(bindData[3])}if(isBind&&!(bindData[1]&1)){bindData[4]=thisArg}if(!isBind&&bindData[1]&1){bitmask|=8}if(isCurry&&!(bindData[1]&4)){bindData[5]=arity}if(isPartial){push.apply(bindData[2]||(bind
 Data[2]=[]),partialArgs)}if(isPartialRight){unshift.apply(bindData[3]||(bindData[3]=[]),partialRightArgs)}bindData[1]|=bitmask;return createWrapper.apply(null,bindData)}var creater=bitmask==1||bitmask===17?baseBind:baseCreateWrapper;return creater([func,bitmask,partialArgs,partialRightArgs,thisArg,arity])}function escapeHtmlChar(match){return htmlEscapes[match]}function getIndexOf(){var result=(result=lodash.indexOf)===indexOf?baseIndexOf:result;return result}function isNative(value){return typeof value=="function"&&reNative.test(value)}var setBindData=!defineProperty?noop:function(func,value){descriptor.value=value;defineProperty(func,"__bindData__",descriptor)};function shimIsPlainObject(value){var ctor,result;if(!(value&&toString.call(value)==objectClass)||(ctor=value.constructor,isFunction(ctor)&&!(ctor instanceof ctor))){return false}forIn(value,function(value,key){result=key});return typeof result=="undefined"||hasOwnProperty.call(value,result)}function unescapeHtmlChar(match)
 {return htmlUnescapes[match]}function isArguments(value){return value&&typeof value=="object"&&typeof value.length=="number"&&toString.call(value)==argsClass||false}var isArray=nativeIsArray||function(value){return value&&typeof value=="object"&&typeof value.length=="number"&&toString.call(value)==arrayClass||false};var shimKeys=function(object){var index,iterable=object,result=[];if(!iterable)return result;if(!objectTypes[typeof object])return result;for(index in iterable){if(hasOwnProperty.call(iterable,index)){result.push(index)}}return result};var keys=!nativeKeys?shimKeys:function(object){if(!isObject(object)){return[]}return nativeKeys(object)};var htmlEscapes={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};var htmlUnescapes=invert(htmlEscapes);var reEscapedHtml=RegExp("("+keys(htmlUnescapes).join("|")+")","g"),reUnescapedHtml=RegExp("["+keys(htmlEscapes).join("")+"]","g");var assign=function(object,source,guard){var index,iterable=object,result=iterable;if(!itera
 ble)return result;var args=arguments,argsIndex=0,argsLength=typeof guard=="number"?2:args.length;if(argsLength>3&&typeof args[argsLength-2]=="function"){var callback=baseCreateCallback(args[--argsLength-1],args[argsLength--],2)}else if(argsLength>2&&typeof args[argsLength-1]=="function"){callback=args[--argsLength]}while(++argsIndex<argsLength){iterable=args[argsIndex];if(iterable&&objectTypes[typeof iterable]){var ownIndex=-1,ownProps=objectTypes[typeof iterable]&&keys(iterable),length=ownProps?ownProps.length:0;while(++ownIndex<length){index=ownProps[ownIndex];result[index]=callback?callback(result[index],iterable[index]):iterable[index]}}}return result};function clone(value,isDeep,callback,thisArg){if(typeof isDeep!="boolean"&&isDeep!=null){thisArg=callback;callback=isDeep;isDeep=false}return baseClone(value,isDeep,typeof callback=="function"&&baseCreateCallback(callback,thisArg,1))}function cloneDeep(value,callback,thisArg){return baseClone(value,true,typeof callback=="function"
 &&baseCreateCallback(callback,thisArg,1))}function create(prototype,properties){var result=baseCreate(prototype);return properties?assign(result,properties):result}var defaults=function(object,source,guard){var index,iterable=object,result=iterable;if(!iterable)return result;var args=arguments,argsIndex=0,argsLength=typeof guard=="number"?2:args.length;while(++argsIndex<argsLength){iterable=args[argsIndex];if(iterable&&objectTypes[typeof iterable]){var ownIndex=-1,ownProps=objectTypes[typeof iterable]&&keys(iterable),length=ownProps?ownProps.length:0;while(++ownIndex<length){index=ownProps[ownIndex];if(typeof result[index]=="undefined")result[index]=iterable[index]}}}return result};function findKey(object,callback,thisArg){var result;callback=lodash.createCallback(callback,thisArg,3);forOwn(object,function(value,key,object){if(callback(value,key,object)){result=key;return false}});return result}function findLastKey(object,callback,thisArg){var result;callback=lodash.createCallback(c
 allback,thisArg,3);forOwnRight(object,function(value,key,object){if(callback(value,key,object)){result=key;return false}});return result}var forIn=function(collection,callback,thisArg){var index,iterable=collection,result=iterable;if(!iterable)return result;if(!objectTypes[typeof iterable])return result;callback=callback&&typeof thisArg=="undefined"?callback:baseCreateCallback(callback,thisArg,3);for(index in iterable){if(callback(iterable[index],index,collection)===false)return result}return result};function forInRight(object,callback,thisArg){var pairs=[];forIn(object,function(value,key){pairs.push(key,value)});var length=pairs.length;callback=baseCreateCallback(callback,thisArg,3);while(length--){if(callback(pairs[length--],pairs[length],object)===false){break}}return object}var forOwn=function(collection,callback,thisArg){var index,iterable=collection,result=iterable;if(!iterable)return result;if(!objectTypes[typeof iterable])return result;callback=callback&&typeof thisArg=="und
 efined"?callback:baseCreateCallback(callback,thisArg,3);var ownIndex=-1,ownProps=objectTypes[typeof iterable]&&keys(iterable),length=ownProps?ownProps.length:0;while(++ownIndex<length){index=ownProps[ownIndex];if(callback(iterable[index],index,collection)===false)return result}return result};function forOwnRight(object,callback,thisArg){var props=keys(object),length=props.length;callback=baseCreateCallback(callback,thisArg,3);while(length--){var key=props[length];if(callback(object[key],key,object)===false){break}}return object}function functions(object){var result=[];forIn(object,function(value,key){if(isFunction(value)){result.push(key)}});return result.sort()}function has(object,key){return object?hasOwnProperty.call(object,key):false}function invert(object){var index=-1,props=keys(object),length=props.length,result={};while(++index<length){var key=props[index];result[object[key]]=key}return result}function isBoolean(value){return value===true||value===false||value&&typeof value=
 ="object"&&toString.call(value)==boolClass||false}function isDate(value){return value&&typeof value=="object"&&toString.call(value)==dateClass||false}function isElement(value){return value&&value.nodeType===1||false}function isEmpty(value){var result=true;if(!value){return result}var className=toString.call(value),length=value.length;if(className==arrayClass||className==stringClass||className==argsClass||className==objectClass&&typeof length=="number"&&isFunction(value.splice)){return!length}forOwn(value,function(){return result=false});return result}function isEqual(a,b,callback,thisArg){return baseIsEqual(a,b,typeof callback=="function"&&baseCreateCallback(callback,thisArg,2))}function isFinite(value){return nativeIsFinite(value)&&!nativeIsNaN(parseFloat(value))}function isFunction(value){return typeof value=="function"}function isObject(value){return!!(value&&objectTypes[typeof value])}function isNaN(value){return isNumber(value)&&value!=+value}function isNull(value){return value
 ===null}function isNumber(value){return typeof value=="number"||value&&typeof value=="object"&&toString.call(value)==numberClass||false}var isPlainObject=!getPrototypeOf?shimIsPlainObject:function(value){if(!(value&&toString.call(value)==objectClass)){return false}var valueOf=value.valueOf,objProto=isNative(valueOf)&&(objProto=getPrototypeOf(valueOf))&&getPrototypeOf(objProto);return objProto?value==objProto||getPrototypeOf(value)==objProto:shimIsPlainObject(value)};function isRegExp(value){return value&&typeof value=="object"&&toString.call(value)==regexpClass||false}function isString(value){return typeof value=="string"||value&&typeof value=="object"&&toString.call(value)==stringClass||false}function isUndefined(value){return typeof value=="undefined"}function mapValues(object,callback,thisArg){var result={};callback=lodash.createCallback(callback,thisArg,3);forOwn(object,function(value,key,object){result[key]=callback(value,key,object)});return result}function merge(object){var a
 rgs=arguments,length=2;if(!isObject(object)){return object}if(typeof args[2]!="number"){length=args.length}if(length>3&&typeof args[length-2]=="function"){var callback=baseCreateCallback(args[--length-1],args[length--],2)}else if(length>2&&typeof args[length-1]=="function"){callback=args[--length]}var sources=slice(arguments,1,length),index=-1,stackA=getArray(),stackB=getArray();while(++index<length){baseMerge(object,sources[index],callback,stackA,stackB)}releaseArray(stackA);releaseArray(stackB);return object}function omit(object,callback,thisArg){var result={};if(typeof callback!="function"){var props=[];forIn(object,function(value,key){props.push(key)});props=baseDifference(props,baseFlatten(arguments,true,false,1));var index=-1,length=props.length;while(++index<length){var key=props[index];result[key]=object[key]}}else{callback=lodash.createCallback(callback,thisArg,3);forIn(object,function(value,key,object){if(!callback(value,key,object)){result[key]=value}})}return result}func
 tion pairs(object){var index=-1,props=keys(object),length=props.length,result=Array(length);while(++index<length){var key=props[index];result[index]=[key,object[key]]}return result}function pick(object,callback,thisArg){var result={};if(typeof callback!="function

<TRUNCATED>