1// Copyright 2018 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5import { GraphView } from "../src/graph-view"; 6import { ScheduleView } from "../src/schedule-view"; 7import { SequenceView } from "../src/sequence-view"; 8import { SourceResolver } from "../src/source-resolver"; 9import { SelectionBroker } from "../src/selection-broker"; 10import { View, PhaseView } from "../src/view"; 11import { GNode } from "./node"; 12 13const multiviewID = "multiview"; 14 15const toolboxHTML = ` 16<div class="graph-toolbox"> 17 <select id="phase-select"> 18 <option disabled selected>(please open a file)</option> 19 </select> 20 <input id="search-input" type="text" title="search nodes for regex" alt="search node for regex" class="search-input" 21 placeholder="find with regexp…"> 22 <label><input id="search-only-visible" type="checkbox" name="instruction-address" alt="Apply search to visible nodes only">only visible</label> 23</div>`; 24 25export class GraphMultiView extends View { 26 sourceResolver: SourceResolver; 27 selectionBroker: SelectionBroker; 28 graph: GraphView; 29 schedule: ScheduleView; 30 sequence: SequenceView; 31 selectMenu: HTMLSelectElement; 32 currentPhaseView: PhaseView; 33 34 createViewElement() { 35 const pane = document.createElement("div"); 36 pane.setAttribute("id", multiviewID); 37 pane.setAttribute("tabindex", "1"); 38 pane.className = "viewpane"; 39 return pane; 40 } 41 42 hide() { 43 this.hideCurrentPhase(); 44 super.hide(); 45 } 46 47 constructor(id, selectionBroker, sourceResolver) { 48 super(id); 49 const view = this; 50 view.sourceResolver = sourceResolver; 51 view.selectionBroker = selectionBroker; 52 const toolbox = document.createElement("div"); 53 toolbox.className = "toolbox-anchor"; 54 toolbox.innerHTML = toolboxHTML; 55 view.divNode.appendChild(toolbox); 56 const searchInput = toolbox.querySelector("#search-input") as HTMLInputElement; 57 const onlyVisibleCheckbox = toolbox.querySelector("#search-only-visible") as HTMLInputElement; 58 searchInput.addEventListener("keyup", e => { 59 if (!view.currentPhaseView) return; 60 view.currentPhaseView.searchInputAction(searchInput, e, onlyVisibleCheckbox.checked); 61 }); 62 view.divNode.addEventListener("keyup", (e: KeyboardEvent) => { 63 if (e.keyCode == 191) { // keyCode == '/' 64 searchInput.focus(); 65 } else if (e.keyCode == 78) { // keyCode == 'n' 66 view.displayNextGraphPhase(); 67 } else if (e.keyCode == 66) { // keyCode == 'b' 68 view.displayPreviousGraphPhase(); 69 } 70 }); 71 searchInput.setAttribute("value", window.sessionStorage.getItem("lastSearch") || ""); 72 this.graph = new GraphView(this.divNode, selectionBroker, view.displayPhaseByName.bind(this), 73 toolbox.querySelector(".graph-toolbox")); 74 this.schedule = new ScheduleView(this.divNode, selectionBroker); 75 this.sequence = new SequenceView(this.divNode, selectionBroker); 76 this.selectMenu = toolbox.querySelector("#phase-select") as HTMLSelectElement; 77 } 78 79 initializeSelect() { 80 const view = this; 81 view.selectMenu.innerHTML = ""; 82 view.sourceResolver.forEachPhase(phase => { 83 const optionElement = document.createElement("option"); 84 let maxNodeId = ""; 85 if (phase.type == "graph" && phase.highestNodeId != 0) { 86 maxNodeId = ` ${phase.highestNodeId}`; 87 } 88 optionElement.text = `${phase.name}${maxNodeId}`; 89 view.selectMenu.add(optionElement); 90 }); 91 this.selectMenu.onchange = function (this: HTMLSelectElement) { 92 const phaseIndex = this.selectedIndex; 93 window.sessionStorage.setItem("lastSelectedPhase", phaseIndex.toString()); 94 view.displayPhase(view.sourceResolver.getPhase(phaseIndex)); 95 }; 96 } 97 98 show() { 99 // Insert before is used so that the display is inserted before the 100 // resizer for the RangeView. 101 this.container.insertBefore(this.divNode, this.container.firstChild); 102 this.initializeSelect(); 103 const lastPhaseIndex = +window.sessionStorage.getItem("lastSelectedPhase"); 104 const initialPhaseIndex = this.sourceResolver.repairPhaseId(lastPhaseIndex); 105 this.selectMenu.selectedIndex = initialPhaseIndex; 106 this.displayPhase(this.sourceResolver.getPhase(initialPhaseIndex)); 107 } 108 109 displayPhase(phase, selection?: Map<string, GNode>) { 110 if (phase.type == "graph") { 111 this.displayPhaseView(this.graph, phase, selection); 112 } else if (phase.type == "schedule") { 113 this.displayPhaseView(this.schedule, phase, selection); 114 } else if (phase.type == "sequence") { 115 this.displayPhaseView(this.sequence, phase, selection); 116 } 117 } 118 119 displayPhaseView(view: PhaseView, data, selection?: Map<string, GNode>) { 120 const rememberedSelection = selection ? selection : this.hideCurrentPhase(); 121 view.initializeContent(data, rememberedSelection); 122 this.currentPhaseView = view; 123 } 124 125 displayPhaseByName(phaseName, selection?: Map<string, GNode>) { 126 const phaseId = this.sourceResolver.getPhaseIdByName(phaseName); 127 this.selectMenu.selectedIndex = phaseId; 128 this.displayPhase(this.sourceResolver.getPhase(phaseId), selection); 129 } 130 131 displayNextGraphPhase() { 132 let nextPhaseIndex = this.selectMenu.selectedIndex + 1; 133 while (nextPhaseIndex < this.sourceResolver.phases.length) { 134 const nextPhase = this.sourceResolver.getPhase(nextPhaseIndex); 135 if (nextPhase.type == "graph") { 136 this.selectMenu.selectedIndex = nextPhaseIndex; 137 window.sessionStorage.setItem("lastSelectedPhase", nextPhaseIndex.toString()); 138 this.displayPhase(nextPhase); 139 break; 140 } 141 nextPhaseIndex += 1; 142 } 143 } 144 145 displayPreviousGraphPhase() { 146 let previousPhaseIndex = this.selectMenu.selectedIndex - 1; 147 while (previousPhaseIndex >= 0) { 148 const previousPhase = this.sourceResolver.getPhase(previousPhaseIndex); 149 if (previousPhase.type == "graph") { 150 this.selectMenu.selectedIndex = previousPhaseIndex; 151 window.sessionStorage.setItem("lastSelectedPhase", previousPhaseIndex.toString()); 152 this.displayPhase(previousPhase); 153 break; 154 } 155 previousPhaseIndex -= 1; 156 } 157 } 158 159 hideCurrentPhase() { 160 let rememberedSelection = null; 161 if (this.currentPhaseView != null) { 162 rememberedSelection = this.currentPhaseView.detachSelection(); 163 this.currentPhaseView.hide(); 164 this.currentPhaseView = null; 165 } 166 return rememberedSelection; 167 } 168 169 onresize() { 170 if (this.currentPhaseView) this.currentPhaseView.onresize(); 171 } 172 173 detachSelection() { 174 return null; 175 } 176} 177