Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: tracing/tracing/value/ui/value_set_table.html

Issue 2162963002: [polymer] Merge of master into polymer10-migration (Closed) Base URL: git@github.com:catapult-project/catapult.git@polymer10-migration
Patch Set: Merge polymer10-migration int polymer10-merge Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 <!DOCTYPE html> 1 <!DOCTYPE html>
2 <!-- 2 <!--
3 Copyright 2016 The Chromium Authors. All rights reserved. 3 Copyright 2016 The Chromium Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style license that can be 4 Use of this source code is governed by a BSD-style license that can be
5 found in the LICENSE file. 5 found in the LICENSE file.
6 --> 6 -->
7 7
8 <link rel="import" href="/tracing/base/raf.html"> 8 <link rel="import" href="/tracing/base/raf.html">
9 <link rel="import" href="/tracing/ui/base/table.html"> 9 <link rel="import" href="/tracing/ui/base/table.html">
10 <link rel="import" href="/tracing/value/ui/diagnostic_span.html"> 10 <link rel="import" href="/tracing/value/ui/diagnostic_span.html">
(...skipping 10 matching lines...) Expand all
21 flex-direction: column; 21 flex-direction: column;
22 } 22 }
23 table-container { 23 table-container {
24 display: flex; 24 display: flex;
25 min-height: 0px; 25 min-height: 0px;
26 overflow-y: auto; 26 overflow-y: auto;
27 } 27 }
28 div#error { 28 div#error {
29 color: red; 29 color: red;
30 } 30 }
31 #histogram {
32 display: none;
33 }
34 #search {
35 max-width: 20em;
36 margin-top: 5px;
37 margin-bottom: 5px;
38 }
31 </style> 39 </style>
32 40
41 <input id="search" placeholder="Find Value name">
42 <div>
43 <input type="checkbox" id="show_all">
44 <label for="show_all">Show all</label>
45 </div>
33 <div id="error"></div> 46 <div id="error"></div>
34 <table-container> 47 <table-container>
35 <tr-ui-b-table id="table"></tr-ui-b-table> 48 <tr-ui-b-table id="table"></tr-ui-b-table>
36 </table-container> 49 </table-container>
37 <tr-v-ui-histogram-span id="histogram"></tr-v-ui-histogram-span> 50 <tr-v-ui-histogram-span id="histogram"></tr-v-ui-histogram-span>
38 </template> 51 </template>
39 </dom-module> 52 </dom-module>
40 53
41 <script> 54 <script>
42 'use strict'; 55 'use strict';
43 tr.exportTo('tr.ui', function() { 56 tr.exportTo('tr.ui', function() {
57
58 /**
59 * @param {!tr.v.Value} value
60 * @param {string} fieldName
61 * @param {*} defaultValue
62 * @return {*}
63 */
64 function getIterationInfoField(value, fieldName, defaultValue) {
65 var iteration = tr.v.d.IterationInfo.getFromValue(value);
66 if (!(iteration instanceof tr.v.d.IterationInfo))
67 return defaultValue;
68 return iteration[fieldName];
69 }
70
71 /**
72 * @param {!tr.v.Value} value
73 * @param {string} fieldName
74 * @return {string}
75 */
76 function getStoryGroupingKeyLabel(value, storyGroupingKey) {
77 var iteration = tr.v.d.IterationInfo.getFromValue(value);
78 if (!(iteration instanceof tr.v.d.IterationInfo))
79 return storyGroupingKey + ': undefined';
80 return storyGroupingKey + ': ' +
81 iteration.storyGroupingKeys[storyGroupingKey];
82 }
83
84 /**
85 * @param {!tr.v.Value} value
86 * @return {string}
87 */
88 var getDisplayLabel = v => getIterationInfoField(v, 'displayLabel', 'Value');
89
90 var SELECTED_VALUE_SETTINGS_KEY = 'tr-v-ui-value-set-table-value';
91 var SHOW_ALL_SETTINGS_KEY = 'tr-v-ui-value-set-table-show-all';
92
93 /**
94 * Recursively groups |values|.
95 * TODO(benjhayden): Use ES6 Maps instead of dictionaries?
96 *
97 * @param {!Array.<!tr.v.Value>} values
98 * @param {!Array.<!function(!tr.v.Value):(string|number)>} groupingCallbacks
99 * @return {!(Object|tr.v.Value)}
100 */
101 function organizeValues(values, groupingCallbacks, level) {
102 if (groupingCallbacks.length === level) {
103 // Recursion base case: there should only be a single value when we've
104 // grouped by every possible grouping.
105 if (values.length > 1) {
106 console.warn('Multiple Values with same name, benchmarkName, ' +
107 'storyGroupingKeys, storyName, start, storysetRepeatCounter, ' +
108 'storyRepeatCounter, and displayLabel', values);
109 }
110 return values[0];
111 }
112
113 // Group the values by the current grouping.
114 var groupedValues = tr.b.group(values, groupingCallbacks[level]);
115
116 // Skip this grouping level if it contains only a single group,
117 // but never skip the zeroth grouping level (value name) nor the last
118 // (displayLabel).
119 if (level > 0 && level < (groupingCallbacks.length - 1) &&
120 tr.b.dictionaryLength(groupedValues) === 1) {
121 return organizeValues(values, groupingCallbacks, level + 1);
122 }
123
124 // Recursively group groupedValues.
125 return tr.b.mapItems(groupedValues, function(key, groupValues) {
126 return organizeValues(groupValues, groupingCallbacks, level + 1);
127 });
128 }
129
44 Polymer({ 130 Polymer({
45 is: 'tr-v-ui-value-set-table', 131 is: 'tr-v-ui-value-set-table',
46 132
47 /** 133 /**
48 * Return true if this view supports this ValueSet. 134 * Return true if this view supports this ValueSet.
49 * Value-set-table supports all possible metrics, so it always returns true. 135 * Value-set-table supports all possible metrics, so it always returns true.
50 * 136 *
51 * @param {!tr.v.ValueSet} values 137 * @param {!tr.v.ValueSet} values
52 * @return {boolean} 138 * @return {boolean}
53 */ 139 */
54 supportsValueSet: function(values) { 140 supportsValueSet: function(values) {
55 return true; 141 return true;
56 }, 142 },
57 143
58 /** 144 /**
59 * This can optionally depend on the ValueSet. 145 * This can optionally depend on the ValueSet.
60 * 146 *
61 * @return {string} 147 * @return {string}
62 */ 148 */
63 get tabLabel() { 149 get tabLabel() {
64 return 'Table'; 150 return 'Table';
65 }, 151 },
66 152
153 created: function() {
154 // TODO(benjhayden): Should these all be ValueSets?
155 /** @type {undefined|!tr.v.ValueSet} */
156 this.values_ = undefined;
157 /** @type {!Array.<!tr.v.Value>} */
158 this.sourceValues_ = [];
159 /** @type {!Object} */
160 this.summaryValuesByGuid_ = {};
161
162 this.rows_ = undefined;
163 this.columns_ = undefined;
164 },
165
67 ready: function() { 166 ready: function() {
68 this.$.table.sortDescending = true; 167 this.$.table.selectionMode = tr.ui.b.TableFormat.SelectionMode.CELL;
69 this.$.table.selectionMode = tr.ui.b.TableFormat.SelectionMode.ROW;
70 this.$.table.tableColumns = [
71 {
72 title: 'Name',
73 value: function(value) {
74 var nameEl = document.createElement('span');
75 Polymer.dom(nameEl).textContent = value.name;
76 if (value.description)
77 nameEl.title = value.description;
78 nameEl.style.textOverflow = 'ellipsis';
79 return nameEl;
80 },
81 cmp: function(a, b) {
82 return a.name.localeCompare(b.name);
83 }
84 },
85 {
86 title: 'Value',
87 align: tr.ui.b.TableFormat.ColumnAlignment.RIGHT,
88 value: function(value) {
89 if (value.diagnostic instanceof tr.v.d.Diagnostic)
90 return tr.v.ui.createDiagnosticSpan(value.diagnostic);
91 if (value.unit)
92 return tr.v.ui.createScalarSpan(value.value, {unit: value.unit});
93 return value.value;
94 },
95 cmp: function(a, b) {
96 return a.value - b.value;
97 }
98 }
99 ];
100 this.$.table.sortColumnIndex = 1;
101 this.$.table.addEventListener('selection-changed', 168 this.$.table.addEventListener('selection-changed',
102 this.onRowSelected_.bind(this)); 169 this.onSelectionChanged_.bind(this));
103 }, 170 this.$.table.addEventListener('selected-column-changed',
104 171 this.onSelectedColumnChanged_.bind(this));
105 onRowSelected_: function() { 172 this.addEventListener('requestSelectionChange',
173 this.onRelatedValueSelected_.bind(this));
174 this.$.search.addEventListener('keyup', this.onSearch_.bind(this));
175 this.$.show_all.checked = tr.b.Settings.get(SHOW_ALL_SETTINGS_KEY, false);
176 this.$.show_all.addEventListener('change',
177 this.onShowAllChange_.bind(this));
178 },
179
180 onShowAllChange_: function() {
181 tr.b.Settings.set(SHOW_ALL_SETTINGS_KEY, this.$.show_all.checked);
182 this.updateContents_();
183 },
184
185 onSelectedColumnChanged_: function() {
186 // Force the table to rebuild the cell values without forgetting which
187 // rows were expanded.
188 var expansionStates = this.getExpansionStates_(this.rows_);
189 this.$.table.tableRows = this.rows_;
190 this.setExpansionStates_(expansionStates, this.rows_);
191 },
192
193 getExpansionStates_: function(rows) {
194 var states = {};
195 for (var i = 0; i < rows.length; ++i) {
196 var row = rows[i];
197 if (row.subRows && row.subRows.length &&
198 this.$.table.getExpandedForTableRow(row)) {
199 states[i] = this.getExpansionStates_(row.subRows);
200 }
201 }
202 return states;
203 },
204
205 setExpansionStates_: function(states, rows) {
206 for (var i = 0; i < rows.length; ++i) {
207 if (states[i]) {
208 this.$.table.setExpandedForTableRow(rows[i], true);
209 this.setExpansionStates_(states[i], rows[i].subRows);
210 }
211 }
212 },
213
214 onSearch_: function() {
215 this.updateContents_();
216 },
217
218 rowMatchesSearch_: function(row) {
219 return row.name.indexOf(this.$.search.value) >= 0;
220 },
221
222 onRelatedValueSelected_: function(event) {
223 var value = event.selection;
224 if (!(value instanceof tr.v.Value))
225 return;
226
227 event.stopPropagation();
228
229 var displayLabel = getDisplayLabel(value);
230 var columnIndex = -1;
231 for (var i = 0; i < this.columns_.length; ++i) {
232 if (this.columns_[i].title === displayLabel) {
233 columnIndex = i;
234 break;
235 }
236 }
237 if (columnIndex < 0)
238 return;
239
240 var hierarchy = [];
241 var found = false;
242 function search(row) {
243 if (row.columns[displayLabel] === value) {
244 for (var hirow in hierarchy) {
245 this.$.table.setExpandedForTableRow(hirow, true);
246 }
247 found = true;
248 this.$.table.selectedTableRow = row;
249 this.$.table.selectedColumnIndex = columnIndex;
250 return;
251 }
252 if (!row.subRows)
253 return;
254 hierarchy.push(row);
255 row.subRows.forEach(search, this);
256 hierarchy.pop(row);
257 }
258 this.rows_.forEach(search, this);
259
260 if (found || this.$.show_all.checked)
261 return;
262
263 // Search hidden values for |value|.
264 for (var test of this.values) {
265 // Skip values that are already displayed -- we would have found them
266 // in search() above.
267 if (this.sourceValues_.indexOf(test) >= 0)
268 continue;
269
270 if (test === value) {
271 this.$.show_all.checked = true;
272 this.onShowAllChange_();
273 this.onRelatedValueSelected_(event);
274 break;
275 }
276 }
277 },
278
279 onSelectionChanged_: function() {
106 var row = this.$.table.selectedTableRow; 280 var row = this.$.table.selectedTableRow;
107 if (row && row.numeric) { 281 var col = this.$.table.selectedColumnIndex;
108 this.$.histogram.style.display = ''; 282 var cell = undefined;
109 this.$.histogram.histogram = row.numeric; 283 if (row && col && this.columns_)
284 cell = row.columns[this.columns_[col].title];
285
286 if ((cell instanceof tr.v.NumericValue) &&
287 (cell.numeric instanceof tr.v.Numeric)) {
288 this.$.histogram.style.display = 'block';
289 this.$.histogram.histogram = cell.numeric;
290
291 tr.b.Settings.set(SELECTED_VALUE_SETTINGS_KEY, JSON.stringify({
292 row: row.name,
293 column: this.columns_[col].title
294 }));
110 } else { 295 } else {
111 this.$.histogram.style.display = 'none'; 296 this.$.histogram.style.display = 'none';
112 } 297 }
113 }, 298 },
114 299
300 handleFailureValues_: function() {
301 this.values.map(function(value) {
302 if (value instanceof tr.v.FailureValue) {
303 Polymer.dom(this.$.error).textContent = value.description;
304 this.$.table.style.display = 'none';
305 this.style.width = '10em';
306 }
307 }, this);
308 },
309
310 addDiagnosticSubRows_: function(value, row, column) {
311 value.diagnostics.forEach(function(name, diagnostic) {
312 if (name === tr.v.SUMMARY_VALUE_MAP_DIAGNOSTIC_NAME)
313 return;
314
315 // If a previous |value| had a diagnostic with the same name, then
316 // there is already a subRow that should contain this diagnostic.
317 for (var subRow of row.subRows) {
318 if (subRow.name === name) {
319 subRow.columns[column] = diagnostic;
320 return;
321 }
322 }
323
324 // This is the first time that a diagnostic with this name has been
325 // seen for Values whose name is |value.name|, so create a new subRow.
326 var subRow = {name: name, columns: {}};
327 subRow.columns[column] = diagnostic;
328 row.subRows.push(subRow);
329 });
330 },
331
332 get values() {
333 return this.values_;
334 },
335
336 findSummaryValues_: function() {
337 this.summaryValuesByGuid_ = {};
338 this.values.map(function(value) {
339 var summaryValueMap = value.diagnostics.get(
340 tr.v.SUMMARY_VALUE_MAP_DIAGNOSTIC_NAME);
341 if (!(summaryValueMap instanceof tr.v.d.RelatedValueMap))
342 return;
343
344 summaryValueMap.values.forEach(function(summaryValue) {
345 this.summaryValuesByGuid_[summaryValue.guid] = summaryValue;
346 }, this);
347 }, this);
348 },
349
115 /** 350 /**
116 * @param {!tr.v.ValueSet} values 351 * @param {!tr.v.ValueSet} values
117 */ 352 */
118 set values(values) { 353 set values(values) {
354 this.values_ = values;
355 this.sourceValues_ = values.sourceValues;
356 this.findSummaryValues_();
357 this.updateContents_();
358 },
359
360 updateContents_: function() {
361 if (!this.values_)
362 return;
119 this.style.width = ''; 363 this.style.width = '';
120 this.$.table.style.display = ''; 364 this.$.table.style.display = '';
121 Polymer.dom(this.$.error).textContent = ''; 365 Polymer.dom(this.$.error).textContent = '';
122 366 this.$.histogram.style.display = 'none';
123 values.map(function(value) { 367
124 if (value instanceof tr.v.FailureValue) { 368 this.handleFailureValues_();
125 Polymer.dom(this.$.error).textContent = value.description;
126 this.$.table.style.display = 'none';
127 this.style.width = '10em';
128 }
129 }, this);
130 if (Polymer.dom(this.$.error).textContent) 369 if (Polymer.dom(this.$.error).textContent)
131 return; 370 return;
132 371
133 this.$.table.tableRows = values.map(function(value) { 372 this.buildRows_();
134 var row = { 373
135 name: value.name, 374 if (this.rows_.length === 0) {
136 value: '', 375 Polymer.dom(this.$.error).textContent = 'zero values';
137 unit: undefined, 376 this.$.table.style.display = 'none';
138 description: value.description, 377 this.style.width = '10em';
139 subRows: [] 378 return;
140 }; 379 }
141 380
142 if (value.numeric) { 381 this.buildColumns_();
143 row.unit = value.numeric.unit; 382
144 if (value.numeric.value !== undefined) { 383 this.$.table.tableColumns = this.columns_;
145 row.value = value.numeric.value; 384 this.$.table.tableRows = this.rows_;
146 } else if (value.numeric.average !== undefined) { 385 this.$.table.sortColumnIndex = 0;
147 row.numeric = value.numeric;
148 row.value = value.numeric.average;
149 }
150 }
151
152 value.diagnostics.forEach(function(name, diagnostic) {
153 row.subRows.push({
154 name: name,
155 diagnostic: diagnostic
156 });
157 });
158
159 return row;
160 });
161
162 this.$.table.rebuild(); 386 this.$.table.rebuild();
163 this.onRowSelected_(); 387 this.selectValue_();
164 388
165 tr.b.requestAnimationFrame(function() { 389 tr.b.requestAnimationFrame(function() {
166 this.style.width = this.$.table.getBoundingClientRect().width; 390 this.style.width = this.$.table.getBoundingClientRect().width;
167 }, this); 391 }, this);
392 },
393
394 selectValue_: function() {
395 var selectedValue = tr.b.Settings.get(
396 SELECTED_VALUE_SETTINGS_KEY, undefined);
397 if (selectedValue) {
398 selectedValue = JSON.parse(selectedValue);
399 for (var row of this.rows_) {
400 if (row.name === selectedValue.row) {
401 for (var coli = 1; coli < this.columns_.length; ++coli) {
402 var column = this.columns_[coli];
403 if (column.title === selectedValue.column) {
404 this.$.table.selectedTableRow = row;
405 this.$.table.selectedColumnIndex = coli;
406 return;
407 }
408 }
409 }
410 }
411 }
412 this.$.table.selectedTableRow = this.rows_[0];
413 this.$.table.selectedColumnIndex = 1;
414 },
415
416 /**
417 * Build table rows recursively from organized Values. The recursion stack
418 * of subRows is maintained in |hierarchy|.
419 *
420 * @param {!Object} organizedValues
421 * @param {!Array.<!Object>} hierarchy
422 */
423 buildRow_: function(organizedValues, hierarchy) {
424 tr.b.iterItems(organizedValues, function(name, value) {
425 if (value instanceof tr.v.Value) {
426 // This recursion base case corresponds to the recursion base case of
427 // organizeValues(). The last groupingCallback is getDisplayLabel,
428 // which defines the columns of the table.
429
430 // Merge Values up the grouping hierarchy.
431 for (var row of hierarchy) {
432 if (row.description === undefined)
433 row.description = value.description;
434
435 if (row.columns[name])
436 row.columns[name] = row.columns[name].merge(value);
437 else
438 row.columns[name] = value;
439 }
440
441 var row = hierarchy[hierarchy.length - 1];
442 this.addDiagnosticSubRows_(value, row, name);
443 } else {
444 // |value| is actually a nested organizedValues.
445 var row = {name: name, subRows: [], columns: {}};
446 hierarchy.push(row);
447 this.buildRow_(value, hierarchy);
448 hierarchy.pop();
449
450 if (hierarchy.length === 0)
451 this.rows_.push(row);
452 else
453 hierarchy[hierarchy.length - 1].subRows.push(row);
454 }
455 }, this);
456 },
457
458 get storyGroupingKeys() {
459 var keys = new Set();
460 for (var value of this.values) {
461 var iteration = tr.v.d.IterationInfo.getFromValue(value);
462 if (!(iteration instanceof tr.v.d.IterationInfo) ||
463 !iteration.storyGroupingKeys)
464 continue;
465
466 for (var key in iteration.storyGroupingKeys)
467 keys.add(key);
468 }
469 return [...keys.values()].sort();
470 },
471
472 /**
473 * A ValueSet is a flat set of Values. Value-set-table must present a
474 * hierarchical view. This method recursively groups this.values as an
475 * intermediate step towards building tableRows in buildRow_().
476 * {
477 * valueA: {
478 * benchmarkA: {
479 * storyA: {
480 * startA: {
481 * storysetRepeatCounterA: {
482 * storyRepeatCounterA: {
483 * displayLabelA: Value,
484 * displayLabelB: Value
485 * }
486 * }
487 * }
488 * }
489 * }
490 * }
491 * }
492 * @return {!Object}
493 */
494 get organizedValues_() {
495 var showingValues = this.$.show_all.checked ?
496 this.values : this.sourceValues_;
497 var values = [];
498 for (var value of showingValues)
499 if (this.summaryValuesByGuid_[value.guid] === undefined)
500 values.push(value);
501
502 var groupingCallbacks = [];
503 groupingCallbacks.push(v => v.name);
504 groupingCallbacks.push(
505 v => getIterationInfoField(v, 'benchmarkName', ''));
506 for (var storyGroupingKey of this.storyGroupingKeys) {
507 // Javascript closures are dumb.
508 groupingCallbacks.push((sgk => (
509 v => getStoryGroupingKeyLabel(v, sgk)))(storyGroupingKey));
510 }
511 groupingCallbacks.push(
512 v => getIterationInfoField(v, 'storyDisplayName', ''));
513 groupingCallbacks.push(
514 v => getIterationInfoField(v, 'benchmarkStartString', ''));
515 groupingCallbacks.push(
516 v => getIterationInfoField(v, 'storysetRepeatCounterLabel', 0));
517 groupingCallbacks.push(
518 v => getIterationInfoField(v, 'storyRepeatCounterLabel', 0));
519 groupingCallbacks.push(getDisplayLabel);
520
521 return organizeValues(values, groupingCallbacks, 0);
522 },
523
524 /* this.rows_ will look something like
525 * [
526 * {
527 * name: 'value name',
528 * columns: {
529 * displayLabelA: Value,
530 * displayLabelB: Value,
531 * },
532 * subRows: [
533 * {
534 * name: 'benchmark name if multiple',
535 * columns: {
536 * displayLabelA: Value,
537 * displayLabelB: Value,
538 * },
539 * subRows: [
540 * {
541 * name: 'story name if multiple',
542 * columns: {
543 * displayLabelA: Value,
544 * displayLabelB: Value,
545 * },
546 * subRows: [
547 * {
548 * name: 'benchmark start if multiple',
549 * columns: {
550 * displayLabelA: Value,
551 * displayLabelB: Value,
552 * },
553 * subRows: [
554 * {
555 * name: 'storyset repeat counter if multiple',
556 * columns: {
557 * displayLabelA: Value,
558 * displayLabelB: Value,
559 * },
560 * subRows: [
561 * {
562 * name: 'story repeat counter if multiple',
563 * columns: {
564 * displayLabelA: Value,
565 * displayLabelB: Value,
566 * },
567 * subRows: [
568 * {
569 * name: 'diagnostic map key',
570 * columns: {
571 * displayLabelA: Diagnostic,
572 * displayLabelB: Diagnostic,
573 * },
574 * }
575 * ]
576 * }
577 * ]
578 * }
579 * ]
580 * }
581 * ]
582 * }
583 * ]
584 * }
585 * ]
586 * }
587 * ]
588 *
589 * Any of those layers may be missing except 'value name'.
590 */
591 buildRows_: function() {
592 this.rows_ = [];
593 var hierarchy = [];
594 this.buildRow_(this.organizedValues_, hierarchy);
595 this.rows_ = this.rows_.filter(this.rowMatchesSearch_.bind(this));
596 },
597
598 get startTimesForDisplayLabels() {
599 var startTimesForDisplayLabels = {};
600 for (var value of this.values) {
601 if (this.summaryValuesByGuid_[value.guid])
602 continue;
603
604 var displayLabel = getDisplayLabel(value);
605 startTimesForDisplayLabels[displayLabel] = Math.min(
606 startTimesForDisplayLabels[displayLabel] || 0,
607 getIterationInfoField(
608 value, 'benchmarkStart', new Date(0)).getTime());
609 }
610 return startTimesForDisplayLabels;
611 },
612
613 get displayLabels() {
614 var startTimesForDisplayLabels = this.startTimesForDisplayLabels;
615 var displayLabels = Object.keys(startTimesForDisplayLabels);
616 displayLabels.sort(function(a, b) {
617 return startTimesForDisplayLabels[a] - startTimesForDisplayLabels[b];
618 });
619 return displayLabels;
620 },
621
622 buildColumn_: function(displayLabel) {
623 function getValueForValue(value) {
624 return value.numeric instanceof tr.v.Numeric ? value.numeric.average :
625 value.numeric.value;
626 }
627
628 return {
629 title: displayLabel,
630 align: tr.ui.b.TableFormat.ColumnAlignment.RIGHT,
631 supportsCellSelection: true,
632 selectable: true,
633
634 value: function(row) {
635 var cell = row.columns[displayLabel];
636 if (cell === undefined)
637 return '';
638
639 if (cell instanceof tr.v.NumericValue) {
640 if (this.$.table.selectedTableColumn &&
641 this.$.table.selectedTableColumn.title !== displayLabel) {
642 var referenceCell = row.columns[
643 this.$.table.selectedTableColumn.title];
644
645 if (referenceCell instanceof tr.v.NumericValue &&
646 cell.numeric.unit === referenceCell.numeric.unit) {
647 var significance = tr.v.Significance.DONT_CARE;
648
649 if (cell.numeric instanceof tr.v.Numeric &&
650 referenceCell.numeric instanceof tr.v.Numeric) {
651 significance = cell.numeric.getDifferenceSignificance(
652 referenceCell.numeric);
653 }
654
655 return tr.v.ui.createScalarSpan(
656 getValueForValue(cell) - getValueForValue(referenceCell),
657 {unit: cell.numeric.unit.correspondingDeltaUnit,
658 significance: significance});
659 }
660 }
661
662 return tr.v.ui.createScalarSpan(cell);
663 }
664 if (cell instanceof tr.v.d.Diagnostic) {
665 var span = tr.v.ui.createDiagnosticSpan(cell);
666 span.style.textAlign = 'left';
667 return span;
668 }
669 throw new Error('Invalid cell', cell);
670 }.bind(this),
671
672 cmp: function(rowA, rowB) {
673 var cellA = rowA.columns[displayLabel];
674 var cellB = rowB.columns[displayLabel];
675 if (!(cellA instanceof tr.v.NumericValue) ||
676 !(cellB instanceof tr.v.NumericValue)) {
677 return undefined;
678 }
679
680 var valueA = getValueForValue(cellA);
681 var valueB = getValueForValue(cellB);
682
683 // If a reference column is selected, compare the *differences*
684 // between the two cells and their references.
685 if (this.$.table.selectedTableColumn &&
686 this.$.table.selectedTableColumn.title !== displayLabel) {
687 var referenceColumn = this.$.table.selectedTableColumn.title;
688 var referenceCellA = rowA.columns[referenceColumn];
689 var referenceCellB = rowB.columns[referenceColumn];
690 if (referenceCellA instanceof tr.v.NumericValue &&
691 referenceCellB instanceof tr.v.NumericValue &&
692 cellA.numeric.unit === referenceCellA.numeric.unit &&
693 cellB.numeric.unit === referenceCellB.numeric.unit) {
694 valueA -= getValueForValue(referenceCellA);
695 valueB -= getValueForValue(referenceCellB);
696 }
697 }
698
699 return valueA - valueB;
700 }.bind(this)
701 };
702 },
703
704 buildColumns_: function() {
705 this.columns_ = [
706 {
707 title: 'Name',
708 align: tr.ui.b.TableFormat.ColumnAlignment.LEFT,
709 supportsCellSelection: false,
710
711 value: function(row) {
712 var nameEl = document.createElement('span');
713 Polymer.dom(nameEl).textContent = row.name;
714 if (row.description)
715 nameEl.title = row.description;
716 nameEl.style.textOverflow = 'ellipsis';
717 return nameEl;
718 },
719
720 cmp: (a, b) => a.name.localeCompare(b.name)
721 }
722 ];
723
724 for (var displayLabel of this.displayLabels)
725 this.columns_.push(this.buildColumn_(displayLabel));
168 } 726 }
169 }); 727 });
170 728
171 tr.ui.registerValueSetView('tr-v-ui-value-set-table'); 729 tr.v.ui.registerValueSetView('tr-v-ui-value-set-table');
172 730
173 return {}; 731 return {};
174 }); 732 });
175 </script> 733 </script>
OLDNEW
« no previous file with comments | « tracing/tracing/value/ui/scalar_span_test.html ('k') | tracing/tracing/value/ui/value_set_table_test.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698