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  }