<commit_before>"use strict";

function getJSON(path, callback) {
  var req = new XMLHttpRequest();
  req.open("GET", path, true);
  req.onreadystatechange = function() {
    if (req.readyState == XMLHttpRequest.DONE && req.status == 200) {
      try {
        var data = JSON.parse(req.responseText);
        callback(data);
      } catch (e) {
        console.log(e);
      }
    }
  };
  req.send();
}

var node;
var link;
var radius = 8;

function tick() {
  node.attr("transform", function(d) {
    return "translate(" + d.x + ", " + d.y + ")";
  });
  link.attr("x1", function(d) { return d.source.x; })
      .attr("y1", function(d) { return d.source.y; })
      .attr("x2", function(d) {
        if (d.source.x == d.target.x) {
          return d.source.x;
        }
        var dx = d.target.x - d.source.x;
        var dx2 = dx * dx;
        var dy = d.target.y - d.source.y;
        var dy2 = dy * dy;
        var delta = Math.sqrt(dx2 + dy2);
        var sign = d.source.x < d.target.x ? 1 : -1;
        return d.source.x + sign * (delta - radius) /
                            Math.sqrt(1 + (dy2 / dx2));
      })
      .attr("y2", function(d) {
        if (d.source.y == d.target.y) {
          return d.source.y;
        }
        var dx = d.target.x - d.source.x;
        var dx2 = dx * dx;
        var dy = d.target.y - d.source.y;
        var dy2 = dy * dy;
        var delta = Math.sqrt(dx2 + dy2);
        var sign = d.source.y < d.target.y ? 1 : -1;
        return d.source.y + sign * (delta - radius) /
                            Math.sqrt(1 + (dx2 / dy2));
      });
}

function nameToIndex(name, nodes) {
  for (var i = 0; i < nodes.length; i++) {
    if (nodes[i].name == name) {
      return i;
    }
  }
  return -1;
}

getJSON("roots.json", function(rootsData) {
  getJSON("intermediates.json", function(intermediatesData) {
    var roots = [];
    for (var root of Object.keys(rootsData)) {
      if (root in intermediatesData) {
        roots.push(root);
      }
    }
    $('#autocomplete').autocomplete({
      source: roots,
      minLength: 0,
      select: function(event, suggestion) {
        doForceMap(suggestion.item.value, rootsData, intermediatesData);
      }
    });
    doForceMap(location.search
               ? decodeURIComponent(location.search.substring(1))
               : roots[0],
               rootsData, intermediatesData);
  });
});

function dragstart(d) {
  d.fixed = true;
}

// Takes something like "O=..., OU=..., CN=..." and returns a shorter name,
// most likely based on the text after "CN=". The wrinkle is that each of O,
// OU, and CN might not be present (although at least one of the three should
// be). Another wrinkle is that commas may appear in the value of each field.
function shortenFullName(name) {
  var cnRegexp = /CN=(.*)$/;
  var match = name.match(cnRegexp);
  if (match) {
    return match[1];
  }
  // Interestingly, if CN isn't present, OU=... is guaranteed to be last, so we
  // can use more or less the same regular expression.
  var ouRegexp = /OU=(.*)$/;
  match = name.match(ouRegexp);
  if (match) {
    return match[1];
  }
  // Same deal with O=...
  var oRegexp = /O=(.*)$/;
  match = name.match(oRegexp);
  if (match) {
    return match[1];
  }
  // Eh, I guess we tried.
  return name;
}

function newNode(name, width, height, constrained, pem) {
  return { x: width / 2, y: height / 2, name: name, constrained: constrained,
           pem: pem };
}

function doForceMap(root, rootsMap, intermediatesMap) {
  if (!(root in intermediatesMap)) {
    console.log(root + " hasn't issued any intermediates?");
    return;
  }

  setCertsplainerEnabled(false);

  var nodes = [];
  var links = [];

  d3.select("svg")
      .remove();

  var width = window.screen.availWidth;
  var height = Math.ceil(0.75 * window.screen.availHeight);

  nodes.push(newNode(root, width, height, false, null));
  var prevNodeCount = nodes.length;
  for (var issueeDN in intermediatesMap[root].Issuees) {
    var issuee = intermediatesMap[root].Issuees[issueeDN];
    if (nameToIndex(issuee.DN, nodes) == -1) {
      nodes.push(
        newNode(issuee.DN, width, height,
                issuee.HasNameConstraints || issuee.IsLengthConstrained,
                issuee.PEM));
    }
  }
  while (prevNodeCount != nodes.length) {
    prevNodeCount = nodes.length;
    for (var n of nodes) {
      if (!(n.name in intermediatesMap)) {
        continue;
      }
      for (var issueeDN in intermediatesMap[n.name].Issuees) {
        var issuee = intermediatesMap[n.name].Issuees[issueeDN];
        if (nameToIndex(issuee.DN, nodes) == -1) {
          nodes.push(
            newNode(issuee.DN, width, height,
                    issuee.HasNameConstraints || issuee.IsLengthConstrained,
                    issuee.PEM));
        }
      }
    }
  }

  for (var nodeIndex in nodes) {
    if (!(nodes[nodeIndex].name in intermediatesMap)) {
      continue;
    }
    for (var issueeDN in intermediatesMap[nodes[nodeIndex].name].Issuees) {
      var issuee = intermediatesMap[nodes[nodeIndex].name].Issuees[issueeDN];
      var targetIndex = nameToIndex(issuee.DN, nodes);
      if (targetIndex > -1 && targetIndex != nodeIndex) {
        links.push({ source: nodes[nodeIndex], target: nodes[targetIndex] });
      }
    }
  }

  var force = d3.layout.force()
    .size([width, height])
    .nodes(nodes)<commit_msg>pathLen of 0 isn't really technically-constrained, it turns out<commit_after>"use strict";

function getJSON(path, callback) {
  var req = new XMLHttpRequest();
  req.open("GET", path, true);
  req.onreadystatechange = function() {
    if (req.readyState == XMLHttpRequest.DONE && req.status == 200) {
      try {
        var data = JSON.parse(req.responseText);
        callback(data);
      } catch (e) {
        console.log(e);
      }
    }
  };
  req.send();
}

var node;
var link;
var radius = 8;

function tick() {
  node.attr("transform", function(d) {
    return "translate(" + d.x + ", " + d.y + ")";
  });
  link.attr("x1", function(d) { return d.source.x; })
      .attr("y1", function(d) { return d.source.y; })
      .attr("x2", function(d) {
        if (d.source.x == d.target.x) {
          return d.source.x;
        }
        var dx = d.target.x - d.source.x;
        var dx2 = dx * dx;
        var dy = d.target.y - d.source.y;
        var dy2 = dy * dy;
        var delta = Math.sqrt(dx2 + dy2);
        var sign = d.source.x < d.target.x ? 1 : -1;
        return d.source.x + sign * (delta - radius) /
                            Math.sqrt(1 + (dy2 / dx2));
      })
      .attr("y2", function(d) {
        if (d.source.y == d.target.y) {
          return d.source.y;
        }
        var dx = d.target.x - d.source.x;
        var dx2 = dx * dx;
        var dy = d.target.y - d.source.y;
        var dy2 = dy * dy;
        var delta = Math.sqrt(dx2 + dy2);
        var sign = d.source.y < d.target.y ? 1 : -1;
        return d.source.y + sign * (delta - radius) /
                            Math.sqrt(1 + (dx2 / dy2));
      });
}

function nameToIndex(name, nodes) {
  for (var i = 0; i < nodes.length; i++) {
    if (nodes[i].name == name) {
      return i;
    }
  }
  return -1;
}

getJSON("roots.json", function(rootsData) {
  getJSON("intermediates.json", function(intermediatesData) {
    var roots = [];
    for (var root of Object.keys(rootsData)) {
      if (root in intermediatesData) {
        roots.push(root);
      }
    }
    $('#autocomplete').autocomplete({
      source: roots,
      minLength: 0,
      select: function(event, suggestion) {
        doForceMap(suggestion.item.value, rootsData, intermediatesData);
      }
    });
    doForceMap(location.search
               ? decodeURIComponent(location.search.substring(1))
               : roots[0],
               rootsData, intermediatesData);
  });
});

function dragstart(d) {
  d.fixed = true;
}

// Takes something like "O=..., OU=..., CN=..." and returns a shorter name,
// most likely based on the text after "CN=". The wrinkle is that each of O,
// OU, and CN might not be present (although at least one of the three should
// be). Another wrinkle is that commas may appear in the value of each field.
function shortenFullName(name) {
  var cnRegexp = /CN=(.*)$/;
  var match = name.match(cnRegexp);
  if (match) {
    return match[1];
  }
  // Interestingly, if CN isn't present, OU=... is guaranteed to be last, so we
  // can use more or less the same regular expression.
  var ouRegexp = /OU=(.*)$/;
  match = name.match(ouRegexp);
  if (match) {
    return match[1];
  }
  // Same deal with O=...
  var oRegexp = /O=(.*)$/;
  match = name.match(oRegexp);
  if (match) {
    return match[1];
  }
  // Eh, I guess we tried.
  return name;
}

function newNode(name, width, height, constrained, pem) {
  return { x: width / 2, y: height / 2, name: name, constrained: constrained,
           pem: pem };
}

function doForceMap(root, rootsMap, intermediatesMap) {
  if (!(root in intermediatesMap)) {
    console.log(root + " hasn't issued any intermediates?");
    return;
  }

  setCertsplainerEnabled(false);

  var nodes = [];
  var links = [];

  d3.select("svg")
      .remove();

  var width = window.screen.availWidth;
  var height = Math.ceil(0.75 * window.screen.availHeight);

  nodes.push(newNode(root, width, height, false, null));
  var prevNodeCount = nodes.length;
  for (var issueeDN in intermediatesMap[root].Issuees) {
    var issuee = intermediatesMap[root].Issuees[issueeDN];
    if (nameToIndex(issuee.DN, nodes) == -1) {
      nodes.push(
        newNode(issuee.DN, width, height,
                issuee.HasNameConstraints,
                issuee.PEM));
    }
  }
  while (prevNodeCount != nodes.length) {
    prevNodeCount = nodes.length;
    for (var n of nodes) {
      if (!(n.name in intermediatesMap)) {
        continue;
      }
      for (var issueeDN in intermediatesMap[n.name].Issuees) {
        var issuee = intermediatesMap[n.name].Issuees[issueeDN];
        if (nameToIndex(issuee.DN, nodes) == -1) {
          nodes.push(
            newNode(issuee.DN, width, height,
                    issuee.HasNameConstraints,
                    issuee.PEM));
        }
      }
    }
  }

  for (var nodeIndex in nodes) {
    if (!(nodes[nodeIndex].name in intermediatesMap)) {
      continue;
    }
    for (var issueeDN in intermediatesMap[nodes[nodeIndex].name].Issuees) {
      var issuee = intermediatesMap[nodes[nodeIndex].name].Issuees[issueeDN];
      var targetIndex = nameToIndex(issuee.DN, nodes);
      if (targetIndex > -1 && targetIndex != nodeIndex) {
        links.push({ source: nodes[nodeIndex], target: nodes[targetIndex] });
      }
    }
  }

  var force = d3.layout.force()
    .size([width, height])
    .nodes(nodes)
{'fraction_non_alphanumeric': 0.14906081458062118, 'fraction_numerical': 0.00827599032917984, 'mean_word_length': 2.231670673076923, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3587923', 'n_tokens_mistral': 3704, 'n_tokens_neox': 3550, 'n_words': 905}