Repository: ambari
Updated Branches:
  refs/heads/trunk a3d1d6732 -> 74ef96200


AMBARI-5237 Implement pagination on "Confirm Hosts" step. (atkach)


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

Branch: refs/heads/trunk
Commit: 74ef96200a8b8ba1bc761d5ad7d561d35721cc4a
Parents: a3d1d67
Author: atkach <[email protected]>
Authored: Thu Mar 27 14:08:14 2014 +0200
Committer: atkach <[email protected]>
Committed: Thu Mar 27 14:08:14 2014 +0200

----------------------------------------------------------------------
 .../app/controllers/wizard/step2_controller.js  |   4 +-
 .../app/controllers/wizard/step3_controller.js  | 149 ++++-------------
 ambari-web/app/messages.js                      |   2 +
 ambari-web/app/styles/application.less          |  19 +++
 .../app/templates/common/items_list_popup.hbs   |  21 +++
 .../wizard/step2_host_name_pattern_popup.hbs    |  21 ---
 ambari-web/app/templates/wizard/step3.hbs       | 158 +++++++++++--------
 ambari-web/app/views/common/table_view.js       |  93 +++++++++--
 ambari-web/app/views/wizard/step3_view.js       | 133 +++++++++++++++-
 9 files changed, 381 insertions(+), 219 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/74ef9620/ambari-web/app/controllers/wizard/step2_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step2_controller.js 
b/ambari-web/app/controllers/wizard/step2_controller.js
index 4d7df4c..e2eec44 100644
--- a/ambari-web/app/controllers/wizard/step2_controller.js
+++ b/ambari-web/app/controllers/wizard/step2_controller.js
@@ -309,8 +309,8 @@ App.WizardStep2Controller = Em.Controller.extend({
         this.hide();
       },
       bodyClass: Ember.View.extend({
-        templateName: 
require('templates/wizard/step2_host_name_pattern_popup'),
-        hostNames: hostNames
+        templateName: require('templates/common/items_list_popup'),
+        items: hostNames
       })
     });
   },

http://git-wip-us.apache.org/repos/asf/ambari/blob/74ef9620/ambari-web/app/controllers/wizard/step3_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step3_controller.js 
b/ambari-web/app/controllers/wizard/step3_controller.js
index f5b2fa6..b3720a3 100644
--- a/ambari-web/app/controllers/wizard/step3_controller.js
+++ b/ambari-web/app/controllers/wizard/step3_controller.js
@@ -38,68 +38,6 @@ App.WizardStep3Controller = Em.Controller.extend({
   stopBootstrap: false,
   isSubmitDisabled: true,
 
-  categoryObject: Em.Object.extend({
-    hostsCount: function () {
-      var category = this;
-      var hosts = this.get('controller.hosts').filter(function(_host) {
-        if (_host.get('bootStatus') == category.get('hostsBootStatus')) {
-          return true;
-        } else {
-          return (_host.get('bootStatus') == 'DONE' && 
category.get('hostsBootStatus') == 'REGISTERING');
-        }
-      }, this);
-      return hosts.get('length');
-    }.property('[email protected]'), // 
'[email protected]'
-    label: function () {
-      return "%@ (%@)".fmt(this.get('value'), this.get('hostsCount'));
-    }.property('value', 'hostsCount')
-  }),
-  getCategory: function(field, value){
-    return this.get('categories').find(function(item){
-      return item.get(field) == value;
-    });
-  },
-
-  categories: function () {
-    var self = this;
-    self.categoryObject.reopen({
-      controller: self,
-      isActive: function(){
-        return this.get('controller.category') == this;
-      }.property('controller.category'),
-      itemClass: function(){
-        return this.get('isActive') ? 'active' : '';
-      }.property('isActive')
-    });
-
-    var categories = [
-      self.categoryObject.create({value: Em.I18n.t('common.all'), hostsCount: 
function () {
-        return this.get('controller.hosts.length');
-      }.property('controller.hosts.length') }),
-      self.categoryObject.create({value: 
Em.I18n.t('installer.step3.hosts.status.installing'), hostsBootStatus: 
'RUNNING'}),
-      self.categoryObject.create({value: 
Em.I18n.t('installer.step3.hosts.status.registering'), hostsBootStatus: 
'REGISTERING'}),
-      self.categoryObject.create({value: Em.I18n.t('common.success'), 
hostsBootStatus: 'REGISTERED' }),
-      self.categoryObject.create({value: Em.I18n.t('common.fail'), 
hostsBootStatus: 'FAILED', last: true })
-    ];
-
-    this.set('category', categories.get('firstObject'));
-
-    return categories;
-  }.property(),
-
-  category: false,
-
-  allChecked: false,
-
-  onAllChecked: function () {
-    var hosts = this.get('visibleHosts');
-    hosts.setEach('isChecked', this.get('allChecked'));
-  }.observes('allChecked'),
-
-  noHostsSelected: function () {
-    return !(this.hosts.someProperty('isChecked', true));
-  }.property('[email protected]'),
-
   isRetryDisabled: true,
   isLoaded: false,
 
@@ -160,20 +98,8 @@ App.WizardStep3Controller = Em.Controller.extend({
 
       hosts.pushObject(hostInfo);
     }
-
-    if(hosts.length > 200) {
-      lazyloading.run({
-        destination: this.get('hosts'),
-        source: hosts,
-        context: this,
-        initSize: 100,
-        chunkSize: 150,
-        delay: 50
-      });
-    } else {
-      this.set('hosts', hosts);
-      this.set('isLoaded', true);
-    }
+    this.set('hosts', hosts);
+    this.set('isLoaded', true);
   },
 
   /**
@@ -195,22 +121,6 @@ App.WizardStep3Controller = Em.Controller.extend({
     return this.get('bootHosts').length != 0 && 
this.get('bootHosts').someProperty('bootStatus', 'RUNNING');
   },
 
-  filterByCategory: function () {
-    var category = this.get('category.hostsBootStatus');
-    if (category) {
-      this.get('hosts').forEach(function (host) {
-        host.set('isVisible', (category === host.get('bootStatus')));
-      });
-    } else { // if (this.get('category') === 'All Hosts')
-      this.get('hosts').setEach('isVisible', true);
-    }
-  }.observes('category', '[email protected]'),
-
-  /* Returns the current set of visible hosts on view (All, Succeeded, Failed) 
*/
-  visibleHosts: function () {
-    return this.get('hosts').filterProperty('isVisible');
-  }.property('[email protected]'),
-
   removeHosts: function (hosts) {
     var self = this;
     App.showConfirmationPopup(function() {
@@ -229,7 +139,7 @@ App.WizardStep3Controller = Em.Controller.extend({
 
   removeSelectedHosts: function () {
     if (!this.get('noHostsSelected')) {
-      var selectedHosts = this.get('visibleHosts').filterProperty('isChecked', 
true);
+      var selectedHosts = this.get('hosts').filterProperty('isChecked', true);
       selectedHosts.forEach(function (_hostInfo) {
         console.log('Removing:  ' + _hostInfo.name);
       });
@@ -237,6 +147,24 @@ App.WizardStep3Controller = Em.Controller.extend({
     }
   },
 
+  /**
+   * show popup with the list of hosts which are selected
+   */
+  selectedHostsPopup: function () {
+    var selectedHosts = 
this.get('hosts').filterProperty('isChecked').mapProperty('name');
+    App.ModalPopup.show({
+      header: Em.I18n.t('installer.step3.selectedHosts.popup.header'),
+      onPrimary: function () {
+        this.hide();
+      },
+      secondary: null,
+      bodyClass: Ember.View.extend({
+        items: selectedHosts,
+        templateName: require('templates/common/items_list_popup')
+      })
+    });
+  },
+
   retryHost: function (hostInfo) {
     this.retryHosts([hostInfo]);
   },
@@ -287,7 +215,7 @@ App.WizardStep3Controller = Em.Controller.extend({
   setRegistrationInProgress: function () {
     var bootHosts = this.get('bootHosts');
     //if hosts aren't loaded yet then registration should be in progress
-    var result = (bootHosts.length === 0);
+    var result = (bootHosts.length === 0 && !this.get('isLoaded'));
     for (var i = 0, l = bootHosts.length; i < l; i++) {
       if (bootHosts[i].get('bootStatus') !== 'REGISTERED' && 
bootHosts[i].get('bootStatus') !== 'FAILED') {
         result = true;
@@ -679,20 +607,18 @@ App.WizardStep3Controller = Em.Controller.extend({
   },
 
   submit: function () {
-    if (!this.get('isSubmitDisabled')) {
-        if(this.get('isHostHaveWarnings')) {
-            var self = this;
-            App.showConfirmationPopup(
-                function(){
-                    self.set('content.hosts', self.get('bootHosts'));
-                    App.router.send('next');
-                },
-                
Em.I18n.t('installer.step3.hostWarningsPopup.hostHasWarnings'));
-        }
-        else {
-              this.set('content.hosts', this.get('bootHosts'));
-              App.router.send('next');
-        }
+    if (this.get('isHostHaveWarnings')) {
+      var self = this;
+      App.showConfirmationPopup(
+        function () {
+          self.set('content.hosts', self.get('bootHosts'));
+          App.router.send('next');
+        },
+        Em.I18n.t('installer.step3.hostWarningsPopup.hostHasWarnings'));
+    }
+    else {
+      this.set('content.hosts', this.get('bootHosts'));
+      App.router.send('next');
     }
   },
 
@@ -1369,13 +1295,6 @@ App.WizardStep3Controller = Em.Controller.extend({
         registeredHosts: self.get('registeredHosts')
       })
     })
-  },
-
-  back: function () {
-    if (this.get('isRegistrationInProgress')) {
-      return;
-    }
-    App.router.send('back');
   }
 
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/74ef9620/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index c4c1440..fbe3880 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -531,6 +531,8 @@ Em.I18n.translations = {
   'installer.step3.hosts.bootLog.registering':'\nRegistering with the 
server...',
   'installer.step3.hostLogPopup.highlight':'click to highlight',
   'installer.step3.hostLogPopup.copy':'press CTRL+C',
+  'installer.step3.hostsTable.selectAll':'Select All Hosts',
+  'installer.step3.selectedHosts.popup.header':'Selected Hosts',
 
   'installer.step4.header':'Choose Services',
   'installer.step4.body':'Choose which services you want to install on your 
cluster.',

http://git-wip-us.apache.org/repos/asf/ambari/blob/74ef9620/ambari-web/app/styles/application.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/application.less 
b/ambari-web/app/styles/application.less
index 6087d93..ac93e5d 100644
--- a/ambari-web/app/styles/application.less
+++ b/ambari-web/app/styles/application.less
@@ -414,6 +414,19 @@ h1 {
     }
   }
   #confirm-hosts {
+    table {
+      margin-bottom: 0;
+      td {
+        input[type="checkbox"] {
+          margin-left: 15px;
+        }
+      }
+      th {
+        input[type="checkbox"] {
+          margin: 0;
+        }
+      }
+    }
     #host-filter {
       margin-top: 3px;
       ul {
@@ -437,6 +450,12 @@ h1 {
         }
       }
     }
+    .page-bar {
+      padding: 5px;
+      .selected-hosts-info {
+        margin: 0;
+      }
+    }
     .progress {
       margin-bottom: 0;
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/74ef9620/ambari-web/app/templates/common/items_list_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/items_list_popup.hbs 
b/ambari-web/app/templates/common/items_list_popup.hbs
new file mode 100644
index 0000000..6544a1b
--- /dev/null
+++ b/ambari-web/app/templates/common/items_list_popup.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 item in view.items}}
+    <p>{{item}}</p>
+{{/each}}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/74ef9620/ambari-web/app/templates/wizard/step2_host_name_pattern_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step2_host_name_pattern_popup.hbs 
b/ambari-web/app/templates/wizard/step2_host_name_pattern_popup.hbs
deleted file mode 100644
index 66e7f48..0000000
--- a/ambari-web/app/templates/wizard/step2_host_name_pattern_popup.hbs
+++ /dev/null
@@ -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.
-}}
-
-{{#each host in view.hostNames}}
-  <p>{{host}}</p>
-{{/each}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/74ef9620/ambari-web/app/templates/wizard/step3.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step3.hbs 
b/ambari-web/app/templates/wizard/step3.hbs
index 2b6fb18..5b8bdad 100644
--- a/ambari-web/app/templates/wizard/step3.hbs
+++ b/ambari-web/app/templates/wizard/step3.hbs
@@ -23,11 +23,11 @@
   <div class="box">
     <div class="box-header">
       <div class="button-section">
-        <a class="btn btn-primary" {{bindAttr disabled="noHostsSelected"}}
-           href="#" {{action removeSelectedHosts target="controller" }}><i
+        <button class="btn btn-primary" {{bindAttr 
disabled="view.noHostsSelected"}}
+           {{action removeSelectedHosts target="controller" }}><i
                 class="icon-trash icon-white"></i>
           {{t installer.step3.removeSelected}}
-        </a>
+        </button>
         {{#unless isRetryDisabled}}
         <a class="btn btn-primary decommission"
            href="#" {{action retrySelectedHosts target="controller"}}><i
@@ -39,9 +39,9 @@
         <div id="host-filter" class="pull-right">
           <ul class="clearfix">
             <li class="first">{{t common.show}}:</li>
-            {{#each category in controller.categories}}
+            {{#each category in view.categories}}
               <li {{bindAttr class="category.itemClass"}}>
-                <a {{action selectCategory category target="controller"}} 
href="#">
+                <a {{action selectCategory category target="view"}} href="#">
                   {{category.label}}
                 </a>
               </li>
@@ -54,64 +54,96 @@
       </div>
     </div>
 
-    <div class="pre-scrollable" style="max-height: 440px;">
-      <table class="table table-bordered table-striped">
-        <thead>
-        <tr>
-          <th class="span1">{{view Ember.Checkbox 
checkedBinding="allChecked"}}</th>
-          <th class="span3">{{t common.host}}</th>
-          <!-- retrieved from local storage initially -->
-          <th class="span3">{{t common.progress}}</th>
-          <th class="span2">{{t common.status}}</th>
-          <!-- given by the parsing function that parses data from bootstrap 
call, dynamically assign the color -->
-          <th class="span3">{{t common.action}}</th>
-          <!-- trash icon -->
-          <!-- retry icon -->
-        </tr>
-        </thead>
-
-        <tbody>
-        {{#each host in hosts}}
-        {{#view App.WizardHostView categoryBinding="controller.category" 
hostInfoBinding="host"}}
-        <td>
-          {{view Ember.Checkbox checkedBinding="host.isChecked"}}
-        </td>
-        <td>
-          {{host.name}}
-        </td>
-        <td>
-          <div {{bindAttr class="host.bootBarColor 
host.isBootDone::progress-striped host.isBootDone::active :progress"}}>
-            <div class="bar" style="width:100%">
-            </div>
+      <div class="pre-scrollable" style="max-height: 440px;">
+          <table class="table table-bordered table-striped">
+              <thead>
+              <tr>
+                  <th class="span1">
+                      <div class="btn-group">
+                          <a class="btn">
+                            {{view Ember.Checkbox 
checkedBinding="view.pageChecked"}}
+                          </a>
+                          <button class="btn dropdown-toggle" 
data-toggle="dropdown">
+                              <span class="caret"></span>
+                          </button>
+                          <ul class="dropdown-menu">
+                              <li>
+                                  <a {{action selectAll target="view"}}>{{t 
installer.step3.hostsTable.selectAll}}</a>
+                              </li>
+                          </ul>
+                      </div>
+                  </th>
+                  <th class="span3">{{t common.host}}</th>
+                  <!-- retrieved from local storage initially -->
+                  <th class="span3">{{t common.progress}}</th>
+                  <th class="span2">{{t common.status}}</th>
+                  <!-- given by the parsing function that parses data from 
bootstrap call, dynamically assign the color -->
+                  <th class="span3">{{t common.action}}</th>
+                  <!-- trash icon -->
+                  <!-- retry icon -->
+              </tr>
+              </thead>
+              <tbody>
+              {{#if view.pageContent}}
+                {{#each host in view.pageContent}}
+                  {{#view App.WizardHostView 
categoryBinding="controller.category" hostInfoBinding="host"}}
+                      <td>
+                        {{view Ember.Checkbox checkedBinding="host.isChecked"}}
+                      </td>
+                      <td>
+                        {{host.name}}
+                      </td>
+                      <td>
+                          <div {{bindAttr class="host.bootBarColor 
host.isBootDone::progress-striped host.isBootDone::active :progress"}}>
+                              <div class="bar" style="width:100%">
+                              </div>
+                          </div>
+                      </td>
+                      <td>
+                          <a href="javascript:void(null)"
+                             data-toggle="modal" {{action hostLogPopup host 
target="controller"}}><span  {{bindAttr 
class="host.bootStatusColor"}}>{{host.bootStatusForDisplay}}</span></a>
+                      </td>
+                      <td>
+                        {{#if view.isRemovable}}<a class="btn btn-mini" 
{{action remove target="view"}}><i
+                                class="icon-trash"></i>
+                          {{t common.remove}}</a>{{/if}}
+                        {{#if view.isRetryable}}<a class="btn btn-mini" 
{{action retry target="view"}}><i
+                                class="icon-repeat"></i>
+                          {{t common.retry}}</a>{{/if}}
+                      </td>
+                  {{/view}}
+                {{/each}}
+              {{else}}
+                  <tr>
+                      <td colspan="5">
+                        {{t hosts.table.noHosts}}
+                      </td>
+                  </tr>
+              {{/if}}
+              </tbody>
+          </table>
+      </div>
+      <div id="hosts">
+          <div class="page-bar">
+              <div class="selected-hosts-info span6">
+                {{#if view.selectedHostsCount}}
+                  <a {{action selectedHostsPopup target="controller"}} 
href="#">
+                    {{view.selectedHostsCount}}
+                    {{pluralize view.selectedHostsCount 
singular="t:hosts.filters.selectedHostInfo" 
plural="t:hosts.filters.selectedHostsInfo"}}
+                  </a>
+                  -
+                  <a {{action unSelectAll target="view"}} href="#">{{t 
hosts.filters.clearSelection}}</a>
+              {{/if}}
+              </div>
+              <div class="info">{{view.paginationInfo}}</div>
+              <div class="paging_two_button">
+                {{view view.paginationFirst}}
+                {{view view.paginationLeft}}
+                {{view view.paginationRight}}
+                {{view view.paginationLast}}
+              </div>
           </div>
-        </td>
-        <td>
-          <a href="javascript:void(null)"
-             data-toggle="modal" {{action hostLogPopup host 
target="controller"}}><span  {{bindAttr 
class="host.bootStatusColor"}}>{{host.bootStatusForDisplay}}</span></a>
-        </td>
-        <td>
-          {{#if view.isRemovable}}<a class="btn btn-mini" {{action remove 
target="view"}}><i class="icon-trash"></i>
-          {{t common.remove}}</a>{{/if}}
-          {{#if view.isRetryable}}<a class="btn btn-mini" {{action retry 
target="view"}}><i class="icon-repeat"></i>
-          {{t common.retry}}</a>{{/if}}
-        </td>
-        {{/view}}
-        {{/each}}
-        {{#unless visibleHosts.length}}
-        <tr>
-            <td colspan="5"><p>{{t installer.step3.hosts.noHosts}}</p></td>
-        </tr>
-        {{/unless}}
-
-        </tbody>
-
-      </table>
-    </div>
-    <div class="box-footer">
-      <hr/>
-      <div class="footer-pagination">
       </div>
-    </div>
   </div>
     {{#if hasMoreRegisteredHosts}}
         <div {{bindAttr class=":alert alert-warn"}}>
@@ -126,7 +158,7 @@
         {{/unless}}
       </div>
   <div class="btn-area">
-    <a class="btn pull-left" {{bindAttr disabled="isRegistrationInProgress"}} 
{{action back target="controller"}}>&larr; {{t common.back}}</a>
-    <a class="btn btn-success pull-right" {{bindAttr 
disabled="isSubmitDisabled"}} {{action submit target="controller"}}>{{t 
common.next}} &rarr;</a>
+    <button class="btn pull-left" {{bindAttr 
disabled="isRegistrationInProgress"}} {{action back}}>&larr; {{t 
common.back}}</button>
+    <button class="btn btn-success pull-right" {{bindAttr 
disabled="isSubmitDisabled"}} {{action submit target="controller"}}>{{t 
common.next}} &rarr;</button>
   </div>
 </div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/74ef9620/ambari-web/app/views/common/table_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/table_view.js 
b/ambari-web/app/views/common/table_view.js
index ffb73f7..ef65cca 100644
--- a/ambari-web/app/views/common/table_view.js
+++ b/ambari-web/app/views/common/table_view.js
@@ -47,6 +47,21 @@ App.TableView = Em.View.extend(App.UserPref, {
   displayLengthOnLoad: null,
 
   /**
+   * The number of rows to show on every page
+   * The value should be a number converted into string type in order to 
support select element API
+   * Example: "10", "25"
+   * @type {String}
+   */
+  displayLength: null,
+
+  /**
+   * default value of display length
+   * The value should be a number converted into string type in order to 
support select element API
+   * Example: "10", "25"
+   */
+  defaultDisplayLength: "10",
+
+  /**
    * Do filtering, using saved in the local storage filter conditions
    */
   willInsertElement:function () {
@@ -54,12 +69,14 @@ App.TableView = Em.View.extend(App.UserPref, {
     var name = this.get('controller.name');
 
     this.set('startIndexOnLoad', App.db.getStartIndex(name));
-    if (App.db.getDisplayLength(name)) {
-      this.set('displayLength', App.db.getDisplayLength(name));
-    } else {
-      self.dataLoading().done(function (initValue) {
-        self.set('displayLength', initValue);
-      });
+    if (!this.get('displayLength')) {
+      if (App.db.getDisplayLength(name)) {
+        this.set('displayLength', App.db.getDisplayLength(name));
+      } else {
+        self.dataLoading().done(function (initValue) {
+          self.set('displayLength', initValue);
+        });
+      }
     }
 
     var filterConditions = App.db.getFilterConditions(name);
@@ -130,7 +147,7 @@ App.TableView = Em.View.extend(App.UserPref, {
   getUserPrefErrorCallback: function () {
     // this user is first time login
     console.log('Persist did NOT find the key');
-    var displayLengthDefault = "10";
+    var displayLengthDefault = this.get('defaultDisplayLength');
     this.set('displayLengthOnLoad', displayLengthDefault);
     if (App.get('isAdmin')) {
       this.postUserPref(this.displayLengthKey(), displayLengthDefault);
@@ -173,7 +190,9 @@ App.TableView = Em.View.extend(App.UserPref, {
     }.property("parentView.startIndex", 'filteredContent.length'),
 
     click: function () {
-      this.get('parentView').previousPage();
+      if (this.get('class') === "paginate_previous") {
+        this.get('parentView').previousPage();
+      }
     }
   }),
 
@@ -189,7 +208,45 @@ App.TableView = Em.View.extend(App.UserPref, {
     }.property("parentView.endIndex", 'filteredContent.length'),
 
     click: function () {
-      this.get('parentView').nextPage();
+      if (this.get('class') === "paginate_next") {
+        this.get('parentView').nextPage();
+      }
+    }
+  }),
+
+  paginationFirst: Ember.View.extend({
+    tagName: 'a',
+    template: Ember.Handlebars.compile('<i class="icon-step-backward"></i>'),
+    classNameBindings: ['class'],
+    class: function () {
+      if ((this.get("parentView.endIndex")) > 
parseInt(this.get("parentView.displayLength"))) {
+        return "paginate_previous";
+      }
+      return "paginate_disabled_previous";
+    }.property("parentView.endIndex", 'filteredContent.length'),
+
+    click: function () {
+      if (this.get('class') === "paginate_previous") {
+        this.get('parentView').firstPage();
+      }
+    }
+  }),
+
+  paginationLast: Ember.View.extend({
+    tagName: 'a',
+    template: Ember.Handlebars.compile('<i class="icon-step-forward"></i>'),
+    classNameBindings: ['class'],
+    class: function () {
+      if (this.get("parentView.endIndex") !== 
this.get("parentView.filteredContent.length")) {
+        return "paginate_next";
+      }
+      return "paginate_disabled_next";
+    }.property("parentView.endIndex", 'filteredContent.length'),
+
+    click: function () {
+      if (this.get('class') === "paginate_next") {
+        this.get('parentView').lastPage();
+      }
     }
   }),
 
@@ -233,12 +290,22 @@ App.TableView = Em.View.extend(App.UserPref, {
       this.set('startIndex', result);
     }
   },
-
   /**
-   * The number of rows to show on every page
-   * @type {Number}
+   * Onclick handler for first page button on the page
    */
-  displayLength: null,
+  firstPage: function () {
+    this.set('startIndex', 1);
+  },
+  /**
+   * Onclick handler for last page button on the page
+   */
+  lastPage: function () {
+    var pagesCount = this.get('filteredContent.length') / 
parseInt(this.get('displayLength'));
+    var startIndex = (this.get('filteredContent.length') % 
parseInt(this.get('displayLength')) === 0) ?
+      (pagesCount - 1) * parseInt(this.get('displayLength')) :
+      Math.floor(pagesCount) * parseInt(this.get('displayLength'));
+    this.set('startIndex', ++startIndex);
+  },
 
   /**
    * Calculates default value for startIndex property after applying filter or 
changing displayLength

http://git-wip-us.apache.org/repos/asf/ambari/blob/74ef9620/ambari-web/app/views/wizard/step3_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/step3_view.js 
b/ambari-web/app/views/wizard/step3_view.js
index 3211e6d..f953e68 100644
--- a/ambari-web/app/views/wizard/step3_view.js
+++ b/ambari-web/app/views/wizard/step3_view.js
@@ -19,25 +19,148 @@
 
 var App = require('app');
 
-App.WizardStep3View = Em.View.extend({
+App.WizardStep3View = App.TableView.extend({
 
   templateName: require('templates/wizard/step3'),
-  category: '',
 
-  didInsertElement: function () {
-    this.get('controller').loadStep();
-  },
+  content:function () {
+    return this.get('controller.hosts');
+  }.property('controller.hosts.length'),
 
   message:'',
   linkText: '',
   status: '',
 
+  selectedCategory: function() {
+    return this.get('categories').findProperty('isActive');
+  }.property('[email protected]'),
+
   registeredHostsMessage: '',
 
+  displayLength: "20",
+
+  didInsertElement: function () {
+    this.get('controller').loadStep();
+  },
+
+  pageChecked: false,
+
+  /**
+   * select checkboxes of hosts on page
+   */
+  onPageChecked: function () {
+    if (this.get('selectionInProgress')) return;
+    this.get('pageContent').setEach('isChecked', this.get('pageChecked'));
+  }.observes('pageChecked'),
+
+  /**
+   * select checkboxes of all hosts
+   */
+  selectAll: function () {
+    this.get('content').setEach('isChecked', true);
+  },
+
+  /**
+   * reset checkbox of all hosts
+   */
+  unSelectAll: function() {
+    this.get('content').setEach('isChecked', false);
+  },
+
+  watchSelectionOnce: function(){
+    Em.run.once(this, 'watchSelection');
+  }.observes('[email protected]'),
+
+  /**
+   * watch selection and calculate flags as:
+   * - noHostsSelected
+   * - selectedHostsCount
+   * - pageChecked
+   */
+  watchSelection: function() {
+    this.set('selectionInProgress', true);
+    this.set('pageChecked', !!this.get('pageContent.length') && 
this.get('pageContent').everyProperty('isChecked', true));
+    this.set('selectionInProgress', false);
+    var noHostsSelected = true;
+    var selectedHostsCount = 0;
+    this.get('content').forEach(function(host){
+      selectedHostsCount += ~~host.get('isChecked');
+      noHostsSelected = (noHostsSelected) ? !host.get('isChecked') : 
noHostsSelected;
+    });
+    this.set('noHostsSelected', noHostsSelected);
+    this.set('selectedHostsCount', selectedHostsCount);
+  },
+
   setRegisteredHosts: function(){
     
this.set('registeredHostsMessage',Em.I18n.t('installer.step3.warning.registeredHosts').format(this.get('controller.registeredHosts').length));
   }.observes('controller.registeredHosts'),
 
+  categoryObject: Em.Object.extend({
+    hostsCount: 0,
+    label: function () {
+      return "%@ (%@)".fmt(this.get('value'), this.get('hostsCount'));
+    }.property('value', 'hostsCount'),
+    isActive: false,
+    itemClass: function () {
+      return this.get('isActive') ? 'active' : '';
+    }.property('isActive')
+  }),
+
+  categories: function () {
+    return [
+      this.categoryObject.create({value: Em.I18n.t('common.all'), 
hostsBootStatus: 'ALL', isActive: true}),
+      this.categoryObject.create({value: 
Em.I18n.t('installer.step3.hosts.status.installing'), hostsBootStatus: 
'RUNNING'}),
+      this.categoryObject.create({value: 
Em.I18n.t('installer.step3.hosts.status.registering'), hostsBootStatus: 
'REGISTERING'}),
+      this.categoryObject.create({value: Em.I18n.t('common.success'), 
hostsBootStatus: 'REGISTERED' }),
+      this.categoryObject.create({value: Em.I18n.t('common.fail'), 
hostsBootStatus: 'FAILED', last: true })
+    ];
+  }.property(),
+
+  countCategoryHosts: function () {
+    var counters = {
+      "RUNNING": 0,
+      "REGISTERING": 0,
+      "REGISTERED": 0,
+      "FAILED": 0
+    };
+    this.get('content').forEach(function (host) {
+      if (counters[host.get('bootStatus')] !== undefined) {
+        counters[host.get('bootStatus')]++;
+      }
+    }, this);
+    counters["ALL"] = this.get('content.length');
+    this.get('categories').forEach(function(category) {
+      category.set('hostsCount', counters[category.get('hostsBootStatus')]);
+    }, this);
+  }.observes('[email protected]'),
+
+
+  /**
+   * filter hosts by category
+   */
+  filter: function () {
+    var result = [];
+    var selectedCategory = this.get('selectedCategory');
+    if (!selectedCategory || selectedCategory.get('hostsBootStatus') === 
'ALL') {
+      result = this.get('content');
+    } else {
+      result = this.get('content').filterProperty('bootStatus', 
this.get('selectedCategory.hostsBootStatus'));
+    }
+    this.set('filteredContent', result);
+  }.observes('[email protected]', 'selectedCategory'),
+  /**
+   * Trigger on Category click
+   * @param {Object} event
+   */
+  selectCategory: function (event) {
+    var categoryStatus = event.context.get('hostsBootStatus');
+    var self = this;
+    this.get('categories').forEach(function (category) {
+      category.set('isActive', (category.get('hostsBootStatus') === 
categoryStatus));
+    });
+    this.watchSelection();
+  },
+
   monitorStatuses: function() {
     var hosts = this.get('controller.bootHosts');
     var failedHosts = hosts.filterProperty('bootStatus', 'FAILED').length;

Reply via email to