create-gojs-kit
Version:
A CLI for downloading GoJS samples, extensions, and docs
719 lines (644 loc) • 28 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
<meta name="description" content="A workflow diagram showing navigation between web pages, with an editable list of comments and to-dos." />
<meta itemprop="description" content="A workflow diagram showing navigation between web pages, with an editable list of comments and to-dos." />
<meta property="og:description" content="A workflow diagram showing navigation between web pages, with an editable list of comments and to-dos." />
<meta name="twitter:description" content="A workflow diagram showing navigation between web pages, with an editable list of comments and to-dos." />
<link rel="preconnect" href="https://rsms.me/">
<link rel="stylesheet" href="../assets/css/style.css">
<!-- Copyright 1998-2025 by Northwoods Software Corporation. -->
<meta itemprop="name" content="Page Flow Diagram Editor" />
<meta property="og:title" content="Page Flow Diagram Editor" />
<meta name="twitter:title" content="Page Flow Diagram Editor" />
<meta property="og:image" content="https://gojs.net/latest/assets/images/screenshots/pageflow.png" />
<meta itemprop="image" content="https://gojs.net/latest/assets/images/screenshots/pageflow.png" />
<meta name="twitter:image" content="https://gojs.net/latest/assets/images/screenshots/pageflow.png" />
<meta property="og:url" content="https://gojs.net/latest/samples/pageFlow.html" />
<meta property="twitter:url" content="https://gojs.net/latest/samples/pageFlow.html" />
<meta name="twitter:card" content="summary_large_image" />
<meta property="og:type" content="website" />
<meta property="twitter:domain" content="gojs.net" />
<title>
Page Flow Diagram Editor | GoJS Diagramming Library
</title>
</head>
<body>
<!-- This top nav is not part of the sample code -->
<nav id="navTop" class=" w-full h-[var(--topnav-h)] z-30 bg-white border-b border-b-gray-200">
<div class="max-w-screen-xl mx-auto flex flex-wrap items-start justify-between px-4">
<a class="text-white bg-nwoods-primary font-bold !leading-[calc(var(--topnav-h)_-_1px)] my-0 px-2 text-4xl lg:text-5xl logo"
href="../">
GoJS
</a>
<div class="relative">
<button id="topnavButton" class="h-[calc(var(--topnav-h)_-_1px)] px-2 m-0 text-gray-900 bg-inherit shadow-none md:hidden hover:!bg-inherit hover:!text-nwoods-accent hover:!shadow-none" aria-label="Navigation">
<svg class="h-7 w-7 block" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<div id="topnavList" class="hidden md:block">
<div class="absolute right-0 z-30 flex flex-col items-end rounded border border-gray-200 p-4 pl-12 shadow bg-white text-gray-900 font-semibold
md:flex-row md:space-x-4 md:items-start md:border-0 md:p-0 md:shadow-none md:bg-inherit">
<a href="../learn/">Learn</a>
<a href="../samples/">Samples</a>
<a href="../intro/">Intro</a>
<a href="../api/">API</a>
<a href="../download.html">Download</a>
<a href="https://forum.nwoods.com/c/gojs/11" target="_blank" rel="noopener">Forum</a>
<a id="tc" href="https://nwoods.com/contact.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/contact.html', 'contact');">Contact</a>
<a id="tb" href="https://nwoods.com/sales/index.html"
target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/sales/index.html', 'buy');">Buy</a>
</div>
</div>
</div>
</div>
</nav>
<script>
window.addEventListener("DOMContentLoaded", function () {
// topnav
var topButton = document.getElementById("topnavButton");
var topnavList = document.getElementById("topnavList");
if (topButton && topnavList) {
topButton.addEventListener("click", function (e) {
topnavList
.classList
.toggle("hidden");
e.stopPropagation();
});
document.addEventListener("click", function (e) {
// if the clicked element isn't the list, close the list
if (!topnavList.classList.contains("hidden") && !e.target.closest("#topnavList")) {
topButton.click();
}
});
// set active <a> element
var url = window
.location
.href
.toLowerCase();
var aTags = topnavList.getElementsByTagName('a');
for (var i = 0; i < aTags.length; i++) {
var lowerhref = aTags[i]
.href
.toLowerCase();
if (lowerhref.endsWith('.html'))
lowerhref = lowerhref.slice(0, -5);
if (url.startsWith(lowerhref)) {
aTags[i]
.classList
.add('active');
break;
}
}
}
});
</script>
<div class="flex flex-col prose">
<div class="w-full max-w-screen-xl mx-auto">
<!-- * * * * * * * * * * * * * -->
<!-- Start of GoJS sample code -->
<script src="https://cdn.jsdelivr.net/npm/gojs@3.1.0"></script>
<div id="allSampleContent" class="p-4 w-full">
<script id="code">
var isDarkMode = false; // is accessed by html elements (button) so must be declared in script scope
function init() {
// colors are almost entirely defined up here to allow for easier theming
var yellow = '#FFB400';
var green = '#7FB800';
var blue = '#00A6ED';
var red = '#D73909';
var white = '#DCE9F9';
var blueShadow = '#407090';
var greenShadow = '#406050';
var yellowShadow = '#804040';
var redShadow = '#705020';
var whiteShadow = '#404050';
var selectColor = 'dodgerBlue';
var undesiredEventTextColor = 'whitesmoke';
var lineColor = '#0D2C54';
var commentLineColor = 'darkgreen';
var darkModeBackgroundColor = '#06162a';
var lightModeBackgroundColor = '#FFFFFF';
var bigfont = 'bold 13pt Helvetica, Arial, sans-serif';
var smallfont = 'bold 11pt Helvetica, Arial, sans-serif';
// Common text styling
function textStyle(tb) {
tb.margin = 10;
tb.wrap = go.Wrap.Fit;
tb.textAlign = 'center';
tb.editable = true;
tb.font = bigfont;
}
myDiagram = new go.Diagram('myDiagramDiv', {
// have mouse wheel events zoom in and out instead of scroll up and down
'toolManager.mouseWheelBehavior': go.WheelMode.Zoom,
initialAutoScale: go.AutoScale.Uniform,
'linkingTool.direction': go.LinkingDirection.ForwardsOnly,
layout: new go.LayeredDigraphLayout({
isInitial: false,
isOngoing: false,
layerSpacing: 100
}),
'undoManager.isEnabled': true
});
// when the document is modified, add a "*" to the title and enable the "Save" button
myDiagram.addDiagramListener('Modified', e => {
const button = document.getElementById('SaveButton');
if (button) button.disabled = !myDiagram.isModified;
const idx = document.title.indexOf('*');
if (myDiagram.isModified) {
if (idx < 0) document.title += '*';
} else {
if (idx >= 0) document.title = document.title.slice(0, idx);
}
});
var defaultAdornment =
new go.Adornment('Spot')
.add(
new go.Panel('Auto')
.add(
new go.Shape({ fill: null, stroke: selectColor, strokeWidth: 3 }),
new go.Placeholder()
),
// the button to create a "next" node, at the top-right corner
go.GraphObject.build('Button', {
alignment: new go.Spot(1, 0, -5, 5),
click: addNodeAndLink
}) // this function is defined below
.bindObject('visible', '', a => !a.diagram.isReadOnly)
.add(
new go.Shape('PlusLine', { desiredSize: new go.Size(6, 6) })
)
);
// define the Node template
myDiagram.nodeTemplate =
new go.Node('Auto', {
selectionAdornmentTemplate: defaultAdornment,
isShadowed: true,
shadowBlur: 2,
shadowColor: yellowShadow,
shadowOffset: new go.Point(4, 6)
})
.bindTwoWay('location', 'loc', go.Point.parse, go.Point.stringify)
.add(
new go.Shape('Rectangle', {
fill: yellow,
stroke: 'rgba(0, 0, 0, 0)',
portId: '',
fromLinkable: true,
toLinkable: true,
cursor: 'pointer',
toEndSegmentLength: 50,
fromEndSegmentLength: 40
}),
new go.TextBlock('Page', {
margin: 10,
font: bigfont,
editable: true
})
.bindTwoWay('text')
);
myDiagram.nodeTemplateMap.add('Source',
new go.Node('Auto', {
isShadowed: true,
shadowBlur: 2,
shadowColor: blueShadow,
shadowOffset: new go.Point(4, 6)
})
.bindTwoWay('location', 'loc', go.Point.parse, go.Point.stringify)
.add(
new go.Shape('RoundedRectangle', {
name: 'SHAPE',
fill: blue,
stroke: 'rgba(0, 0, 0, 0)',
portId: '',
fromLinkable: true,
cursor: 'pointer',
fromEndSegmentLength: 40
})
.bind('fill', 'color'),
new go.TextBlock('Source')
.apply(textStyle)
.bindTwoWay('text')
)
);
myDiagram.nodeTemplateMap.add('DesiredEvent',
new go.Node('Auto', {
isShadowed: true,
shadowBlur: 2,
shadowColor: greenShadow,
shadowOffset: new go.Point(4, 6)
})
.bindTwoWay('location', 'loc', go.Point.parse, go.Point.stringify)
.add(
new go.Shape('RoundedRectangle', {
fill: green,
stroke: null,
portId: '',
toLinkable: true,
toEndSegmentLength: 50
})
.bind('fill', 'color'),
new go.TextBlock('Success!')
.apply(textStyle)
.bindTwoWay('text')
)
);
// Undesired events have special adornments that allows adding and removing additional "reasons"
var UndesiredEventAdornmentModify =
new go.Adornment('Spot')
.add(
new go.Panel('Auto')
.add(
new go.Shape({ fill: null, stroke: selectColor, strokeWidth: 4 }),
new go.Placeholder()
),
// the button to create a new reason, at the bottom-left corner
go.GraphObject.build('Button', {
alignment: new go.Spot(1, 1, -5, -5),
click: addReason
}) // this function is defined below
.bindObject('visible', '', a => !a.diagram.isReadOnly)
.add(
new go.Shape('TriangleDown', { desiredSize: new go.Size(10, 10) })
),
//the button to remove the most recent reason, above the "create" button
go.GraphObject.build('Button', {
alignment: new go.Spot(1, 1, -5, -22),
click: removeReason
}) //this function is also defined below
.bindObject('visible', '', a => !a.diagram.isReadOnly)
.add(
new go.Shape('TriangleUp', { desiredSize: new go.Size(10, 10) })
)
);
var reasonTemplate =
new go.Panel('Horizontal')
.add(
new go.TextBlock('•', {
margin: 4,
stroke: undesiredEventTextColor,
font: smallfont
}),
new go.TextBlock('Reason', {
margin: 4,
maxSize: new go.Size(200, NaN),
wrap: go.Wrap.Fit,
stroke: undesiredEventTextColor,
editable: true,
font: smallfont
})
.bindTwoWay('text')
);
myDiagram.nodeTemplateMap.add('UndesiredEvent',
new go.Node('Auto', {
isShadowed: true,
shadowBlur: 2,
shadowColor: redShadow,
shadowOffset: new go.Point(4, 6),
selectionAdornmentTemplate: UndesiredEventAdornmentModify
})
.bindTwoWay('location', 'loc', go.Point.parse, go.Point.stringify)
.add(
new go.Shape('RoundedRectangle', {
fill: red,
stroke: null,
portId: '',
toLinkable: true,
toEndSegmentLength: 50
}),
new go.Panel('Vertical', { defaultAlignment: go.Spot.Top })
.add(
new go.TextBlock('Drop')
.apply(textStyle)
.set({
stroke: undesiredEventTextColor,
minSize: new go.Size(80, NaN)
})
.bindTwoWay('text'),
new go.Panel('Vertical', {
defaultAlignment: go.Spot.TopLeft,
itemTemplate: reasonTemplate
})
.bindTwoWay('itemArray', 'reasonsList')
)
)
);
myDiagram.nodeTemplateMap.add('Comment',
new go.Node('Auto')
.bindTwoWay('location', 'loc', go.Point.parse, go.Point.stringify)
.add(
new go.Shape('Rectangle', {
portId: '',
fill: white,
stroke: null,
fromLinkable: true
}),
new go.TextBlock('A comment', {
margin: 10,
maxSize: new go.Size(200, NaN),
wrap: go.Wrap.Fit,
editable: true,
font: smallfont
})
.bindTwoWay('text')
// no ports, because no links are allowed to connect with a comment
)
);
// clicking the add button on an UndesiredEvent node inserts a new text object into the panel
function addReason(e, obj) {
var adorn = obj.part;
if (adorn === null) return;
e.handled = true;
var arr = adorn.adornedPart.data.reasonsList;
myDiagram.startTransaction('add reason');
myDiagram.model.addArrayItem(arr, {});
myDiagram.commitTransaction('add reason');
}
// clicking the remove button will remove the most recent text object added to the panel
function removeReason(e, obj) {
var adorn = obj.part;
if (adorn === null) return;
e.handled = true;
var arr = adorn.adornedPart.data.reasonsList;
myDiagram.startTransaction('remove reason');
myDiagram.model.removeArrayItem(arr, arr.length - 1);
myDiagram.commitTransaction('remove reason');
}
// clicking the button of a default node inserts a new node to the right of the selected node,
// and adds a link to that new node
function addNodeAndLink(e, obj) {
var adorn = obj.part;
if (adorn === null) return;
e.handled = true;
var diagram = adorn.diagram;
diagram.startTransaction('Add State');
// get the node data for which the user clicked the button
var fromNode = adorn.adornedPart;
var fromData = fromNode.data;
// create a new "State" data object, positioned off to the right of the adorned Node
var toData = { text: 'new' };
var p = fromNode.location;
toData.loc = p.x + 200 + ' ' + p.y; // the "loc" property is a string, not a Point object
// add the new node data to the model
var model = diagram.model;
model.addNodeData(toData);
// create a link data from the old node data to the new node data
var linkdata = {};
linkdata[model.linkFromKeyProperty] = model.getKeyForNodeData(fromData);
linkdata[model.linkToKeyProperty] = model.getKeyForNodeData(toData);
// and add the link data to the model
model.addLinkData(linkdata);
// select the new Node
var newnode = diagram.findNodeForData(toData);
diagram.select(newnode);
diagram.commitTransaction('Add State');
}
// replace the default Link template in the linkTemplateMap
myDiagram.linkTemplate =
new go.Link({ // the whole link panel
curve: go.Curve.Bezier,
toShortLength: 5,
fromSpot: go.Spot.Right,
toSpot: go.Spot.Left
})
.add(
new go.Shape({ // the link shape
stroke: lineColor,
strokeWidth: 2.5
}),
new go.Shape({ // the arrowhead
toArrow: 'Kite',
fill: lineColor,
stroke: lineColor,
scale: 1.5
})
);
// comments will have different links
myDiagram.linkTemplateMap.add('Comment',
new go.Link({
fromSpot: go.Spot.Center,
toSpot: go.Spot.Center
})
.add(
new go.Shape({
strokeWidth: 1.5,
stroke: 'darkgreen'
})
)
);
// when a new link is created, determine what template to use
myDiagram.addDiagramListener('LinkDrawn', e => {
var link = e.subject;
if (link.fromNode.category === 'Comment') {
link.category = 'Comment';
}
});
var palette = new go.Palette('myPaletteDiv', {
// share the template map with the Palette
nodeTemplateMap: myDiagram.nodeTemplateMap,
autoScale: go.AutoScale.Uniform // everything always fits in viewport
});
palette.model.nodeDataArray = [
{ category: 'Source' },
{}, // default node
{ category: 'DesiredEvent' },
{ category: 'UndesiredEvent', reasonsList: [{}] },
{ category: 'Comment' }
];
// read in the JSON-format data from the "mySavedModel" element
load();
layout();
}
function layout() {
myDiagram.layoutDiagram(true);
}
// Show the diagram's model in JSON format
function save() {
document.getElementById('mySavedModel').value = myDiagram.model.toJson();
myDiagram.isModified = false;
}
function load() {
myDiagram.model = go.Model.fromJson(document.getElementById('mySavedModel').value);
}
window.addEventListener('DOMContentLoaded', init);
</script>
<div id="sample">
<div style="width: 100%; display: flex; justify-content: space-between; background-color: #fcfcfc">
<div id="myPaletteDiv" style="width: 100px; margin-right: 2px; background-color: whitesmoke; border: 1px solid black; position: relative">
<canvas
tabindex="0"
style="position: absolute; top: 0px; left: 0px; z-index: 2; user-select: none; touch-action: none; width: 99px; height: 479px"
width="148"
height="718"
>This text is displayed if your browser does not support the Canvas HTML element.</canvas
>
<div style="position: absolute; overflow: auto; width: 99px; height: 479px; z-index: 1">
<div style="position: absolute; width: 1px; height: 1px"></div>
</div>
</div>
<div id="myDiagramDiv" style="flex-grow: 1; height: 480px; border: 1px solid black; position: relative">
<canvas
tabindex="0"
style="position: absolute; top: 0px; left: 0px; z-index: 2; user-select: none; touch-action: none; width: 953px; height: 479px"
width="1429"
height="718"
>This text is displayed if your browser does not support the Canvas HTML element.</canvas
>
<div style="position: absolute; overflow: auto; width: 953px; height: 479px; z-index: 1">
<div style="position: absolute; width: 1px; height: 1px"></div>
</div>
</div>
</div>
<button onclick="layout()">Diagram Layout</button>
<p>
This workflow diagram uses the <a href="../api/symbols/LayeredDigraphLayout.html" target="api">LayeredDigraphLayout</a> to display some data about the flow
of a fictional web site. You can add to the Diagram by dragging Nodes from the <a href="../api/symbols/Palette.html" target="api">Palette</a> and by buttons
that appear when clicking on the Page (yellow) and Drop (red) Nodes.
</p>
<p>
All nodes in this sample have editable text. To activate the <a href="../api/symbols/TextEditingTool.html" target="api">TextEditingTool</a>, click on a node
to select it and click on its text once selected.
</p>
<p>
Several Link relationships are defined. Hovering over the sides of many nodes changes the mouse cursor to a pointer. Clicking and dragging in these areas
creates a new link with the <a href="../api/symbols/LinkingTool.html" target="api">LinkingTool</a>. The node definitions contain several rules, for instance
you cannot drag links to Source (blue) Nodes, only from them, and you cannot have multiple links between the same two nodes, among others.
</p>
<p>
Most of the source code for this sample is in defining pleasing Node templates. Much of the functionality seen is done with built-in GoJS components. This
is by no means an exhaustive sample, so be sure to check out the other samples to the left, or take a look at the
<a href="../intro/">Introductory Documents</a> for a more structured tutorial on different GoJS concepts.
</p>
<button id="SaveButton" onclick="save()" disabled="">Save</button>
<button onclick="load()">Load</button>
Diagram Model saved in JSON format:
<br />
<textarea id="mySavedModel" style="width: 100%; height: 300px">
{ "class": "go.GraphLinksModel",
"copiesArrays": true,
"copiesArrayObjects": true,
"nodeDataArray": [
{ "key": -1, "category": "Source", "text": "Search" },
{ "key": -2, "category": "Source", "text": "Referral" },
{ "key": -3, "category": "Source", "text": "Advertising" },
{ "key": 0, "text": "Homepage" },
{ "key": 1, "text": "Products" },
{ "key": 2, "text": "Buy" },
{ "key": 3, "text": "Samples" },
{ "key": 5, "text": "Documentation" },
{ "key": 6, "text": "Download" },
{ "key": 100, "category": "DesiredEvent", "text": "Ordered!" },
{ "key": 101, "category": "DesiredEvent", "text": "Downloaded!" },
{ "key": 200, "category": "UndesiredEvent",
"reasonsList": [
{"text":"Needs redesign?"},
{"text":"Wrong Product?"} ]},
{ "key": 201, "category": "UndesiredEvent",
"reasonsList": [
{"text":"Need better samples?"},
{"text":"Bad landing page for Advertising?"} ]},
{ "key": 202, "category": "UndesiredEvent",
"reasonsList": [
{"text":"Reconsider Pricing?"},
{"text":"Confusing Cart?"} ]},
{ "key": 300, "category": "Comment", "text": "Add notes with general comments for the next team meeting" }
],
"linkDataArray": [
{ "from": -1, "to": 0 },
{ "from": -2, "to": 0 },
{ "from": -2, "to": 3 },
{ "from": -3, "to": 3 },
{ "from": 0, "to": 1 },
{ "from": 1, "to": 2 },
{ "from": 1, "to": 3 },
{ "from": 0, "to": 5 },
{ "from": 5, "to": 3 },
{ "from": 3, "to": 2 },
{ "from": 3, "to": 6 },
{ "from": 2, "to": 100 },
{ "from": 6, "to": 101 },
{ "from": 0, "to": 200 },
{ "from": 3, "to": 201 },
{ "from": 2, "to": 202 }
]
}
</textarea
>
</div>
</div>
<!-- * * * * * * * * * * * * * -->
<!-- End of GoJS sample code -->
</div>
<div id="allTagDescriptions" class="p-4 w-full max-w-screen-xl mx-auto">
<hr/>
<h3 class="text-xl">GoJS Features in this sample</h3>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>Item Arrays</h4>
<p>
It is sometimes useful to display a variable number of elements in a node by data binding to a JavaScript Array.
In GoJS, this is simply achieved by binding (or setting) <a href="../api/symbols/Panel.html#itemArray" target="api">Panel.itemArray</a>.
The <a href="../api/symbols/Panel.html" target="api">Panel</a> will create an element in the panel for each value in the Array.
More information can be found in the <a href="../intro/itemArrays.html">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#itemarrays">Related samples</a>
</p>
<hr>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>Layered Digraph Layout</h4>
<p>
This predefined layout is used for placing Nodes of a general directed graph in layers (rows or columns). This is more general than <a href="../api/symbols/TreeLayout.html">TreeLayout</a>,
as it does not require that the graph be tree-structured.
More information can be found in the <a href="../intro/layouts.html#LayeredDigraphLayout">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#layered-digraph">Related samples</a>
</p>
<hr>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>Palette</h4>
<p>
A <a href="../api/symbols/Palette.html" target="api">Palette</a> is a subclass of <a href="../api/symbols/Diagram.html" target="api">Diagram</a> that is used to display a number of <a href="../api/symbols/Part.html" target="api">Part</a>s that
can be dragged into the diagram that is being modified by the user.
The initialization of a <a href="../api/symbols/Palette.html" target="api">Palette</a> is just like the initialization of any <a href="../api/symbols/Diagram.html" target="api">Diagram</a>.
Like Diagrams, you can have more than one Palette on the page at the same time.
</p>
<p>
More information can be found in the <a href="../intro/palette.html">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#palette">Related samples</a>
</p>
<hr>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
<h4>Buttons</h4>
<p>
GoJS defines several <a href="../api/symbols/Panel.html" target="api">Panel</a>s for common uses.
These include "Button", "TreeExpanderButton", "SubGraphExpanderButton", "PanelExpanderButton", "ContextMenuButton", and "CheckBoxButton".
"ContextMenuButton"s are typically used inside of "ContextMenu" Panels;
"CheckBoxButton"s are used in the implementation of "CheckBox" Panels.
</p>
<p>
These predefined panels can be used as if they were <a href="../api/symbols/Panel.html" target="api">Panel</a>-derived classes in calls to <a href="../api/symbols/GraphObject.html#build" target="api">GraphObject.build</a>.
They are implemented as simple visual trees of <a href="../api/symbols/GraphObject.html" target="api">GraphObject</a>s in <a href="../api/symbols/Panel.html" target="api">Panel</a>s,
with pre-set properties and event handlers.
</p>
<p>
More information can be found in the <a href="../intro/buttons.html">GoJS Intro</a>.
</p>
<p>
<a href="../samples/index.html#buttons">Related samples</a>
</p>
<hr>
<!-- blacklist tags that do not correspond to a specific GoJS feature -->
</div>
</div>
</body>
<!-- This script is part of the gojs.net website, and is not needed to run the sample -->
<script src="../assets/js/goSamples.js"></script>
</html>