367 lines
14 KiB
HTML
367 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Page Flow</title>
|
|
<meta name="description" content="A flow diagram showing navigation between web pages, with an editable list of comments and to-dos." />
|
|
<!-- Copyright 1998-2017 by Northwoods Software Corporation. -->
|
|
<meta charset="UTF-8">
|
|
<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 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
|
|
|
|
var yellowgrad = $(go.Brush, "Linear", { 0: "rgb(254, 201, 0)", 1: "rgb(254, 162, 0)" });
|
|
var greengrad = $(go.Brush, "Linear", { 0: "#98FB98", 1: "#9ACD32" });
|
|
var bluegrad = $(go.Brush, "Linear", { 0: "#B0E0E6", 1: "#87CEEB" });
|
|
var redgrad = $(go.Brush, "Linear", { 0: "#C45245", 1: "#871E1B" });
|
|
var whitegrad = $(go.Brush, "Linear", { 0: "#F0F8FF", 1: "#E6E6FA" });
|
|
|
|
var bigfont = "bold 13pt Helvetica, Arial, sans-serif";
|
|
var smallfont = "bold 11pt Helvetica, Arial, sans-serif";
|
|
|
|
// Common text styling
|
|
function textStyle() {
|
|
return {
|
|
margin: 6,
|
|
wrap: go.TextBlock.WrapFit,
|
|
textAlign: "center",
|
|
editable: true,
|
|
font: bigfont
|
|
}
|
|
}
|
|
|
|
myDiagram =
|
|
$(go.Diagram, "myDiagramDiv",
|
|
{
|
|
// have mouse wheel events zoom in and out instead of scroll up and down
|
|
"toolManager.mouseWheelBehavior": go.ToolManager.WheelZoom,
|
|
allowDrop: true, // support drag-and-drop from the Palette
|
|
initialAutoScale: go.Diagram.Uniform,
|
|
"linkingTool.direction": go.LinkingTool.ForwardsOnly,
|
|
initialContentAlignment: go.Spot.Center,
|
|
layout: $(go.LayeredDigraphLayout, { isInitial: false, isOngoing: false, layerSpacing: 50 }),
|
|
"undoManager.isEnabled": true
|
|
});
|
|
|
|
// when the document is modified, add a "*" to the title and enable the "Save" button
|
|
myDiagram.addDiagramListener("Modified", function(e) {
|
|
var button = document.getElementById("SaveButton");
|
|
if (button) button.disabled = !myDiagram.isModified;
|
|
var idx = document.title.indexOf("*");
|
|
if (myDiagram.isModified) {
|
|
if (idx < 0) document.title += "*";
|
|
} else {
|
|
if (idx >= 0) document.title = document.title.substr(0, idx);
|
|
}
|
|
});
|
|
|
|
var defaultAdornment =
|
|
$(go.Adornment, "Spot",
|
|
$(go.Panel, "Auto",
|
|
$(go.Shape, { fill: null, stroke: "dodgerblue", strokeWidth: 4 }),
|
|
$(go.Placeholder)),
|
|
// the button to create a "next" node, at the top-right corner
|
|
$("Button",
|
|
{ alignment: go.Spot.TopRight,
|
|
click: addNodeAndLink }, // this function is defined below
|
|
new go.Binding("visible", "", function(a) { return !a.diagram.isReadOnly; }).ofObject(),
|
|
$(go.Shape, "PlusLine", { desiredSize: new go.Size(6, 6) })
|
|
)
|
|
);
|
|
|
|
// define the Node template
|
|
myDiagram.nodeTemplate =
|
|
$(go.Node, "Auto",
|
|
{ selectionAdornmentTemplate: defaultAdornment },
|
|
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
|
|
// define the node's outer shape, which will surround the TextBlock
|
|
$(go.Shape, "Rectangle",
|
|
{ fill: yellowgrad, stroke: "black",
|
|
portId: "", fromLinkable: true, toLinkable: true, cursor: "pointer",
|
|
toEndSegmentLength: 50, fromEndSegmentLength: 40 }),
|
|
$(go.TextBlock, "Page",
|
|
{ margin: 6,
|
|
font: bigfont,
|
|
editable: true },
|
|
new go.Binding("text", "text").makeTwoWay()));
|
|
|
|
myDiagram.nodeTemplateMap.add("Source",
|
|
$(go.Node, "Auto",
|
|
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
|
|
$(go.Shape, "RoundedRectangle",
|
|
{ fill: bluegrad,
|
|
portId: "", fromLinkable: true, cursor: "pointer", fromEndSegmentLength: 40}),
|
|
$(go.TextBlock, "Source", textStyle(),
|
|
new go.Binding("text", "text").makeTwoWay())
|
|
));
|
|
|
|
myDiagram.nodeTemplateMap.add("DesiredEvent",
|
|
$(go.Node, "Auto",
|
|
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
|
|
$(go.Shape, "RoundedRectangle",
|
|
{ fill: greengrad, portId: "", toLinkable: true, toEndSegmentLength: 50 }),
|
|
$(go.TextBlock, "Success!", textStyle(),
|
|
new go.Binding("text", "text").makeTwoWay())
|
|
));
|
|
|
|
// Undesired events have a special adornment that allows adding additional "reasons"
|
|
var UndesiredEventAdornment =
|
|
$(go.Adornment, "Spot",
|
|
$(go.Panel, "Auto",
|
|
$(go.Shape, { fill: null, stroke: "dodgerblue", strokeWidth: 4 }),
|
|
$(go.Placeholder)),
|
|
// the button to create a "next" node, at the top-right corner
|
|
$("Button",
|
|
{ alignment: go.Spot.BottomRight,
|
|
click: addReason }, // this function is defined below
|
|
new go.Binding("visible", "", function(a) { return !a.diagram.isReadOnly; }).ofObject(),
|
|
$(go.Shape, "TriangleDown", { desiredSize: new go.Size(10, 10) })
|
|
)
|
|
);
|
|
|
|
var reasonTemplate = $(go.Panel, "Horizontal",
|
|
$(go.TextBlock, "Reason",
|
|
{
|
|
margin: new go.Margin(4,0,0,0),
|
|
maxSize: new go.Size(200, NaN),
|
|
wrap: go.TextBlock.WrapFit,
|
|
stroke: "whitesmoke",
|
|
editable: true,
|
|
font: smallfont
|
|
},
|
|
new go.Binding("text", "text").makeTwoWay())
|
|
);
|
|
|
|
|
|
myDiagram.nodeTemplateMap.add("UndesiredEvent",
|
|
$(go.Node, "Auto",
|
|
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
|
|
{ selectionAdornmentTemplate: UndesiredEventAdornment },
|
|
$(go.Shape, "RoundedRectangle",
|
|
{ fill: redgrad, portId: "", toLinkable: true, toEndSegmentLength: 50 }),
|
|
$(go.Panel, "Vertical", {defaultAlignment: go.Spot.TopLeft},
|
|
|
|
$(go.TextBlock, "Drop", textStyle(),
|
|
{ stroke: "whitesmoke",
|
|
minSize: new go.Size(80, NaN) },
|
|
new go.Binding("text", "text").makeTwoWay()),
|
|
|
|
$(go.Panel, "Vertical",
|
|
{ defaultAlignment: go.Spot.TopLeft,
|
|
itemTemplate: reasonTemplate },
|
|
new go.Binding("itemArray", "reasonsList").makeTwoWay()
|
|
)
|
|
)
|
|
));
|
|
|
|
myDiagram.nodeTemplateMap.add("Comment",
|
|
$(go.Node, "Auto",
|
|
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
|
|
$(go.Shape, "Rectangle",
|
|
{ portId: "", fill: whitegrad, fromLinkable: true }),
|
|
$(go.TextBlock, "A comment",
|
|
{ margin: 9,
|
|
maxSize: new go.Size(200, NaN),
|
|
wrap: go.TextBlock.WrapFit,
|
|
editable: true,
|
|
font: smallfont },
|
|
new go.Binding("text", "text").makeTwoWay())
|
|
// no ports, because no links are allowed to connect with a comment
|
|
));
|
|
|
|
// clicking the 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 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 =
|
|
$(go.Link, // the whole link panel
|
|
new go.Binding("points").makeTwoWay(),
|
|
{ curve: go.Link.Bezier, toShortLength: 15 },
|
|
new go.Binding("curviness", "curviness"),
|
|
$(go.Shape, // the link shape
|
|
{ stroke: "#2F4F4F", strokeWidth: 2.5 }),
|
|
$(go.Shape, // the arrowhead
|
|
{ toArrow: "kite", fill: "#2F4F4F", stroke: null, scale: 2 })
|
|
);
|
|
|
|
myDiagram.linkTemplateMap.add("Comment",
|
|
$(go.Link, { selectable: false },
|
|
$(go.Shape, { strokeWidth: 2, stroke: "darkgreen" })));
|
|
|
|
|
|
var palette =
|
|
$(go.Palette, "palette", // create a new Palette in the HTML DIV element "palette"
|
|
{
|
|
// share the template map with the Palette
|
|
nodeTemplateMap: myDiagram.nodeTemplateMap,
|
|
autoScale: go.Diagram.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);
|
|
}
|
|
</script>
|
|
</head>
|
|
<body onload="init()">
|
|
<div id="sample">
|
|
<div style="width:100%; white-space:nowrap;">
|
|
<span style="display: inline-block; vertical-align: top; width:100px">
|
|
<div id="palette" style="border: solid 1px black; height: 480px"></div>
|
|
</span>
|
|
<span style="display: inline-block; vertical-align: top; width:85%">
|
|
<div id="myDiagramDiv" style="border: solid 1px black; height: 480px"></div>
|
|
</span>
|
|
</div>
|
|
<p>
|
|
This sample uses the <a>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>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>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>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()">Save</button>
|
|
<button onclick="load()">Load</button>
|
|
<button onclick="layout()">Layout</button>
|
|
Diagram Model saved in JSON format:
|
|
<br />
|
|
<textarea id="mySavedModel" style="width:100%;height:300px">
|
|
{ "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?"} ]},
|
|
|
|
{ "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>
|
|
</body>
|
|
</html>
|