| Index: tracing/tracing/value/ui/value_set_table.html
|
| diff --git a/tracing/tracing/value/ui/value_set_table.html b/tracing/tracing/value/ui/value_set_table.html
|
| index 76fc197afce591037985a390aaa31283247d38cb..4eb50e885fa2fbd4b6402bb4dbbf3b495affb564 100644
|
| --- a/tracing/tracing/value/ui/value_set_table.html
|
| +++ b/tracing/tracing/value/ui/value_set_table.html
|
| @@ -28,8 +28,21 @@ found in the LICENSE file.
|
| div#error {
|
| color: red;
|
| }
|
| + #histogram {
|
| + display: none;
|
| + }
|
| + #search {
|
| + max-width: 20em;
|
| + margin-top: 5px;
|
| + margin-bottom: 5px;
|
| + }
|
| </style>
|
|
|
| + <input id="search" placeholder="Find Value name">
|
| + <div>
|
| + <input type="checkbox" id="show_all">
|
| + <label for="show_all">Show all</label>
|
| + </div>
|
| <div id="error"></div>
|
| <table-container>
|
| <tr-ui-b-table id="table"></tr-ui-b-table>
|
| @@ -41,6 +54,79 @@ found in the LICENSE file.
|
| <script>
|
| 'use strict';
|
| tr.exportTo('tr.ui', function() {
|
| +
|
| + /**
|
| + * @param {!tr.v.Value} value
|
| + * @param {string} fieldName
|
| + * @param {*} defaultValue
|
| + * @return {*}
|
| + */
|
| + function getIterationInfoField(value, fieldName, defaultValue) {
|
| + var iteration = tr.v.d.IterationInfo.getFromValue(value);
|
| + if (!(iteration instanceof tr.v.d.IterationInfo))
|
| + return defaultValue;
|
| + return iteration[fieldName];
|
| + }
|
| +
|
| + /**
|
| + * @param {!tr.v.Value} value
|
| + * @param {string} fieldName
|
| + * @return {string}
|
| + */
|
| + function getStoryGroupingKeyLabel(value, storyGroupingKey) {
|
| + var iteration = tr.v.d.IterationInfo.getFromValue(value);
|
| + if (!(iteration instanceof tr.v.d.IterationInfo))
|
| + return storyGroupingKey + ': undefined';
|
| + return storyGroupingKey + ': ' +
|
| + iteration.storyGroupingKeys[storyGroupingKey];
|
| + }
|
| +
|
| + /**
|
| + * @param {!tr.v.Value} value
|
| + * @return {string}
|
| + */
|
| + var getDisplayLabel = v => getIterationInfoField(v, 'displayLabel', 'Value');
|
| +
|
| + var SELECTED_VALUE_SETTINGS_KEY = 'tr-v-ui-value-set-table-value';
|
| + var SHOW_ALL_SETTINGS_KEY = 'tr-v-ui-value-set-table-show-all';
|
| +
|
| + /**
|
| + * Recursively groups |values|.
|
| + * TODO(benjhayden): Use ES6 Maps instead of dictionaries?
|
| + *
|
| + * @param {!Array.<!tr.v.Value>} values
|
| + * @param {!Array.<!function(!tr.v.Value):(string|number)>} groupingCallbacks
|
| + * @return {!(Object|tr.v.Value)}
|
| + */
|
| + function organizeValues(values, groupingCallbacks, level) {
|
| + if (groupingCallbacks.length === level) {
|
| + // Recursion base case: there should only be a single value when we've
|
| + // grouped by every possible grouping.
|
| + if (values.length > 1) {
|
| + console.warn('Multiple Values with same name, benchmarkName, ' +
|
| + 'storyGroupingKeys, storyName, start, storysetRepeatCounter, ' +
|
| + 'storyRepeatCounter, and displayLabel', values);
|
| + }
|
| + return values[0];
|
| + }
|
| +
|
| + // Group the values by the current grouping.
|
| + var groupedValues = tr.b.group(values, groupingCallbacks[level]);
|
| +
|
| + // Skip this grouping level if it contains only a single group,
|
| + // but never skip the zeroth grouping level (value name) nor the last
|
| + // (displayLabel).
|
| + if (level > 0 && level < (groupingCallbacks.length - 1) &&
|
| + tr.b.dictionaryLength(groupedValues) === 1) {
|
| + return organizeValues(values, groupingCallbacks, level + 1);
|
| + }
|
| +
|
| + // Recursively group groupedValues.
|
| + return tr.b.mapItems(groupedValues, function(key, groupValues) {
|
| + return organizeValues(groupValues, groupingCallbacks, level + 1);
|
| + });
|
| + }
|
| +
|
| Polymer({
|
| is: 'tr-v-ui-value-set-table',
|
|
|
| @@ -64,111 +150,583 @@ tr.exportTo('tr.ui', function() {
|
| return 'Table';
|
| },
|
|
|
| + created: function() {
|
| + // TODO(benjhayden): Should these all be ValueSets?
|
| + /** @type {undefined|!tr.v.ValueSet} */
|
| + this.values_ = undefined;
|
| + /** @type {!Array.<!tr.v.Value>} */
|
| + this.sourceValues_ = [];
|
| + /** @type {!Object} */
|
| + this.summaryValuesByGuid_ = {};
|
| +
|
| + this.rows_ = undefined;
|
| + this.columns_ = undefined;
|
| + },
|
| +
|
| ready: function() {
|
| - this.$.table.sortDescending = true;
|
| - this.$.table.selectionMode = tr.ui.b.TableFormat.SelectionMode.ROW;
|
| - this.$.table.tableColumns = [
|
| - {
|
| - title: 'Name',
|
| - value: function(value) {
|
| - var nameEl = document.createElement('span');
|
| - Polymer.dom(nameEl).textContent = value.name;
|
| - if (value.description)
|
| - nameEl.title = value.description;
|
| - nameEl.style.textOverflow = 'ellipsis';
|
| - return nameEl;
|
| - },
|
| - cmp: function(a, b) {
|
| - return a.name.localeCompare(b.name);
|
| - }
|
| - },
|
| - {
|
| - title: 'Value',
|
| - align: tr.ui.b.TableFormat.ColumnAlignment.RIGHT,
|
| - value: function(value) {
|
| - if (value.diagnostic instanceof tr.v.d.Diagnostic)
|
| - return tr.v.ui.createDiagnosticSpan(value.diagnostic);
|
| - if (value.unit)
|
| - return tr.v.ui.createScalarSpan(value.value, {unit: value.unit});
|
| - return value.value;
|
| - },
|
| - cmp: function(a, b) {
|
| - return a.value - b.value;
|
| + this.$.table.selectionMode = tr.ui.b.TableFormat.SelectionMode.CELL;
|
| + this.$.table.addEventListener('selection-changed',
|
| + this.onSelectionChanged_.bind(this));
|
| + this.$.table.addEventListener('selected-column-changed',
|
| + this.onSelectedColumnChanged_.bind(this));
|
| + this.addEventListener('requestSelectionChange',
|
| + this.onRelatedValueSelected_.bind(this));
|
| + this.$.search.addEventListener('keyup', this.onSearch_.bind(this));
|
| + this.$.show_all.checked = tr.b.Settings.get(SHOW_ALL_SETTINGS_KEY, false);
|
| + this.$.show_all.addEventListener('change',
|
| + this.onShowAllChange_.bind(this));
|
| + },
|
| +
|
| + onShowAllChange_: function() {
|
| + tr.b.Settings.set(SHOW_ALL_SETTINGS_KEY, this.$.show_all.checked);
|
| + this.updateContents_();
|
| + },
|
| +
|
| + onSelectedColumnChanged_: function() {
|
| + // Force the table to rebuild the cell values without forgetting which
|
| + // rows were expanded.
|
| + var expansionStates = this.getExpansionStates_(this.rows_);
|
| + this.$.table.tableRows = this.rows_;
|
| + this.setExpansionStates_(expansionStates, this.rows_);
|
| + },
|
| +
|
| + getExpansionStates_: function(rows) {
|
| + var states = {};
|
| + for (var i = 0; i < rows.length; ++i) {
|
| + var row = rows[i];
|
| + if (row.subRows && row.subRows.length &&
|
| + this.$.table.getExpandedForTableRow(row)) {
|
| + states[i] = this.getExpansionStates_(row.subRows);
|
| + }
|
| + }
|
| + return states;
|
| + },
|
| +
|
| + setExpansionStates_: function(states, rows) {
|
| + for (var i = 0; i < rows.length; ++i) {
|
| + if (states[i]) {
|
| + this.$.table.setExpandedForTableRow(rows[i], true);
|
| + this.setExpansionStates_(states[i], rows[i].subRows);
|
| + }
|
| + }
|
| + },
|
| +
|
| + onSearch_: function() {
|
| + this.updateContents_();
|
| + },
|
| +
|
| + rowMatchesSearch_: function(row) {
|
| + return row.name.indexOf(this.$.search.value) >= 0;
|
| + },
|
| +
|
| + onRelatedValueSelected_: function(event) {
|
| + var value = event.selection;
|
| + if (!(value instanceof tr.v.Value))
|
| + return;
|
| +
|
| + event.stopPropagation();
|
| +
|
| + var displayLabel = getDisplayLabel(value);
|
| + var columnIndex = -1;
|
| + for (var i = 0; i < this.columns_.length; ++i) {
|
| + if (this.columns_[i].title === displayLabel) {
|
| + columnIndex = i;
|
| + break;
|
| + }
|
| + }
|
| + if (columnIndex < 0)
|
| + return;
|
| +
|
| + var hierarchy = [];
|
| + var found = false;
|
| + function search(row) {
|
| + if (row.columns[displayLabel] === value) {
|
| + for (var hirow in hierarchy) {
|
| + this.$.table.setExpandedForTableRow(hirow, true);
|
| }
|
| + found = true;
|
| + this.$.table.selectedTableRow = row;
|
| + this.$.table.selectedColumnIndex = columnIndex;
|
| + return;
|
| }
|
| - ];
|
| - this.$.table.sortColumnIndex = 1;
|
| - this.$.table.addEventListener('selection-changed',
|
| - this.onRowSelected_.bind(this));
|
| + if (!row.subRows)
|
| + return;
|
| + hierarchy.push(row);
|
| + row.subRows.forEach(search, this);
|
| + hierarchy.pop(row);
|
| + }
|
| + this.rows_.forEach(search, this);
|
| +
|
| + if (found || this.$.show_all.checked)
|
| + return;
|
| +
|
| + // Search hidden values for |value|.
|
| + for (var test of this.values) {
|
| + // Skip values that are already displayed -- we would have found them
|
| + // in search() above.
|
| + if (this.sourceValues_.indexOf(test) >= 0)
|
| + continue;
|
| +
|
| + if (test === value) {
|
| + this.$.show_all.checked = true;
|
| + this.onShowAllChange_();
|
| + this.onRelatedValueSelected_(event);
|
| + break;
|
| + }
|
| + }
|
| },
|
|
|
| - onRowSelected_: function() {
|
| + onSelectionChanged_: function() {
|
| var row = this.$.table.selectedTableRow;
|
| - if (row && row.numeric) {
|
| - this.$.histogram.style.display = '';
|
| - this.$.histogram.histogram = row.numeric;
|
| + var col = this.$.table.selectedColumnIndex;
|
| + var cell = undefined;
|
| + if (row && col && this.columns_)
|
| + cell = row.columns[this.columns_[col].title];
|
| +
|
| + if ((cell instanceof tr.v.NumericValue) &&
|
| + (cell.numeric instanceof tr.v.Numeric)) {
|
| + this.$.histogram.style.display = 'block';
|
| + this.$.histogram.histogram = cell.numeric;
|
| +
|
| + tr.b.Settings.set(SELECTED_VALUE_SETTINGS_KEY, JSON.stringify({
|
| + row: row.name,
|
| + column: this.columns_[col].title
|
| + }));
|
| } else {
|
| this.$.histogram.style.display = 'none';
|
| }
|
| },
|
|
|
| + handleFailureValues_: function() {
|
| + this.values.map(function(value) {
|
| + if (value instanceof tr.v.FailureValue) {
|
| + Polymer.dom(this.$.error).textContent = value.description;
|
| + this.$.table.style.display = 'none';
|
| + this.style.width = '10em';
|
| + }
|
| + }, this);
|
| + },
|
| +
|
| + addDiagnosticSubRows_: function(value, row, column) {
|
| + value.diagnostics.forEach(function(name, diagnostic) {
|
| + if (name === tr.v.SUMMARY_VALUE_MAP_DIAGNOSTIC_NAME)
|
| + return;
|
| +
|
| + // If a previous |value| had a diagnostic with the same name, then
|
| + // there is already a subRow that should contain this diagnostic.
|
| + for (var subRow of row.subRows) {
|
| + if (subRow.name === name) {
|
| + subRow.columns[column] = diagnostic;
|
| + return;
|
| + }
|
| + }
|
| +
|
| + // This is the first time that a diagnostic with this name has been
|
| + // seen for Values whose name is |value.name|, so create a new subRow.
|
| + var subRow = {name: name, columns: {}};
|
| + subRow.columns[column] = diagnostic;
|
| + row.subRows.push(subRow);
|
| + });
|
| + },
|
| +
|
| + get values() {
|
| + return this.values_;
|
| + },
|
| +
|
| + findSummaryValues_: function() {
|
| + this.summaryValuesByGuid_ = {};
|
| + this.values.map(function(value) {
|
| + var summaryValueMap = value.diagnostics.get(
|
| + tr.v.SUMMARY_VALUE_MAP_DIAGNOSTIC_NAME);
|
| + if (!(summaryValueMap instanceof tr.v.d.RelatedValueMap))
|
| + return;
|
| +
|
| + summaryValueMap.values.forEach(function(summaryValue) {
|
| + this.summaryValuesByGuid_[summaryValue.guid] = summaryValue;
|
| + }, this);
|
| + }, this);
|
| + },
|
| +
|
| /**
|
| * @param {!tr.v.ValueSet} values
|
| */
|
| set values(values) {
|
| + this.values_ = values;
|
| + this.sourceValues_ = values.sourceValues;
|
| + this.findSummaryValues_();
|
| + this.updateContents_();
|
| + },
|
| +
|
| + updateContents_: function() {
|
| + if (!this.values_)
|
| + return;
|
| this.style.width = '';
|
| this.$.table.style.display = '';
|
| Polymer.dom(this.$.error).textContent = '';
|
| + this.$.histogram.style.display = 'none';
|
|
|
| - values.map(function(value) {
|
| - if (value instanceof tr.v.FailureValue) {
|
| - Polymer.dom(this.$.error).textContent = value.description;
|
| - this.$.table.style.display = 'none';
|
| - this.style.width = '10em';
|
| - }
|
| - }, this);
|
| + this.handleFailureValues_();
|
| if (Polymer.dom(this.$.error).textContent)
|
| return;
|
|
|
| - this.$.table.tableRows = values.map(function(value) {
|
| - var row = {
|
| - name: value.name,
|
| - value: '',
|
| - unit: undefined,
|
| - description: value.description,
|
| - subRows: []
|
| - };
|
| -
|
| - if (value.numeric) {
|
| - row.unit = value.numeric.unit;
|
| - if (value.numeric.value !== undefined) {
|
| - row.value = value.numeric.value;
|
| - } else if (value.numeric.average !== undefined) {
|
| - row.numeric = value.numeric;
|
| - row.value = value.numeric.average;
|
| - }
|
| - }
|
| + this.buildRows_();
|
|
|
| - value.diagnostics.forEach(function(name, diagnostic) {
|
| - row.subRows.push({
|
| - name: name,
|
| - diagnostic: diagnostic
|
| - });
|
| - });
|
| + if (this.rows_.length === 0) {
|
| + Polymer.dom(this.$.error).textContent = 'zero values';
|
| + this.$.table.style.display = 'none';
|
| + this.style.width = '10em';
|
| + return;
|
| + }
|
|
|
| - return row;
|
| - });
|
| + this.buildColumns_();
|
|
|
| + this.$.table.tableColumns = this.columns_;
|
| + this.$.table.tableRows = this.rows_;
|
| + this.$.table.sortColumnIndex = 0;
|
| this.$.table.rebuild();
|
| - this.onRowSelected_();
|
| + this.selectValue_();
|
|
|
| tr.b.requestAnimationFrame(function() {
|
| this.style.width = this.$.table.getBoundingClientRect().width;
|
| }, this);
|
| + },
|
| +
|
| + selectValue_: function() {
|
| + var selectedValue = tr.b.Settings.get(
|
| + SELECTED_VALUE_SETTINGS_KEY, undefined);
|
| + if (selectedValue) {
|
| + selectedValue = JSON.parse(selectedValue);
|
| + for (var row of this.rows_) {
|
| + if (row.name === selectedValue.row) {
|
| + for (var coli = 1; coli < this.columns_.length; ++coli) {
|
| + var column = this.columns_[coli];
|
| + if (column.title === selectedValue.column) {
|
| + this.$.table.selectedTableRow = row;
|
| + this.$.table.selectedColumnIndex = coli;
|
| + return;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| + this.$.table.selectedTableRow = this.rows_[0];
|
| + this.$.table.selectedColumnIndex = 1;
|
| + },
|
| +
|
| + /**
|
| + * Build table rows recursively from organized Values. The recursion stack
|
| + * of subRows is maintained in |hierarchy|.
|
| + *
|
| + * @param {!Object} organizedValues
|
| + * @param {!Array.<!Object>} hierarchy
|
| + */
|
| + buildRow_: function(organizedValues, hierarchy) {
|
| + tr.b.iterItems(organizedValues, function(name, value) {
|
| + if (value instanceof tr.v.Value) {
|
| + // This recursion base case corresponds to the recursion base case of
|
| + // organizeValues(). The last groupingCallback is getDisplayLabel,
|
| + // which defines the columns of the table.
|
| +
|
| + // Merge Values up the grouping hierarchy.
|
| + for (var row of hierarchy) {
|
| + if (row.description === undefined)
|
| + row.description = value.description;
|
| +
|
| + if (row.columns[name])
|
| + row.columns[name] = row.columns[name].merge(value);
|
| + else
|
| + row.columns[name] = value;
|
| + }
|
| +
|
| + var row = hierarchy[hierarchy.length - 1];
|
| + this.addDiagnosticSubRows_(value, row, name);
|
| + } else {
|
| + // |value| is actually a nested organizedValues.
|
| + var row = {name: name, subRows: [], columns: {}};
|
| + hierarchy.push(row);
|
| + this.buildRow_(value, hierarchy);
|
| + hierarchy.pop();
|
| +
|
| + if (hierarchy.length === 0)
|
| + this.rows_.push(row);
|
| + else
|
| + hierarchy[hierarchy.length - 1].subRows.push(row);
|
| + }
|
| + }, this);
|
| + },
|
| +
|
| + get storyGroupingKeys() {
|
| + var keys = new Set();
|
| + for (var value of this.values) {
|
| + var iteration = tr.v.d.IterationInfo.getFromValue(value);
|
| + if (!(iteration instanceof tr.v.d.IterationInfo) ||
|
| + !iteration.storyGroupingKeys)
|
| + continue;
|
| +
|
| + for (var key in iteration.storyGroupingKeys)
|
| + keys.add(key);
|
| + }
|
| + return [...keys.values()].sort();
|
| + },
|
| +
|
| + /**
|
| + * A ValueSet is a flat set of Values. Value-set-table must present a
|
| + * hierarchical view. This method recursively groups this.values as an
|
| + * intermediate step towards building tableRows in buildRow_().
|
| + * {
|
| + * valueA: {
|
| + * benchmarkA: {
|
| + * storyA: {
|
| + * startA: {
|
| + * storysetRepeatCounterA: {
|
| + * storyRepeatCounterA: {
|
| + * displayLabelA: Value,
|
| + * displayLabelB: Value
|
| + * }
|
| + * }
|
| + * }
|
| + * }
|
| + * }
|
| + * }
|
| + * }
|
| + * @return {!Object}
|
| + */
|
| + get organizedValues_() {
|
| + var showingValues = this.$.show_all.checked ?
|
| + this.values : this.sourceValues_;
|
| + var values = [];
|
| + for (var value of showingValues)
|
| + if (this.summaryValuesByGuid_[value.guid] === undefined)
|
| + values.push(value);
|
| +
|
| + var groupingCallbacks = [];
|
| + groupingCallbacks.push(v => v.name);
|
| + groupingCallbacks.push(
|
| + v => getIterationInfoField(v, 'benchmarkName', ''));
|
| + for (var storyGroupingKey of this.storyGroupingKeys) {
|
| + // Javascript closures are dumb.
|
| + groupingCallbacks.push((sgk => (
|
| + v => getStoryGroupingKeyLabel(v, sgk)))(storyGroupingKey));
|
| + }
|
| + groupingCallbacks.push(
|
| + v => getIterationInfoField(v, 'storyDisplayName', ''));
|
| + groupingCallbacks.push(
|
| + v => getIterationInfoField(v, 'benchmarkStartString', ''));
|
| + groupingCallbacks.push(
|
| + v => getIterationInfoField(v, 'storysetRepeatCounterLabel', 0));
|
| + groupingCallbacks.push(
|
| + v => getIterationInfoField(v, 'storyRepeatCounterLabel', 0));
|
| + groupingCallbacks.push(getDisplayLabel);
|
| +
|
| + return organizeValues(values, groupingCallbacks, 0);
|
| + },
|
| +
|
| + /* this.rows_ will look something like
|
| + * [
|
| + * {
|
| + * name: 'value name',
|
| + * columns: {
|
| + * displayLabelA: Value,
|
| + * displayLabelB: Value,
|
| + * },
|
| + * subRows: [
|
| + * {
|
| + * name: 'benchmark name if multiple',
|
| + * columns: {
|
| + * displayLabelA: Value,
|
| + * displayLabelB: Value,
|
| + * },
|
| + * subRows: [
|
| + * {
|
| + * name: 'story name if multiple',
|
| + * columns: {
|
| + * displayLabelA: Value,
|
| + * displayLabelB: Value,
|
| + * },
|
| + * subRows: [
|
| + * {
|
| + * name: 'benchmark start if multiple',
|
| + * columns: {
|
| + * displayLabelA: Value,
|
| + * displayLabelB: Value,
|
| + * },
|
| + * subRows: [
|
| + * {
|
| + * name: 'storyset repeat counter if multiple',
|
| + * columns: {
|
| + * displayLabelA: Value,
|
| + * displayLabelB: Value,
|
| + * },
|
| + * subRows: [
|
| + * {
|
| + * name: 'story repeat counter if multiple',
|
| + * columns: {
|
| + * displayLabelA: Value,
|
| + * displayLabelB: Value,
|
| + * },
|
| + * subRows: [
|
| + * {
|
| + * name: 'diagnostic map key',
|
| + * columns: {
|
| + * displayLabelA: Diagnostic,
|
| + * displayLabelB: Diagnostic,
|
| + * },
|
| + * }
|
| + * ]
|
| + * }
|
| + * ]
|
| + * }
|
| + * ]
|
| + * }
|
| + * ]
|
| + * }
|
| + * ]
|
| + * }
|
| + * ]
|
| + * }
|
| + * ]
|
| + *
|
| + * Any of those layers may be missing except 'value name'.
|
| + */
|
| + buildRows_: function() {
|
| + this.rows_ = [];
|
| + var hierarchy = [];
|
| + this.buildRow_(this.organizedValues_, hierarchy);
|
| + this.rows_ = this.rows_.filter(this.rowMatchesSearch_.bind(this));
|
| + },
|
| +
|
| + get startTimesForDisplayLabels() {
|
| + var startTimesForDisplayLabels = {};
|
| + for (var value of this.values) {
|
| + if (this.summaryValuesByGuid_[value.guid])
|
| + continue;
|
| +
|
| + var displayLabel = getDisplayLabel(value);
|
| + startTimesForDisplayLabels[displayLabel] = Math.min(
|
| + startTimesForDisplayLabels[displayLabel] || 0,
|
| + getIterationInfoField(
|
| + value, 'benchmarkStart', new Date(0)).getTime());
|
| + }
|
| + return startTimesForDisplayLabels;
|
| + },
|
| +
|
| + get displayLabels() {
|
| + var startTimesForDisplayLabels = this.startTimesForDisplayLabels;
|
| + var displayLabels = Object.keys(startTimesForDisplayLabels);
|
| + displayLabels.sort(function(a, b) {
|
| + return startTimesForDisplayLabels[a] - startTimesForDisplayLabels[b];
|
| + });
|
| + return displayLabels;
|
| + },
|
| +
|
| + buildColumn_: function(displayLabel) {
|
| + function getValueForValue(value) {
|
| + return value.numeric instanceof tr.v.Numeric ? value.numeric.average :
|
| + value.numeric.value;
|
| + }
|
| +
|
| + return {
|
| + title: displayLabel,
|
| + align: tr.ui.b.TableFormat.ColumnAlignment.RIGHT,
|
| + supportsCellSelection: true,
|
| + selectable: true,
|
| +
|
| + value: function(row) {
|
| + var cell = row.columns[displayLabel];
|
| + if (cell === undefined)
|
| + return '';
|
| +
|
| + if (cell instanceof tr.v.NumericValue) {
|
| + if (this.$.table.selectedTableColumn &&
|
| + this.$.table.selectedTableColumn.title !== displayLabel) {
|
| + var referenceCell = row.columns[
|
| + this.$.table.selectedTableColumn.title];
|
| +
|
| + if (referenceCell instanceof tr.v.NumericValue &&
|
| + cell.numeric.unit === referenceCell.numeric.unit) {
|
| + var significance = tr.v.Significance.DONT_CARE;
|
| +
|
| + if (cell.numeric instanceof tr.v.Numeric &&
|
| + referenceCell.numeric instanceof tr.v.Numeric) {
|
| + significance = cell.numeric.getDifferenceSignificance(
|
| + referenceCell.numeric);
|
| + }
|
| +
|
| + return tr.v.ui.createScalarSpan(
|
| + getValueForValue(cell) - getValueForValue(referenceCell),
|
| + {unit: cell.numeric.unit.correspondingDeltaUnit,
|
| + significance: significance});
|
| + }
|
| + }
|
| +
|
| + return tr.v.ui.createScalarSpan(cell);
|
| + }
|
| + if (cell instanceof tr.v.d.Diagnostic) {
|
| + var span = tr.v.ui.createDiagnosticSpan(cell);
|
| + span.style.textAlign = 'left';
|
| + return span;
|
| + }
|
| + throw new Error('Invalid cell', cell);
|
| + }.bind(this),
|
| +
|
| + cmp: function(rowA, rowB) {
|
| + var cellA = rowA.columns[displayLabel];
|
| + var cellB = rowB.columns[displayLabel];
|
| + if (!(cellA instanceof tr.v.NumericValue) ||
|
| + !(cellB instanceof tr.v.NumericValue)) {
|
| + return undefined;
|
| + }
|
| +
|
| + var valueA = getValueForValue(cellA);
|
| + var valueB = getValueForValue(cellB);
|
| +
|
| + // If a reference column is selected, compare the *differences*
|
| + // between the two cells and their references.
|
| + if (this.$.table.selectedTableColumn &&
|
| + this.$.table.selectedTableColumn.title !== displayLabel) {
|
| + var referenceColumn = this.$.table.selectedTableColumn.title;
|
| + var referenceCellA = rowA.columns[referenceColumn];
|
| + var referenceCellB = rowB.columns[referenceColumn];
|
| + if (referenceCellA instanceof tr.v.NumericValue &&
|
| + referenceCellB instanceof tr.v.NumericValue &&
|
| + cellA.numeric.unit === referenceCellA.numeric.unit &&
|
| + cellB.numeric.unit === referenceCellB.numeric.unit) {
|
| + valueA -= getValueForValue(referenceCellA);
|
| + valueB -= getValueForValue(referenceCellB);
|
| + }
|
| + }
|
| +
|
| + return valueA - valueB;
|
| + }.bind(this)
|
| + };
|
| + },
|
| +
|
| + buildColumns_: function() {
|
| + this.columns_ = [
|
| + {
|
| + title: 'Name',
|
| + align: tr.ui.b.TableFormat.ColumnAlignment.LEFT,
|
| + supportsCellSelection: false,
|
| +
|
| + value: function(row) {
|
| + var nameEl = document.createElement('span');
|
| + Polymer.dom(nameEl).textContent = row.name;
|
| + if (row.description)
|
| + nameEl.title = row.description;
|
| + nameEl.style.textOverflow = 'ellipsis';
|
| + return nameEl;
|
| + },
|
| +
|
| + cmp: (a, b) => a.name.localeCompare(b.name)
|
| + }
|
| + ];
|
| +
|
| + for (var displayLabel of this.displayLabels)
|
| + this.columns_.push(this.buildColumn_(displayLabel));
|
| }
|
| });
|
|
|
| - tr.ui.registerValueSetView('tr-v-ui-value-set-table');
|
| + tr.v.ui.registerValueSetView('tr-v-ui-value-set-table');
|
|
|
| return {};
|
| });
|
|
|