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> {{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": "*",