blob: f9becaee0e4a79c9cc449ab518420bdae49a1671 [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 2015 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<link rel="import" href="/tracing/base/iteration_helpers.html">
<link rel="import" href="/tracing/base/statistics.html">
<link rel="import" href="/tracing/ui/analysis/generic_object_view.html">
<link rel="import" href="/tracing/ui/base/table.html">
<link rel="import" href="/tracing/value/generic_table.html">
<link rel="import" href="/tracing/value/ui/array_of_numbers_span.html">
<polymer-element name="tr-v-ui-generic-table-view">
<template>
<style>
:host {
display: flex;
}
#table {
flex: 1 1 auto;
align-self: stretch;
}
</style>
<tr-ui-b-table id="table"></tr-ui-b-table>
</template>
</polymer-element>
<script>
'use strict';
tr.exportTo('tr.v.ui', function() {
var TEXT_COLUMN_MODE = 1;
var NUMERIC_COLUMN_MODE = 2;
var ELEMENT_COLUMN_MODE = 3;
function isNumeric(value) {
// TODO(nduca): Also consider other units that are numeric.
if ((typeof value) === 'number')
return true;
else if (value instanceof Number)
return true;
return false;
}
function GenericTableViewTotalsItem(opt_values) {
if (opt_values !== undefined)
this.values = opt_values;
else
this.values = [];
}
function GenericTableViewColumnDescriptor(fieldName, firstFieldValue) {
this.title = fieldName;
this.fieldName = fieldName;
this.updateModeGivenValue(firstFieldValue);
}
GenericTableViewColumnDescriptor.prototype = {
get columnMode() {
return this.columnMode_;
},
get isInNumericMode() {
return this.columnMode_ === NUMERIC_COLUMN_MODE;
},
cmp: function(a, b) {
if (this.columnMode_ === ELEMENT_COLUMN_MODE)
return 0;
return tr.b.comparePossiblyUndefinedValues(a, b, function(a, b) {
var vA = a[this.fieldName];
var vB = b[this.fieldName];
return tr.b.comparePossiblyUndefinedValues(vA, vB, function(vA, vB) {
if (vA.localeCompare)
return vA.localeCompare(vB);
return vA - vB;
}, this);
}, this);
},
updateModeGivenValue: function(fieldValue) {
if (this.columnMode_ === undefined) {
if (fieldValue === undefined || fieldValue === null)
return;
if (isNumeric(fieldValue)) {
this.columnMode_ = NUMERIC_COLUMN_MODE;
return;
}
if (fieldValue instanceof HTMLElement) {
this.columnMode_ = ELEMENT_COLUMN_MODE;
return;
}
this.columnMode_ = TEXT_COLUMN_MODE;
return;
}
// Undefineds & nulls shouldn't change the mode.
if (fieldValue === undefined || fieldValue === null)
return;
// If we were already in numeric mode, then we don't
// need to put it into numeric mode again. And, if we were
// previously in text mode, then we can't go into numeric mode now.
if (isNumeric(fieldValue))
return;
if (fieldValue instanceof HTMLElement) {
this.columnMode_ = ELEMENT_COLUMN_MODE;
return;
}
if (this.columnMode_ === NUMERIC_COLUMN_MODE)
this.columnMode_ = TEXT_COLUMN_MODE;
},
value: function(item) {
var fieldValue = item[this.fieldName];
if (fieldValue instanceof GenericTableViewTotalsItem) {
var span = document.createElement('tr-v-ui-array-of-numbers-span');
span.summaryMode = tr.v.ui.ArrayOfNumbersSummaryModes.TOTAL_MODE;
span.numbers = fieldValue.values;
return span;
}
if (fieldValue === undefined)
return '-';
if (fieldValue instanceof HTMLElement)
return fieldValue;
if (fieldValue instanceof Object) {
var gov = document.createElement('tr-ui-a-generic-object-view');
gov.object = fieldValue;
return gov;
}
// TODO(nduca): Use units objects if applicable.
return fieldValue;
}
};
Polymer('tr-v-ui-generic-table-view', {
created: function() {
this.items_ = undefined;
this.importantColumNames_ = [];
},
get items() {
return this.items_;
},
set items(itemsOrGenericTable) {
if (itemsOrGenericTable === undefined) {
this.items_ = undefined;
} else if (itemsOrGenericTable instanceof Array) {
this.items_ = itemsOrGenericTable;
} else if (itemsOrGenericTable instanceof tr.v.GenericTable) {
this.items_ = itemsOrGenericTable.items;
}
this.updateContents_();
},
get importantColumNames() {
return this.importantColumNames_;
},
set importantColumNames(importantColumNames) {
this.importantColumNames_ = importantColumNames;
this.updateContents_();
},
createColumns_: function() {
var columnsByName = {};
this.items_.forEach(function(item) {
tr.b.iterItems(item, function(itemFieldName, itemFieldValue) {
var colDesc = columnsByName[itemFieldName];
if (colDesc !== undefined) {
colDesc.updateModeGivenValue(itemFieldValue);
return;
}
colDesc = new GenericTableViewColumnDescriptor(
itemFieldName, itemFieldValue);
columnsByName[itemFieldName] = colDesc;
}, this);
}, this);
var columns = tr.b.dictionaryValues(columnsByName);
if (columns.length === 0)
return undefined;
// Sort by name.
var isColumnNameImportant = {};
var importantColumNames = this.importantColumNames || [];
importantColumNames.forEach(function(icn) {
isColumnNameImportant[icn] = true;
});
columns.sort(function(a, b) {
var iA = isColumnNameImportant[a.title] ? 1 : 0;
var iB = isColumnNameImportant[b.title] ? 1 : 0;
if ((iB - iA) !== 0)
return iB - iA;
return a.title.localeCompare(b.title);
});
// Set sizes. This is convoluted by the fact that the first
// table column must have fixed size.
var colWidthPercentage;
if (columns.length == 1)
colWidthPercentage = '100%';
else
colWidthPercentage = (100 / (columns.length - 1)).toFixed(3) + '%';
columns[0].width = '250px';
for (var i = 1; i < columns.length; i++)
columns[i].width = colWidthPercentage;
return columns;
},
createFooterRowsIfNeeded_: function(columns) {
// Make totals row if needed.
var hasColumnThatIsNumeric = columns.some(function(column) {
return column.isInNumericMode;
});
if (!hasColumnThatIsNumeric)
return [];
var totalsItems = {};
columns.forEach(function(column) {
if (!column.isInNumericMode)
return;
var totalsItem = new GenericTableViewTotalsItem();
this.items_.forEach(function(item) {
var fieldValue = item[column.fieldName];
if (fieldValue === undefined || fieldValue === null)
return;
totalsItem.values.push(fieldValue);
});
totalsItems[column.fieldName] = totalsItem;
}, this);
return [totalsItems];
},
updateContents_: function() {
var columns;
if (this.items_ !== undefined)
columns = this.createColumns_();
if (!columns) {
this.$.table.tableColumns = [];
this.$.table.tableRows = [];
this.$.table.footerRows = [];
return;
}
this.$.table.tableColumns = columns;
this.$.table.tableRows = this.items_;
this.$.table.footerRows = this.createFooterRowsIfNeeded_(columns);
this.$.table.rebuild();
},
get selectionMode() {
return this.$.table.selectionMode;
},
set selectionMode(selectionMode) {
this.$.table.selectionMode = selectionMode;
},
get rowHighlightStyle() {
return this.$.table.rowHighlightStyle;
},
set rowHighlightStyle(rowHighlightStyle) {
this.$.table.rowHighlightStyle = rowHighlightStyle;
},
get cellHighlightStyle() {
return this.$.table.cellHighlightStyle;
},
set cellHighlightStyle(cellHighlightStyle) {
this.$.table.cellHighlightStyle = cellHighlightStyle;
}
});
return {
GenericTableViewTotalsItem: GenericTableViewTotalsItem,
GenericTableViewColumnDescriptor: GenericTableViewColumnDescriptor
};
});
</script>