1<!DOCTYPE html>
2<!--
3Copyright (c) 2013 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7
8<link rel="import" href="/tracing/base/base.html">
9<link rel="import" href="/tracing/base/iteration_helpers.html">
10<link rel="import" href="/tracing/base/statistics.html">
11<link rel="import" href="/tracing/model/event_set.html">
12<link rel="import" href="/tracing/ui/analysis/analysis_link.html">
13<link rel="import" href="/tracing/ui/analysis/multi_event_summary.html">
14<link rel="import" href="/tracing/ui/base/table.html">
15<link rel="import" href="/tracing/value/ui/scalar_span.html">
16<link rel="import" href="/tracing/value/unit.html">
17
18</script>
19<polymer-element name='tr-ui-a-multi-event-summary-table'>
20  <template>
21    <style>
22    :host {
23      display: flex;
24    }
25    #table {
26      flex: 1 1 auto;
27      align-self: stretch;
28    }
29    </style>
30    <tr-ui-b-table id="table">
31    </tr-ui-b-table>
32    </div>
33  </template>
34  <script>
35  'use strict';
36
37  Polymer({
38    ready: function() {
39      this.showTotals_ = false;
40      this.eventsHaveDuration_ = true;
41      this.eventsHaveSubRows_ = true;
42      this.eventsByTitle_ = undefined;
43    },
44
45    updateTableColumns_: function(rows, maxValues) {
46      var hasCpuData = false;
47      var hasAlerts = false;
48      rows.forEach(function(row) {
49        if (row.cpuDuration !== undefined)
50          hasCpuData = true;
51        if (row.cpuSelfTime !== undefined)
52          hasCpuData = true;
53        if (row.numAlerts)
54          hasAlerts = true;
55      });
56
57      var ownerDocument = this.ownerDocument;
58
59      var columns = [];
60
61      columns.push({
62        title: 'Name',
63        value: function(row) {
64          if (row.title === 'Totals')
65            return 'Totals';
66
67          var linkEl = document.createElement('tr-ui-a-analysis-link');
68          linkEl.setSelectionAndContent(function() {
69            return new tr.model.EventSet(row.events);
70          }, row.title);
71          return linkEl;
72        },
73        width: '350px',
74        cmp: function(rowA, rowB) {
75          return rowA.title.localeCompare(rowB.title);
76        }
77      });
78      if (this.eventsHaveDuration_) {
79        columns.push({
80          title: 'Wall Duration',
81          value: function(row) {
82            return tr.v.ui.createScalarSpan(row.duration, {
83              unit: tr.v.Unit.byName.timeDurationInMs,
84              total: row.totalsRow ? undefined : maxValues.duration,
85              ownerDocument: ownerDocument,
86              rightAlign: true
87            });
88          },
89          width: '<upated further down>',
90          cmp: function(rowA, rowB) {
91            return rowA.duration - rowB.duration;
92          }
93        });
94      }
95
96      if (this.eventsHaveDuration_ && hasCpuData) {
97        columns.push({
98          title: 'CPU Duration',
99          value: function(row) {
100            return tr.v.ui.createScalarSpan(row.cpuDuration, {
101              unit: tr.v.Unit.byName.timeDurationInMs,
102              total: row.totalsRow ? undefined : maxValues.cpuDuration,
103              ownerDocument: ownerDocument,
104              rightAlign: true
105            });
106          },
107          width: '<upated further down>',
108          cmp: function(rowA, rowB) {
109            return rowA.cpuDuration - rowB.cpuDuration;
110          }
111        });
112      }
113
114      if (this.eventsHaveSubRows_ && this.eventsHaveDuration_) {
115        columns.push({
116          title: 'Self time',
117          value: function(row) {
118            return tr.v.ui.createScalarSpan(row.selfTime, {
119              unit: tr.v.Unit.byName.timeDurationInMs,
120              total: row.totalsRow ? undefined : maxValues.selfTime,
121              ownerDocument: ownerDocument,
122              rightAlign: true
123            });
124          },
125          width: '<upated further down>',
126          cmp: function(rowA, rowB) {
127            return rowA.selfTime - rowB.selfTime;
128          }
129        });
130      }
131
132      if (this.eventsHaveSubRows_ && this.eventsHaveDuration_ && hasCpuData) {
133        columns.push({
134          title: 'CPU Self Time',
135          value: function(row) {
136            return tr.v.ui.createScalarSpan(row.cpuSelfTime, {
137              unit: tr.v.Unit.byName.timeDurationInMs,
138              total: row.totalsRow ? undefined : maxValues.cpuSelfTime,
139              ownerDocument: ownerDocument,
140              rightAlign: true
141            });
142          },
143          width: '<upated further down>',
144          cmp: function(rowA, rowB) {
145            return rowA.cpuSelfTime - rowB.cpuSelfTime;
146          }
147        });
148      }
149      columns.push({
150        title: 'Occurrences',
151        value: function(row) {
152          return row.numEvents;
153        },
154        width: '<upated further down>',
155        cmp: function(rowA, rowB) {
156          return rowA.numEvents - rowB.numEvents;
157        }
158      });
159
160      var alertsColumnIndex;
161      if (hasAlerts) {
162        columns.push({
163          title: 'Num Alerts',
164          value: function(row) {
165            return row.numAlerts;
166          },
167          width: '<upated further down>',
168          cmp: function(rowA, rowB) {
169            return rowA.numAlerts - rowB.numAlerts;
170          }
171        });
172        alertsColumnIndex = columns.length - 1;
173      }
174      var colWidthPercentage;
175      if (columns.length == 1)
176        colWidthPercentage = '100%';
177      else
178        colWidthPercentage = (100 / (columns.length - 1)).toFixed(3) + '%';
179
180      for (var i = 1; i < columns.length; i++)
181        columns[i].width = colWidthPercentage;
182
183      this.$.table.tableColumns = columns;
184
185      if (hasAlerts) {
186        this.$.table.sortColumnIndex = alertsColumnIndex;
187        this.$.table.sortDescending = true;
188      }
189    },
190
191    configure: function(config) {
192      if (config.eventsByTitle === undefined)
193        throw new Error('Required: eventsByTitle');
194
195      if (config.showTotals !== undefined)
196        this.showTotals_ = config.showTotals;
197      else
198        this.showTotals_ = true;
199
200      if (config.eventsHaveDuration !== undefined)
201        this.eventsHaveDuration_ = config.eventsHaveDuration;
202      else
203        this.eventsHaveDuration_ = true;
204
205      if (config.eventsHaveSubRows !== undefined)
206        this.eventsHaveSubRows_ = config.eventsHaveSubRows;
207      else
208        this.eventsHaveSubRows_ = true;
209
210      this.eventsByTitle_ = config.eventsByTitle;
211      this.updateContents_();
212    },
213
214    get showTotals() {
215      return this.showTotals_;
216    },
217
218    set showTotals(showTotals) {
219      this.showTotals_ = showTotals;
220      this.updateContents_();
221    },
222
223    get eventsHaveDuration() {
224      return this.eventsHaveDuration_;
225    },
226
227    set eventsHaveDuration(eventsHaveDuration) {
228      this.eventsHaveDuration_ = eventsHaveDuration;
229      this.updateContents_();
230    },
231
232    get eventsHaveSubRows() {
233      return this.eventsHaveSubRows_;
234    },
235
236    set eventsHaveSubRows(eventsHaveSubRows) {
237      this.eventsHaveSubRows_ = eventsHaveSubRows;
238      this.updateContents_();
239    },
240
241    get eventsByTitle() {
242      return this.eventsByTitle_;
243    },
244
245    set eventsByTitle(eventsByTitle) {
246      this.eventsByTitle_ = eventsByTitle;
247      this.updateContents_();
248    },
249
250    get selectionBounds() {
251      return this.selectionBounds_;
252    },
253
254    set selectionBounds(selectionBounds) {
255      this.selectionBounds_ = selectionBounds;
256      this.updateContents_();
257    },
258
259    updateContents_: function() {
260      var eventsByTitle;
261      if (this.eventsByTitle_ !== undefined)
262        eventsByTitle = this.eventsByTitle_;
263      else
264        eventsByTitle = [];
265
266      var allEvents = [];
267      var rows = [];
268      tr.b.iterItems(
269          eventsByTitle,
270          function(title, eventsOfSingleTitle) {
271            allEvents.push.apply(allEvents, eventsOfSingleTitle);
272            var row = new tr.ui.analysis.MultiEventSummary(
273              title, eventsOfSingleTitle);
274            rows.push(row);
275          });
276
277      this.updateTableColumns_(rows);
278      this.$.table.tableRows = rows;
279
280      var maxValues = {
281        duration: undefined,
282        selfTime: undefined,
283        cpuSelfTime: undefined,
284        cpuDuration: undefined
285      };
286
287      if (this.eventsHaveDuration) {
288        for (var column in maxValues) {
289          maxValues[column] = tr.b.Statistics.max(rows, function(event) {
290                  return event[column];
291          });
292        }
293      }
294
295      var footerRows = [];
296
297      if (this.showTotals_) {
298        var multiEventSummary = new tr.ui.analysis.MultiEventSummary(
299          'Totals', allEvents);
300        footerRows.push(multiEventSummary);
301      }
302
303
304      this.updateTableColumns_(rows, maxValues);
305      this.$.table.tableRows = rows;
306
307      // TODO(selection bounds).
308
309      // TODO(sorting)
310
311      this.$.table.footerRows = footerRows;
312      this.$.table.rebuild();
313    }
314  });
315  </script>
316</polymer-element>
317