github.com/aclements/go-misc@v0.0.0-20240129233631-2f6ede80790c/rtcheck/static/main.js (about) 1 "use strict"; 2 3 function initOrder(strings, edges) { 4 // Hook into the graph edges. 5 var labelRe = /^l([0-9]+)-l([0-9]+)$/; 6 $.each(edges, function(_, edge) { 7 var g = $(document.getElementById(edge.EdgeID)); 8 // Increase the size of the click target by making a second, 9 // invisible, larger path element. 10 var path = $("path:first", g); 11 path.clone().attr("stroke-width", "10px").attr("stroke", "transparent").appendTo(path.parent()); 12 // On click, update the info box. 13 g. 14 css({cursor: "pointer"}). 15 on("click", function(ev) { 16 showEdge(strings, edge); 17 }); 18 }); 19 enableHighlighting($("#graph")[0]); 20 zoomify($("#graph")[0], $("#graphWrap")[0]); 21 $("#graph").css("visibility", "visible"); 22 } 23 24 function showEdge(strings, edge) { 25 var info = $("#info"); 26 info.empty().scrollTop(0); 27 28 // Show summary information. 29 $("<p>").appendTo(info).text( 30 edge.Paths.length + " path(s) acquire " + edge.Locks[0] + ", then " + edge.Locks[1] + ":" 31 ).css({fontWeight: "bold"}); 32 33 $.each(edge.Paths, function(_, path) { 34 var p = $("<p>").appendTo(info).css("white-space", "nowrap"); 35 $("<div>").appendTo(p).text(strings[path.RootFn]); 36 function posText(pathID, line) { 37 // Keep only the trailing part of the path. 38 return strings[pathID].replace(/.*\//, "") + ":" + line; 39 } 40 function renderStack(stack) { 41 var elided = []; 42 var elideDiv; 43 // Render each frame. 44 $.each(stack.Op, function(i) { 45 var div = $("<div>").appendTo(p); 46 var indent = i == 0 ? "1em" : "2em"; 47 var showFirst = 2, showLast = 3; 48 if (i >= showFirst && stack.Op.length - i > showLast) { 49 // Elide middle of the path. 50 if (elided.length === 0) { 51 elideDiv = $("<div>").appendTo(p).css("padding-left", indent); 52 } 53 elided.push(div[0]); 54 } 55 div.appendTo(p); 56 // TODO: Link to path somehow. 57 div.text(strings[stack.Op[i]] + " at " + posText(stack.P[i], stack.L[i])); 58 div.css("padding-left", indent); 59 }); 60 // If we elided frames, update the show link. 61 if (elided.length === 1) { 62 // No point in eliding one frame. 63 elideDiv.hide(); 64 } else if (elided.length > 0) { 65 elideDiv.text("... show " + elided.length + " elided frames ...").css({color: "#00e", cursor: "pointer"}); 66 $(elided).hide(); 67 elideDiv.on("click", function(ev) { 68 elideDiv.hide(); 69 $(elided).show(); 70 }) 71 } 72 } 73 renderStack(path.From); 74 renderStack(path.To); 75 }); 76 } 77 78 // enableHighlighting takes an GraphViz-generated SVG and enables 79 // interactive highlighting when the mouse hovers over nodes and 80 // edges. 81 function enableHighlighting(svg) { 82 var nodes = {}, edges = {}; 83 function all(opacity) { 84 $.each(nodes, function(_, node) { 85 $(node.dom).clearQueue().fadeTo('fast', opacity); 86 }) 87 $.each(edges, function(_, edge) { 88 $(edge.dom).clearQueue().fadeTo('fast', opacity); 89 }) 90 } 91 92 function highlight(dom) { 93 $(dom).clearQueue().fadeTo('fast', 1); 94 } 95 96 // Process nodes. 97 $(".node", svg).each(function(_, node) { 98 var id = $("title", node).text(); 99 var info = {dom: node, edges: []}; 100 nodes[id] = info; 101 $(node).on("mouseenter", function() { 102 all(0.25); 103 highlight(node); 104 $.each(info.edges, function(_, edge) { 105 highlight(edge.dom); 106 if (edge.from !== id) 107 highlight(nodes[edge.from].dom); 108 if (edge.to !== id) 109 highlight(nodes[edge.to].dom); 110 }); 111 }).on("mouseleave", function() { 112 all(1); 113 }); 114 }); 115 116 // Process edges. 117 $(".edge", svg).each(function(_, edge) { 118 var id = $("title", edge).text(); 119 var m = id.match(/^(.*)->(.*)$/); 120 var info = {dom: edge, from: m[1], to: m[2]}; 121 edges[edge.id] = info; 122 nodes[info.from].edges.push(info); 123 nodes[info.to].edges.push(info); 124 $(edge).on("mouseenter", function() { 125 all(0.25); 126 highlight(edge); 127 highlight(nodes[info.from].dom); 128 highlight(nodes[info.to].dom); 129 }).on("mouseleave", function() { 130 all(1); 131 }); 132 }); 133 } 134 135 // zoomify makes drags and wheel events on element fill pan and zoom 136 // svg. It initially centers the svg at (0, 0) and scales it down to 137 // fit in fill. Hence, the caller should center the svg element within 138 // fill. 139 function zoomify(svg, fill) { 140 var svg = $(svg); 141 var fill = $(fill); 142 143 // Wrap svg in a group we can transform. 144 var g = $(document.createElementNS("http://www.w3.org/2000/svg", "g")); 145 g.append(svg.children()).appendTo(svg); 146 147 // Create an initial transform to center and fit the svg. 148 var bbox = g[0].getBBox(); 149 var scale = Math.min(fill.width() / bbox.width, fill.height() / bbox.height); 150 if (scale > 1) scale = 1; 151 var mat = svg[0].createSVGMatrix(). 152 translate(-bbox.x, -bbox.y). 153 scale(scale). 154 translate(-bbox.width/2, -bbox.height/2); 155 var transform = svg[0].createSVGTransform(); 156 transform.setMatrix(mat); 157 g[0].transform.baseVal.insertItemBefore(transform, 0); 158 159 // Handle drags. 160 var lastpos; 161 function mousemove(ev) { 162 if (ev.buttons == 0) { 163 fill.off("mousemove"); 164 return; 165 } 166 var deltaX = ev.pageX - lastpos.pageX; 167 var deltaY = ev.pageY - lastpos.pageY; 168 lastpos = ev; 169 var transform = svg[0].createSVGTransform(); 170 transform.setTranslate(deltaX, deltaY); 171 g[0].transform.baseVal.insertItemBefore(transform, 0); 172 g[0].transform.baseVal.consolidate(); 173 ev.preventDefault(); 174 } 175 fill.on("mousedown", function(ev) { 176 lastpos = ev; 177 fill.on("mousemove", mousemove); 178 ev.preventDefault(); 179 }); 180 fill.on("mouseup", function(ev) { 181 fill.off("mousemove"); 182 ev.preventDefault(); 183 }); 184 185 // Handle zooms. 186 var point = svg[0].createSVGPoint(); 187 fill.on("wheel", function(ev) { 188 var delta = ev.originalEvent.deltaY; 189 // rates is the delta required to scale by a factor of 2. 190 var rates = [ 191 500, // WheelEvent.DOM_DELTA_PIXEL 192 30, // WheelEvent.DOM_DELTA_LINE 193 0.5, // WheelEvent.DOM_DELTA_PAGE 194 ]; 195 var factor = Math.pow(2, -delta / rates[ev.originalEvent.deltaMode]); 196 point.x = ev.clientX === undefined ? ev.originalEvent.clientX : ev.clientX; 197 point.y = ev.clientY === undefined ? ev.originalEvent.clientY : ev.clientY; 198 var center = point.matrixTransform(svg[0].getScreenCTM().inverse()); 199 200 // Scale by factor around center. 201 var mat = svg[0].createSVGMatrix(). 202 translate(center.x, center.y). 203 scale(factor). 204 translate(-center.x, -center.y); 205 var transform = svg[0].createSVGTransform(); 206 transform.setMatrix(mat); 207 g[0].transform.baseVal.insertItemBefore(transform, 0); 208 g[0].transform.baseVal.consolidate(); 209 ev.preventDefault(); 210 }); 211 }