2020-07-22 15:00:22 +00:00
|
|
|
// Copyright 2020 the V8 project authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
2020-11-02 09:27:28 +00:00
|
|
|
import { V8CustomElement, DOM, typeToColor } from "../helper.mjs";
|
2020-10-19 16:55:20 +00:00
|
|
|
import { FocusEvent, SelectionEvent } from "../events.mjs";
|
2020-08-13 12:55:19 +00:00
|
|
|
|
2020-11-02 09:27:28 +00:00
|
|
|
DOM.defineCustomElement(
|
2020-08-13 12:55:19 +00:00
|
|
|
"./map-panel/map-transitions",
|
|
|
|
(templateText) =>
|
|
|
|
class MapTransitions extends V8CustomElement {
|
2020-10-19 10:45:42 +00:00
|
|
|
_map;
|
|
|
|
_selectedMapLogEntries;
|
2020-10-19 16:55:20 +00:00
|
|
|
_displayedMapsInTree;
|
|
|
|
_showMapsUpdateId;
|
2020-08-13 12:55:19 +00:00
|
|
|
constructor() {
|
|
|
|
super(templateText);
|
|
|
|
this.transitionView.addEventListener("mousemove", (e) =>
|
|
|
|
this.handleTransitionViewChange(e)
|
|
|
|
);
|
|
|
|
this.currentNode = this.transitionView;
|
|
|
|
this.currentMap = undefined;
|
|
|
|
}
|
2020-08-13 09:14:29 +00:00
|
|
|
|
2020-08-13 12:55:19 +00:00
|
|
|
get transitionView() {
|
|
|
|
return this.$("#transitionView");
|
|
|
|
}
|
2020-08-13 09:14:29 +00:00
|
|
|
|
2020-08-13 12:55:19 +00:00
|
|
|
get tooltip() {
|
|
|
|
return this.$("#tooltip");
|
|
|
|
}
|
2020-08-13 09:14:29 +00:00
|
|
|
|
2020-08-13 12:55:19 +00:00
|
|
|
get tooltipContents() {
|
|
|
|
return this.$("#tooltipContents");
|
|
|
|
}
|
2020-08-13 09:14:29 +00:00
|
|
|
|
2020-08-13 12:55:19 +00:00
|
|
|
set map(value) {
|
2020-10-19 10:45:42 +00:00
|
|
|
this._map = value;
|
2020-08-13 12:55:19 +00:00
|
|
|
this.showMap();
|
|
|
|
}
|
2020-08-13 09:14:29 +00:00
|
|
|
|
2020-08-13 12:55:19 +00:00
|
|
|
handleTransitionViewChange(e) {
|
|
|
|
this.tooltip.style.left = e.pageX + "px";
|
|
|
|
this.tooltip.style.top = e.pageY + "px";
|
|
|
|
let map = e.target.map;
|
|
|
|
if (map) {
|
|
|
|
this.tooltipContents.innerText = map.description;
|
|
|
|
}
|
2020-08-13 09:14:29 +00:00
|
|
|
}
|
|
|
|
|
2020-08-13 12:55:19 +00:00
|
|
|
selectMap(map) {
|
2020-10-19 16:55:20 +00:00
|
|
|
this.dispatchEvent(new SelectionEvent([map]));
|
2020-08-13 12:55:19 +00:00
|
|
|
}
|
2020-08-13 09:14:29 +00:00
|
|
|
|
2020-08-13 12:55:19 +00:00
|
|
|
showMap() {
|
2020-10-19 10:45:42 +00:00
|
|
|
if (this.currentMap === this._map) return;
|
|
|
|
this.currentMap = this._map;
|
|
|
|
this.selectedMapLogEntries = [this._map];
|
2020-10-19 16:55:20 +00:00
|
|
|
this.showMaps();
|
2020-08-13 12:55:19 +00:00
|
|
|
}
|
2020-08-13 09:14:29 +00:00
|
|
|
|
2020-08-13 12:55:19 +00:00
|
|
|
showMaps() {
|
2020-10-19 16:55:20 +00:00
|
|
|
clearTimeout(this._showMapsUpdateId);
|
|
|
|
this._showMapsUpdateId = setTimeout(() => this._showMaps(), 250);
|
|
|
|
}
|
|
|
|
_showMaps() {
|
2020-08-13 12:55:19 +00:00
|
|
|
this.transitionView.style.display = "none";
|
2020-11-02 09:27:28 +00:00
|
|
|
DOM.removeAllChildren(this.transitionView);
|
2020-10-19 16:55:20 +00:00
|
|
|
this._displayedMapsInTree = new Set();
|
|
|
|
// Limit view to 200 maps for performance reasons.
|
|
|
|
this.selectedMapLogEntries.slice(0, 200).forEach((map) =>
|
2020-08-25 06:31:43 +00:00
|
|
|
this.addMapAndParentTransitions(map));
|
2020-10-19 16:55:20 +00:00
|
|
|
this._displayedMapsInTree = undefined;
|
2020-08-13 12:55:19 +00:00
|
|
|
this.transitionView.style.display = "";
|
|
|
|
}
|
2020-08-13 09:14:29 +00:00
|
|
|
|
2020-10-19 10:13:33 +00:00
|
|
|
set selectedMapLogEntries(list) {
|
2020-10-19 10:45:42 +00:00
|
|
|
this._selectedMapLogEntries = list;
|
2020-08-13 12:55:19 +00:00
|
|
|
this.showMaps();
|
|
|
|
}
|
2020-08-13 09:14:29 +00:00
|
|
|
|
2020-10-19 10:13:33 +00:00
|
|
|
get selectedMapLogEntries() {
|
2020-10-19 10:45:42 +00:00
|
|
|
return this._selectedMapLogEntries;
|
2020-08-13 12:55:19 +00:00
|
|
|
}
|
2020-08-13 09:14:29 +00:00
|
|
|
|
2020-08-13 12:55:19 +00:00
|
|
|
addMapAndParentTransitions(map) {
|
|
|
|
if (map === void 0) return;
|
2020-10-19 16:55:20 +00:00
|
|
|
if (this._displayedMapsInTree.has(map)) return;
|
|
|
|
this._displayedMapsInTree.add(map);
|
2020-08-13 12:55:19 +00:00
|
|
|
this.currentNode = this.transitionView;
|
|
|
|
let parents = map.getParents();
|
|
|
|
if (parents.length > 0) {
|
|
|
|
this.addTransitionTo(parents.pop());
|
|
|
|
parents.reverse().forEach((each) => this.addTransitionTo(each));
|
|
|
|
}
|
|
|
|
let mapNode = this.addSubtransitions(map);
|
|
|
|
// Mark and show the selected map.
|
|
|
|
mapNode.classList.add("selected");
|
|
|
|
if (this.selectedMap == map) {
|
|
|
|
setTimeout(
|
|
|
|
() =>
|
|
|
|
mapNode.scrollIntoView({
|
|
|
|
behavior: "smooth",
|
|
|
|
block: "nearest",
|
|
|
|
inline: "nearest",
|
|
|
|
}),
|
|
|
|
1
|
|
|
|
);
|
|
|
|
}
|
2020-08-13 09:14:29 +00:00
|
|
|
}
|
|
|
|
|
2020-08-13 12:55:19 +00:00
|
|
|
addSubtransitions(map) {
|
|
|
|
let mapNode = this.addTransitionTo(map);
|
|
|
|
// Draw outgoing linear transition line.
|
|
|
|
let current = map;
|
|
|
|
while (current.children.length == 1) {
|
|
|
|
current = current.children[0].to;
|
|
|
|
this.addTransitionTo(current);
|
|
|
|
}
|
|
|
|
return mapNode;
|
2020-08-13 09:14:29 +00:00
|
|
|
}
|
|
|
|
|
2020-08-13 12:55:19 +00:00
|
|
|
addTransitionEdge(map) {
|
|
|
|
let classes = ["transitionEdge"];
|
2020-11-02 09:27:28 +00:00
|
|
|
let edge = DOM.div(classes);
|
2020-10-26 18:02:54 +00:00
|
|
|
edge.style.backgroundColor = typeToColor(map.edge);
|
2020-11-02 09:27:28 +00:00
|
|
|
let labelNode = DOM.div("transitionLabel");
|
2020-08-13 12:55:19 +00:00
|
|
|
labelNode.innerText = map.edge.toString();
|
|
|
|
edge.appendChild(labelNode);
|
|
|
|
return edge;
|
|
|
|
}
|
2020-08-13 09:14:29 +00:00
|
|
|
|
2020-08-13 12:55:19 +00:00
|
|
|
addTransitionTo(map) {
|
|
|
|
// transition[ transitions[ transition[...], transition[...], ...]];
|
2020-10-30 22:09:32 +00:00
|
|
|
this._displayedMapsInTree?.add(map);
|
2020-11-02 09:27:28 +00:00
|
|
|
let transition = DOM.div("transition");
|
2020-08-13 12:55:19 +00:00
|
|
|
if (map.isDeprecated()) transition.classList.add("deprecated");
|
|
|
|
if (map.edge) {
|
|
|
|
transition.appendChild(this.addTransitionEdge(map));
|
|
|
|
}
|
|
|
|
let mapNode = this.addMapNode(map);
|
|
|
|
transition.appendChild(mapNode);
|
2020-08-13 09:14:29 +00:00
|
|
|
|
2020-11-02 09:27:28 +00:00
|
|
|
let subtree = DOM.div("transitions");
|
2020-08-13 12:55:19 +00:00
|
|
|
transition.appendChild(subtree);
|
2020-08-13 09:14:29 +00:00
|
|
|
|
2020-08-13 12:55:19 +00:00
|
|
|
this.currentNode.appendChild(transition);
|
|
|
|
this.currentNode = subtree;
|
2020-08-13 09:14:29 +00:00
|
|
|
|
2020-08-13 12:55:19 +00:00
|
|
|
return mapNode;
|
|
|
|
}
|
2020-08-13 09:14:29 +00:00
|
|
|
|
2020-10-19 16:55:20 +00:00
|
|
|
addMapNode(map) {
|
2020-11-02 09:27:28 +00:00
|
|
|
let node = DOM.div("map");
|
2020-10-26 18:02:54 +00:00
|
|
|
if (map.edge) node.style.backgroundColor = typeToColor(map.edge);
|
2020-10-19 16:55:20 +00:00
|
|
|
node.map = map;
|
|
|
|
node.addEventListener("click", () => this.selectMap(map));
|
|
|
|
if (map.children.length > 1) {
|
|
|
|
node.innerText = map.children.length;
|
2020-11-02 09:27:28 +00:00
|
|
|
let showSubtree = DOM.div("showSubtransitions");
|
2020-10-19 16:55:20 +00:00
|
|
|
showSubtree.addEventListener("click", (e) =>
|
|
|
|
this.toggleSubtree(e, node)
|
|
|
|
);
|
|
|
|
node.appendChild(showSubtree);
|
|
|
|
} else if (map.children.length == 0) {
|
|
|
|
node.innerHTML = "●";
|
|
|
|
}
|
|
|
|
this.currentNode.appendChild(node);
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-13 12:55:19 +00:00
|
|
|
toggleSubtree(event, node) {
|
|
|
|
let map = node.map;
|
|
|
|
event.target.classList.toggle("opened");
|
|
|
|
let transitionsNode = node.parentElement.querySelector(".transitions");
|
|
|
|
let subtransitionNodes = transitionsNode.children;
|
|
|
|
if (subtransitionNodes.length <= 1) {
|
|
|
|
// Add subtransitions excepth the one that's already shown.
|
|
|
|
let visibleTransitionMap =
|
|
|
|
subtransitionNodes.length == 1
|
|
|
|
? transitionsNode.querySelector(".map").map
|
|
|
|
: void 0;
|
|
|
|
map.children.forEach((edge) => {
|
|
|
|
if (edge.to != visibleTransitionMap) {
|
|
|
|
this.currentNode = transitionsNode;
|
|
|
|
this.addSubtransitions(edge.to);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// remove all but the first (currently selected) subtransition
|
|
|
|
for (let i = subtransitionNodes.length - 1; i > 0; i--) {
|
|
|
|
transitionsNode.removeChild(subtransitionNodes[i]);
|
2020-08-13 09:14:29 +00:00
|
|
|
}
|
2020-07-22 15:00:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-08-13 12:55:19 +00:00
|
|
|
);
|