"use strict";
/**
* Licensed Materials - Property of IBM
* IBM Cognos Products: Cognos Analytics
* Copyright IBM Corp. 2015, 2018
* US Government Users Restricted Rights -
* Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*/
define(['doT', 'q', 'jquery', 'underscore', 'bi/glass/app/ContentView', 'bi/commons/utils/BidiUtil', 'bacontentnav/utils/WidgetNavigator', 'bi/admin/nls/StringResource', 'bi/admin/common/actions/AddInputRow', 'text!bi/admin/common/ui/listview/templates/ListViewTemplate.html', 'text!bi/admin/common/ui/listview/templates/EmptyListViewTemplate.html', 'datatables', 'bi/commons/utils/Utils', 'bi/glass/utils/ClassFactory'], function (dot, Q, $, _, ContentView, BidiUtil, WidgetNavigator, StringResource, AddInputRow, ListViewTemplate, EmptyListViewTemplate, datatables, Utils, ClassFactory) {
'use strict'; //NOSONAR: meant to be strict
var ListView = ContentView.extend({
aSelectedRows: [],
multiSelect: true,
touchMultiSelectEnabled: false,
init: function init(options) {
ListView.inherited('init', this, arguments);
if (options !== undefined) {
this.accessibleLabel = options.accessibleLabel || "";
this.formatContent = options.formatContent || true;
} else {
this.accessibleLabel = "";
this.formatContent = true;
}
$.extend(this, options);
this.aSelectedRows = [];
},
createColumnObject: function createColumnObject(columnSpec, ColumnModule) {
return new ColumnModule($.extend(columnSpec, {
'listControl': this,
'glassContext': this.glassContext
}));
},
isMultitouchActive: function isMultitouchActive() {
return this.multiSelect;
},
_createColumns: function _createColumns() {
return Promise.all(_.map(this.dataAdaptor.getColumnSpecs(), function (colSpec, index) {
if (colSpec.type || colSpec.module) {
var path = colSpec.module || 'bi/content_apps/common/ui/list_columns/' + colSpec.type;
return ClassFactory.instantiate(path, _.extend({}, colSpec, {
'listControl': this,
'glassContext': this.glassContext
})).then(function (col) {
delete col.type;
return col.getSpec().then(function (spec) {
_.extend(col, spec);
col.aTargets = [index];
col.bSortable = !!colSpec.sortable;
if (colSpec.width) {
col.sWidth = colSpec.width;
}
return col;
});
}.bind(this));
} else {
return null;
}
}.bind(this)));
},
setFocus: function setFocus() {
var $firstFocusEl = this.$el.find("td[tabindex='0']");
if ($firstFocusEl.length === 0) {
return false;
} else {
$firstFocusEl.focus();
return true;
}
},
clearShowWorking: function clearShowWorking() {
this.$el.find("tbody tr[role='row']").remove();
this.showWorking();
},
showWorking: function showWorking() {
var loadingAnimation = Utils.getLoadingAnimation(1);
var $container = $('
');
$container.append(loadingAnimation);
this.$el.append($container);
},
hideWorking: function hideWorking() {
var workingEl = this.$el.find(".bi-admin-working");
$(workingEl).remove();
},
render: function render() {
this.showWorking();
return Promise.all([this._createColumns(), this.dataAdaptor.getRows()]).then(function (resp) {
this.$el.empty();
var columns = resp[0];
var rows = resp[1].rows;
if (this.dataAdaptor.checkboxSelection && this.contentView.getSearchTerm) {
var text = this.contentView.getSearchTerm().toLowerCase();
rows = _.filter(rows, function (row) {
return (row['defaultName'] || "").toLowerCase().indexOf(text) !== -1;
});
}
var sHtml;
this.hideWorking();
if (rows.length > 0) {
sHtml = dot.template(ListViewTemplate)({
sortable: this.dataAdaptor.supportSorting,
accessibleLabel: this.accessibleLabel,
tableCaption: StringResource.get('listViewTableCaption'),
columns: columns
});
this.$el.append(sHtml);
return this._renderTable(columns, rows).then(function () {
this.widgetKeyController = new WidgetNavigator({
$el: this.$el.find(".listControl"),
focusClass: "contentListFocusable",
fCallBack: function fCallBack() {}
});
}.bind(this));
} else {
sHtml = '' + StringResource.get('noEntries') + '
';
this.$el.append(sHtml);
}
}.bind(this));
},
resize: function resize() {
var sh = this._calcYBound();
var sb = this.$el.find('.dataTables_scrollBody');
if (sb) {
sb.height(sh);
}
},
reload: function reload(append) {
var deferred = Q.defer();
this.showWorking();
if (!this._dTable) {
this.render().done(function () {
this.hideWorking();
deferred.resolve();
}.bind(this));
} else {
if (this._reloading) {
return;
}
this.waitForReload = true;
this._reloading = true;
if (!append) {
this._dTable.clear();
this._dTable.draw();
}
this.dataAdaptor.getRows().done(function (result) {
var rows = result.rows;
if (!append && rows.length === 0) {
var emptyHTML = '' + StringResource.get('noEntries') + '
';
this.$el.append(emptyHTML);
} else {
this.$el.find(".emptyTableContent").remove();
this._dTable.rows.add(result.rows).draw(false);
this._setTextDirection();
this._clearRows(true);
this.resize();
}
this._reloading = false;
this.hideWorking();
setTimeout(function () {
delete this.waitForReload;
}.bind(this), 1000);
deferred.resolve();
}.bind(this));
}
return deferred.promise;
},
_sortListener: function _sortListener() {},
filter: function filter(text, propertyName) {
if (this.dataAdaptor.checkboxSelection) {
this._clearRows(true);
}
this.removeEmptyTableMessage();
var rows = this.dataAdaptor.getFilteredRows();
if (this._dTable && rows) {
propertyName = propertyName || 'defaultName';
text = text.toLowerCase();
var filteredRows = _.filter(rows, function (row) {
return (row[propertyName] || "").toLowerCase().indexOf(text) !== -1;
});
this._dTable.rows().remove();
this._dTable.rows.add(filteredRows).draw(false);
if (filteredRows.length < 1) {
this._noResults();
} else {
this._setTextDirection();
}
}
},
_renderTable: function _renderTable(columns, rows) {
//NOSONAR
var deferred = Q.defer();
var xbound = this.$el.width();
var datatableSettings = {
'data': rows,
'sScrollY': this._calcYBound(),
'sScrollX': xbound,
'sDom': 'rtS',
'bServerSide': false,
'bFilter': false,
'bInfo': false,
'bAutoWidth': false,
'bPaginate': false,
'bDeferRender': true,
'orderClasses': false,
'bSort': this.dataAdaptor.isServerSorting(),
'asStripeClasses': [''],
'aoColumnDefs': columns,
'fnInitComplete': function (oSettings) {
this.isInitialized = true;
var tabEl = $(oSettings.nScrollHead).find("table");
this._scrollNode = this.$el.find('.dataTables_scrollBody');
var lab = tabEl.attr("aria-label");
tabEl.removeAttr("aria-label");
tabEl.attr("aria-labelledby", "adminTab_tableHeaderLab1 adminTab_tableHeaderLab2 ");
var hiddenDiv1 = $('');
var hiddenDiv2 = $('');
tabEl.parent().append(hiddenDiv1);
tabEl.parent().append(hiddenDiv2);
this._scrollNode.bind('scroll', this._onScroll.bind(this));
deferred.resolve();
}.bind(this),
'fnDrawCallback': function (oSettings) {
this._drawCallback(oSettings);
}.bind(this)
};
var defaultOrder = this.dataAdaptor.sortOrder;
if (datatableSettings.bSort && defaultOrder) {
datatableSettings.aaSorting = defaultOrder;
}
var $listTable = this.$el.find('table.listControl.bi-admin-table-list'); // cache all indices the selected tr elements
var aSelectedIndices = this._cachedSelectedRows();
this._dTable = $listTable.DataTable(datatableSettings);
$listTable.attr("role", "application");
this._reApplySelections(aSelectedIndices); // process row events
this._dTable.on('click', 'tr', this.handleClick.bind(this));
this._dTable.on('keydown', 'tr', this._handlekeydown.bind(this));
this._dTable.on('hold', this.handleHoldEvent.bind(this));
var self = this; // Locate checkbox header (if supported)
if (this.dataAdaptor.checkboxSelection) {
this._renderSelectAllCheckBox();
} // Customize datatable's sorting behavior
if (this.dataAdaptor.supportSorting && !datatableSettings.bSort) {
var $headers = this.$el.find("thead th");
$headers.each(function () {
var $h = $(this);
var index = $h.attr('colindex') * 1;
var col = columns[index];
if (col && col.sortable) {
$h.removeClass('sorting_disabled');
if (defaultOrder && index === defaultOrder[0]) {
$h.addClass('sorting_' + defaultOrder[1]);
}
$h.on('primaryaction', function () {
var asc = $h.hasClass('sorting_asc');
var nextOrder = asc ? 'desc' : 'asc';
$headers.removeClass('sorting_asc sorting_desc');
$h.addClass('sorting_' + nextOrder);
self.dataAdaptor.setOrder(index, nextOrder);
self.reload();
});
}
});
}
this.$el.find('div.dataTables_scrollBody').on('scroll', function () {
if ($(this).scrollTop() + $(this).innerHeight() >= this.scrollHeight && self.dataAdaptor.hasMore()) {
self.reload(true);
}
});
this._setTextDirection();
return deferred.promise;
},
_isClickableCol: function _isClickableCol(evt) {
var toElem = evt.relatedTarget || evt.toElement || evt.target;
return $(toElem).hasClass('nameColumnDiv');
},
_handlekeydown: function _handlekeydown(evt) {
if (evt.which === 13 || evt.which === 32) {
this.handleClick(evt);
}
},
handleClick: function handleClick(evt) {
//NOSONAR
var trNode = this._findRowNode(evt.currentTarget);
if ($(evt.currentTarget).hasClass("ellipsesButton")) {
if (this.aSelectedRows.length === 0) {
this._selectRow(trNode);
} else if (this.aSelectedRows.length === 1) {
var match = false;
_.each(this.aSelectedRows, function (oRow) {
if ($(oRow)[0].rowIndex === trNode.rowIndex) {
match = true;
}
});
if (!match) {
this._clearRows(true);
this._selectRow(trNode);
}
}
if (this._isClickableCol(evt)) {
this._handleSingleSelect(trNode, evt);
} else {
this._handleContextMenu(evt);
}
return false;
} // click still fired by browser for hold events on iPad, this is to catch and nullify the click
if (evt.type === 'click' && this.multiSelectEvent && this.multiSelectEvent.type === "hold") {
return;
}
if (evt.which === 3 && (evt.shiftKey || evt.ctrlKey || evt.metaKey || $(trNode).hasClass('selected'))) {
return false;
} // process click
if (evt.shiftKey && this.multiSelect) {
// get last selected row
var oLastSelectedRow = _.last(this.aSelectedRows);
var iLastSelectedRowIndex = 0;
if (oLastSelectedRow) {
iLastSelectedRowIndex = oLastSelectedRow.rowIndex;
} // clear all rows of 'selected' class
this._clearRows(true);
var iEndRow = Math.max(iLastSelectedRowIndex, trNode.rowIndex);
var iStartRow = Math.min(iLastSelectedRowIndex, trNode.rowIndex); // Get filtered table rows
var aNodes = this._dTable.$('tr', {
"filter": "applied"
});
var i;
for (i = iStartRow; i <= iEndRow; i = i + 1) {
this._handleMultiSelect($(aNodes[i - 1]), evt.target);
}
} // process click
else if ((evt.ctrlKey || evt.metaKey) && this.multiSelect || evt.type === 'tap' && this.touchMultiSelectEnabled || this.dataAdaptor.checkboxSelection && evt.currentTarget.classList.contains('admin-checkbox')) {
this._handleMultiSelect(trNode, evt.target);
} // process normal mouse click
else if (this._handleSingleSelect(trNode, evt) === false) {
return false;
}
},
_setTextDirection: function _setTextDirection() {
var $divName = this.$el.find('div.nameColumnDiv');
$divName.each(function () {
$(this).attr('dir', BidiUtil.resolveBaseTextDir(this.innerHTML));
});
},
_noResults: function _noResults() {
var emptyHTML = '' + StringResource.get('noEntries') + '
';
this.$el.append(emptyHTML);
},
_handleMultiSelect: function _handleMultiSelect(trNode, target, fromCheckbox) {
// toggle 'selected' class of current row
if (!($(trNode).hasClass('selected') && $(target).hasClass('forceRowSelection'))) {
$(trNode).toggleClass('selected');
if (this.dataAdaptor.checkboxSelection && !fromCheckbox) {
var $checkbox = $(trNode).find('.admin-checkbox');
$checkbox.prop('checked', !$checkbox.prop('checked'));
}
} // maintain the array of selected rows
var iIndex = this.aSelectedRows.indexOf(trNode);
if (iIndex > -1) {
if (!$(target).hasClass('forceRowSelection')) {
this._updateSelectedRows('-', iIndex);
}
} else {
this._updateSelectedRows('+', trNode);
}
},
_handleSingleSelect: function _handleSingleSelect(trNode, evt) {
if (this.aSelectedRows.length === 1 && trNode.rowIndex === this.aSelectedRows[0].rowIndex) {
if (this.deselectCallback) {
this.deselectCallback();
}
$(trNode).removeClass('selected');
this._checkRow(trNode, true);
this._updateSelectedRows('c');
}
this._selectSingleRow(trNode, evt);
},
getActionPayload: function getActionPayload() {
//method required by content apps column class...need to return a payload I guess
var deferred = Q.defer(); //don't want the content apps column to do a thing....
deferred.reject();
return deferred.promise;
},
_selectSingleRow: function _selectSingleRow(trNode, evt) {
this._clearRows(true);
this._selectRow(trNode); //trigger event when row is highlighted
this.$el.trigger('com.ibm.admin.listItemHighlighted', this.getSelectedObjects()[0]);
if (this.singleSelectCallback && this._isClickableCol(evt)) {
this.singleSelectCallback(this.getSelectedObjects()[0]);
}
},
_handleContextMenu: function _handleContextMenu(evt) {
var position = {};
if (evt.pageX === undefined || evt.gesture === undefined || evt.gesture.center === undefined || evt.gesture.center.pageX === undefined) {
position = $(evt.target).offset();
} else {
position.left = evt.pageX || evt.gesture.center.pageX;
position.top = evt.pageY || evt.gesture.center.pageY;
}
var data = {
'position': {
"pageX": position.left,
"pageY": position.top
},
'selectedObject': this.getSelectedObjects()
};
if (this.contextMenuCallback) {
this.contextMenuCallback(data);
}
},
handleHoldEvent: function handleHoldEvent(evt) {
this._clearRows(true);
this.touchMultiSelectEnabled = true;
var node = this._findRowNode(evt.target.parentNode);
$(node).addClass('selected');
this._updateSelectedRows('+', node);
this.multiSelectEvent = evt;
},
_findRowNode: function _findRowNode(node) {
while (node.nodeName.toLowerCase() !== 'tr') {
node = node.parentNode;
}
return node;
},
/**
Clear all selected rows in dataTable
**/
_clearRows: function _clearRows(isFromAdmin) {
if (isFromAdmin) {
if (this.dataAdaptor.checkboxSelection) {
var $checkboxHeader = this.$el.find('thead th .admin-header-checkbox input');
$checkboxHeader.prop('checked', false);
}
_.each(this.aSelectedRows, function (oRow) {
$(oRow).removeClass('selected');
this._checkRow(oRow, false);
}.bind(this));
this._updateSelectedRows('c'); // clear button and counter for multi-select on touch devices
this.touchMultiSelectEnabled = false;
}
},
/**
Returns an array of objects that are being represented by the selected rows
**/
getSelectedObjects: function getSelectedObjects() {
var i;
var rowObjects = [];
for (i = 0; i < this.aSelectedRows.length; i += 1) {
var obj = this._dTable.row(this.aSelectedRows[i]).data();
rowObjects.push(obj);
}
return rowObjects;
},
/**
Select a row in dataTable
**/
_selectRow: function _selectRow(trNode) {
$(trNode).addClass('selected');
this._updateSelectedRows('+', trNode);
if (this.dataAdaptor.checkboxSelection) {
var $checkbox = $(trNode).find('.admin-checkbox');
$checkbox.prop('checked', true);
}
},
_formatContentHelper: function _formatContentHelper(oSettings, i, rowData) {
//add in the role of gridcell to the td's
if (rowData.anCells) {
for (var cellIndex = 0; cellIndex < rowData.anCells.length; cellIndex += 1) {
var $cell = $(rowData.anCells[cellIndex]);
var scope = oSettings.aoColumns[cellIndex].scope;
if (scope === 'row') {
$cell.attr("role", "rowheader");
} else {
$cell.attr("role", "gridcell");
}
$cell.attr("tabindex", "-1");
}
}
this._processColumnWeights(oSettings);
if (this.formatContent && rowData.anCells && rowData.anCells[i]) {
oSettings.aoColumns[i].formatContent(rowData.anCells[i]);
}
},
_drawCallback: function _drawCallback(oSettings) {
for (var i = 0; i < oSettings.aoColumns.length; i++) {
if (oSettings.aoColumns[i].formatContent) {
oSettings.aoData.forEach(this._formatContentHelper.bind(this, oSettings, i));
}
}
if (this.widgetKeyController) {
this.widgetKeyController.setInitialTabIndex();
}
},
_processColumnWeights: function _processColumnWeights(oSettings) {
var weightSum = 0;
var percentSum = 0;
oSettings.aoColumns.forEach(function (column) {
if (column.weight) {
weightSum += column.weight;
} else if (column.sWidth && column.sWidth.slice(-1) === '%') {
percentSum += parseInt(column.sWidth.slice(0, -1), 10);
}
});
oSettings.aoColumns.forEach(function (column) {
if (column.weight) {
column.sWidth = Math.floor(column.weight / weightSum * (100 - percentSum)) + '%';
}
});
},
_calcYBound: function _calcYBound() {
var headerHeight = this.$el.find('th').height();
if (this.$el.height() - headerHeight < headerHeight) {
return "70%";
} else {
return this.$el.height() - headerHeight;
}
},
addInput: function addInput(type, pid, accountExplorer, listAdaptor) {
//running multiple instances of AddInputRow simultaneously leads to unnecessary bugs,
//therefore, only manage one instance at a time, instances will terminate when finished
if (this.activeInputForm === null) {
this.activeInputForm = new AddInputRow({
'oListControl': this,
'glassContext': this.glassContext,
'data': this._dTable.rows().data(),
'accountExplorer': accountExplorer,
'listAdaptor': listAdaptor
});
this.activeInputForm.execute(type, pid);
}
},
insertToTable: function insertToTable(rowObj) {
this._dTable.rows.add([rowObj]).draw(true);
},
removeEmptyTableMessage: function removeEmptyTableMessage() {
this.$el.find('.emptyTableContent').remove();
},
adjustScrollForPageDown: function adjustScrollForPageDown() {
var $sb = $(this.$el.find('.dataTables_scrollBody'));
$sb.scrollTop($sb.scrollTop() + 50);
},
_clearTable: function _clearTable() {
this._dTable.clear().draw();
},
setPaging: function setPaging(isPaged) {
this.pagingOn = isPaged;
},
_onScroll: function _onScroll(event) {
if (!this.scrollIgnore && !this.waitForReload) {
var $target = $(event.target); // If we've scrolled to the bottom
if ($target.scrollTop() + $target.innerHeight() >= $target[0].scrollHeight - 20) {
$(this).trigger("nextPage", event);
this.scrollIgnore = true;
setTimeout(function () {
delete this.scrollIgnore;
}.bind(this), 500);
} else if ($target.scrollTop() === 0) {
$(this).trigger("previousPage", event);
this.scrollIgnore = true;
setTimeout(function () {
delete this.scrollIgnore;
}.bind(this), 500);
}
}
},
_isShortTable: function _isShortTable() {
var listContainer = this.$el.find('.ca-listContainer');
return listContainer.height() < 200;
},
_updateSelectedRows: function _updateSelectedRows(action, options) {
switch (action) {
case '-':
var removeAtIndex = this.aSelectedRows.indexOf(options);
if (removeAtIndex !== -1) {
this.aSelectedRows.splice(removeAtIndex, 1);
}
break;
case '+':
this.aSelectedRows.push(options);
break;
case 'c':
this.aSelectedRows = [];
break;
case '=':
this.aSelectedRows = options;
break;
default:
break;
}
if (this.onSelectionChange) {
this.onSelectionChange();
}
},
_cachedSelectedRows: function _cachedSelectedRows() {
// cache all indices the selected tr elements
var aSelectedIndices = [];
if (this._dTable && this.aSelectedRows.length > 0) {
var selectedRows = this._dTable.$('tr.selected');
for (var index = 0; index < selectedRows.length; ++index) {
aSelectedIndices.push($(selectedRows[index]).index());
}
}
return aSelectedIndices;
},
_reApplySelections: function _reApplySelections(aSelectedIndices) {
this.aSelectedRows = []; // reapply previous selections
for (var index = 0; index < aSelectedIndices.length; ++index) {
var trNode = this._dTable.$('tr')[aSelectedIndices[index]];
trNode.classList.add('selected');
this._checkRow(trNode, true);
this._updateSelectedRows('+', trNode);
}
},
_renderSelectAllCheckBox: function _renderSelectAllCheckBox() {
var $checkboxHeader = this.$el.find('thead th .admin-header-checkbox:not(:has(input))');
$checkboxHeader.html('');
$checkboxHeader.find('input').click(this._toggleSelectAll.bind(this));
},
_toggleSelectAll: function _toggleSelectAll(evt) {
if (evt.currentTarget.checked) {
var rows = this._dTable.$('tr:not(.selected)');
for (var index = 0; index < rows.length; ++index) {
this._selectRow(rows[index]);
}
} else {
this._clearRows(true);
}
},
_deselectRow: function _deselectRow(trNode) {
if (this.deselectCallback) {
this.deselectCallback();
}
$(trNode).removeClass('selected');
this._checkRow(trNode, false);
this._updateSelectedRows('-', trNode);
},
_checkRow: function _checkRow(trNode, check) {
if (this.dataAdaptor.checkboxSelection) {
var $checkbox = $(trNode).find('.admin-checkbox');
$checkbox.prop('checked', check);
}
}
});
return ListView;
});