Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

https://github.com/thu-vis/MutualDetector
01 April 2026, 16:33:42 UTC
  • Code
  • Branches (2)
  • Releases (0)
  • Visits
    • Branches
    • Releases
    • HEAD
    • refs/heads/main
    • refs/heads/video
    No releases to show
  • 5c3066e
  • /
  • src
  • /
  • plugins
  • /
  • render_text_tree.js
Raw File Download Save again
Take a new snapshot of a software origin

If the archived software origin currently browsed is not synchronized with its upstream version (for instance when new commits have been issued), you can explicitly request Software Heritage to take a new snapshot of it.

Use the form below to proceed. Once a request has been submitted and accepted, it will be processed as soon as possible. You can then check its processing state by visiting this dedicated page.
swh spinner

Processing "take a new snapshot" request ...

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
  • revision
  • snapshot
origin badgecontent badge
swh:1:cnt:aa8dd8e0547784c018abd5c8a5423a0508f5affb
origin badgedirectory badge
swh:1:dir:d810d13a526521471406fb682b82d7b4a0267eab
origin badgerevision badge
swh:1:rev:2b019233d6851facadec8e9215cc805eef47932c
origin badgesnapshot badge
swh:1:snp:79fcba32a7d79dcc00f7bb22b535a3094a18ec7e

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
  • revision
  • snapshot
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
Tip revision: 2b019233d6851facadec8e9215cc805eef47932c authored by Changjian Chen on 20 May 2024, 01:52:04 UTC
update readme
Tip revision: 2b01923
render_text_tree.js
import * as d3 from "d3";
import * as Global from "./global";
import { exit_type } from "./layout_text";
import {tree_process} from "../plugins/tree_data_helper"

const TextTree = function(parent) {
  let that = this;
  that.parent = parent;

  that.tree_node_group = that.parent.tree_node_group;
  that.rest_node_group = that.parent.rest_node_group;

  // text position
  that.text_height = that.parent.text_height;

  // node width
  that.max_text_width = that.parent.max_text_width;

  // detection result layout
  that.layout_width = that.parent.layout_width;
  that.layout_height = that.parent.layout_height;
  that.node_width = that.parent.node_width;
  that.layer_height = that.parent.layer_height;

  // bar size
  that.bar_width = that.parent.bar_width;
  that.bar_height = that.parent.bar_height;
  that.rounded_r = that.parent.rounded_r;

  that.editing_state = false;
  that.merge_action = null;
  that.target_node = null;
  that.focus_node_for_edit = null;
  that.highlight_fixed = false;

  that.parent.svg.select("defs").remove();
  that.defs = that.parent.svg
    .append("defs");
  that.defs
    .append("pattern")
    .attr("id", "edit-svg-icon")
    .attr("viewBox", "0 0 20 20")
    .attr("width", "100%")
    .attr("height", "100%")
    .append("svg")
    .attr("viewBox", "0 0 1024 1024")
    .attr("width", "20px")
    .attr("height", "20px")
    .append("path")
    .attr(
      "d",
      "M800 128.992c-24.512 0-48.512 9.504-67.008 28L416 472.992l-7.008 7.008-1.984 10.016-22.016 112-9.984 46.976 47.008-9.984 112-22.016 9.984-1.984 7.008-7.008 316-316.992A94.976 94.976 0 0 0 800 128.96z m0 62.016c7.488 0 14.88 3.84 22.016 10.976 14.24 14.272 14.24 29.76 0 44L512 556.032l-55.008 11.008 11.008-55.008 310.016-310.016c7.104-7.104 14.496-10.976 21.984-10.976zM128 256v640h640V473.984l-64 64V832H192V320h294.016l64-64z"
    )
    .style("fill", "rgb(114,114,114)");

    that.radialGradient = that.defs
        .append("radialGradient")
        .attr("id", "button-gradient")
        .attr("cx", "50%")
        .attr("cy", "50%")
        .attr("r", "40%")
        .attr("fx", "50%")
        .attr("fy", "50%");
    that.radialGradient.append("stop")
        .attr("offset", "0%")
        .style("stop-opacity", 0.5)
        .style("stop-color", "lightgrey");
    that.radialGradient.append("stop")
        .attr("offset", "100%")
        .style("stop-opacity", 1)
        .style("stop-color", "darkgrey");

  that.panel_info = [
    { x: -1, y: -1, name: "absorb" },
    { x: 0, y: -1, name: "join" },
    { x: -1, y: 0, name: "collapse" },
    { x: 0, y: 0, name: "cancel" },
  ];

  that.textClickHandler = function(evt) {
    that.highlight_fixed = true;
    window.d3 = d3;
    // if (document.querySelector("#overlay") !== null) return; // kludgy singleton approach
    that.text_element = d3.select(evt.target.parentElement).select("text");
    that.selected_input_data = that.text_element.data()[0];
    let bbox = that.text_element.node().getBoundingClientRect();
    const value = that.selected_input_data.full_name;
    let wrapper = d3
      .select("#wrapper")
      .append("div")
      .attr("id", "overlay")
      // .style("top", (that.parent.tree_node_group_y + that.selected_input_data.y + 6) + "px")
      // .style("left", (that.parent.tree_node_group_x + that.selected_input_data.x + that.layer_height / 2 + 12) + "px")
      .style("top", bbox.top + "px")
      .style("left", bbox.left + "px")
      .append("div")
      .attr("id", "obg")
      .style("display", "flex");
    // wrapper.append("span")
    //     .attr("id", "edit-title")
    //     .text("Edit node name");
    that.input = wrapper
      .append("input")
      .attr("id", "edit-input")
      .attr("type", "text")
      .attr("text-align", "middle")
      // .attr("data-src", data.id)
      // .attr("data-maxtextwidth", value.length)
      .style("max-width", that.get_text_length(value) + 20 + "px")
      .attr("value", value);
  };

  that.parent.svg.on("click", () => {
    that.highlight_fixed = false;
    that.dehighlight();
    if (that.input === undefined) return;
    that.selected_input_data.name = that.input.node().value;
    that.selected_input_data.full_name = that.selected_input_data.name;
    let text = that.wrap(that.selected_input_data.name);
    if (text.length !== that.selected_input_data.name.length) {
      that.selected_input_data.name = text + "...";
    }
    console.log(
      "edited name",
      that.selected_input_data.full_name,
      that.selected_input_data.id
    );
    that.parent.set_name_edit_history({
      id: that.selected_input_data.id,
      name: that.selected_input_data.full_name,
    });
    d3.select(".tree-node#id-" + that.selected_input_data.id)
      .select("text")
      .text(that.selected_input_data.name);
    d3.select(".tree-node#id-" + that.selected_input_data.id)
      .select("title")
      .text(that.selected_input_data.full_name);
    d3.select("#overlay").remove();
    that.input = undefined;
  });

  that.change_selected_flag = function(d, flag) {
    console.log("change selected flag", d.selected_flag, flag);
    that.create_ani = 0;
    that.remove_ani = 0;
    that.update_ani /= 2;
    d.selected_flag = flag;
    that.parent.set_selected_flag(that.parent.tree);
    console.log("change selected flag after", d.selected_flag, flag);
  };

  that.set_animation_time = function() {
    // animation
    that.create_ani = that.parent.create_ani;
    that.update_ani = that.parent.update_ani;
    that.remove_ani = that.parent.remove_ani;
  };
  that.set_animation_time();

  that.set_focus_node = function(nodes) {
    that.parent.set_focus_node(nodes);
  };

  that.fetch_word = function() {
    that.parent.$store.dispatch("fetch_word");
  };

  that.fetch_image = function() {
    that.parent.$store.dispatch("fetch_image");
  };

  that.set_selected_node = function(node) {
    that.parent.set_selected_node(node);
  };

  that.sub_component_update = function(nodes, rest_nodes) {
    // update state
    that.tree_node_group_x = that.parent.tree_node_group_x;
    that.tree_node_group_y = that.parent.tree_node_group_y;
    that.expand_tree = that.parent.expand_tree;

    that.get_text_length = function(text) {
      let self = that.tree_node_group
        .append("text")
        .attr("id", "temp")
        .attr("class", "node-name")
        .attr("text-anchor", "start")
        .attr("font-size", "18px");
      self.text(text);
      let textLength = self.node().getComputedTextLength();
      that.tree_node_group.select("#temp").remove();
      return textLength;
    };

    (that.wrap = function(text) {
      let self = that.tree_node_group
        .append("text")
        .attr("id", "temp")
        .attr("class", "node-name")
        .attr("text-anchor", "start")
        .attr("font-size", "18px");
      self.text(text);
      let textLength = self.node().getComputedTextLength();
      if (textLength < that.max_text_width - 30 - 5) {
        that.tree_node_group.select("#temp").remove();
        return text;
      }
      self.text(text + "...");
      textLength = self.node().getComputedTextLength();
      // console.log("wrap text", text);
      while (textLength > that.max_text_width - 30 - 2 * 5 && text.length > 0) {
        text = text.slice(0, -1);
        self.text(text + "...");
        textLength = self.node().getComputedTextLength();
      }
      that.tree_node_group.select("#temp").remove();
      return text;
    }),
      (that.node_wrap = function(node) {
        let text = node.full_name;
        text = that.wrap(text);
        if (text.length === node.full_name.length) {
          node.name = text;
        } else {
          node.name = text + "...";
        }
      });
    nodes.forEach(that.node_wrap);

    // update view
    that.e_nodes = that.tree_node_group
      .selectAll(".tree-node")
      .data(nodes, (d) => d.id);

    that.e_rest_nodes = that.rest_node_group
      .selectAll(".rest-tree-node")
      .data(rest_nodes, (d) => d.id);

    that.create();
    that.update();
    that.remove();

    // that.tree_node_group
    //     .selectAll(".tree-node").select("text").each(that.wrap); // dirty manner

    that.set_animation_time();
  };

  that.create = function() {
    that.node_create();
    that.rest_node_create();
  };

  that.node_create = function() {
    let dragstarted = function() {
      that.highlight_fixed = true;
      let tag = d3.select(this).node().parentElement;
      tag = d3.select(tag);
      console.log("drag start", tag);
      that.timer = setTimeout(function(){
        that.focus_node_for_edit = tag.data()[0];
        that.tree_node_group
          .append("rect")
          .attr("id", "dragnode")
          .attr("rx", that.layer_height / 12)
          .attr("ry", that.layer_height / 12)
          .attr("x", tag.data()[0].x + that.layer_height / 4)
          .attr("y", tag.data()[0].y + (-that.layer_height * 0.8) / 2)
          .attr("height", that.layer_height * 0.8)
          .attr("width", () => {
            return that.max_text_width;
          })
          .style("pointer-events", "none")
          .style("stroke", "black")
          .style("fill", "white")
          .style("opacity", 0.5);
      }, 1)
    };

    let dragged = function(event) {
      that.editing_state = true;
      // d3.select(this)
      //     .attr("transform",
      //     "translate(" + event.x + ", " + event.y + ")");
      that.tree_node_group
        .select("#dragnode")
        .attr("x", event.x + that.layer_height / 4)
        .attr("y", event.y - that.layer_height * 0.4);
     that.tree_node_group
        .select("#merge-panel-g")
        .attr("transform",  "translate(" + (event.x + that.layer_height / 4 + that.max_text_width  + 5) 
        + ", " + (event.y - that.layer_height * 0.4 + that.layer_height * 0.8) + ")");
    };

    let dragended = function() {
      console.log("drag end");
      that.highlight_fixed = false;
      that.dehighlight();

      if (that.merge_action){
        if (that.merge_action === "join"){
          console.log("join");
        }
        else if (that.merge_action === "absorb"){
          console.log("absorb");
          let focus_node_parent = that.focus_node_for_edit.parent;
          that.focus_node_parent = focus_node_parent;
          let children_ids = focus_node_parent.all_children.map(d => d.id);
          let idx = children_ids.indexOf(that.focus_node_for_edit.id);
          focus_node_parent.all_children.splice(idx, 1);
          that.target_node.all_children.push(that.focus_node_for_edit);
          that.focus_node_for_edit.parent = that.target_node;
          that.focus_node_for_edit.depth = that.focus_node_for_edit.parent.depth + 1;
          console.log(that.focus_node_for_edit.name);
          console.log(that.target_node.name, that.target_node.all_children.map(d => d.name));
          console.log(focus_node_parent.name, focus_node_parent.all_children.map(d => d.name));
          tree_process(that.parent.tree);
          that.set_focus_node([that.focus_node_for_edit]);
        }
        else if (that.merge_action === "collapse"){
          console.log("collapse");
        }
    }

      that.editing_state = false;
      that.tree_node_group.select("#dragnode").remove();
      that.tree_node_group.selectAll("#merge-panel-g").remove();
      that.focus_node_for_edit = null;
      that.target_node = null;
      that.merge_action = null;
    };

    let drag = d3
      .drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended);

    // node circle
    let node_groups = that.e_nodes
      .enter()
      .append("g")
      .attr("class", "tree-node")
      .attr("id", (d) => "id-" + d.id)
      .attr("transform", (d) => "translate(" + d.x + ", " + d.y + ")");
    node_groups
      .transition()
      .duration(that.create_ani)
      .delay(that.remove_ani + that.update_ani)
      .style("opacity", 1);
    node_groups
      .append("title")
      .style("font-size", "0.835vw")
      .text((d) => d.full_name);
    node_groups
      .append("rect")
      .attr("class", "background")
      .attr("rx", that.layer_height / 12)
      .attr("ry", that.layer_height / 12)
      .attr("x", that.layer_height / 4)
      .attr("y", (-that.layer_height * 0.8) / 2)
      .attr("height", that.layer_height * 0.8)
      .attr("width", () => {
        return that.max_text_width;
      })
      // TODO:
      // .style("fill", d => d.selected_flag ? Global.DarkGray : "#EBEBF3")
      .style("fill", "#EBEBF3")
      .style("fill-opacity", 0)
      .call(drag)
      .on("mouseover", (ev) => {
        let element = d3.select(ev.toElement).node().parentElement;
        // element = d3.select(element);
        that.highlight(ev);
        if (that.editing_state) {
          console.log("dragged hover");
          if (!that.target_node){
              that.target_node = d3.select(ev.toElement).data()[0];
          }
          else{
            console.log("treggle ", that.target_node.id, element.id);
              if (that.target_node.id != element.id){
                console.log("treggle differnt target node");
                that.tree_node_group.selectAll("#merge-panel-g").remove();
                that.target_node = d3.select(ev.toElement).data()[0];
              }
          }
          that.PanelWidth = 40;
          that.PanelHeight = 40;
          that.ImageMargin = that.PanelWidth * 0.12;
          that.PanelMargin = 0;
          that.PanelOffset = 1;
          if (that.tree_node_group.selectAll("#merge-panel-g").node()) return;
          let is_leaf = d3.select(ev.toElement).data()[0].all_children.length == 0;
          console.log("is leaf", is_leaf);
          let panel = that.tree_node_group
            .append("g")
            .attr("id", "merge-panel-g")
            .attr("transform",  "translate(" + 0 + ", " + 0 + ")");
          panel
            .append("text")
            .attr("x", 0)
            .attr("y", 0)
            .attr("text-anchor", "start")
            .attr("font-size", "18px")
            .text("move to")
            .attr("display",  is_leaf ? "none":"block");
          panel.append("image")
            .attr("x", 0)
            .attr("y", -10)
            .attr("width", 10)
            .attr("xlink:href", that.parent.server_url + "/icon/" + "forbid")
            .attr("display", is_leaf ? "block":"none");
          if (!is_leaf){
              that.merge_action = "absorb";
          }
          else{
              that.merge_action = null;
          }
        
        }
      })
      .on("mouseout", () => {
        // that.hideTooltip();
        that.dehighlight();
        if (that.editing_state){
          console.log("that.merge_action", that.merge_action);
          setTimeout(() =>{
            if (!that.merge_action) {
                that.tree_node_group.select("#merge-panel-g").remove();
                that.target_node = null;
            }
          }, 2);
        }
      })
      .on("click", (ev, d) => {
        console.log("on click tree node");
        // that.change_selected_flag(d, !d.selected_flag);
        if (d.all_children.length && d.all_children.length > 0){
          that.set_focus_node([d]);
          return;
        }
        // TODO: double click
        let node = {
          full_name: d.full_name,
          id: d.id,
        };
        that.set_selected_node(node);

        d3.select(".loading-wordcloud-svg")
          .transition()
          .duration(1)
          .style("opacity", 1);
        Global.disable_global_interaction();
        that.fetch_word();
        that.fetch_image();
      })
      .transition()
      .duration(that.create_ani)
      .delay(that.remove_ani + that.update_ani)
      .style("fill-opacity", 1);

    // precision and recall
    let bars = node_groups
      .append("g")
      .attr("class", "node-bars")
      .style("pointer-events", "none")
      .attr(
        "transform",
        () =>
          "translate(" +
          (that.max_text_width - 15) +
          ", " +
          -(that.bar_height - that.rounded_r) / 2 +
          ")"
      );
    let individual_bar = bars
      .selectAll("g.bar")
      .data((d) => {
        let precision = {
          value: d.data.precision,
          color: "#c982ce",
          type: "precision",
        };
        let recall = {
          value: d.data.recall,
          color: "#4fa7ff",
          type: "recall",
        };
        return [precision, recall];
      })
      .enter()
      .append("g")
      .attr("class", "bar")
      .attr(
        "transform",
        (_, i) => "translate(" + i * (that.bar_width + 3) + ", " + 0 + ")"
      );
    individual_bar
      .append("rect")
      .attr("class", "bar-background")
      .attr("rx", that.rounded_r)
      .attr("ry", that.rounded_r)
      .attr("width", that.bar_width)
      .attr("height", that.bar_height)
      .style("fill", "none")
      .style("stroke", (d) => d.color)
      .style("stroke-width", 1);

    individual_bar
      .append("rect")
      .attr("class", (d) => "bar-value bar-" + d.type)
      .attr("rx", that.rounded_r)
      .attr("ry", that.rounded_r)
      .attr("width", that.bar_width)
      .attr("height", that.bar_height)
      .style("fill", (d) => d.color)
      .style("clip-path", (d) => {
        return `inset(${(1 - d.value) *
          that.bar_height}px ${0}px ${0}px ${0}px)`;
      });
    bars
      .style("opacity", 0)
      .transition()
      .duration(that.create_ani)
      .delay(that.remove_ani + that.update_ani)
      .style("opacity", 1);

    // edit icon
    node_groups
      .append("rect")
      .attr("class", "edit-icon")
      .attr("width", "15px")
      .attr("height", "15px")
      .attr("fill", "url(#edit-svg-icon)")
      .attr("x", that.layer_height / 4 + that.max_text_width - 1)
      .attr("y", (-that.layer_height * 0.8) / 2 - 2)
      .style("opacity", 0)
      .on("click", (ev) => {
        that.textClickHandler(ev);
        ev.stopPropagation();
      })
      .on("mouseover", (ev) => {
        that.highlight(ev);
      })
      .on("mouseout", () => {
        // that.hideTooltip();
        that.dehighlight();
      });

    node_groups
      .append("path")
      .attr("class", (d) => "icon icon-" + d.type)
      .attr("d", function(d) {
        return Global.node_icon(0, 0, d.type);
      })
      .style("fill", Global.GrayColor)
      .style("opacity", 0)
      .transition()
      .duration(that.create_ani)
      .delay(that.update_ani + that.remove_ani)
      .style("opacity", () => (that.expand_tree ? 1 : 0));

    node_groups
      .append("rect")
      .attr("class", (d) => "icon-background icon-bg-" + d.type)
      .attr("x", -8)
      .attr("y", -8)
      .attr("width", 16)
      .attr("height", 16)
      .style("fill", "white")
      .style("opacity", 0)
      .on("mouseover", (ev, d) => {
        that.icon_highlight(ev, d);
      })
      .on("mouseout", (ev, d) => {
        that.icon_dehighlight(ev, d);
      })
      .on("click", (ev, d) => {
        console.log("click icon", d.type, d);
        if (!that.expand_tree) return;
        // if (d.type > 0) return;
        console.log("click tree node", d.name);
        // that.highlight(ev, d, "rgb(211, 211, 229)");
        that.set_focus_node([d]);
      });

    // node name
    node_groups
      .append("text")
      .attr("class", "node-name")
      .text((d) => {
        return d.name;
      })
      .attr("text-anchor", "start")
      .attr("x", that.layer_height / 2 - 2)
      .attr("font-size", "18px")
      .style("fill", Global.DeepGray)
      .attr("dy", ".3em")
      .style("opacity", 0)
      .transition()
      .duration(that.create_ani)
      .delay(that.update_ani + that.remove_ani)
      .style("opacity", 1);
    // node_groups.append("foreignObject")
    //     .attr("x", that.layer_height / 2)
    //     .attr("y", -10)
    //     .attr("width", ( (that.max_text_width - 30) - 2 * 5))
    //     .attr("height", 20)
    //     .append("div")
    //     .append("input")
    //     .attr("value", d => d.name);

    // node link
    node_groups
      .append("path")
      .attr("class", "node-link")
      .attr("d", (d) => {
        return (
          "M" +
          d.link_x +
          ", " +
          d.link_top +
          " L " +
          d.link_x +
          ", " +
          d.link_bottom
        );
      })
      .style("stroke", Global.GrayColor)
      .style("stroke-width", 0.5)
      .style("opacity", 0)
      .transition()
      .duration(that.create_ani)
      .delay(that.update_ani + that.remove_ani)
      .style("opacity", 1);
  };

  that.rest_node_create = function() {
    let node_groups = that.e_rest_nodes
      .enter()
      .append("g")
      .attr("class", "rest-tree-node")
      .attr("id", (d) => "id-" + d.id)
      .attr("transform", (d) => "translate(" + d.x + ", " + d.y + ")")
      .style("opacity", 0)
      .on("click", (ev, d) => {
        console.log("set focus node");
        that.set_focus_node(d.rest_children);
      });
    node_groups
      .append("path")
      .attr("class", "icon")
      .attr("d", Global.node_icon(0, 0, 0))
      .attr("fill", Global.GrayColor);
    node_groups
      .selectAll("rest-node-rect")
      .data((d) => {
        console.log("rest node groups data", d);
        return d.rest_children;
      })
      .enter()
      .append("rect")
      .attr("class", "rest-node-rect")
      .attr("rx", that.layer_height / 12)
      .attr("ry", that.layer_height / 12)
      .attr("x", (d) => that.layer_height / 4 + d.x_delta)
      .attr("y", (d) => d.y_delta)
      .attr("height", that.layer_height * 0.6)
      .attr("width", () => {
        return that.max_text_width;
      })
      .style("fill", (d) =>
        d.last_rest_children ? "#EBEBF3" : "rgb(211, 211, 229)"
      )
      .style("stroke", "white")
      .style("stroke-width", 1);
    node_groups
      .transition()
      .duration((d) => {
        if (d.prev_vis) {
          // return that.remove_ani * 0.5;
          return that.create_ani;
        } else {
          return that.create_ani;
        }
      })
      .delay((d) => {
        if (d.prev_vis) {
          // return that.remove_ani * 0.5 + that.remove_ani;
          return that.remove_ani + that.update_ani;
        } else {
          return that.remove_ani + that.update_ani;
        }
      })
      .style("opacity", 1);
  };

  that.update = function() {
    that.node_update();
    that.rest_node_update();
  };

  that.node_update = function() {
    that.tree_node_group
      .transition()
      .duration(that.update_ani)
      .delay(that.remove_ani)
      .attr("transform", () => {
        return (
          "translate(" +
          that.tree_node_group_x +
          ", " +
          that.tree_node_group_y +
          ")"
        );
      });
    that.e_nodes
      .transition()
      .duration(that.update_ani)
      .delay(that.remove_ani)
      .attr("transform", (d) => "translate(" + d.x + ", " + d.y + ")");
    that.e_nodes
      .select("rect.background")
      .transition()
      .duration(that.update_ani)
      .delay(that.remove_ani)
      .attr("x", that.layer_height / 4)
      .attr("y", (-that.layer_height * 0.8) / 2)
      .attr("height", that.layer_height * 0.8)
      .attr("width", () => {
        // return Global.getTextWidth(d.data.name, "16px Roboto, sans-serif") + that.layer_height / 2;
        return that.max_text_width;
      })
      .style("fill", (d) => (d.selected_flag ? Global.DarkGray : "#EBEBF3"));

    if (that.focus_node) {
      that.tree_node_group
        .select("#id-" + that.focus_node[0].id)
        .select("rect.background")
        .transition()
        .duration(that.create_ani)
        .delay(that.remove_ani + that.update_ani + that.create_ani)
        .style("fill", "#EBEBF3");
    }

    that.e_nodes
      .select("path.icon")
      .transition()
      .duration(that.update_ani)
      .delay(that.remove_ani)
      .attr("d", function(d) {
        return Global.node_icon(0, 0, d.type);
      })
      .style("opacity", () => (that.expand_tree ? 1 : 0))
      .style("fill", Global.GrayColor);
    that.e_nodes
      .select("rect.icon-background")
      .attr("class", (d) => "icon-background icon-bg-" + d.type);

    // that.e_nodes
    //     .select("text")
    //     .transition()
    //     .duration(that.update_ani)
    //     .delay(that.remove_ani)
    //     .text((d) => d.name)
    //     .style("opacity", 1);

    that.e_nodes
      .select("path.node-link")
      .transition()
      .duration(that.update_ani)
      .delay(that.remove_ani)
      .attr("d", (d) => {
        return (
          "M" +
          d.link_x +
          ", " +
          d.link_top +
          " L " +
          d.link_x +
          ", " +
          d.link_bottom
        );
      });

    that.e_nodes
      .select("g.node-bars")
      .transition()
      .duration(that.update_ani)
      .delay(that.remove_ani)
      .style("pointer-event", "none")
      .attr(
        "transform",
        () =>
          "translate(" +
          (that.max_text_width - 15) +
          ", " +
          -(that.bar_height - that.rounded_r) / 2 +
          ")"
      );
    that.e_nodes
      .select("g.node-bars")
      .selectAll("g.bar")
      .data((d) => {
        let precision = {
          value: d.data.precision,
          color: "#c982ce",
          type: "precision",
        };
        let recall = {
          value: d.data.recall,
          color: "#4fa7ff",
          type: "recall",
        };
        return [precision, recall];
      })
      .attr(
        "transform",
        (_, i) => "translate(" + i * (that.bar_width + 3) + ", " + 0 + ")"
      )
      .selectAll(".bar-value")
      .style("clip-path", (d) => {
        return `inset(${(1 - d.value) *
          that.bar_height}px ${0}px ${0}px ${0}px)`;
      });
  };

  that.rest_node_update = function() {
    that.rest_node_group
      .transition()
      .duration(that.update_ani)
      .delay(that.remove_ani)
      .attr("transform", () => {
        let x = that.layer_height / 2;
        if (!that.expand_tree) {
          x = 0;
        }
        return (
          "translate(" +
          x +
          ", " +
          (that.text_height + that.layer_height / 2) +
          ")"
        );
      });
    that.e_rest_nodes
      .transition()
      .duration(that.update_ani)
      .delay(that.remove_ani)
      .attr("transform", (d) => "translate(" + d.x + ", " + d.y + ")");
    that.e_rest_nodes
      .selectAll("rest-node-rect")
      .attr("y", (d, i) => (-that.layer_height * 0.8) / 2 + i * 10)
      .attr("width", () => {
        return that.max_text_width;
      });
  };

  that.remove = function() {
    that.node_remove();
    that.rest_node_remove();
  };

  that.node_remove = function() {
    that.e_nodes.exit().attr("", (d) => {
      let [type, translate] = exit_type(d);
      d.exit_type = type;
      d.translate = translate;
      d.exit_duration = d.exit_type > 1 ? that.update_ani : that.remove_ani;
      d.exit_delay = d.exit_type > 1 ? that.remove_ani : 0;
    });
    that.e_nodes
      .exit()
      .transition()
      .duration((d) => d.exit_duration)
      .delay((d) => d.exit_delay)
      .attr("transform", (d) => d.translate + " scale(1, 0)")
      .style("opacity", 0)
      .remove();
  };

  that.rest_node_remove = function() {
    that.e_rest_nodes.exit().attr("", (d) => {
      let [type, translate] = exit_type(d.parent);
      d.exit_type = type;
      d.translate = translate;
      d.exit_duration = d.exit_type > 1 ? that.update_ani : that.remove_ani;
      d.exit_delay = d.exit_type > 1 ? that.remove_ani : 0;
      console.log(
        "rest_node_remove",
        d.exit_type,
        d.exit_duration,
        d.exit_delay,
        d.translate
      );
    });
    that.e_rest_nodes
      .exit()
      .transition()
      .duration((d) => d.exit_duration)
      .delay((d) => d.exit_delay)
      .attr("transform", (d) => d.translate + " scale(1, 0)")
      .style("opacity", 0)
      .remove();
  };

  // that.highlight = function(ev, d, color) {
  that.highlight = function(ev, color) {
    // console.log("highlight in tree");
    if (that.highlight_fixed) return;
    let self = d3.select(ev.target.parentElement);
    let d = self.data()[0];
    self.selectAll(".edit-icon").style("opacity", 1);
    // color = color || "rgb(219, 219, 233)";
    color = color || "#ffa953";
    that.tree_node_group
      .select("#id-" + d.id)
      .select("rect.background")
      // .style("fill", color);
      .style('stroke', color)
      .style("stroke-width", 2);
    if (Global.linked_highlight) that.connection_highlight(d);
  };

  // that.dehighlight = function(ev, d) {
  that.dehighlight = function() {
    if (that.highlight_fixed) return;
    // let self = d3.select(ev.target.parentElement);
    // let d = self.data()[0];
    that.tree_node_group
      .selectAll(".tree-node")
      .selectAll(".edit-icon")
      .style("opacity", 0);
    that.tree_node_group
      .selectAll(".tree-node")
      .select("rect.background")
      // .style("fill", "#EBEBF3");
      .style("stroke", "");
    for (let i = 0; i < that.parent.selected_node.node_ids.length; i++){
      let id = that.parent.selected_node.node_ids[i];
      d3.selectAll(`#id-${id}`)
          .selectAll(".background").style('stroke', "#ffa953")
          .style("stroke-width", 2);
    }
    if (Global.linked_highlight) that.connection_dehilight();
  };

  that.connection_highlight = function(node) {
    that.parent.connection_view.highlight_by_node(node);
  };

  that.connection_dehilight = function() {
    that.parent.connection_view.dehighlight();
  };

  that.icon_highlight = function(ev, d) {
    console.log("icon-highlight");
    // if (d.type > 0) return;
    that.tree_node_group
      .select("#id-" + d.id)
      .select("path.icon")
      .style("fill", "#1f1f1f");
  };

  that.icon_dehighlight = function(ev, d) {
    that.tree_node_group
      .select("#id-" + d.id)
      .select("path.icon")
      .style("fill", Global.GrayColor);
  };

  that.clean = function() {
    that.tree_node_group.selectAll(".tree-node").remove();
    that.rest_node_group.selectAll(".rest-tree-node").remove();
    // clean selected state
    for (let i = 0; i < that.parent.selected_node.node_ids.length; i++) {
      let tmp_node = {
        full_name: that.parent.selected_node.nodes[i].full_name,
        id: that.parent.selected_node.node_ids[i],
      };
      that.set_selected_node(tmp_node);
    }
  };
};

export default TextTree;

back to top

Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API