Commit a2de6939 authored by Zeynep Cankara's avatar Zeynep Cankara Committed by Commit Bot

[tools][system-analyzer] Add helper class

This CL adds a helper class for commonly used
helper methods inside web components, decreasing
the amount of duplicated code across the app.

Bug: v8:10667, v8:10644

Change-Id: I754396a9b3598d0930a82fc487857e946bfd3805
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2299359Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Commit-Queue: Zeynep Cankara <zcankara@google.com>
Cr-Commit-Position: refs/heads/master@{#68899}
parent c7e58ce9
...@@ -29,3 +29,67 @@ function defineCustomElement(name, generator) { ...@@ -29,3 +29,67 @@ function defineCustomElement(name, generator) {
.then( .then(
templateText => customElements.define(name, generator(templateText))); templateText => customElements.define(name, generator(templateText)));
} }
// DOM Helpers
function removeAllChildren(node) {
let range = document.createRange();
range.selectNodeContents(node);
range.deleteContents();
}
function $(id) {
return document.querySelector(id)
}
function div(classes) {
let node = document.createElement('div');
if (classes !== void 0) {
if (typeof classes === 'string') {
node.classList.add(classes);
} else {
classes.forEach(cls => node.classList.add(cls));
}
}
return node;
}
class V8CustomElement extends HTMLElement {
constructor(templateText) {
super();
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = templateText;
}
$(id) {
return this.shadowRoot.querySelector(id);
}
querySelectorAll(query) {
return this.shadowRoot.querySelectorAll(query);
}
div(classes) {return div(classes)}
table(className) {
let node = document.createElement('table')
if (className) node.classList.add(className)
return node;
}
td(textOrNode) {
let node = document.createElement('td');
if (typeof textOrNode === 'object') {
node.appendChild(textOrNode);
} else {
node.innerText = textOrNode;
}
return node;
}
tr(){
return document.createElement('tr');
}
removeAllChildren(node) { return removeAllChildren(node); }
}
export {defineCustomElement, V8CustomElement, removeAllChildren, $, div};
...@@ -4,13 +4,12 @@ ...@@ -4,13 +4,12 @@
import {Group} from './ic-model.mjs'; import {Group} from './ic-model.mjs';
import CustomIcProcessor from "./ic-processor.mjs"; import CustomIcProcessor from "./ic-processor.mjs";
import {defineCustomElement, V8CustomElement} from './helper.mjs';
defineCustomElement('ic-panel', (templateText) => defineCustomElement('ic-panel', (templateText) =>
class ICPanel extends HTMLElement { class ICPanel extends V8CustomElement {
constructor() { constructor() {
super(); super(templateText);
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = templateText;
this.groupKey.addEventListener( this.groupKey.addEventListener(
'change', e => this.updateTable(e)); 'change', e => this.updateTable(e));
this.$('#filterICTimeBtn').addEventListener( this.$('#filterICTimeBtn').addEventListener(
...@@ -20,14 +19,6 @@ defineCustomElement('ic-panel', (templateText) => ...@@ -20,14 +19,6 @@ defineCustomElement('ic-panel', (templateText) =>
this._endTime = 0; this._endTime = 0;
} }
$(id) {
return this.shadowRoot.querySelector(id);
}
querySelectorAll(query) {
return this.shadowRoot.querySelectorAll(query);
}
set entries(value){ set entries(value){
this._entries = value; this._entries = value;
} }
...@@ -108,12 +99,6 @@ defineCustomElement('ic-panel', (templateText) => ...@@ -108,12 +99,6 @@ defineCustomElement('ic-panel', (templateText) =>
return a; return a;
} }
removeAllChildren(node) {
while (node.firstChild) {
node.removeChild(node.firstChild);
}
}
td(tr, content, className) { td(tr, content, className) {
let node = document.createElement("td"); let node = document.createElement("td");
if (typeof content == "object") { if (typeof content == "object") {
......
...@@ -13,7 +13,7 @@ found in the LICENSE file. --> ...@@ -13,7 +13,7 @@ found in the LICENSE file. -->
<script type="module" src="index.mjs"></script> <script type="module" src="index.mjs"></script>
<script type="module" src="index.mjs"></script> <script type="module" src="index.mjs"></script>
<link rel="stylesheet" type="text/css" href="./index.css"> <link rel="stylesheet" type="text/css" href="./index.css">
<script src="helper.js"></script> <script type="module" src="helper.mjs"></script>
<script src="../splaytree.js"></script> <script src="../splaytree.js"></script>
<script src="../codemap.js"></script> <script src="../codemap.js"></script>
......
// Copyright 2020 the V8 project authors. All rights reserved. // Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import {defineCustomElement, V8CustomElement} from './helper.mjs';
defineCustomElement('log-file-reader', (templateText) => defineCustomElement('log-file-reader', (templateText) =>
class LogFileReader extends HTMLElement { class LogFileReader extends V8CustomElement {
constructor() { constructor() {
super(); super(templateText);
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = templateText;
this.addEventListener('click', e => this.handleClick(e)); this.addEventListener('click', e => this.handleClick(e));
this.addEventListener('dragover', e => this.handleDragOver(e)); this.addEventListener('dragover', e => this.handleDragOver(e));
this.addEventListener('drop', e => this.handleChange(e)); this.addEventListener('drop', e => this.handleChange(e));
...@@ -15,10 +14,6 @@ defineCustomElement('log-file-reader', (templateText) => ...@@ -15,10 +14,6 @@ defineCustomElement('log-file-reader', (templateText) =>
this.$('#fileReader').addEventListener('keydown', e => this.handleKeyEvent(e)); this.$('#fileReader').addEventListener('keydown', e => this.handleKeyEvent(e));
} }
$(id) {
return this.shadowRoot.querySelector(id);
}
get section() { get section() {
return this.$('#fileReaderSection'); return this.$('#fileReaderSection');
} }
......
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
// found in the LICENSE file. // found in the LICENSE file.
import {kChunkWidth, kChunkHeight} from './map-processor.mjs'; import {kChunkWidth, kChunkHeight} from './map-processor.mjs';
import {div, removeAllChildren, $} from './helper.mjs';
class State { class State {
constructor(mapPanelId, timelinePanelId) { constructor(mapPanelId, timelinePanelId) {
...@@ -68,60 +71,6 @@ class State { ...@@ -68,60 +71,6 @@ class State {
} }
} }
// =========================================================================
// DOM Helper
function $(id) {
return document.querySelector(id)
}
function removeAllChildren(node) {
while (node.lastChild) {
node.removeChild(node.lastChild);
}
}
function selectOption(select, match) {
let options = select.options;
for (let i = 0; i < options.length; i++) {
if (match(i, options[i])) {
select.selectedIndex = i;
return;
}
}
}
function div(classes) {
let node = document.createElement('div');
if (classes !== void 0) {
if (typeof classes === 'string') {
node.classList.add(classes);
} else {
classes.forEach(cls => node.classList.add(cls));
}
}
return node;
}
function table(className) {
let node = document.createElement('table')
if (className) node.classList.add(className)
return node;
}
function td(textOrNode) {
let node = document.createElement('td');
if (typeof textOrNode === 'object') {
node.appendChild(textOrNode);
} else {
node.innerText = textOrNode;
}
return node;
}
function tr() {
return document.createElement('tr');
}
class Navigation { class Navigation {
constructor(state, view) { constructor(state, view) {
this.state = state; this.state = state;
...@@ -695,4 +644,4 @@ function transitionTypeToColor(type) { ...@@ -695,4 +644,4 @@ function transitionTypeToColor(type) {
return '#9B6EDC'; return '#9B6EDC';
} }
export { State, div, table, tr, td}; export { State };
\ No newline at end of file \ No newline at end of file
...@@ -3,27 +3,18 @@ ...@@ -3,27 +3,18 @@
// found in the LICENSE file. // found in the LICENSE file.
import "./stats-panel.mjs"; import "./stats-panel.mjs";
import {V8Map} from "./map-processor.mjs"; import {V8Map} from "./map-processor.mjs";
import {defineCustomElement, V8CustomElement} from './helper.mjs';
defineCustomElement('map-panel', (templateText) => defineCustomElement('map-panel', (templateText) =>
class MapPanel extends HTMLElement { class MapPanel extends V8CustomElement {
constructor() { constructor() {
super(); super(templateText);
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = templateText;
this.transitionView.addEventListener( this.transitionView.addEventListener(
'mousemove', e => this.handleTransitionViewChange(e)); 'mousemove', e => this.handleTransitionViewChange(e));
this.$('#searchBarBtn').addEventListener( this.$('#searchBarBtn').addEventListener(
'click', e => this.handleSearchBar(e)); 'click', e => this.handleSearchBar(e));
} }
$(id) {
return this.shadowRoot.querySelector(id);
}
querySelectorAll(query) {
return this.shadowRoot.querySelectorAll(query);
}
get transitionView() { get transitionView() {
return this.$('#transitionView'); return this.$('#transitionView');
} }
......
// Copyright 2020 the V8 project authors. All rights reserved. // Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import {div, table, tr, td} from './map-model.mjs'; import {V8CustomElement, defineCustomElement} from './helper.mjs';
defineCustomElement('stats-panel', (templateText) => defineCustomElement('stats-panel', (templateText) =>
class StatsPanel extends HTMLElement { class StatsPanel extends V8CustomElement {
constructor() { constructor() {
super(); super(templateText);
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = templateText;
this.timeline_ = undefined; this.timeline_ = undefined;
} }
$(id) {
return this.shadowRoot.querySelector(id);
}
querySelectorAll(query) {
return this.shadowRoot.querySelectorAll(query);
}
get stats() { get stats() {
return this.$('#stats'); return this.$('#stats');
} }
// decouple stats panel
removeAllChildren(node) {
while (node.lastChild) {
node.removeChild(node.lastChild);
}
}
set timeline(value){ set timeline(value){
this.timeline_ = value; this.timeline_ = value;
} }
...@@ -68,18 +50,18 @@ defineCustomElement('stats-panel', (templateText) => ...@@ -68,18 +50,18 @@ defineCustomElement('stats-panel', (templateText) =>
]; ];
let text = ''; let text = '';
let tableNode = table('transitionType'); let tableNode = this.table('transitionType');
tableNode.innerHTML = tableNode.innerHTML =
'<thead><tr><td>Color</td><td>Type</td><td>Count</td><td>Percent</td></tr></thead>'; '<thead><tr><td>Color</td><td>Type</td><td>Count</td><td>Percent</td></tr></thead>';
let name, filter; let name, filter;
//TODO(zc) timeline //TODO(zc) timeline
let total = this.timeline.size(); let total = this.timeline.size();
pairs.forEach(([name, color, filter]) => { pairs.forEach(([name, color, filter]) => {
let row = tr(); let row = this.tr();
if (color !== null) { if (color !== null) {
row.appendChild(td(div(['colorbox', color]))); row.appendChild(this.td(this.div(['colorbox', color])));
} else { } else {
row.appendChild(td('')); row.appendChild(this.td(''));
} }
row.onclick = (e) => { row.onclick = (e) => {
// lazily compute the stats // lazily compute the stats
...@@ -90,32 +72,32 @@ defineCustomElement('stats-panel', (templateText) => ...@@ -90,32 +72,32 @@ defineCustomElement('stats-panel', (templateText) =>
this.dispatchEvent(new CustomEvent( this.dispatchEvent(new CustomEvent(
'change', {bubbles: true, composed: true, detail: node.maps})); 'change', {bubbles: true, composed: true, detail: node.maps}));
}; };
row.appendChild(td(name)); row.appendChild(this.td(name));
let count = this.timeline.count(filter); let count = this.timeline.count(filter);
row.appendChild(td(count)); row.appendChild(this.td(count));
let percent = Math.round(count / total * 1000) / 10; let percent = Math.round(count / total * 1000) / 10;
row.appendChild(td(percent.toFixed(1) + '%')); row.appendChild(this.td(percent.toFixed(1) + '%'));
tableNode.appendChild(row); tableNode.appendChild(row);
}); });
this.stats.appendChild(tableNode); this.stats.appendChild(tableNode);
} }
updateNamedTransitionsStats() { updateNamedTransitionsStats() {
let tableNode = table('transitionTable'); let tableNode = this.table('transitionTable');
let nameMapPairs = Array.from(this.timeline.transitions.entries()); let nameMapPairs = Array.from(this.timeline.transitions.entries());
tableNode.innerHTML = tableNode.innerHTML =
'<thead><tr><td>Propery Name</td><td>#</td></tr></thead>'; '<thead><tr><td>Propery Name</td><td>#</td></tr></thead>';
nameMapPairs.sort((a, b) => b[1].length - a[1].length).forEach(([ nameMapPairs.sort((a, b) => b[1].length - a[1].length).forEach(([
name, maps name, maps
]) => { ]) => {
let row = tr(); let row = this.tr();
row.maps = maps; row.maps = maps;
row.addEventListener( row.addEventListener(
'click', 'click',
e => this.dispatchEvent(new CustomEvent( e => this.dispatchEvent(new CustomEvent(
'change', {bubbles: true, composed: true, detail: e.target.parentNode.maps.map(map => map.to)}))); 'change', {bubbles: true, composed: true, detail: e.target.parentNode.maps.map(map => map.to)})));
row.appendChild(td(name)); row.appendChild(this.td(name));
row.appendChild(td(maps.length)); row.appendChild(this.td(maps.length));
tableNode.appendChild(row); tableNode.appendChild(row);
}); });
this.stats.appendChild(tableNode); this.stats.appendChild(tableNode);
......
// Copyright 2020 the V8 project authors. All rights reserved. // Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import {defineCustomElement, V8CustomElement} from './helper.mjs';
defineCustomElement('timeline-panel', (templateText) => defineCustomElement('timeline-panel', (templateText) =>
class TimelinePanel extends HTMLElement { class TimelinePanel extends V8CustomElement {
constructor() { constructor() {
super(); super(templateText);
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = templateText;
this.timelineOverview.addEventListener( this.timelineOverview.addEventListener(
'mousemove', e => this.handleTimelineIndicatorMove(e)); 'mousemove', e => this.handleTimelineIndicatorMove(e));
} }
$(id) {
return this.shadowRoot.querySelector(id);
}
querySelectorAll(query) {
return this.shadowRoot.querySelectorAll(query);
}
get timelineOverview() { get timelineOverview() {
return this.$('#timelineOverview'); return this.$('#timelineOverview');
} }
...@@ -41,7 +31,6 @@ defineCustomElement('timeline-panel', (templateText) => ...@@ -41,7 +31,6 @@ defineCustomElement('timeline-panel', (templateText) =>
return this.$('#timeline'); return this.$('#timeline');
} }
handleTimelineIndicatorMove(event) { handleTimelineIndicatorMove(event) {
if (event.buttons == 0) return; if (event.buttons == 0) return;
let timelineTotalWidth = this.timelineCanvas.offsetWidth; let timelineTotalWidth = this.timelineCanvas.offsetWidth;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment