
scout.table.columns.Column.js Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2014-2015 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
scout.Column = function() {
this.minWidth = scout.Column.DEFAULT_MIN_WIDTH;
this.showSeparator = true; // currently a UI-only property, defaults to true
this.filterType = 'TextColumnUserFilter';
this.comparator = scout.comparators.TEXT;
};
scout.Column.DEFAULT_MIN_WIDTH = 50;
scout.Column.NARROW_MIN_WIDTH = 32; // for columns without text (icon, check box)
scout.Column.prototype.init = function(model) {
this.session = model.session;
// Copy all properties from model to this
$.extend(this, model);
// Fill in the missing default values
scout.defaultValues.applyTo(this);
// InitialWidth is only sent if it differs from width
if (this.initialWidth === undefined) {
this.initialWidth = scout.nvl(this.width, 0);
}
if (this.aggregationFunction) {
this.setAggregationFunction(this.aggregationFunction);
}
};
/**
* Converts the cell if it is of type string to an object with
* a property 'text' with the original value.
*
* Example:
* 'My Company' --> { text: 'MyCompany'; }
*
* @see JsonCell.java
*/
scout.Column.prototype.initCell = function(cell) {
if (typeof cell !== 'object') {
cell = {
text: cell
};
}
this._initCell(cell);
// when cell doesn't define horiz. alignment - use value from column
if (cell.horizontalAlignment === undefined) {
cell.horizontalAlignment = this.horizontalAlignment;
}
scout.defaultValues.applyTo(cell, 'Cell');
return cell;
};
/**
* Override this method to impl. type specific init cell behavior.
* The default impl. does nothing.
*/
scout.Column.prototype._initCell = function(cell) {
// NOP
};
scout.Column.prototype.buildCellForRow = function(row) {
var cell = this.table.cell(this, row);
return this.buildCell(cell, row);
};
scout.Column.prototype.buildCell = function(cell, row) {
var text = cell.text || '';
if (!cell.htmlEnabled) {
if (!cell.encodedText) {
// Encode text and cache it, encoding is expensive
cell.encodedText = scout.strings.encode(text);
}
text = cell.encodedText;
if (this.table.multilineText) {
text = scout.strings.nl2br(text, false);
}
}
var iconId = cell.iconId;
var icon = this._icon(iconId, !!text) || '';
var cssClass = this._cellCssClass(cell);
var style = this._cellStyle(cell);
if (cell.errorStatus) {
row.hasError = true;
}
var content;
if (!text && !icon) {
// If every cell of a row is empty the row would collapse, using nbsp makes sure the row is as height as the others even if it is empty
content = ' ';
cssClass = scout.strings.join(' ', cssClass, 'empty');
} else {
content = icon + text;
}
var cellHtml = '';
cellHtml += '';
if (scout.device.tableAdditionalDivRequired) {
cellHtml += '';
// same calculation in scout.Table.prototype.resizeColumn
}
cellHtml += content;
if (scout.device.tableAdditionalDivRequired) {
cellHtml += '';
}
cellHtml += '';
return cellHtml;
};
scout.Column.prototype._icon = function(iconId, hasText) {
var cssClass, icon;
if (!iconId) {
return;
}
cssClass = 'table-cell-icon';
if (hasText) {
cssClass += ' with-text';
}
icon = scout.icons.parseIconId(iconId);
if (icon.isFontIcon()) {
cssClass += ' font-icon';
return '' + icon.iconCharacter + '';
} else {
cssClass += ' image-icon';
return '
';
}
};
scout.Column.prototype._cellCssClass = function(cell) {
var cssClass = 'table-cell';
if (this.mandatory) {
cssClass += ' mandatory';
}
if (!this.table.multilineText || !this.textWrap) {
cssClass += ' white-space-nowrap';
}
if (cell.editable) {
cssClass += ' editable';
}
if (cell.errorStatus) {
cssClass += ' has-error';
}
cssClass += ' halign-' + scout.Table.parseHorizontalAlignment(cell.horizontalAlignment);
var columnPosition = this.table.columns.indexOf(this);
if (columnPosition === 0) {
cssClass += ' first';
}
if (columnPosition === this.table.columns.length - 1) {
cssClass += ' last';
}
//TODO [5.2] cgu: cssClass is actually only sent for cells, should we change this in model? discuss with jgu
if (cell.cssClass) {
cssClass += ' ' + cell.cssClass;
} else if (this.cssClass) {
cssClass += ' ' + this.cssClass;
}
return cssClass;
};
scout.Column.prototype._cellStyle = function(cell) {
var style,
width = this.width;
if (width === 0) {
return 'display: none;';
}
style = 'min-width: ' + width + 'px; max-width: ' + width + 'px; ';
style += scout.styles.legacyStyle(cell);
if (this.backgroundEffect && cell.value !== undefined) {
if (!this.backgroundEffectFunc) {
this.backgroundEffectFunc = this._resolveBackgroundEffectFunc();
}
var backgroundStyle = this.backgroundEffectFunc(this._preprocessValueForGrouping(cell.value));
if (backgroundStyle.backgroundColor) {
style += 'background-color: ' + backgroundStyle.backgroundColor + ';';
}
if (backgroundStyle.backgroundImage) {
style += 'background-image: ' + backgroundStyle.backgroundImage + ';';
}
}
return style;
};
scout.Column.prototype.onMouseUp = function(event, $row) {
var row = $row.data('row'),
cell = this.table.cell(this, row);
if (this.table.enabled && row.enabled && cell.editable && !event.ctrlKey && !event.shiftKey) {
this.table.prepareCellEdit(row.id, this.id, true);
}
};
scout.Column.prototype.startCellEdit = function(row, fieldId) {
var popup,
$row = row.$row,
cell = this.table.cell(this, row),
$cell = this.table.$cell(this, $row);
cell.field = this.session.getOrCreateModelAdapter(fieldId, this.table);
// Override field alignment with the cell's alignment
cell.field.gridData.horizontalAlignment = cell.horizontalAlignment;
popup = scout.create('CellEditorPopup', {
parent: this.table,
column: this,
row: row,
cell: cell
});
popup.$anchor = $cell;
popup.open(this.table.$data);
return popup;
};
// TODO CGU/AWE cleanup these cellValue/TextForXY methods, currently they are very confusing
/**
* Returns the cell value to be used for grouping and filtering (chart, column filter).
*/
scout.Column.prototype.cellValueForGrouping = function(row) {
var cell = this.table.cell(this, row);
if (cell.value !== undefined) {
return this._preprocessValueForGrouping(cell.value);
}
if (!cell.text) {
return null;
}
return this._preprocessTextForValueGrouping(cell.text, cell.htmlEnabled);
};
scout.Column.prototype._preprocessValueForGrouping = function(value) {
return value;
};
scout.Column.prototype._preprocessTextForValueGrouping = function(text, htmlEnabled) {
return this._preprocessText(text, {
removeHtmlTags: htmlEnabled,
removeNewlines: true,
trim: true
});
};
/**
* Returns the cell text to be used for table grouping
*/
scout.Column.prototype.cellTextForGrouping = function(row) {
var cell = this.table.cell(this, row);
return this._preprocessTextForGrouping(cell.text, cell.htmlEnabled);
};
scout.Column.prototype._preprocessTextForGrouping = function(text, htmlEnabled) {
return this._preprocessText(text, {
removeHtmlTags: htmlEnabled,
trim: true
});
};
/**
* Returns the cell text to be used for the text filter
*/
scout.Column.prototype.cellTextForTextFilter = function(row) {
var cell = this.table.cell(this, row);
return this._preprocessTextForTextFilter(cell.text, cell.htmlEnabled);
};
scout.Column.prototype._preprocessTextForTextFilter = function(text, htmlEnabled) {
return this._preprocessText(text, {
removeHtmlTags: htmlEnabled
});
};
/**
* Removes html tags, converts to single line, removes leading and trailing whitespaces.
*/
scout.Column.prototype._preprocessText = function(text, options) {
if (text === null || text === undefined) {
return text;
}
options = options || {};
if (options.removeHtmlTags) {
text = scout.strings.plainText(text);
}
if (options.removeNewlines) {
text = text.replace('\n', ' ');
}
if (options.trim) {
text = text.trim();
}
return text;
};
scout.Column.prototype.setAggregationFunction = function(func) {
this.aggregationFunction = func;
if (func === 'sum') {
this.aggrStart = scout.aggregation.sumStart;
this.aggrStep = scout.aggregation.sumStep;
this.aggrFinish = scout.aggregation.sumFinish;
this.aggrSymbol = scout.aggregation.sumSymbol;
} else if (func === 'avg') {
this.aggrStart = scout.aggregation.avgStart;
this.aggrStep = scout.aggregation.avgStep;
this.aggrFinish = scout.aggregation.avgFinish;
this.aggrSymbol = scout.aggregation.avgSymbol;
} else if (func === 'min') {
this.aggrStart = scout.aggregation.minStart;
this.aggrStep = scout.aggregation.minStep;
this.aggrFinish = scout.aggregation.minFinish;
this.aggrSymbol = scout.aggregation.minSymbol;
} else if (func === 'max') {
this.aggrStart = scout.aggregation.maxStart;
this.aggrStep = scout.aggregation.maxStep;
this.aggrFinish = scout.aggregation.maxFinish;
this.aggrSymbol = scout.aggregation.maxSymbol;
}
};
scout.Column.prototype.createAggrGroupCell = function(row) {
var cell = this.table.cell(this, row);
return {
// value necessary for value based columns (e.g. checkbox column)
value: cell.value,
text: this.cellTextForGrouping(row),
iconId: cell.iconId,
horizontalAlignment: this.horizontalAlignment,
cssClass: 'table-aggregate-cell'
};
};
scout.Column.prototype.createAggrEmptyCell = function() {
return {
empty: true
};
};
scout.Column.prototype.setBackgroundEffect = function(effect, notifyServer) {
if (this.backgroundEffect === effect) {
return;
}
this.backgroundEffect = effect;
this.backgroundEffectFunc = this._resolveBackgroundEffectFunc();
notifyServer = scout.nvl(notifyServer, true);
if (notifyServer) {
this.table._sendColumnBackgroundEffectChanged(this);
}
if (this.backgroundEffect && (this.minValue === undefined || this.maxValue === undefined)) {
// No need to calculate the values again when switching background effects
// If background effect is turned off and on again values will be recalculated
// This is necessary because in the meantime rows may got updated, deleted etc.
this.calculateMinMaxValues();
}
if (!this.backgroundEffect) {
// Clear to make sure values are calculated anew the next time a background effect gets set
this.minValue = undefined;
this.maxValue = undefined;
}
if (this.table.rendered) {
this._renderBackgroundEffect();
}
};
/**
* Recalculates the min / max values and renders the background effect again.
*/
scout.Column.prototype.updateBackgroundEffect = function() {
this.calculateMinMaxValues();
if (this.table.rendered) {
this._renderBackgroundEffect();
}
};
scout.Column.prototype._resolveBackgroundEffectFunc = function() {
var effect = this.backgroundEffect;
if (effect === 'colorGradient1') {
return this._colorGradient1.bind(this);
}
if (effect === 'colorGradient2') {
return this._colorGradient2.bind(this);
}
if (effect === 'barChart') {
return this._barChart.bind(this);
}
if (effect !== null) {
$.log.warn('Unsupported backgroundEffect: ' + effect);
return function() {
return {};
};
}
};
scout.Column.prototype._renderBackgroundEffect = function() {
this.table.filteredRows().forEach(function(row) {
if (!row.$row) {
return;
}
var cell = this.table.cell(this, row),
$cell = this.table.$cell(this, row.$row);
if (cell.value !== undefined) {
$cell[0].style.cssText = this._cellStyle(cell);
}
}, this);
};
scout.Column.prototype.calculateOptimalWidth = function() {
var row, rows = this.table.rows,
optimalWidth = this.minWidth;
for (var i = 0; i < rows.length; i++) {
row = rows[i];
var $div = $(this.buildCellForRow(row));
$div.removeAttr('style');
$div.hide();
this.table.$data.append($div);
optimalWidth = optimalWidth < $div.outerWidth() ? $div.outerWidth() : optimalWidth;
$div.remove();
}
return optimalWidth;
};
scout.Column.prototype.calculateMinMaxValues = function() {
var row, minValue, maxValue, value,
rows = this.table.rows;
for (var i = 0; i < rows.length; i++) {
row = rows[i];
value = this.cellValueForGrouping(row);
if (value < minValue || minValue === undefined) {
minValue = value;
}
if (value > maxValue || maxValue === undefined) {
maxValue = value;
}
}
this.minValue = minValue;
this.maxValue = maxValue;
};
scout.Column.prototype._colorGradient1 = function(value) {
var startStyle = scout.styles.get('column-background-effect-gradient1-start', 'backgroundColor'),
endStyle = scout.styles.get('column-background-effect-gradient1-end', 'backgroundColor'),
startColor = scout.styles.rgb(startStyle.backgroundColor),
endColor = scout.styles.rgb(endStyle.backgroundColor);
return this._colorGradient(value, startColor, endColor);
};
scout.Column.prototype._colorGradient2 = function(value) {
var startStyle = scout.styles.get('column-background-effect-gradient2-start', 'backgroundColor'),
endStyle = scout.styles.get('column-background-effect-gradient2-end', 'backgroundColor'),
startColor = scout.styles.rgb(startStyle.backgroundColor),
endColor = scout.styles.rgb(endStyle.backgroundColor);
return this._colorGradient(value, startColor, endColor);
};
scout.Column.prototype._colorGradient = function(value, startColor, endColor) {
var level = (value - this.minValue) / (this.maxValue - this.minValue);
var r = Math.ceil(startColor.red - level * (startColor.red - endColor.red)),
g = Math.ceil(startColor.green - level * (startColor.green - endColor.green)),
b = Math.ceil(startColor.blue - level * (startColor.blue - endColor.blue));
return {
backgroundColor: 'rgb(' + r + ',' + g + ', ' + b + ')'
};
};
scout.Column.prototype._barChart = function(value) {
var level = Math.ceil((value - this.minValue) / (this.maxValue - this.minValue) * 100) + '';
var color = scout.styles.get('column-background-effect-bar-chart', 'backgroundColor').backgroundColor;
return {
backgroundImage: 'linear-gradient(to left, ' + color + ' 0%, ' + color + ' ' + level + '%, transparent ' + level + '%, transparent 100% )'
};
};
/**
* Returns a type specific column user-filter. The default impl. returns a ColumnUserFilter.
* Sub-classes that must return another type, must simply change the value of the 'filterType' property.
*/
scout.Column.prototype.createFilter = function(model) {
return scout.create(this.filterType, {
session: this.session,
table: this.table,
column: this
});
};
/**
* Override this method to install a specific compare function on a column instance.
* The default impl. installs a generic comparator working with less than and greater than.
*
* @return Whether or not it was possible to install a compare function. If not, client side sorting is disabled.
* Default impl. returns a
*/
scout.Column.prototype.prepareForSorting = function() {
return this.comparator.install(this.session);
};
scout.Column.prototype.compare = function(row1, row2) {
var valueA = this.table.cellValue(this, row1);
var valueB = this.table.cellValue(this, row2);
return this.comparator.compare(valueA, valueB);
};
© 2015 - 2025 Weber Informatics LLC | Privacy Policy