forked from NorthwoodsSoftware/GoJS
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtreeMapper.html
232 lines (215 loc) · 9.43 KB
/
treeMapper.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>GoJS Tree Mapper</title>
<meta name="description" content="A tree mapper diagram to show and edit the relationships between items in two different trees." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Copyright 1998-2019 by Northwoods Software Corporation. -->
<script src="../release/go.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<script id="code">
function TreeNode() {
go.Node.call(this);
this.treeExpandedChanged = function(node) {
if (node.containingGroup !== null) {
node.containingGroup.findExternalLinksConnected().each(function(l) { l.invalidateRoute(); });
}
};
}
go.Diagram.inherit(TreeNode, go.Node);
TreeNode.prototype.findVisibleNode = function() {
// redirect links to lowest visible "ancestor" in the tree
var n = this;
while (n !== null && !n.isVisible()) {
n = n.findTreeParentNode();
}
return n;
};
// end TreeNode
function MappingLink() {
go.Link.call(this);
}
go.Diagram.inherit(MappingLink, go.Link);
MappingLink.prototype.getLinkPoint = function(node, port, spot, from, ortho, othernode, otherport) {
var r = port.getDocumentBounds();
var group = node.containingGroup;
var b = (group !== null) ? group.actualBounds : node.actualBounds;
var op = othernode.getDocumentPoint(go.Spot.Center);
var x = (op.x > r.centerX) ? b.right : b.left;
return new go.Point(x, r.centerY);
};
// end MappingLink
function init() {
if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this
var $ = go.GraphObject.make; // for conciseness in defining templates
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
"commandHandler.copiesTree": true,
"commandHandler.deletesTree": true,
// newly drawn links always map a node in one tree to a node in another tree
"linkingTool.archetypeLinkData": { category: "Mapping" },
"linkingTool.linkValidation": checkLink,
"relinkingTool.linkValidation": checkLink,
"undoManager.isEnabled": true,
"ModelChanged": function(e) {
if (e.isTransactionFinished) { // show the model data in the page's TextArea
document.getElementById("mySavedModel").textContent = e.model.toJson();
}
}
});
// All links must go from a node inside the "Left Side" Group to a node inside the "Right Side" Group.
function checkLink(fn, fp, tn, tp, link) {
// make sure the nodes are inside different Groups
if (fn.containingGroup === null || fn.containingGroup.data.key !== -1) return false;
if (tn.containingGroup === null || tn.containingGroup.data.key !== -2) return false;
//// optional limit to a single mapping link per node
//if (fn.linksConnected.any(function(l) { return l.category === "Mapping"; })) return false;
//if (tn.linksConnected.any(function(l) { return l.category === "Mapping"; })) return false;
return true;
}
// Each node in a tree is defined using the default nodeTemplate.
myDiagram.nodeTemplate =
$(TreeNode,
{ movable: false, copyable: false, deletable: false }, // user cannot move an individual node
// no Adornment: instead change panel background color by binding to Node.isSelected
{ selectionAdorned: false },
// whether the user can start drawing a link from or to this node depends on which group it's in
new go.Binding("fromLinkable", "group", function(k) { return k === -1; }),
new go.Binding("toLinkable", "group", function(k) { return k === -2; }),
$("TreeExpanderButton", // support expanding/collapsing subtrees
{
width: 14, height: 14,
"ButtonIcon.stroke": "white",
"ButtonIcon.strokeWidth": 2,
"ButtonBorder.fill": "goldenrod",
"ButtonBorder.stroke": null,
"ButtonBorder.figure": "Rectangle",
"_buttonFillOver": "darkgoldenrod",
"_buttonStrokeOver": null,
"_buttonFillPressed": null
}),
$(go.Panel, "Horizontal",
{ position: new go.Point(16, 0) },
new go.Binding("background", "isSelected", function(s) { return (s ? "lightblue" : "white"); }).ofObject(),
//// optional icon for each tree node
//$(go.Picture,
// { width: 14, height: 14,
// margin: new go.Margin(0, 4, 0, 0),
// imageStretch: go.GraphObject.Uniform,
// source: "images/defaultIcon.png" },
// new go.Binding("source", "src")),
$(go.TextBlock,
new go.Binding("text", "key", function(s) { return "item " + s; }))
) // end Horizontal Panel
); // end Node
// These are the links connecting tree nodes within each group.
myDiagram.linkTemplate = $(go.Link); // without lines
myDiagram.linkTemplate = // with lines
$(go.Link,
{
selectable: false,
routing: go.Link.Orthogonal,
fromEndSegmentLength: 4,
toEndSegmentLength: 4,
fromSpot: new go.Spot(0.001, 1, 7, 0),
toSpot: go.Spot.Left
},
$(go.Shape,
{ stroke: "lightgray" }));
// These are the blue links connecting a tree node on the left side with one on the right side.
myDiagram.linkTemplateMap.add("Mapping",
$(MappingLink,
{ isTreeLink: false, isLayoutPositioned: false, layerName: "Foreground" },
{ fromSpot: go.Spot.Right, toSpot: go.Spot.Left },
{ relinkableFrom: true, relinkableTo: true },
$(go.Shape, { stroke: "blue", strokeWidth: 2 })
));
myDiagram.groupTemplate =
$(go.Group, "Auto",
new go.Binding("position", "xy", go.Point.parse).makeTwoWay(go.Point.stringify),
{
deletable: false,
layout:
$(go.TreeLayout,
{
alignment: go.TreeLayout.AlignmentStart,
angle: 0,
compaction: go.TreeLayout.CompactionNone,
layerSpacing: 16,
layerSpacingParentOverlap: 1,
nodeIndentPastParent: 1.0,
nodeSpacing: 0,
setsPortSpot: false,
setsChildPortSpot: false
})
},
$(go.Shape, { fill: "white", stroke: "lightgray" }),
$(go.Panel, "Vertical",
{ defaultAlignment: go.Spot.Left },
$(go.TextBlock,
{ font: "bold 14pt sans-serif", margin: new go.Margin(5, 5, 0, 5) },
new go.Binding("text")),
$(go.Placeholder, { padding: 5 })
)
);
var nodeDataArray = [
{ isGroup: true, key: -1, text: "Left Side", xy: "0 0" },
{ isGroup: true, key: -2, text: "Right Side", xy: "300 0" }
];
var linkDataArray = [
{ from: 6, to: 1012, category: "Mapping" },
{ from: 4, to: 1006, category: "Mapping" },
{ from: 9, to: 1004, category: "Mapping" },
{ from: 1, to: 1009, category: "Mapping" },
{ from: 14, to: 1010, category: "Mapping" }
];
// initialize tree on left side
var root = { key: 0, group: -1 };
nodeDataArray.push(root);
for (var i = 0; i < 11;) {
i = makeTree(3, i, 17, nodeDataArray, linkDataArray, root, -1, root.key);
}
// initialize tree on right side
root = { key: 1000, group: -2 };
nodeDataArray.push(root);
for (var i = 0; i < 15;) {
i = makeTree(3, i, 15, nodeDataArray, linkDataArray, root, -2, root.key);
}
myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
}
// help create a random tree structure
function makeTree(level, count, max, nodeDataArray, linkDataArray, parentdata, groupkey, rootkey) {
var numchildren = Math.floor(Math.random() * 10);
for (var i = 0; i < numchildren; i++) {
if (count >= max) return count;
count++;
var childdata = { key: rootkey + count, group: groupkey };
nodeDataArray.push(childdata);
linkDataArray.push({ from: parentdata.key, to: childdata.key });
if (level > 0 && Math.random() > 0.5) {
count = makeTree(level - 1, count, max, nodeDataArray, linkDataArray, childdata, groupkey, rootkey);
}
}
return count;
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: 1px solid black; width: 700px; height: 350px"></div>
<p>
This sample is like the <a href="records.html">Mapping Fields of Records</a> sample,
but it has a collapsible tree of nodes on each side, rather than a simple list of items.
The implementation of the trees comes from the <a href="treeView.html">Tree View</a> sample.
</p>
<p>
Draw new links by dragging from any field (i.e. any tree node).
Reconnect a selected link by dragging its diamond-shaped handle.
</p>
<p>The model data, automatically updated after each change or undo or redo:</p>
<textarea id="mySavedModel" style="width:100%;height:300px"></textarea>
</div>
</body>
</html>