Browse Source

AMBARI-979. More refactoring of App Browser code. (yusaku)

git-svn-id: https://svn.apache.org/repos/asf/incubator/ambari/branches/AMBARI-666@1406453 13f79535-47bb-0310-9956-ffa450edef68
Yusaku Sako 12 years ago
parent
commit
6fb8f3d8ca
28 changed files with 614 additions and 858 deletions
  1. 2 0
      AMBARI-666-CHANGES.txt
  2. 0 24
      ambari-web/app/controllers/main/apps/runs/item_controller.js
  3. 0 29
      ambari-web/app/controllers/main/apps/runs/jobs/bar_controller.js
  4. 0 23
      ambari-web/app/controllers/main/apps/runs/jobs/dag_controller.js
  5. 0 24
      ambari-web/app/controllers/main/apps/runs/jobs_controller.js
  6. 0 30
      ambari-web/app/controllers/main/apps/runs_controller.js
  7. 27 8
      ambari-web/app/controllers/main/apps_controller.js
  8. 40 34
      ambari-web/app/models/app.js
  9. 16 2
      ambari-web/app/models/run.js
  10. 10 2
      ambari-web/app/styles/apps.less
  11. 12 12
      ambari-web/app/templates/main/apps.hbs
  12. 14 1
      ambari-web/app/templates/main/apps/item.hbs
  13. 3 3
      ambari-web/app/templates/main/apps/item/bar.hbs
  14. 31 31
      ambari-web/app/templates/main/apps/item/dag.hbs
  15. 1 1
      ambari-web/app/templates/main/apps/list_row.hbs
  16. 0 63
      ambari-web/app/templates/main/apps/runs.hbs
  17. 0 21
      ambari-web/app/templates/main/apps/runs/item.hbs
  18. 0 24
      ambari-web/app/templates/main/apps/runs/jobs.hbs
  19. 196 237
      ambari-web/app/utils/data_table.js
  20. 2 0
      ambari-web/app/views.js
  21. 17 11
      ambari-web/app/views/main/apps/item/bar_view.js
  22. 16 17
      ambari-web/app/views/main/apps/item/dag_view.js
  23. 45 1
      ambari-web/app/views/main/apps/item_view.js
  24. 0 26
      ambari-web/app/views/main/apps/runs/item_view.js
  25. 0 48
      ambari-web/app/views/main/apps/runs/jobs/menu_view.js
  26. 0 29
      ambari-web/app/views/main/apps/runs/jobs_view.js
  27. 0 63
      ambari-web/app/views/main/apps/runs_view.js
  28. 182 94
      ambari-web/app/views/main/apps_view.js

+ 2 - 0
AMBARI-666-CHANGES.txt

@@ -368,6 +368,8 @@ AMBARI-666 branch (unreleased changes)
 
 
   IMPROVEMENTS
   IMPROVEMENTS
 
 
+  AMBARI-979. More refactoring of App Browser code. (yusaku)
+
   AMBARI-947. Make it easier to test Deploy (Install, Start + Test) step
   AMBARI-947. Make it easier to test Deploy (Install, Start + Test) step
   of the wizard. (yusaku)
   of the wizard. (yusaku)
 
 

+ 0 - 24
ambari-web/app/controllers/main/apps/runs/item_controller.js

@@ -1,24 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-var App = require('app');
-
-App.MainAppsRunsItemController = Em.Controller.extend({
-  name:'mainAppsRunsItemController',
-  content: null
-})

+ 0 - 29
ambari-web/app/controllers/main/apps/runs/jobs/bar_controller.js

@@ -1,29 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-var App = require('app');
-
-App.MainAppsRunsJobsBarController = Em.ArrayController.extend({
-  name:'mainAppsRunsJobsBarController',
-  job: null,
-  activeJobId: null,
-  selectJob: function(event) {
-    this.set('job', event.context);
-    this.set('activeJobId', event.context.get('jobId'));
-  }
-})

+ 0 - 23
ambari-web/app/controllers/main/apps/runs/jobs/dag_controller.js

@@ -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.
- */
-
-var App = require('app');
-
-App.MainJobsAppsRunsDagController = Em.ArrayController.extend({
-  name:'mainJobsAppsRunsDagController'
-})

+ 0 - 24
ambari-web/app/controllers/main/apps/runs/jobs_controller.js

@@ -1,24 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-var App = require('app');
-
-App.MainAppsRunsJobsController = Em.ArrayController.extend({
-  name:'mainAppsRunsJobsController',
-  content: null
-})

+ 0 - 30
ambari-web/app/controllers/main/apps/runs_controller.js

@@ -1,30 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-var App = require('app');
-
-App.MainAppsRunsController = Em.ArrayController.extend({
-  name:'mainAppsRunsController',
-  content:null,
-  routeHome:function(){
-      App.router.get('mainAppsController').get('routeHome')();
-  },
-  routeApps: function(){
-      App.router.transitionTo('main.apps');
-  }
-})

+ 27 - 8
ambari-web/app/controllers/main/apps_controller.js

@@ -24,6 +24,7 @@ App.MainAppsController = Em.ArrayController.extend({
   content:App.Run.find(),
   content:App.Run.find(),
   apps:App.App.find(),
   apps:App.App.find(),
   staredRuns: [],
   staredRuns: [],
+  filteredRuns: [],
   routeHome:function () {
   routeHome:function () {
     App.router.transitionTo('main.dashboard');
     App.router.transitionTo('main.dashboard');
     var view = Ember.View.views['main_menu'];
     var view = Ember.View.views['main_menu'];
@@ -31,6 +32,19 @@ App.MainAppsController = Em.ArrayController.extend({
       this.set('active', this.get('content.routing') == 'dashboard' ? "active" : "");
       this.set('active', this.get('content.routing') == 'dashboard' ? "active" : "");
     });
     });
   },
   },
+  clearFilteredRuns: function() {
+    this.set('filteredRuns', []);
+    this.set('filteredRunsLength', 0);
+  },
+  addFilteredRun: function(id) {
+    this.get('filteredRuns').push(this.getRunById(id));
+    this.set('filteredRunsLength', this.get('filteredRuns').length);
+  },
+  /**
+   * Get run by id
+   * @param id run identifier (NOT runId)
+   * @return {*} run if exists, undefined - not exists
+   */
   getRunById: function(id) {
   getRunById: function(id) {
     var r;
     var r;
     this.get('content').forEach(function(item){
     this.get('content').forEach(function(item){
@@ -40,6 +54,11 @@ App.MainAppsController = Em.ArrayController.extend({
     });
     });
     return r;
     return r;
   },
   },
+  /**
+   * Check if run with such id exists
+   * @param id run identifier (NOT runId)
+   * @return {Boolean} true - record with this id exists, false - not exists
+   */
   issetStaredRun: function(id) {
   issetStaredRun: function(id) {
     r = false;
     r = false;
     this.get('staredRuns').forEach(function(item){
     this.get('staredRuns').forEach(function(item){
@@ -49,8 +68,11 @@ App.MainAppsController = Em.ArrayController.extend({
     });
     });
     return r;
     return r;
   },
   },
+  /**
+   * Click on star on table row
+   * @return {Boolean} false for prevent default event handler
+   */
   starClick: function() {
   starClick: function() {
-    console.log(event);
     event.srcElement.classList.toggle('stared');
     event.srcElement.classList.toggle('stared');
     var id = parseInt(event.srcElement.parentNode.childNodes[1].innerText);
     var id = parseInt(event.srcElement.parentNode.childNodes[1].innerText);
     if (!this.issetStaredRun(id)) {
     if (!this.issetStaredRun(id)) {
@@ -65,14 +87,11 @@ App.MainAppsController = Em.ArrayController.extend({
     this.set('staredRunsLength', this.get('staredRuns').length);
     this.set('staredRunsLength', this.get('staredRuns').length);
     return false;
     return false;
   },
   },
+  /**
+   * Flush all starred runs
+   */
   clearStars: function() {
   clearStars: function() {
     this.set('staredRuns', []);
     this.set('staredRuns', []);
     this.set('staredRunsLength', 0);
     this.set('staredRunsLength', 0);
-    $('.icon-star').removeClass('stared');
-  },
-  /**
-   * Row, which is expanded at the moment, will update this property.
-   * Used to collapse rows, which are not used at the moment
-   */
-  expandedRowId : null
+  }
 })
 })

+ 40 - 34
ambari-web/app/models/app.js

@@ -20,49 +20,55 @@
 var App = require('app');
 var App = require('app');
 
 
 App.App = DS.Model.extend({
 App.App = DS.Model.extend({
-  appName:DS.attr('string'),
-  type:DS.attr('string'),
-  numJobsTotal:DS.attr('number'),
-  userName:DS.attr('string'),
-  executionTime:DS.attr('string'),
-  runs:DS.hasMany('App.Run')
+  appName: DS.attr('string'),
+  type: DS.attr('string'),
+  numJobsTotal: DS.attr('number'),
+  userName: DS.attr('string'),
+  executionTime: DS.attr('string'),
+  runs: DS.hasMany('App.Run'),
+  isRunning: function () {
+    if (!this.get('isLoaded')) {
+      return false;
+    }
+    return this.get('runs').someProperty('isRunning', true);
+  }.property('runs.@each.isRunning')
 });
 });
 
 
 App.App.FIXTURES = [
 App.App.FIXTURES = [
   {
   {
-    id:1,
-    app_name:'pigs.sh',
-    type:'Hive',
-    num_jobs_total:5,
-    user_name:'root',
-    execution_time:'1347629541543',
-    runs:[1, 2, 3]
+    id: 1,
+    app_name: 'pigs.sh',
+    type: 'Hive',
+    num_jobs_total: 5,
+    user_name: 'root',
+    execution_time: '1347629541543',
+    runs: [1, 2, 3]
   },
   },
   {
   {
-    id:2,
-    app_name:'pigsm.sh',
-    type:'pig',
-    num_jobs_total:3,
-    user_name:'user1',
-    execution_time:'1347656741515',
-    runs:[6, 4, 5]
+    id: 2,
+    app_name: 'pigsm.sh',
+    type: 'pig',
+    num_jobs_total: 3,
+    user_name: 'user1',
+    execution_time: '1347656741515',
+    runs: [6, 4, 5]
   },
   },
   {
   {
-    id:3,
-    app_name:'pigsmo.sh',
-    type:'pig',
-    num_jobs_total:4,
-    user_name:'user3',
-    execution_time:'1347629587687',
-    runs:[7, 8, 9, 10, 11]
+    id: 3,
+    app_name: 'pigsmo.sh',
+    type: 'pig',
+    num_jobs_total: 4,
+    user_name: 'user3',
+    execution_time: '1347629587687',
+    runs: [7, 8, 9, 10, 11]
   },
   },
   {
   {
-    id:4,
-    app_name:'pigsmok.sh',
-    type:'MapReduce',
-    num_jobs_total:0,
-    user_name:'root',
-    execution_time:'134762957834',
-    runs:[]
+    id: 4,
+    app_name: 'pigsmok.sh',
+    type: 'MapReduce',
+    num_jobs_total: 0,
+    user_name: 'root',
+    execution_time: '134762957834',
+    runs: []
   }
   }
 ]
 ]

+ 16 - 2
ambari-web/app/models/run.js

@@ -18,6 +18,7 @@
 
 
 
 
 var App = require('app');
 var App = require('app');
+var date = require('utils/date');
 
 
 App.Run = DS.Model.extend({
 App.Run = DS.Model.extend({
   runId:DS.attr('string'),
   runId:DS.attr('string'),
@@ -31,7 +32,20 @@ App.Run = DS.Model.extend({
   input: DS.attr('number'),
   input: DS.attr('number'),
   output: DS.attr('number'),
   output: DS.attr('number'),
   appId:DS.attr('number'),
   appId:DS.attr('number'),
-  jobs:DS.hasMany('App.Job')
+  jobs:DS.hasMany('App.Job'),
+  duration: function() {
+    return date.dateFormatInterval(((parseInt(this.get('lastUpdateTime')) - parseInt(this.get('startTime')))/1000));
+  }.property('lastUpdateTime', 'startTime'),
+  isRunning: function () {
+    if (!this.get('isLoaded')) {
+      return false;
+    }
+    console.log('RUN: ' + this.get('id')+' '+this.get('jobs').someProperty('status', 'RUNNING'));
+    return this.get('jobs').someProperty('status', 'RUNNING');
+  }.property('jobs.@each.status'),
+  lastUpdateTimeFormatted: function() {
+    return date.dateFormat(this.get('lastUpdateTime'));
+  }.property('lastUpdateTime')
 });
 });
 
 
 App.Run.FIXTURES = [
 App.Run.FIXTURES = [
@@ -145,7 +159,7 @@ App.Run.FIXTURES = [
     run_id:'pig_5',
     run_id:'pig_5',
     parent_run_id:null,
     parent_run_id:null,
     workflow_context:'{dag:{"1":["3","5"],"5":["7"],"3":["1"]}}',
     workflow_context:'{dag:{"1":["3","5"],"5":["7"],"3":["1"]}}',
-    user_name:'user1',
+    user_name:'jsmith',
     start_time:1347539541508,
     start_time:1347539541508,
     last_update_time:'1347639541508',
     last_update_time:'1347639541508',
     num_jobs_total:4,
     num_jobs_total:4,

+ 10 - 2
ambari-web/app/styles/apps.less

@@ -1,4 +1,7 @@
 #apps{
 #apps{
+  td .red {
+    color: red;
+  }
   .table thead th{
   .table thead th{
     vertical-align:top;
     vertical-align:top;
   }
   }
@@ -21,9 +24,11 @@
       color: inherit;
       color: inherit;
     }
     }
   }
   }
-  .a {
+  a.a {
+    cursor: pointer;
     width:25px;
     width:25px;
     height: 25px;
     height: 25px;
+    display: block;
     background-position: center center;
     background-position: center center;
     background-repeat: no-repeat;
     background-repeat: no-repeat;
     position: relative;
     position: relative;
@@ -31,7 +36,10 @@
     margin-left:-13px;
     margin-left:-13px;
     margin-top: 36px;
     margin-top: 36px;
     font-size:30px;
     font-size:30px;
-    color:red;
+    color: gray;
+    &.active {
+      color: #08C;
+    }
   }
   }
   .avg-info {
   .avg-info {
     font-size:16px;
     font-size:16px;

+ 12 - 12
ambari-web/app/templates/main/apps.hbs

@@ -23,7 +23,7 @@
   <table class="table table-bordered table-stripe avg-table" >
   <table class="table table-bordered table-stripe avg-table" >
             <tbody>
             <tbody>
             <tr >
             <tr >
-              <td rowspan="3" class="avg-star"><div class="icon-star a"></div></td>
+              <td rowspan="3" class="avg-star"><a href="#" {{action "avgStarClick" target="view"}} class="icon-star a"></a></td>
               <td></td>
               <td></td>
               <td>Jobs</td>
               <td>Jobs</td>
               <td>Input</td>
               <td>Input</td>
@@ -34,27 +34,27 @@
             </tr>
             </tr>
             <tr class="avg-info">
             <tr class="avg-info">
               <td>Avg</td>
               <td>Avg</td>
-              <td>{{view.stared.jobs.avg}}</td>
-              <td>{{view.stared.input.avg}}</td>
-              <td>{{view.stared.output.avg}}</td>
-              <td>{{view.stared.duration.avg}}</td>
-              <td>{{view.stared.times.oldest}}</td>
-              <td>{{view.stared.times.youngest}}</td>
+              <td>{{view.avgData.jobs.avg}}</td>
+              <td>{{view.avgData.input.avg}}</td>
+              <td>{{view.avgData.output.avg}}</td>
+              <td>{{view.avgData.duration.avg}}</td>
+              <td>{{view.avgData.times.oldest}}</td>
+              <td>{{view.avgData.times.youngest}}</td>
             </tr>
             </tr>
              <tr class="compare-info">
              <tr class="compare-info">
 
 
               <td>Min / Max</td>
               <td>Min / Max</td>
-              <td>{{view.stared.jobs.min}} / {{view.stared.jobs.max}}</td>
-              <td>{{view.stared.input.min}} / {{view.stared.input.max}}</td>
-              <td>{{view.stared.output.min}} / {{view.stared.output.max}}</td>
-              <td>{{view.stared.duration.min}} / {{view.stared.duration.max}}</td>
+              <td>{{view.avgData.jobs.min}} / {{view.avgData.jobs.max}}</td>
+              <td>{{view.avgData.input.min}} / {{view.avgData.input.max}}</td>
+              <td>{{view.avgData.output.min}} / {{view.avgData.output.max}}</td>
+              <td>{{view.avgData.duration.min}} / {{view.avgData.duration.max}}</td>
               <td></td>
               <td></td>
               <td></td>
               <td></td>
              </tr>
              </tr>
             </tbody>
             </tbody>
   </table>
   </table>
     <div id="filter_info" class="row">
     <div id="filter_info" class="row">
-      <div class="span3">Show: <a href="javascript:void(0)">Filtered ({{view.filtered}})</a>&nbsp;&#124;&nbsp;<a href="javascript:void(0)">Starred ({{view.stared.count}})</a></div>
+      <div class="span3">Show: <a href="javascript:void(0)">Filtered ({{view.filtered}})</a>&nbsp;&#124;&nbsp;<a href="javascript:void(0)">Starred ({{view.staredData.count}})</a></div>
       <div class="span2"><a href="#" {{action "clearFilters" target="view"}}>Clear filters</a>&nbsp;&#124;&nbsp;<a href="#" {{action "clearStars" target="controller"}}>Clear stars</a></div>
       <div class="span2"><a href="#" {{action "clearFilters" target="view"}}>Clear filters</a>&nbsp;&#124;&nbsp;<a href="#" {{action "clearStars" target="controller"}}>Clear stars</a></div>
     </div>
     </div>
     <div>
     <div>

+ 14 - 1
ambari-web/app/templates/main/apps/item.hbs

@@ -15,6 +15,19 @@
 * See the License for the specific language governing permissions and
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * limitations under the License.
 -->
 -->
+<td colspan="9">
+<ul class="nav nav-tabs">
+  {{#each tab in view.menuTabs}}
+  <li {{bindAttr class="tab.active"}} {{action "switchTab" tab target="view" }}>
+  <a href="javascript:void(0)">{{tab.label}}</a>
+  </li>
+  {{/each}}
+</ul>
+<div id="content">
+  {{view view.containerView currentViewBinding="App.MainAppsItemDagView"}}
+</div>
+ </td>
+
+
 
 
-<p>App Item</p>
 
 

+ 3 - 3
ambari-web/app/templates/main/apps/runs/jobs/bar.hbs → ambari-web/app/templates/main/apps/item/bar.hbs

@@ -24,11 +24,11 @@
   </button>
   </button>
   <ul class="dropdown-menu">
   <ul class="dropdown-menu">
     {{#each job in view.content}}
     {{#each job in view.content}}
-    <li><a {{action "selectJob" job target="controller"}} href="javascript:void(null)">{{job.id}}</a></li>
+    <li><a {{action "selectJob" job target="view"}} href="javascript:void(null)">{{job.id}}</a></li>
     {{/each}}
     {{/each}}
   </ul>
   </ul>
 </div>
 </div>
-<div>{{controller.activeJobId}}</div>
+<div></div>
 <div id="graph1">
 <div id="graph1">
   <div style="float:left">
   <div style="float:left">
     <div id="graph1_desc" class="graph_desc">
     <div id="graph1_desc" class="graph_desc">
@@ -54,4 +54,4 @@
     <div id="tasks_legend"></div>
     <div id="tasks_legend"></div>
   </div>
   </div>
   <div style="clear: both"></div>
   <div style="clear: both"></div>
-</div>
+</div>

+ 31 - 31
ambari-web/app/templates/main/apps/runs/jobs/dag.hbs → ambari-web/app/templates/main/apps/item/dag.hbs

@@ -17,37 +17,37 @@
 -->
 -->
 <div id="jobs" class="box">
 <div id="jobs" class="box">
   <div class="box-header">
   <div class="box-header">
-  <div>
-  <div id="dag_viewer"></div>
-  </div>
-  <h2>Jobs list</h2>
-    <table class="table table-bordered table-striped" id="dataTable">
-    <thead>
-    <tr>
-      <th>Job &#35;</th>
-      <th>Job Id</th>
-      <th>Status</th>
-      <th>Maps</th>
-      <th>Reduces</th>
-      <th>Input</th>
-      <th>Output</th>
-      <th>Duration</th>
-    </tr>
-    </thead>
-    <tbody>
-    {{#each job in view.content}}
-    <tr>
-      <td>{{job.id}}</td>
-      <td>{{job.jobId}}</td>
-      <td>{{job.status}}</td>
-      <td>{{job.maps}}</td>
-      <td>{{job.reduces}}</td>
-      <td>{{job.input}}</td>
-      <td>{{job.output}}</td>
-      <td>{{job.mapsRuntime}}</td>
-    </tr>
-    {{/each}}
-    </tbody>
+    <div>
+      <div id="dag_viewer"></div>
+    </div>
+    <h2>Jobs list</h2>
+    <table class="table table-bordered table-striped" id="innerTable">
+      <thead>
+      <tr>
+        <th>Job &#35;</th>
+        <th>Job Id</th>
+        <th>Status</th>
+        <th>Maps</th>
+        <th>Reduces</th>
+        <th>Input</th>
+        <th>Output</th>
+        <th>Duration</th>
+      </tr>
+      </thead>
+      <tbody>
+      {{#each job in view.content}}
+      <tr>
+        <td>{{job.id}}</td>
+        <td>{{job.jobId}}</td>
+        <td>{{job.status}}</td>
+        <td>{{job.maps}}</td>
+        <td>{{job.reduces}}</td>
+        <td>{{job.input}}</td>
+        <td>{{job.output}}</td>
+        <td>{{job.mapsRuntime}}</td>
+      </tr>
+      {{/each}}
+      </tbody>
     </table>
     </table>
   </div>
   </div>
 </div>
 </div>

+ 1 - 1
ambari-web/app/templates/main/apps/list_row.hbs

@@ -24,4 +24,4 @@
 <td>{{run.input}}</td>
 <td>{{run.input}}</td>
 <td>{{run.output}}</td>
 <td>{{run.output}}</td>
 <td>{{run.duration}}</td>
 <td>{{run.duration}}</td>
-<td>{{run.lastUpdateTime}}</td>
+<td>{{run.lastUpdateTimeFormatted}} {{#if run.isRunning}}<b class='red'>*</b>{{/if}}</td>

+ 0 - 63
ambari-web/app/templates/main/apps/runs.hbs

@@ -1,63 +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>
-  <a href='#' {{action "routeHome" target="controller"}}><i class="icon-home"></i></a>
-  &nbsp;/&nbsp;<a href='#' {{action "routeApps" target="controller"}}>Apps</a>&nbsp;/&nbsp;
-  {{view.appItem.appName}}
-</div>
-<p>Jobs: {{view.appItem.numJobsTotal}}</p>
-<div id="runs" class="box">
-  <div class="box-header">
-  <h2>Runs list</h2>
-    <table class="table table-bordered table-striped" id="dataTable">
-    <thead>
-    <tr>
-      <th>Run &#35;</th>
-      <th>Run Date</th>
-      <th>Maps</th>
-      <th>Reduces</th>
-      <th>Input</th>
-      <th>Output</th>
-      <th>Duration</th>
-      <th></th>
-    </tr>
-    </thead>
-    <tbody>
-    {{#each run in view.content}}
-    <tr>
-      <td>
-      <label class="checkbox">
-                {{view view.RunsCheckboxView}} {{run.id}}
-      </label>
-      </td>
-      <td>{{run.startTime}}</td>
-      <td>10</td>
-      <td>15</td>
-      <td>1300</td>
-      <td>200</td>
-      <td>{{run.lastUpdateTime}}</td>
-      <td><a href="#"{{action "showRun" run}}>Details</a></td>
-    </tr>
-    {{/each}}
-    </tbody>
-    </table>
-    </div>
-</div>
-{{outlet}}
-

+ 0 - 21
ambari-web/app/templates/main/apps/runs/item.hbs

@@ -1,21 +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.
--->
-
-
-<h2>runItem template</h2>
-

+ 0 - 24
ambari-web/app/templates/main/apps/runs/jobs.hbs

@@ -1,24 +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.
--->
-
-<h5>/{{view.appItem.appName}}/{{view.runItem.id}}</h5>
-<p>Jobs: {{view.runItem.numJobsTotal}}</p>
-<div class="content">
-  {{view App.MainJobsAppsRunsMenuView}}
-  {{outlet}}
-</div>

+ 196 - 237
ambari-web/app/utils/data_table.js

@@ -18,7 +18,7 @@
 
 
 jQuery.extend(jQuery.fn.dataTableExt.oSort, {
 jQuery.extend(jQuery.fn.dataTableExt.oSort, {
   // @see utils/date.js
   // @see utils/date.js
-  "ambari-date-pre":function (date_string) {
+  "ambari-date-pre": function (date_string) {
     date_string = $(date_string).text(); // strip Ember script tags
     date_string = $(date_string).text(); // strip Ember script tags
     var date = date_string.substring(4);
     var date = date_string.substring(4);
     var month = date.substring(1, 4);
     var month = date.substring(1, 4);
@@ -33,285 +33,244 @@ jQuery.extend(jQuery.fn.dataTableExt.oSort, {
     return year + month + day + hours + minutes;
     return year + month + day + hours + minutes;
   },
   },
 
 
-  "ambari-date-asc":function (a, b) {
+  "ambari-date-asc": function (a, b) {
     return a - b;
     return a - b;
   },
   },
 
 
-  "ambari-date-desc":function (a, b) {
+  "ambari-date-desc": function (a, b) {
     return b - a;
     return b - a;
   }
   }
 });
 });
 
 
 jQuery.extend(jQuery.fn.dataTableExt.oApi, {
 jQuery.extend(jQuery.fn.dataTableExt.oApi, {
-    "fnFilterClear":function ( oSettings )
-    {
-        /* Remove global filter */
-        oSettings.oPreviousSearch.sSearch = "";
-
-        /* Remove the text of the global filter in the input boxes */
-        if ( typeof oSettings.aanFeatures.f != 'undefined' )
-        {
-            var n = oSettings.aanFeatures.f;
-            for ( var i=0, iLen=n.length ; i<iLen ; i++ )
-            {
-                $('input', n[i]).val( '' );
-            }
-        }
-
-        /* Remove the search text for the column filters - NOTE - if you have input boxes for these
-         * filters, these will need to be reset
-         */
-        for ( var i=0, iLen=oSettings.aoPreSearchCols.length ; i<iLen ; i++ )
-        {
-            oSettings.aoPreSearchCols[i].sSearch = "";
-        }
+  "fnFilterClear": function (oSettings) {
+    /* Remove global filter */
+    oSettings.oPreviousSearch.sSearch = "";
+
+    /* Remove the text of the global filter in the input boxes */
+    if (typeof oSettings.aanFeatures.f != 'undefined') {
+      var n = oSettings.aanFeatures.f;
+      for (var i = 0, iLen = n.length; i < iLen; i++) {
+        $('input', n[i]).val('');
+      }
+    }
 
 
-        /* Redraw */
-        oSettings.oApi._fnReDraw( oSettings );
+    /* Remove the search text for the column filters - NOTE - if you have input boxes for these
+     * filters, these will need to be reset
+     */
+    for (var i = 0, iLen = oSettings.aoPreSearchCols.length; i < iLen; i++) {
+      oSettings.aoPreSearchCols[i].sSearch = "";
     }
     }
+
+    /* Redraw */
+    oSettings.oApi._fnReDraw(oSettings);
+  }
 });
 });
 
 
 jQuery.extend(jQuery.fn.dataTableExt.oApi, {
 jQuery.extend(jQuery.fn.dataTableExt.oApi, {
-    "fnGetColumnData":function ( oSettings, iColumn, bUnique, bFiltered, bIgnoreEmpty ) {
-        // check that we have a column id
-        if ( typeof iColumn == "undefined" ) return [];
-
-        // by default we only wany unique data
-        if ( typeof bUnique == "undefined" ) bUnique = true;
+  "fnGetColumnData": function (oSettings, iColumn, bUnique, bFiltered, bIgnoreEmpty) {
+    // check that we have a column id
+    if (typeof iColumn == "undefined") return [];
 
 
-        // by default we do want to only look at filtered data
-        if ( typeof bFiltered == "undefined" ) bFiltered = true;
+    // by default we only wany unique data
+    if (typeof bUnique == "undefined") bUnique = true;
 
 
-        // by default we do not wany to include empty values
-        if ( typeof bIgnoreEmpty == "undefined" ) bIgnoreEmpty = true;
+    // by default we do want to only look at filtered data
+    if (typeof bFiltered == "undefined") bFiltered = true;
 
 
-        // list of rows which we're going to loop through
-        var aiRows;
+    // by default we do not wany to include empty values
+    if (typeof bIgnoreEmpty == "undefined") bIgnoreEmpty = true;
 
 
-        // use only filtered rows
-        if (bFiltered == true) aiRows = oSettings.aiDisplay;
-        // use all rows
-        else aiRows = oSettings.aiDisplayMaster; // all row numbers
+    // list of rows which we're going to loop through
+    var aiRows;
 
 
-        // set up data array
-        var asResultData = new Array();
+    // use only filtered rows
+    if (bFiltered == true) aiRows = oSettings.aiDisplay;
+    // use all rows
+    else aiRows = oSettings.aiDisplayMaster; // all row numbers
 
 
-        for (var i=0,c=aiRows.length; i<c; i++) {
-            iRow = aiRows[i];
-            var sValue = this.fnGetData(iRow, iColumn);
+    // set up data array
+    var asResultData = new Array();
 
 
-            // ignore empty values?
-            if (bIgnoreEmpty == true && sValue.length == 0) continue;
+    for (var i = 0, c = aiRows.length; i < c; i++) {
+      iRow = aiRows[i];
+      var sValue = this.fnGetData(iRow, iColumn);
 
 
-            // ignore unique values?
-            else if (bUnique == true && jQuery.inArray(sValue, asResultData) > -1) continue;
+      // ignore empty values?
+      if (bIgnoreEmpty == true && sValue.length == 0) continue;
 
 
-            // else push the value onto the result data array
-            else asResultData.push(sValue);
-        }
+      // ignore unique values?
+      else if (bUnique == true && jQuery.inArray(sValue, asResultData) > -1) continue;
 
 
-        return asResultData;
+      // else push the value onto the result data array
+      else asResultData.push(sValue);
     }
     }
+
+    return asResultData;
+  }
 });
 });
 
 
 jQuery.extend($.fn.dataTableExt.afnFiltering.push(
 jQuery.extend($.fn.dataTableExt.afnFiltering.push(
-    function( oSettings, aData, iDataIndex ) {
-        var inputFilters = [
-            {iColumn:'3', elementId: 'user_filter', type:'multiple'},
-            {iColumn:'4', elementId: 'jobs_filter', type:'number' },
-            {iColumn:'5', elementId: 'input_filter', type:'number' },
-            {iColumn:'6', elementId: 'output_filter', type:'number' },
-            {iColumn:'7', elementId: 'duration_filter', type:'number' },
-            {iColumn:'8', elementId: 'rundate_filter', type:'date' }
-        ];
-        var match = true;
-        for(i = 0; i < inputFilters.length; i++){
-            switch(inputFilters[i].type){
-                case 'date':
-                    if(jQuery('#'+inputFilters[i].elementId).val() !== 'Any' && match) {
-                        dateFilter(jQuery('#'+inputFilters[i].elementId).val(), aData[inputFilters[i].iColumn]);
-                    }
-                break;
-                case 'number':
-                    if(jQuery('#'+inputFilters[i].elementId).val() && match){
-                        numberFilter(jQuery('#'+inputFilters[i].elementId).val(), aData[inputFilters[i].iColumn]);
-                    }
-                    break;
-                case 'multiple':
-                    if(jQuery('#'+inputFilters[i].elementId).val() && match){
-                        multipleFilter(jQuery('#'+inputFilters[i].elementId).val(), aData[inputFilters[i].iColumn]);
-                    }
+    function (oSettings, aData, iDataIndex) {
+      var inputFilters = [
+        {iColumn: '3', elementId: 'user_filter', type: 'multiple'},
+        {iColumn: '4', elementId: 'jobs_filter', type: 'number' },
+        {iColumn: '5', elementId: 'input_filter', type: 'number' },
+        {iColumn: '6', elementId: 'output_filter', type: 'number' },
+        {iColumn: '7', elementId: 'duration_filter', type: 'time' },
+        {iColumn: '8', elementId: 'rundate_filter', type: 'date' }
+      ];
+      var match = true;
+      for (i = 0; i < inputFilters.length; i++) {
+        switch (inputFilters[i].type) {
+          case 'date':
+            if (jQuery('#' + inputFilters[i].elementId).val() !== 'Any' && match) {
+              dateFilter(jQuery('#' + inputFilters[i].elementId).val(), aData[inputFilters[i].iColumn]);
             }
             }
-        }
-        function multipleFilter(condition, rowValue){
-            var options = condition.split(',');
-            match = false;
-            rowValue = (jQuery(rowValue).text()) ? jQuery(rowValue).text() : rowValue;
-            for(var i = 0; i < options.length; i++) {
-                if (options[i] === rowValue) match = true;
+            break;
+          case 'number':
+            if (jQuery('#' + inputFilters[i].elementId).val() && match) {
+              numberFilter(jQuery('#' + inputFilters[i].elementId).val(), aData[inputFilters[i].iColumn]);
+            }
+            break;
+          case 'multiple':
+            if (jQuery('#' + inputFilters[i].elementId).val() && match) {
+              multipleFilter(jQuery('#' + inputFilters[i].elementId).val(), aData[inputFilters[i].iColumn]);
+            }
+            break;
+          case 'time':
+            if (jQuery('#' + inputFilters[i].elementId).val() && match) {
+              timeFilter(jQuery('#' + inputFilters[i].elementId).val(), aData[inputFilters[i].iColumn]);
             }
             }
+            break;
         }
         }
-
-        function dateFilter(condition, rowValue) {
-            var nowTime = new Date().getTime();
-            var oneDayPast = nowTime - 86400000;
-            var twoDaysPast = nowTime - 172800000;
-            var sevenDaysPast = nowTime - 604800000;
-            var fourteenDaysPast = nowTime - 1209600000;
-            var thirtyDaysPast = nowTime - 2592000000;
-            rowValue = (jQuery(rowValue).text()) ? jQuery(rowValue).text() : rowValue;
-            rowValue = new Date(rowValue).getTime();
+      }
+      function multipleFilter(condition, rowValue) {
+        var options = condition.split(',');
+        match = false;
+        rowValue = (jQuery(rowValue).text()) ? jQuery(rowValue).text() : rowValue;
+        for (var i = 0; i < options.length; i++) {
+          if (options[i] === rowValue) match = true;
+        }
+      }
+
+      function timeFilter(rangeExp, rowValue) {
+        var compareChar = rangeExp.charAt(0);
+        var compareScale = rangeExp.charAt(rangeExp.length - 1);
+        var compareValue = isNaN(parseInt(compareScale)) ? parseInt(rangeExp.substr(1, rangeExp.length - 2)) : parseInt(rangeExp.substr(1, rangeExp.length - 1));
+        rowValue = (jQuery(rowValue).text()) ? jQuery(rowValue).text() : rowValue;
+        var convertedRowValue = parseInt(rowValue.substr(0, 2)) * 3600 + parseInt(rowValue.substr(3, 5)) * 60 + parseInt(rowValue.substr(6, 8));
+        switch (compareScale) {
+          case 'm':
+            convertedRowValue /= 60;
+            break;
+          case 'h':
+            convertedRowValue /= 3600;
+            break;
+        }
+        match = false;
+        switch (compareChar) {
+          case '<':
+            if (compareValue > convertedRowValue) match = true;
+            break;
+          case '>':
+            if (compareValue < convertedRowValue) match = true;
+            break;
+          case '=':
+            if (compareValue == convertedRowValue) match = true;
+            break;
+          default:
             match = false;
             match = false;
-            switch (condition) {
-                case 'Any':
-                    match = true;
-                    break;
-                case 'Past 1 Day':
-                    if (nowTime > rowValue && rowValue > oneDayPast) match = true;
-                    break;
-                case 'Past 2 Days':
-                    if (nowTime > rowValue && rowValue > twoDaysPast) match = true;
-                    break;
-                case 'Past 7 Days':
-                    if (nowTime > rowValue && rowValue > sevenDaysPast) match = true;
-                    break;
-                case 'Past 14 Days':
-                    if (nowTime > rowValue && rowValue > fourteenDaysPast) match = true;
-                    break;
-                case 'Past 30 Days':
-                    if (nowTime > rowValue && rowValue > thirtyDaysPast) match = true;
-                    break;
-            }
         }
         }
-        function numberFilter(rangeExp, rowValue){
-            var compareChar = rangeExp.charAt(0);
-            var compareValue = parseInt(rangeExp.substr(1, rangeExp.length - 1));
-            rowValue = (jQuery(rowValue).text()) ? jQuery(rowValue).text() : rowValue;
+      }
+
+      function dateFilter(condition, rowValue) {
+        var nowTime = new Date().getTime();
+        var oneDayPast = nowTime - 86400000;
+        var twoDaysPast = nowTime - 172800000;
+        var sevenDaysPast = nowTime - 604800000;
+        var fourteenDaysPast = nowTime - 1209600000;
+        var thirtyDaysPast = nowTime - 2592000000;
+        rowValue = (jQuery(rowValue).text()) ? jQuery(rowValue).text() : rowValue;
+        var lastChar = rowValue.charAt(rowValue.length - 1);
+        rowValue = lastChar === '*' ? rowValue.substr(0, rowValue.length - 1) : rowValue;
+        rowValue = new Date(rowValue).getTime();
+        match = false;
+        switch (condition) {
+          case 'Any':
+            match = true;
+            break;
+          case 'Past 1 Day':
+            if (nowTime > rowValue && rowValue > oneDayPast) match = true;
+            break;
+          case 'Past 2 Days':
+            if (nowTime > rowValue && rowValue > twoDaysPast) match = true;
+            break;
+          case 'Past 7 Days':
+            if (nowTime > rowValue && rowValue > sevenDaysPast) match = true;
+            break;
+          case 'Past 14 Days':
+            if (nowTime > rowValue && rowValue > fourteenDaysPast) match = true;
+            break;
+          case 'Past 30 Days':
+            if (nowTime > rowValue && rowValue > thirtyDaysPast) match = true;
+            break;
+          case 'Running Now':
+            if (lastChar === '*') match = true;
+            break;
+        }
+      }
+
+      function numberFilter(rangeExp, rowValue) {
+        var compareChar = rangeExp.charAt(0);
+        var compareValue = parseInt(rangeExp.substr(1, rangeExp.length - 1));
+        rowValue = (jQuery(rowValue).text()) ? jQuery(rowValue).text() : rowValue;
+        match = false;
+        switch (compareChar) {
+          case '<':
+            if (compareValue > rowValue) match = true;
+            break;
+          case '>':
+            if (compareValue < rowValue) match = true;
+            break;
+          case '=':
+            if (compareValue == rowValue) match = true;
+            break;
+          default:
             match = false;
             match = false;
-            switch (compareChar) {
-                case '<':
-                    if(compareValue > rowValue) match = true;
-                    break;
-                case '>':
-                    if(compareValue < rowValue) match = true;
-                    break;
-                case '=':
-                    if(compareValue == rowValue) match = true;
-                    break;
-                default:
-                    match = false;
-            }
         }
         }
-        return match;
+      }
+
+      return match;
     }
     }
 )
 )
 );
 );
 
 
 
 
-
-
 jQuery.extend(jQuery.fn.dataTableExt.oApi, {
 jQuery.extend(jQuery.fn.dataTableExt.oApi, {
-    "fnFilterClear":function ( oSettings )
-    {
-        /* Remove global filter */
-        oSettings.oPreviousSearch.sSearch = "";
-
-        /* Remove the text of the global filter in the input boxes */
-        if ( typeof oSettings.aanFeatures.f != 'undefined' )
-        {
-            var n = oSettings.aanFeatures.f;
-            for ( var i=0, iLen=n.length ; i<iLen ; i++ )
-            {
-                $('input', n[i]).val( '' );
-            }
-        }
-
-        /* Remove the search text for the column filters - NOTE - if you have input boxes for these
-         * filters, these will need to be reset
-         */
-        for ( var i=0, iLen=oSettings.aoPreSearchCols.length ; i<iLen ; i++ )
-        {
-            oSettings.aoPreSearchCols[i].sSearch = "";
-        }
-
-        /* Redraw */
-        oSettings.oApi._fnReDraw( oSettings );
+  "fnFilterClear": function (oSettings) {
+    /* Remove global filter */
+    oSettings.oPreviousSearch.sSearch = "";
+
+    /* Remove the text of the global filter in the input boxes */
+    if (typeof oSettings.aanFeatures.f != 'undefined') {
+      var n = oSettings.aanFeatures.f;
+      for (var i = 0, iLen = n.length; i < iLen; i++) {
+        $('input', n[i]).val('');
+      }
     }
     }
-});
-jQuery.extend($.fn.dataTableExt.afnFiltering.push(
-    function( oSettings, aData, iDataIndex ) {
-        var inputFilters = [
-            {iColumn:'4', elementId: 'jobs_filter', type:'number'},
-            {iColumn:'5', elementId: 'input_filter', type:'number' },
-            {iColumn:'6', elementId: 'output_filter', type:'number' },
-            {iColumn:'7', elementId: 'duration_filter', type:'number' },
-            {iColumn:'8', elementId: 'rundate_filter', type:'date' }
-        ];
-        var match = true;
-        for(i = 0; i < inputFilters.length; i++){
-            if(inputFilters[i].type === 'date'){
-                if(jQuery('#'+inputFilters[i].elementId).val() !== 'Any' && match) {
-                    dateFilter(jQuery('#'+inputFilters[i].elementId).val(), aData[inputFilters[i].iColumn]);
-                }
-            }
-            if(inputFilters[i].type === 'number'){
-                if(jQuery('#'+inputFilters[i].elementId).val() && match){
-                    numberFilter(jQuery('#'+inputFilters[i].elementId).val(), aData[inputFilters[i].iColumn]);
-                }
-            }
-        }
 
 
-        function dateFilter(condition, rowValue) {
-            var nowTime = new Date().getTime();
-            var oneDayPast = nowTime - 86400000;
-            var twoDaysPast = nowTime - 172800000;
-            var sevenDaysPast = nowTime - 604800000;
-            var fourteenDaysPast = nowTime - 1209600000;
-            var thirtyDaysPast = nowTime - 2592000000;
-            rowValue = (jQuery(rowValue).text()) ? jQuery(rowValue).text() : rowValue;
-            rowValue = new Date(rowValue).getTime();
-            match = false;
-            switch (condition) {
-                case 'Past 1 Day':
-                    if (nowTime > rowValue && rowValue > oneDayPast) match = true;
-                    break;
-                case 'Past 2 Days':
-                    if (nowTime > rowValue && rowValue > twoDaysPast) match = true;
-                    break;
-                case 'Past 7 Days':
-                    if (nowTime > rowValue && rowValue > sevenDaysPast) match = true;
-                    break;
-                case 'Past 14 Days':
-                    if (nowTime > rowValue && rowValue > fourteenDaysPast) match = true;
-                    break;
-                case 'Past 30 Days':
-                    if (nowTime > rowValue && rowValue > thirtyDaysPast) match = true;
-                    break;
-            }
-        }
-        function numberFilter(rangeExp, rowValue){
-            var compareChar = rangeExp.charAt(0);
-            var compareValue = parseInt(rangeExp.substr(1, rangeExp.length - 1));
-            rowValue = (jQuery(rowValue).text()) ? jQuery(rowValue).text() : rowValue;
-            match = false;
-            switch (compareChar) {
-                case '<':
-                    if(compareValue > rowValue) match = true;
-                    break;
-                case '>':
-                    if(compareValue < rowValue) match = true;
-                    break;
-                case '=':
-                    if(compareValue == rowValue) match = true;
-                    break;
-                default:
-                    match = false;
-            }
-        }
-        return match;
+    /* Remove the search text for the column filters - NOTE - if you have input boxes for these
+     * filters, these will need to be reset
+     */
+    for (var i = 0, iLen = oSettings.aoPreSearchCols.length; i < iLen; i++) {
+      oSettings.aoPreSearchCols[i].sSearch = "";
     }
     }
-)
-);
+
+    /* Redraw */
+    oSettings.oApi._fnReDraw(oSettings);
+  }
+});
 
 
 
 
 
 

+ 2 - 0
ambari-web/app/views.js

@@ -101,6 +101,8 @@ require('views/main/charts/heatmap/heatmap_host');
 require('views/main/charts/heatmap/heatmap_host_detail');
 require('views/main/charts/heatmap/heatmap_host_detail');
 require('views/main/apps_view');
 require('views/main/apps_view');
 require('views/main/apps/item_view');
 require('views/main/apps/item_view');
+require('views/main/apps/item/bar_view');
+require('views/main/apps/item/dag_view');
 require('views/installer');
 require('views/installer');
 require('views/wizard/controls_view');
 require('views/wizard/controls_view');
 require('views/wizard/step1_view');
 require('views/wizard/step1_view');

+ 17 - 11
ambari-web/app/views/main/apps/runs/jobs/bar_view.js → ambari-web/app/views/main/apps/item/bar_view.js

@@ -18,21 +18,25 @@
 
 
 var App = require('app');
 var App = require('app');
 var graph = require('utils/graph');
 var graph = require('utils/graph');
-App.MainAppsRunsJobsBarView = Em.View.extend({
-    templateName: require('templates/main/apps/runs/jobs/bar'),
+
+App.MainAppsItemBarView = Em.View.extend({
+    templateName: require('templates/main/apps/item/bar'),
     content:function(){
     content:function(){
-        return App.router.get('mainAppsRunsItemController.content').get('jobs');
-    }.property('App.router.mainAppsRunsItemController.content'),
+        return this.get('parentView.jobs');
+    }.property('parentView.jobs'),
     firstJob: function() {
     firstJob: function() {
         return this.get('content').get('firstObject');
         return this.get('content').get('firstObject');
     }.property('content'),
     }.property('content'),
+    activeJob:null,
+    selectJob: function(event) {
+        this.set('activeJob', event.context);
+
+    },
     didInsertElement:function (){
     didInsertElement:function (){
-        this.get('controller').set('activeJobId', this.get('firstJob').get('jobId'));
-        this.get('controller').set('job',this.get('firstJob'));
-        this.get('controller').set('job', null);
+        this.set('activeJob', this.get('firstJob'));
     },
     },
     draw: function() {
     draw: function() {
-        if(!this.get('controller').get('job')){
+        if(!this.get('activeJob')){
             return;//when job is not defined
             return;//when job is not defined
         }
         }
         width = 500;
         width = 500;
@@ -43,7 +47,9 @@ App.MainAppsRunsJobsBarView = Em.View.extend({
         if (null == desc1.html() || null == desc2.html()) return;
         if (null == desc1.html() || null == desc2.html()) return;
         desc1.css('display','block');
         desc1.css('display','block');
         desc2.css('display','block');
         desc2.css('display','block');
-        graph.drawJobTimeline(this.get('controller').get('job').get('jobTimeline'), width, height, '#chart', 'legend', 'timeline1');
-        graph.drawJobTasks(this.get('controller').get('job').get('jobTaskview'), width, height, '#job_tasks', 'tasks_legend', 'timeline2');
-    }.observes('controller.job')
+        graph.drawJobTimeline(this.get('activeJob').get('jobTimeline'), width, height, '#chart', 'legend', 'timeline1');
+        graph.drawJobTasks(this.get('activeJob').get('jobTaskview'), width, height, '#job_tasks', 'tasks_legend', 'timeline2');
+    }.observes('activeJob')
+
+
 });
 });

+ 16 - 17
ambari-web/app/views/main/apps/runs/jobs/dag_view.js → ambari-web/app/views/main/apps/item/dag_view.js

@@ -18,34 +18,33 @@
 
 
 var App = require('app');
 var App = require('app');
 
 
-App.MainAppsRunsJobsDagView = Em.View.extend({
-  templateName: require('templates/main/apps/runs/jobs/dag'),
+App.MainAppsItemDagView = Em.View.extend({
+  templateName: require('templates/main/apps/item/dag'),
   content:function(){
   content:function(){
-     return App.router.get('mainAppsRunsItemController.content').get('jobs');
-  }.property('App.router.mainAppsRunsItemController.content'),
+    return this.get('parentView.jobs');
+  }.property('parentView.jobs'),
   classNames:['table','dataTable'],
   classNames:['table','dataTable'],
   jobs: function(){
   jobs: function(){
     var c = this.get('content');
     var c = this.get('content');
     var result = new Array();
     var result = new Array();
     c.forEach(function(item, index){
     c.forEach(function(item, index){
       result[index] = new Object({
       result[index] = new Object({
-          'name' : item.get('id'),
-          'status' : (item.get('status') == 'COMPLETE')? true : false,
-          'info' : [],
-          'input' : 2,
-          'output' : 3
+        'name' : item.get('id'),
+        'status' : (item.get('status') == 'COMPLETE')? true : false,
+        'info' : [],
+        'input' : 2,
+        'output' : 3
       })
       })
     });
     });
     return result;
     return result;
   }.property('content'),
   }.property('content'),
   didInsertElement:function (){
   didInsertElement:function (){
-    var oTable = this.$('#dataTable').dataTable({
-    });
-    var dagSchema = App.router.get('mainAppsRunsItemController.content').get('workflowContext');
-    var jobs = this.get('jobs');
+    var innerTable = this.$('#innerTable').dataTable({});
+    var dagSchema = this.get('parentView.parentView.run').get('workflowContext');
+    var jobs = this.get('content');
     var graph = new DagViewer(false,'dag_viewer')
     var graph = new DagViewer(false,'dag_viewer')
-    .setPhysicalParametrs(800,250,-800,0.01)
-    .setData(dagSchema,jobs)
-    .drawDag(10,20,100);
-    }
+        .setPhysicalParametrs(800,250,-800,0.01)
+        .setData(dagSchema,jobs)
+        .drawDag(10,20,100);
+  }
 });
 });

+ 45 - 1
ambari-web/app/views/main/apps/item_view.js

@@ -19,6 +19,50 @@
 var App = require('app');
 var App = require('app');
 
 
 App.MainAppsItemView = Em.View.extend({
 App.MainAppsItemView = Em.View.extend({
+  tagName: 'tr',
   templateName:require('templates/main/apps/item'),
   templateName:require('templates/main/apps/item'),
-  content:null
+  menuTabs:[
+    Em.Object.create({
+      label:'DAG',
+      active:'active',
+      content:'App.MainAppsItemDagView'
+    }),
+    Em.Object.create({
+      label:'Jobs',
+      active:'',
+      content:'App.MainAppsItemBarView'
+    })
+  ],
+  run:function(){
+    return this.get('parentView.run');
+  }.property('parentView.run'),
+  activeTab:null,
+  switchTab:function(event){
+    var tabs = this.get('menuTabs');
+    for(var i = 0; i < tabs.length; i++){
+      if(tabs[i] === event.context) tabs[i].set('active', 'active');
+      else tabs[i].set('active', '');
+    }
+    this.set('activeTab', event.context);
+  },
+  containerView:Em.ContainerView.extend({
+    elementId:'cont',
+    jobs:function(){
+      return this.get('parentView.run').get('jobs');
+    }.property('parentView.run'),
+    onchange:function(){
+      var view;
+      if(this.get('parentView.activeTab').get('label') === 'DAG'){
+        var view = App.MainAppsItemDagView.create();
+      }
+      if(this.get('parentView.activeTab').get('label') === 'Jobs'){
+        var view = App.MainAppsItemBarView.create();
+      }
+      if(this.get('childViews')){
+        this.get('childViews').get('firstObject').destroy();
+        this.get('childViews').removeObject(this.get('childViews').get('firstObject'));
+      }
+      this.get('childViews').pushObject(view);
+    }.observes('parentView.activeTab')
+  })
 });
 });

+ 0 - 26
ambari-web/app/views/main/apps/runs/item_view.js

@@ -1,26 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-var App = require('app');
-
-App.MainAppsRunsItemView = Em.View.extend({
-    templateName: require('templates/main/apps/runs/item'),
-    content:function(){
-    return App.router.get('mainAppsRunsItemController.content');
-    }.property('App.router.mainAppsRunsItemController.content')
-});

+ 0 - 48
ambari-web/app/views/main/apps/runs/jobs/menu_view.js

@@ -1,48 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-var App = require('app');
-
-App.MainJobsAppsRunsMenuView = Em.CollectionView.extend({
-  tagName: 'ul',
-  classNames: ["nav", "nav-tabs"],
-  content:[
-    { label:'DAG', routing:'dag', active:"active"},
-    { label:'BAR', routing:'bar'}
-  ],
-
-  init: function(){ this._super(); this.activateView(); },
-
-  activateView:function () {
-    $.each(this._childViews, function () {
-      this.set('active', (this.get('content.routing') == 'dag' ? "active" : ""));
-    });
-  },
-
-  deactivateChildViews: function() {
-    $.each(this._childViews, function(){
-      this.set('active', "");
-    });
-  },
-
-  itemViewClass: Em.View.extend({
-    classNameBindings: ["active"],
-    active: "",
-    template: Ember.Handlebars.compile('<a {{action showGraph view.content.routing }} href="#"> {{unbound view.content.label}}</a>')
-  })
-});

+ 0 - 29
ambari-web/app/views/main/apps/runs/jobs_view.js

@@ -1,29 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-var App = require('app');
-
-App.MainAppsRunsJobsView = Em.View.extend({
-  templateName: require('templates/main/apps/runs/jobs'),
-  runItem: function(){
-    return App.router.get('mainAppsRunsItemController.content');
-  }.property('App.router.mainAppsRunsItemController.content'),
-  appItem:function(){
-    return App.router.get('mainAppsItemController.content');
-  }.property('App.router.mainAppsItemController.content')
-});

+ 0 - 63
ambari-web/app/views/main/apps/runs_view.js

@@ -1,63 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-var App = require('app');
-var validator = require('utils/validator');
-var date = require('utils/date');
-
-App.MainAppsRunsView = Em.View.extend({
-  templateName: require('templates/main/apps/runs'),
-  classNames:['table','dataTable'],
-  didInsertElement:function (){
-    var oTable = this.$('#dataTable').dataTable({
-        "aoColumns": [
-            null,
-            { "sType": "ambari-date" },
-            null,
-            null,
-            null,
-            null,
-            null,
-            null
-        ]
-    });
-  },
-  RunsCheckboxView: Em.Checkbox.extend({
-    content: null,
-    isChecked: false,
-    change: function(event) {
-
-    }
-  }),
-  content:function(){
-    var content = App.router.get('mainAppsItemController.content').get('runs');
-    content.forEach(function(item){
-      item.set('numJobs', item.get('jobs').get('content').length );
-      var startTime = item.get('startTime');
-      var lastUpdateTime = item.get('lastUpdateTime');
-      item.set('startTime', date.dateFormat(item.get('startTime')));
-      if (validator.isValidInt(lastUpdateTime)) {
-        item.set('lastUpdateTime', date.dateFormatInterval((lastUpdateTime - startTime)/1000));
-      }
-    });
-    return content;
-  }.property('App.router.mainAppsItemController.content'),
-  appItem:function(){
-    return App.router.get('mainAppsItemController.content');
-  }.property('App.router.mainAppsItemController.content')
-});

+ 182 - 94
ambari-web/app/views/main/apps_view.js

@@ -18,42 +18,90 @@
 
 
 var App = require('app');
 var App = require('app');
 var date = require('utils/date');
 var date = require('utils/date');
+var validator = require('utils/validator');
 
 
 App.MainAppsView = Em.View.extend({
 App.MainAppsView = Em.View.extend({
   templateName:require('templates/main/apps'),
   templateName:require('templates/main/apps'),
+  /**
+   * List of runs
+   */
   content:function () {
   content:function () {
     var content =  this.get('controller').get('content');
     var content =  this.get('controller').get('content');
     content.forEach(function(item){
     content.forEach(function(item){
       var app = App.store.find(App.App, item.get('appId'));
       var app = App.store.find(App.App, item.get('appId'));
       item.set('appName', app.get('appName'));
       item.set('appName', app.get('appName'));
       item.set('type', app.get('type'));
       item.set('type', app.get('type'));
-      item.set('duration', date.dateFormatInterval(((item.get('lastUpdateTime') - item.get('startTime'))/1000)));
-      item.set('lastUpdateTime', date.dateFormat(item.get('lastUpdateTime')));
       item.set('numJobsTotal' ,item.get('jobs').get('content').length);
       item.set('numJobsTotal' ,item.get('jobs').get('content').length);
-      item.get('jobs').forEach(function(item){
-
+      item.get('jobs').forEach(function(item) {
       });
       });
     });
     });
     return content;
     return content;
   }.property('App.router.mainAppsController.content'),
   }.property('App.router.mainAppsController.content'),
-  users: function(){
+  /**
+   * List of users
+   */
+  users: function() {
     var result = new Array();
     var result = new Array();
-    this.get('content').forEach(function(item){
+    this.get('content').forEach(function(item) {
        result.push(item.get('userName'));
        result.push(item.get('userName'));
     });
     });
     return jQuery.unique(result);
     return jQuery.unique(result);
   }.property('content'),
   }.property('content'),
+  /**
+   * jQuery dataTable object
+   */
   oTable:null,
   oTable:null,
+  /**
+   * jQuery collection of stars icons (in dataTables). Saved here for easy "turning off"
+   */
+  smallStarsIcons: null,
+  /**
+   * Count of filtered runs
+   */
   filtered:null,
   filtered:null,
-  stared: function() {
-    var content =  this.get('controller.staredRuns');
+  /**
+   * Flag for avgData
+   */
+  whatAvgShow: true, // true - for filtered data, false - for starred
+  /**
+   * avg data for display. Can be stared or filtered. Based on whatAvgShow
+   */
+  avgData: function() {
+    if (this.get('whatAvgShow')) {
+      return this.get('filteredData');
+    }
+    else {
+      return this.get('staredData');
+    }
+  }.property('filteredData', 'staredData', 'whatAvgShow'),
+  /**
+   * Avg data of filtered runs
+   */
+  filteredData: function() {
+    return this.getAvgData(this.get('controller.filteredRuns'));
+  }.property('controller.filteredRunsLength'),
+  /**
+   * Avg data of stared runs
+   */
+  staredData: function() {
+    return this.getAvgData(this.get('controller.staredRuns'));
+  }.property('controller.staredRunsLength'),
+  setFilteredRuns: function(data) {
+
+  },
+  /**
+   * Common method for calculation avg data (filtered or stored - based on param content)
+   * @param content data-array
+   * @return {*} array with calculated data
+   */
+  getAvgData: function(content) {
     var avgJobs = 0.0, minJobs = 0, maxJobs = 0, avgInput = 0, minInput = 0, maxInput = 0, avgOutput = 0, minOutput = 0, maxOutput = 0, avgDuration = 0.0, minDuration = 0, maxDuration = 0, oldest = 0, youngest = 0;
     var avgJobs = 0.0, minJobs = 0, maxJobs = 0, avgInput = 0, minInput = 0, maxInput = 0, avgOutput = 0, minOutput = 0, maxOutput = 0, avgDuration = 0.0, minDuration = 0, maxDuration = 0, oldest = 0, youngest = 0;
     if (content.length > 0) {
     if (content.length > 0) {
       minJobs = content[0].get('numJobsTotal');
       minJobs = content[0].get('numJobsTotal');
       minInput = content[0].get('input');
       minInput = content[0].get('input');
       minOutput = content[0].get('output');
       minOutput = content[0].get('output');
-      oldest = date.dateUnformat(content[0].get('lastUpdateTime'));
-      youngest = date.dateUnformat(content[0].get('lastUpdateTime'));
+      oldest = content[0].get('lastUpdateTime');
+      youngest = content[0].get('lastUpdateTime');
       minDuration = date.dateUnformatInterval(content[0].get('duration'));
       minDuration = date.dateUnformatInterval(content[0].get('duration'));
     }
     }
     content.forEach(function(item) {
     content.forEach(function(item) {
@@ -93,17 +141,29 @@ App.MainAppsView = Em.View.extend({
           maxDuration = date.dateUnformatInterval(item.get('duration'));
           maxDuration = date.dateUnformatInterval(item.get('duration'));
         }
         }
       }
       }
-      if (date.dateUnformat(item.get('lastUpdateTime')) < oldest) {
-        oldest = date.dateUnformat(item.get('lastUpdateTime'));
+      if (item.get('lastUpdateTime') < oldest) {
+        oldest = item.get('lastUpdateTime');
       }
       }
       else {
       else {
-        if (date.dateUnformat(item.get('lastUpdateTime')) > youngest) {
-          youngest = date.dateUnformat(item.get('lastUpdateTime'));
+        if (item.get('lastUpdateTime') > youngest) {
+          youngest = item.get('lastUpdateTime');
         }
         }
       }
       }
     });
     });
-    oldest = oldest != 0 ? oldest.substring(0, 4) + '-' + oldest.substring(4, 6) + '-' + oldest.substring(6, 8) : '';
-    youngest = youngest != 0 ? youngest.substring(0, 4) + '-' + youngest.substring(4, 6) + '-' + youngest.substring(6, 8) : '';
+    if (oldest != 0) {
+      d = new Date(oldest*1);
+      oldest = d.toDateString();
+    }
+    else {
+      oldest = '0000-00-00';
+    }
+    if (youngest != 0) {
+      d = new Date(youngest*1);
+      youngest = d.toDateString();
+    }
+    else {
+      youngest = '0000-00-00';
+    }
     ret = {
     ret = {
       'count': this.get('controller.staredRuns').length,
       'count': this.get('controller.staredRuns').length,
       'jobs': {
       'jobs': {
@@ -132,16 +192,12 @@ App.MainAppsView = Em.View.extend({
       }
       }
     };
     };
     return ret;
     return ret;
-  }.property('controller.staredRunsLength'),
-  /*starsStats: function() {
-    var content =  this.get('controller.staredRuns');
-    var avgJobs = 0.0, minJobs = 0, maxJobs = 0, avgInput = 0, minInput = 0, maxInput = 0, avgOutput = 0, minOutput = 0, maxOutput = 0;
-    content.forEach(function(item) {
-      avgJobs += item.get('numJobsTotal') / content.length;
-    });
-    this.set('avgJobs', 1);
-  }.property('controller.staredRunsLength'),*/
-  clearFilters:function(event){
+  },
+  /**
+   * Reset filters
+   * @param event
+   */
+  clearFilters:function(event) {
     this._childViews.forEach(function(item){
     this._childViews.forEach(function(item){
       if(item.get('tagName') === 'input') {
       if(item.get('tagName') === 'input') {
           item.set('value','');
           item.set('value','');
@@ -153,11 +209,32 @@ App.MainAppsView = Em.View.extend({
     this.get('oTable').fnFilterClear();
     this.get('oTable').fnFilterClear();
     this.set('filtered',this.get('oTable').fnSettings().fnRecordsDisplay());
     this.set('filtered',this.get('oTable').fnSettings().fnRecordsDisplay());
   },
   },
+  /**
+   * Click on big star on the avg block
+   */
+  avgStarClick: function() {
+    this.set('whatAvgShow', !this.get('whatAvgShow'));
+    event.srcElement.classList.toggle('active');
+  },
+  /**
+   * "turn off" stars in the table
+   */
+  clearStars: function(){
+    if (this.get('controller.staredRunsLength') == 0 && this.get('smallStarsIcons') != null) {
+      this.get('smallStarsIcons').removeClass('stared');
+    }
+  }.observes('controller.staredRunsLength'),
+  /**
+   * jQuery dataTable init
+   */
   didInsertElement:function () {
   didInsertElement:function () {
+    var smallStars = $('#dataTable .icon-star');
+    this.set('smallStarsIcons', smallStars);
     var oTable = this.$('#dataTable').dataTable({
     var oTable = this.$('#dataTable').dataTable({
       "sDom": '<"search-bar"f><"clear">rt<"page-bar"lip><"clear">',
       "sDom": '<"search-bar"f><"clear">rt<"page-bar"lip><"clear">',
       "fnDrawCallback": function( oSettings ) {
       "fnDrawCallback": function( oSettings ) {
         //change average info after table filtering
         //change average info after table filtering
+        // no need more. 31.10.2012
       },
       },
       "oLanguage": {
       "oLanguage": {
         "sSearch": "<i class='icon-question-sign'>&nbsp;Search</i>",
         "sSearch": "<i class='icon-question-sign'>&nbsp;Search</i>",
@@ -192,8 +269,8 @@ App.MainAppsView = Em.View.extend({
    *
    *
    * @param event
    * @param event
    */
    */
-  clearFilters:function(event){
-    this._childViews.forEach(function(item){
+  clearFilters:function(event) {
+    this._childViews.forEach(function(item) {
     if(item.get('tagName') === 'input') {
     if(item.get('tagName') === 'input') {
       item.set('value','');
       item.set('value','');
     }
     }
@@ -206,6 +283,7 @@ App.MainAppsView = Em.View.extend({
     });
     });
     this.get('oTable').fnFilterClear();
     this.get('oTable').fnFilterClear();
     this.set('filtered',this.get('oTable').fnSettings().fnRecordsDisplay());
     this.set('filtered',this.get('oTable').fnSettings().fnRecordsDisplay());
+    this.setFilteredRuns(this.get('oTable')._('tr', {"filter":"applied"}));
   },
   },
   /**
   /**
    * apply each filter to dataTable
    * apply each filter to dataTable
@@ -214,7 +292,7 @@ App.MainAppsView = Em.View.extend({
    * @param {iColumn} number of column by which filter
    * @param {iColumn} number of column by which filter
    * @param {value}
    * @param {value}
    */
    */
-  applyFilter:function(parentView, iColumn, value){
+  applyFilter:function(parentView, iColumn, value) {
       value = (value) ? value : '';
       value = (value) ? value : '';
       parentView.get('oTable').fnFilter(value, iColumn);
       parentView.get('oTable').fnFilter(value, iColumn);
       parentView.set('filtered',parentView.get('oTable').fnSettings().fnRecordsDisplay());
       parentView.set('filtered',parentView.get('oTable').fnSettings().fnRecordsDisplay());
@@ -222,14 +300,18 @@ App.MainAppsView = Em.View.extend({
   /**
   /**
    * refresh average info in top block when filtered changes
    * refresh average info in top block when filtered changes
    */
    */
-  averageRefresh:function(){
-
+  averageRefresh:function() {
+    var rows = this.get('oTable')._('tr', {"filter":"applied"});
+    this.get('controller').clearFilteredRuns();
+    for(var i = 0; i < rows.length; i++) {
+      this.get('controller').addFilteredRun($(rows[i][0]).find('span.hidden').text());
+    }
   }.observes('filtered'),
   }.observes('filtered'),
   /**
   /**
    * dataTable filter views
    * dataTable filter views
    */
    */
   typeSelectView: Em.Select.extend({
   typeSelectView: Em.Select.extend({
-    classNames:['input-small'],
+    classNames: ['input-small'],
     selected: 'Any',
     selected: 'Any',
     content:['Any', 'Pig', 'Hive', 'mapReduce'],
     content:['Any', 'Pig', 'Hive', 'mapReduce'],
     change:function(event){
     change:function(event){
@@ -241,68 +323,70 @@ App.MainAppsView = Em.View.extend({
       this._parentView.set('filtered',this._parentView.get('oTable').fnSettings().fnRecordsDisplay());
       this._parentView.set('filtered',this._parentView.get('oTable').fnSettings().fnRecordsDisplay());
     }
     }
   }),
   }),
+  /**
+   * Filter-field for RunDate
+   */
   rundateSelectView: Em.Select.extend({
   rundateSelectView: Em.Select.extend({
     change:function(e) {
     change:function(e) {
       console.log(this.get('selection'));
       console.log(this.get('selection'));
     },
     },
-    content: ['Any', 'Running Now', 'Past 1 Day', 'Past 2 Days', 'Past 7 Days', 'Past 14 Days', 'Past 30 Days', 'Custom'],
+    content: ['Any', 'Running Now', 'Past 1 Day', 'Past 2 Days', 'Past 7 Days', 'Past 14 Days', 'Past 30 Days'],
     selected: 'Any',
     selected: 'Any',
     classNames:['input-medium'],
     classNames:['input-medium'],
     elementId: 'rundate_filter',
     elementId: 'rundate_filter',
-    change:function(e) {
-      this._parentView.get('oTable').fnFilter('', 8);
+    change:function(event) {
+      this.get('parentView').get('applyFilter')(this.get('parentView'), 8);
     }
     }
   }),
   }),
+  /**
+   * Filter-field for AppId
+   */
   appidFilterView: Em.TextField.extend({
   appidFilterView: Em.TextField.extend({
     classNames:['input-small'],
     classNames:['input-small'],
     type:'text',
     type:'text',
     placeholder: 'Any ID',
     placeholder: 'Any ID',
     filtering:function(){
     filtering:function(){
-      console.log(this.get('value'));
-      this._parentView.get('oTable').fnFilter(this.get('value') ,0);
-      this._parentView.set('filtered',this._parentView.get('oTable').fnSettings().fnRecordsDisplay());
+        this.get('parentView').get('applyFilter')(this.get('parentView'), 0, this.get('value'));
     }.observes('value')
     }.observes('value')
   }),
   }),
+  /**
+   * Filter-field for name
+   */
   nameFilterView: Em.TextField.extend({
   nameFilterView: Em.TextField.extend({
     classNames:['input-small'],
     classNames:['input-small'],
     type:'text',
     type:'text',
     placeholder: 'Any Name',
     placeholder: 'Any Name',
     filtering:function(){
     filtering:function(){
-      this._parentView.get('oTable').fnFilter(this.get('value') ,1);
-      this._parentView.set('filtered',this._parentView.get('oTable').fnSettings().fnRecordsDisplay());
+      this.get('parentView').get('applyFilter')(this.get('parentView'), 1, this.get('value'));
     }.observes('value')
     }.observes('value')
   }),
   }),
-  userFilterView: Em.View.extend({
-    classNames:['btn-group'],
-    template: Ember.Handlebars.compile(
-        '<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">'+
-        'User<span class="caret"></span></a>'+
-        '<ul class="dropdown-menu filter-components">'+
-        '<li><a><input type="checkbox">1</a></li>'+
-        '<li><a><input type="checkbox">1</a></li>'+
-        '</ul>'
-    )
-  }),
+  /**
+   * Filter-field for jobs
+   */
   jobsFilterView: Em.TextField.extend({
   jobsFilterView: Em.TextField.extend({
     classNames:['input-mini'],
     classNames:['input-mini'],
     type:'text',
     type:'text',
     placeholder: 'Any ',
     placeholder: 'Any ',
     elementId:'jobs_filter',
     elementId:'jobs_filter',
     filtering:function(){
     filtering:function(){
-      this._parentView.get('oTable').fnFilter('', 4);
-      this._parentView.set('filtered',this._parentView.get('oTable').fnSettings().fnRecordsDisplay());
+      this.get('parentView').get('applyFilter')(this.get('parentView'), 4);
     }.observes('value')
     }.observes('value')
   }),
   }),
+  /**
+   * Filter-field for Input
+   */
   inputFilterView: Em.TextField.extend({
   inputFilterView: Em.TextField.extend({
     classNames:['input-mini'],
     classNames:['input-mini'],
     type:'text',
     type:'text',
     placeholder: 'Any ',
     placeholder: 'Any ',
     elementId: 'input_filter',
     elementId: 'input_filter',
     filtering:function(){
     filtering:function(){
-      this._parentView.get('oTable').fnFilter('' ,5);
-      this._parentView.set('filtered',this._parentView.get('oTable').fnSettings().fnRecordsDisplay());
+      this.get('parentView').get('applyFilter')(this.get('parentView'), 5);
     }.observes('value')
     }.observes('value')
   }),
   }),
+  /**
+   * Filter-field for Output
+   */
   outputFilterView: Em.TextField.extend({
   outputFilterView: Em.TextField.extend({
     classNames:['input-mini'],
     classNames:['input-mini'],
     type:'text',
     type:'text',
@@ -312,6 +396,9 @@ App.MainAppsView = Em.View.extend({
         this.get('parentView').get('applyFilter')(this.get('parentView'), 6);
         this.get('parentView').get('applyFilter')(this.get('parentView'), 6);
     }.observes('value')
     }.observes('value')
   }),
   }),
+  /**
+   * Filter-field for Duration
+   */
   durationFilterView: Em.TextField.extend({
   durationFilterView: Em.TextField.extend({
     classNames:['input-mini'],
     classNames:['input-mini'],
     type:'text',
     type:'text',
@@ -321,6 +408,9 @@ App.MainAppsView = Em.View.extend({
       this.get('parentView').get('applyFilter')(this.get('parentView'), 7);
       this.get('parentView').get('applyFilter')(this.get('parentView'), 7);
     }.observes('value')
     }.observes('value')
   }),
   }),
+  /**
+   * Filter-list for User
+   */
   userFilterView: Em.View.extend({
   userFilterView: Em.View.extend({
     classNames:['btn-group'],
     classNames:['btn-group'],
     classNameBindings: ['open'],
     classNameBindings: ['open'],
@@ -351,17 +441,17 @@ App.MainAppsView = Em.View.extend({
       'Apply</button>'
       'Apply</button>'
     ),
     ),
     allComponentsChecked:false,
     allComponentsChecked:false,
-    toggleAllComponents: function(){
+    toggleAllComponents: function() {
       var checked = this.get('allComponentsChecked');
       var checked = this.get('allComponentsChecked');
       this.get('users').forEach(function(item){
       this.get('users').forEach(function(item){
         item.set('checked',checked);
         item.set('checked',checked);
       });
       });
     }.observes('allComponentsChecked'),
     }.observes('allComponentsChecked'),
-    clickFilterButton:function(event){
+    clickFilterButton:function(event) {
       this.set('open', !this.get('open'));
       this.set('open', !this.get('open'));
       this.set('isApplyDisabled', !this.get('isApplyDisabled'));
       this.set('isApplyDisabled', !this.get('isApplyDisabled'));
     },
     },
-    clearFilter:function(self){
+    clearFilter:function(self) {
       self.set('allComponentsChecked', true);
       self.set('allComponentsChecked', true);
       self.set('allComponentsChecked', false);
       self.set('allComponentsChecked', false);
       jQuery('#user_filter').val([]);
       jQuery('#user_filter').val([]);
@@ -378,6 +468,13 @@ App.MainAppsView = Em.View.extend({
       this.get('parentView').get('applyFilter')(this.get('parentView'), 3);
       this.get('parentView').get('applyFilter')(this.get('parentView'), 3);
     }
     }
   }),
   }),
+  /**
+   *  Object contain views of expanded row
+   */
+  expandedRow:Ember.Object.create({
+    rowView:null,
+    rowChildView:null
+  }),
   /**
   /**
    * This Container View is used to render static table row(appTableRow) and additional dynamic content
    * This Container View is used to render static table row(appTableRow) and additional dynamic content
    */
    */
@@ -389,42 +486,40 @@ App.MainAppsView = Em.View.extend({
     id: function(){
     id: function(){
       return this.get('run.id');
       return this.get('run.id');
     }.property("run.id"),
     }.property("run.id"),
-    currentId: null,
-    /**
-     * Variable for dynamic view
-     */
-    contentView : null,
 
 
     /**
     /**
-     * Show additional content appropriated for this row
+     * Show/hide additional content appropriated for this row
      */
      */
-    expand : function(){
-      var view = App.MainAppsItemView.create();
-      this.set('contentView', view);
-      this.get('childViews').pushObject(view);
-      this.set('currentId', this.get('id'));
-      //this.set('controller.expandedRowId', this.get('id'));
-    },
-
-    /**
-     * Check whether user opens another row. If yes - hide current content
-     */
-    hideExpand : function(){
-      var contentView = this.get('contentView');
-      if(this.get('controller.expandedRowId') !== this.get('id') && contentView){
-        this.get('childViews').removeObject(contentView);
-        contentView.destroy();
-        this.set('contentView', null);
+    expandToggle : function(){
+      var newView = App.MainAppsItemView.create();
+      var expandedView = this.get('parentView.expandedRow');
+      if(expandedView.get('rowView')) {
+        if(this === expandedView.get('rowView')) {
+          expandedView.get('rowView').get('childViews').removeObject(expandedView.get('rowChildView'));
+          expandedView.get('rowChildView').destroy();
+          expandedView.set('rowView', null);
+          expandedView.set('rowChildView', null);
+        } else {
+          //expandedView.get('rowView').get('childViews').removeObject(expandedView.get('rowChildView'));
+          expandedView.get('rowChildView').destroy();
+          expandedView.set('rowView', this);
+          expandedView.set('rowChildView', newView);
+          this.get('childViews').pushObject(newView);
+        }
+      } else {
+          expandedView.set('rowView', this);
+          expandedView.set('rowChildView', newView);
+          this.get('childViews').pushObject(newView);
       }
       }
-    }.observes('controller.expandedRowId')
+    }
   }),
   }),
-
+  /**
+   * Table-row view
+   */
   appTableRow: Em.View.extend({
   appTableRow: Em.View.extend({
     templateName : require('templates/main/apps/list_row'),
     templateName : require('templates/main/apps/list_row'),
     classNames:['app-table-row'],
     classNames:['app-table-row'],
-    classNameBindings: ['rowClass'],
     tagName: "tr",
     tagName: "tr",
-    rowOpened:0,
     mouseEnter: function(event, view){
     mouseEnter: function(event, view){
       $(event.currentTarget).addClass("hover")
       $(event.currentTarget).addClass("hover")
     },
     },
@@ -432,16 +527,9 @@ App.MainAppsView = Em.View.extend({
       $(event.currentTarget).removeClass("hover");
       $(event.currentTarget).removeClass("hover");
     },
     },
     click: function(event,view){
     click: function(event,view){
-      var target=$(event.currentTarget);
-      // if rowOpend=1 new value=0 and visaversa
-      this.set("rowOpened",1- this.get("rowOpened"));
-
-      this.get('parentView').expand();
+      this.get('parentView').expandToggle();
+    }
 
 
-    },
-    rowClass: function () {
-      return this.get('rowOpened') ? "row-opened" : "row-closed";
-    }.property('rowOpened')
 
 
   })
   })