TEZ-4329. Import external tez component em-table
diff --git a/tez-ui/src/main/resources/META-INF/LICENSE.txt b/tez-ui/src/main/resources/META-INF/LICENSE.txt
index 354d745..608dc61 100644
--- a/tez-ui/src/main/resources/META-INF/LICENSE.txt
+++ b/tez-ui/src/main/resources/META-INF/LICENSE.txt
@@ -232,7 +232,6 @@
- more-js v0.8.2 (https://github.com/sreenaths/snippet-ss)
- snippet-ss v1.11.0 (https://github.com/sreenaths/snippet-ss)
- em-tgraph v0.0.4 (https://github.com/sreenaths/em-tgraph)
- - em-table v0.3.12 (https://github.com/sreenaths/em-table)
- ember-cli-app-version v1.0.0 (https://github.com/EmberSherpa/ember-cli-app-version) - Authored by Taras Mankovski <tarasm@gmail.com>
- ember-cli-auto-register v1.1.0 (https://github.com/williamsbdev/ember-cli-auto-register) - Copyright © 2015 Brandon Williams http://williamsbdev.com
- ember-cli-content-security-policy v0.4.0 (https://github.com/rwjblue/ember-cli-content-security-policy)
diff --git a/tez-ui/src/main/webapp/app/components/em-table-cell.js b/tez-ui/src/main/webapp/app/components/em-table-cell.js
new file mode 100644
index 0000000..d4e6a54
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table-cell.js
@@ -0,0 +1,124 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+import layout from '../templates/components/em-table-cell';
+
+export default Ember.Component.extend({
+ layout: layout,
+
+ classNames: ['table-cell'],
+ classNameBindings: ['innerCell', 'isWaiting'],
+
+ innerCell: Ember.computed('index', function () {
+ if(this.get('index')) {
+ return 'inner';
+ }
+ }),
+
+ row: null,
+ columnDefinition: null,
+
+ isWaiting: false,
+
+ _value: null,
+ _observedPath: null,
+ _comment: null,
+ _cellContent: Ember.computed({
+ set: function (key, value, prevValue) {
+ if(value !== prevValue) {
+ this.highlightCell();
+ }
+ return value;
+ }
+ }),
+
+ _addObserver: function (path) {
+ this._removeObserver();
+ this.get('row').addObserver(path, this, this._onValueChange);
+ this.set('_observedPath', path);
+ },
+
+ _removeObserver: function () {
+ var path = this.get('_observedPath');
+ if(path) {
+ this.get('row').removeObserver(path, this, this._onValueChange);
+ this.set('_observedPath', null);
+ }
+ },
+
+ _pathObserver: Ember.on('init', Ember.observer('row', 'columnDefinition.contentPath', 'columnDefinition.observePath', function () {
+ var path = this.get('columnDefinition.contentPath');
+ if(path && this.get('columnDefinition.observePath')) {
+ this._addObserver(path);
+ }
+ })),
+
+ _onValueChange: function (row, path) {
+ this.set('_value', row.get(path));
+ },
+
+ setContent: function (content) {
+ var comment;
+
+ if(content && content.hasOwnProperty("content")) {
+ comment = content.comment;
+ content = content.content;
+ }
+
+ this.setProperties({
+ _comment: comment,
+ _cellContent: content,
+ isWaiting: false
+ });
+ },
+
+ _cellContentObserver: Ember.on('init', Ember.observer('row', 'columnDefinition', '_value', function () {
+ var cellContent = this.get('columnDefinition').getCellContent(this.get('row'), this.get("_value")),
+ that = this;
+
+ if(cellContent && cellContent.then) {
+ cellContent.then(function (content) {
+ that.setContent(content);
+ });
+ this.set('isWaiting', true);
+ }
+ else if(cellContent === undefined && this.get('columnDefinition.observePath')) {
+ this.set('isWaiting', true);
+ }
+ else {
+ this.setContent(cellContent);
+ }
+ })),
+
+ highlightCell: function () {
+ var element = this.$();
+ if(element) {
+ element.removeClass("bg-transition");
+ element.addClass("highlight");
+ Ember.run.later(function () {
+ element.addClass("bg-transition");
+ element.removeClass("highlight");
+ }, 100);
+ }
+ },
+
+ willDestroy: function () {
+ this._removeObserver();
+ }
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-column.js b/tez-ui/src/main/webapp/app/components/em-table-column.js
new file mode 100644
index 0000000..a3cb5a9
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table-column.js
@@ -0,0 +1,109 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+import layout from '../templates/components/em-table-column';
+
+export default Ember.Component.extend({
+ layout: layout,
+
+ definition: null,
+ rows: null,
+ index: 0,
+
+ tableDefinition: null,
+ dataProcessor: null,
+ adjustedWidth: null,
+ defaultWidth: "",
+
+ classNames: ['table-column'],
+ classNameBindings: ['inner', 'extraClassNames'],
+
+ inner: Ember.computed('index', function () {
+ return !!this.get('index');
+ }),
+
+ extraClassNames: Ember.computed("definition.classNames", function () {
+ var classNames = this.get("definition.classNames");
+ if(classNames) {
+ return classNames.join(" ");
+ }
+ }),
+
+ didInsertElement: function () {
+ Ember.run.scheduleOnce('afterRender', this, function() {
+ this.setWidth();
+ this.setMinWidth();
+ });
+ },
+
+ setMinWidth: Ember.observer("definition.minWidth", function () {
+ this.$().css("minWidth", this.get('definition.minWidth'));
+ }),
+
+ setWidth: Ember.observer("adjustedWidth", "defaultWidth", function () {
+ var thisElement = this.$();
+ thisElement.css("width", this.get('adjustedWidth') || this.get('defaultWidth'));
+ Ember.run.scheduleOnce('afterRender', this, function() {
+ this.get('parentView').send('columnWidthChanged', thisElement.width(), this.get("definition"), this.get("index"));
+ });
+ }),
+
+ _onColResize: function (event) {
+ var data = event.data,
+ width;
+
+ if(!data.startEvent) {
+ data.startEvent = event;
+ }
+
+ width = data.startWidth + event.clientX - data.startEvent.clientX;
+ data.thisObj.set('adjustedWidth', width);
+ },
+
+ _endColResize: function (event) {
+ var thisObj = event.data.thisObj;
+ Ember.$(document).off('mousemove', thisObj._onColResize);
+ Ember.$(document).off('mouseup', thisObj._endColResize);
+ },
+
+ actions: {
+ sort: function () {
+ var definition = this.get('definition'),
+ beforeSort = definition.get('beforeSort');
+
+ if(!beforeSort || beforeSort.call(definition, definition)) {
+ let columnId = this.get('definition.id'),
+ sortOrder = this.get('tableDefinition.sortOrder') === 'desc' ? 'asc' : 'desc';
+
+ this.get('parentView').send('sort', columnId, sortOrder);
+ }
+ },
+ startColResize: function () {
+ var mouseTracker = {
+ thisObj: this,
+ startWidth: this.$().width(),
+ startEvent: null
+ };
+
+ Ember.$(document).on('mousemove', mouseTracker, this._onColResize);
+ Ember.$(document).on('mouseup', mouseTracker, this._endColResize);
+ }
+ }
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-facet-panel-values.js b/tez-ui/src/main/webapp/app/components/em-table-facet-panel-values.js
new file mode 100644
index 0000000..ec88181
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table-facet-panel-values.js
@@ -0,0 +1,199 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+import layout from '../templates/components/em-table-facet-panel-values';
+
+const LIST_LIMIT = 7;
+
+export default Ember.Component.extend({
+ layout: layout,
+
+ data: null,
+ checkedCount: null,
+
+ tableDefinition: null,
+ dataProcessor: null,
+
+ tmpFacetConditions: null,
+
+ hideValues: true,
+
+ currentPage: 1,
+
+ classNames: ['em-table-facet-panel-values'],
+ classNameBindings: ['hideValues', 'hideFilter', 'hideSelectAll'],
+
+ filterText: null,
+ allButtonTitle: Ember.computed("filterText", function () {
+ let filterText = this.get("filterText");
+ return filterText ? `Select all with substring '${filterText}'` : "Select all";
+ }),
+ isVisible: Ember.computed("data.facets.length", "tableDefinition.minValuesToDisplay", function () {
+ return this.get("data.facets.length") >= this.get("tableDefinition.minValuesToDisplay");
+ }),
+ hideFilter: Ember.computed("allFacets.length", function () {
+ return this.get("allFacets.length") < LIST_LIMIT;
+ }),
+ hideSelectAll: Ember.computed("fieldFacetConditions", "checkedCount", "data.facets", function () {
+ return this.get("fieldFacetConditions.in.length") === this.get("data.facets.length");
+ }),
+
+ fieldFacetConditions: Ember.computed("tmpFacetConditions", "data.column.id", function () {
+ var columnID = this.get("data.column.id"),
+ conditions = this.get(`tmpFacetConditions.${columnID}`),
+ facets = this.get("data.facets") || [];
+
+ if(!conditions) {
+ conditions = {
+ in: facets.map(facet => facet.value)
+ };
+ this.set(`tmpFacetConditions.${columnID}`, conditions);
+ }
+
+ return conditions;
+ }),
+
+ allFacets: Ember.computed("data.facets", "fieldFacetConditions", function () {
+ var facets = this.get("data.facets") || [],
+
+ checkedValues = this.get("fieldFacetConditions.in"),
+ selectionHash = {};
+
+ if(checkedValues) {
+ checkedValues.forEach(function (valueText) {
+ selectionHash[valueText] = 1;
+ });
+ }
+
+ return Ember.A(facets.map(function (facet) {
+ facet = Ember.Object.create(facet);
+ facet.set("checked", selectionHash[facet.value]);
+
+ if(!facet.get("displayText")) {
+ facet.set("displayText", facet.get("value"));
+ }
+
+ return facet;
+ }));
+ }),
+
+ filteredFacets: Ember.computed("allFacets", "filterText", function () {
+ var allFacets = this.get("allFacets"),
+ filterText = this.get("filterText"),
+ filteredFacets;
+
+ if(filterText) {
+ filteredFacets = allFacets.filter(function (facet) {
+ return facet.get("value").match(filterText);
+ });
+ }
+ else {
+ filteredFacets = allFacets;
+ }
+
+ return filteredFacets;
+ }),
+
+ _filterObserver: Ember.observer("filterText", function () {
+ this.set("currentPage", 1);
+ }),
+
+ totalPages: Ember.computed("filteredFacets.length", "tableDefinition.facetValuesPageSize", function () {
+ return Math.ceil(this.get("filteredFacets.length") / this.get("tableDefinition.facetValuesPageSize"));
+ }),
+ showPagination: Ember.computed("totalPages", function () {
+ return this.get("totalPages") > 1;
+ }),
+ showPrevious: Ember.computed("currentPage", function () {
+ return this.get("currentPage") > 1;
+ }),
+ showNext: Ember.computed("currentPage", "totalPages", function () {
+ return this.get("currentPage") < this.get("totalPages");
+ }),
+
+ paginatedFacets: Ember.computed("filteredFacets", "currentPage", "tableDefinition.facetValuesPageSize", function () {
+ let currentPage = this.get("currentPage"),
+ pageSize = this.get("tableDefinition.facetValuesPageSize");
+ return this.get("filteredFacets").slice(
+ (currentPage - 1) * pageSize,
+ currentPage * pageSize);
+ }),
+
+ actions: {
+ changePage: function (factor) {
+ var newPage = this.get("currentPage") + factor;
+ if(newPage > 0 && newPage <= this.get("totalPages")) {
+ this.set("currentPage", newPage);
+ }
+ },
+ toggleValueDisplay: function () {
+ this.toggleProperty("hideValues");
+ this.get("parentView").sendAction("toggleValuesDisplayAction", !this.get("hideValues"), this.get("data"));
+ },
+ clickedCheckbox: function (facet) {
+ var checkedValues = this.get("fieldFacetConditions.in"),
+ value = facet.get("value"),
+ valueIndex = checkedValues.indexOf(value);
+
+ facet.toggleProperty("checked");
+
+ if(facet.get("checked")) {
+ if(valueIndex === -1) {
+ checkedValues.push(value);
+ }
+ }
+ else if(valueIndex !== -1) {
+ checkedValues.splice(valueIndex, 1);
+ }
+
+ this.set("checkedCount", checkedValues.length);
+ },
+
+ selectAll: function () {
+ var filteredFacets = this.get("filteredFacets"),
+ checkedValues = this.get("fieldFacetConditions.in");
+
+ filteredFacets.forEach(function (facet) {
+ if(!facet.get("checked")) {
+ checkedValues.push(facet.get("value"));
+ }
+
+ facet.set("checked", true);
+ });
+
+ this.set("fieldFacetConditions.in", checkedValues);
+ this.set("checkedCount", checkedValues.length);
+ },
+ clickedOnly: function (facet) {
+ var allFacets = this.get("allFacets"),
+ checkedValues = [];
+
+ allFacets.forEach(function (facet) {
+ facet.set("checked", false);
+ });
+
+ facet.set("checked", true);
+ checkedValues.push(facet.get("value"));
+
+ this.set("fieldFacetConditions.in", checkedValues);
+ this.set("checkedCount", checkedValues.length);
+ }
+ }
+
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-facet-panel.js b/tez-ui/src/main/webapp/app/components/em-table-facet-panel.js
new file mode 100644
index 0000000..fdbd8f5
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table-facet-panel.js
@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+import layout from '../templates/components/em-table-facet-panel';
+
+export default Ember.Component.extend({
+ layout: layout,
+
+ classNames: ["em-table-facet-panel"],
+ classNameBindings: ['isEmpty', 'hideFilter'],
+
+ isVisible: Ember.computed.alias('tableDefinition.enableFaceting'),
+
+ tableDefinition: null,
+ dataProcessor: null,
+ tmpFacetConditions: {},
+
+ filterText: null,
+ isEmpty: Ember.computed("dataProcessor.facetedFields.length", function () {
+ return this.get("dataProcessor.facetedFields.length") === 0;
+ }),
+ hideFilter: Ember.computed("dataProcessor.facetedFields.length", "tableDefinition.minFieldsForFilter", function () {
+ return this.get("dataProcessor.facetedFields.length") < this.get("tableDefinition.minFieldsForFilter");
+ }),
+
+ didInsertElement: Ember.observer("filterText", "dataProcessor.facetedFields", function () {
+ var fields = this.get("dataProcessor.facetedFields"),
+ filterText = this.get("filterText"),
+ filterRegex = new RegExp(filterText, "i"),
+ elements = Ember.$(this.get("element")).find(".field-list>li");
+
+ elements.each(function (index, element) {
+ var foundMatch = !filterText || Ember.get(fields, `${index}.column.headerTitle`).match(filterRegex);
+ Ember.$(element)[foundMatch ? "show" : "hide"]();
+ });
+ }),
+
+ _facetConditionsObserver: Ember.observer("tableDefinition.facetConditions", "dataProcessor.processedRows.[]", function () {
+ var facetConditions = Ember.$.extend({}, this.get("tableDefinition.facetConditions"));
+ this.set("tmpFacetConditions", facetConditions);
+ }),
+
+ actions: {
+ applyFilters: function () {
+ var tmpFacetConditions = this.get("tmpFacetConditions"),
+ facetedFields = this.get("dataProcessor.facetedFields"),
+ normalizedTmpFacetConditions = {};
+
+ facetedFields.forEach(function (field) {
+ var column = field.column,
+ columnId = column.get("id"),
+ facetType = column.get("facetType"),
+ normalizedConditions;
+
+ if(facetType) {
+ normalizedConditions = facetType.normaliseConditions(tmpFacetConditions[columnId], field.facets);
+ if(normalizedConditions) {
+ normalizedTmpFacetConditions[columnId] = normalizedConditions;
+ }
+ }
+ });
+
+ this.set("tableDefinition.facetConditions", normalizedTmpFacetConditions);
+ },
+ clearFilters: function () {
+ this.set("tmpFacetConditions", {});
+ this.set("tableDefinition.facetConditions", {});
+ },
+ }
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-header-cell.js b/tez-ui/src/main/webapp/app/components/em-table-header-cell.js
new file mode 100644
index 0000000..c0a8e12
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table-header-cell.js
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+import layout from '../templates/components/em-table-header-cell';
+
+export default Ember.Component.extend({
+ layout: layout,
+
+ title: null, // Header cell Name
+ attributeBindings: ['title'],
+
+ definition: null,
+ tableDefinition: null,
+ dataProcessor: null,
+
+ classNames: ['table-header-cell'],
+ classNameBindings: ['isSorting'],
+
+ isSorting: Ember.computed("dataProcessor.isSorting", function () {
+ return this.get("dataProcessor.isSorting") && this.get('tableDefinition.sortColumnId') === this.get('definition.id');
+ }),
+
+ sortIconCSS: Ember.computed('tableDefinition.sortOrder', 'tableDefinition.sortColumnId', function () {
+ if(this.get('tableDefinition.sortColumnId') === this.get('definition.id')) {
+ return this.get('tableDefinition.sortOrder');
+ }
+ }),
+
+ sortToggledTitle: Ember.computed('tableDefinition.sortOrder', 'tableDefinition.sortColumnId', function () {
+ if(this.get('tableDefinition.sortColumnId') === this.get('definition.id')) {
+ switch(this.get('tableDefinition.sortOrder')) {
+ case "asc":
+ return "descending";
+ case "desc":
+ return "ascending";
+ }
+ }
+ }),
+
+ actions: {
+ sort: function () {
+ this.get('parentView').send('sort');
+ },
+ startColResize: function () {
+ this.get('parentView').send('startColResize');
+ }
+ }
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-linked-cell.js b/tez-ui/src/main/webapp/app/components/em-table-linked-cell.js
new file mode 100644
index 0000000..c42c566
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table-linked-cell.js
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+import layout from '../templates/components/em-table-linked-cell';
+
+export default Ember.Component.extend({
+ layout: layout,
+
+ definition: null,
+ content: null,
+
+ normalizedLinks: Ember.computed("content", function () {
+ var content = this.get("content"),
+ links;
+
+ if(content) {
+ if(!Array.isArray(content)) {
+ content = [content];
+ }
+
+ links = content.map(function (link) {
+ var model,
+ text = Ember.get(link, "text") || Ember.get(link, "displayText");
+
+ if(text) {
+ link = Ember.Object.create(link, {
+ text: text
+ });
+
+ if(link.get("model") === undefined) {
+ link.set("model", link.get("id"));
+ }
+
+ model = link.get("model");
+ link.set("withModel", model !== undefined);
+
+ return link;
+ }
+ });
+
+ links = links.filter(function (link) {
+ return link;
+ });
+ }
+
+ return links;
+ })
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-pagination-ui.js b/tez-ui/src/main/webapp/app/components/em-table-pagination-ui.js
new file mode 100644
index 0000000..858928b
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table-pagination-ui.js
@@ -0,0 +1,98 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+import layout from '../templates/components/em-table-pagination-ui';
+
+export default Ember.Component.extend({
+ layout: layout,
+
+ tableDefinition: null,
+ dataProcessor: null,
+
+ classNames: ['pagination-ui'],
+ isVisible: Ember.computed.alias('tableDefinition.enablePagination'),
+
+ showFirst: Ember.computed('_possiblePages', function () {
+ return this.get("dataProcessor.totalPages") && this.get('_possiblePages.0.pageNum') !== 1;
+ }),
+
+ showLast: Ember.computed('_possiblePages', 'dataProcessor.totalPages', function () {
+ var possiblePages = this.get("_possiblePages");
+ if(possiblePages.length) {
+ return possiblePages[possiblePages.length - 1].pageNum !== this.get("dataProcessor.totalPages");
+ }
+ }),
+
+ rowCountOptions: Ember.computed('tableDefinition.rowCountOptions', 'tableDefinition.rowCount', function () {
+ var options = this.get('tableDefinition.rowCountOptions'),
+ rowCount = this.get('tableDefinition.rowCount');
+
+ return options.map(function (option) {
+ return {
+ value: option,
+ selected: option === rowCount
+ };
+ });
+ }),
+
+ _possiblePages: Ember.computed('tableDefinition.pageNum', 'dataProcessor.totalPages', function () {
+ var pageNum = this.get('tableDefinition.pageNum'),
+ totalPages = this.get('dataProcessor.totalPages'),
+ possiblePages = [],
+ startPage = 1,
+ endPage = totalPages,
+ delta = 0;
+
+ if(totalPages > 5) {
+ startPage = pageNum - 2;
+ endPage = pageNum + 2;
+
+ if(startPage < 1) {
+ delta = 1 - startPage;
+ }
+ else if(endPage > totalPages) {
+ delta = totalPages - endPage;
+ }
+
+ startPage += delta;
+ endPage += delta;
+ }
+
+ while(startPage <= endPage) {
+ possiblePages.push({
+ isCurrent: startPage === pageNum,
+ pageNum: startPage++
+ });
+ }
+
+ return possiblePages;
+ }),
+
+ actions: {
+ rowSelected: function (value) {
+ value = parseInt(value);
+ if(this.get('tableDefinition.rowCount') !== value) {
+ this.get('parentView').send('rowChanged', value);
+ }
+ },
+ changePage: function (value) {
+ this.get('parentView').send('pageChanged', value);
+ }
+ }
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-progress-cell.js b/tez-ui/src/main/webapp/app/components/em-table-progress-cell.js
new file mode 100644
index 0000000..32f75c4
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table-progress-cell.js
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+import layout from '../templates/components/em-table-progress-cell';
+
+export default Ember.Component.extend({
+ layout: layout,
+
+ content: null,
+
+ message: Ember.computed("content", function () {
+ var content = this.get("content");
+
+ if(content === undefined || content === null) {
+ return "Not Available!";
+ }
+ else if(isNaN(parseFloat(content))){
+ return "Invalid Data!";
+ }
+ }),
+
+ _definition: Ember.computed("definition", function () {
+ return Ember.Object.extend({
+ valueMin: 0,
+ valueMax: 1,
+ striped: true,
+ style: null
+ }).create(this.get("definition"));
+ })
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-search-ui.js b/tez-ui/src/main/webapp/app/components/em-table-search-ui.js
new file mode 100644
index 0000000..58c4f75
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table-search-ui.js
@@ -0,0 +1,95 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+import layout from '../templates/components/em-table-search-ui';
+
+export default Ember.Component.extend({
+ layout: layout,
+
+ tableDefinition: null,
+ dataProcessor: null,
+
+ classNames: ['search-ui'],
+ classNameBindings: ['hasError'],
+ isVisible: Ember.computed.alias('tableDefinition.enableSearch'),
+
+ searchTypes: ["Regex", "SQL"],
+ actualSearchType: null,
+
+ text: Ember.computed.oneWay('tableDefinition.searchText'),
+
+ _actualSearchTypeDecider: Ember.observer("tableDefinition.searchType", "text", function () {
+ var searchType = this.get("tableDefinition.searchType"),
+ actualSearchType = this.get("actualSearchType");
+
+ switch(searchType) {
+ case "SQL":
+ case "Regex":
+ actualSearchType = searchType;
+ break;
+
+ case "manual":
+ if(!actualSearchType) {
+ actualSearchType = "Regex";
+ }
+ // Will be set from the template
+ break;
+
+ case "auto":
+ var text = this.get("text"),
+ columns = this.get('tableDefinition.columns');
+
+ if(text) {
+ actualSearchType = this.get("dataProcessor.sql").validateClause(text, columns) ? "SQL" : "Regex";
+ }
+ else {
+ actualSearchType = null;
+ }
+ break;
+ }
+
+ this.set("actualSearchType", actualSearchType);
+ }),
+
+ hasError: Ember.computed("text", "actualSearchType", "tableDefinition.searchType", function () {
+ var text = this.get("text"),
+ columns = this.get('tableDefinition.columns'),
+ actualSearchType = this.get("actualSearchType");
+
+ if(text) {
+ switch(actualSearchType) {
+ case "SQL":
+ return !this.get("dataProcessor.sql").validateClause(text, columns);
+ case "Regex":
+ try {
+ new RegExp(text);
+ }
+ catch(e) {
+ return true;
+ }
+ }
+ }
+ }),
+
+ actions: {
+ search: function () {
+ this.get('parentView').send('search', this.get('text'), this.get("actualSearchType"));
+ }
+ }
+});
diff --git a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js b/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
index 7751719..7f1fee8 100644
--- a/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
+++ b/tez-ui/src/main/webapp/app/components/em-table-status-cell.js
@@ -17,8 +17,10 @@
*/
import Ember from 'ember';
+import layout from '../templates/components/em-table-status-cell';
export default Ember.Component.extend({
+ layout: layout,
content: null,
diff --git a/tez-ui/src/main/webapp/app/components/em-table.js b/tez-ui/src/main/webapp/app/components/em-table.js
new file mode 100644
index 0000000..79aae3d
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/components/em-table.js
@@ -0,0 +1,281 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+import Definition from '../utils/table-definition';
+import ColumnDefinition from '../utils/column-definition';
+import DataProcessor from '../utils/data-processor';
+
+import layout from '../templates/components/em-table';
+
+const DEFAULT_ROW_HIGHLIGHT_COLOR = "#EEE";
+
+function createAssigner(targetPath, targetKey, sourcePath) {
+ return Ember.on("init", Ember.observer(targetPath, sourcePath, function () {
+ var target = this.get(targetPath),
+ source = this.get(sourcePath);
+ if(target && source !== undefined) {
+ target.set(targetKey, source);
+ }
+ }));
+}
+
+const HANDLERS = {
+ // Mouse handlers
+ mouseOver: function(event) {
+ var index = Ember.$(this).index() + 1;
+ event.data.highlightRow(index);
+ },
+ mouseLeave: function(event) {
+ event.data.highlightRow(-1);
+ },
+
+ // Scroll handler
+ onScroll: function(event) {
+ var tableBody = event.currentTarget,
+ scrollValues = event.data.get("scrollValues");
+
+ scrollValues.set("left", tableBody.scrollLeft);
+ scrollValues.set("width", tableBody.scrollWidth);
+ }
+};
+
+export default Ember.Component.extend({
+ layout: layout,
+
+ classNames: ["em-table"],
+ classNameBindings: ["showScrollShadow", "showLeftScrollShadow", "showRightScrollShadow"],
+
+ definition: null,
+ dataProcessor: null,
+
+ highlightRowOnMouse: false, // Could be true or {color: "#XYZ"}
+
+ headerComponentNames: ['em-table-search-ui', 'em-table-pagination-ui'],
+ footerComponentNames: ['em-table-pagination-ui'],
+
+ leftPanelComponentName: "em-table-facet-panel",
+ rightPanelComponentName: "",
+
+ columnWidthChangeAction: null,
+
+ scrollChangeAction: null,
+ scrollValues: null,
+ _widthTrackerTimer: null,
+
+ init: function() {
+ this._super();
+ this.set("scrollValues", Ember.Object.create({
+ left: 0,
+ width: 0,
+ viewPortWidth: 0
+ }));
+ },
+
+ showScrollShadow: false,
+ showLeftScrollShadow: false,
+ showRightScrollShadow: false,
+
+ assignDefinitionInProcessor: createAssigner('_dataProcessor', 'tableDefinition', '_definition'),
+ assignRowsInProcessor: createAssigner('_dataProcessor', 'rows', 'rows'),
+ assignColumnsInDefinition: createAssigner('_definition', 'columns', 'columns'),
+
+ assignEnableSortInDefinition: createAssigner('_definition', 'enableSort', 'enableSort'),
+ assignEnableSearchInDefinition: createAssigner('_definition', 'enableSearch', 'enableSearch'),
+ assignEnablePaginationInDefinition: createAssigner('_definition', 'enablePagination', 'enablePagination'),
+ assignRowCountInDefinition: createAssigner('_definition', 'rowCount', 'rowCount'),
+
+ _definition: Ember.computed('definition', 'definitionClass', function () {
+ return this.get('definition') || (this.get('definitionClass') || Definition).create();
+ }),
+ _dataProcessor: Ember.computed('dataProcessor', 'dataProcessorClass', function () {
+ return this.get('dataProcessor') || (this.get('dataProcessorClass') || DataProcessor).create();
+ }),
+
+ displayFooter: Ember.computed("_definition.minRowsForFooter", "_dataProcessor.processedRows.length", function () {
+ return this.get("_definition.minRowsForFooter") <= this.get("_dataProcessor.processedRows.length");
+ }),
+
+ _processedRowsObserver: Ember.observer('_dataProcessor.processedRows', function () {
+ this.sendAction('rowsChanged', this.get('_dataProcessor.processedRows'));
+ }),
+
+ _setColumnWidth: function (columns) {
+ var widthText = (100 / columns.length) + "%";
+ columns.forEach(function (column) {
+ if(!column.width) {
+ column.width = widthText;
+ }
+ });
+ },
+
+ _columns: Ember.computed('_definition.columns', function () {
+ var rawColumns = this.get('_definition.columns'),
+ normalisedColumns = {
+ left: [],
+ center: [],
+ right: [],
+ length: rawColumns.length
+ };
+
+ rawColumns.forEach(function (column) {
+ normalisedColumns[column.get("pin")].push({
+ definition: column,
+ width: column.width
+ });
+ });
+
+ if(normalisedColumns.center.length === 0) {
+ normalisedColumns.center = [{
+ definition: ColumnDefinition.fillerColumn,
+ }];
+ }
+
+ this._setColumnWidth(normalisedColumns.center);
+
+ return normalisedColumns;
+ }),
+
+ message: Ember.computed('_dataProcessor.message', '_columns.length', '_dataProcessor.processedRows.length', function () {
+ var message = this.get("_dataProcessor.message");
+ if(message) {
+ return message;
+ }
+ else if(!this.get('_columns.length')) {
+ return "No columns available!";
+ }
+ else if(!this.get("_dataProcessor.processedRows.length")) {
+ let identifiers = Ember.String.pluralize(this.get('_definition.recordType') || "record");
+ return `No ${identifiers} available!`;
+ }
+ }),
+
+ highlightRow: function (index) {
+ var element = Ember.$(this.get("element")),
+ sheet = element.find("style")[0].sheet,
+ elementID = element.attr("id"),
+ color = this.get("highlightRowOnMouse.color") || DEFAULT_ROW_HIGHLIGHT_COLOR;
+
+ try {
+ sheet.deleteRule(0);
+ }catch(e){}
+
+ if(index >= 0) {
+ sheet.insertRule(`#${elementID} .table-cell:nth-child(${index}){ background-color: ${color}; }`, 0);
+ }
+ },
+
+ didInsertElement: function () {
+ Ember.run.scheduleOnce('afterRender', this, function() {
+ this.highlightRowOnMouseObserver();
+ this.scrollChangeActionObserver();
+ });
+ },
+
+ highlightRowOnMouseObserver: Ember.observer("highlightRowOnMouse", function () {
+ var highlightRowOnMouse = this.get("highlightRowOnMouse"),
+ element = this.get("element");
+
+ if(element) {
+ element = Ember.$(element).find(".table-mid");
+
+ if(highlightRowOnMouse) {
+ element.on('mouseover', '.table-cell', this, HANDLERS.mouseOver);
+ element.on('mouseleave', this, HANDLERS.mouseLeave);
+ }
+ else {
+ element.off('mouseover', '.table-cell', HANDLERS.mouseOver);
+ element.off('mouseleave', HANDLERS.mouseLeave);
+ }
+ }
+ }),
+
+ scrollValuesObserver: Ember.observer("scrollValues.left", "scrollValues.width", "scrollValues.viewPortWidth", function () {
+ var scrollValues = this.get("scrollValues");
+
+ this.sendAction("scrollChangeAction", scrollValues);
+
+
+ this.set("showLeftScrollShadow", scrollValues.left > 1);
+ this.set("showRightScrollShadow", scrollValues.left < (scrollValues.width - scrollValues.viewPortWidth));
+ }),
+
+ scrollChangeActionObserver: Ember.observer("scrollChangeAction", "message", "showScrollShadow", function () {
+ Ember.run.scheduleOnce('afterRender', this, function() {
+ var addScrollListener = this.get("scrollChangeAction") || this.get("showScrollShadow"),
+ element = this.$().find(".table-body"),
+ scrollValues = this.get("scrollValues");
+
+ if(addScrollListener && element) {
+ element = element.get(0);
+
+ clearInterval(this.get("_widthTrackerTimer"));
+
+ if(element) {
+ if(addScrollListener) {
+ Ember.$(element).on('scroll', this, HANDLERS.onScroll);
+
+ this.set("_widthTrackerTimer", setInterval(function () {
+ scrollValues.setProperties({
+ width: element.scrollWidth,
+ viewPortWidth: element.offsetWidth
+ });
+ }, 1000));
+ }
+ else {
+ element.off('scroll', HANDLERS.onScroll);
+ }
+ }
+ }
+ });
+ }),
+
+ willDestroyElement: function () {
+ this._super();
+ clearInterval(this.get("_widthTrackerTimer"));
+ Ember.$(this.$().find(".table-body")).off();
+ Ember.$(this.$().find(".table-mid")).off();
+ Ember.$(this.$()).off();
+ },
+
+ actions: {
+ search: function (searchText, actualSearchType) {
+ this.set('_definition.searchText', searchText);
+ this.set('_definition._actualSearchType', actualSearchType);
+ this.sendAction("searchAction", searchText);
+ },
+ sort: function (sortColumnId, sortOrder) {
+ this.get("_definition").setProperties({
+ sortColumnId,
+ sortOrder
+ });
+ this.sendAction("sortAction", sortColumnId, sortOrder);
+ },
+ rowChanged: function (rowCount) {
+ this.set('_definition.rowCount', rowCount);
+ this.sendAction("rowAction", rowCount);
+ },
+ pageChanged: function (pageNum) {
+ this.set('_definition.pageNum', pageNum);
+ this.sendAction("pageAction", pageNum);
+ },
+ columnWidthChanged: function (width, columnDefinition, index) {
+ this.sendAction("columnWidthChangeAction", width, columnDefinition, index);
+ }
+ }
+});
diff --git a/tez-ui/src/main/webapp/app/controllers/app/configs.js b/tez-ui/src/main/webapp/app/controllers/app/configs.js
index 838abc1..e8f13fc 100644
--- a/tez-ui/src/main/webapp/app/controllers/app/configs.js
+++ b/tez-ui/src/main/webapp/app/controllers/app/configs.js
@@ -20,7 +20,7 @@
import Ember from 'ember';
import TableController from '../table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
var MoreObject = more.Object;
diff --git a/tez-ui/src/main/webapp/app/controllers/app/dags.js b/tez-ui/src/main/webapp/app/controllers/app/dags.js
index bb4502a..1febc66 100644
--- a/tez-ui/src/main/webapp/app/controllers/app/dags.js
+++ b/tez-ui/src/main/webapp/app/controllers/app/dags.js
@@ -17,7 +17,7 @@
*/
import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
export default MultiTableController.extend({
breadcrumbs: [{
diff --git a/tez-ui/src/main/webapp/app/controllers/counters-table.js b/tez-ui/src/main/webapp/app/controllers/counters-table.js
index 42361b4..37bae66 100644
--- a/tez-ui/src/main/webapp/app/controllers/counters-table.js
+++ b/tez-ui/src/main/webapp/app/controllers/counters-table.js
@@ -20,7 +20,7 @@
import Ember from 'ember';
import TableController from './table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../utils/column-definition';
var MoreObject = more.Object;
diff --git a/tez-ui/src/main/webapp/app/controllers/dag/attempts.js b/tez-ui/src/main/webapp/app/controllers/dag/attempts.js
index 47e95d9..4616638 100644
--- a/tez-ui/src/main/webapp/app/controllers/dag/attempts.js
+++ b/tez-ui/src/main/webapp/app/controllers/dag/attempts.js
@@ -17,7 +17,7 @@
*/
import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
export default MultiTableController.extend({
breadcrumbs: [{
diff --git a/tez-ui/src/main/webapp/app/controllers/dag/graphical.js b/tez-ui/src/main/webapp/app/controllers/dag/graphical.js
index c55ab8b..cc4130f 100644
--- a/tez-ui/src/main/webapp/app/controllers/dag/graphical.js
+++ b/tez-ui/src/main/webapp/app/controllers/dag/graphical.js
@@ -19,7 +19,7 @@
import Ember from 'ember';
import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
export default MultiTableController.extend({
diff --git a/tez-ui/src/main/webapp/app/controllers/dag/index/index.js b/tez-ui/src/main/webapp/app/controllers/dag/index/index.js
index c9adde4..6196b9a 100644
--- a/tez-ui/src/main/webapp/app/controllers/dag/index/index.js
+++ b/tez-ui/src/main/webapp/app/controllers/dag/index/index.js
@@ -19,7 +19,7 @@
import Ember from 'ember';
import MultiTableController from '../../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../../utils/column-definition';
export default MultiTableController.extend({
columns: ColumnDefinition.make([{
diff --git a/tez-ui/src/main/webapp/app/controllers/dag/swimlane.js b/tez-ui/src/main/webapp/app/controllers/dag/swimlane.js
index bbac40b..1fe2988 100644
--- a/tez-ui/src/main/webapp/app/controllers/dag/swimlane.js
+++ b/tez-ui/src/main/webapp/app/controllers/dag/swimlane.js
@@ -19,7 +19,7 @@
import Ember from 'ember';
import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
import VertexProcess from '../../utils/vertex-process';
import fullscreen from 'em-tgraph/utils/fullscreen';
diff --git a/tez-ui/src/main/webapp/app/controllers/dag/tasks.js b/tez-ui/src/main/webapp/app/controllers/dag/tasks.js
index 92f674a..834abf9 100644
--- a/tez-ui/src/main/webapp/app/controllers/dag/tasks.js
+++ b/tez-ui/src/main/webapp/app/controllers/dag/tasks.js
@@ -17,7 +17,7 @@
*/
import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
export default MultiTableController.extend({
breadcrumbs: [{
diff --git a/tez-ui/src/main/webapp/app/controllers/dag/vertices.js b/tez-ui/src/main/webapp/app/controllers/dag/vertices.js
index 313a5a9..34295e5 100644
--- a/tez-ui/src/main/webapp/app/controllers/dag/vertices.js
+++ b/tez-ui/src/main/webapp/app/controllers/dag/vertices.js
@@ -17,7 +17,7 @@
*/
import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
export default MultiTableController.extend({
breadcrumbs: [{
diff --git a/tez-ui/src/main/webapp/app/controllers/home/index.js b/tez-ui/src/main/webapp/app/controllers/home/index.js
index 754e5e6..53c4e6d 100644
--- a/tez-ui/src/main/webapp/app/controllers/home/index.js
+++ b/tez-ui/src/main/webapp/app/controllers/home/index.js
@@ -19,8 +19,8 @@
import Ember from 'ember';
import TableController from '../table';
-import ColumnDefinition from 'em-table/utils/column-definition';
-import TableDefinition from 'em-table/utils/table-definition';
+import ColumnDefinition from '../../utils/column-definition';
+import TableDefinition from '../../utils/table-definition';
export default TableController.extend({
diff --git a/tez-ui/src/main/webapp/app/controllers/home/queries.js b/tez-ui/src/main/webapp/app/controllers/home/queries.js
index ba7e6e3..b5e483d 100644
--- a/tez-ui/src/main/webapp/app/controllers/home/queries.js
+++ b/tez-ui/src/main/webapp/app/controllers/home/queries.js
@@ -19,8 +19,8 @@
import Ember from 'ember';
import TableController from '../table';
-import ColumnDefinition from 'em-table/utils/column-definition';
-import TableDefinition from 'em-table/utils/table-definition';
+import ColumnDefinition from '../../utils/column-definition';
+import TableDefinition from '../../utils/table-definition';
export default TableController.extend({
diff --git a/tez-ui/src/main/webapp/app/controllers/query/configs.js b/tez-ui/src/main/webapp/app/controllers/query/configs.js
index 8dcc91c..d828088 100644
--- a/tez-ui/src/main/webapp/app/controllers/query/configs.js
+++ b/tez-ui/src/main/webapp/app/controllers/query/configs.js
@@ -20,7 +20,7 @@
import Ember from 'ember';
import TableController from '../table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
var MoreObject = more.Object;
diff --git a/tez-ui/src/main/webapp/app/controllers/query/timeline.js b/tez-ui/src/main/webapp/app/controllers/query/timeline.js
index b52fc26..a7cd85e 100644
--- a/tez-ui/src/main/webapp/app/controllers/query/timeline.js
+++ b/tez-ui/src/main/webapp/app/controllers/query/timeline.js
@@ -19,7 +19,7 @@
import Ember from 'ember';
import TableController from '../table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
var MoreObject = more.Object;
diff --git a/tez-ui/src/main/webapp/app/controllers/table.js b/tez-ui/src/main/webapp/app/controllers/table.js
index 57adf00..01aec40 100644
--- a/tez-ui/src/main/webapp/app/controllers/table.js
+++ b/tez-ui/src/main/webapp/app/controllers/table.js
@@ -20,7 +20,7 @@
import Ember from 'ember';
import AbstractController from './abstract';
-import TableDefinition from 'em-table/utils/table-definition';
+import TableDefinition from '../utils/table-definition';
import isIOCounter from '../utils/misc';
import CounterColumnDefinition from '../utils/counter-column-definition';
diff --git a/tez-ui/src/main/webapp/app/controllers/task/attempts.js b/tez-ui/src/main/webapp/app/controllers/task/attempts.js
index a6acaec..04eb22a 100644
--- a/tez-ui/src/main/webapp/app/controllers/task/attempts.js
+++ b/tez-ui/src/main/webapp/app/controllers/task/attempts.js
@@ -17,7 +17,7 @@
*/
import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
import AutoCounterColumn from '../../mixins/auto-counter-column';
diff --git a/tez-ui/src/main/webapp/app/controllers/vertex/attempts.js b/tez-ui/src/main/webapp/app/controllers/vertex/attempts.js
index b07be92..107ecdb 100644
--- a/tez-ui/src/main/webapp/app/controllers/vertex/attempts.js
+++ b/tez-ui/src/main/webapp/app/controllers/vertex/attempts.js
@@ -17,7 +17,7 @@
*/
import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
import AutoCounterColumn from '../../mixins/auto-counter-column';
diff --git a/tez-ui/src/main/webapp/app/controllers/vertex/configs.js b/tez-ui/src/main/webapp/app/controllers/vertex/configs.js
index 1cf4a3d..2e1d94e 100644
--- a/tez-ui/src/main/webapp/app/controllers/vertex/configs.js
+++ b/tez-ui/src/main/webapp/app/controllers/vertex/configs.js
@@ -20,7 +20,7 @@
import Ember from 'ember';
import TableController from '../table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
var MoreObject = more.Object;
diff --git a/tez-ui/src/main/webapp/app/controllers/vertex/tasks.js b/tez-ui/src/main/webapp/app/controllers/vertex/tasks.js
index 560c8ba..dac000e 100644
--- a/tez-ui/src/main/webapp/app/controllers/vertex/tasks.js
+++ b/tez-ui/src/main/webapp/app/controllers/vertex/tasks.js
@@ -17,7 +17,7 @@
*/
import MultiTableController from '../multi-table';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from '../../utils/column-definition';
import AutoCounterColumn from '../../mixins/auto-counter-column';
diff --git a/tez-ui/src/main/webapp/app/styles/app.less b/tez-ui/src/main/webapp/app/styles/app.less
index f8a66e3..5caf44a 100644
--- a/tez-ui/src/main/webapp/app/styles/app.less
+++ b/tez-ui/src/main/webapp/app/styles/app.less
@@ -41,11 +41,18 @@
@import "em-swimlane";
@import "em-tooltip";
@import "em-swimlane-vertex-name";
+@import "em-table.less";
+@import "em-table-facet-panel.less";
@import "em-table-status-cell";
@import "query-timeline";
@import "home-table-controls";
@import "em-progress";
@import "em-breadcrumbs";
+@import "pagination-ui.less";
+@import "progress-cell.less";
+@import "search-ui.less";
+@import "status-cell.less";
+@import "variables.less";
// Modals
@import "column-selector";
diff --git a/tez-ui/src/main/webapp/app/styles/em-table-facet-panel.less b/tez-ui/src/main/webapp/app/styles/em-table-facet-panel.less
new file mode 100644
index 0000000..28be8f0
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/styles/em-table-facet-panel.less
@@ -0,0 +1,218 @@
+/**
+ * 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-table-facet-panel {
+ width: 160px;
+
+ margin: 5px 5px 0 0;
+
+ border: 1px solid @border-color;
+ border-radius: @border-radius;
+
+ padding: 5px 10px 10px 10px;
+
+ background-color: @table-bg;
+
+ overflow: hidden;
+
+ .field-filter-box {
+ width: 100%;
+ }
+
+ &.hide-filter {
+ .field-filter-box {
+ display: none;
+ }
+ }
+
+ .filter-message {
+ color: #999;
+ }
+
+ h4 {
+ text-align: center;
+ margin-top: 5px;
+ margin-bottom: 0px;
+ }
+
+ ul {
+ list-style-type: none;
+ }
+
+ li {
+ margin: 2px 0;
+ }
+
+ ul.field-list {
+ padding-top: 5px;
+ padding-left: 0px;
+
+ .em-table-facet-panel-values {
+
+ position: relative;
+
+ .field-name {
+ .no-select;
+
+ padding-right: 20px;
+
+ cursor: pointer;
+ display: flex;
+
+ &::before {
+ content: "\25bc";
+ font-size: .7em;
+ color: @text-light;
+ margin-top: 5px;
+ }
+
+ .field-title {
+ overflow-x: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ margin: 0 3px;
+ }
+
+ .field-count {
+ color: @text-light;
+ white-space: nowrap;
+ }
+
+ .all-button {
+ margin-left: 5px;
+ position: absolute;
+ right: 0px;
+ }
+ }
+
+ &.hide-select-all {
+ .field-name {
+ padding-right: 0px;
+
+ .all-button {
+ display: none;
+ }
+ }
+ }
+
+ .value-list {
+ overflow: hidden;
+
+ padding-left: 10px;
+
+ .filter-box {
+ width: 100%;
+ }
+
+ li {
+ display: flex;
+
+ .checkbox-container {
+ order: 0;
+ flex: 0 1 auto;
+ align-self: auto;
+
+ padding-right: 5px;
+ }
+
+ .facet-value {
+ order: 0;
+ flex: 1 1 auto;
+ align-self: auto;
+ padding-right: 2px;
+
+ overflow-x: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .only-button {
+ order: 0;
+ flex: 0 1 auto;
+ align-self: auto;
+
+ cursor: pointer;
+
+ display: none;
+ padding: 0 5px;
+ }
+
+ .facet-count {
+ order: 0;
+ flex: 0 1 auto;
+ align-self: auto;
+
+ &:hover{
+ text-decoration: none;
+ }
+ }
+
+ &:hover {
+ .only-button {
+ display: inline;
+ }
+ }
+ }
+
+ .pagination-controls {
+ padding-top: 5px;
+
+ position: relative;
+
+ .arrows {
+ position: absolute;
+ top: 5px;
+ right: 0px;
+ }
+
+ span {
+ user-select: none;
+ color: lightgrey;
+
+ &.active {
+ cursor: pointer;
+ color: #3B99FC;
+ }
+ }
+ }
+
+ }
+
+ &.hide-values {
+ .value-list {
+ display: none;
+ }
+
+ .field-name::before {
+ transform: rotate(-90deg) translate(2px, 2px);
+ }
+
+ .field-name .all-button {
+ display: none;
+ }
+ }
+
+ &.hide-filter {
+ .filter-box {
+ display: none;
+ }
+ }
+
+ }
+ }
+}
diff --git a/tez-ui/src/main/webapp/app/styles/em-table.less b/tez-ui/src/main/webapp/app/styles/em-table.less
new file mode 100644
index 0000000..dbea8a5
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/styles/em-table.less
@@ -0,0 +1,346 @@
+/**
+ * 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.
+ */
+
+
+// Imports
+@import (once) "bower_components/bootstrap/less/bootstrap";
+
+@import (once) "bower_components/snippet-ss/less/use";
+@import (once) "bower_components/snippet-ss/less/background";
+@import (once) "bower_components/snippet-ss/less/effects";
+
+@import "./variables";
+@import "./shared";
+
+@import "./search-ui";
+@import "./pagination-ui";
+
+@import "./progress-cell";
+@import "./status-cell";
+
+@import "./em-table-facet-panel";
+
+.em-table {
+ font-size: @font-size;
+ color: @text-color;
+
+ margin: 10px 0px;
+ overflow: hidden;
+
+ .table-header {
+ .clear-fix;
+ }
+
+ .table-mid {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: nowrap;
+ justify-content: flex-start;
+ align-content: stretch;
+ align-items: flex-start;
+
+ .table-panel-left, table-panel-right, .table-body-left, .table-body-right {
+ order: 0;
+ flex: 0 1 auto;
+ align-self: auto;
+ }
+
+ .table-body, .table-message {
+ order: 0;
+ flex: 1 1 auto;
+ align-self: auto;
+ border: 1px solid @border-color;
+
+ margin-top: 5px;
+ }
+
+ &>div:nth-child(2) {
+ border-top-left-radius: @border-radius;
+ border-bottom-left-radius: @border-radius;
+ border-left: 1px solid @border-color;
+ }
+
+ &>div:nth-last-child(2) {
+ border-top-right-radius: @border-radius;
+ border-bottom-right-radius: @border-radius;
+ border-right: 1px solid @border-color;
+ }
+ }
+
+ .table-footer {
+ .clear-fix;
+ }
+
+ .table-body-left, .table-body-right, .table-body {
+ border-top: 1px solid @border-color;
+ border-bottom: 1px solid @border-color;
+ background-color: @table-bg;
+ }
+
+ .table-message {
+ border-radius: @border-radius;
+ background-color: @table-bg;
+
+ text-align: center;
+ padding: 10px;
+ }
+
+ .table-body-left, .table-body-right, .table-body {
+ margin: 5px 0px;
+ }
+
+ .table-body-left, .table-body-right {
+ white-space: nowrap;
+ font-size: 0; // If not set, each column will have a space in between
+ }
+
+ .table-body{
+ .force-scrollbar;
+
+ .table-scroll-body {
+ //Adding this here will keep the column, and table background same white
+ //making the UI look better when scroll bar is shown
+ .dotted-bg;
+
+ white-space: nowrap;
+ font-size: 0; // If not set, each column will have a space in between
+ }
+ }
+
+ &.show-scroll-shadow {
+ .left-scroll-shadow, .right-scroll-shadow {
+ order: 0;
+ flex: 0 1 auto;
+ align-self: stretch;
+ position: relative;
+
+ opacity: 0;
+ transition: opacity 0.3s;
+
+ width: 0px;
+ z-index: 99;
+
+ pointer-events: none;
+
+ .shadow-container {
+ position: absolute;
+ overflow: hidden;
+
+ top: 0px;
+ bottom: 0px;
+ width: 50px;
+
+ &:before {
+ content: "";
+ position: absolute;
+
+ top: 10px;
+ bottom: 15px;
+ width: 50px;
+ }
+ }
+ }
+ .left-scroll-shadow {
+ .shadow-container {
+ &:before {
+ left: -50px;
+ box-shadow: 12px 0 40px -4px rgba(0, 0, 0, 0.2);
+ }
+ }
+ }
+ .right-scroll-shadow {
+ .shadow-container {
+ right: 0px;
+ &:before {
+ left: 50px;
+ box-shadow: -12px 0 40px -4px rgba(0, 0, 0, 0.2);
+ }
+ }
+ }
+
+ &.show-left-scroll-shadow {
+ .left-scroll-shadow {
+ opacity: 1;
+ }
+ }
+ &.show-right-scroll-shadow {
+ .right-scroll-shadow {
+ opacity: 1;
+ }
+ }
+ }
+
+ .table-column {
+ .use-border-padding-in-width-height;
+
+ background-color: @table-bg;
+
+ vertical-align: top;
+ overflow: hidden;
+ display: inline-block;
+ min-width: 150px;
+
+ &.inner {
+ border-left: 1px solid @border-color;
+ }
+
+ // Just the shaded header
+ .table-header-cell {
+ background-color: @bg-grey;
+ border-bottom: 1px solid @border-color;
+
+ &.is-sorting {
+ .animated-stripes;
+ }
+ }
+
+ .header-body, .table-cell {
+ font-size: @font-size;
+ white-space: nowrap;
+
+ text-overflow: ellipsis;
+ overflow: hidden;
+
+ height: 2.1em;
+ padding: 5px;
+
+ .ember-view {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+ }
+
+ .header-body {
+ font-weight: bold;
+
+ padding-right: 1.1em; // To compensate space occupied by sort/resize buttons
+ position: relative; // So that buttons can be positioned
+
+ .sort-bar {
+ cursor: pointer;
+ position: absolute;
+
+ left: 0;
+ right: .5em;
+ top: 0;
+ bottom: 0;
+ }
+
+ .sort-icon {
+ cursor: pointer;
+ position: absolute;
+ right: .5em;
+ top: .2em;
+
+ &:before, &:after {
+ font-size: .7em;
+ opacity: .5;
+ position: absolute;
+ }
+
+ &:before {
+ content: "\25B2";
+
+ top: 0em;
+ right: 0px;
+ }
+
+ &:after {
+ content: "\25BC";
+
+ top: 1em;
+ right: 0px;
+ }
+
+ &.asc{
+ &:before {
+ opacity: 1;
+ }
+ &:after {
+ opacity: .5;
+ }
+ }
+
+ &.desc {
+ &:before {
+ opacity: .5;
+ }
+ &:after {
+ opacity: 1;
+ }
+ }
+ }
+
+ .resize-column:after {
+ content: "\22EE";
+ cursor: col-resize;
+ opacity: .3;
+
+ position: absolute;
+ right: 2px;
+ top: 6px;
+ }
+ }
+
+ .table-cell {
+ position: relative;
+
+ .comment-indicator {
+ position: absolute;
+
+ color: white;
+ font-size: 10px;
+ padding-left: 4px;
+
+ top: -4px;
+ right: -4px;
+
+ width: 10px;
+ height: 10px;
+ background-color: orange;
+ border-radius: 10px;
+
+ opacity: 0.6;
+
+ &:hover {
+ top: -2px;
+ right: -2px;
+ }
+ }
+
+ &.bg-transition {
+ -webkit-transition: box-shadow 500ms ease-out 500ms;
+ -moz-transition: box-shadow 500ms ease-out 500ms;
+ -o-transition: box-shadow 500ms ease-out 500ms;
+ transition: box-shadow 500ms ease-out 500ms;
+ }
+
+ &.highlight {
+ box-shadow: 0 0 60px lighten(@brand-primary, 10%) inset;
+ }
+ &.is-waiting {
+ .animated-stripes;
+ }
+ &.inner {
+ border-top: 1px dotted @border-color;
+ margin-top: -1px;
+ }
+ }
+ }
+
+}
diff --git a/tez-ui/src/main/webapp/app/styles/pagination-ui.less b/tez-ui/src/main/webapp/app/styles/pagination-ui.less
new file mode 100644
index 0000000..df3d7c0
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/styles/pagination-ui.less
@@ -0,0 +1,88 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.pagination-ui {
+ .inline-block;
+ .align-top;
+
+ float: right;
+
+ .page-list {
+ .inline-block;
+ .align-top;
+
+ overflow: hidden;
+
+ border: 1px solid @border-color;
+ border-radius: 5px;
+ background-color: @table-bg;
+
+ padding: 0px;
+
+ font-size: 0px;
+
+ li {
+ .inline-block;
+
+ padding: 6px 12px;
+ height: 32px;
+
+ font-size: @font-size;
+ color: @text-light;
+
+ border-left: 1px solid @border-color;
+
+ pointer-events: none;
+
+ &.clickable {
+ pointer-events: auto;
+ color: @text-color;
+
+ &:hover {
+ background-color: @bg-grey;
+ cursor: pointer;
+ }
+ }
+ }
+
+ .total-page-count {
+ font-size: .8em;
+ }
+
+ :first-child {
+ border-left: none;
+ }
+ }
+
+ .row-select {
+ margin-left: 5px;
+
+ display: inline-block;
+ text-align: center;
+
+ select {
+ cursor: pointer;
+ }
+ }
+}
+
+.table-footer {
+ .pagination-ui {
+ position: static;
+ }
+}
diff --git a/tez-ui/src/main/webapp/app/styles/progress-cell.less b/tez-ui/src/main/webapp/app/styles/progress-cell.less
new file mode 100644
index 0000000..9030216
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/styles/progress-cell.less
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+.table-cell {
+ .em-progress-container {
+ .progress {
+ margin: -1px 0 0 0;
+ }
+ }
+}
diff --git a/tez-ui/src/main/webapp/app/styles/search-ui.less b/tez-ui/src/main/webapp/app/styles/search-ui.less
new file mode 100644
index 0000000..b850031
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/styles/search-ui.less
@@ -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.
+ */
+
+.search-ui {
+ .inline-block;
+ .align-top;
+
+ max-width: 500px;
+
+ margin-bottom: 2px;
+
+ .type-select {
+ width: 5em;
+ margin-right: -3px !important;
+ }
+
+ .search-syntax {
+ .label;
+ .label-default;
+ .no-select;
+ }
+}
diff --git a/tez-ui/src/main/webapp/app/styles/shared.less b/tez-ui/src/main/webapp/app/styles/shared.less
index b34cfa6..b448c30 100644
--- a/tez-ui/src/main/webapp/app/styles/shared.less
+++ b/tez-ui/src/main/webapp/app/styles/shared.less
@@ -33,6 +33,99 @@
padding-left: 10px;
}
+.no-select {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+
+ cursor: default;
+}
+
+.no-display {
+ display: none !important;
+}
+
+.no-visible {
+ visibility: hidden !important;
+}
+
+.no-margin {
+ margin: 0px !important;
+}
+
+.no-pointer {
+ pointer-events: none;
+}
+
+.inactive {
+ .no-pointer;
+ opacity: 0.4;
+}
+
+.no-wrap {
+ white-space: nowrap;
+}
+
+.no-border {
+ border: none !important;
+}
+
+.align-top {
+ vertical-align: top;
+}
+
+.align-super {
+ vertical-align: super;
+}
+
+.inline-block {
+ display: inline-block;
+}
+
+.dotted-background {
+ background:
+ radial-gradient(#EEE 15%, transparent 17%) 0 0,
+ radial-gradient(#EEE 15%, transparent 17%) 5px -5px,
+ radial-gradient(#EEE 15%, transparent 17%) 5px 5px;
+ background-color: #DDD;
+ background-size: 10px 10px;
+}
+
+.absolute {
+ position: absolute;
+}
+
+.use-gpu {
+ -webkit-transform: translateZ(0);
+ -moz-transform: translateZ(0);
+ -ms-transform: translateZ(0);
+ -o-transform: translateZ(0);
+ transform: translateZ(0);
+}
+
+.force-scrollbar {
+ overflow: auto;
+
+ &::-webkit-scrollbar {
+ -webkit-appearance: none;
+ }
+ &::-webkit-scrollbar:vertical {
+ width: 11px;
+ }
+ &::-webkit-scrollbar:horizontal {
+ height: 11px;
+ }
+ &::-webkit-scrollbar-thumb {
+ border-radius: 8px;
+ border: 2px solid #EEE;
+ background-color: #BBB;
+ }
+ &::-webkit-scrollbar-track {
+ background-color: #EEE;
+ border-radius: 8px;
+ }
+}
+
.align-checknradio {
input[type=checkbox], input[type=radio] {
vertical-align: middle;
@@ -41,6 +134,28 @@
}
}
+.left-divider{
+ padding-left: 5px;
+ border-left: 1px solid lightgrey;
+ margin-left: 5px;
+}
+
+.clear-fix {
+ &:after {
+ content: " ";
+ visibility: hidden;
+ display: block;
+ height: 0;
+ clear: both;
+ }
+}
+
+.animated-stripes {
+ .diagonal-stripes-background(#FFF, #EEE);
+ .animate;
+ .white-inner-glow;
+}
+
.diagnostics {
padding: 10px;
white-space: pre-line;
diff --git a/tez-ui/src/main/webapp/app/styles/status-cell.less b/tez-ui/src/main/webapp/app/styles/status-cell.less
new file mode 100644
index 0000000..57f39cf
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/styles/status-cell.less
@@ -0,0 +1,86 @@
+/**
+ * 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-table-status-cell {
+ overflow: visible !important;
+
+ .status {
+ .label;
+ .label-default;
+ }
+ .status-icon {
+ // Must be defined by the top-level project
+ }
+
+ .status-new, .status-inited, .status-started {
+ .status-icon {
+ // Must be defined by the top-level project
+ }
+ }
+
+ .status-initializing, .status-scheduled {
+ .label-primary;
+ .status-icon {
+ // Must be defined by the top-level project
+ }
+ }
+
+ .status-running, .status-in-progress {
+ .diagonal-stripes-bg;
+ .animate;
+ .label-info;
+ .status-icon {
+ // Must be defined by the top-level project
+ }
+ }
+
+ .status-committing {
+ .label-info;
+ .status-icon {
+ // Must be defined by the top-level project
+ }
+ }
+
+ .status-finished, .status-succeeded {
+ .label-success;
+ .status-icon {
+ // Must be defined by the top-level project
+ }
+ }
+
+ .status-terminating {
+ .label-warning;
+ .status-icon {
+ // Must be defined by the top-level project
+ }
+ }
+
+ .status-failed {
+ .label-warning;
+ .status-icon {
+ // Must be defined by the top-level project
+ }
+ }
+
+ .status-killed, .status-error {
+ .label-danger;
+ .status-icon {
+ // Must be defined by the top-level project
+ }
+ }
+}
diff --git a/tez-ui/src/main/webapp/app/styles/variables.less b/tez-ui/src/main/webapp/app/styles/variables.less
new file mode 100644
index 0000000..e6dbeca
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/styles/variables.less
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@text-light: #BBBBBB;
+@text-color: #222222;
+
+@bg-grey: #f0f0f0;
+@table-bg: white;
+
+@border-color: #dcdcdc;
+@border-radius: 5px;
+
+@font-size: 14px;
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-table-cell.hbs b/tez-ui/src/main/webapp/app/templates/components/em-table-cell.hbs
new file mode 100644
index 0000000..777be93
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-cell.hbs
@@ -0,0 +1,41 @@
+{{!
+ * 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.
+}}
+
+{{#if isWaiting}}
+ Waiting...
+{{else}}
+ {{#if columnDefinition.cellComponentName}}
+ {{component columnDefinition.cellComponentName content=_cellContent definition=columnDefinition.cellDefinition}}
+ {{else}}
+ {{#unless columnDefinition.cellDefinition}}
+ {{txt _cellContent}}
+ {{else}}
+ {{txt _cellContent
+ type=columnDefinition.cellDefinition.type
+ format=columnDefinition.cellDefinition.format
+ timeZone=columnDefinition.cellDefinition.timeZone
+ valueFormat=columnDefinition.cellDefinition.valueFormat
+ valueTimeZone=columnDefinition.cellDefinition.valueTimeZone
+ valueUnit=columnDefinition.cellDefinition.valueUnit
+ }}
+ {{/unless}}
+ {{/if}}
+ {{#if _comment}}
+ <div title="{{_comment}}" class="comment-indicator"></div>
+ {{/if}}
+{{/if}}
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-table-column.hbs b/tez-ui/src/main/webapp/app/templates/components/em-table-column.hbs
new file mode 100644
index 0000000..a3908db
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-column.hbs
@@ -0,0 +1,22 @@
+{{!
+ * 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-table-header-cell title=definition.headerTitle definition=definition tableDefinition=tableDefinition dataProcessor=dataProcessor}}
+{{#each rows as |row rowIndex|}}
+ {{em-table-cell columnDefinition=definition row=row index=rowIndex}}
+{{/each}}
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-table-facet-panel-values.hbs b/tez-ui/src/main/webapp/app/templates/components/em-table-facet-panel-values.hbs
new file mode 100644
index 0000000..579e9eb
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-facet-panel-values.hbs
@@ -0,0 +1,50 @@
+{{!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+}}
+
+<div class="field-name" {{action "toggleValueDisplay"}} title={{data.column.headerTitle}}>
+ <div class="field-title">{{data.column.headerTitle}}</div>
+ <div class="field-count">({{data.facets.length}})</div>
+ <a class="all-button" title={{allButtonTitle}} {{action "selectAll" bubbles=false}}>All</a>
+</div>
+
+<ul class="value-list">
+ {{input type="text" class="filter-box" value=filterText placeholder="Filter"}}
+
+ {{#if showPagination}}
+ <div class="pagination-controls">
+ {{currentPage}}/{{totalPages}}
+ <div class="arrows">
+ <span {{action "changePage" -1}} class="{{if showPrevious 'active'}}">◄</span>
+ <span {{action "changePage" 1}} class="{{if showNext 'active'}}">►</span>
+ </div>
+ </div>
+ {{/if}}
+
+ {{#each paginatedFacets key="value" as |facet index|}}
+ <li title={{facet.displayText}}>
+ <div class="checkbox-container">
+ <input type="checkbox" checked={{facet.checked}} onclick={{action "clickedCheckbox" facet}} />
+ </div>
+ <div class="facet-value">{{facet.displayText}}</div>
+ <a class="only-button" {{action "clickedOnly" facet}}>only</a>
+ <a class="facet-count">{{facet.count}}</a>
+ </li>
+ {{else}}
+ <span class="filter-message">No fields!</span>
+ {{/each}}
+</ul>
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-table-facet-panel.hbs b/tez-ui/src/main/webapp/app/templates/components/em-table-facet-panel.hbs
new file mode 100644
index 0000000..d513786
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-facet-panel.hbs
@@ -0,0 +1,33 @@
+{{!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+}}
+
+{{#if dataProcessor.facetedFields.length}}
+ <ul class="field-list">
+ {{input type="text" class="field-filter-box" value=filterText placeholder="Filter"}}
+ {{#each dataProcessor.facetedFields key="column.id" as |field fieldIndex|}}
+ <li>{{component field.column.facetType.componentName data=field tableDefinition=tableDefinition dataProcessor=dataProcessor tmpFacetConditions=tmpFacetConditions}}</li>
+ {{/each}}
+ </ul>
+
+ <div class="buttons">
+ <button type="button" class="btn btn-primary" {{action "applyFilters"}}>Apply</button>
+ <button type="button" class="btn btn-default" {{action "clearFilters"}}>Clear</button>
+ </div>
+{{else}}
+ <h4>Not Available!</h4>
+{{/if}}
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-table-header-cell.hbs b/tez-ui/src/main/webapp/app/templates/components/em-table-header-cell.hbs
new file mode 100644
index 0000000..c48eb7a
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-header-cell.hbs
@@ -0,0 +1,30 @@
+{{!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+}}
+
+<div class="header-body">
+ {{title}}
+ {{#if tableDefinition.enableSort}}{{#if definition.enableSort}}
+ <span title="Sort {{sortToggledTitle}}" class="sort-icon {{sortIconCSS}}" {{action 'sort'}}></span>
+ {{#if tableDefinition.headerAsSortButton}}
+ <span class="sort-bar" {{action 'sort'}}></span>
+ {{/if}}
+ {{/if}}{{/if}}
+ {{#if tableDefinition.enableColumnResize}}{{#if definition.enableColumnResize}}
+ <span title="Resize column" class="resize-column" {{action 'startColResize' on="mouseDown"}}></span>
+ {{/if}}{{/if}}
+</div>
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-table-linked-cell.hbs b/tez-ui/src/main/webapp/app/templates/components/em-table-linked-cell.hbs
new file mode 100644
index 0000000..8a82631
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-linked-cell.hbs
@@ -0,0 +1,41 @@
+{{!
+ * 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.
+}}
+
+{{#if normalizedLinks.length}}
+ {{#each normalizedLinks as |link|}}
+ {{#if link.routeName}}
+ {{#if link.withModel}}
+ {{#link-to link.routeName link.model target=definition.target}}
+ {{link.text}}
+ {{/link-to}}
+ {{else}}
+ {{#link-to link.routeName target=definition.target}}
+ {{link.text}}
+ {{/link-to}}
+ {{/if}}
+ {{else if link.href}}
+ <a href={{link.href}} target={{definition.target}} download={{link.download}}>
+ {{link.text}}
+ </a>
+ {{else}}
+ {{link.text}}
+ {{/if}}
+ {{/each}}
+{{else}}
+ <span class="txt-message">Not Available!</span>
+{{/if}}
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-table-pagination-ui.hbs b/tez-ui/src/main/webapp/app/templates/components/em-table-pagination-ui.hbs
new file mode 100644
index 0000000..78edcae
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-pagination-ui.hbs
@@ -0,0 +1,45 @@
+{{!
+ * 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.
+}}
+
+<ul class="page-list">
+ {{#if showFirst}}
+ <li title="Go to first page" class="clickable" {{action 'changePage' 1}}>
+ First
+ </li>
+ {{/if}}
+ {{#each _possiblePages as |page|}}
+ <li class="{{if page.isCurrent 'is-current' 'clickable'}}" {{action 'changePage' page.pageNum}}>
+ {{page.pageNum}}
+ </li>
+ {{/each}}
+ {{#if showLast}}
+ <li title="Go to last page, {{dataProcessor.totalPages}} page(s) available in total" class="clickable" {{action 'changePage' dataProcessor.totalPages}}>
+ Last - {{dataProcessor.totalPages}}
+ </li>
+ {{/if}}
+</ul>
+
+<div class='row-select'>
+ <select title="Select rows to display" class="form-control" onchange={{action "rowSelected" value="target.value"}}>
+ {{#each rowCountOptions as |option|}}
+ <option value={{option.value}} selected={{option.selected}}>
+ {{option.value}} Rows
+ </option>
+ {{/each}}
+ </select>
+</div>
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-table-progress-cell.hbs b/tez-ui/src/main/webapp/app/templates/components/em-table-progress-cell.hbs
new file mode 100644
index 0000000..9df8be0
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-progress-cell.hbs
@@ -0,0 +1,29 @@
+{{!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+}}
+
+{{#if message}}
+ <span class="em-message">{{message}}</span>
+{{else}}
+ {{em-progress
+ value=content
+ valueMin=_definition.valueMin
+ valueMax=_definition.valueMax
+ striped=_definition.striped
+ style=_definition.style
+ }}
+{{/if}}
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-table-search-ui.hbs b/tez-ui/src/main/webapp/app/templates/components/em-table-search-ui.hbs
new file mode 100644
index 0000000..13cb1dd
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table-search-ui.hbs
@@ -0,0 +1,52 @@
+{{!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+}}
+
+<div class="input-group">
+ {{#if (eq tableDefinition.searchType "manual")}}
+ <span class="input-group-btn">
+ <select class="type-select form-control btn btn-default" onchange={{action (mut actualSearchType) value="target.value"}}>
+ {{#each searchTypes as |searchType|}}
+ <option value={{searchType}} selected={{eq searchType actualSearchType}}>
+ {{searchType}}
+ </option>
+ {{/each}}
+ </select>
+ </span>
+ {{/if}}
+
+ {{input
+ type="text"
+ class="form-control"
+ placeholder="Search..."
+ enter="search"
+ value=text
+ }}
+
+ <span class="input-group-btn">
+ <button class="btn btn-default {{if dataProcessor.isSearching 'animated-stripes'}}" type="button" {{action "search"}}>
+ {{#if dataProcessor.isSearching}}
+ Searching...
+ {{else}}
+ {{#if (eq tableDefinition.searchType "auto")}}
+ {{actualSearchType}}
+ {{/if}}
+ Search
+ {{/if}}
+ </button>
+ </span>
+</div>
diff --git a/tez-ui/src/main/webapp/app/templates/components/em-table.hbs b/tez-ui/src/main/webapp/app/templates/components/em-table.hbs
new file mode 100644
index 0000000..f7be9d4
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/components/em-table.hbs
@@ -0,0 +1,106 @@
+{{!
+ * 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.
+}}
+
+{{!--To add CSS rules at runtime!--}}
+<style></style>
+
+{{!--Header--}}
+<div class='table-header'>
+ {{#each headerComponentNames as |componentName|}}
+ {{component componentName tableDefinition=_definition dataProcessor=_dataProcessor}}
+ {{/each}}
+</div>
+
+<div class="table-mid">
+ <div class='table-panel-left'>
+ {{#if leftPanelComponentName}}
+ {{component leftPanelComponentName tableDefinition=_definition dataProcessor=_dataProcessor}}
+ {{/if}}
+ </div>
+
+ {{#if message}}
+ <h4 class="table-message">{{message}}</h4>
+ {{else}}
+ {{!--Body--}}
+ {{#if _columns.left.length}}
+ <div class='table-body-left'>
+ {{#each _columns.left as |column colIndex|}}
+ {{em-table-column
+ rows=_dataProcessor.processedRows
+ definition=column.definition
+ defaultWidth=column.width
+ tableDefinition=_definition
+ dataProcessor=_dataProcessor
+ index=colIndex
+ }}
+ {{/each}}
+ </div>
+ {{/if}}
+
+ <span class='left-scroll-shadow'>
+ <span class='shadow-container'></span>
+ </span>
+ <div class='table-body'>
+ <div class='table-scroll-body'>
+ {{#each _columns.center as |column colIndex|}}
+ {{em-table-column
+ rows=_dataProcessor.processedRows
+ definition=column.definition
+ defaultWidth=column.width
+ tableDefinition=_definition
+ dataProcessor=_dataProcessor
+ index=colIndex
+ }}
+ {{/each}}
+ </div>
+ </div>
+ <span class='right-scroll-shadow'>
+ <span class='shadow-container'></span>
+ </span>
+
+ {{#if _columns.right.length}}
+ <div class='table-body-right'>
+ {{#each _columns.right as |column colIndex|}}
+ {{em-table-column
+ rows=_dataProcessor.processedRows
+ definition=column.definition
+ defaultWidth=column.width
+ tableDefinition=_definition
+ dataProcessor=_dataProcessor
+ index=colIndex
+ }}
+ {{/each}}
+ </div>
+ {{/if}}
+ {{/if}}
+
+ <div class='table-panel-right'>
+ {{#if rightPanelComponentName}}
+ {{component rightPanelComponentName tableDefinition=_definition dataProcessor=_dataProcessor}}
+ {{/if}}
+ </div>
+</div>
+
+{{!--Footer--}}
+{{#if displayFooter}}
+ <div class='table-footer'>
+ {{#each footerComponentNames as |componentName|}}
+ {{component componentName tableDefinition=_definition dataProcessor=_dataProcessor}}
+ {{/each}}
+ </div>
+{{/if}}
diff --git a/tez-ui/src/main/webapp/app/utils/column-definition.js b/tez-ui/src/main/webapp/app/utils/column-definition.js
new file mode 100644
index 0000000..1316866
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/utils/column-definition.js
@@ -0,0 +1,125 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+import facetTypes from './facet-types';
+
+function getContentAtPath(row) {
+ var contentPath = this.get('contentPath');
+
+ if(contentPath) {
+ return Ember.get(row, contentPath);
+ }
+ else {
+ throw new Error("contentPath not set!");
+ }
+}
+
+function returnEmptyString() {
+ return "";
+}
+
+var ColumnDefinition = Ember.Object.extend({
+ id: "",
+ headerTitle: "Not Available!",
+
+ classNames: [],
+
+ cellComponentName: null,
+
+ enableSearch: true,
+ enableSort: true,
+ enableColumnResize: true,
+
+ width: null,
+ minWidth: "150px",
+
+ contentPath: null,
+ observePath: false,
+
+ cellDefinition: null,
+
+ pin: "center",
+
+ facetType: facetTypes.VALUES,
+
+ beforeSort: null,
+ getCellContent: getContentAtPath,
+ getSearchValue: getContentAtPath,
+ getSortValue: getContentAtPath,
+
+ init: function () {
+ if(!this.get("id")) {
+ throw new Error("ID is not set.");
+ }
+ },
+});
+
+ColumnDefinition.make = function (rawDefinition) {
+ if(Array.isArray(rawDefinition)) {
+ return rawDefinition.map(function (def) {
+ return ColumnDefinition.create(def);
+ });
+ }
+ else if(typeof rawDefinition === 'object') {
+ return ColumnDefinition.create(rawDefinition);
+ }
+ else {
+ throw new Error("rawDefinition must be an Array or an Object.");
+ }
+};
+
+ColumnDefinition.makeFromModel = function (ModelClass, columnOptions) {
+ var attributes = Ember.get(ModelClass, 'attributes'),
+ columns = [];
+ if(attributes) {
+ attributes.forEach(function (meta, name) {
+ var column = Ember.Object.create({
+ id: name,
+ headerTitle: name.capitalize(),
+ contentPath: name,
+ });
+
+ if(columnOptions) {
+ column.setProperties(columnOptions);
+ }
+
+ columns.push(column);
+ });
+
+ return ColumnDefinition.make(columns);
+ }
+ else {
+ throw new Error("Value passed is not a model class");
+ }
+};
+
+ColumnDefinition.fillerColumn = ColumnDefinition.create({
+ id: "fillerColumn",
+ headerTitle: "",
+ getCellContent: returnEmptyString,
+ getSearchValue: returnEmptyString,
+ getSortValue: returnEmptyString,
+
+ enableSearch: false,
+ enableSort: false,
+ enableColumnResize: false,
+});
+
+export default ColumnDefinition;
diff --git a/tez-ui/src/main/webapp/app/utils/counter-column-definition.js b/tez-ui/src/main/webapp/app/utils/counter-column-definition.js
index d66e551..5590e10 100644
--- a/tez-ui/src/main/webapp/app/utils/counter-column-definition.js
+++ b/tez-ui/src/main/webapp/app/utils/counter-column-definition.js
@@ -19,7 +19,7 @@
import Ember from 'ember';
import isIOCounter from '../utils/misc';
-import ColumnDefinition from 'em-table/utils/column-definition';
+import ColumnDefinition from './column-definition';
/*
* Returns a counter value from for a row
diff --git a/tez-ui/src/main/webapp/app/utils/data-processor.js b/tez-ui/src/main/webapp/app/utils/data-processor.js
new file mode 100644
index 0000000..07d31c0
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/utils/data-processor.js
@@ -0,0 +1,275 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+import SQL from './sql';
+
+/**
+ * Handles Sorting, Searching & Pagination
+ */
+export default Ember.Object.extend({
+ isSorting: false,
+ isSearching: false,
+
+ tableDefinition: null,
+
+ sql: SQL.create(),
+
+ rows: [],
+ _sortedRows: [],
+ _searchedRows: [],
+ _facetFilteredRows: [],
+
+ _searchObserver: Ember.on("init", Ember.observer('tableDefinition.searchText', 'tableDefinition._actualSearchType', '_sortedRows.[]', function () {
+ Ember.run.once(this, "startSearch");
+ })),
+
+ _sortObserver: Ember.on("init", Ember.observer(
+ 'tableDefinition.sortColumnId',
+ 'tableDefinition.sortOrder',
+ 'rows.[]', function () {
+ Ember.run.once(this, "startSort");
+ })),
+
+ _facetedFilterObserver: Ember.on("init", Ember.observer('tableDefinition.facetConditions', '_searchedRows.[]', function () {
+ Ember.run.once(this, "startFacetedFilter");
+ })),
+
+ regexSearch: function (clause, rows, columns) {
+ var regex;
+
+ try {
+ regex = new RegExp(clause, "i");
+ }
+ catch(e) {
+ regex = new RegExp("", "i");
+ }
+
+ function checkRow(column) {
+ var value;
+ if(!column.get('enableSearch')) {
+ return false;
+ }
+ value = column.getSearchValue(this);
+
+ if(typeof value === 'string') {
+ value = value.toLowerCase();
+ return value.match(regex);
+ }
+
+ return false;
+ }
+
+ return rows.filter(function (row) {
+ return columns.some(checkRow, row);
+ });
+ },
+
+ startSearch: function () {
+ var searchText = String(this.get('tableDefinition.searchText')),
+ rows = this.get('_sortedRows') || [],
+ columns = this.get('tableDefinition.columns'),
+ actualSearchType = this.get('tableDefinition._actualSearchType'),
+ that = this;
+
+ if(searchText) {
+ this.set("isSearching", true);
+
+ Ember.run.later(function () {
+ var result;
+
+ switch(actualSearchType) {
+ case "SQL":
+ result = that.get("sql").search(searchText, rows, columns);
+ break;
+
+ //case "Regex": Commenting as default will be called anyways
+ default:
+ result = that.regexSearch(searchText, rows, columns);
+ break;
+ }
+
+ that.setProperties({
+ _searchedRows: result,
+ isSearching: false
+ });
+ });
+ }
+ else {
+ this.set("_searchedRows", rows);
+ }
+ },
+
+ compareFunction: function (a, b){
+ // Checking for undefined and null to handle some special cases in JavaScript comparison
+ // Eg: 1 > undefined = false & 1 < undefined = false
+ // "a1" > null = false & "a1" < null = false
+ if(a === undefined || a === null) {
+ return -1;
+ }
+ else if(b === undefined || b === null) {
+ return 1;
+ }
+ else if(a < b) {
+ return -1;
+ }
+ else if(a > b) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ },
+
+ startSort: function () {
+ var rows = this.get('rows'),
+ tableDefinition = this.get('tableDefinition'),
+ sortColumnId = this.get('tableDefinition.sortColumnId'),
+ descending = this.get('tableDefinition.sortOrder') === 'desc',
+ that = this,
+ column;
+
+ if(tableDefinition) {
+ column = tableDefinition.get('columns').find(function (element) {
+ return element.get('id') === sortColumnId;
+ });
+ }
+
+ if(rows && Array.isArray(rows.content)) {
+ rows = rows.toArray();
+ }
+
+ if(rows && rows.get('length') > 0 && column) {
+ this.set('isSorting', true);
+
+ Ember.run.later(function () {
+ /*
+ * Creating sortArray as calling getSortValue form inside the
+ * sort function every time would be more costly.
+ */
+ var sortArray = rows.map(function (row) {
+ return {
+ value: column.getSortValue(row),
+ row: row
+ };
+ }),
+ compareFunction = that.get("compareFunction");
+
+ sortArray.sort(function (a, b) {
+ var result = compareFunction(a.value, b.value);
+ if(descending && result) {
+ result = -result;
+ }
+ return result;
+ });
+
+ that.setProperties({
+ _sortedRows: sortArray.map(function (record) {
+ return record.row;
+ }),
+ isSorting: false
+ });
+ });
+ }
+ else {
+ this.set('_sortedRows', rows);
+ }
+ },
+
+ startFacetedFilter: function () {
+ var clause = this.get("sql").createFacetClause(this.get('tableDefinition.facetConditions'), this.get("tableDefinition.columns")),
+ rows = this.get('_searchedRows') || [],
+ columns = this.get('tableDefinition.columns'),
+ that = this;
+
+ if(clause && columns) {
+ this.set("isSearching", true);
+
+ Ember.run.later(function () {
+ var result = that.get("sql").search(clause, rows, columns);
+
+ that.setProperties({
+ _facetFilteredRows: result,
+ isSearching: false
+ });
+ });
+ }
+ else {
+ this.set("_facetFilteredRows", rows);
+ }
+ },
+
+ facetedFields: Ember.computed('_searchedRows.[]', 'tableDefinition.columns', function () {
+ var searchedRows = this.get("_searchedRows"),
+ columns = this.get('tableDefinition.columns'),
+ fields = [];
+
+ if(columns) {
+ columns.forEach(function (column) {
+ var facetedData;
+ if(column.facetType) {
+ facetedData = column.facetType.facetRows(column, searchedRows);
+ if(facetedData) {
+ fields.push({
+ column: column,
+ facets: facetedData
+ });
+ }
+ }
+ });
+ }
+
+ return fields;
+ }),
+
+ pageDetails: Ember.computed("tableDefinition.rowCount", "tableDefinition.pageNum", "_facetFilteredRows.length", function () {
+ var tableDefinition = this.get("tableDefinition"),
+
+ pageNum = tableDefinition.get('pageNum'),
+ rowCount = tableDefinition.get('rowCount'),
+
+ startIndex = (pageNum - 1) * rowCount,
+
+ totalRecords = this.get('_facetFilteredRows.length');
+
+ if(startIndex < 0) {
+ startIndex = 0;
+ }
+
+ return {
+ pageNum: pageNum,
+ totalPages: Math.ceil(totalRecords / rowCount),
+ rowCount: rowCount,
+
+ startIndex: startIndex,
+
+ fromRecord: totalRecords ? startIndex + 1 : 0,
+ toRecord: Math.min(startIndex + rowCount, totalRecords),
+ totalRecords: totalRecords
+ };
+ }),
+ totalPages: Ember.computed.alias("pageDetails.totalPages"), // Adding an alias for backward compatibility
+
+ // Paginate
+ processedRows: Ember.computed('_facetFilteredRows.[]', 'tableDefinition.rowCount', 'tableDefinition.pageNum', function () {
+ var rowCount = this.get('tableDefinition.rowCount'),
+ startIndex = (this.get('tableDefinition.pageNum') - 1) * rowCount;
+ return this.get('_facetFilteredRows').slice(startIndex, startIndex + rowCount);
+ }),
+});
diff --git a/tez-ui/src/main/webapp/app/utils/facet-types.js b/tez-ui/src/main/webapp/app/utils/facet-types.js
new file mode 100644
index 0000000..0a340bb
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/utils/facet-types.js
@@ -0,0 +1,85 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+var facetTypes = {
+ VALUES: {
+ componentName: "em-table-facet-panel-values",
+
+ toClause: function (column, facetConditions) {
+ var values, clauses = [];
+
+ if(facetConditions) {
+ if(Ember.get(facetConditions, "in.length")) {
+ values = facetConditions.in.map(function (value) {
+ value = value.replace(/'/g, "''");
+ return `'${value}'`;
+ });
+ clauses.push(`${column.id} IN (${values})`);
+ }
+
+ if(Ember.get(facetConditions, "notIn.length")) {
+ values = facetConditions.notIn.map(function (value) {
+ value = value.replace(/'/g, "''");
+ return `'${value}'`;
+ });
+ clauses.push(`${column.id} NOT IN (${values})`);
+ }
+
+ return clauses.join(" AND ");
+ }
+ },
+
+ facetRows: function (column, rows) {
+ var facetedDataHash = {},
+ facetedDataArr = [];
+
+ rows.forEach(function (row) {
+ var value = column.getSearchValue(row);
+
+ if(typeof value === "string") {
+ if(!facetedDataHash[value]) {
+ facetedDataHash[value] = {
+ count: 0,
+ value: value
+ };
+ facetedDataArr.push(facetedDataHash[value]);
+ }
+ facetedDataHash[value].count++;
+ }
+
+ });
+
+ if(facetedDataArr.length) {
+ facetedDataArr = facetedDataArr.sort(function (a, b) {
+ return -(a.count - b.count); // Sort in reverse order
+ });
+ return facetedDataArr;
+ }
+ },
+
+ normaliseConditions: function (conditions, data) {
+ if(Ember.get(conditions, "in.length") < data.length) {
+ return conditions;
+ }
+ }
+ },
+};
+
+export default facetTypes;
diff --git a/tez-ui/src/main/webapp/app/utils/sql.js b/tez-ui/src/main/webapp/app/utils/sql.js
new file mode 100644
index 0000000..81db3a0
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/utils/sql.js
@@ -0,0 +1,94 @@
+/*global alasql*/
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import Ember from 'ember';
+
+/*
+ * A wrapper around AlaSQL
+ */
+export default Ember.Object.extend({
+
+ constructQuery: function(clause) {
+ return `SELECT * FROM ? WHERE ${clause}`;
+ },
+
+ validateClause: function (clause, columns) {
+ clause = clause.toString();
+
+ var query = this.constructQuery(this.normaliseClause(clause, columns || [])),
+ valid = false;
+
+ if(clause.match(/\W/g)) { // If it contain special characters including space
+ try {
+ alasql(query, [[{}]]);
+ valid = true;
+ }
+ catch(e) {}
+ }
+
+ return valid;
+ },
+
+ createFacetClause: function (conditions, columns) {
+ if(conditions && columns) {
+ return columns.map(function (column) {
+ if(column.get("facetType")) {
+ return column.get("facetType.toClause")(column, conditions[Ember.get(column, "id")]);
+ }
+ }).filter(clause => clause).join(" AND ");
+ }
+ },
+
+ normaliseClause: function (clause, columns) {
+ clause = clause.toString();
+ columns.forEach(function (column) {
+ var headerTitle = column.get("headerTitle");
+ clause = clause.replace(new RegExp(`"${headerTitle}"`, "gi"), column.get("id"));
+ });
+ return clause;
+ },
+
+ search: function (clause, rows, columns) {
+ clause = this.normaliseClause(clause, columns);
+
+ // Convert into a form that alasql can digest easily
+ var dataSet = rows.map(function (row, index) {
+ var rowObj = {
+ _index_: index
+ };
+
+ columns.forEach(function (column) {
+ if(column.get("enableSearch") && row) {
+ rowObj[column.get("id")] = column.getSearchValue(row);
+ }
+ });
+
+ return rowObj;
+ });
+
+ // Search
+ dataSet = alasql(this.constructQuery(clause), [dataSet]);
+
+ return dataSet.map(function (data) {
+ return rows[data._index_];
+ });
+ }
+
+});
diff --git a/tez-ui/src/main/webapp/app/utils/table-definition.js b/tez-ui/src/main/webapp/app/utils/table-definition.js
new file mode 100644
index 0000000..c304ec4
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/utils/table-definition.js
@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Object.extend({
+
+ recordType: "",
+
+ // Search
+ enableSearch: true,
+ searchText: '',
+ searchType: 'auto', // Can be either of auto, manual, regex OR sql
+ _actualSearchType: "Regex", // Set from em-table-search-ui
+
+ // Faceting
+ enableFaceting: false,
+ facetConditions: null,
+ minFieldsForFilter: 15,
+ minValuesToDisplay: 2,
+ facetValuesPageSize: 10,
+
+ // Sort
+ enableSort: true,
+ sortColumnId: '',
+ sortOrder: '',
+ headerAsSortButton: false,
+
+ // Pagination
+ enablePagination: true,
+ pageNum: 1,
+ rowCount: 10,
+ rowCountOptions: [5, 10, 25, 50, 100],
+
+ enableColumnResize: true,
+ showScrollShadow: false,
+
+ minRowsForFooter: 25,
+
+ columns: [],
+
+ _pageNumResetObserver: Ember.observer('searchText', 'facetConditions', 'rowCount', function () {
+ this.set('pageNum', 1);
+ }),
+
+});
diff --git a/tez-ui/src/main/webapp/bower.json b/tez-ui/src/main/webapp/bower.json
index 56a69f3..cca56d8 100644
--- a/tez-ui/src/main/webapp/bower.json
+++ b/tez-ui/src/main/webapp/bower.json
@@ -1,6 +1,7 @@
{
"name": "tez-ui",
"dependencies": {
+ "alasql": "^0.4.0",
"ember": "2.2.0",
"ember-cli-shims": "0.0.6",
"ember-cli-test-loader": "0.2.1",
diff --git a/tez-ui/src/main/webapp/ember-cli-build.js b/tez-ui/src/main/webapp/ember-cli-build.js
index 7bbc77d..e4217e9 100644
--- a/tez-ui/src/main/webapp/ember-cli-build.js
+++ b/tez-ui/src/main/webapp/ember-cli-build.js
@@ -71,6 +71,7 @@
app.import('bower_components/codemirror/mode/sql/sql.js');
app.import('bower_components/codemirror/mode/pig/pig.js');
app.import('bower_components/codemirror/lib/codemirror.css');
+ app.import('bower_components/alasql/dist/alasql.js');
return app.toTree(new MergeTrees([configEnv, zipWorker, copyFonts]));
};
diff --git a/tez-ui/src/main/webapp/package.json b/tez-ui/src/main/webapp/package.json
index fa80389..ad3aa74 100644
--- a/tez-ui/src/main/webapp/package.json
+++ b/tez-ui/src/main/webapp/package.json
@@ -61,7 +61,6 @@
"phantomjs-prebuilt": "2.1.13"
},
"dependencies": {
- "em-table": "0.11.3",
"em-tgraph": "0.0.14"
}
}
diff --git a/tez-ui/src/main/webapp/tests/integration/components/em-table-cell-test.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-cell-test.js
new file mode 100644
index 0000000..ccf5358
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-cell-test.js
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+import ColumnDefinition from '../../../utils/column-definition';
+
+moduleForComponent('em-table-cell', 'Integration | Component | em table cell', {
+ integration: true
+});
+
+test('Basic rendering test', function(assert) {
+ var columnDefinition = ColumnDefinition.create({
+ id: 'id',
+ contentPath: 'keyA'
+ }),
+ row = Ember.Object.create({
+ keyA: 'valueA',
+ keyB: 'valueB'
+ });
+
+ this.set('columnDefinition', columnDefinition);
+ this.set('row', row);
+ this.render(hbs`{{em-table-cell columnDefinition=columnDefinition row=row}}`);
+
+ assert.equal(this.$().text().trim(), 'valueA');
+});
diff --git a/tez-ui/src/main/webapp/tests/integration/components/em-table-column-test.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-column-test.js
new file mode 100644
index 0000000..96eff7a
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-column-test.js
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('em-table-column', 'Integration | Component | em table column', {
+ integration: true
+});
+
+test('Basic rendering test', function(assert) {
+ this.render(hbs`{{em-table-column}}`);
+
+ assert.equal(this.$().text().trim(), '');
+});
diff --git a/tez-ui/src/main/webapp/tests/integration/components/em-table-facet-panel-test.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-facet-panel-test.js
new file mode 100644
index 0000000..cc0f1f0
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-facet-panel-test.js
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('em-table-facet-panel', 'Integration | Component | em table facet panel', {
+ integration: true
+});
+
+test('Basic renders', function(assert) {
+
+ // Set any properties with this.set('myProperty', 'value');
+ // Handle any actions with this.on('myAction', function(val) { ... });" + EOL + EOL +
+
+ this.render(hbs`{{em-table-facet-panel}}`);
+
+ assert.equal(this.$().text().replace(/\n|\r\n|\r| /g, '').trim(), 'NotAvailable!');
+
+ // Template block usage:" + EOL +
+ this.render(hbs`
+ {{#em-table-facet-panel}}
+ template block text
+ {{/em-table-facet-panel}}
+ `);
+
+ assert.equal(this.$().text().replace(/\n|\r\n|\r| /g, '').trim(), 'NotAvailable!');
+});
diff --git a/tez-ui/src/main/webapp/tests/integration/components/em-table-facet-panel-values-test.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-facet-panel-values-test.js
new file mode 100644
index 0000000..f401a7d
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-facet-panel-values-test.js
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('em-table-facet-panel-values', 'Integration | Component | em table facet panel values', {
+ integration: true
+});
+
+test('Basic render test', function(assert) {
+
+ // Set any properties with this.set('myProperty', 'value');
+ // Handle any actions with this.on('myAction', function(val) { ... });" + EOL + EOL +
+
+ this.set("tmpFacetConditions", {});
+ this.render(hbs`{{em-table-facet-panel-values tmpFacetConditions=tmpFacetConditions}}`);
+
+ assert.ok(this.$().text().trim());
+
+ // Template block usage:" + EOL +
+ this.render(hbs`
+ {{#em-table-facet-panel-values tmpFacetConditions=tmpFacetConditions}}
+ template block text
+ {{/em-table-facet-panel-values}}
+ `);
+
+ assert.ok(this.$().text().trim());
+});
diff --git a/tez-ui/src/main/webapp/tests/integration/components/em-table-header-cell-test.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-header-cell-test.js
new file mode 100644
index 0000000..0c502ce
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-header-cell-test.js
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('em-table-header-cell', 'Integration | Component | em table header cell', {
+ integration: true
+});
+
+test('Basic rendering test', function(assert) {
+ this.render(hbs`{{em-table-header-cell}}`);
+
+ assert.equal(this.$().text().trim(), '');
+});
diff --git a/tez-ui/src/main/webapp/tests/integration/components/em-table-linked-cell-test.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-linked-cell-test.js
new file mode 100644
index 0000000..7553c41
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-linked-cell-test.js
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('em-table-linked-cell', 'Integration | Component | em table linked cell', {
+ integration: true
+});
+
+test('Basic rendering test', function(assert) {
+ this.render(hbs`{{em-table-linked-cell}}`);
+
+ assert.equal(this.$().text().trim(), 'Not Available!');
+});
diff --git a/tez-ui/src/main/webapp/tests/integration/components/em-table-pagination-ui-test.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-pagination-ui-test.js
new file mode 100644
index 0000000..0333d0c
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-pagination-ui-test.js
@@ -0,0 +1,204 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+import DataProcessor from '../../../utils/data-processor';
+import TableDefinition from '../../../utils/table-definition';
+
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('em-table-pagination-ui', 'Integration | Component | em table pagination ui', {
+ integration: true
+});
+
+test('Basic rendering test', function(assert) {
+ var customRowCount = 25,
+ definition = TableDefinition.create({
+ rowCount: customRowCount
+ }),
+ processor;
+
+ Ember.run(function () {
+ processor = DataProcessor.create({
+ tableDefinition: definition,
+ rows: Ember.A([Ember.Object.create()])
+ });
+ });
+
+ this.set('definition', definition);
+ this.set('processor', processor);
+ this.render(hbs`{{em-table-pagination-ui tableDefinition=definition dataProcessor=processor}}`);
+
+ var paginationItems = this.$('li');
+ assert.equal(paginationItems.length, 1);
+ assert.equal($(paginationItems[0]).text().trim(), "1");
+
+ var rowSelection = this.$('select')[0];
+ assert.ok(rowSelection);
+ assert.equal($(rowSelection).val(), customRowCount);
+});
+
+test('No data test', function(assert) {
+ var customRowCount = 2,
+ definition = TableDefinition.create({
+ rowCount: customRowCount
+ }),
+ processor;
+
+ Ember.run(function () {
+ processor = DataProcessor.create({
+ tableDefinition: definition,
+ rows: Ember.A()
+ });
+ });
+
+ this.set('definition', definition);
+ this.set('processor', processor);
+ this.render(hbs`{{em-table-pagination-ui tableDefinition=definition dataProcessor=processor}}`);
+
+ var paginationItems = this.$('li');
+ assert.equal(paginationItems.length, 0);
+});
+
+test('Multiple page test; without first & last', function(assert) {
+ var customRowCount = 2,
+ definition = TableDefinition.create({
+ rowCount: customRowCount
+ }),
+ processor;
+
+ Ember.run(function () {
+ processor = DataProcessor.create({
+ tableDefinition: definition,
+ rows: Ember.A([Ember.Object.create(), Ember.Object.create(), Ember.Object.create()])
+ });
+ });
+
+ this.set('definition', definition);
+ this.set('processor', processor);
+ this.render(hbs`{{em-table-pagination-ui tableDefinition=definition dataProcessor=processor}}`);
+
+ var paginationItems = this.$('li');
+ assert.equal(paginationItems.length, 2);
+ assert.equal($(paginationItems[0]).text().trim(), "1");
+ assert.equal($(paginationItems[1]).text().trim(), "2");
+});
+
+test('Display last test', function(assert) {
+ var customRowCount = 5,
+ definition = TableDefinition.create({
+ rowCount: customRowCount
+ }),
+ processor,
+ rows = [];
+
+ for(var i = 0; i < 100; i++) {
+ rows.push(Ember.Object.create());
+ }
+
+ Ember.run(function () {
+ processor = DataProcessor.create({
+ tableDefinition: definition,
+ rows: Ember.A(rows)
+ });
+ });
+
+ this.set('definition', definition);
+ this.set('processor', processor);
+ this.render(hbs`{{em-table-pagination-ui tableDefinition=definition dataProcessor=processor}}`);
+
+ var paginationItems = this.$('li');
+ assert.equal(paginationItems.length, 6);
+ assert.equal($(paginationItems[0]).text().trim(), "1");
+ assert.equal($(paginationItems[1]).text().trim(), "2");
+ assert.equal($(paginationItems[2]).text().trim(), "3");
+ assert.equal($(paginationItems[3]).text().trim(), "4");
+ assert.equal($(paginationItems[4]).text().trim(), "5");
+ assert.equal($(paginationItems[5]).text().trim(), "Last - 20");
+});
+
+test('Display first test', function(assert) {
+ var customRowCount = 5,
+ definition = TableDefinition.create({
+ pageNum: 20,
+ rowCount: customRowCount
+ }),
+ processor,
+ rows = [];
+
+ for(var i = 0; i < 100; i++) {
+ rows.push(Ember.Object.create());
+ }
+
+ Ember.run(function () {
+ processor = DataProcessor.create({
+ tableDefinition: definition,
+ rows: Ember.A(rows)
+ });
+ });
+
+ this.set('definition', definition);
+ this.set('processor', processor);
+ this.render(hbs`{{em-table-pagination-ui tableDefinition=definition dataProcessor=processor}}`);
+
+ var paginationItems = this.$('li');
+ assert.equal(paginationItems.length, 6);
+ assert.equal($(paginationItems[0]).text().trim(), "First");
+ assert.equal($(paginationItems[1]).text().trim(), "16");
+ assert.equal($(paginationItems[2]).text().trim(), "17");
+ assert.equal($(paginationItems[3]).text().trim(), "18");
+ assert.equal($(paginationItems[4]).text().trim(), "19");
+ assert.equal($(paginationItems[5]).text().trim(), "20");
+});
+
+test('Display first & last test', function(assert) {
+ var customRowCount = 5,
+ definition = TableDefinition.create({
+ pageNum: 10,
+ rowCount: customRowCount
+ }),
+ processor,
+ rows = [];
+
+ for(var i = 0; i < 100; i++) {
+ rows.push(Ember.Object.create());
+ }
+
+ Ember.run(function () {
+ processor = DataProcessor.create({
+ tableDefinition: definition,
+ rows: Ember.A(rows)
+ });
+ });
+
+ this.set('definition', definition);
+ this.set('processor', processor);
+ this.render(hbs`{{em-table-pagination-ui tableDefinition=definition dataProcessor=processor}}`);
+
+ var paginationItems = this.$('li');
+ assert.equal(paginationItems.length, 7);
+ assert.equal($(paginationItems[0]).text().trim(), "First");
+ assert.equal($(paginationItems[1]).text().trim(), "8");
+ assert.equal($(paginationItems[2]).text().trim(), "9");
+ assert.equal($(paginationItems[3]).text().trim(), "10");
+ assert.equal($(paginationItems[4]).text().trim(), "11");
+ assert.equal($(paginationItems[5]).text().trim(), "12");
+ assert.equal($(paginationItems[6]).text().trim(), "Last - 20");
+});
diff --git a/tez-ui/src/main/webapp/tests/integration/components/em-table-progress-cell-test.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-progress-cell-test.js
new file mode 100644
index 0000000..b7eced3
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-progress-cell-test.js
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('em-table-progress-cell', 'Integration | Component | em table progress cell', {
+ integration: true
+});
+
+test('Basic creation test', function(assert) {
+
+ // Set any properties with this.set('myProperty', 'value');
+ // Handle any actions with this.on('myAction', function(val) { ... });" + EOL + EOL +
+
+ this.render(hbs`{{em-table-progress-cell content=0.5}}`);
+
+ assert.equal(this.$().text().trim(), '50%');
+
+ // Template block usage:" + EOL +
+ this.render(hbs`
+ {{#em-table-progress-cell content=0.5}}
+ template block text
+ {{/em-table-progress-cell}}
+ `);
+
+ assert.equal(this.$().text().trim(), '50%');
+});
diff --git a/tez-ui/src/main/webapp/tests/integration/components/em-table-search-ui-test.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-search-ui-test.js
new file mode 100644
index 0000000..0cd2bbc
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-search-ui-test.js
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('em-table-search-ui', 'Integration | Component | em table search ui', {
+ integration: true
+});
+
+test('Basic rendering test', function(assert) {
+ this.render(hbs`{{em-table-search-ui}}`);
+
+ assert.equal(this.$().text().trim(), 'Search');
+});
diff --git a/tez-ui/src/main/webapp/tests/integration/components/em-table-test.js b/tez-ui/src/main/webapp/tests/integration/components/em-table-test.js
new file mode 100644
index 0000000..96baf79
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/components/em-table-test.js
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+import TableDefinition from '../../../utils/table-definition';
+import ColumnDefinition from '../../../utils/column-definition';
+
+moduleForComponent('em-table', 'Integration | Component | em table', {
+ integration: true
+});
+
+test('Basic rendering test', function(assert) {
+ this.render(hbs`{{em-table}}`);
+
+ assert.equal(this.$('.table-message').text().trim(), 'No columns available!');
+});
+
+test('Records missing test', function(assert) {
+ var definition = TableDefinition.create({
+ recordType: "vertex"
+ });
+
+ this.set("columns", [ColumnDefinition.fillerColumn]);
+
+ this.render(hbs`{{em-table columns=columns}}`);
+ assert.equal(this.$('.table-message').text().trim(), 'No records available!');
+
+ this.set("definition", definition);
+ this.render(hbs`{{em-table columns=columns definition=definition}}`);
+ assert.equal(this.$('.table-message').text().trim(), 'No vertices available!');
+});
diff --git a/tez-ui/src/main/webapp/tests/integration/em-table-status-cell-test.js b/tez-ui/src/main/webapp/tests/integration/em-table-status-cell-test.js
new file mode 100644
index 0000000..3148339
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/integration/em-table-status-cell-test.js
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('em-table-status-cell', 'Integration | Component | em table status cell', {
+ integration: true
+});
+
+test('Basic creation test', function(assert) {
+
+ this.render(hbs`{{em-table-status-cell}}`);
+
+ assert.equal(this.$().text().trim(), 'Not Available!');
+
+ // Template block usage:" + EOL +
+ this.render(hbs`
+ {{#em-table-status-cell}}
+ template block text
+ {{/em-table-status-cell}}
+ `);
+
+ assert.equal(this.$().text().trim(), 'Not Available!');
+});
diff --git a/tez-ui/src/main/webapp/tests/unit/utils/column-definition-test.js b/tez-ui/src/main/webapp/tests/unit/utils/column-definition-test.js
new file mode 100644
index 0000000..5ee9a49
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/unit/utils/column-definition-test.js
@@ -0,0 +1,104 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+import ColumnDefinition from '../../../utils/column-definition';
+import { module, test } from 'qunit';
+
+module('Unit | Utility | column definition');
+
+test('Class creation test', function(assert) {
+ assert.ok(ColumnDefinition);
+
+ assert.ok(ColumnDefinition.make);
+ assert.ok(ColumnDefinition.makeFromModel);
+});
+
+test('make - Instance creation test', function(assert) {
+
+ var definition = ColumnDefinition.make({
+ id: "testId"
+ });
+ var definitions = ColumnDefinition.make([{
+ id: "testId 1"
+ },{
+ id: "testId 2"
+ }]);
+
+ // Single
+ assert.ok(definition);
+
+ // Multiple
+ assert.ok(definitions);
+ assert.ok(Array.isArray(definitions));
+ assert.equal(definitions.length, 2);
+});
+
+test('make - Instance creation failure test', function(assert) {
+ assert.throws(function () {
+ ColumnDefinition.make({});
+ });
+});
+
+test('makeFromModel test', function(assert) {
+ var attributes = Ember.Map.create(),
+ DummyModel = Ember.Object.create({
+ attributes: attributes
+ }),
+ getCellContent = function () {},
+ columns;
+
+ attributes.set("attr1", "path1");
+ attributes.set("attr2", "path2");
+ attributes.set("attr3", "path3");
+
+ columns = ColumnDefinition.makeFromModel(DummyModel, {
+ getCellContent: getCellContent
+ });
+
+ assert.equal(columns.length, 3);
+ assert.equal(columns[0].id, "attr1");
+ assert.equal(columns[0].headerTitle, "Attr1");
+ assert.equal(columns[0].contentPath, "attr1");
+ assert.equal(columns[0].getCellContent, getCellContent);
+});
+
+test('Instance test', function(assert) {
+ var definition = ColumnDefinition.make({
+ id: "testId",
+ contentPath: "a.b"
+ });
+ var data = Ember.Object.create({
+ a: {
+ b: 42
+ }
+ });
+
+ assert.ok(definition.getCellContent);
+ assert.ok(definition.getSearchValue);
+ assert.ok(definition.getSortValue);
+
+ assert.equal(definition.id, "testId");
+ assert.equal(definition.headerTitle, "Not Available!");
+ assert.equal(definition.minWidth, "150px");
+ assert.equal(definition.contentPath, "a.b");
+
+ assert.equal(definition.getCellContent(data), 42);
+ assert.equal(definition.getSearchValue(data), 42);
+ assert.equal(definition.getSortValue(data), 42);
+});
diff --git a/tez-ui/src/main/webapp/tests/unit/utils/data-processor-test.js b/tez-ui/src/main/webapp/tests/unit/utils/data-processor-test.js
new file mode 100644
index 0000000..58f52dd
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/unit/utils/data-processor-test.js
@@ -0,0 +1,137 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+import DataProcessor from '../../../utils/data-processor';
+import ColumnDefinition from '../../../utils/column-definition';
+import { module, test } from 'qunit';
+
+module('Unit | Utility | data processor');
+
+test('Class creation test', function(assert) {
+ assert.ok(DataProcessor);
+});
+
+test('Instance default test', function(assert) {
+ var processor;
+
+ Ember.run(function () {
+ processor = DataProcessor.create({
+ tableDefinition: Ember.Object.create(),
+ startSearch: function () {
+ // Test Search
+ },
+ startSort: function () {
+ // Test Sort
+ }
+ });
+ });
+
+ assert.ok(processor);
+ assert.equal(processor.get('isSorting'), false);
+ assert.equal(processor.get('isSearching'), false);
+
+ assert.ok(processor._searchObserver);
+ assert.ok(processor._sortObserver);
+ assert.ok(processor.startSearch);
+ assert.ok(processor.startSort);
+ assert.ok(processor.compareFunction);
+ assert.ok(processor.totalPages);
+ assert.ok(processor.processedRows);
+});
+
+test('compareFunction test', function(assert) {
+ var processor;
+
+ Ember.run(function () {
+ processor = DataProcessor.create({
+ tableDefinition: Ember.Object.create(),
+ startSearch: function () {},
+ startSort: function () {}
+ });
+ });
+
+ assert.equal(processor.compareFunction(1, 1), 0);
+ assert.equal(processor.compareFunction(1, 2), -1);
+ assert.equal(processor.compareFunction(2, 1), 1);
+
+ assert.equal(processor.compareFunction("a", "a"), 0);
+ assert.equal(processor.compareFunction("a", "b"), -1);
+ assert.equal(processor.compareFunction("b", "a"), 1);
+
+ assert.equal(processor.compareFunction(null, null), -1);
+ assert.equal(processor.compareFunction(1, null), 1);
+ assert.equal(processor.compareFunction(null, 2), -1);
+ assert.equal(processor.compareFunction("a", null), 1);
+ assert.equal(processor.compareFunction(null, "b"), -1);
+
+ assert.equal(processor.compareFunction(undefined, undefined), -1);
+ assert.equal(processor.compareFunction(1, undefined), 1);
+ assert.equal(processor.compareFunction(undefined, 2), -1);
+ assert.equal(processor.compareFunction("a", undefined), 1);
+ assert.equal(processor.compareFunction(undefined, "b"), -1);
+});
+
+test('startSearch test', function(assert) {
+ var processor,
+ runLater = Ember.run.later;
+
+ assert.expect(3);
+
+ Ember.run.later = function (callback) {
+ callback();
+ assert.equal(processor.get("_searchedRows.length"), 2);
+ assert.equal(processor.get("_searchedRows.0.foo"), "Foo1");
+ assert.equal(processor.get("_searchedRows.1.foo"), "Foo12");
+
+ Ember.run.later = runLater; // Reset
+ };
+
+ Ember.run(function () {
+ processor = DataProcessor.create({
+ tableDefinition: Ember.Object.create({
+ searchText: "foo1",
+ columns: [ColumnDefinition.create({
+ id: "foo",
+ contentPath: 'foo'
+ }), ColumnDefinition.create({
+ id: "bar",
+ contentPath: 'bar'
+ })]
+ }),
+ startSort: function () {
+ // Test Sort
+ },
+ _sortedRows: [Ember.Object.create({
+ foo: "Foo1",
+ bar: "Bar1"
+ }), Ember.Object.create({
+ foo: "Foo12",
+ bar: "Bar2"
+ }), Ember.Object.create({
+ foo: "Foo3",
+ bar: "Bar3"
+ }), Ember.Object.create({
+ foo: "Foo4",
+ bar: "Bar4"
+ })],
+ });
+ });
+
+});
diff --git a/tez-ui/src/main/webapp/tests/unit/utils/facet-types-test.js b/tez-ui/src/main/webapp/tests/unit/utils/facet-types-test.js
new file mode 100644
index 0000000..f3af952
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/unit/utils/facet-types-test.js
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import facetTypes from '../../../utils/facet-types';
+import { module, test } from 'qunit';
+
+module('Unit | Utility | facet types');
+
+test('Basic creation test', function(assert) {
+ assert.ok(facetTypes);
+
+ assert.ok(facetTypes.VALUES);
+});
diff --git a/tez-ui/src/main/webapp/tests/unit/utils/sql-test.js b/tez-ui/src/main/webapp/tests/unit/utils/sql-test.js
new file mode 100644
index 0000000..7aed218
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/unit/utils/sql-test.js
@@ -0,0 +1,90 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import SQL from '../../../utils/sql';
+import ColumnDefinition from '../../../utils/column-definition';
+import { module, test } from 'qunit';
+
+module('Unit | Utility | sql');
+
+test('Class creation test', function(assert) {
+ var sql = SQL.create();
+
+ assert.ok(sql.constructQuery);
+ assert.ok(sql.validateClause);
+ assert.ok(sql.normaliseClause);
+ assert.ok(sql.search);
+});
+
+test('constructQuery test', function(assert) {
+ var sql = SQL.create();
+
+ assert.equal(sql.constructQuery("x = y"), "SELECT * FROM ? WHERE x = y");
+});
+
+test('validateClause test', function(assert) {
+ var sql = SQL.create();
+
+ assert.ok(sql.validateClause("x = y"));
+ assert.ok(sql.validateClause("x = y AND a = b"));
+ assert.ok(sql.validateClause("(x = y OR y = z) AND a = b"));
+ assert.ok(sql.validateClause("x BETWEEN 1 AND 2"));
+
+ assert.notOk(sql.validateClause("foo"));
+ assert.notOk(sql.validateClause("foo bar"));
+ assert.notOk(sql.validateClause("^[a-z0-9_-]{3,16}$"));
+ assert.notOk(sql.validateClause("^[a-z0-9_-]{6,18}$"));
+ assert.notOk(sql.validateClause("^[a-z0-9-]+$"));
+});
+
+test('normaliseClause test', function(assert) {
+ var sql = SQL.create(),
+ column = ColumnDefinition.create({
+ headerTitle: "Column Header",
+ id: "columnID",
+ contentPath: "col"
+ });
+
+ assert.equal(sql.normaliseClause('"Column Header" = value', [column]), "columnID = value");
+ assert.equal(sql.normaliseClause('"Another Column Header" = value', [column]), '"Another Column Header" = value');
+});
+
+test('search test', function(assert) {
+ var sql = SQL.create(),
+ data = [{
+ colA: "x1",
+ colB: "y1"
+ }, {
+ colA: "x2",
+ colB: "y2"
+ }, {
+ colA: "x1",
+ colB: "y3"
+ }],
+ columns = [ColumnDefinition.create({
+ headerTitle: "Column A",
+ id: "colA",
+ contentPath: "colA"
+ })];
+
+ var result = sql.search('"Column A" = "x1"', data, columns);
+
+ assert.equal(result.length, 2);
+ assert.equal(result[0].colB, "y1");
+ assert.equal(result[1].colB, "y3");
+});
diff --git a/tez-ui/src/main/webapp/tests/unit/utils/table-definition-test.js b/tez-ui/src/main/webapp/tests/unit/utils/table-definition-test.js
new file mode 100644
index 0000000..234994b
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/unit/utils/table-definition-test.js
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import TableDefinition from '../../../utils/table-definition';
+import { module, test } from 'qunit';
+
+module('Unit | Utility | table definition');
+
+test('Class creation test', function(assert) {
+ assert.ok(TableDefinition);
+});
+
+test('Default instance test', function(assert) {
+ var definition = TableDefinition.create();
+
+ assert.ok(definition);
+
+ assert.equal(definition.pageNum, 1);
+ assert.equal(definition.rowCount, 10);
+ assert.equal(definition.minRowsForFooter, 25);
+});
+
+test('Page-num reset test', function(assert) {
+ var definition = TableDefinition.create();
+
+ assert.equal(definition.pageNum, 1);
+
+ definition.set("pageNum", 5);
+ assert.equal(definition.pageNum, 5);
+
+ definition.set("searchText", "x");
+ assert.equal(definition.pageNum, 1);
+
+ definition.set("pageNum", 5);
+ definition.set("rowCount", 5);
+ assert.equal(definition.pageNum, 1);
+});
diff --git a/tez-ui/src/main/webapp/yarn.lock b/tez-ui/src/main/webapp/yarn.lock
index 00250e8..660ac80 100644
--- a/tez-ui/src/main/webapp/yarn.lock
+++ b/tez-ui/src/main/webapp/yarn.lock
@@ -1391,16 +1391,6 @@
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
-em-table@0.11.3:
- version "0.11.3"
- resolved "https://registry.yarnpkg.com/em-table/-/em-table-0.11.3.tgz#20e605cc3814214e644199399a2383cee8d23eeb"
- dependencies:
- ember-cli-htmlbars "^1.0.1"
- ember-cli-less "^1.4.0"
- source-map "^0.5.6"
- optionalDependencies:
- phantomjs-prebuilt "2.1.13"
-
em-tgraph@0.0.14:
version "0.0.14"
resolved "https://registry.yarnpkg.com/em-tgraph/-/em-tgraph-0.0.14.tgz#4d48b911760f85dec41904e4056ec52542391cc1"