| 1 | const 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"> →|<!--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"> →|<!--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 | ; |
| 123 | const 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 | ; |
| 295 | const 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 | |