in ui-modules/utils/table/index.js [112:300]
function link(scope, element, attrs, ngModelCtrl) {
scope.ctrl.rowUiState = attrs.rowUiState;
scope.ctrl.rowUiStateParams = scope.$eval(attrs.rowUiStateParams);
scope.ctrl.state = {
columns: scope.$eval(attrs.columns),
sorts: [],
search: '',
filters: {}
};
if (!(scope.ctrl.state.columns instanceof Array)) {
throw new Error('Field "columns" in table options must be of type "Array"');
}
scope.ctrl.state.columnSpanCount = 0;
scope.ctrl.state.columnExplicitWidthCount = 0;
scope.ctrl.state.columnExplicitWidthSum = 0;
scope.ctrl.tableLayoutFixed = false;
scope.ctrl.state.columns.forEach((column, index) => {
if (!(column instanceof Object)) {
throw new Error(`Column with index "${index}" must be of type "Object"`);
}
let field = column.field;
if (!column.hasOwnProperty('header')) {
if (field) {
column.header = field.replace(/([a-z])([A-Z])/g, (_, a, A) => a+' '+A).replace(/^([a-z])(.*)/, (_, a, bc) => a.toUpperCase()+bc)
} else {
throw new Error(`Column with index ${index} does not has the required field "header"`);
}
}
if (!column.hasOwnProperty('template') && !column.hasOwnProperty('templateUrl')) {
if (field) {
column.template = `{{ item['${field}'] }}`;
} else {
throw new Error(`Column with index ${index} requires either "template" or "templateUrl" field`);
}
}
if (!column.hasOwnProperty('id')) {
if (field) {
column.id = field;
} else {
column.id = 'col-'+index;
column.idAutogenerated = true;
}
} else {
column.tdClass = column.tdClass || column.id;
}
if (!column.hasOwnProperty('orderBy') && field) {
column.orderBy = field;
}
column.hidden = column.hidden || false;
column.regex = new RegExp(`(?:\\s|^)${column.id}:(\\S*)(?:\\s|$)`, 'i')
if (!column.idAutogenerated) {
column.idForTypeahead = column.id;
}
});
function recomputeSpanCount() {
// this should be recomputed when columns are hidden/shown
// without this, the "No results" message may be slightly too wide when columns are hidden
var columnSpanCount = 0,
columnExplicitWidthCount = 0,
columnExplicitWidthSum = 0,
tableLayoutFixed = false;
scope.ctrl.state.columns.forEach((column, index) => {
if (column.hidden) return;
columnSpanCount += (column.colspan || 1);
tableLayoutFixed |= column.colspan;
if (column.width) {
columnExplicitWidthCount ++;
columnExplicitWidthSum += column.width;
}
});
scope.ctrl.state.columnSpanCount = columnSpanCount;
scope.ctrl.state.columnExplicitWidthCount = columnExplicitWidthCount;
scope.ctrl.state.columnExplicitWidthSum = columnExplicitWidthSum;
scope.ctrl.tableLayoutFixed = tableLayoutFixed;
if (attrs.colWidth) {
var minWidth = (scope.ctrl.state.columnExplicitWidthSum +
(scope.ctrl.state.columnSpanCount - scope.ctrl.state.columnExplicitWidthCount) * attrs.colWidth);
if (isNaN(parseFloat(minWidth)) || !isFinite(minWidth)) {
// not a valid number in the end: could install units-css library and do unit maths, but not worth it
$log.error(`Error computing column widths (got ${scope.ctrl.minWidth}): ensure no values have units`);
} else {
scope.ctrl.minWidth = minWidth + 'px';
}
}
}
recomputeSpanCount();
scope.hideColumn = (column,) => {
column.hidden = !column.hidden;
recomputeSpanCount();
};
let sha = new jssha('SHA-512', 'TEXT');
sha.update(scope.ctrl.rowUiState || '');
sha.update(JSON.stringify(scope.ctrl.rowUiStateParams) || '');
sha.update(JSON.stringify(scope.ctrl.state.columns) || '');
let hash = sha.getHash('HEX');
if (sessionStorage) {
let state = sessionStorage.getItem(`${MODULE_NAME}.state.${hash}`);
if (state !== null) {
scope.ctrl.state = Object.assign(scope.ctrl.state, JSON.parse(state));
scope.ctrl.state.columns.forEach(column => column.regex = new RegExp(`(?:\\s|^)${column.id}:(\\S*)(?:\\s|$)`, 'i'));
}
scope.$watch('ctrl.state', (newValue, oldValue) => {
if (!angular.equals(newValue, oldValue)) {
sessionStorage.setItem(`${MODULE_NAME}.state.${hash}`, JSON.stringify(newValue));
}
}, true);
}
scope.$watchCollection('ctrl.items', function(value) {
ngModelCtrl.$setViewValue(value);
ngModelCtrl.$validate();
});
ngModelCtrl.$render = function() {
if (!ngModelCtrl.$viewValue) {
ngModelCtrl.$viewValue = [];
}
scope.ctrl.items = ngModelCtrl.$viewValue;
};
scope.$applyAsync(() => {
element[0].querySelectorAll('th div').forEach(elm => {
angular.element(elm).data('initialWidth', elm.offsetWidth);
});
element[0].querySelectorAll('span.column-resizer').forEach(elm => {
elm.ondragstart = function() { return false; };
elm.addEventListener('mousedown', function(e) {
if (e.which === 1) {
// left mouse click
scope.ctrl.dragStart(e);
}
}, false);
});
});
scope.$watch('ctrl.state.search', (newValue, oldValue) => {
if (newValue === oldValue) {
return;
}
let filters = {};
let remaining = newValue;
let words = [];
// get any phrases in double quotes
let qw = /(?:\s|^)"([^"]*)"(?:\s|$)/;
var match;
while (match=qw.exec(remaining)) {
words.push(match[1]);
remaining = remaining.replace(qw, ' ');
}
// now get anything that matches column prefix
scope.ctrl.state.columns.forEach(column => {
if (column.hidden) {
return;
}
let matches = remaining.match(column.regex);
if (matches === null) {
return;
}
filters[column.id] = matches[1];
remaining = remaining.replace(column.regex, ' ');
});
// remaining items are split
remaining = remaining.trim();
if (remaining.length > 0) {
words = words.concat(remaining.split(/\s+/));
}
words = words.length==0 ? null : words.length==1 ? words[0] : words;
if (Object.keys(filters).length > 0) {
if (words) {
filters[''] = words;
}
}
scope.ctrl.state.filters = Object.keys(filters).length > 0 ? filters : words;
});
}