1/* Three figures added in September 2026, built from the document corpus itself: 2 A. the discourse peculiar to each document type (keyness.json) 3 B. the citation network between documents (citations.json) 4 C. the actors named in each document type and what they do (actors_by_type.json) 5 Each figure renders only if its container is present on the page. */ 6(function () { 7 "use strict"; 8 9 var PALETTE = ["#3f6fb5", "#c2632c", "#4a8a67", "#8b5a9e", "#b0843a", "#5b8ca8", 10 "#a8555f", "#6b7a4a", "#8a6a55", "#7a7a8c", "#457f8a", "#9c6b3f"]; 11 var colourOf = function (values) { 12 var scale = {}; 13 values.forEach(function (v, i) { scale[v] = PALETTE[i % PALETTE.length]; }); 14 return function (v) { return scale[v] || "#8a8a8a"; }; 15 }; 16 var el = function (id) { return document.getElementById(id); }; 17 var clear = function (node) { while (node.firstChild) node.removeChild(node.firstChild); }; 18 19 function typeSelector(container, types, onChange) { 20 var bar = document.createElement("div"); 21 bar.className = "fig2-types"; 22 types.forEach(function (t, i) { 23 var b = document.createElement("button"); 24 b.type = "button"; 25 b.textContent = t; 26 b.className = "fig2-type" + (i === 0 ? " is-on" : ""); 27 b.addEventListener("click", function () { 28 bar.querySelectorAll(".fig2-type").forEach(function (o) { o.classList.remove("is-on"); }); 29 b.classList.add("is-on"); 30 onChange(t); 31 }); 32 bar.appendChild(b); 33 }); 34 container.appendChild(bar); 35 } 36 37 /* ââ A. Discourse of each document type âââââââââââââââââââââââââââââââââââ */ 38 function keyness(data) { 39 var host = el("fig-keyness"); 40 if (!host) return; 41 var types = Object.keys(data).sort(function (a, b) { 42 return data[b][0].category_docs - data[a][0].category_docs; 43 }); 44 var chart = document.createElement("div"); 45 chart.className = "fig2-chart"; 46 typeSelector(host, types, draw); 47 host.appendChild(chart); 48 49 function draw(type) { 50 clear(chart); 51 var terms = data[type].slice(0, 18); 52 var max = d3.max(terms, function (d) { return d.z; }); 53 var note = document.createElement("p"); 54 note.className = "fig2-note"; 55 note.textContent = terms[0].category_docs + " documents of this type. Candidate concepts are the " 56 + "noun chunks of each document, so verbs and bare adjectives are excluded; each is scored by the " 57 + "log-odds ratio of its document frequency in this type against the rest of the corpus, with an " 58 + "informative Dirichlet prior (Monroe, Colaresi and Quinn 2008), which keeps rare concepts and " 59 + "small types comparable. A concept counts only if more than one company uses it. Bars show that " 60 + "score; the figure on the right is the number of documents of this type naming the concept."; 61 chart.appendChild(note); 62 var list = document.createElement("ul"); 63 list.className = "fig2-bars"; 64 terms.forEach(function (t) { 65 var li = document.createElement("li"); 66 var label = document.createElement("span"); 67 label.className = "fig2-term"; 68 label.textContent = t.term; 69 var track = document.createElement("span"); 70 track.className = "fig2-track"; 71 var bar = document.createElement("span"); 72 bar.className = "fig2-bar"; 73 bar.style.width = Math.max(2, (t.z / max) * 100) + "%"; 74 bar.style.background = PALETTE[0]; 75 track.appendChild(bar); 76 var value = document.createElement("span"); 77 value.className = "fig2-value"; 78 value.textContent = t.docs + "/" + t.category_docs + " docs"; 79 li.appendChild(label); li.appendChild(track); li.appendChild(value); 80 list.appendChild(li); 81 }); 82 chart.appendChild(list); 83 } 84 draw(types[0]); 85 } 86 87 /* ââ B. Citation network ââââââââââââââââââââââââââââââââââââââââââââââââââ */ 88 function network(data) { 89 var host = el("fig-network"); 90 if (!host) return; 91 var companies = Array.from(new Set(data.nodes.map(function (n) { return n.company; }))).sort(); 92 var categories = Array.from(new Set(data.nodes.map(function (n) { return n.category; }))).sort(); 93 var colour = colourOf(categories); 94 var state = {company: "All companies", category: null}; 95 var applyHighlight = function () {}; 96 97 var controls = document.createElement("div"); 98 controls.className = "fig2-types";
99 ["All companies"].concat(companies).forEach(function (c, i) { 100 var b = document.createElement("button"); 101 b.type = "button"; b.textContent = c; 102 b.className = "fig2-type" + (i === 0 ? " is-on" : ""); 103 b.addEventListener("click", function () { 104 controls.querySelectorAll(".fig2-type").forEach(function (o) { o.classList.remove("is-on"); }); 105 b.classList.add("is-on"); state.company = c; draw(); 106 }); 107 controls.appendChild(b); 108 }); 109 host.appendChild(controls); 110 111 var legend = document.createElement("div"); 112 legend.className = "fig2-legend"; 113 categories.forEach(function (c) { 114 var s = document.createElement("button"); 115 s.type = "button"; 116 s.className = "fig2-key fig2-key-btn"; 117 s.innerHTML = '<i style="background:' + colour(c) + '"></i>' + c; 118 s.addEventListener("click", function () { 119 state.category = state.category === c ? null : c; 120 legend.querySelectorAll(".fig2-key-btn").forEach(function (o) { 121 o.classList.toggle("is-on", o === s && state.category === c); 122 }); 123 applyHighlight(); 124 }); 125 legend.appendChild(s); 126 }); 127 host.appendChild(legend); 128 129 var wrap = document.createElement("div"); 130 wrap.className = "fig2-canvas"; 131 host.appendChild(wrap); 132 var tip = document.createElement("div"); 133 tip.className = "fig2-tip"; tip.hidden = true; 134 host.appendChild(tip); 135 136 function draw() { 137 clear(wrap); 138 var nodes = data.nodes.filter(function (n) { 139 return state.company === "All companies" || n.company === state.company; 140 }).map(function (n) { return Object.assign({}, n); }); 141 var keep = new Set(nodes.map(function (n) { return n.key; })); 142 var links = data.edges.filter(function (e) { 143 return keep.has(e.source) && keep.has(e.target); 144 }).map(function (e) { return {source: e.source, target: e.target, type: e.type}; }); 145 // Keep the picture readable: drop documents that end up with no visible tie. 146 var linked = new Set(); 147 links.forEach(function (l) { linked.add(l.source); linked.add(l.target); }); 148 nodes = nodes.filter(function (n) { return linked.has(n.key); }); 149 150 var width = wrap.clientWidth || 900, height = 560; 151 var svg = d3.select(wrap).append("svg") 152 .attr("viewBox", "0 0 " + width + " " + height) 153 .attr("width", "100%").attr("height", height); 154 var g = svg.append("g"); 155 svg.call(d3.zoom().scaleExtent([0.3, 4]).on("zoom", function (ev) { 156 g.attr("transform", ev.transform); 157 })); 158 159 var r = function (n) { return 3 + Math.sqrt(n.cited_by) * 2.4; }; 160 var sim = d3.forceSimulation(nodes) 161 .force("link", d3.forceLink(links).id(function (d) { return d.key; }).distance(60).strength(0.25)) 162 .force("charge", d3.forceManyBody().strength(-90)) 163 .force("centre", d3.forceCenter(width / 2, height / 2)) 164 .force("collide", d3.forceCollide().radius(function (d) { return r(d) + 3; })); 165 166 var link = g.append("g").attr("stroke", "#c9c4bb").attr("stroke-opacity", 0.55) 167 .selectAll("line").data(links).join("line") 168 .attr("stroke-width", function (d) { return d.type === "title" ? 1.4 : 0.7; }) 169 .attr("stroke-dasharray", function (d) { return d.type === "title" ? null : "2,2"; }); 170 171 var node = g.append("g").selectAll("circle").data(nodes).join("circle") 172 .attr("r", r) 173 .attr("fill", function (d) { return colour(d.category); }) 174 .attr("stroke", "#fff").attr("stroke-width", 0.8) 175 .style("cursor", "pointer") 176 .on("mousemove", function (ev, d) { 177 tip.hidden = false; 178 tip.innerHTML = "<strong>" + d.title + "</strong><br>" + d.company + " â " + d.category 179 + "<br>cited by " + d.cited_by + ", cites " + d.cites; 180 var box = host.getBoundingClientRect(); 181 tip.style.left = (ev.clientX - box.left + 12) + "px"; 182 tip.style.top = (ev.clientY - box.top + 12) + "px"; 183 }) 184 .on("mouseleave", function () { tip.hidden = true; }) 185 .on("click", function (ev, d) { if (d.url) window.open(d.url, "_blank", "noopener"); }) 186 .call(d3.drag() 187 .on("start", function (ev, d) { if (!ev.active) sim.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; }) 188 .on("drag", function (ev, d) { d.fx = ev.x; d.fy = ev.y; }) 189 .on("end", function (ev, d) { if (!ev.active) sim.alphaTarget(0); d.fx = null; d.fy = null; })); 190 191 var labelled = nodes.slice().sort(function (a, b) { return b.cited_by - a.cited_by; }).slice(0, 12); 192 var label = g.append("g").selectAll("text").data(labelled).join("text") 193 .text(function (d) { return d.title.length > 42 ? d.title.slice(0, 40) + "â¦" : d.title; }) 194 .attr("font-size", 10).attr("fill", "#2b2b2b").attr("pointer-events", "none") 195 .attr("paint-order", "stroke").attr("stroke", "#fdfcf9").attr("stroke-width", 3); 196 197 applyHighlight = function () { 198 var on = state.category; 199 node.attr("opacity", function (d) { return !on || d.category === on ? 1 : 0.12; }) 200 .attr("stroke", function (d) { return on && d.category === on ? "#23211e" : "#fff"; }) 201 .attr("stroke-width", function (d) { return on && d.category === on ? 1.4 : 0.8; }); 202 link.attr("stroke-opacity", function (d) { 203 if (!on) return 0.55; 204 return d.source.category === on || d.target.category === on ? 0.5 : 0.05; 205 }); 206 label.attr("opacity", function (d) { return !on || d.category === on ? 1 : 0.15; }); 207 }; 208 applyHighlight(); 209 210 function ticked() { 211 link.attr("x1", function (d) { return d.source.x; }).attr("y1", function (d) { return d.source.y; }) 212 .attr("x2", function (d) { return d.target.x; }).attr("y2", function (d) { return d.target.y; }); 213 node.attr("cx", function (d) { return d.x; }).attr("cy", function (d) { return d.y; }); 214 label.attr("x", function (d) { return d.x + r(d) + 3; }).attr("y", function (d) { return d.y + 3; }); 215 } 216 sim.on("tick", ticked); 217 // Inside a slide of the presentation the browser runs animations slowly, so the layout 218 // is computed at once and shown settled. 219 if (new URLSearchParams(location.search).has("embed")) { 220 sim.stop(); 221 for (var t = 0; t < 400; t++) sim.tick(); 222 ticked(); 223 } 224 225 var count = document.createElement("p"); 226 count.className = "fig2-note"; 227 count.textContent = nodes.length + " documents and " + links.length 228 + " ties. A solid line means one document names another by title; a dotted line means it links to it. " 229 + "Circles grow with the number of documents citing them. Click a circle to open the document."; 230 wrap.appendChild(count); 231 } 232 draw(); 233 } 234 235 /* ââ C. Actors per document type ââââââââââââââââââââââââââââââââââââââââââ */ 236 function actors(data) { 237 var host = el("fig-actors"); 238 if (!host) return; 239 var types = Object.keys(data).sort(function (a, b) { 240 return d3.sum(data[b], function (d) { return d.documents; }) 241 - d3.sum(data[a], function (d) { return d.documents; }); 242 }); 243 var relations = Array.from(new Set([].concat.apply([], types.map(function (t) { 244 return data[t].map(function (a) { return a.main_relation; }); 245 }
245)))).sort(); 246 var colour = colourOf(relations); 247 248 var picked = null; 249 var legend = document.createElement("div"); 250 legend.className = "fig2-legend"; 251 relations.forEach(function (rel) { 252 var s = document.createElement("button"); 253 s.type = "button"; 254 s.className = "fig2-key fig2-key-btn"; 255 s.innerHTML = '<i style="background:' + colour(rel) + '"></i>' + rel; 256 s.addEventListener("click", function () { 257 picked = picked === rel ? null : rel; 258 legend.querySelectorAll(".fig2-key-btn").forEach(function (o) { 259 o.classList.toggle("is-on", o === s && picked === rel); 260 }); 261 d3.select(chart).selectAll("g rect") 262 .attr("opacity", function (d) { return !picked || d.data.main_relation === picked ? 1 : 0.12; }); 263 d3.select(chart).selectAll("g text") 264 .attr("opacity", function (d) { return !picked || d.data.main_relation === picked ? 1 : 0.12; }); 265 }); 266 legend.appendChild(s); 267 }); 268 269 var chart = document.createElement("div"); 270 chart.className = "fig2-chart"; 271 typeSelector(host, types, draw); 272 host.appendChild(legend); 273 host.appendChild(chart); 274 275 function draw(type) { 276 clear(chart); 277 picked = null; 278 legend.querySelectorAll(".fig2-key-btn").forEach(function (o) { o.classList.remove("is-on"); }); 279 var items = data[type].slice(0, 24); 280 var width = chart.clientWidth || 900, height = 420; 281 var root = d3.hierarchy({children: items}).sum(function (d) { return d.documents; }) 282 .sort(function (a, b) { return b.value - a.value; }); 283 d3.treemap().size([width, height]).paddingInner(3).round(true)(root); 284 var svg = d3.select(chart).append("svg") 285 .attr("viewBox", "0 0 " + width + " " + height) 286 .attr("width", "100%").attr("height", height); 287 var cell = svg.selectAll("g").data(root.leaves()).join("g") 288 .attr("transform", function (d) { return "translate(" + d.x0 + "," + d.y0 + ")"; }); 289 cell.append("rect") 290 .attr("width", function (d) { return d.x1 - d.x0; }) 291 .attr("height", function (d) { return d.y1 - d.y0; }) 292 .attr("fill", function (d) { return colour(d.data.main_relation); }) 293 .attr("rx", 2); 294 cell.append("title").text(function (d) { 295 return d.data.actor + " â " + d.data.main_relation + "\n" + d.data.documents 296 + " documents of this type"; 297 }); 298 cell.append("text").attr("x", 5).attr("y", 14) 299 .attr("font-size", 11).attr("fill", "#fff") 300 .text(function (d) { 301 var space = d.x1 - d.x0; 302 if (space < 46 || d.y1 - d.y0 < 18) return ""; 303 var chars = Math.floor(space / 6.2); 304 return d.data.actor.length > chars ? d.data.actor.slice(0, chars - 1) + "â¦" : d.data.actor; 305 }); 306 cell.append("text").attr("x", 5).attr("y", 27) 307 .attr("font-size", 9.5).attr("fill", "rgba(255,255,255,.85)") 308 .text(function (d) { 309 return (d.x1 - d.x0) < 80 || (d.y1 - d.y0) < 34 ? "" : d.data.documents + " docs"; 310 }); 311 var note = document.createElement("p"); 312 note.className = "fig2-note"; 313 note.textContent = "Each square is an actor credited in documents of this type; its size is the number " 314 + "of such documents, and its colour the role those documents most often give it: a source of the " 315 + "definition of a conduct or risk, conceiving a training method, taking part in training, creating a " 316 + "benchmark, external evaluation or red teaming. Actors and roles come from the second, " 317 + "retrieval-grounded reading, and each was found verbatim on its page in the index; an actor merely " 318 + "named in a document, without one of these roles, is not counted. The authoring company is not " 319 + "counted in its own documents. Click a role in the legend to highlight it."; 320 chart.appendChild(note); 321 } 322 draw(types[0]); 323 } 324 325 /* ââ B2. Actor types across document types ââââââââââââââââââââââââââââââââ */ 326 function actorTypes(data) { 327 var host = el("fig-actor-types"); 328 if (!host) return; 329 var max = 0;
330 data.types.forEach(function (t) { 331 data.categories.forEach(function (c) { max = Math.max(max, data.matrix[t][c]); }); 332 }); 333 334 var tip = document.createElement("div"); 335 tip.className = "fig2-tip"; 336 tip.hidden = true; 337 host.appendChild(tip); 338 339 var table = document.createElement("table"); 340 table.className = "fig2-matrix"; 341 var head = document.createElement("tr"); 342 head.appendChild(document.createElement("th")); 343 data.categories.forEach(function (c, i) { 344 var th = document.createElement("th"); 345 th.dataset.col = String(i); 346 var span = document.createElement("span"); 347 span.textContent = c; 348 th.appendChild(span); 349 head.appendChild(th); 350 }); 351 table.appendChild(head); 352 353 function show(ev, cell) { 354 var t = cell.dataset.type, c = cell.dataset.category, n = cell.dataset.count; 355 var who = ((data.examples[t] || {})[c] || []); 356 tip.hidden = false; 357 tip.innerHTML = "<strong>" + t + "</strong> in <strong>" + c + "</strong><br>" 358 + n + (n === "1" ? " document" : " documents") + " of this type name an actor of this type" 359 + (who.length ? "<br><span class=\"fig2-tip-who\">" + who.join(", ") 360 + (who.length >= 6 ? " â¦" : "") + "</span>" : ""); 361 var box = host.getBoundingClientRect(); 362 var x = ev.clientX - box.left + 14, y = ev.clientY - box.top + 14; 363 tip.style.left = Math.min(x, host.clientWidth - tip.offsetWidth - 8) + "px"; 364 tip.style.top = y + "px"; 365 } 366 function highlight(row, col, on) { 367 table.querySelectorAll('[data-row="' + row + '"], [data-col="' + col + '"]') 368 .forEach(function (e) { e.classList.toggle("is-lit", on); }); 369 } 370 371 data.types.forEach(function (t, r) { 372 var tr = document.createElement("tr"); 373 var label = document.createElement("th"); 374 label.className = "fig2-matrix-row"; 375 label.dataset.row = String(r); 376 label.innerHTML = '<i style="background:' + (data.colors[t] || "#b9b1a2") + '"></i>' + t; 377 tr.appendChild(label); 378 data.categories.forEach(function (c, i) { 379 var n = data.matrix[t][c]; 380 var td = document.createElement("td"); 381 td.dataset.row = String(r); 382 td.dataset.col = String(i); 383 if (n) { 384 td.className = "has-value"; 385 td.dataset.type = t; 386 td.dataset.category = c; 387 td.dataset.count = String(n); 388 td.style.background = data.colors[t] || "#6f6a62"; 389 // Square root keeps the smaller counts visible next to the internal actors. 390 td.style.opacity = String(0.14 + 0.86 * Math.sqrt(n) / Math.sqrt(max)); 391 td.textContent = n; 392 td.addEventListener("mousemove", function (ev) { show(ev, td); }); 393 td.addEventListener("mouseenter", function () { highlight(r, i, true); }); 394 td.addEventListener("mouseleave", function () { 395 highlight(r, i, false); tip.hidden = true; 396 }); 397 } 398 tr.appendChild(td); 399 }); 400 table.appendChild(tr); 401 }); 402 host.appendChild(table); 403 404 var note = document.createElement("p"); 405 note.className = "fig2-note"; 406 note.textContent = "Rows are actor types, columns document types. A cell counts the documents of " 407 + "that type in which an actor of that type is credited with one of four roles: a source of the " 408 + "definition of a conduct or risk, a part in training (conceiving the method or taking part), the " 409 + "creation of a benchmark, or the evaluation or red teaming of a model. Every actor and every role was " 410 + "checked against its page in the index, and each actor's type against the corpus and, where needed, " 411 + "the web. The authoring company is not counted in its own documents. Colour follows the actor type, " 412 + "its strength the count (square-root scale). Hover a cell for the actors behind it."; 413 host.appendChild(note); 414 } 415 416 function load(file, fn) { 417 fetch(file + "?v=20260918").then(function (r) { return r.json(); }).then(fn) 418 .catch(function (e) { console.warn("figures2: could not load " + file, e); }); 419 } 420 document.addEventListener("DOMContentLoaded", function () { 421 if (el("fig-keyness")) load("keyness.json", keyness); 422 if (el("fig-network")) load("citations.json", network); 423 if (el("fig-actors")) load("actors_by_type.json", actors); 424 if (el("fig-actor-types")) load("actor_types_by_category.json", actorTypes); 425 }); 426})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.