blob: 969a9871a90acd6c057420a990a0c808266b4a12 [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 2013 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/math/rect.html">
<link rel="import" href="/tracing/base/scalar.html">
<link rel="import" href="/tracing/base/utils.html">
<link rel="import" href="/tracing/model/event_set.html">
<link rel="import" href="/tracing/model/object_instance.html">
<link rel="import" href="/tracing/model/object_snapshot.html">
<link rel="import" href="/tracing/ui/analysis/analysis_link.html">
<link rel="import" href="/tracing/ui/base/table.html">
<link rel="import" href="/tracing/ui/base/ui.html">
<link rel="import" href="/tracing/value/ui/scalar_span.html">
<dom-module id='tr-ui-a-generic-object-view'>
<template>
<style>
:host {
display: block;
font-family: monospace;
}
</style>
<div id="content">
</div>
</template>
</dom-module>
<script>
'use strict';
function isTable(object) {
if (!(object instanceof Array) ||
(object.length < 2)) return false;
for (const colName in object[0]) {
if (typeof colName !== 'string') return false;
}
for (let i = 0; i < object.length; ++i) {
if (!(object[i] instanceof Object)) return false;
for (const colName in object[i]) {
if (i && (object[0][colName] === undefined)) return false;
const cellType = typeof object[i][colName];
if (cellType !== 'string' && cellType !== 'number') return false;
}
if (i) {
for (const colName in object[0]) {
if (object[i][colName] === undefined) return false;
}
}
}
return true;
}
Polymer({
is: 'tr-ui-a-generic-object-view',
ready() {
this.object_ = undefined;
},
get object() {
return this.object_;
},
set object(object) {
this.object_ = object;
this.updateContents_();
},
updateContents_() {
Polymer.dom(this.$.content).textContent = '';
this.appendElementsForType_('', this.object_, 0, 0, 5, '');
},
appendElementsForType_(
label, object, indent, depth, maxDepth, suffix) {
if (depth > maxDepth) {
this.appendSimpleText_(
label, indent, '<recursion limit reached>', suffix);
return;
}
if (object === undefined) {
this.appendSimpleText_(label, indent, 'undefined', suffix);
return;
}
if (object === null) {
this.appendSimpleText_(label, indent, 'null', suffix);
return;
}
if (!(object instanceof Object)) {
const type = typeof object;
if (type !== 'string') {
return this.appendSimpleText_(label, indent, String(object), suffix);
}
let objectReplaced = false;
if ((object[0] === '{' && object[object.length - 1] === '}') ||
(object[0] === '[' && object[object.length - 1] === ']')) {
try {
object = JSON.parse(object);
objectReplaced = true;
} catch (e) {
}
}
if (!objectReplaced) {
if (object.includes('\n')) {
const lines = object.split('\n');
lines.forEach(function(line, i) {
let text;
let ioff;
let ll;
let ss;
if (i === 0) {
text = '"' + line;
ioff = 0;
ll = label;
ss = '';
} else if (i < lines.length - 1) {
text = line;
ioff = 1;
ll = '';
ss = '';
} else {
text = line + '"';
ioff = 1;
ll = '';
ss = suffix;
}
const el = this.appendSimpleText_(
ll, indent + ioff * label.length + ioff, text, ss);
el.style.whiteSpace = 'pre';
return el;
}, this);
return;
}
if (tr.b.isUrl(object)) {
const link = document.createElement('a');
link.href = object;
link.textContent = object;
this.appendElementWithLabel_(label, indent, link, suffix);
return;
}
this.appendSimpleText_(
label, indent, '"' + object + '"', suffix);
return;
}
}
if (object instanceof tr.model.ObjectSnapshot) {
const link = document.createElement('tr-ui-a-analysis-link');
link.selection = new tr.model.EventSet(object);
this.appendElementWithLabel_(label, indent, link, suffix);
return;
}
if (object instanceof tr.model.ObjectInstance) {
const link = document.createElement('tr-ui-a-analysis-link');
link.selection = new tr.model.EventSet(object);
this.appendElementWithLabel_(label, indent, link, suffix);
return;
}
if (object instanceof tr.b.math.Rect) {
this.appendSimpleText_(label, indent, object.toString(), suffix);
return;
}
if (object instanceof tr.b.Scalar) {
const el = this.ownerDocument.createElement('tr-v-ui-scalar-span');
el.value = object;
el.inline = true;
this.appendElementWithLabel_(label, indent, el, suffix);
return;
}
if (object instanceof Array) {
this.appendElementsForArray_(
label, object, indent, depth, maxDepth, suffix);
return;
}
this.appendElementsForObject_(
label, object, indent, depth, maxDepth, suffix);
},
appendElementsForArray_(
label, object, indent, depth, maxDepth, suffix) {
if (object.length === 0) {
this.appendSimpleText_(label, indent, '[]', suffix);
return;
}
if (isTable(object)) {
const table = document.createElement('tr-ui-b-table');
const columns = [];
for (const colName of Object.keys(object[0])) {
let allStrings = true;
let allNumbers = true;
for (let i = 0; i < object.length; ++i) {
if (typeof(object[i][colName]) !== 'string') {
allStrings = false;
}
if (typeof(object[i][colName]) !== 'number') {
allNumbers = false;
}
if (!allStrings && !allNumbers) break;
}
const column = {title: colName};
column.value = function(row) {
return row[colName];
};
if (allStrings) {
column.cmp = function(x, y) {
return x[colName].localeCompare(y[colName]);
};
} else if (allNumbers) {
column.cmp = function(x, y) {
return x[colName] - y[colName];
};
}
columns.push(column);
}
table.tableColumns = columns;
table.tableRows = object;
this.appendElementWithLabel_(label, indent, table, suffix);
table.rebuild();
return;
}
this.appendElementsForType_(
label + '[',
object[0],
indent, depth + 1, maxDepth,
object.length > 1 ? ',' : ']' + suffix);
for (let i = 1; i < object.length; i++) {
this.appendElementsForType_(
'',
object[i],
indent + label.length + 1, depth + 1, maxDepth,
i < object.length - 1 ? ',' : ']' + suffix);
}
return;
},
appendElementsForObject_(
label, object, indent, depth, maxDepth, suffix) {
const keys = Object.keys(object);
if (keys.length === 0) {
this.appendSimpleText_(label, indent, '{}', suffix);
return;
}
this.appendElementsForType_(
label + '{' + keys[0] + ': ',
object[keys[0]],
indent, depth, maxDepth,
keys.length > 1 ? ',' : '}' + suffix);
for (let i = 1; i < keys.length; i++) {
this.appendElementsForType_(
keys[i] + ': ',
object[keys[i]],
indent + label.length + 1, depth + 1, maxDepth,
i < keys.length - 1 ? ',' : '}' + suffix);
}
},
appendElementWithLabel_(label, indent, dataElement, suffix) {
const row = document.createElement('div');
const indentSpan = document.createElement('span');
indentSpan.style.whiteSpace = 'pre';
for (let i = 0; i < indent; i++) {
Polymer.dom(indentSpan).textContent += ' ';
}
Polymer.dom(row).appendChild(indentSpan);
const labelSpan = document.createElement('span');
Polymer.dom(labelSpan).textContent = label;
Polymer.dom(row).appendChild(labelSpan);
Polymer.dom(row).appendChild(dataElement);
const suffixSpan = document.createElement('span');
Polymer.dom(suffixSpan).textContent = suffix;
Polymer.dom(row).appendChild(suffixSpan);
row.dataElement = dataElement;
Polymer.dom(this.$.content).appendChild(row);
},
appendSimpleText_(label, indent, text, suffix) {
const el = this.ownerDocument.createElement('span');
Polymer.dom(el).textContent = text;
this.appendElementWithLabel_(label, indent, el, suffix);
return el;
}
});
</script>
<dom-module id='tr-ui-a-generic-object-view-with-label'>
<template>
<style>
:host {
display: block;
}
</style>
</template>
</dom-module>
<script>
'use strict';
Polymer({
is: 'tr-ui-a-generic-object-view-with-label',
ready() {
this.labelEl_ = document.createElement('div');
this.genericObjectView_ =
document.createElement('tr-ui-a-generic-object-view');
Polymer.dom(this.root).appendChild(this.labelEl_);
Polymer.dom(this.root).appendChild(this.genericObjectView_);
},
get label() {
return Polymer.dom(this.labelEl_).textContent;
},
set label(label) {
Polymer.dom(this.labelEl_).textContent = label;
},
get object() {
return this.genericObjectView_.object;
},
set object(object) {
this.genericObjectView_.object = object;
}
});
</script>