HTML preprocessors can make writing HTML more powerful or convenient. For instance, Markdown is designed to be easier to write and read for text documents and you could write a loop in Pug.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. So you don't have access to higher-up elements like the <html>
tag. If you want to add classes there that can affect the whole document, this is the place to do it.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. If you need things in the <head>
of the document, put that code here.
The resource you are linking to is using the 'http' protocol, which may not work when the browser is using https.
CSS preprocessors help make authoring CSS easier. All of them offer things like variables and mixins to provide convenient abstractions.
It's a common practice to apply CSS to a page that styles elements such that they are consistent across all browsers. We offer two of the most popular choices: normalize.css and a reset. Or, choose Neither and nothing will be applied.
To get the best cross-browser support, it is a common practice to apply vendor prefixes to CSS properties and values that require them to work. For instance -webkit-
or -moz-
.
We offer two popular choices: Autoprefixer (which processes your CSS server-side) and -prefix-free (which applies prefixes via a script, client-side).
Any URL's added here will be added as <link>
s in order, and before the CSS in the editor. You can use the CSS from another Pen by using it's URL and the proper URL extention.
You can apply CSS to your Pen from any stylesheet on the web. Just put a URL to it here and we'll apply it, in the order you have them, before the CSS in the Pen itself.
You can also link to another Pen here (use the .css
URL Extension) and we'll pull the CSS from that Pen and include it. If it's using a matching preprocessor, use the appropriate URL Extension and we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
JavaScript preprocessors can help make authoring JavaScript easier and more convenient.
Babel includes JSX processing.
Any URL's added here will be added as <script>
s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.
You can apply a script from anywhere on the web to your Pen. Just put a URL to it here and we'll add it, in the order you have them, before the JavaScript in the Pen itself.
If the script you link to has the file extension of a preprocessor, we'll attempt to process it before applying.
You can also link to another Pen here, and we'll pull the JavaScript from that Pen and include it. If it's using a matching preprocessor, we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
Search for and use JavaScript packages from npm here. By selecting a package, an import
statement will be added to the top of the JavaScript editor for this package.
Using packages here is powered by Skypack, which makes packages from npm not only available on a CDN, but prepares them for native JavaScript ES6 import
usage.
All packages are different, so refer to their docs for how they work.
If you're using React / ReactDOM, make sure to turn on Babel for the JSX processing.
If active, Pens will autosave every 30 seconds after being saved once.
If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.
If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.
Visit your global Editor Settings.
<html>
<head>
<title>GoJS Example</title>
<script src="https://gojs.net/latest/release/go.js"></script>
</head>
<body>
<div id="pedigree-container"></div>
</body>
</html>
'use strict';
var myDiagram;
var $ = go.GraphObject.make;
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) {
// 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 + 30 + spouseB.actualBounds.width;
vertex.height = Math.max(spouseA.actualBounds.height, spouseB.actualBounds.height);
vertex.focus = new go.Point(spouseA.actualBounds.width + 30 / 2, vertex.height / 2);
} else {
var anymarriage = false;
var mit = node.linksConnected;
while (mit.next()) {
var link = mit.value;
if (link.isLabeledLink) { // assume a marriage Link has a label Node
anymarriage = true;
break;
}
}
// don't add a vertex for any married person!
// instead, code above adds label node for marriage link
if (!anymarriage) {
var vertex = net.addNode(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
var mit = link.toNode.linksConnected;
while (mit.next()) {
var l = mit.value;
if (l.data.category !== "Marriage") continue;
// 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);
}
}
}
}
}
};
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 = [];
for (var it = this.network.vertexes.iterator; it.next();) {
var v = it.value;
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)
for (it = this.network.vertexes.iterator; it.next();) {
var v = it.value;
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
var it = this.network.vertexes.iterator;
while (it.next()) {
var v = it.value;
if (v.node === null) continue;
if (v.node.isLinkLabel) continue;
v.node.position = new go.Point(v.x, v.y);
}
// position the spouses of each marriage vertex
it.reset();
while (it.next()) {
var v = it.value;
if (v.node === null) continue;
if (!v.node.isLinkLabel) continue;
var labnode = v.node;
var lablink = labnode.labeledLink;
var spouseA = lablink.fromNode;
var spouseB = lablink.toNode;
// see if the parents are on the desired sides, to avoid a link crossing
var aParentsNode = this.findParentsMarriageLabelNode(spouseA);
var bParentsNode = this.findParentsMarriageLabelNode(spouseB);
if (aParentsNode !== null && bParentsNode !== null && aParentsNode.position.x > bParentsNode.position.x) {
// swap the spouses
spouseA = lablink.toNode;
spouseB = lablink.fromNode;
}
spouseA.position = new go.Point(v.x, v.y);
spouseB.position = new go.Point(v.x + spouseA.actualBounds.width + 30, 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;
}
}
};
GenogramLayout.prototype.findParentsMarriageLabelNode = function(node) {
var it = node.findNodesInto();
while (it.next()) {
var n = it.value;
if (n.isLinkLabel) return n;
}
return null;
};
function goBuildPedigree(pedigreeData, noDisplayNodes, selectedPersonId) {
if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this
myDiagram =
$(go.Diagram, "myDiagram", {
initialAutoScale: go.Diagram.Uniform,
initialContentAlignment: go.Spot.Center,
// when a node is selected, draw a big yellow circle behind it
nodeSelectionAdornmentTemplate: $(go.Adornment, "Auto", {
layerName: "Grid"
}, // the predefined layer that is behind everything else
$(go.Shape, "RoundedRectangle", {
fill: "transparent",
stroke: '#009ADD',
strokeWidth: 2,
strokeDashArray: [5, 5]
}),
$(go.Placeholder)
)
});
function attrFill(a) {
switch (a) {
case "Breast":
return "#FF99FF";
case "Cervical":
return "#226F86";
case "Triple Negative Breast":
return "#FF99FF";
case "Colon":
return "#333300";
case "Melanoma":
return "#000000";
case "Uterine":
return "#FFCCCC";
case "Endometrial":
return "#FFCCCC";
case "Biliary Tract":
return "#0000CC";
case "Bladder":
return "#FFFF33";
case "Brain":
return "#999999";
case "Esophogus":
return "transparent";
case "Gastric":
return "#6666FF";
case "Kidney":
return "#FF9900";
case "Leukemia":
return "#E48241";
case "Liver and Bile Duct":
return "#1D8141";
case "Non-Hodgkin Lymphoma":
return "transparent";
case "Oral Cavity and Pharynx":
return "transparent";
case "Other":
return "#FFFF99";
case "Ovarian":
return "#33FFFF";
case "Pancreatic":
return "#9900CC";
case "Prostate":
return "#99CCFF";
case "Rectal":
return "#660000";
case "Lung":
return "#FFFFFF";
case "Small Bowel":
return "#660000";
case "Stomach":
return "#6666FF";
case "Thyroid":
return "transparent";
case "Ureter\/Renal Pelvis":
return "#FFFF33";
case "Urinary Tract":
return "#FFFF33";
case "Skin":
return "#0033FF";
case "Skin cancer - non-melanoma":
return "#FFFFFF";
case "Hodgkin's Lymphoma":
return "#FFFFFF";
default:
return "transparent";
}
}
var tlpt = new go.Point(1, 1);
var trpt = new go.Point(20, 1);
var brpt = new go.Point(20, 20);
var blpt = new go.Point(1, 20);
function malePosition(a) {
switch (a) {
case "Breast":
return tlpt;
case "Triple Negative Breast":
return tlpt;
case "Cervical":
return tlpt;
case "Colon":
return trpt;
case "Melanoma":
return brpt;
case "Uterine":
return blpt;
case "Endometrial":
return blpt;
case "Biliary Tract":
return trpt;
case "Non-Hodgkin Lymphoma":
return trpt;
case "Oral Cavity and Pharynx":
return trpt;
case "Lung":
return trpt;
case "Bladder":
return brpt;
case "Thyroid":
return brpt;
case "Brain":
return trpt;
case "Gastric":
return trpt;
case "Kidney":
return trpt;
case "Leukemia":
return blpt;
case "Liver and Bile Duct":
return trpt;
case "Other":
return brpt;
case "Ovarian":
return trpt;
case "Pancreatic":
return blpt;
case "Prostate":
return blpt;
case "Rectal":
return trpt;
case "Small Bowel":
return trpt;
case "Stomach":
return brpt;
case "Ureter\/Renal Pelvis":
return trpt;
case "Urinary Tract":
return trpt;
case "Skin":
return brpt;
case "Skin cancer - non-melanoma":
return brpt;
case "Hodgkin's Lymphoma":
return brpt;
default:
tbrpt;
}
}
var tlarc = go.Geometry.parse("M20 20 B 180 90 20 20 19 19 z", true);
var trarc = go.Geometry.parse("M20 20 B 270 90 20 20 19 19 z", true);
var brarc = go.Geometry.parse("M20 20 B 0 90 20 20 19 19 z", true);
var blarc = go.Geometry.parse("M20 20 B 90 90 20 20 19 19 z", true);
function femaleGeometry(a) {
switch (a) {
case "Breast":
return tlarc;
case "Triple Negative Breast":
return tlarc;
case "Colon":
return trarc;
case "Cervical":
return tlarc;
case "Non-Hodgkin Lymphoma":
return trarc;
case "Oral Cavity and Pharynx":
return trarc;
case "Melanoma":
return brarc;
case "Uterine":
return blarc;
case "Thyroid":
return blarc;
case "Endometrial":
return blarc;
case "Biliary Tract":
return trarc;
case "Bladder":
return brarc;
case "Brain":
return trarc;
case "Lung":
return trarc;
case "Gastric":
return trarc;
case "Kidney":
return trarc;
case "Leukemia":
return blarc;
case "Liver and Bile Duct":
return trarc;
case "Other":
return brarc;
case "Ovarian":
return trarc;
case "Pancreatic":
return blarc;
case "Prostate":
return blarc;
case "Rectal":
return trarc;
case "Small Bowel":
return trarc;
case "Stomach":
return brarc;
case "Ureter\/Renal Pelvis":
return trarc;
case "Urinary Tract":
return trarc;
case "Skin":
return brarc;
case "Skin cancer - non-melanoma":
return brarc;
case "Hodgkin's Lymphoma":
return brarc;
default:
tbrpt;
}
}
//lets create our decease shapes
// 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", {
locationSpot: go.Spot.Center,
locationObjectName: "ICON",
movable:false
},
$(go.Panel, {
name: "ICON"
},
$(go.Shape, "Rectangle", {
width: 40,
height: 40,
strokeWidth: 2,
fill: "white",
portId: ""
}),
$(go.Panel, {
itemTemplate: $(go.Panel,
$(go.Shape, "Rectangle", {
width: 19,
height: 19,
stroke: null,
strokeWidth: 0
},
new go.Binding("fill", "", attrFill),
new go.Binding("position", "", malePosition))
),
margin: 1
},
new go.Binding("itemArray", "a")
)
),
$(go.TextBlock, {
textAlign: "center",
maxSize: new go.Size(80, NaN)
},
new go.Binding("text", "n"))
));
// two different node templates, one for each sex,
// named by the category value in the node data object
myDiagram.nodeTemplateMap.add("U", // unknown
$(go.Node, "Vertical", {
locationSpot: go.Spot.Center,
locationObjectName: "ICON",
movable:false
},
$(go.Panel, {
name: "ICON"
},
$(go.Shape, "Triangle", {
width: 40,
height: 40,
strokeWidth: 2,
fill: "white",
portId: ""
}),
$(go.Panel, {
itemTemplate: $(go.Panel,
$(go.Shape, "Rectangle", {
width: 19,
height: 19,
stroke: null,
strokeWidth: 0
},
new go.Binding("fill", "", attrFill),
new go.Binding("position", "", malePosition))
),
margin: 1
},
new go.Binding("itemArray", "a")
)
),
$(go.TextBlock, {
textAlign: "center",
maxSize: new go.Size(80, NaN)
},
new go.Binding("text", "n"))
));
myDiagram.nodeTemplateMap.add("MD", // male
$(go.Node, "Vertical", {
locationSpot: go.Spot.Center,
locationObjectName: "ICON",
movable:false
},
$(go.Panel, {
name: "ICON"
},
$(go.Shape, "Rectangle", {
width: 40,
height: 40,
strokeWidth: 2,
fill: "white",
portId: ""
}),
$(go.Shape, "Line2", {
width: 40,
height: 40,
stroke: "red",
strokeWidth: 2
}),
$(go.Panel, {
itemTemplate: $(go.Panel,
$(go.Shape, "Rectangle", {
width: 19,
height: 19,
stroke: null,
strokeWidth: 0
},
new go.Binding("fill", "", attrFill),
new go.Binding("position", "", malePosition)
)
),
margin: 1
},
new go.Binding("itemArray", "a")
)
),
$(go.TextBlock, {
textAlign: "center",
maxSize: new go.Size(80, NaN)
},
new go.Binding("text", "n"))
));
myDiagram.nodeTemplateMap.add("F", // female
$(go.Node, "Vertical", {
locationSpot: go.Spot.Center,
locationObjectName: "ICON",
movable:false
},
$(go.Panel, {
name: "ICON"
},
$(go.Shape, "Ellipse", {
width: 40,
height: 40,
strokeWidth: 2,
fill: "white",
portId: ""
}),
$(go.Panel, {
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", "a")
)
),
$(go.TextBlock, {
textAlign: "center",
maxSize: new go.Size(80, NaN)
},
new go.Binding("text", "n"))
));
myDiagram.nodeTemplateMap.add("FD", // female
$(go.Node, "Vertical", {
locationSpot: go.Spot.Center,
locationObjectName: "ICON",
movable:false
},
$(go.Panel, {
name: "ICON"
},
$(go.Shape, "Ellipse", {
width: 40,
height: 40,
strokeWidth: 2,
fill: "white",
portId: ""
}),
$(go.Shape, "Line2", {
width: 40,
height: 40,
stroke: "red",
strokeWidth: 2
}),
$(go.Panel, {
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", "a")
)
),
$(go.TextBlock, {
textAlign: "center",
maxSize: new go.Size(80, NaN)
},
new go.Binding("text", "n"))
));
// the representation of each label node -- nothing shows on a Marriage Link
myDiagram.nodeTemplateMap.add("LinkLabel",
$(go.Node, {
selectable: false,
width: 1,
height: 1,
fromEndSegmentLength: 20
}));
myDiagram.linkTemplateMap.add("Marriage", // for marriage relationships
$(go.Link, {
selectable: false
},
$(go.Shape, {
strokeWidth: 2,
stroke: "black"
})
));
myDiagram.linkTemplate = // for parent-child relationships
$(go.Link, {
routing: go.Link.Orthogonal,
layerName: "Background",
curviness : 10,
selectable: false,
fromSpot: go.Spot.Bottom,
toSpot: go.Spot.Top
},
$(go.Shape, {strokeWidth : 2},
new go.Binding("strokeDashArray", "dash")
)
);
myDiagram.layout =
$(GenogramLayout, {
direction: 90,
layerSpacing: 30,
columnSpacing: 10
});
// n: name, s: sex, m: mother, f: father, ux: wife, vir: husband, a: attributes/markers
setupDiagram(myDiagram, pedigreeData, noDisplayNodes, selectedPersonId /* focus on this person */ );
return myDiagram;
}
// create and initialize the Diagram.model given an array of node data representing people
function setupDiagram(diagram, array, focusIds, selectedNodeId) {
diagram.model =
go.GraphObject.make(go.GraphLinksModel, { // declare support for link label nodes
linkLabelKeysProperty: "labelKeys",
// this property determines which template is used
nodeCategoryProperty: "s",
// create all of the nodes for people
nodeDataArray: array
});
setupMarriages(diagram);
setupParents(diagram);
for (var i in focusIds) {
var node = diagram.findNodeForKey(focusIds[i]);
if (node !== null) {
diagram.select(node);
var it = node.linksConnected;
while (it.next()) {
var l = it.value;
if (!l.isLabeledLink) continue;
l.opacity = 0;
var spouse = l.getOtherNode(node);
spouse.opacity = 0;
spouse.pickable = false;
}
}
}
var node = diagram.findNodeForKey(selectedNodeId);
diagram.select(node);
}
function findMarriage(diagram, a, b) {
var nodeA = diagram.findNodeForKey(a);
var nodeB = diagram.findNodeForKey(b);
if (nodeA !== null && nodeB !== null) {
var it = nodeA.findLinksBetween(nodeB); // in either direction
while (it.next()) {
var link = it.value;
// Link.data.category === "Marriage" means it's a marriage relationship
if (link.data !== null && link.data.category === "Marriage") return link;
}
}
return null;
}
// now process the node data to determine marriages
function setupMarriages(diagram) {
var model = diagram.model;
var nodeDataArray = model.nodeDataArray;
for (var i = 0; i < nodeDataArray.length; i++) {
var data = nodeDataArray[i];
var key = data.key;
var uxs = data.ux;
if (uxs !== undefined) {
if (typeof uxs === "number") uxs = [uxs];
for (var j = 0; j < uxs.length; j++) {
var wife = uxs[j];
if (key === wife) {
// or warn no reflexive marriages
continue;
}
var link = findMarriage(diagram, key, wife);
if (link === null) {
// add a label node for the marriage link
var mlab = {
s: "LinkLabel"
};
model.addNodeData(mlab);
// add the marriage link itself, also referring to the label node
var mdata = {
from: key,
to: wife,
labelKeys: [mlab.key],
category: "Marriage"
};
model.addLinkData(mdata);
}
}
}
var virs = data.vir;
if (virs !== undefined) {
if (typeof virs === "number") virs = [virs];
for (var j = 0; j < virs.length; j++) {
var husband = virs[j];
if (key === husband) {
// or warn no reflexive marriages
continue;
}
var link = findMarriage(diagram, key, husband);
if (link === null) {
// add a label node for the marriage link
var mlab = {
s: "LinkLabel"
};
model.addNodeData(mlab);
// add the marriage link itself, also referring to the label node
var mdata = {
from: key,
to: husband,
labelKeys: [mlab.key],
category: "Marriage"
};
model.addLinkData(mdata);
}
}
}
}
}
// process parent-child relationships once all marriages are known
function setupParents(diagram) {
var model = diagram.model;
var nodeDataArray = model.nodeDataArray;
for (var i = 0; i < nodeDataArray.length; i++) {
var data = nodeDataArray[i];
var key = data.key;
var mother = data.m;
var father = data.f;
if (mother !== undefined && father !== undefined) {
var link = findMarriage(diagram, mother, father);
if (link === null) {
// or warn no known mother or no known father or no known marriage between them
if (window.console) window.console.log("unknown marriage: " + mother + " & " + father);
continue;
}
var mdata = link.data;
var mlabkey = mdata.labelKeys[0];
var cdata = {
from: mlabkey,
to: key
};
myDiagram.model.addLinkData(cdata);
}
}
}
function GenogramLayout() {
go.LayeredDigraphLayout.call(this);
}
// end GenogramLayout class
function buildPedigree(data, selectedPersonId) {
var formattedPedigree = formatPedigree(data);
var diagram = goBuildPedigree(formattedPedigree.pedigreeData, formattedPedigree.noDisplay, selectedPersonId);
diagram.addDiagramListener("InitialLayoutCompleted", function() {
var node = diagram.findNodeForKey(1);
diagram.add($(go.Part,
{
position: node.getDocumentPoint(new go.Spot(.05, .25)).offset(-30, 0),
pickable: false, selectable: false
},
$(go.Shape, "Arrow", { width: 30, height: 15, angle: 330 })
));
});
//make an image of the
var img = diagram.makeImage();
addImage(img);
return diagram;
}
function formatPedigree(pedigree) {
var d = [];
//first we must loop through pedigree and determine husbands and wives (ux, vir)
for (var i in pedigree) {
var father_key = parseInt(getPersonByID(pedigree, pedigree[i].father_id));
var mother_key = parseInt(getPersonByID(pedigree, pedigree[i].mother_id));
if (father_key !== -1 && mother_key !== -1) {
pedigree[father_key].ux = parseInt(pedigree[i].mother_id);
pedigree[mother_key].vir = parseInt(pedigree[i].father_id);
// var father = getPersonByProperty(pedigree[i].father_id, "father_id", pedigree);
// father.ux = pedigree[i].mother_id;
}
// if(typeof pedigree[pedigree[i].mother_id + 1] !== "undefined" && pedigree[pedigree[i].father_id + 1].father_id !== 0)
// pedigree[pedigree[i].mother_id + 1].vir = pedigree[i].father_id;
}
var fakePersonCounter = 0;
var probandSpouse = 100000;
var noDisplayNodes = [1];
for (var i in pedigree) {
var pName = pedigree[i].first_name;
if (typeof(pedigree[i].last_name) !== "undefined" && pedigree[i].last_name !== null)
pName += " " + pedigree[i].last_name;
var person = {
key: parseInt(pedigree[i].person_id),
n: pName,
s: pedigree[i].gender,
dash : null,
width : 2
};
if (pedigree[i].is_living === 0 && pedigree[i].person_id !== 1){
person.s += "D";
}
if(pedigree[i].adopted === 'true'){
person.dash = [5,5];
}
//console.log(pedigree[i].relationship_type);
if (pedigree[i].mother_id != 0) {
person.m = parseInt(pedigree[i].mother_id);
} else if (pedigree[i].relationship_type == "SON" || pedigree[i].relationship_type == "DAUGHTER") {
person.m = probandSpouse;
}
if (pedigree[i].father_id != 0)
person.f = parseInt(pedigree[i].father_id);
else if (pedigree[i].relationship_type == "SON" || pedigree[i].relationship_type == "DAUGHTER") {
person.f = probandSpouse;
}
if (typeof pedigree[i].vir !== "undefined")
person.vir = parseInt(pedigree[i].vir);
if (typeof pedigree[i].ux !== "undefined")
person.ux = parseInt(pedigree[i].ux);
person.a = [];
for (var j in pedigree[i].cancer_history) {
person.a.push(pedigree[i].cancer_history[j].name);
if (pedigree[i].cancer_history[j].name)
person.n += "\n" + pedigree[i].cancer_history[j].name;
if (pedigree[i].cancer_history[j].age_of_diagnosis)
person.n += " (" + pedigree[i].cancer_history[j].age_of_diagnosis + ")";
}
if (pedigree[i].person_id >= fakePersonCounter) {
fakePersonCounter = parseInt(pedigree[i].person_id) + 1;
}
//d[parseInt(pedigree[i].person_id) - 1] = person;
d.push(person);
}
//now we need to loop through nieces and nephews to establish correct relationships
for (var i in pedigree) {
var p = pedigree[i];
if (pedigree[i].relationship_type == "NEPHEW" || pedigree[i].relationship_type == "NIECE" || pedigree[i].relationship_type == "MALE_COUSIN" || pedigree[i].relationship_type == "FEMALE_COUSIN") {
//if the persons mother does not exist create the mother and assign relationships
if (p.mother_id == 0) {
fakePersonCounter++;
var person = {
key: parseInt(fakePersonCounter),
n: "",
s: "F",
vir: parseInt(p.father_id)
};
d.push(person);
noDisplayNodes.push(parseInt(p.father_id));
//pedigree[i].mother_id = fakePersonCounter;
//now assign the mother_id and any other siblings
//now we must iterate through the pedigree where the father_id's match
for (var j in d) {
var p2 = d[j];
if (p2.f == p.father_id) {
d[j].m = fakePersonCounter;
}
if (p2.key == p.father_id) {
d[j].ux = fakePersonCounter;
}
}
for (var k in pedigree) {
if (pedigree[k].father_id == pedigree[i].father_id)
pedigree[k].mother_id = fakePersonCounter;
}
}
if (p.father_id == 0) {
//console.log("Fahter ID = " + p.father_id);
fakePersonCounter++;
var person = {
key: parseInt(fakePersonCounter),
n: "",
s: "M",
ux: parseInt(p.mother_id)
}
d.push(person);
noDisplayNodes.push(parseInt(p.mother_id));
//now assign the mother_id and any other siblings
//now we must iterate through the pedigree where the father_id's match
for (var j in d) {
var p2 = d[j];
if (p2.m == p.mother_id) {
d[j].f = fakePersonCounter;
}
if (p2.key == p.mother_id) {
d[j].vir = fakePersonCounter;
}
}
for (var k in pedigree) {
if (pedigree[k].mother_id == pedigree[i].mother_id)
pedigree[k].father_id = fakePersonCounter;
}
}
}
}
d.push({
key: probandSpouse,
n: "",
s: (d[0].s == "M") ? "F" : "M"
});
d[0].ux = probandSpouse;
return {
pedigreeData: d,
noDisplay: noDisplayNodes
};
}
function isInt(n) {
return typeof n === 'number' && n % 1 == 0;
}
function getPersonByID(pedigree, id) {
for (var i in pedigree) {
if (pedigree[i].person_id == id) {
return i; //return the property
}
}
return -1;
}
window.addImage = function(img) {
var obj = document.getElementById("imagePedigree");
img.className = "images";
obj.appendChild(img);
}
var windowObjectReference = null; // global variable
function openRequestedPopup(strUrl, strWindowName) {
if (windowObjectReference == null || windowObjectReference.closed) {
windowObjectReference = window.open(strUrl, strWindowName,
"resizable,scrollbars,status");
} else {
windowObjectReference.focus();
};
}
function getPedigreeData() {
return [{
"person_id": 1,
"father_id": 2,
"mother_id": 3,
"first_name": "Janalee Adams",
"last_name": "",
"gender": "F",
"is_living": "1",
"side": "na",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "proband",
"age": 29,
"dob": "05/30/1984",
"adopted": "true",
"ancestry": {
"id": 3,
"name": "Ashkenazi",
"is_ashkenazi": true
}
}, {
"person_id": 2,
"father_id": 4,
"mother_id": 5,
"first_name": "Father",
"last_name": "",
"gender": "M",
"is_living": "1",
"ashkenazi": "",
"side": "na",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "FATHER",
"ux": 3
}, {
"person_id": 3,
"father_id": 6,
"mother_id": 7,
"first_name": "Mother",
"last_name": "",
"gender": "F",
"is_living": "1",
"ashkenazi": "",
"side": "na",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "MOTHER",
"vir": 2
}, {
"person_id": 4,
"father_id": 0,
"mother_id": 0,
"first_name": "Paternal Grandfather",
"last_name": "",
"gender": "M",
"is_living": "1",
"ashkenazi": "",
"side": "na",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "PATERNAL_GRANDFATHER",
"ux": 5
}, {
"person_id": 5,
"father_id": 0,
"mother_id": 0,
"first_name": "Paternal Grandmother",
"last_name": "",
"gender": "F",
"is_living": "1",
"ashkenazi": "",
"side": "na",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "PATERNAL_GRANDMOTHER",
"vir": 4
}, {
"person_id": 6,
"father_id": 0,
"mother_id": 0,
"first_name": "Maternal Grandfather",
"last_name": "",
"gender": "M",
"is_living": "1",
"ashkenazi": "",
"side": "na",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "MATERNAL_GRANDFATHER",
"ux": 7
}, {
"person_id": 7,
"father_id": 0,
"mother_id": 0,
"first_name": "Maternal Grandmother",
"last_name": "",
"gender": "F",
"is_living": "1",
"ashkenazi": "",
"side": "na",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "MATERNAL_GRANDMOTHER",
"vir": 6
}, {
"person_id": 8,
"father_id": 2,
"mother_id": 3,
"first_name": "Brother 1",
"last_name": "",
"gender": "M",
"is_living": "1",
"ashkenazi": "",
"side": "na",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "BROTHER"
}, {
"person_id": 9,
"father_id": 2,
"mother_id": 3,
"first_name": "Sister 1",
"last_name": "",
"gender": "F",
"is_living": "1",
"ashkenazi": "",
"side": "na",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "SISTER"
}, {
"person_id": 10,
"father_id": 0,
"mother_id": 1,
"first_name": "Son 1",
"last_name": "",
"gender": "M",
"is_living": "1",
"ashkenazi": "",
"side": "na",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "SON"
}, {
"person_id": 11,
"father_id": 0,
"mother_id": 1,
"first_name": "Son 2",
"last_name": "",
"gender": "M",
"is_living": "1",
"ashkenazi": "",
"side": "na",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "SON"
}, {
"person_id": 12,
"father_id": 0,
"mother_id": 1,
"first_name": "Son 3",
"last_name": "",
"gender": "M",
"is_living": "1",
"ashkenazi": "",
"side": "na",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "SON"
}, {
"person_id": 13,
"father_id": 0,
"mother_id": 1,
"first_name": "Daughter 1",
"last_name": "",
"gender": "F",
"is_living": "1",
"ashkenazi": "",
"side": "na",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "DAUGHTER"
}, {
"person_id": 14,
"father_id": 4,
"mother_id": 5,
"first_name": "Uncle 1",
"last_name": "",
"gender": "M",
"is_living": "1",
"ashkenazi": "",
"side": "FATHERS",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "PATERNAL_UNCLE"
}, {
"person_id": 15,
"father_id": 4,
"mother_id": 5,
"first_name": "Uncle 2",
"last_name": "",
"gender": "M",
"is_living": "1",
"ashkenazi": "",
"side": "FATHERS",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "PATERNAL_UNCLE"
}, {
"person_id": 16,
"father_id": 4,
"mother_id": 5,
"first_name": "Uncle 3",
"last_name": "",
"gender": "M",
"is_living": "1",
"ashkenazi": "",
"side": "FATHERS",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "PATERNAL_UNCLE"
}, {
"person_id": 17,
"father_id": 6,
"mother_id": 7,
"first_name": "Uncle 1",
"last_name": "",
"gender": "M",
"is_living": "1",
"ashkenazi": "",
"side": "MOTHERS",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "MATERNAL_UNCLE"
}, {
"person_id": 18,
"father_id": 6,
"mother_id": 7,
"first_name": "Uncle 2",
"last_name": "",
"gender": "M",
"is_living": "1",
"ashkenazi": "",
"side": "MOTHERS",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "MATERNAL_UNCLE"
}, {
"person_id": 19,
"father_id": 6,
"mother_id": 7,
"first_name": "Aunt 1",
"last_name": "",
"gender": "F",
"is_living": "1",
"ashkenazi": "",
"side": "MOTHERS",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "MATERNAL_AUNT"
}, {
"person_id": 20,
"father_id": 6,
"mother_id": 7,
"first_name": "Aunt 2",
"last_name": "",
"gender": "F",
"is_living": "1",
"ashkenazi": "",
"side": "MOTHERS",
"has_cancer": 0,
"has_mutation": 0,
"cancer_history": [],
"gene_mutations": [],
"relationship_type": "MATERNAL_AUNT"
}];
}
function init() {
document.getElementById("pedigree-container").innerHTML = '';
document.getElementById("pedigree-container").innerHTML = '<div id="myDiagram" style="height:400px;"></div>';
var diagram = buildPedigree(getPedigreeData(), 1);
console.log(diagram);
diagram.addDiagramListener("ObjectSingleClicked",
function(e, t) {
var mousePt = $scope.diagram.lastInput.viewPoint;
var part = e.subject.part;
if (!(part instanceof go.Link)) {
$scope.key = e.subject.part.ul.key;
$scope.selectedPersonId = $scope.findIndexWithAttr($scope.pedigree, 'person_id',$scope.key);
$scope.selectedPerson = $scope.pedigree[$scope.selectedPersonId];
$scope.$apply();
}
}
);
diagram.addDiagramListener("ObjectDoubleClicked",
function(e, t) {
var mousePt = $scope.diagram.lastInput.viewPoint;
var part = e.subject.part;
if (!(part instanceof go.Link)) {
$scope.key = e.subject.part.ul.key;
console.log($scope.key);
}
}
);
}
init();
Also see: Tab Triggers