1const char HTMLLogger_html[] =
2 R"x(<!doctype html>)x" "\n"
3 R"x(<html>)x" "\n"
4 R"x(<!-- HTMLLogger.cpp ----------------------------------------------------)x" "\n"
5 R"x()x" "\n"
6 R"x( Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.)x" "\n"
7 R"x( See https://llvm.org/LICENSE.txt for license information.)x" "\n"
8 R"x( SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception)x" "\n"
9 R"x()x" "\n"
10 R"x(//===------------------------------------------------------------------------>)x" "\n"
11 R"x()x" "\n"
12 R"x(<head>)x" "\n"
13 R"x(<?INJECT?>)x" "\n"
14 R"x()x" "\n"
15 R"x(<template id="value-template">)x" "\n"
16 R"x( <details class="value" open>)x" "\n"
17 R"x( <summary>)x" "\n"
18 R"x( <span>{{v.kind}})x" "\n"
19 R"x( <template data-if="v.value_id"><span class="address">#{{v.value_id}}</span></template>)x" "\n"
20 R"x( </span>)x" "\n"
21 R"x( <template data-if="v.location">)x" "\n"
22 R"x( <span class="location">{{v.type}} <span class="address">@{{v.location}}</span></span>)x" "\n"
23 R"x( </template>)x" "\n"
24 R"x( </summary>)x" "\n"
25 R"x( <template)x" "\n"
26 R"x( data-for="kv in Object.entries(v)")x" "\n"
27 R"x( data-if="['kind', 'value_id', 'type', 'location'].indexOf(kv[0]) < 0">)x" "\n"
28 R"x( <div class="property"><span class="key">{{kv[0]}}</span>)x" "\n"
29 R"x( <template data-if="typeof(kv[1]) != 'object'">{{kv[1]}}</template>)x" "\n"
30 R"x( <template data-if="typeof(kv[1]) == 'object'" data-let="v = kv[1]">)x" "\n"
31 R"x( <template data-use="value-template"></template>)x" "\n"
32 R"x( </template>)x" "\n"
33 R"x( </div>)x" "\n"
34 R"x( </template>)x" "\n"
35 R"x( </details>)x" "\n"
36 R"x(</template>)x" "\n"
37 R"x()x" "\n"
38 R"x(</head>)x" "\n"
39 R"x()x" "\n"
40 R"x(<body>)x" "\n"
41 R"x()x" "\n"
42 R"x(<section id="timeline" data-selection="">)x" "\n"
43 R"x(<header>Timeline</header>)x" "\n"
44 R"x(<template data-for="entry in timeline">)x" "\n"
45 R"x( <div id="{{entry.block}}:{{entry.iter}}" data-bb="{{entry.block}}" class="entry">)x" "\n"
46 R"x( <span class="counter"></span>)x" "\n"
47 R"x( {{entry.block}})x" "\n"
48 R"x( <template data-if="entry.post_visit">(post-visit)</template>)x" "\n"
49 R"x( <template data-if="!entry.post_visit">({{entry.iter}})</template>)x" "\n"
50 R"x( <template data-if="entry.converged"> &#x2192;&#x7c;<!--Rightwards arrow, vertical line--></template>)x" "\n"
51 R"x( </div>)x" "\n"
52 R"x(</template>)x" "\n"
53 R"x(</section>)x" "\n"
54 R"x()x" "\n"
55 R"x(<section id="function" data-selection="">)x" "\n"
56 R"x(<header>Function</header>)x" "\n"
57 R"x(<div id="code"></div>)x" "\n"
58 R"x(<div id="cfg"></div>)x" "\n"
59 R"x(</section>)x" "\n"
60 R"x()x" "\n"
61 R"x(<section id="block" data-selection="bb">)x" "\n"
62 R"x(<header><template>Block {{selection.bb}}</template></header>)x" "\n"
63 R"x(<div id="iterations">)x" "\n"
64 R"x( <template data-for="iter in cfg[selection.bb].iters">)x" "\n"
65 R"x( <a class="chooser {{selection.bb}}:{{iter.iter}}" data-iter="{{selection.bb}}:{{iter.iter}}">)x" "\n"
66 R"x( <template data-if="iter.post_visit">Post-visit</template>)x" "\n"
67 R"x( <template data-if="!iter.post_visit">{{iter.iter}}</template>)x" "\n"
68 R"x( <template data-if="iter.converged"> &#x2192;&#x7c;<!--Rightwards arrow, vertical line--></template>)x" "\n"
69 R"x( </a>)x" "\n"
70 R"x( </template>)x" "\n"
71 R"x(</div>)x" "\n"
72 R"x(<table id="bb-elements">)x" "\n"
73 R"x(<template>)x" "\n"
74 R"x( <tr id="{{selection.bb}}.0">)x" "\n"
75 R"x( <td class="{{selection.bb}}">{{selection.bb}}.0</td>)x" "\n"
76 R"x( <td>(initial state)</td>)x" "\n"
77 R"x( </tr>)x" "\n"
78 R"x(</template>)x" "\n"
79 R"x(<template data-for="elt in cfg[selection.bb].elements">)x" "\n"
80 R"x( <tr id="{{selection.bb}}.{{elt_index+1}}">)x" "\n"
81 R"x( <td class="{{selection.bb}}">{{selection.bb}}.{{elt_index+1}}</td>)x" "\n"
82 R"x( <td>{{elt}}</td>)x" "\n"
83 R"x( </tr>)x" "\n"
84 R"x(</template>)x" "\n"
85 R"x(</table>)x" "\n"
86 R"x(</section>)x" "\n"
87 R"x()x" "\n"
88 R"x(<section id="element" data-selection="iter,elt">)x" "\n"
89 R"x(<template data-let="state = states[selection.iter + '_' + selection.elt]">)x" "\n"
90 R"x(<header>)x" "\n"
91 R"x( <template data-if="state.element == 0">{{state.block}} initial state</template>)x" "\n"
92 R"x( <template data-if="state.element != 0">Element {{selection.elt}}</template>)x" "\n"
93 R"x( <template data-if="state.post_visit"> (post-visit)</template>)x" "\n"
94 R"x( <template data-if="!state.post_visit"> (iteration {{state.iter}})</template>)x" "\n"
95 R"x(</header>)x" "\n"
96 R"x(<template data-if="state.value" data-let="v = state.value">)x" "\n"
97 R"x( <h2>Value</h2>)x" "\n"
98 R"x( <template data-use="value-template"></template>)x" "\n"
99 R"x(</template>)x" "\n"
100 R"x(<template data-if="state.logs">)x" "\n"
101 R"x( <h2>Logs</h2>)x" "\n"
102 R"x( <pre>{{state.logs}}</pre>)x" "\n"
103 R"x(</template>)x" "\n"
104 R"x(<h2>Built-in lattice</h2>)x" "\n"
105 R"x(<pre>{{state.builtinLattice}}</pre>)x" "\n"
106 R"x(</template>)x" "\n"
107 R"x(</section>)x" "\n"
108 R"x()x" "\n"
109 R"x(<script>)x" "\n"
110 R"x(addBBColors(Object.keys(HTMLLoggerData.cfg).length);)x" "\n"
111 R"x(watchSelection(HTMLLoggerData);)x" "\n"
112 R"x(updateSelection({}, HTMLLoggerData);)x" "\n"
113 R"x(// Copy code and cfg from <template>s into the body.)x" "\n"
114 R"x(for (tmpl of document.querySelectorAll('template[data-copy]')))x" "\n"
115 R"x( document.getElementById(tmpl.dataset.copy).replaceChildren()x" "\n"
116 R"x( ...tmpl.content.cloneNode(/*deep=*/true).childNodes);)x" "\n"
117 R"x(</script>)x" "\n"
118 R"x()x" "\n"
119 R"x(</body>)x" "\n"
120 R"x(</html>)x" "\n"
121 R"x()x" "\n"
122 ;
123const char HTMLLogger_css[] =
124 R"x(/*===-- HTMLLogger.css ----------------------------------------------------===)x" "\n"
125 R"x(*)x" "\n"
126 R"x(* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.)x" "\n"
127 R"x(* See https://llvm.org/LICENSE.txt for license information.)x" "\n"
128 R"x(* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception)x" "\n"
129 R"x(*)x" "\n"
130 R"x(*===----------------------------------------------------------------------===*/)x" "\n"
131 R"x(html { font-family: sans-serif; })x" "\n"
132 R"x(body { margin: 0; display: flex; justify-content: left; })x" "\n"
133 R"x(body > * { box-sizing: border-box; })x" "\n"
134 R"x(body > section {)x" "\n"
135 R"x( border: 1px solid black;)x" "\n"
136 R"x( min-width: 20em;)x" "\n"
137 R"x( overflow: auto;)x" "\n"
138 R"x( max-height: 100vh;)x" "\n"
139 R"x(})x" "\n"
140 R"x(section header {)x" "\n"
141 R"x( background-color: #008;)x" "\n"
142 R"x( color: white;)x" "\n"
143 R"x( font-weight: bold;)x" "\n"
144 R"x( font-size: large;)x" "\n"
145 R"x( padding-right: 0.5em;)x" "\n"
146 R"x(})x" "\n"
147 R"x(section h2 {)x" "\n"
148 R"x( font-size: medium;)x" "\n"
149 R"x( margin-bottom: 0.5em;)x" "\n"
150 R"x( padding-top: 0.5em;)x" "\n"
151 R"x( border-top: 1px solid #aaa;)x" "\n"
152 R"x(})x" "\n"
153 R"x(#timeline {)x" "\n"
154 R"x( min-width: max-content;)x" "\n"
155 R"x( counter-reset: entry_counter;)x" "\n"
156 R"x(})x" "\n"
157 R"x(#timeline .entry .counter::before {)x" "\n"
158 R"x( counter-increment: entry_counter;)x" "\n"
159 R"x( content: counter(entry_counter) ":";)x" "\n"
160 R"x(})x" "\n"
161 R"x(#timeline .entry .counter {)x" "\n"
162 R"x( display: inline-block;)x" "\n"
163 R"x( min-width: 2em; /* Enough space for two digits and a colon */)x" "\n"
164 R"x( text-align: right;)x" "\n"
165 R"x(})x" "\n"
166 R"x(#timeline .entry.hover {)x" "\n"
167 R"x( background-color: #aaa;)x" "\n"
168 R"x(})x" "\n"
169 R"x(#timeline .entry.iter-select {)x" "\n"
170 R"x( background-color: #aac;)x" "\n"
171 R"x(})x" "\n"
172 R"x()x" "\n"
173 R"x(#bb-elements {)x" "\n"
174 R"x( font-family: monospace;)x" "\n"
175 R"x( font-size: x-small;)x" "\n"
176 R"x( border-collapse: collapse;)x" "\n"
177 R"x(})x" "\n"
178 R"x(#bb-elements td:nth-child(1) {)x" "\n"
179 R"x( text-align: right;)x" "\n"
180 R"x( width: 4em;)x" "\n"
181 R"x( border-right: 1px solid #008;)x" "\n"
182 R"x( padding: 0.3em 0.5em;)x" "\n"
183 R"x()x" "\n"
184 R"x( font-weight: bold;)x" "\n"
185 R"x( color: #888;)x" "\n"
186 R"x(};)x" "\n"
187 R"x(#bb-elements tr.hover {)x" "\n"
188 R"x( background-color: #abc;)x" "\n"
189 R"x(})x" "\n"
190 R"x(#bb-elements tr.elt-select {)x" "\n"
191 R"x( background-color: #acf;)x" "\n"
192 R"x(})x" "\n"
193 R"x(#iterations {)x" "\n"
194 R"x( display: flex;)x" "\n"
195 R"x(})x" "\n"
196 R"x(#iterations .chooser {)x" "\n"
197 R"x( flex-grow: 1;)x" "\n"
198 R"x( text-align: center;)x" "\n"
199 R"x( padding-left: 0.2em;)x" "\n"
200 R"x(})x" "\n"
201 R"x(#iterations .chooser :last-child {)x" "\n"
202 R"x( padding-right: 0.2em;)x" "\n"
203 R"x(})x" "\n"
204 R"x(#iterations .chooser:not(.iter-select).hover {)x" "\n"
205 R"x( background-color: #ddd;)x" "\n"
206 R"x(})x" "\n"
207 R"x(#iterations .iter-select {)x" "\n"
208 R"x( font-weight: bold;)x" "\n"
209 R"x(})x" "\n"
210 R"x(#iterations .chooser:not(.iter-select) {)x" "\n"
211 R"x( text-decoration: underline;)x" "\n"
212 R"x( color: blue;)x" "\n"
213 R"x( cursor: pointer;)x" "\n"
214 R"x( background-color: #ccc;)x" "\n"
215 R"x(})x" "\n"
216 R"x()x" "\n"
217 R"x(code.filename {)x" "\n"
218 R"x( font-weight: bold;)x" "\n"
219 R"x( color: black;)x" "\n"
220 R"x( background-color: #ccc;)x" "\n"
221 R"x( display: block;)x" "\n"
222 R"x( text-align: center;)x" "\n"
223 R"x(})x" "\n"
224 R"x(code.line {)x" "\n"
225 R"x( display: block;)x" "\n"
226 R"x( white-space: pre;)x" "\n"
227 R"x(})x" "\n"
228 R"x(code.line:before { /* line numbers */)x" "\n"
229 R"x( content: attr(data-line);)x" "\n"
230 R"x( display: inline-block;)x" "\n"
231 R"x( width: 2em;)x" "\n"
232 R"x( text-align: right;)x" "\n"
233 R"x( padding-right: 2px;)x" "\n"
234 R"x( background-color: #ccc;)x" "\n"
235 R"x( border-right: 1px solid #888;)x" "\n"
236 R"x( margin-right: 8px;)x" "\n"
237 R"x(})x" "\n"
238 R"x(code.line:has(.bb-select):before {)x" "\n"
239 R"x( border-right: 4px solid black;)x" "\n"
240 R"x( margin-right: 5px;)x" "\n"
241 R"x(})x" "\n"
242 R"x(.c.hover, .bb.hover {)x" "\n"
243 R"x( filter: saturate(200%) brightness(90%);)x" "\n"
244 R"x(})x" "\n"
245 R"x(.c.elt-select {)x" "\n"
246 R"x( box-shadow: inset 0 -4px 2px -2px #a00;)x" "\n"
247 R"x(})x" "\n"
248 R"x(.bb.bb-select polygon {)x" "\n"
249 R"x( stroke-width: 4px;)x" "\n"
250 R"x( filter: brightness(70%) saturate(150%);)x" "\n"
251 R"x(})x" "\n"
252 R"x(.bb { user-select: none; })x" "\n"
253 R"x(.bb polygon { fill: white; })x" "\n"
254 R"x(#cfg {)x" "\n"
255 R"x( position: relative;)x" "\n"
256 R"x( margin-left: 0.5em;)x" "\n"
257 R"x(})x" "\n"
258 R"x()x" "\n"
259 R"x(.value {)x" "\n"
260 R"x( border: 1px solid #888;)x" "\n"
261 R"x( font-size: x-small;)x" "\n"
262 R"x( flex-grow: 1;)x" "\n"
263 R"x(})x" "\n"
264 R"x(.value > summary {)x" "\n"
265 R"x( background-color: #ace;)x" "\n"
266 R"x( display: flex;)x" "\n"
267 R"x( cursor: pointer;)x" "\n"
268 R"x(})x" "\n"
269 R"x(.value > summary::before {)x" "\n"
270 R"x( content: '\25ba'; /* Black Right-Pointing Pointer */)x" "\n"
271 R"x( margin-right: 0.5em;)x" "\n"
272 R"x( font-size: 0.9em;)x" "\n"
273 R"x(})x" "\n"
274 R"x(.value[open] > summary::before {)x" "\n"
275 R"x( content: '\25bc'; /* Black Down-Pointing Triangle */)x" "\n"
276 R"x(})x" "\n"
277 R"x(.value > summary > .location {)x" "\n"
278 R"x( margin-left: auto;)x" "\n"
279 R"x(})x" "\n"
280 R"x(.value .address {)x" "\n"
281 R"x( font-size: xx-small;)x" "\n"
282 R"x( font-family: monospace;)x" "\n"
283 R"x( color: #888;)x" "\n"
284 R"x(})x" "\n"
285 R"x(.value .property {)x" "\n"
286 R"x( display: flex;)x" "\n"
287 R"x( margin-top: 0.5em;)x" "\n"
288 R"x(})x" "\n"
289 R"x(.value .property .key {)x" "\n"
290 R"x( font-weight: bold;)x" "\n"
291 R"x( min-width: 5em;)x" "\n"
292 R"x(})x" "\n"
293 R"x()x" "\n"
294 ;
295const char HTMLLogger_js[] =
296 R"x(//===-- HTMLLogger.js -----------------------------------------------------===//)x" "\n"
297 R"x(//)x" "\n"
298 R"x(// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.)x" "\n"
299 R"x(// See https://llvm.org/LICENSE.txt for license information.)x" "\n"
300 R"x(// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception)x" "\n"
301 R"x(//)x" "\n"
302 R"x(//===----------------------------------------------------------------------===//)x" "\n"
303 R"x()x" "\n"
304 R"x(// Based on selected objects, hide/show sections & populate data from templates.)x" "\n"
305 R"x(//)x" "\n"
306 R"x(// For example, if the selection is {bb="BB4", elt="BB4.6" iter="BB4:2"}:)x" "\n"
307 R"x(// - show the "block" and "element" sections)x" "\n"
308 R"x(// - re-render templates within these sections (if selection changed))x" "\n"
309 R"x(// - apply "bb-select" to items with class class "BB4", etc)x" "\n"
310 R"x(let selection = {};)x" "\n"
311 R"x(function updateSelection(changes, data) {)x" "\n"
312 R"x( Object.assign(selection, changes);)x" "\n"
313 R"x()x" "\n"
314 R"x( data = Object.create(data);)x" "\n"
315 R"x( data.selection = selection;)x" "\n"
316 R"x( for (root of document.querySelectorAll('[data-selection]')))x" "\n"
317 R"x( updateSection(root, data);)x" "\n"
318 R"x()x" "\n"
319 R"x( for (var k in changes))x" "\n"
320 R"x( applyClassIf(k + '-select', classSelector(changes[k]));)x" "\n"
321 R"x(})x" "\n"
322 R"x()x" "\n"
323 R"x(// Given <section data-selection="x,y">:)x" "\n"
324 R"x(// - hide section if selections x or y are null)x" "\n"
325 R"x(// - re-render templates if x or y have changed)x" "\n"
326 R"x(function updateSection(root, data) {)x" "\n"
327 R"x( let changed = root.selection == null;)x" "\n"
328 R"x( root.selection ||= {};)x" "\n"
329 R"x( for (key of root.dataset.selection.split(',')) {)x" "\n"
330 R"x( if (!key) continue;)x" "\n"
331 R"x( if (data.selection[key] != root.selection[key]) {)x" "\n"
332 R"x( root.selection[key] = data.selection[key];)x" "\n"
333 R"x( changed = true;)x" "\n"
334 R"x( })x" "\n"
335 R"x( if (data.selection[key] == null) {)x" "\n"
336 R"x( root.hidden = true;)x" "\n"
337 R"x( return;)x" "\n"
338 R"x( })x" "\n"
339 R"x( })x" "\n"
340 R"x( if (changed) {)x" "\n"
341 R"x( root.hidden = false;)x" "\n"
342 R"x( for (tmpl of root.getElementsByTagName('template')))x" "\n"
343 R"x( reinflate(tmpl, data);)x" "\n"
344 R"x( })x" "\n"
345 R"x(})x" "\n"
346 R"x()x" "\n"
347 R"x(// Expands template `tmpl` based on input `data`:)x" "\n"
348 R"x(// - interpolates {{expressions}} in text and attributes)x" "\n"
349 R"x(// - <template> tags can modify expansion: if, for etc)x" "\n"
350 R"x(// Outputs to `parent` element, inserting before `next`.)x" "\n"
351 R"x(function inflate(tmpl, data, parent, next) {)x" "\n"
352 R"x( // We use eval() as our expression language in templates!)x" "\n"
353 R"x( // The templates are static and trusted.)x" "\n"
354 R"x( let evalExpr = (expr, data) => eval('with (data) { ' + expr + ' }');)x" "\n"
355 R"x( let interpolate = (str, data) =>)x" "\n"
356 R"x( str.replace(/\{\{(.*?)\}\}/g, (_, expr) => evalExpr(expr, data)))x" "\n"
357 R"x( // Anything other than <template> tag: copy, interpolate, recursively inflate.)x" "\n"
358 R"x( if (tmpl.nodeName != 'TEMPLATE') {)x" "\n"
359 R"x( let clone = tmpl.cloneNode();)x" "\n"
360 R"x( clone.inflated = true;)x" "\n"
361 R"x( if (clone instanceof Text))x" "\n"
362 R"x( clone.textContent = interpolate(clone.textContent, data);)x" "\n"
363 R"x( if (clone instanceof Element) {)x" "\n"
364 R"x( for (attr of clone.attributes))x" "\n"
365 R"x( attr.value = interpolate(attr.value, data);)x" "\n"
366 R"x( for (c of tmpl.childNodes))x" "\n"
367 R"x( inflate(c, data, clone, /*next=*/null);)x" "\n"
368 R"x( })x" "\n"
369 R"x( return parent.insertBefore(clone, next);)x" "\n"
370 R"x( })x" "\n"
371 R"x( // data-use="xyz": use <template id="xyz"> instead. (Allows recursion.))x" "\n"
372 R"x( if ('use' in tmpl.dataset))x" "\n"
373 R"x( return inflate(document.getElementById(tmpl.dataset.use), data, parent, next);)x" "\n"
374 R"x( // <template> tag handling. Base case: recursively inflate.)x" "\n"
375 R"x( function handle(data) {)x" "\n"
376 R"x( for (c of tmpl.content.childNodes))x" "\n"
377 R"x( inflate(c, data, parent, next);)x" "\n"
378 R"x( })x" "\n"
379 R"x( // Directives on <template> tags modify behavior.)x" "\n"
380 R"x( const directives = {)x" "\n"
381 R"x( // data-for="x in expr": expr is enumerable, bind x to each in turn)x" "\n"
382 R"x( 'for': (nameInExpr, data, proceed) => {)x" "\n"
383 R"x( let [name, expr] = nameInExpr.split(' in ');)x" "\n"
384 R"x( let newData = Object.create(data);)x" "\n"
385 R"x( let index = 0;)x" "\n"
386 R"x( for (val of evalExpr(expr, data) || []) {)x" "\n"
387 R"x( newData[name] = val;)x" "\n"
388 R"x( newData[name + '_index'] = index++;)x" "\n"
389 R"x( proceed(newData);)x" "\n"
390 R"x( })x" "\n"
391 R"x( },)x" "\n"
392 R"x( // data-if="expr": only include contents if expression is truthy)x" "\n"
393 R"x( 'if': (expr, data, proceed) => { if (evalExpr(expr, data)) proceed(data); },)x" "\n"
394 R"x( // data-let="x = expr": bind x to value of expr)x" "\n"
395 R"x( 'let': (nameEqExpr, data, proceed) => {)x" "\n"
396 R"x( let [name, expr] = nameEqExpr.split(' = ');)x" "\n"
397 R"x( let newData = Object.create(data);)x" "\n"
398 R"x( newData[name] = evalExpr(expr, data);)x" "\n"
399 R"x( proceed(newData);)x" "\n"
400 R"x( },)x" "\n"
401 R"x( })x" "\n"
402 R"x( // Compose directive handlers on top of the base handler.)x" "\n"
403 R"x( for (let [dir, value] of Object.entries(tmpl.dataset).reverse()) {)x" "\n"
404 R"x( if (dir in directives) {)x" "\n"
405 R"x( let proceed = handle;)x" "\n"
406 R"x( handle = (data) => directives[dir](value, data, proceed);)x" "\n"
407 R"x( })x" "\n"
408 R"x( })x" "\n"
409 R"x( handle(data);)x" "\n"
410 R"x(})x" "\n"
411 R"x(// Expand a template, after first removing any prior expansion of it.)x" "\n"
412 R"x(function reinflate(tmpl, data) {)x" "\n"
413 R"x( // Clear previously rendered template contents.)x" "\n"
414 R"x( while (tmpl.nextSibling && tmpl.nextSibling.inflated))x" "\n"
415 R"x( tmpl.parentNode.removeChild(tmpl.nextSibling);)x" "\n"
416 R"x( inflate(tmpl, data, tmpl.parentNode, tmpl.nextSibling);)x" "\n"
417 R"x(})x" "\n"
418 R"x()x" "\n"
419 R"x(// Handle a mouse event on a region containing selectable items.)x" "\n"
420 R"x(// This might end up changing the hover state or the selection state.)x" "\n"
421 R"x(//)x" "\n"
422 R"x(// targetSelector describes what target HTML element is selectable.)x" "\n"
423 R"x(// targetToID specifies how to determine the selection from it:)x" "\n"
424 R"x(// hover: a function from target to the class name to highlight)x" "\n"
425 R"x(// bb: a function from target to the basic-block name to select (BB4))x" "\n"
426 R"x(// elt: a function from target to the CFG element name to select (BB4.5))x" "\n"
427 R"x(// iter: a function from target to the BB iteration to select (BB4:2))x" "\n"
428 R"x(// If an entry is missing, the selection is unmodified.)x" "\n"
429 R"x(// If an entry is null, the selection is always cleared.)x" "\n"
430 R"x(function mouseEventHandler(event, targetSelector, targetToID, data) {)x" "\n"
431 R"x( var target = event.type == "mouseout" ? null : event.target.closest(targetSelector);)x" "\n"
432 R"x( let selTarget = k => (target && targetToID[k]) ? targetToID[k](target) : null;)x" "\n"
433 R"x( if (event.type == "click") {)x" "\n"
434 R"x( let newSel = {};)x" "\n"
435 R"x( for (var k in targetToID) {)x" "\n"
436 R"x( if (k == 'hover') continue;)x" "\n"
437 R"x( let t = selTarget(k);)x" "\n"
438 R"x( newSel[k] = t;)x" "\n"
439 R"x( })x" "\n"
440 R"x( updateSelection(newSel, data);)x" "\n"
441 R"x( } else if ("hover" in targetToID) {)x" "\n"
442 R"x( applyClassIf("hover", classSelector(selTarget("hover")));)x" "\n"
443 R"x( })x" "\n"
444 R"x(})x" "\n"
445 R"x(function watch(rootSelector, targetSelector, targetToID, data) {)x" "\n"
446 R"x( var root = document.querySelector(rootSelector);)x" "\n"
447 R"x( for (event of ['mouseout', 'mousemove', 'click']))x" "\n"
448 R"x( root.addEventListener(event, e => mouseEventHandler(e, targetSelector, targetToID, data));)x" "\n"
449 R"x(})x" "\n"
450 R"x(function watchSelection(data) {)x" "\n"
451 R"x( let lastIter = (bb) => `${bb}:${data.cfg[bb].iters}`;)x" "\n"
452 R"x( watch('#code', '.c', {)x" "\n"
453 R"x( hover: e => e.dataset.elt,)x" "\n"
454 R"x( bb: e => e.dataset.bb,)x" "\n"
455 R"x( elt: e => e.dataset.elt,)x" "\n"
456 R"x( // If we're already viewing an iteration of this BB, stick with the same.)x" "\n"
457 R"x( iter: e => (selection.iter && selection.bb == e.dataset.bb) ? selection.iter : lastIter(e.dataset.bb),)x" "\n"
458 R"x( }, data);)x" "\n"
459 R"x( watch('#cfg', '.bb', {)x" "\n"
460 R"x( hover: e => e.id,)x" "\n"
461 R"x( bb: e => e.id,)x" "\n"
462 R"x( elt: e => e.id + ".0",)x" "\n"
463 R"x( iter: e => lastIter(e.id),)x" "\n"
464 R"x( }, data);)x" "\n"
465 R"x( watch('#timeline', '.entry', {)x" "\n"
466 R"x( hover: e => [e.id, e.dataset.bb],)x" "\n"
467 R"x( bb: e => e.dataset.bb,)x" "\n"
468 R"x( elt: e => e.dataset.bb + ".0",)x" "\n"
469 R"x( iter: e => e.id,)x" "\n"
470 R"x( }, data);)x" "\n"
471 R"x( watch('#bb-elements', 'tr', {)x" "\n"
472 R"x( hover: e => e.id,)x" "\n"
473 R"x( elt: e => e.id,)x" "\n"
474 R"x( }, data);)x" "\n"
475 R"x( watch('#iterations', '.chooser', {)x" "\n"
476 R"x( hover: e => e.dataset.iter,)x" "\n"
477 R"x( iter: e => e.dataset.iter,)x" "\n"
478 R"x( }, data);)x" "\n"
479 R"x( updateSelection({}, data);)x" "\n"
480 R"x(})x" "\n"
481 R"x(function applyClassIf(cls, query) {)x" "\n"
482 R"x( document.querySelectorAll('.' + cls).forEach(elt => elt.classList.remove(cls));)x" "\n"
483 R"x( document.querySelectorAll(query).forEach(elt => elt.classList.add(cls));)x" "\n"
484 R"x(})x" "\n"
485 R"x(// Turns a class name into a CSS selector matching it, with some wrinkles:)x" "\n"
486 R"x(// - we treat id="foo" just like class="foo" to avoid repetition in the HTML)x" "\n"
487 R"x(// - cls can be an array of strings, we match them all)x" "\n"
488 R"x(function classSelector(cls) {)x" "\n"
489 R"x( if (cls == null) return null;)x" "\n"
490 R"x( if (Array.isArray(cls)) return cls.map(classSelector).join(', ');)x" "\n"
491 R"x( var escaped = cls.replace('.', '\\.').replace(':', '\\:');)x" "\n"
492 R"x( // don't require id="foo" class="foo")x" "\n"
493 R"x( return '.' + escaped + ", #" + escaped;)x" "\n"
494 R"x(})x" "\n"
495 R"x()x" "\n"
496 R"x(// Add a stylesheet defining colors for n basic blocks.)x" "\n"
497 R"x(function addBBColors(n) {)x" "\n"
498 R"x( let sheet = new CSSStyleSheet();)x" "\n"
499 R"x( // hex values to subtract from fff to get a base color)x" "\n"
500 R"x( options = [0x001, 0x010, 0x011, 0x100, 0x101, 0x110, 0x111];)x" "\n"
501 R"x( function color(hex) {)x" "\n"
502 R"x( return "#" + hex.toString(16).padStart(3, "0");)x" "\n"
503 R"x( })x" "\n"
504 R"x( function add(selector, property, hex) {)x" "\n"
505 R"x( sheet.insertRule(`${selector} { ${property}: ${color(hex)}; }`))x" "\n"
506 R"x( })x" "\n"
507 R"x( for (var i = 0; i < n; ++i) {)x" "\n"
508 R"x( let opt = options[i%options.length];)x" "\n"
509 R"x( add(`.B${i}`, 'background-color', 0xfff - 2*opt);)x" "\n"
510 R"x( add(`#B${i} polygon`, 'fill', 0xfff - 2*opt);)x" "\n"
511 R"x( add(`#B${i} polygon`, 'stroke', 0x888 - 4*opt);)x" "\n"
512 R"x( })x" "\n"
513 R"x( document.adoptedStyleSheets.push(sheet);)x" "\n"
514 R"x(})x" "\n"
515 R"x()x" "\n"
516 ;
517