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/rect.html">
9<link rel="import" href="/tracing/base/utils.html">
10<link rel="import" href="/tracing/model/event_set.html">
11<link rel="import" href="/tracing/model/object_instance.html">
12<link rel="import" href="/tracing/model/object_snapshot.html">
13<link rel="import" href="/tracing/ui/analysis/analysis_link.html">
14<link rel="import" href="/tracing/ui/base/table.html">
15<link rel="import" href="/tracing/ui/base/ui.html">
16<link rel="import" href="/tracing/value/numeric.html">
17<link rel="import" href="/tracing/value/ui/scalar_span.html">
18
19<polymer-element name="tr-ui-a-generic-object-view"
20    is="HTMLUnknownElement">
21  <template>
22    <style>
23    :host {
24      display: block;
25      font-family: monospace;
26    }
27    </style>
28    <div id="content">
29    </div>
30  </template>
31
32  <script>
33  'use strict';
34
35  function isTable(object) {
36    if (!(object instanceof Array) ||
37        (object.length < 2)) return false;
38    for (var colName in object[0]) {
39      if (typeof colName !== 'string') return false;
40    }
41    for (var i = 0; i < object.length; ++i) {
42      if (!(object[i] instanceof Object)) return false;
43      for (var colName in object[i]) {
44        if (i && (object[0][colName] === undefined)) return false;
45        var cellType = typeof object[i][colName];
46        if (cellType !== 'string' && cellType != 'number') return false;
47      }
48      if (i) {
49        for (var colName in object[0]) {
50          if (object[i][colName] === undefined) return false;
51        }
52      }
53    }
54    return true;
55  }
56
57  Polymer({
58    ready: function() {
59      this.object_ = undefined;
60    },
61
62    get object() {
63      return this.object_;
64    },
65
66    set object(object) {
67      this.object_ = object;
68      this.updateContents_();
69    },
70
71    updateContents_: function() {
72      this.$.content.textContent = '';
73      this.appendElementsForType_('', this.object_, 0, 0, 5, '');
74    },
75
76    appendElementsForType_: function(
77        label, object, indent, depth, maxDepth, suffix) {
78      if (depth > maxDepth) {
79        this.appendSimpleText_(
80            label, indent, '<recursion limit reached>', suffix);
81        return;
82      }
83
84      if (object === undefined) {
85        this.appendSimpleText_(label, indent, 'undefined', suffix);
86        return;
87      }
88
89      if (object === null) {
90        this.appendSimpleText_(label, indent, 'null', suffix);
91        return;
92      }
93
94      if (!(object instanceof Object)) {
95        var type = typeof object;
96        if (type == 'string') {
97          var objectReplaced = false;
98          if ((object[0] == '{' && object[object.length - 1] == '}') ||
99              (object[0] == '[' && object[object.length - 1] == ']')) {
100            try {
101              object = JSON.parse(object);
102              objectReplaced = true;
103            } catch (e) {
104            }
105          }
106          if (!objectReplaced) {
107            if (object.indexOf('\n') !== -1) {
108              var lines = object.split('\n');
109              lines.forEach(function(line, i) {
110                var text, ioff, ll, ss;
111                if (i == 0) {
112                  text = '"' + line;
113                  ioff = 0;
114                  ll = label;
115                  ss = '';
116                } else if (i < lines.length - 1) {
117                  text = line;
118                  ioff = 1;
119                  ll = '';
120                  ss = '';
121                } else {
122                  text = line + '"';
123                  ioff = 1;
124                  ll = '';
125                  ss = suffix;
126                }
127
128                var el = this.appendSimpleText_(
129                    ll, indent + ioff * label.length + ioff, text, ss);
130                el.style.whiteSpace = 'pre';
131                return el;
132              }, this);
133              return;
134            } else {
135              this.appendSimpleText_(
136                  label, indent, '"' + object + '"', suffix);
137              return;
138            }
139          }
140          else {
141            /* Fall through to the flow below */
142          }
143        } else {
144          return this.appendSimpleText_(label, indent, object, suffix);
145        }
146      }
147
148      if (object instanceof tr.model.ObjectSnapshot) {
149        var link = document.createElement('tr-ui-a-analysis-link');
150        link.selection = new tr.model.EventSet(object);
151        this.appendElementWithLabel_(label, indent, link, suffix);
152        return;
153      }
154
155      if (object instanceof tr.model.ObjectInstance) {
156        var link = document.createElement('tr-ui-a-analysis-link');
157        link.selection = new tr.model.EventSet(object);
158        this.appendElementWithLabel_(label, indent, link, suffix);
159        return;
160      }
161
162      if (object instanceof tr.b.Rect) {
163        this.appendSimpleText_(label, indent, object.toString(), suffix);
164        return;
165      }
166
167      if (object instanceof tr.v.ScalarNumeric) {
168        var el = this.ownerDocument.createElement('tr-v-ui-scalar-span');
169        el.value = object;
170        this.appendElementWithLabel_(label, indent, el, suffix);
171        return;
172      }
173
174      if (object instanceof Array) {
175        this.appendElementsForArray_(
176            label, object, indent, depth, maxDepth, suffix);
177        return;
178      }
179
180      this.appendElementsForObject_(
181          label, object, indent, depth, maxDepth, suffix);
182    },
183
184    appendElementsForArray_: function(
185        label, object, indent, depth, maxDepth, suffix) {
186      if (object.length == 0) {
187        this.appendSimpleText_(label, indent, '[]', suffix);
188        return;
189      }
190
191      if (isTable(object)) {
192        var table = document.createElement('tr-ui-b-table');
193        var columns = [];
194        tr.b.iterItems(object[0], function(colName) {
195          columns.push({title: colName, value: function(row) {
196            return row[colName];
197          }});
198        });
199        table.tableColumns = columns;
200        table.tableRows = object;
201        this.appendElementWithLabel_(label, indent, table, suffix);
202        table.rebuild();
203        return;
204      }
205
206      this.appendElementsForType_(
207          label + '[',
208          object[0],
209          indent, depth + 1, maxDepth,
210          object.length > 1 ? ',' : ']' + suffix);
211      for (var i = 1; i < object.length; i++) {
212        this.appendElementsForType_(
213            '',
214            object[i],
215            indent + label.length + 1, depth + 1, maxDepth,
216            i < object.length - 1 ? ',' : ']' + suffix);
217      }
218      return;
219    },
220
221    appendElementsForObject_: function(
222        label, object, indent, depth, maxDepth, suffix) {
223      var keys = tr.b.dictionaryKeys(object);
224      if (keys.length == 0) {
225        this.appendSimpleText_(label, indent, '{}', suffix);
226        return;
227      }
228
229      this.appendElementsForType_(
230          label + '{' + keys[0] + ': ',
231          object[keys[0]],
232          indent, depth, maxDepth,
233          keys.length > 1 ? ',' : '}' + suffix);
234      for (var i = 1; i < keys.length; i++) {
235        this.appendElementsForType_(
236            keys[i] + ': ',
237            object[keys[i]],
238            indent + label.length + 1, depth + 1, maxDepth,
239            i < keys.length - 1 ? ',' : '}' + suffix);
240      }
241    },
242
243    appendElementWithLabel_: function(label, indent, dataElement, suffix) {
244      var row = document.createElement('div');
245
246      var indentSpan = document.createElement('span');
247      indentSpan.style.whiteSpace = 'pre';
248      for (var i = 0; i < indent; i++)
249        indentSpan.textContent += ' ';
250      row.appendChild(indentSpan);
251
252      var labelSpan = document.createElement('span');
253      labelSpan.textContent = label;
254      row.appendChild(labelSpan);
255
256      row.appendChild(dataElement);
257      var suffixSpan = document.createElement('span');
258      suffixSpan.textContent = suffix;
259      row.appendChild(suffixSpan);
260
261      row.dataElement = dataElement;
262      this.$.content.appendChild(row);
263    },
264
265    appendSimpleText_: function(label, indent, text, suffix) {
266      var el = this.ownerDocument.createElement('span');
267      el.textContent = text;
268      this.appendElementWithLabel_(label, indent, el, suffix);
269      return el;
270    }
271  });
272  </script>
273</polymer-element>
274
275<polymer-element name="tr-ui-a-generic-object-view-with-label"
276    is="HTMLUnknownElement">
277  <template>
278    <style>
279    :host {
280      display: block;
281    }
282    </style>
283  </template>
284
285  <script>
286  'use strict';
287
288  Polymer({
289    ready: function() {
290      this.labelEl_ = document.createElement('div');
291      this.genericObjectView_ =
292          document.createElement('tr-ui-a-generic-object-view');
293      this.shadowRoot.appendChild(this.labelEl_);
294      this.shadowRoot.appendChild(this.genericObjectView_);
295    },
296
297    get label() {
298      return this.labelEl_.textContent;
299    },
300
301    set label(label) {
302      this.labelEl_.textContent = label;
303    },
304
305    get object() {
306      return this.genericObjectView_.object;
307    },
308
309    set object(object) {
310      this.genericObjectView_.object = object;
311    }
312  });
313  </script>
314</polymer-element>
315