<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Simple Block Editor</title>
<meta name="description" content="A simple block diagram editor that includes context menus for changing shapes and colors." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<script src="go.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<script id="code">
function FlowTreeLayout() {
go.TreeLayout.call(this);
this.angle = 90;
}
go.Diagram.inherit(FlowTreeLayout, go.TreeLayout);
FlowTreeLayout.prototype.commitLinks = function() {
this.network.edges.each(function(e) {
var l = e.link;
if (!l) return;
var fromFig = l.fromNode.data.figure || "Rectangle";
var toFig = l.toNode.data.figure || "Rectangle";
if (l.toNode.location.y < l.fromNode.location.y) {
l.routing = go.Link.AvoidsNodes;
l.fromSpot = go.Spot.LeftRightSides;
l.toSpot = (toFig === "Circle" || toFig === "Diamond") ? go.Spot.Top : go.Spot.LeftRightSides;
l.fromEndSegmentLength = 20;
l.toEndSegmentLength = 20;
} else {
l.routing = go.Link.Orthogonal;
l.fromSpot = (fromFig === "Diamond") ? go.Spot.LeftRightSides : go.Spot.Bottom;
l.toSpot = go.Spot.Top;
l.fromEndSegmentLength = 10;
l.toEndSegmentLength = 10;
}
});
go.TreeLayout.prototype.commitLinks.call(this);
}
function init() {
if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this
var $ = go.GraphObject.make;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
layout: $(FlowTreeLayout, { nodeSpacing: 40, layerSpacing: 40 }),
"clickCreatingTool.archetypeNodeData": { text: "NEW NODE" }, // create a new node by double-clicking in background
"LinkDrawn": function(e) { UpdateSpots(e.subject); },
"undoManager.isEnabled": true
});
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{
locationSpot: go.Spot.Center, locationObjectName: "SHAPE",
minSize: new go.Size(40, 40),
},
$(go.Shape,
{ // the border
name: "SHAPE", fill: "white", strokeWidth: 1.5,
portId: "", cursor: "pointer",
fromLinkable: true, toLinkable: true
},
new go.Binding("figure"),
new go.Binding("fill"),
new go.Binding("stroke")),
$(go.TextBlock,
{ margin: 2, textAlign: "center", editable: true },
// this Binding is TwoWay due to the user editing the text with the TextEditingTool
new go.Binding("text").makeTwoWay())
);
function makeArrowButton(fig) {
var maker = function(e, shape) {
e.handled = true;
e.diagram.model.commit(function(m) {
var selnode = shape.part.adornedPart;
// create a new node
var nodedata = { figure: fig, text: "NEW \nNODE" };
m.addNodeData(nodedata); // add to model
// create a link from the selected node to the new node
var linkdata = { from: selnode.key, to: m.getKeyForNodeData(nodedata) };
m.addLinkData(linkdata); // add to model
// select it, and scroll to it
var newnode = e.diagram.findNodeForData(nodedata);
e.diagram.select(newnode);
setTimeout(function() {
e.diagram.commandHandler.scrollToPart(newnode);
}, 20);
});
};
return $(go.Shape,
{
figure: fig,
width: 24,
height: 24,
fill: "orange", strokeWidth: 0,
isActionable: true, // needed because it's in an Adornment
click: maker, contextClick: maker
});
}
myDiagram.nodeTemplate.selectionAdornmentTemplate =
$(go.Adornment, "Spot",
$(go.Placeholder, { padding: 10 }),
$(go.Panel, "Horizontal",
{ alignment: go.Spot.Bottom, alignmentFocus: go.Spot.Top },
makeArrowButton("Rectangle"),
makeArrowButton("Diamond"),
makeArrowButton("Circle")
)
);
// Link template
myDiagram.linkTemplate =
$(go.Link,
{
layerName: "Foreground",
routing: go.Link.Orthogonal, corner: 6
},
$(go.Shape, { strokeWidth: 1.5 }),
$(go.Shape, { toArrow: "Standard" }),
$(go.TextBlock,
{ alignmentFocus: new go.Spot(0, 1, -4, 0), editable: true },
new go.Binding("text").makeTwoWay()) // TwoWay due to user editing with TextEditingTool
);
load();
}
// save a model to and load a model from JSON-formatted text, displayed below the Diagram
function save() {
var str = myDiagram.model.toJson();
document.getElementById("mySavedModel").value = str;
}
function load() {
var str = document.getElementById("mySavedModel").value;
myDiagram.model = go.Model.fromJson(str);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:800px"></div>
<div id="buttons">
<button id="loadModel" onclick="load()">Load</button>
<button id="saveModel" onclick="save()">Save</button>
</div>
<textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "GraphLinksModel",
"nodeDataArray": [
{"key":1, "text":"Alpha", "figure":"Circle"},
{"key":2, "text":"Beta", "stroke":"blue", "figure":"Diamond"},
{"key":3, "text":"Gamma", "stroke":"green"},
{"key":4, "text":"Delta", "stroke":"red"},
{"key":5, "text":"Zeta", "stroke":"blue"},
{"key":6, "text":"Eta", "fill":"pink", "figure":"Circle"},
{"key":7, "text":"Theta", "stroke":"green", "fill":"lightgreen"},
{"key":8, "text":"Iota"},
{"key":9, "text":"Kappa", "fill":"pink", "figure":"Circle"},
{"key":10, "text":"Lambda"},
{"key":11, "text":"Mu"}
],
"linkDataArray": [
{"from":1, "to":10},
{"from":10, "to": 11},
{"from":11, "to": 2},
{"from":11, "to": 10},
{"from":2, "to":3},
{"from":3, "to":4},
{"from":4, "to":5},
{"from":5, "to":6},
{"from":5, "to":3},
{"from":2, "to":7},
{"from":7, "to":8},
{"from":8, "to":9}
]}
</textarea>
</div>
</body>
</html>
This produces: