Add node in genogram

i am asking how to add new node or block to the genogram on click and add ancestors or descendants. i worked with flow chart and there is a panel that i can drag and drop to the diagram, how to do so in the genogram.

Derived from an earlier version of the Genogram sample:

<!DOCTYPE html>
<html>
<head>
  <title>Genogram</title>
  <!-- Copyright 1998-2021 by Northwoods Software Corporation. -->
  <meta name="description" content="A genogram is a family tree diagram for visualizing hereditary patterns.">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <script src="go.js"></script>
  <script id="code">
    function init() {
      var $ = go.GraphObject.make;

      myDiagram =
        $(go.Diagram, "myDiagramDiv",
          {
            initialAutoScale: go.Diagram.Uniform,
            "undoManager.isEnabled": true,
            layout:  // use a custom layout, defined below
              $(GenogramLayout,
                { direction: 90, layerSpacing: 30, columnSpacing: 10 })
          });

      // start off with a single person
      var model = new go.GraphLinksModel();
      model.nodeCategoryProperty = "sex";
      model.copiesArrays = true;
      model.linkLabelKeysProperty = "labelKeys";
      model.nodeDataArray = [
        { name: "person", sex: "M", attr: ["A", "G"], loc: "0 0" }
      ];
      myDiagram.model = model;


      // Styling and binding conversion functions
      // Current there is no way for the user to edit genetic attributes --
      // the values in the "attr" Array

      // determine the color for each attribute shape
      function attrFill(a) {
        switch (a) {
          case "A": return "green";
          case "B": return "orange";
          case "C": return "red";
          case "D": return "cyan";
          case "E": return "gold";
          case "F": return "pink";
          case "G": return "blue";
          case "H": return "brown";
          case "I": return "purple";
          case "J": return "chartreuse";
          case "K": return "lightgray";
          case "L": return "magenta";
          case "S": return "red";
          default: return "transparent";
        }
      }

      // determine the geometry for each attribute shape in a male;
      // except for the slash these are all squares at each of the four corners of the overall square
      var tlsq = go.Geometry.parse("F M1 1 l19 0 0 19 -19 0z");
      var trsq = go.Geometry.parse("F M20 1 l19 0 0 19 -19 0z");
      var brsq = go.Geometry.parse("F M20 20 l19 0 0 19 -19 0z");
      var blsq = go.Geometry.parse("F M1 20 l19 0 0 19 -19 0z");
      var slash = go.Geometry.parse("F M38 0 L40 0 40 2 2 40 0 40 0 38z");
      function maleGeometry(a) {
        switch (a) {
          case "A": return tlsq;
          case "B": return tlsq;
          case "C": return tlsq;
          case "D": return trsq;
          case "E": return trsq;
          case "F": return trsq;
          case "G": return brsq;
          case "H": return brsq;
          case "I": return brsq;
          case "J": return blsq;
          case "K": return blsq;
          case "L": return blsq;
          case "S": return slash;
          default: return tlsq;
        }
      }

      // determine the geometry for each attribute shape in a female;
      // except for the slash these are all pie shapes at each of the four quadrants of the overall circle
      var tlarc = go.Geometry.parse("F M20 20 B 180 90 20 20 19 19 z");
      var trarc = go.Geometry.parse("F M20 20 B 270 90 20 20 19 19 z");
      var brarc = go.Geometry.parse("F M20 20 B 0 90 20 20 19 19 z");
      var blarc = go.Geometry.parse("F M20 20 B 90 90 20 20 19 19 z");
      function femaleGeometry(a) {
        switch (a) {
          case "A": return tlarc;
          case "B": return tlarc;
          case "C": return tlarc;
          case "D": return trarc;
          case "E": return trarc;
          case "F": return trarc;
          case "G": return brarc;
          case "H": return brarc;
          case "I": return brarc;
          case "J": return blarc;
          case "K": return blarc;
          case "L": return blarc;
          case "S": return slash;
          default: return tlarc;
        }
      }

      function nodeStyle() {
        return [
          { // some node properties
            locationSpot: go.Spot.Center,
            locationObjectName: "ICON",
            selectionObjectName: "ICON",
            // show some Buttons when a person is selected
            selectionAdornmentTemplate:
              $(go.Adornment, "Spot",
                $(go.Panel, "Auto",
                  $(go.Shape,
                    {
                      fill: "transparent", strokeWidth: 4, stroke: "dodgerblue",
                      spot1: go.Spot.TopLeft, spot2: go.Spot.BottomRight
                    },
                    new go.Binding("figure", "sex", function(s) { return s === "F" ? "Circle" : "Square"; })),
                  $(go.Placeholder, { margin: 2 })
                ),
                $("Button",
                  { alignment: go.Spot.Top, alignmentFocus: go.Spot.Bottom },
                  // only visible when there aren't already parents
                  new go.Binding("visible", "", function(ad) {
                    return !ad.diagram.isReadOnly &&
                          findParentsMarriageLabelNode(ad.adornedPart) === null;
                  }).ofObject(),
                  {
                    click: function(e, button) {
                      var node = button.part.adornedPart;
                      addParentsToPerson(node);
                      node.isSelected = false;
                    }
                  },
                  $(go.TextBlock, "Add\nParents", { textAlign: "center" })),
                $("Button",
                  { alignment: go.Spot.Right, alignmentFocus: go.Spot.Left },
                  // only when changes are allowed
                  new go.Binding("visible", "", function(ad) { return !ad.diagram.isReadOnly; }).ofObject(),
                  {
                    click: function(e, button) {
                      var node = button.part.adornedPart;
                      addSpouseToPerson(node);
                      node.isSelected = false;
                    }
                  },
                  $(go.TextBlock, "Add\nSpouse", { textAlign: "center" })),
                $("Button",
                  { alignment: go.Spot.Left, alignmentFocus: go.Spot.Right },
                  // only visible when unmarried
                  new go.Binding("visible", "", function(ad) {
                    return !ad.diagram.isReadOnly &&
                          findMarriageLinks(ad.adornedPart).count === 0;
                  }).ofObject(),
                  {
                    click: function(e, button) {
                      var node = button.part.adornedPart;
                      node.diagram.commit(function(diag) {
                        node.category = (node.category === "M") ? "F" : "M";
                      }, "changed sex");
                    }
                  },
                  $(go.TextBlock, "Change\nSex", { textAlign: "center" })
                )
              )
          },
          // and a node binding
          new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify)
        ];
      }

      // Templates

      // two different node templates, one for each sex,
      // named by the category value in the node data object
      myDiagram.nodeTemplateMap.add("M",  // male
        $(go.Node, "Vertical", nodeStyle(),
          $(go.Panel,
            { name: "ICON", background: "transparent" },
            $(go.Shape, "Square",
              { width: 40, height: 40, strokeWidth: 2, fill: "white", portId: "" }),
            $(go.Panel,
              { // for each attribute show a Shape at a particular place in the overall square
                itemTemplate:
                  $(go.Panel,
                    $(go.Shape,
                      { stroke: null, strokeWidth: 0 },
                      new go.Binding("fill", "", attrFill),
                      new go.Binding("geometry", "", maleGeometry))
                  ),
                margin: 1
              },
              new go.Binding("itemArray", "attr")
            )
          ),
          $(go.TextBlock,
            { textAlign: "center", maxSize: new go.Size(80, NaN), editable: true },
            new go.Binding("text", "name").makeTwoWay())
        ));

      myDiagram.nodeTemplateMap.add("F",  // female
        $(go.Node, "Vertical", nodeStyle(),
          $(go.Panel,
            { name: "ICON", background: "transparent" },
            $(go.Shape, "Circle",
              { width: 40, height: 40, strokeWidth: 2, fill: "white", portId: "" }),
            $(go.Panel,
              { // for each attribute show a Shape at a particular place in the overall circle
                itemTemplate:
                  $(go.Panel,
                    $(go.Shape,
                      { stroke: null, strokeWidth: 0 },
                      new go.Binding("fill", "", attrFill),
                      new go.Binding("geometry", "", femaleGeometry))
                  ),
                margin: 1
              },
              new go.Binding("itemArray", "attr")
            )
          ),
          $(go.TextBlock,
            { textAlign: "center", maxSize: new go.Size(80, NaN), editable: true },
            new go.Binding("text", "name").makeTwoWay())
        ));

      // the representation of each label node -- nothing shows on a Marriage Link
      myDiagram.nodeTemplateMap.add("LinkLabel",
        $(go.Node,
          { selectable: false, layerName: "Foreground", fromEndSegmentLength: 20 },
          $("Button",
            new go.Binding("visible", "", function(ad) { return !ad.diagram.isReadOnly; }).ofObject(),
            {
              click: function(e, button) {
                var labnode = button.part;
                var child = addChildToMarriage(labnode.labeledLink);
                child.diagram.select(child);
              }
            },
            $(go.TextBlock, "+")
          )));


      myDiagram.linkTemplate =  // for parent-child relationships
        $(go.Link,
          {
            routing: go.Link.Orthogonal, curviness: 15,
            layerName: "Background", selectable: false,
            fromSpot: go.Spot.Bottom, toSpot: go.Spot.Top
          },
          $(go.Shape, { strokeWidth: 1.5 })
        );

      myDiagram.linkTemplateMap.add("Marriage",  // for marriage relationships
        $(go.Link,
          { selectable: false, routing: go.Link.AvoidsNodes },
          $(go.Shape, { strokeWidth: 3, stroke: "blue" })
        ));
    }


    // Various convenient graph navigation functions

    // return the label Node for a marriage which is the Node with which all Links
    // to children Nodes are connected; null if there are no parents
    function findParentsMarriageLabelNode(node) {
      if (node.category === "LinkLabel") throw new Error("node is not a person: " + node.key);
      var it = node.findNodesInto();
      while (it.next()) {
        var n = it.value;
        if (n.category === "LinkLabel") return n;
      }
      return null;
    }

    // return the Marriage Link of a person's parent Nodes, if the person has any parents in the diagram;
    // null if there are no parents
    function findParentsMarriageLink(node) {
      var labnode = findParentsMarriageLabelNode(node);
      return labnode ? labnode.labeledLink : null;
    }

    // return an Array of the given node's two parent Nodes; both will be null if no parents are known
    function findParentNodes(node) {
      if (node.category === "LinkLabel") throw new Error("node is not a person: " + node.key);
      var mlink = findParentsMarriageLink(node);
      var parents = [null, null];
      if (mlink) {
        parents[0] = mlink.fromNode;
        parents[1] = mlink.toNode;
      }
      return parents;
    }

    // return a collection of Marriage Links connected with a person; may be an empty collection
    function findMarriageLinks(node) {
      if (node.category === "LinkLabel") throw new Error("node is not a person: " + node.key);
      return node.linksConnected.filter(function(l) { return l.category === "Marriage"; });
    }

    // return a collection of person Nodes connected by marriage Links
    function findSpouseNodes(node) {
      if (node.category === "LinkLabel") throw new Error("node is not a person: " + node.key);
      return findMarriageLinks(node).map(function(l) { return l.getOtherNode(node); });
    }

    // return the label Nodes of Marriage Links, one per marriage;
    // each can be Link.fromNode of marriage to children Links
    function findMarriageLabelNodes(node) {
      if (node.category === "LinkLabel") throw new Error("node is not a person: " + node.key);
      return findMarriageLinks(node).map(function(l) { return findMarriageLabelNode(l); });
    }

    // return the label Node of a Marriage Link; should not be null if graph is correctly constructed
    function findMarriageLabelNode(link) {
      if (link.category !== "Marriage") throw new Error("link is not a marriage: " + link);
      var labnode = link.labelNodes.first();
      if (!labnode) throw new Error("Marriage Link has no label Node " + link);
      return labnode;
    }

    // return the children of a Marriage Link
    function findMarriageChildrenNodes(link) {
      if (link.category !== "Marriage") throw new Error("link is not a marriage: " + link);
      return findMarriageLabelNode(link).findNodesConnected();
    }


    // Graph modification functions

    function addParentsToPerson(node) {
      if (node.category === "LinkLabel") throw new Error("node is not a person: " + node.key);
      node.diagram.model.commit(function(m) {
        var newfatherdata = {
          name: "father of (" + node.data.name + ")",
          sex: "M",
          attr: ["C", "D"]
        };
        m.addNodeData(newfatherdata);
        var newmotherdata = {
          name: "mother of (" + node.data.name + ")",
          sex: "F",
          attr: ["E", "G"]
        };
        m.addNodeData(newmotherdata);
        var labelnodedata = {
          sex: "LinkLabel",
        };
        m.addNodeData(labelnodedata);
        var marriagedata = {
          category: "Marriage",
          from: m.getKeyForNodeData(newfatherdata),
          to: m.getKeyForNodeData(newmotherdata),
          labelKeys: [m.getKeyForNodeData(labelnodedata)]
        };
        m.addLinkData(marriagedata);
        var childlinkdata = {
          from: m.getKeyForNodeData(labelnodedata),
          to: node.key
        };
        m.addLinkData(childlinkdata);
      }, "added parents");
    }

    function addSpouseToPerson(node) {
      if (node.category === "LinkLabel") throw new Error("node is not a person: " + node.key);
      node.diagram.model.commit(function(m) {
        var newspousedata = {
          name: "spouse of " + node.data.name,
          sex: (node.category === "M") ? "F" : "M",
          attr: ["G", "H"]
        };
        m.addNodeData(newspousedata);
        var labelnodedata = {
          sex: "LinkLabel",
        };
        m.addNodeData(labelnodedata);
        var marriagedata = {
          category: "Marriage",
          from: node.key,
          to: m.getKeyForNodeData(newspousedata),
          labelKeys: [m.getKeyForNodeData(labelnodedata)]
        };
        m.addLinkData(marriagedata);
      }, "added spouse");
    }

    function addChildToMarriage(mlink) {
      if (mlink.category !== "Marriage") throw new Error("link is not a marriage: " + mlink);
      var labnode = findMarriageLabelNode(mlink);

      var father = null;
      var mother = null;
      if (mlink.fromNode.category === "M") mother = mlink.fromNode;
      else father = mlink.fromNode;
      if (mlink.toNode.category === "M") mother = mlink.toNode;
      else father = mlink.toNode;
      if (!father || !mother) throw new Error("new child node for marriage link requires two parents " + mlink);

      var newdata = null;
      mlink.diagram.model.commit(function(m) {
        newdata = {
          name: "child of (" + father.data.name + ") and (" + mother.data.name + ")",
          sex: "M",
          attr: ["I", "J"]
        };
        m.addNodeData(newdata);
        var newlink = {
          from: labnode.key,
          to: m.getKeyForNodeData(newdata)
        };
        m.addLinkData(newlink);
      }, "added child");
      return mlink.diagram.findNodeForData(newdata);
    }


    // A custom layout that shows the two families related to a person's parents
    function GenogramLayout() {
      go.LayeredDigraphLayout.call(this);
      this.initializeOption = go.LayeredDigraphLayout.InitDepthFirstIn;
      this.spouseSpacing = 30;  // minimum space between spouses
    }
    go.Diagram.inherit(GenogramLayout, go.LayeredDigraphLayout);

    GenogramLayout.prototype.makeNetwork = function(coll) {
      // generate LayoutEdges for each parent-child Link
      var net = this.createNetwork();
      if (coll instanceof go.Diagram) {
        this.add(net, coll.nodes, true);
        this.add(net, coll.links, true);
      } else if (coll instanceof go.Group) {
        this.add(net, coll.memberParts, false);
      } else if (coll.iterator) {
        this.add(net, coll.iterator, false);
      }
      return net;
    };

    // internal method for creating LayeredDigraphNetwork where husband/wife pairs are represented
    // by a single LayeredDigraphVertex corresponding to the label Node on the marriage Link
    GenogramLayout.prototype.add = function(net, coll, nonmemberonly) {
      var multiSpousePeople = new go.Set();
      // consider all Nodes in the given collection
      var it = coll.iterator;
      while (it.next()) {
        var node = it.value;
        if (!(node instanceof go.Node)) continue;
        if (!node.isLayoutPositioned || !node.isVisible()) continue;
        if (nonmemberonly && node.containingGroup !== null) continue;
        // if it's an unmarried Node, or if it's a Link Label Node, create a LayoutVertex for it
        if (node.isLinkLabel) {
          // get marriage Link
          var link = node.labeledLink;
          var spouseA = link.fromNode;
          var spouseB = link.toNode;
          // create vertex representing both husband and wife
          var vertex = net.addNode(node);
          // now define the vertex size to be big enough to hold both spouses
          vertex.width = spouseA.actualBounds.width + this.spouseSpacing + spouseB.actualBounds.width;
          vertex.height = Math.max(spouseA.actualBounds.height, spouseB.actualBounds.height);
          vertex.focus = new go.Point(spouseA.actualBounds.width + this.spouseSpacing / 2, vertex.height / 2);
        } else {
          // don't add a vertex for any married person!
          // instead, code above adds label node for marriage link
          // assume a marriage Link has a label Node
          var marriages = 0;
          node.linksConnected.each(function(l) { if (l.isLabeledLink) marriages++; });
          if (marriages === 0) {
            var vertex = net.addNode(node);
          } else if (marriages > 1) {
            multiSpousePeople.add(node);
          }
        }
      }
      // now do all Links
      it.reset();
      while (it.next()) {
        var link = it.value;
        if (!(link instanceof go.Link)) continue;
        if (!link.isLayoutPositioned || !link.isVisible()) continue;
        if (nonmemberonly && link.containingGroup !== null) continue;
        // if it's a parent-child link, add a LayoutEdge for it
        if (!link.isLabeledLink) {
          var parent = net.findVertex(link.fromNode);  // should be a label node
          var child = net.findVertex(link.toNode);
          if (child !== null) {  // an unmarried child
            net.linkVertexes(parent, child, link);
          } else {  // a married child
            link.toNode.linksConnected.each(function(l) {
              if (!l.isLabeledLink) return;  // if it has no label node, it's a parent-child link
              // found the Marriage Link, now get its label Node
              var mlab = l.labelNodes.first();
              // parent-child link should connect with the label node,
              // so the LayoutEdge should connect with the LayoutVertex representing the label node
              var mlabvert = net.findVertex(mlab);
              if (mlabvert !== null) {
                net.linkVertexes(parent, mlabvert, link);
              }
            });
          }
        }
      }

      while (multiSpousePeople.count > 0) {
        // find all collections of people that are indirectly married to each other
        var node = multiSpousePeople.first();
        var cohort = new go.Set();
        this.extendCohort(cohort, node);
        // then encourage them all to be the same generation by connecting them all with a common vertex
        var dummyvert = net.createVertex();
        net.addVertex(dummyvert);
        var marriages = new go.Set();
        cohort.each(function(n) {
          n.linksConnected.each(function(l) {
            marriages.add(l);
          })
        });
        marriages.each(function(link) {
          // find the vertex for the marriage link (i.e. for the label node)
          var mlab = link.labelNodes.first()
          var v = net.findVertex(mlab);
          if (v !== null) {
            net.linkVertexes(dummyvert, v, null);
          }
        });
        // done with these people, now see if there are any other multiple-married people
        multiSpousePeople.removeAll(cohort);
      }
    };

    // collect all of the people indirectly married with a person
    GenogramLayout.prototype.extendCohort = function(coll, node) {
      if (coll.has(node)) return;
      coll.add(node);
      var lay = this;
      node.linksConnected.each(function(l) {
        if (l.isLabeledLink) {  // if it's a marriage link, continue with both spouses
          lay.extendCohort(coll, l.fromNode);
          lay.extendCohort(coll, l.toNode);
        }
      });
    };

    GenogramLayout.prototype.assignLayers = function() {
      go.LayeredDigraphLayout.prototype.assignLayers.call(this);
      var horiz = this.direction == 0.0 || this.direction == 180.0;
      // for every vertex, record the maximum vertex width or height for the vertex's layer
      var maxsizes = [];
      this.network.vertexes.each(function(v) {
        var lay = v.layer;
        var max = maxsizes[lay];
        if (max === undefined) max = 0;
        var sz = (horiz ? v.width : v.height);
        if (sz > max) maxsizes[lay] = sz;
      });
      // now make sure every vertex has the maximum width or height according to which layer it is in,
      // and aligned on the left (if horizontal) or the top (if vertical)
      this.network.vertexes.each(function(v) {
        var lay = v.layer;
        var max = maxsizes[lay];
        if (horiz) {
          v.focus = new go.Point(0, v.height / 2);
          v.width = max;
        } else {
          v.focus = new go.Point(v.width / 2, 0);
          v.height = max;
        }
      });
      // from now on, the LayeredDigraphLayout will think that the Node is bigger than it really is
      // (other than the ones that are the widest or tallest in their respective layer).
    };

    GenogramLayout.prototype.commitNodes = function() {
      go.LayeredDigraphLayout.prototype.commitNodes.call(this);
      // position regular nodes
      this.network.vertexes.each(function(v) {
        if (v.node !== null && !v.node.isLinkLabel) {
          v.node.position = new go.Point(v.x, v.y);
        }
      });
      // position the spouses of each marriage vertex
      var layout = this;
      this.network.vertexes.each(function(v) {
        if (v.node === null) return;
        if (!v.node.isLinkLabel) return;
        var labnode = v.node;
        var lablink = labnode.labeledLink;
        // In case the spouses are not actually moved, we need to have the marriage link
        // position the label node, because LayoutVertex.commit() was called above on these vertexes.
        // Alternatively we could override LayoutVetex.commit to be a no-op for label node vertexes.
        lablink.invalidateRoute();
        var spouseA = lablink.fromNode;
        var spouseB = lablink.toNode;
        // prefer fathers on the left, mothers on the right
        if (spouseA.data.sex === "F") {  // sex is female
          var temp = spouseA;
          spouseA = spouseB;
          spouseB = temp;
        }
        // see if the parents are on the desired sides, to avoid a link crossing
        var aParentsNode = layout.findParentsMarriageLabelNode(spouseA);
        var bParentsNode = layout.findParentsMarriageLabelNode(spouseB);
        if (aParentsNode !== null && bParentsNode !== null && aParentsNode.position.x > bParentsNode.position.x) {
          // swap the spouses
          var temp = spouseA;
          spouseA = spouseB;
          spouseB = temp;
        }
        spouseA.position = new go.Point(v.x, v.y);
        spouseB.position = new go.Point(v.x + spouseA.actualBounds.width + layout.spouseSpacing, v.y);
        if (spouseA.opacity === 0) {
          var pos = new go.Point(v.centerX - spouseA.actualBounds.width / 2, v.y);
          spouseA.position = pos;
          spouseB.position = pos;
        } else if (spouseB.opacity === 0) {
          var pos = new go.Point(v.centerX - spouseB.actualBounds.width / 2, v.y);
          spouseA.position = pos;
          spouseB.position = pos;
        }
      });
      // position only-child nodes to be under the marriage label node
      this.network.vertexes.each(function(v) {
        if (v.node === null || v.node.linksConnected.count > 1) return;
        var mnode = layout.findParentsMarriageLabelNode(v.node);
        if (mnode !== null && mnode.linksConnected.count === 1) {  // if only one child
          var mvert = layout.network.findVertex(mnode);
          var newbnds = v.node.actualBounds.copy();
          newbnds.x = mvert.centerX - v.node.actualBounds.width / 2;
          // see if there's any empty space at the horizontal mid-point in that layer
          var overlaps = layout.diagram.findObjectsIn(newbnds, function(x) { return x.part; }, function(p) { return p !== v.node; }, true);
          if (overlaps.count === 0) {
            v.node.move(newbnds.position);
          }
        }
      });
    };

    GenogramLayout.prototype.findParentsMarriageLabelNode = function(node) {
      var it = node.findNodesInto();
      while (it.next()) {
        var n = it.value;
        if (n.isLinkLabel) return n;
      }
      return null;
    };
    // end GenogramLayout class


    // Show the diagram's model in JSON format that the user may edit
    function save() {
      document.getElementById("mySavedModel").value = myDiagram.model.toJson();
      myDiagram.isModified = false;
    }
    function load() {
      myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
    }
    </script>
</head>
<body onload="init()">
<div id="sample">
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
  <p>A <em>genogram</em> or <em>pedigree chart</em> is an extended family tree diagram that displays information about each person or each relationship.</p>
  <p>
    Each person (Node), when selected, shows buttons to add parents (if the person does not already have parents)
    and to "marry" another person.  In this app, a "marriage" is not a legal relationship but a relationship that
    issued children.
  </p>
  <p>
    There are functions that convert an attribute value into a brush color or Shape geometry,
    to be added to the Node representing the person.
  </p>
  <p>
    A custom <a>LayeredDigraphLayout</a> does the layout, assuming there is a central person whose mother and father
    each have their own ancestors.
    The overridden <b>add</b> function allows husband/wife pairs to be represented by a single <a>LayeredDigraphVertex</a>.
  </p>
  <button id="SaveButton" onclick="save()">Save</button>
  <button onclick="load()">Load</button>
  Diagram Model saved in JSON format:
  <textarea id="mySavedModel" style="width:100%;height:300px">
  </textarea>
</div>
</body>
</html>