You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ab...@apache.org on 2014/05/16 17:59:05 UTC

[5/7] AMBARI-5791 Create skeleton Slider Apps table. (ababiichuk)

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/app/assets/font/fontawesome-webfont.ttf
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/assets/font/fontawesome-webfont.ttf b/contrib/views/slider/src/main/resources/ui/app/assets/font/fontawesome-webfont.ttf
new file mode 100644
index 0000000..d365924
Binary files /dev/null and b/contrib/views/slider/src/main/resources/ui/app/assets/font/fontawesome-webfont.ttf differ

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/app/assets/font/fontawesome-webfont.woff
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/assets/font/fontawesome-webfont.woff b/contrib/views/slider/src/main/resources/ui/app/assets/font/fontawesome-webfont.woff
new file mode 100644
index 0000000..b9bd17e
Binary files /dev/null and b/contrib/views/slider/src/main/resources/ui/app/assets/font/fontawesome-webfont.woff differ

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/app/config/router.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/config/router.js b/contrib/views/slider/src/main/resources/ui/app/config/router.js
index 52485b9..cf5068f 100755
--- a/contrib/views/slider/src/main/resources/ui/app/config/router.js
+++ b/contrib/views/slider/src/main/resources/ui/app/config/router.js
@@ -19,5 +19,5 @@
 'use strict';
 
 module.exports = App.Router.map(function() {
-    // this.resource('about');
+  this.resource("slider_apps", { path: "/slider" });
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/app/controllers/slider.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/controllers/slider.js b/contrib/views/slider/src/main/resources/ui/app/controllers/slider.js
new file mode 100644
index 0000000..b9f3698
--- /dev/null
+++ b/contrib/views/slider/src/main/resources/ui/app/controllers/slider.js
@@ -0,0 +1,20 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+App.SliderAppsController = Ember.ArrayController.extend({
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/app/helpers/helper.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/helpers/helper.js b/contrib/views/slider/src/main/resources/ui/app/helpers/helper.js
new file mode 100644
index 0000000..75323c2
--- /dev/null
+++ b/contrib/views/slider/src/main/resources/ui/app/helpers/helper.js
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+String.prototype.format = function () {
+  var args = arguments;
+  return this.replace(/{(\d+)}/g, function (match, number) {
+    return typeof args[number] != 'undefined' ? args[number] : match;
+  });
+};
+
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/app/initialize.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/initialize.js b/contrib/views/slider/src/main/resources/ui/app/initialize.js
index 5971d2c..63edecf 100755
--- a/contrib/views/slider/src/main/resources/ui/app/initialize.js
+++ b/contrib/views/slider/src/main/resources/ui/app/initialize.js
@@ -21,6 +21,7 @@
 window.App = require('config/app');
 require('config/router');
 require('config/store');
+require('translations');
 
 // Load all modules in order automagically. Ember likes things to work this
 // way so everything is in the App.* namespace.

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/app/models/slider_app.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/models/slider_app.js b/contrib/views/slider/src/main/resources/ui/app/models/slider_app.js
index e514be6..18679df 100644
--- a/contrib/views/slider/src/main/resources/ui/app/models/slider_app.js
+++ b/contrib/views/slider/src/main/resources/ui/app/models/slider_app.js
@@ -85,7 +85,7 @@ App.SliderApp.FIXTURES = [
     index: 'indx1',
     yarnId: 'y1',
     name: 'name1',
-    status: 'good',
+    status: 'Running',
     user: 'u1',
     started: 1400132912,
     ended: 1400152912,
@@ -100,7 +100,7 @@ App.SliderApp.FIXTURES = [
     index: 'indx2',
     yarnId: 'y2',
     name: 'name2',
-    status: 'good',
+    status: 'Running',
     user: 'u2',
     started: 1400132912,
     ended: 1400152912,
@@ -115,7 +115,7 @@ App.SliderApp.FIXTURES = [
     index: 'indx3',
     yarnId: 'y3',
     name: 'name3',
-    status: 'good',
+    status: 'Running',
     user: 'u3',
     started: 1400132912,
     ended: 1400152912,
@@ -130,7 +130,7 @@ App.SliderApp.FIXTURES = [
     index: 'indx4',
     yarnId: 'y4',
     name: 'name4',
-    status: 'good',
+    status: 'Running',
     user: 'u4',
     started: 1400132912,
     ended: 1400152912,
@@ -145,7 +145,7 @@ App.SliderApp.FIXTURES = [
     index: 'indx5',
     yarnId: 'y5',
     name: 'name5',
-    status: 'good',
+    status: 'Running',
     user: 'u5',
     started: 1400132912,
     ended: 1400152912,
@@ -155,4 +155,10 @@ App.SliderApp.FIXTURES = [
     quickLinks: [3, 4],
     runtimeProperties: [1, 2, 3]
   }
-];
\ No newline at end of file
+];
+
+App.SliderApp.Status = {
+  running: "Running",
+  frozen: "Frozen",
+  destroyed: "Destroyed"
+};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/app/routes/IndexRoute.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/routes/IndexRoute.js b/contrib/views/slider/src/main/resources/ui/app/routes/IndexRoute.js
deleted file mode 100755
index a24ec1a..0000000
--- a/contrib/views/slider/src/main/resources/ui/app/routes/IndexRoute.js
+++ /dev/null
@@ -1,25 +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.
- */
-
-'use strict';
-
-module.exports = App.IndexRoute = Ember.Route.extend({
-  model: function() {
-    return ['red', 'yellow', 'blue'];
-  }
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/app/routes/main.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/routes/main.js b/contrib/views/slider/src/main/resources/ui/app/routes/main.js
new file mode 100644
index 0000000..22807b8
--- /dev/null
+++ b/contrib/views/slider/src/main/resources/ui/app/routes/main.js
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+App.IndexRoute = Ember.Route.extend({
+  redirect: function() {
+    this.transitionTo('slider_apps');
+  }
+});
+
+App.SliderAppsRoute = Ember.Route.extend({
+  setupController: function(controller) {
+    controller.set('model', App.SliderApp.FIXTURES);
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/app/styles/application.less
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/styles/application.less b/contrib/views/slider/src/main/resources/ui/app/styles/application.less
new file mode 100644
index 0000000..e5f3335
--- /dev/null
+++ b/contrib/views/slider/src/main/resources/ui/app/styles/application.less
@@ -0,0 +1,286 @@
+#slider-apps-table  {
+  .create-app {
+    margin-top:27px;
+  }
+#slider-table {
+    margin-top: 10px;
+    margin-bottom: 10px;
+    font-size: 13px\9;
+    table-layout: fixed;
+
+    th {
+      width: 17.5%!important;
+    }
+
+    select {
+      height: 23px;
+      margin-bottom: 10px;
+      font-size: 75%;
+    }
+
+    .label-row {
+      font-size: 0.9em;
+      th {
+        padding: 4px;
+      }
+      .active-sort {
+        color: #555555;
+        text-decoration: none;
+        background-color: #e5e5e5;
+        -webkit-box-shadow: inset 0 5px 8px rgba(0, 0, 0, 0.100);
+        -moz-box-shadow: inset 0 5px 8px rgba(0, 0, 0, 0.100);
+        box-shadow: inset 0 5px 8px rgba(0, 0, 0, 0.100);
+      }
+    }
+
+    #filter-row {
+      th {
+        padding: 0px;
+        padding-left: 4px;
+      }
+      .active-filter {
+        color: #555555;
+        text-decoration: none;
+        background-color: #e5e5e5;
+        -webkit-box-shadow: inset 0 -5px 8px rgba(0, 0, 0, 0.05);
+        -moz-box-shadow: inset 0 -5px 8px rgba(0, 0, 0, 0.05);
+        box-shadow: inset 0 -5px 8px rgba(0, 0, 0, 0.05);
+      }
+      input {
+        font-size: 12px;
+        height: 14px;
+      }
+      .filter-btn {
+        color: #999999;
+        font-size: 12px;
+        line-height: 14px;
+        padding-left: 6px;
+        text-align: left;
+        width: 100px;
+        .icon-filter {
+          color: #999999;
+        }
+      }
+    }
+    thead {
+      background: none repeat scroll 0 0 #F8F8F8;
+    }
+  }
+  .page-bar {
+    border: 1px solid #E4E4E4;
+    color: #7B7B7B;
+    text-align: right;
+    font-size: 12px;
+    label {
+      font-size: 12px;
+    }
+    div {
+      display: inline-block;
+      margin:0 10px;
+    }
+    .filtered-hosts-info, .selected-hosts-info {
+      text-align: left;
+      margin-top: 8px;
+      margin-left: 17px;
+    }
+    .items-on-page {
+      label {
+        display:inline;
+      }
+      select {
+        margin-bottom: 4px;
+        margin-top: 4px;
+        width:70px;
+        font-size: 12px;
+        height: 27px;
+      }
+    }
+    .paging_two_button {
+      a.paginate_disabled_next, a.paginate_disabled_previous {
+        color: gray;
+        &:hover {
+          color: gray;
+          text-decoration: none;
+          cursor: default;
+        }
+      }
+
+      a.paginate_next, a.paginate_previous {
+        &:hover {
+          text-decoration: none;
+          cursor: pointer;
+        }
+      }
+      a {
+        padding:0 5px;
+      }
+    }
+  }
+  .box-header {
+    margin-left: 0;
+    .btn-group {
+      float: left;
+    }
+    .btn.decommission {
+      margin-left: 5px;
+    }
+    .btn.add-host-button {
+      margin-bottom: 10px;
+      margin-top: -5px;
+    }
+    .hosts-actions {
+      margin-right: 10px;
+    }
+    .health-status-bar {
+      font-size: 0.9em;
+      margin-left: 0;
+      color: #b4b4b4;
+      a {
+        text-decoration: none;
+      }
+      .category-item:hover {
+        cursor: pointer;
+        a {
+          color: #ffffff;
+        }
+      }
+      .active {
+        a {
+          color: #ffffff;
+        }
+        background-color: #888888;
+        border-color: #888888;
+        -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.25);
+        -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.25);
+        box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.25);
+      }
+      .category-item.active:hover {
+        a {
+          color: #ffffff;
+        }
+      }
+    }
+  }
+
+  .filter-input-width{
+    width:68%;
+  }
+  .table {
+    table-layout: fixed;
+    th {
+      border-top: none;
+    }
+    th, td {
+      padding-left: 4px;
+      padding-right: 4px;
+      border-left-width: 0;
+    }
+    input[type="checkbox"] {
+      margin: -2px 0 0 0;
+    }
+
+    .sorting_asc {
+      background: url(data:image/png;base64,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) no-repeat right 50
 %;
+    }
+    .sorting_desc {
+      background: url(data:image/png;base64,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) no-repeat right 50%;
+    }
+    .sorting {
+      background: url(data:image/png;base64,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) no-repeat right 50%;
+    }
+    div.view-wrapper {
+      input[type="checkbox"], .btn-group {
+        margin-bottom: 9px;
+      }
+    }
+
+    a.ui-icon-circle-close {
+      float: right;
+      opacity: 0.2;
+      padding: 1px 0;
+      position: relative;
+      right: 0px;
+      margin-top: 3px;
+      z-index: 10;
+      &:hover {
+        opacity: 0.7;
+      }
+    }
+    .notActive {
+      a.ui-icon-circle-close {
+        visibility: hidden;
+      }
+    }
+  }
+
+  .open-group > .dropdown-menu {
+    display: block;
+  }
+  .nav-pills li.disabled {
+    display: block;
+    margin: 2px 0;
+    padding: 8px 12px;
+    line-height: 14px;
+  }
+  .box-footer .footer-pagination {
+    float: right;
+    .nav {
+      margin-bottom: 0;
+    }
+    .dropdown {
+      margin-top: 3px;
+    }
+    .dropdown {
+      margin-top: 3px;
+    }
+    .dropdown select {
+      width: 60px;
+    }
+    .page-listing a {
+      line-height: 0;
+      border: none;
+      margin: 0;
+      margin-right: 10px;
+      cursor: pointer;
+      color: #0088CC;
+      padding: 8px 0;
+      float: left;
+      text-decoration: underline;
+    }
+    .page-listing a:hover {
+      text-decoration: none;
+    }
+    .page-listing {
+      width: 100px;
+      .table {
+        th.name {
+          width: 300px;
+          a.filter-label {
+            width: 57px;
+            display: block;
+            float: left;
+          }
+        }
+      }
+    }
+  }
+  .host-components-expander {
+    .icon-caret-right, .icon-caret-down {
+      vertical-align: middle;
+      margin-right: 5px;
+      margin-bottom: 2px;
+      text-decoration: none;
+    }
+  }
+  .host-components {
+    display: none;
+    padding-left: 13px;
+  }
+  .sort-wrapper .column-name {
+    cursor: pointer;
+    padding-right: 18px;
+  }
+  .table-bordered  {
+    border-left:1px solid #dddddd;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/app/templates/slider_apps.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/templates/slider_apps.hbs b/contrib/views/slider/src/main/resources/ui/app/templates/slider_apps.hbs
new file mode 100644
index 0000000..a56b468
--- /dev/null
+++ b/contrib/views/slider/src/main/resources/ui/app/templates/slider_apps.hbs
@@ -0,0 +1,92 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<div id="slider-apps-table">
+  <div class="box-header row">
+    <div class="pull-left">
+      <h1>{{t slider.apps.title}}</h1>
+    </div>
+    <div class="pull-right create-app">
+      <a href="#" class="btn btn-inverse">
+        <i class="icon-plus"></i><span>&nbsp;{{t slider.apps.create}}</span>
+      </a>
+    </div>
+  </div>
+  <table class="datatable table table-bordered table-striped" id="slider-table">
+    <thead>
+    {{#view view.sortView classNames="label-row" contentBinding="view.filteredContent"}}
+      {{view view.parentView.nameSort}}
+      {{view view.parentView.statusSort}}
+      {{view view.parentView.typeSort}}
+      {{view view.parentView.userSort}}
+      {{view view.parentView.startSort}}
+      {{view view.parentView.endSort}}
+    {{/view}}
+    <tr id="filter-row">
+      <th>{{view view.nameFilterView}}</th>
+      <th>{{view view.statusFilterView}}</th>
+      <th>{{view view.typeFilterView}}</th>
+      <th>{{view view.userFilterView}}</th>
+      <th>{{view view.startFilterView}}</th>
+      <th></th>
+    </tr>
+    </thead>
+    <tbody>
+    {{#if view.pageContent}}
+      {{#each slider in view.pageContent}}
+        {{#view view.SliderView contentBinding="slider"}}
+
+          <td><a href="#">{{slider.name}}</a></td>
+
+          <td>{{slider.status}}</td>
+
+          <td>{{slider.appType}}</td>
+
+          <td>{{slider.user}}</td>
+
+          <td>{{slider.started}}</td>
+
+          <td>{{slider.ended}}</td>
+
+        {{/view}}
+      {{/each}}
+    {{else}}
+      <tr>
+        <td class="first"></td>
+        <td colspan="11">
+          {{t tableView.filters.noItems}}
+        </td>
+      </tr>
+    {{/if}}
+    </tbody>
+  </table>
+  <div class="page-bar">
+    <div class="filtered-hosts-info span4">
+      <label>{{view.filteredContentInfo}} - <a {{action clearFilters target="view"}}
+          href="#">{{t tableView.filters.clearAllFilters}}</a></label>
+    </div>
+    <div class="items-on-page">
+      <label>{{t common.show}} {{view view.rowsPerPageSelectView selectionBinding="view.displayLength"}}</label>
+    </div>
+    <div class="info">{{view.paginationInfo}}</div>
+    <div class="paging_two_button">
+      {{view view.paginationLeft}}
+      {{view view.paginationRight}}
+    </div>
+  </div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/app/translations.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/translations.js b/contrib/views/slider/src/main/resources/ui/app/translations.js
new file mode 100644
index 0000000..032a8d1
--- /dev/null
+++ b/contrib/views/slider/src/main/resources/ui/app/translations.js
@@ -0,0 +1,37 @@
+/**
+ * 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.
+ */
+
+
+Em.I18n.translations = {
+
+  'common' : {
+    'show': "Show"
+  },
+  'tableView.filters.all': 'All',
+  'tableView.filters.filtered': 'Filtered',
+  'tableView.filters.clearFilters': 'Clear filters',
+  'tableView.filters.paginationInfo': '{0} - {1} of {2}',
+  'tableView.filters.clearAllFilters': 'clear filters',
+  'tableView.filters.showAll': 'Show All',
+  'tableView.filters.clearSelection': 'clear selection All',
+  'tableView.filters.noItems' : 'There are no items to show',
+
+  'slider.apps.title': 'Slider Apps',
+  'slider.apps.create': 'Create App',
+  'sliderApps.filters.info': '{0} of {1} sliders showing'
+};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/app/views/common/filter_view.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/views/common/filter_view.js b/contrib/views/slider/src/main/resources/ui/app/views/common/filter_view.js
new file mode 100644
index 0000000..315a349
--- /dev/null
+++ b/contrib/views/slider/src/main/resources/ui/app/views/common/filter_view.js
@@ -0,0 +1,340 @@
+/**
+ * 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.
+ */
+
+/**
+ * Wrapper View for all filter components. Layout template and common actions are located inside of it.
+ * Logic specific for data component(input, select, or custom multi select, which fire any changes on interface) are
+ * located in inner view - <code>filterView</code>.
+ *
+ * If we want to have input filter, put <code>textFieldView</code> to it.
+ * All inner views implemented below this view.
+ * @type {*}
+ */
+
+var App = require('config/app');
+
+var wrapperView = Ember.View.extend({
+  classNames: ['view-wrapper'],
+  layout: Ember.Handlebars.compile('<a href="#" {{action "clearFilter" target="view"}} class="ui-icon ui-icon-circle-close"></a> {{yield}}'),
+  template: Ember.Handlebars.compile(
+    '{{#if view.fieldId}}<input type="hidden" id="{{unbound view.fieldId}}" value="" />{{/if}}' +
+    '{{view view.filterView}}' +
+    '{{#if view.showApply}}<button {{action "setValueOnApply" target="view"}} class="apply-btn btn"><span>Apply</span></button>{{/if}} '
+  ),
+
+  value: null,
+
+  /**
+   * Column index
+   */
+  column: null,
+
+  /**
+   * If this field is exists we dynamically create hidden input element and set value there.
+   * Used for some cases, where this values will be used outside of component
+   */
+  fieldId: null,
+
+  clearFilter: function(){
+    this.set('value', this.get('emptyValue'));
+    if(this.get('setPropertyOnApply')){
+      this.setValueOnApply();
+    }
+    return false;
+  },
+
+  setValueOnApply: function() {
+    if(this.get('value') == null){
+      this.set('value', '')
+    }
+    this.set(this.get('setPropertyOnApply'), this.get('value'));
+    return false;
+  },
+
+  /**
+   * Use to determine whether filter is clear or not. Also when we want to set empty value
+   */
+  emptyValue: '',
+
+  /**
+   * Whether our <code>value</code> is empty or not
+   * @return {Boolean}
+   */
+  isEmpty: function(){
+    if(this.get('value') === null){
+      return true;
+    }
+    return this.get('value').toString() === this.get('emptyValue').toString();
+  },
+
+  /**
+   * Show/Hide <code>Clear filter</code> button.
+   * Also this method updates computed field related to <code>fieldId</code> if it exists.
+   * Call <code>onChangeValue</code> callback when everything is done.
+   */
+  showClearFilter: function(){
+    if(!this.get('parentNode')){
+      return;
+    }
+    // get the sort view element in the same column to current filter view to highlight them together
+    var relatedSort = $(this.get('element')).parents('thead').find('.sort-view-' + this.get('column'));
+    if(this.isEmpty()){
+      this.get('parentNode').removeClass('active-filter');
+      this.get('parentNode').addClass('notActive');
+      relatedSort.removeClass('active-sort');
+    } else {
+      this.get('parentNode').removeClass('notActive');
+      this.get('parentNode').addClass('active-filter');
+      relatedSort.addClass('active-sort');
+    }
+
+    if(this.get('fieldId')){
+      this.$('> input').eq(0).val(this.get('value'));
+    }
+
+    this.onChangeValue();
+  }.observes('value'),
+
+  /**
+   * Callback for value changes
+   */
+  onChangeValue: function(){
+
+  },
+
+  /**
+   * Filter components is located here. Should be redefined
+   */
+  filterView: Em.View,
+
+  /**
+   * Update class of parentNode(hide clear filter button) on page load
+   */
+  didInsertElement: function(){
+    var parent = this.$().parent();
+    this.set('parentNode', parent);
+    parent.addClass('notActive');
+  }
+});
+
+/**
+ * Simple input control for wrapperView
+ */
+var textFieldView = Ember.TextField.extend({
+  type:'text',
+ // placeholder: Em.I18n.t('any'),
+  valueBinding: "parentView.value"
+});
+
+/**
+ * Simple multiselect control for wrapperView.
+ * Used to render blue button and popup, which opens on button click.
+ * All content related logic should be implemented manually outside of it
+ */
+var componentFieldView = Ember.View.extend({
+  classNames: ['btn-group'],
+  classNameBindings: ['isFilterOpen:open:'],
+
+  /**
+   * Whether popup is shown or not
+   */
+  isFilterOpen: false,
+
+  /**
+   * We have <code>value</code> property similar to inputs <code>value</code> property
+   */
+  valueBinding: 'parentView.value',
+
+  /**
+   * Clear filter to initial state
+   */
+  clearFilter: function(){
+    this.set('value', '');
+  },
+
+  /**
+   * Onclick handler for <code>cancel filter</code> button
+   */
+  closeFilter:function () {
+    $(document).unbind('click');
+    this.set('isFilterOpen', false);
+  },
+
+  /**
+   * Onclick handler for <code>apply filter</code> button
+   */
+  applyFilter:function() {
+    this.closeFilter();
+  },
+
+  /**
+   * Onclick handler for <code>show component filter</code> button.
+   * Also this function is used in some other places
+   */
+  clickFilterButton:function () {
+    var self = this;
+    this.set('isFilterOpen', !this.get('isFilterOpen'));
+    if (this.get('isFilterOpen')) {
+
+      var dropDown = this.$('.filter-components');
+      var firstClick = true;
+      $(document).bind('click', function (e) {
+        if (!firstClick && $(e.target).closest(dropDown).length == 0) {
+          self.set('isFilterOpen', false);
+          $(document).unbind('click');
+        }
+        firstClick = false;
+      });
+    }
+  }
+});
+
+/**
+ * Simple select control for wrapperView
+ */
+var selectFieldView = Ember.Select.extend({
+  selectionBinding: 'parentView.value',
+  contentBinding: 'parentView.content'
+});
+
+/**
+ * Result object, which will be accessible outside
+ * @type {Object}
+ */
+module.exports = {
+  /**
+   * You can access wrapperView outside
+   */
+  wrapperView : wrapperView,
+
+  /**
+   * And also controls views if need it
+   */
+  textFieldView : textFieldView,
+  selectFieldView: selectFieldView,
+  componentFieldView: componentFieldView,
+
+  /**
+   * Quick create input filters
+   * @param config parameters of <code>wrapperView</code>
+   */
+  createTextView : function(config){
+    config.fieldType = config.fieldType || 'input-medium';
+    config.filterView = textFieldView.extend({
+      classNames : [ config.fieldType ]
+    });
+
+    return wrapperView.extend(config);
+  },
+
+  /**
+   * Quick create multiSelect filters
+   * @param config parameters of <code>wrapperView</code>
+   */
+  createComponentView : function(config){
+    config.clearFilter = function(){
+      this.forEachChildView(function(item){
+        if(item.clearFilter){
+          item.clearFilter();
+        }
+      });
+      return false;
+    };
+
+    return wrapperView.extend(config);
+  },
+
+  /**
+   * Quick create select filters
+   * @param config parameters of <code>wrapperView</code>
+   */
+  createSelectView: function(config){
+
+    config.fieldType = config.fieldType || 'input-medium';
+    config.filterView = selectFieldView.extend({
+      classNames : [ config.fieldType ],
+      attributeBindings: ['disabled','multiple'],
+      disabled: false
+    });
+    config.emptyValue = '';//Em.I18n.t('any');
+
+    return wrapperView.extend(config);
+  },
+  /**
+   * returns the filter function, which depends on the type of property
+   * @param type
+   * @param isGlobal check is search global
+   * @return {Function}
+   */
+  getFilterByType: function(type, isGlobal){
+    switch (type){
+      case 'boolean':
+        return function (origin, compareValue){
+          return origin === compareValue;
+        };
+        break;
+      case 'number':
+        return function(rowValue, rangeExp){
+          var compareChar = rangeExp.charAt(0);
+          var compareValue;
+          var match = false;
+          if (rangeExp.length == 1) {
+            if (isNaN(parseInt(compareChar))) {
+              // User types only '=' or '>' or '<', so don't filter column values
+              match = true;
+              return match;
+            }
+            else {
+              compareValue = parseFloat(parseFloat(rangeExp).toFixed(2));
+            }
+          }
+          else {
+            if (isNaN(parseInt(compareChar))) {
+              compareValue = parseFloat(parseFloat(rangeExp.substr(1, rangeExp.length)).toFixed(2));
+            }
+            else {
+              compareValue = parseFloat(parseFloat(rangeExp.substr(0, rangeExp.length)).toFixed(2));
+            }
+          }
+          rowValue = parseFloat((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:
+              if (rangeExp == rowValue) match = true;
+          }
+          return match;
+        };
+        break;
+      case 'string':
+      default:
+        return function(origin, compareValue){
+          var regex = new RegExp(compareValue,"i");
+          return regex.test(origin);
+        }
+    }
+  }
+};

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/app/views/common/sort_view.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/views/common/sort_view.js b/contrib/views/slider/src/main/resources/ui/app/views/common/sort_view.js
new file mode 100644
index 0000000..436ed69
--- /dev/null
+++ b/contrib/views/slider/src/main/resources/ui/app/views/common/sort_view.js
@@ -0,0 +1,206 @@
+/**
+ * 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('config/app');
+
+/**
+ * Wrapper View for all sort components. Layout template and common actions are located inside of it.
+ * Logic specific for sort fields
+ * located in inner view - <code>fieldView</code>.
+ *
+ * @type {*}
+ */
+var wrapperView = Em.View.extend({
+  tagName: 'tr',
+
+  classNames: ['sort-wrapper'],
+
+  willInsertElement:function () {
+    if(this.get('parentView.tableFilteringComplete')){
+      this.get('parentView').set('filteringComplete', true);
+    }
+  },
+
+  /**
+   * Load sort statuses from local storage
+   * Works only after finish filtering in the parent View
+   */
+  loadSortStatuses: function() {
+    var statuses = App.db.getSortingStatuses(this.get('controller.name'));
+    if (!this.get('parentView.filteringComplete')) return;
+    if (statuses) {
+      var childViews = this.get('childViews');
+      var self = this;
+      statuses.forEach(function(st) {
+        if (st.status != 'sorting') {
+          var sortOrder = false;
+          if(st.status == 'sorting_desc') {
+            sortOrder = true;
+          }
+          self.sort(childViews.findProperty('name', st.name), sortOrder);
+          childViews.findProperty('name', st.name).set('status', (sortOrder)?'sorting_desc':'sorting_asc');
+          self.get('controller').set('sortingColumn', childViews.findProperty('name', st.name));
+        }
+        else {
+          childViews.findProperty('name', st.name).set('status', st.status);
+        }
+      });
+    }
+    this.get('parentView').showProperPage();
+  }.observes('parentView.filteringComplete'),
+
+  /**
+   * Save sort statuses to local storage
+   * Works only after finish filtering in the parent View
+   */
+  saveSortStatuses: function() {
+    if (!this.get('parentView.filteringComplete')) return;
+    var statuses = [];
+    this.get('childViews').forEach(function(childView) {
+      statuses.push({
+        name: childView.get('name'),
+        status: childView.get('status')
+      });
+    });
+    App.db.setSortingStatuses(this.get('controller.name'), statuses);
+  }.observes('childViews.@each.status'),
+
+  /**
+   * sort content by property
+   * @param property
+   * @param order true - DESC, false - ASC
+   */
+  sort: function(property, order, returnSorted){
+    returnSorted = returnSorted ? true : false;
+    var content = this.get('content').toArray();
+    var sortFunc = this.getSortFunc(property, order);
+    this.resetSort();
+    content.sort(sortFunc);
+    if(returnSorted){
+      return content;
+    }else{
+      this.set('content', content);
+    }
+  },
+
+  isSorting: false,
+
+  onContentChange: function () {
+    if (!this.get('isSorting') && this.get('content.length')) {
+      this.get('childViews').forEach(function (view) {
+        if (view.status !== 'sorting') {
+          var status = view.get('status');
+          this.set('isSorting', true);
+          this.sort(view, status == 'sorting_desc');
+          this.set('isSorting', false);
+          view.set('status', status);
+        }
+      }, this);
+    }
+  }.observes('content.length'),
+
+  /**
+   * reset all sorts fields
+   */
+  resetSort: function(){
+    this.get('childViews').setEach('status', 'sorting');
+  },
+  /**
+   * determines sort function depending on the type of sort field
+   * @param property
+   * @param order
+   * @return {*}
+   */
+  getSortFunc: function(property, order){
+    var func;
+    switch (property.get('type')){
+      case 'number':
+        func = function (a, b) {
+          var a = parseFloat(Em.get(a, property.get('name')));
+          var b = parseFloat(Em.get(b, property.get('name')));
+          if (order) {
+            return b - a;
+          } else {
+            return a - b;
+          }
+        };
+        break;
+      default:
+        func = function(a,b){
+          if(order){
+            if (Em.get(a, property.get('name')) > Em.get(b, property.get('name')))
+              return -1;
+            if (Em.get(a, property.get('name')) < Em.get(b, property.get('name')))
+              return 1;
+            return 0;
+          } else {
+            if (Em.get(a, property.get('name')) < Em.get(b, property.get('name')))
+              return -1;
+            if (Em.get(a, property.get('name')) > Em.get(b, property.get('name')))
+              return 1;
+            return 0;
+          }
+        }
+    }
+    return func;
+  }
+});
+/**
+ * particular view that contain sort field properties:
+ * name - name of property in content table
+ * type(optional) - specific type to sort
+ * displayName - label to display
+ * @type {*}
+ */
+var fieldView = Em.View.extend({
+  template:Em.Handlebars.compile('<span {{bindAttr class="view.status :column-name"}}>{{view.displayName}}</span>'),
+  classNameBindings: ['viewNameClass'],
+  tagName: 'th',
+  name: null,
+  displayName: null,
+  status: 'sorting',
+  viewNameClass: function () {
+    return 'sort-view-' + this.get('column');
+  }.property(),
+  type: null,
+  column: 0,
+  /**
+   * callback that run sorting and define order of sorting
+   * @param event
+   */
+  click: function(event){
+    if(this.get('status') === 'sorting_desc'){
+      this.get('parentView').sort(this, false);
+      this.set('status', 'sorting_asc');
+    }
+    else {
+      this.get('parentView').sort(this, true);
+      this.set('status', 'sorting_desc');
+    }
+    this.get('controller').set('sortingColumn', this);
+  }
+});
+
+/**
+ * Result object, which will be accessible outside
+ * @type {Object}
+ */
+module.exports = {
+  wrapperView: wrapperView,
+  fieldView: fieldView
+};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/app/views/common/table_view.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/views/common/table_view.js b/contrib/views/slider/src/main/resources/ui/app/views/common/table_view.js
new file mode 100644
index 0000000..1fdf0a7
--- /dev/null
+++ b/contrib/views/slider/src/main/resources/ui/app/views/common/table_view.js
@@ -0,0 +1,409 @@
+/**
+ * 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('config/app');
+var filters = require('views/common/filter_view');
+var sort = require('views/common/sort_view');
+
+App.TableView = Em.View.extend({
+
+  /**
+   * Defines to show pagination or show all records
+   * @type {Boolean}
+   */
+  pagination: true,
+
+  /**
+   * Shows if all data is loaded and filtered
+   * @type {Boolean}
+   */
+  filteringComplete: false,
+
+  /**
+   * intermediary for filteringComplete
+   * @type {Boolean}
+   */
+  tableFilteringComplete: false,
+
+  /**
+   * Loaded from local storage startIndex value
+   * @type {Number}
+   */
+  startIndexOnLoad: null,
+
+  /**
+   * Loaded from server persist value
+   * @type {Number}
+   */
+  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",
+
+  /**
+   * Persist-key of current table displayLength property
+   * @param {String} loginName current user login name
+   * @returns {String}
+   */
+  displayLengthKey: function (loginName) {
+    if (App.get('testMode')) {
+      return 'pagination_displayLength';
+    }
+    loginName = loginName ? loginName : App.router.get('loginName');
+    return this.get('controller.name') + '-pagination-displayLength-' + loginName;
+  },
+
+  /**
+   * Set received from server value to <code>displayLengthOnLoad</code>
+   * @param {Number} response
+   * @param {Object} request
+   * @param {Object} data
+   * @returns {*}
+   */
+  getUserPrefSuccessCallback: function (response, request, data) {
+    console.log('Got DisplayLength value from server with key ' + data.key + '. Value is: ' + response);
+    this.set('displayLengthOnLoad', response);
+    return response;
+  },
+
+  /**
+   * Set default value to <code>displayLengthOnLoad</code> (and send it on server) if value wasn't found on server
+   * @returns {Number}
+   */
+  getUserPrefErrorCallback: function () {
+    // this user is first time login
+    console.log('Persist did NOT find the key');
+    var displayLengthDefault = this.get('defaultDisplayLength');
+    this.set('displayLengthOnLoad', displayLengthDefault);
+    if (App.get('isAdmin')) {
+      this.postUserPref(this.displayLengthKey(), displayLengthDefault);
+    }
+    return displayLengthDefault;
+  },
+
+  /**
+   * Do pagination after filtering and sorting
+   * Don't call this method! It's already used where it's need
+   */
+  showProperPage: function() {
+    var self = this;
+    Em.run.next(function() {
+      Em.run.next(function() {
+        if(self.get('startIndexOnLoad')) {
+          self.set('startIndex', self.get('startIndexOnLoad'));
+        }
+      });
+    });
+  },
+
+  /**
+   * Return pagination information displayed on the page
+   * @type {String}
+   */
+  paginationInfo: function () {
+    return Em.I18n.t('tableView.filters.paginationInfo').format(this.get('startIndex'), this.get('endIndex'), this.get('filteredContent.length'));
+  }.property('displayLength', 'filteredContent.length', 'startIndex', 'endIndex'),
+
+  paginationLeft: Ember.View.extend({
+    tagName: 'a',
+    template: Ember.Handlebars.compile('<i class="icon-arrow-left"></i>'),
+    classNameBindings: ['class'],
+    class: function () {
+      if (this.get("parentView.startIndex") > 1) {
+        return "paginate_previous";
+      }
+      return "paginate_disabled_previous";
+    }.property("parentView.startIndex", 'filteredContent.length'),
+
+    click: function () {
+      if (this.get('class') === "paginate_previous") {
+        this.get('parentView').previousPage();
+      }
+    }
+  }),
+
+  paginationRight: Ember.View.extend({
+    tagName: 'a',
+    template: Ember.Handlebars.compile('<i class="icon-arrow-right"></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').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();
+      }
+    }
+  }),
+
+  /**
+   * Select View with list of "rows-per-page" options
+   * @type {Ember.View}
+   */
+  rowsPerPageSelectView: Em.Select.extend({
+    content: ['10', '25', '50', '100'],
+    change: function () {
+      this.get('parentView').saveDisplayLength();
+    }
+  }),
+
+  saveDisplayLength: function() {
+    var self = this;
+    Em.run.next(function() {
+      if (!App.testMode) {
+        if (App.get('isAdmin')) {
+          self.postUserPref(self.displayLengthKey(), self.get('displayLength'));
+        }
+      }
+    });
+  },
+  /**
+   * Start index for displayed content on the page
+   */
+  startIndex: 1,
+
+  /**
+   * Calculate end index for displayed content on the page
+   */
+  endIndex: function () {
+    if (this.get('pagination')) {
+      return Math.min(this.get('filteredContent.length'), this.get('startIndex') + parseInt(this.get('displayLength')) - 1);
+    } else {
+      return this.get('filteredContent.length')
+    }
+  }.property('startIndex', 'displayLength', 'filteredContent.length'),
+
+  /**
+   * Onclick handler for previous page button on the page
+   */
+  previousPage: function () {
+    var result = this.get('startIndex') - parseInt(this.get('displayLength'));
+    this.set('startIndex', (result < 2) ? 1 : result);
+  },
+
+  /**
+   * Onclick handler for next page button on the page
+   */
+  nextPage: function () {
+    var result = this.get('startIndex') + parseInt(this.get('displayLength'));
+    if (result - 1 < this.get('filteredContent.length')) {
+      this.set('startIndex', result);
+    }
+  },
+  /**
+   * Onclick handler for first page button on the page
+   */
+  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
+   */
+  updatePaging: function (controller, property) {
+    var displayLength = this.get('displayLength');
+    var filteredContentLength = this.get('filteredContent.length');
+    if (property == 'displayLength') {
+      this.set('startIndex', Math.min(1, filteredContentLength));
+    } else if (!filteredContentLength) {
+      this.set('startIndex', 0);
+    } else if (this.get('startIndex') > filteredContentLength) {
+      this.set('startIndex', Math.floor((filteredContentLength - 1) / displayLength) * displayLength + 1);
+    } else if (!this.get('startIndex')) {
+      this.set('startIndex', 1);
+    }
+  }.observes('displayLength', 'filteredContent.length'),
+
+  /**
+   * Apply each filter to each row
+   *
+   * @param {Number} iColumn number of column by which filter
+   * @param {Object} value
+   * @param {String} type
+   */
+  updateFilter: function (iColumn, value, type) {
+    var filterCondition = this.get('filterConditions').findProperty('iColumn', iColumn);
+    if (filterCondition) {
+      filterCondition.value = value;
+    }
+    else {
+      filterCondition = {
+        iColumn: iColumn,
+        value: value,
+        type: type
+      };
+      this.get('filterConditions').push(filterCondition);
+    }
+    this.filtersUsedCalc();
+    this.filter();
+  },
+
+
+  /**
+   * Contain filter conditions for each column
+   * @type {Array}
+   */
+  filterConditions: [],
+
+  /**
+   * Contains content after implementing filters
+   * @type {Array}
+   */
+  filteredContent: [],
+
+  /**
+   * Determine if <code>filteredContent</code> is empty or not
+   * @type {Boolean}
+   */
+  hasFilteredItems: function() {
+    return !!this.get('filteredContent.length');
+  }.property('filteredContent.length'),
+
+  /**
+   * Contains content to show on the current page of data page view
+   * @type {Array}
+   */
+  pageContent: function () {
+    return this.get('filteredContent').slice(this.get('startIndex') - 1, this.get('endIndex'));
+  }.property('filteredContent.length', 'startIndex', 'endIndex'),
+
+  /**
+   * Filter table by filterConditions
+   */
+  filter: function () {
+    var content = this.get('content');
+    var filterConditions = this.get('filterConditions').filterProperty('value');
+    var result;
+    var assoc = this.get('colPropAssoc');
+    if (filterConditions.length) {
+      result = content.filter(function (item) {
+         var match = true;
+        filterConditions.forEach(function (condition) {
+          var filterFunc = filters.getFilterByType(condition.type, false);
+          if (match) {
+            match = filterFunc(Em.get(item, assoc[condition.iColumn]), condition.value);
+          }
+        });
+        return match;
+      });
+      this.set('filteredContent', result);
+    } else {
+      this.set('filteredContent', content.toArray());
+    }
+  }.observes('content.length'),
+
+  /**
+   * Does any filter is used on the page
+   * @type {Boolean}
+   */
+  filtersUsed: false,
+
+  /**
+   * Determine if some filters are used on the page
+   * Set <code>filtersUsed</code> value
+   */
+  filtersUsedCalc: function() {
+    var filterConditions = this.get('filterConditions');
+    if (!filterConditions.length) {
+      this.set('filtersUsed', false);
+      return;
+    }
+    var filtersUsed = false;
+    filterConditions.forEach(function(filterCondition) {
+      if (filterCondition.value.toString() !== '') {
+        filtersUsed = true;
+      }
+    });
+    this.set('filtersUsed', filtersUsed);
+  },
+
+  /**
+   * Run <code>clearFilter</code> in the each child filterView
+   */
+  clearFilters: function() {
+    this.set('filterConditions', []);
+    this.get('_childViews').forEach(function(childView) {
+      if (childView['clearFilter']) {
+        childView.clearFilter();
+      }
+    });
+  }
+
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/app/views/slider.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/views/slider.js b/contrib/views/slider/src/main/resources/ui/app/views/slider.js
new file mode 100644
index 0000000..caf9692
--- /dev/null
+++ b/contrib/views/slider/src/main/resources/ui/app/views/slider.js
@@ -0,0 +1,150 @@
+/**
+ * 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 filters = require('views/common/filter_view');
+var sort = require('views/common/sort_view');
+
+App.SliderAppsView = App.TableView.extend({
+
+  statusList: [
+    "",
+    App.SliderApp.Status.running,
+    App.SliderApp.Status.frozen,
+    App.SliderApp.Status.destroyed,
+  ],
+
+  content: function () {
+    return this.get('controller.model');
+  }.property('controller.model.length'),
+
+  didInsertElement: function () {
+    this.set('filteredContent',this.get('content'));
+  },
+
+  filteredContentInfo: function () {
+    return Em.I18n.t('sliderApps.filters.info').format(this.get('filteredContent.length'), this.get('content.length'));
+  }.property('content.length', 'filteredContent.length'),
+
+  sortView: sort.wrapperView,
+  nameSort: sort.fieldView.extend({
+    column: 0,
+    name:'name',
+    displayName: "Name"
+  }),
+
+  statusSort: sort.fieldView.extend({
+    column: 1,
+    name:'status',
+    displayName: "Status"
+  }),
+
+  typeSort: sort.fieldView.extend({
+    column: 2,
+    name:'appType',
+    displayName: "Type"
+  }),
+
+  userSort: sort.fieldView.extend({
+    column: 3,
+    name:'user',
+    displayName: "User"
+  }),
+
+  startSort: sort.fieldView.extend({
+    column: 4,
+    name:'started',
+    displayName: "Start Time",
+    type: "number"
+  }),
+
+  endSort: sort.fieldView.extend({
+    column: 5,
+    name:'ended',
+    displayName: "End Time",
+    type: "number"
+  }),
+
+  SliderView: Em.View.extend({
+    content:null,
+    tagName: 'tr'
+  }),
+
+  /**
+   * Filter view for name column
+   * Based on <code>filters</code> library
+   */
+  nameFilterView: filters.createTextView({
+    column: 0,
+    fieldType: 'filter-input-width',
+    onChangeValue: function(){
+      this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'string');
+    }
+  }),
+
+  statusFilterView: filters.createSelectView({
+    column: 1,
+    fieldType: 'filter-input-width',
+    content: function() {
+      return this.get('parentView.statusList');
+    }.property('parentView.statusList'),
+    onChangeValue: function(){
+      this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'string');
+    }
+  }),
+
+  typeFilterView: filters.createTextView({
+    column: 2,
+    fieldType: 'filter-input-width',
+    onChangeValue: function(){
+      this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'string');
+    }
+  }),
+
+  userFilterView: filters.createTextView({
+    column: 3,
+    fieldType: 'filter-input-width',
+    onChangeValue: function(){
+      this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'string');
+    }
+  }),
+
+  startFilterView: filters.createSelectView({
+    column: 4,
+    fieldType: 'filter-input-width',
+    content: ['', 'Past 1 hour',  'Past 1 Day', 'Past 2 Days', 'Past 7 Days', 'Past 14 Days', 'Past 30 Days', 'Custom'],
+    onChangeValue: function(){
+      this.get('parentView').updateFilter(this.get('column'), this.get('value'), 'date');
+    },
+    type: 'number'
+  }),
+
+  /**
+   * associations between host property and column index
+   * @type {Array}
+   */
+  colPropAssoc: function(){
+    var associations = [];
+    associations[0] = 'name';
+    associations[1] = 'status';
+    associations[2] = 'appType';
+    associations[3] = 'user';
+    associations[4] = 'started';
+    associations[5] = 'ended';
+    return associations;
+  }.property()
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/config.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/config.js b/contrib/views/slider/src/main/resources/ui/config.js
index d019cf2..d506483 100755
--- a/contrib/views/slider/src/main/resources/ui/config.js
+++ b/contrib/views/slider/src/main/resources/ui/config.js
@@ -32,7 +32,9 @@ exports.config = {
           'vendor/scripts/common/jquery.js',
           'vendor/scripts/common/handlebars.js',
           'vendor/scripts/development/ember.js',
-          'vendor/scripts/development/ember-data.js'
+          'vendor/scripts/production/ember-data.js',
+          'vendor/scripts/common/ember-i18n-1.4.1.js',
+          'vendor/scripts/common/bootstrap.js'
         ]
       }
     },
@@ -41,7 +43,11 @@ exports.config = {
         'stylesheets/app.css': /^(app|vendor)/
       },
       order: {
-        before: ['vendor/styles/normalize.css']
+        before: [
+          'vendor/styles/bootstrap.css',
+          'vendor/styles/font-awesome.css',
+          'vendor/styles/font-awesome-ie7.css'
+        ]
       }
     },
     templates: {
@@ -68,7 +74,9 @@ exports.config = {
               'vendor/scripts/common/jquery.js',
               'vendor/scripts/common/handlebars.js',
               'vendor/scripts/production/ember.js',
-              'vendor/scripts/production/ember-data.js'
+              'vendor/scripts/production/ember-data.js',
+              'vendor/scripts/common/ember-i18n-1.4.1.js',
+              'vendor/scripts/common/bootstrap.js'
             ]
           }
         }

http://git-wip-us.apache.org/repos/asf/ambari/blob/358cb97d/contrib/views/slider/src/main/resources/ui/package.json
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/package.json b/contrib/views/slider/src/main/resources/ui/package.json
index e8efe2e..2d2ea16 100755
--- a/contrib/views/slider/src/main/resources/ui/package.json
+++ b/contrib/views/slider/src/main/resources/ui/package.json
@@ -21,12 +21,13 @@
   },
   "dependencies": {
     "javascript-brunch": "~1.7.0",
+    "less-brunch":">= 1.0 < 1.5",
     "css-brunch": "~1.7.0",
     "stylus-brunch": "~1.7.0",
     "uglify-js-brunch": "~1.7.0",
     "clean-css-brunch": "~1.7.0",
     "auto-reload-brunch": "~1.7.0",
-    "ember-handlebars-brunch": "git://github.com/gcollazo/ember-handlebars-brunch.git#master"
+    "ember-handlebars-brunch": "git://github.com/fuseelements/ember-handlebars-brunch#fix/ember-1.3.0"
   },
   "devDependencies": {
     "karma": "*",