feat: 后端全量更新 - 含所有本次需求

- Contract.php: 返回合约账户余额(balance_contract)
- My.php: 地址管理增加BTC/ETH
- AppContract.php: 一键平仓(closeall)
- AppProxy.php: 代理专属注册链接 + 分级权限(L1/L2)
- site.php: 手续费减半(0.018→0.009)
- agent_permission_setup.sql: 代理权限SQL
- crypto_news_crawler.py: 新闻自动采集脚本
This commit is contained in:
li
2026-03-30 20:16:32 +08:00
commit 1b24994e74
6721 changed files with 1308571 additions and 0 deletions
+155
View File
@@ -0,0 +1,155 @@
<!DOCTYPE html>
<html>
<head>
<title>HTML DOM Tree</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Interactive diagram showing the structure of the HTML DOM of this HTML page, allowing collapsing/expanding of subtrees." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<script id="code">
var names = {}; // hash to keep track of what names have been used
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",
{
initialAutoScale: go.Diagram.UniformToFill,
// define the layout for the diagram
layout: $(go.TreeLayout, { nodeSpacing: 5, layerSpacing: 30 })
});
// Define a simple node template consisting of text followed by an expand/collapse button
myDiagram.nodeTemplate =
$(go.Node, "Horizontal",
{ selectionChanged: nodeSelectionChanged }, // this event handler is defined below
$(go.Panel, "Auto",
$(go.Shape, { fill: "#1F4963", stroke: null }),
$(go.TextBlock,
{
font: "bold 13px Helvetica, bold Arial, sans-serif",
stroke: "white", margin: 3
},
new go.Binding("text", "key"))
),
$("TreeExpanderButton")
);
// Define a trivial link template with no arrowhead.
myDiagram.linkTemplate =
$(go.Link,
{ selectable: false },
$(go.Shape)); // the link shape
// create the model for the DOM tree
myDiagram.model =
$(go.TreeModel, {
isReadOnly: true, // don't allow the user to delete or copy nodes
// build up the tree in an Array of node data
nodeDataArray: traverseDom(document.activeElement)
});
}
// Walk the DOM, starting at document, and return an Array of node data objects representing the DOM tree
// Typical usage: traverseDom(document.activeElement)
// The second and third arguments are internal, used when recursing through the DOM
function traverseDom(node, parentName, dataArray) {
if (parentName === undefined) parentName = null;
if (dataArray === undefined) dataArray = [];
// skip everything but HTML Elements
if (!(node instanceof Element)) return;
// Ignore the navigation menus
if (node.id === "navindex" || node.id === "navtop") return;
// add this node to the nodeDataArray
var name = getName(node);
var data = { key: name, name: name };
dataArray.push(data);
// add a link to its parent
if (parentName !== null) {
data.parent = parentName;
}
// find all children
var l = node.childNodes.length;
for (var i = 0; i < l; i++) {
traverseDom(node.childNodes[i], name, dataArray);
}
return dataArray;
}
// Give every node a unique name
function getName(node) {
var n = node.nodeName;
if (node.id) n = n + " (" + node.id + ")";
var namenum = n; // make sure the name is unique
var i = 1;
while (names[namenum] !== undefined) {
namenum = n + i;
i++;
}
names[namenum] = node;
return namenum;
}
// When a Node is selected, highlight the corresponding HTML element.
function nodeSelectionChanged(node) {
if (node.isSelected) {
names[node.data.name].style.backgroundColor = "lightblue";
} else {
names[node.data.name].style.backgroundColor = "";
}
}
</script>
</head>
<body onload="init()">
<div id="sample">
<!-- The DIV needs an explicit size or else we won't see anything. -->
<div id="myDiagramDiv" style="border: 1px solid black; width:100%; height:300px"></div>
<p>
This sample shows the DOM (Document Object Model) of this webpage displayed as a tree.
Each Node in the Diagram shows information about the corresponding HTML element in the DOM.
</p>
<p>
When a node is selected, the background color of the corresponding HTML Element changes to lightblue.
Below the diagram are some more HTML elements to illustrate the effect.
This sample also makes use of the <a href="../intro/buttons.html" target="_blank">TreeExpanderButton</a>,
which allows for parent nodes to expand and collapse their child nodes. Buttons are defined in <a href="../extensions/Buttons.js">Buttons.js</a>.
</p>
<p id="lastParagraph">
Elements with an id attribute are noted in parenthesis.
</p>
<div id="otherInfo">
<div id="tableContainer" style="display: inline-block;">
<table style="border: 1px; border-collapse: collapse;">
<tr>
<th id="firstHeader">Table header</th>
<th id="secondHeader">Table header 2</th>
</tr>
<tr>
<td>row 1, cell 1</td>
<td>row 1, cell 2</td>
</tr>
<tr>
<td>row 2, cell 1</td>
<td>row 2, cell 2</td>
</tr>
</table>
</div>
<div id="listContainer" style="display: inline-block; border: 1px solid gray; margin-left: 10px; width: 100px">
<p>My grocery list</p>
<ul id="groceryList">
<li>Coffee</li>
<li>Milk</li>
<li>Bread</li>
</ul>
</div>
<p>
For more uses of the <a>TreeLayout</a> see the <a href="DOMTree.html">DOM Tree</a> and <a href="visualTree.html">Visual Tree</a> samples.
</p>
</div>
</div>
</body>
</html>
+250
View File
@@ -0,0 +1,250 @@
<!DOCTYPE html>
<html>
<head>
<title>IVR Tree</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="An IVR (Interactive Voice Response) diagram showing a call-menu tree with various prompts and responses." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../extensions/Figures.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
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
allowCopy: false,
"draggingTool.dragsTree": true,
"commandHandler.deletesTree": true,
layout:
$(go.TreeLayout,
{ angle: 90, arrangement: go.TreeLayout.ArrangementFixedRoots }),
"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 bluegrad = $(go.Brush, "Linear", { 0: "#C4ECFF", 1: "#70D4FF" });
var greengrad = $(go.Brush, "Linear", { 0: "#B1E2A5", 1: "#7AE060" });
// each action is represented by a shape and some text
var actionTemplate =
$(go.Panel, "Horizontal",
$(go.Shape,
{ width: 12, height: 12 },
new go.Binding("figure"),
new go.Binding("fill")
),
$(go.TextBlock,
{ font: "10pt Verdana, sans-serif" },
new go.Binding("text")
)
);
// each regular Node has body consisting of a title followed by a collapsible list of actions,
// controlled by a PanelExpanderButton, with a TreeExpanderButton underneath the body
myDiagram.nodeTemplate = // the default node template
$(go.Node, "Vertical",
new go.Binding("isTreeExpanded").makeTwoWay(), // remember the expansion state for
new go.Binding("wasTreeExpanded").makeTwoWay(), // when the model is re-loaded
{ selectionObjectName: "BODY" },
// the main "BODY" consists of a RoundedRectangle surrounding nested Panels
$(go.Panel, "Auto",
{ name: "BODY" },
$(go.Shape, "Rectangle",
{ fill: bluegrad, stroke: null }
),
$(go.Panel, "Vertical",
{ margin: 3 },
// the title
$(go.TextBlock,
{
stretch: go.GraphObject.Horizontal,
font: "bold 12pt Verdana, sans-serif"
},
new go.Binding("text", "question")
),
// the optional list of actions
$(go.Panel, "Vertical",
{ stretch: go.GraphObject.Horizontal, visible: false }, // not visible unless there is more than one action
new go.Binding("visible", "actions", function(acts) {
return (Array.isArray(acts) && acts.length > 0);
}),
// headered by a label and a PanelExpanderButton inside a Table
$(go.Panel, "Table",
{ stretch: go.GraphObject.Horizontal },
$(go.TextBlock, "Choices",
{
alignment: go.Spot.Left,
font: "10pt Verdana, sans-serif"
}
),
$("PanelExpanderButton", "COLLAPSIBLE", // name of the object to make visible or invisible
{ column: 1, alignment: go.Spot.Right }
)
), // end Table panel
// with the list data bound in the Vertical Panel
$(go.Panel, "Vertical",
{
name: "COLLAPSIBLE", // identify to the PanelExpanderButton
padding: 2,
stretch: go.GraphObject.Horizontal, // take up whole available width
background: "white", // to distinguish from the node's body
defaultAlignment: go.Spot.Left, // thus no need to specify alignment on each element
itemTemplate: actionTemplate // the Panel created for each item in Panel.itemArray
},
new go.Binding("itemArray", "actions") // bind Panel.itemArray to nodedata.actions
) // end action list Vertical Panel
) // end optional Vertical Panel
) // end outer Vertical Panel
), // end "BODY" Auto Panel
$(go.Panel, // this is underneath the "BODY"
{ height: 17 }, // always this height, even if the TreeExpanderButton is not visible
$("TreeExpanderButton")
)
);
// define a second kind of Node:
myDiagram.nodeTemplateMap.add("Terminal",
$(go.Node, "Spot",
$(go.Shape, "Circle",
{ width: 55, height: 55, fill: greengrad, stroke: null }
),
$(go.TextBlock,
{ font: "10pt Verdana, sans-serif" },
new go.Binding("text")
)
)
);
myDiagram.linkTemplate =
$(go.Link, go.Link.Orthogonal,
{ deletable: false, corner: 10 },
$(go.Shape,
{ strokeWidth: 2 }
),
$(go.TextBlock, go.Link.OrientUpright,
{
background: "white",
visible: false, // unless the binding sets it to true for a non-empty string
segmentIndex: -2,
segmentOrientation: go.Link.None
},
new go.Binding("text", "answer"),
// hide empty string;
// if the "answer" property is undefined, visible is false due to above default setting
new go.Binding("visible", "answer", function(a) { return (a ? true : false); })
)
);
var nodeDataArray = [
{
key: 1, question: "Greeting",
actions: [
{ text: "Sales", figure: "ElectricalHazard", fill: "blue" },
{ text: "Parts and Services", figure: "FireHazard", fill: "red" },
{ text: "Representative", figure: "IrritationHazard", fill: "yellow" }
]
},
{
key: 2, question: "Sales",
actions: [
{ text: "Compact", figure: "ElectricalHazard", fill: "blue" },
{ text: "Mid-Size", figure: "FireHazard", fill: "red" },
{ text: "Large", figure: "IrritationHazard", fill: "yellow" }
]
},
{
key: 3, question: "Parts and Services",
actions: [
{ text: "Maintenance", figure: "ElectricalHazard", fill: "blue" },
{ text: "Repairs", figure: "FireHazard", fill: "red" },
{ text: "State Inspection", figure: "IrritationHazard", fill: "yellow" }
]
},
{ key: 4, question: "Representative" },
{ key: 5, question: "Compact" },
{ key: 6, question: "Mid-Size" },
{
key: 7, question: "Large",
actions: [
{ text: "SUV", figure: "ElectricalHazard", fill: "blue" },
{ text: "Van", figure: "FireHazard", fill: "red" }
]
},
{ key: 8, question: "Maintenance" },
{ key: 9, question: "Repairs" },
{ key: 10, question: "State Inspection" },
{ key: 11, question: "SUV" },
{ key: 12, question: "Van" },
{ key: 13, category: "Terminal", text: "Susan" },
{ key: 14, category: "Terminal", text: "Eric" },
{ key: 15, category: "Terminal", text: "Steven" },
{ key: 16, category: "Terminal", text: "Tom" },
{ key: 17, category: "Terminal", text: "Emily" },
{ key: 18, category: "Terminal", text: "Tony" },
{ key: 19, category: "Terminal", text: "Ken" },
{ key: 20, category: "Terminal", text: "Rachel" }
];
var linkDataArray = [
{ from: 1, to: 2, answer: 1 },
{ from: 1, to: 3, answer: 2 },
{ from: 1, to: 4, answer: 3 },
{ from: 2, to: 5, answer: 1 },
{ from: 2, to: 6, answer: 2 },
{ from: 2, to: 7, answer: 3 },
{ from: 3, to: 8, answer: 1 },
{ from: 3, to: 9, answer: 2 },
{ from: 3, to: 10, answer: 3 },
{ from: 7, to: 11, answer: 1 },
{ from: 7, to: 12, answer: 2 },
{ from: 5, to: 13 },
{ from: 6, to: 14 },
{ from: 11, to: 15 },
{ from: 12, to: 16 },
{ from: 8, to: 17 },
{ from: 9, to: 18 },
{ from: 10, to: 19 },
{ from: 4, to: 20 }
];
// create the Model with the above data, and assign to the Diagram
myDiagram.model = $(go.GraphLinksModel,
{
copiesArrays: true,
copiesArrayObjects: true,
nodeDataArray: nodeDataArray,
linkDataArray: linkDataArray
});
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:500px"></div>
<p>
An <em>IVR tree</em>, or Interactive Voice Response Tree, is typically used by
automated answering systems to direct calls to the correct party. This particular example
is for a car dealership to route calls.
</p>
<p>
This Interactive Voice Response Tree (IVR tree) has nodes that contain a collapsible list of actions, controlled by a <b>PanelExpanderButton</b>,
with a <b>TreeExpanderButton</b> underneath the body. See the <a href="../intro/buttons.html">Intro page on Buttons</a> for more GoJS button information.
</p>
</div>
</body>
</html>
+196
View File
@@ -0,0 +1,196 @@
<!DOCTYPE html>
<html>
<head>
<title>PERT chart</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A PERT chart: a diagram for visualizing and analyzing task dependencies and bottlenecks." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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 more concise visual tree definitions
// colors used, named for easier identification
var blue = "#0288D1";
var pink = "#B71C1C";
var pinkfill = "#F8BBD0";
var bluefill = "#B3E5FC";
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
initialAutoScale: go.Diagram.Uniform,
layout: $(go.LayeredDigraphLayout)
});
// The node template shows the activity name in the middle as well as
// various statistics about the activity, all surrounded by a border.
// The border's color is determined by the node data's ".critical" property.
// Some information is not available as properties on the node data,
// but must be computed -- we use converter functions for that.
myDiagram.nodeTemplate =
$(go.Node, "Auto",
$(go.Shape, "Rectangle", // the border
{ fill: "white", strokeWidth: 2 },
new go.Binding("fill", "critical", function(b) { return (b ? pinkfill : bluefill); }),
new go.Binding("stroke", "critical", function(b) { return (b ? pink : blue); })),
$(go.Panel, "Table",
{ padding: 0.5 },
$(go.RowColumnDefinition, { column: 1, separatorStroke: "black" }),
$(go.RowColumnDefinition, { column: 2, separatorStroke: "black" }),
$(go.RowColumnDefinition, { row: 1, separatorStroke: "black", background: "white", coversSeparators: true }),
$(go.RowColumnDefinition, { row: 2, separatorStroke: "black" }),
$(go.TextBlock, // earlyStart
new go.Binding("text", "earlyStart"),
{ row: 0, column: 0, margin: 5, textAlign: "center" }),
$(go.TextBlock,
new go.Binding("text", "length"),
{ row: 0, column: 1, margin: 5, textAlign: "center" }),
$(go.TextBlock, // earlyFinish
new go.Binding("text", "",
function(d) { return (d.earlyStart + d.length).toFixed(2); }),
{ row: 0, column: 2, margin: 5, textAlign: "center" }),
$(go.TextBlock,
new go.Binding("text", "text"),
{
row: 1, column: 0, columnSpan: 3, margin: 5,
textAlign: "center", font: "bold 14px sans-serif"
}),
$(go.TextBlock, // lateStart
new go.Binding("text", "",
function(d) { return (d.lateFinish - d.length).toFixed(2); }),
{ row: 2, column: 0, margin: 5, textAlign: "center" }),
$(go.TextBlock, // slack
new go.Binding("text", "",
function(d) { return (d.lateFinish - (d.earlyStart + d.length)).toFixed(2); }),
{ row: 2, column: 1, margin: 5, textAlign: "center" }),
$(go.TextBlock, // lateFinish
new go.Binding("text", "lateFinish"),
{ row: 2, column: 2, margin: 5, textAlign: "center" })
) // end Table Panel
); // end Node
// The link data object does not have direct access to both nodes
// (although it does have references to their keys: .from and .to).
// This conversion function gets the GraphObject that was data-bound as the second argument.
// From that we can get the containing Link, and then the Link.fromNode or .toNode,
// and then its node data, which has the ".critical" property we need.
//
// But note that if we were to dynamically change the ".critical" property on a node data,
// calling myDiagram.model.updateTargetBindings(nodedata) would only update the color
// of the nodes. It would be insufficient to change the appearance of any Links.
function linkColorConverter(linkdata, elt) {
var link = elt.part;
if (!link) return blue;
var f = link.fromNode;
if (!f || !f.data || !f.data.critical) return blue;
var t = link.toNode;
if (!t || !t.data || !t.data.critical) return blue;
return pink; // when both Link.fromNode.data.critical and Link.toNode.data.critical
}
// The color of a link (including its arrowhead) is red only when both
// connected nodes have data that is ".critical"; otherwise it is blue.
// This is computed by the binding converter function.
myDiagram.linkTemplate =
$(go.Link,
{ toShortLength: 6, toEndSegmentLength: 20 },
$(go.Shape,
{ strokeWidth: 4 },
new go.Binding("stroke", "", linkColorConverter)),
$(go.Shape, // arrowhead
{ toArrow: "Triangle", stroke: null, scale: 1.5 },
new go.Binding("fill", "", linkColorConverter))
);
// here's the data defining the graph
var nodeDataArray = [
{ key: 1, text: "Start", length: 0, earlyStart: 0, lateFinish: 0, critical: true },
{ key: 2, text: "a", length: 4, earlyStart: 0, lateFinish: 4, critical: true },
{ key: 3, text: "b", length: 5.33, earlyStart: 0, lateFinish: 9.17, critical: false },
{ key: 4, text: "c", length: 5.17, earlyStart: 4, lateFinish: 9.17, critical: true },
{ key: 5, text: "d", length: 6.33, earlyStart: 4, lateFinish: 15.01, critical: false },
{ key: 6, text: "e", length: 5.17, earlyStart: 9.17, lateFinish: 14.34, critical: true },
{ key: 7, text: "f", length: 4.5, earlyStart: 10.33, lateFinish: 19.51, critical: false },
{ key: 8, text: "g", length: 5.17, earlyStart: 14.34, lateFinish: 19.51, critical: true },
{ key: 9, text: "Finish", length: 0, earlyStart: 19.51, lateFinish: 19.51, critical: true }
];
var linkDataArray = [
{ from: 1, to: 2 },
{ from: 1, to: 3 },
{ from: 2, to: 4 },
{ from: 2, to: 5 },
{ from: 3, to: 6 },
{ from: 4, to: 6 },
{ from: 5, to: 7 },
{ from: 6, to: 8 },
{ from: 7, to: 9 },
{ from: 8, to: 9 }
];
myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
// create an unbound Part that acts as a "legend" for the diagram
myDiagram.add(
$(go.Node, "Auto",
$(go.Shape, "Rectangle", // the border
{ fill: bluefill }),
$(go.Panel, "Table",
$(go.RowColumnDefinition, { column: 1, separatorStroke: "black" }),
$(go.RowColumnDefinition, { column: 2, separatorStroke: "black" }),
$(go.RowColumnDefinition, { row: 1, separatorStroke: "black", background: bluefill, coversSeparators: true }),
$(go.RowColumnDefinition, { row: 2, separatorStroke: "black" }),
$(go.TextBlock, "Early Start",
{ row: 0, column: 0, margin: 5, textAlign: "center" }),
$(go.TextBlock, "Length",
{ row: 0, column: 1, margin: 5, textAlign: "center" }),
$(go.TextBlock, "Early Finish",
{ row: 0, column: 2, margin: 5, textAlign: "center" }),
$(go.TextBlock, "Activity Name",
{
row: 1, column: 0, columnSpan: 3, margin: 5,
textAlign: "center", font: "bold 14px sans-serif"
}),
$(go.TextBlock, "Late Start",
{ row: 2, column: 0, margin: 5, textAlign: "center" }),
$(go.TextBlock, "Slack",
{ row: 2, column: 1, margin: 5, textAlign: "center" }),
$(go.TextBlock, "Late Finish",
{ row: 2, column: 2, margin: 5, textAlign: "center" })
) // end Table Panel
));
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:400px"></div>
<p>
This sample demonstrates how to create a simple PERT chart. A PERT chart is a project management tool used to schedule and coordinate tasks within a project.
</p>
<p>
Each node represents an activity and displays several pieces of information about each one.
The node template is basically a <a>Panel</a> of type <a>Panel,Table</a> holding several <a>TextBlock</a>s
that are data-bound to properties of the Activity, all surrounded by a rectangular border.
The lines separating the text are implemented by setting the <a>RowColumnDefinition.separatorStroke</a>
for two columns and two rows. The separators are not seen in the middle because the middle row
of each node has its <a>RowColumnDefinition.background</a> set to white,
and <a>RowColumnDefinition.coversSeparators</a> set to true.
</p>
<p>
The "critical" property on the activity data object controls whether the node is drawn with a red brush or a blue one.
There is a special converter that is used to determine the brush used by the links.
</p>
<p>
The light blue legend is implemented by a separate Part implemented in a manner similar to the Node template.
However it is not bound to data -- there is no JavaScript object in the model representing the legend.
</p>
</div>
</body>
</html>
+108
View File
@@ -0,0 +1,108 @@
<!DOCTYPE html>
<html>
<head>
<title>Absolute positioning within the viewport</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A diagram that does not scroll or zoom and has a fixed area in which to move parts." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
fixedBounds: new go.Rect(0, 0, 500, 300), // document is always 500x300 units
allowHorizontalScroll: false, // disallow scrolling or panning
allowVerticalScroll: false,
allowZoom: false, // disallow zooming
"animationManager.isEnabled": false,
"undoManager.isEnabled": true,
"ModelChanged": function(e) { // just for demonstration purposes,
if (e.isTransactionFinished) { // show the model data in the page's TextArea
document.getElementById("mySavedModel").textContent = e.model.toJson();
}
}
});
// the background Part showing the fixed bounds of the diagram contents
myDiagram.add(
$(go.Part,
{ layerName: "Grid", position: myDiagram.fixedBounds.position },
$(go.Shape, { fill: "oldlace", strokeWidth: 0, desiredSize: myDiagram.fixedBounds.size })
));
// this function is the Node.dragComputation, to limit the movement of the parts
// use GRIDPT instead of PT if DraggingTool.isGridSnapEnabled and movement should snap to grid
function stayInFixedArea(part, pt, gridpt) {
var diagram = part.diagram;
if (diagram === null) return pt;
// compute the document area without padding
var v = diagram.documentBounds.copy();
v.subtractMargin(diagram.padding);
// get the bounds of the part being dragged
var b = part.actualBounds;
var loc = part.location;
// now limit the location appropriately
var x = Math.max(v.x, Math.min(pt.x, v.right - b.width)) + (loc.x - b.x);
var y = Math.max(v.y, Math.min(pt.y, v.bottom - b.height)) + (loc.y - b.y);
return new go.Point(x, y);
}
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{ dragComputation: stayInFixedArea },
// get the size from the model data
new go.Binding("desiredSize", "size", go.Size.parse),
// get and set the position in the model data
new go.Binding("position", "pos", go.Point.parse).makeTwoWay(go.Point.stringify),
// temporarily put selected nodes in Foreground layer
new go.Binding("layerName", "isSelected", function(s) { return s ? "Foreground" : ""; }).ofObject(),
$(go.Shape, "Rectangle",
{ strokeWidth: 0 }, // avoid extra thickness from the stroke
new go.Binding("fill", "color")),
$(go.TextBlock,
new go.Binding("text", "color"))
);
myDiagram.model = new go.GraphLinksModel([
{ "key": "Alpha", "pos": "0 0", "size": "50 50", "color": "lightblue" },
{ "key": "Beta", "pos": "276 19", "size": "100 100", "color": "orange" },
{ "key": "Gamma", "pos": "44 214", "size": "100 50", "color": "lightgreen" },
{ "key": "Delta", "pos": "239 171", "size": "50 100", "color": "pink" }
],
[
{ from: "Alpha", to: "Beta" },
{ from: "Alpha", to: "Gamma" },
{ from: "Gamma", to: "Delta" },
{ from: "Delta", to: "Alpha" }
]);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="width:100%; height:400px"></div>
<p>
Absolute positioning within the viewport, with no scrolling (or panning) or zooming allowed.
</p>
<p>
There is a special colored background Part that shows the fixed area where Parts may be.
It is in the "Grid" Layer so that it is not selectable and is always behind the regular Parts.
</p>
<p>
Parts may not be dragged outside of the fixed document area of the diagram.
This is implemented by a custom <a>Part.dragComputation</a> function.
</p>
<p>
Note that the user may still scroll or zoom the whole page.
</p>
<p>The model data, automatically updated after each change or undo or redo:</p>
<textarea id="mySavedModel" style="width:100%;height:250px"></textarea>
</div>
</body>
</html>
+264
View File
@@ -0,0 +1,264 @@
<!DOCTYPE html>
<html>
<head>
<title>Add or Remove Columns</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Interactively adding, resizing, and removing rows and columns of a Table Panel in a GoJS Node." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
"undoManager.isEnabled": true
});
myDiagram.nodeTemplate =
$(go.Node, "Auto",
$(go.Shape, { fill: "white" }),
$(go.Panel, "Table",
new go.Binding("itemArray", "people"),
$(go.RowColumnDefinition,
{ row: 0, background: "lightgray" }),
$(go.RowColumnDefinition,
{ row: 1, separatorStroke: "black" }),
// the table headers -- remains even if itemArray is empty
$(go.Panel, "TableRow",
{ isPanelMain: true },
new go.Binding("itemArray", "columnDefinitions"),
{
itemTemplate: // bound to a column definition object
$(go.Panel,
new go.Binding("column"),
$(go.TextBlock,
{ margin: new go.Margin(2, 2, 0, 2), font: "bold 10pt sans-serif" },
new go.Binding("text"))
)
}
),
{ // the rows for the people
defaultAlignment: go.Spot.Left,
defaultColumnSeparatorStroke: "black",
itemTemplate: // bound to a person/row data object
$(go.Panel, "TableRow",
// which in turn consists of a collection of cell objects,
// held by the "columns" property in an Array
new go.Binding("itemArray", "columns"),
// you could also have other Bindings here for the whole row
{
itemTemplate: // bound to a cell object
$(go.Panel, // each of which as "attr" and "text" properties
{ stretch: go.GraphObject.Fill, alignment: go.Spot.TopLeft },
new go.Binding("column", "attr",
function(a, elt) { // ELT is this bound item/cell Panel
// elt.data will be the cell object
// elt.panel.data will be the person/row data object
// elt.part.data will be the node data object
// "columnDefinitions" is on the node data object, so:
var cd = findColumnDefinitionForName(elt.part.data, a);
if (cd !== null) return cd.column;
throw new Error("unknown column name: " + a);
}),
// you could also have other Bindings here for this cell
$(go.TextBlock, { editable: true },
{ margin: new go.Margin(2, 2, 0, 2), wrap: go.TextBlock.None },
new go.Binding("text").makeTwoWay())
)
}
)
}
)
);
myDiagram.model =
$(go.GraphLinksModel,
{
copiesArrays: true,
copiesArrayObjects: true,
nodeDataArray: [
{ // first node
key: 1,
columnDefinitions: [
// each column definition needs to specify the column used
{ attr: "name", text: "Name", column: 0 },
{ attr: "phone", text: "Phone #", column: 1 },
{ attr: "office", text: "Office", column: 2 }
],
people: [ // the table of people
// each row is a person with an Array of Objects associating a column name with a text value
{ columns: [{ attr: "name", text: "Alice" }, { attr: "phone", text: "2345" }, { attr: "office", text: "C4-E18" }] },
{ columns: [{ attr: "name", text: "Bob" }, { attr: "phone", text: "9876" }, { attr: "office", text: "E1-B34" }] },
{ columns: [{ attr: "name", text: "Carol" }, { attr: "phone", text: "1111" }, { attr: "office", text: "C4-E23" }] },
{ columns: [{ attr: "name", text: "Ted" }, { attr: "phone", text: "2222" }, { attr: "office", text: "C4-E197" }] }
]
},
{ // second node
key: 2,
columnDefinitions: [
{ attr: "name", text: "Name", column: 0 },
{ attr: "phone", text: "Phone #", column: 2 }, // note the different order of columns
{ attr: "office", text: "Office", column: 1 }
],
people: [
{ columns: [{ attr: "name", text: "Robert" }, { attr: "phone", text: "5656" }, { attr: "office", text: "B1-A27" }] },
{ columns: [{ attr: "name", text: "Natalie" }, { attr: "phone", text: "5698" }, { attr: "office", text: "B1-B6" }] }
]
}
],
linkDataArray: [
{ from: 1, to: 2 }
]
}
);
}
// Add or remove a person row from the selected node's table of people.
function insertIntoArray() {
var n = myDiagram.selection.first();
if (n === null) return;
var d = n.data;
myDiagram.startTransaction("insertIntoTable");
// add item as second in the list, at index #1
// of course this new data could be more realistic:
myDiagram.model.insertArrayItem(d.people, 1, {
columns: [{ attr: "name", text: "Elena" },
{ attr: "phone", text: "456" },
{ attr: "office", text: "LA" }]
});
myDiagram.commitTransaction("insertIntoTable");
}
function removeFromArray() {
var n = myDiagram.selection.first();
if (n === null) return;
var d = n.data;
myDiagram.startTransaction("removeFromTable");
// remove second item of list, at index #1
myDiagram.model.removeArrayItem(d.people, 1);
myDiagram.commitTransaction("removeFromTable");
}
// add or remove a column from the selected node's table of people
function findColumnDefinitionForName(nodedata, attrname) {
var columns = nodedata.columnDefinitions;
for (var i = 0; i < columns.length; i++) {
if (columns[i].attr === attrname) return columns[i];
}
return null;
}
function findColumnDefinitionForColumn(nodedata, idx) {
var columns = nodedata.columnDefinitions;
for (var i = 0; i < columns.length; i++) {
if (columns[i].column === idx) return columns[i];
}
return null;
}
function addColumn(attrname) {
var n = myDiagram.selection.first();
if (n === null) return;
var d = n.data;
// if name is not given, find an unused column name
if (attrname === undefined || attrname === "") {
attrname = "new";
var count = 1;
while (findColumnDefinitionForName(d, attrname) !== null) {
attrname = "new" + (count++).toString();
}
}
// find an unused column #
var col = 3;
while (findColumnDefinitionForColumn(d, col) !== null) {
col++;
}
myDiagram.startTransaction("addColumn");
var model = myDiagram.model;
// add a column definition for the node's whole table
model.addArrayItem(d.columnDefinitions, {
attr: attrname,
text: attrname,
column: col
});
// add cell to each person in the node's table of people
var people = d.people;
for (var j = 0; j < people.length; j++) {
var person = people[j];
model.addArrayItem(person.columns, {
attr: attrname,
text: Math.floor(Math.random() * 1000).toString()
});
}
myDiagram.commitTransaction("addColumn");
}
function removeColumn() {
var n = myDiagram.selection.first();
if (n === null) return;
var d = n.data;
var coldef = d.columnDefinitions[3]; // get the fourth column
if (coldef === undefined) return;
var attrname = coldef.attr;
myDiagram.startTransaction("removeColumn");
var model = myDiagram.model;
model.removeArrayItem(d.columnDefinitions, 3);
// update columns for each person in this table
var people = d.people;
for (var j = 0; j < people.length; j++) {
var person = people[j];
var columns = person.columns;
for (var k = 0; k < columns.length; k++) {
var cell = columns[k];
if (cell.attr === attrname) {
// get rid of this attribute cell from the person.columns Array
model.removeArrayItem(columns, k);
break;
}
}
}
myDiagram.commitTransaction("removeColumn");
}
function swapTwoColumns() {
myDiagram.startTransaction("swapColumns");
var model = myDiagram.model;
myDiagram.selection.each(function(n) {
if (!(n instanceof go.Node)) return;
var d = n.data;
var phonedef = findColumnDefinitionForName(d, "phone");
if (phonedef === null) return;
var phonecolumn = phonedef.column; // remember the column number
var officedef = findColumnDefinitionForName(d, "office");
if (officedef === null) return;
var officecolumn = officedef.column; // and this one too
model.setDataProperty(phonedef, "column", officecolumn);
model.setDataProperty(officedef, "column", phonecolumn);
model.updateTargetBindings(d); // update all bindings, to get the cells right
});
myDiagram.commitTransaction("swapColumns");
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:400px"></div>
<p>Add a row or Remove the second row of the table held by the selected node:</p>
<button onclick="insertIntoArray()">Insert Into Array</button>
<button onclick="removeFromArray()">Remove From Array</button>
<p>Add a column or Remove the fourth column from the table of the selected node:</p>
<button onclick="addColumn()">Add Column</button>
<button onclick="removeColumn()">Remove Column</button>
<p>Swap the "phone" and "office" columns for each selected node:</p>
<button onclick="swapTwoColumns()">Swap Two Columns</button>
<p>See also the <a href="../extensions/ColumnResizing.html">Column and Row Resizing Tools</a></p>
</div>
</body>
</html>
+175
View File
@@ -0,0 +1,175 @@
<!DOCTYPE html>
<html>
<head>
<title>Adding a Custom Node to a Palette</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="An example of having the user customize a Palette by adding copies of Diagram nodes to the Palette's Model." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../extensions/Figures.js"></script>
<link rel='stylesheet' href='../extensions/DataInspector.css' />
<script src="../extensions/DataInspector.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;
// initialize main Diagram
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
"undoManager.isEnabled": true
});
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{ locationSpot: go.Spot.Center },
new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify),
$(go.Shape, "Circle",
{
fill: "white", stroke: "gray", strokeWidth: 2,
portId: "", fromLinkable: true, toLinkable: true,
fromLinkableDuplicates: true, toLinkableDuplicates: true,
fromLinkableSelfNode: true, toLinkableSelfNode: true
},
new go.Binding("stroke", "color"),
new go.Binding("figure")),
$(go.TextBlock,
{
margin: new go.Margin(5, 5, 3, 5), font: "10pt sans-serif",
minSize: new go.Size(16, 16), maxSize: new go.Size(120, NaN),
textAlign: "center", editable: true
},
new go.Binding("text").makeTwoWay())
);
// initialize Palette
myPalette =
$(go.Palette, "myPaletteDiv",
{
nodeTemplate: myDiagram.nodeTemplate,
contentAlignment: go.Spot.Center,
layout:
$(go.GridLayout,
{ wrappingColumn: 1, cellSize: new go.Size(2, 2) }),
"ModelChanged": function(e) { // just for demonstration purposes,
if (e.isTransactionFinished) { // show the model data in the page's TextArea
document.getElementById("mySavedPaletteModel").textContent = e.model.toJson();
}
}
});
// now add the initial contents of the Palette
myPalette.model.nodeDataArray = [
{ text: "Circle", color: "blue", figure: "Circle" },
{ text: "Square", color: "purple", figure: "Square" },
{ text: "Ellipse", color: "orange", figure: "Ellipse" },
{ text: "Rectangle", color: "red", figure: "Rectangle" },
{ text: "Rounded\nRectangle", color: "green", figure: "RoundedRectangle" },
{ text: "Triangle", color: "purple", figure: "Triangle" },
];
// initialize Overview
myOverview =
$(go.Overview, "myOverviewDiv",
{
observed: myDiagram,
contentAlignment: go.Spot.Center
});
var inspector = new Inspector('myInspectorDiv', myDiagram,
{
// uncomment this line to only inspect the named properties below instead of all properties on each object:
// includesOwnProperties: false,
properties: {
"text": {},
// key would be automatically added for nodes, but we want to declare it read-only also:
"key": { readOnly: true, show: Inspector.showIfPresent },
// color would be automatically added for nodes, but we want to declare it a color also:
"color": { type: 'color' },
"figure": {}
}
});
load();
}
// save a model to and load a model from Json 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);
}
function addToPalette() {
var node = myDiagram.selection.filter(function(p) { return p instanceof go.Node; }).first();
if (node !== null) {
myPalette.startTransaction();
var item = myPalette.model.copyNodeData(node.data);
myPalette.model.addNodeData(item);
myPalette.commitTransaction("added item to palette");
}
}
// The user cannot delete selected nodes in the Palette with the Delete key or Control-X,
// but they can if they do so programmatically.
function removeFromPalette() {
myPalette.commandHandler.deleteSelection();
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div style="width:100%; white-space:nowrap;">
<span style="display: inline-block; vertical-align: top; padding: 2px; width:140px">
<div id="myPaletteDiv" style="background-color: whitesmoke; border: solid 1px black; height: 400px"></div>
<div id="myOverviewDiv" style="border: solid 1px black; height: 100px"></div>
</span>
<span style="display: inline-block; vertical-align: top; padding: 2px; width:500px">
<div id="myDiagramDiv" style="border: solid 1px black; height: 500px"></div>
</span>
<span style="display: inline-block; vertical-align: top; padding: 2px; width:200px">
<div id="myInspectorDiv" class="inspector"></div>
</span>
</div>
<p>
This sample supports the normal kind of drag-and-drop from a <a>Palette</a> to a <a>Diagram</a>.
The Data <a>Inspector</a> allows you to edit the properties of a selected node in the diagram.
</p>
<p>
This sample also supports dynamically adding a copy of a selected node in the diagram to the
palette by the "Add To Palette" button.
See the current state of the palette's model in the top textarea.
The palette is <a>Diagram.isReadOnly</a>, so the user cannot delete selected nodes from the palette.
But the "Delete From Palette" button removes any selected nodes from the palette.
</p>
<div>
<button onclick="addToPalette()">Add To Palette</button>
<button onclick="removeFromPalette()">Delete From Palette</button>
Palette model:
</div>
<textarea id="mySavedPaletteModel" style="width:100%;height:200px"></textarea>
<div>
<button id="loadModel" onclick="load()">Load</button>
<button id="saveModel" onclick="save()">Save</button>
Diagram model:
</div>
<textarea id="mySavedModel" style="width:100%;height:200px">
{ "class": "go.GraphLinksModel",
"nodeDataArray": [
{ "key": 1, "text": "hello", "figure":"Circle", "color":"green", "location":"0 0" },
{ "key": 2, "text": "world", "figure":"Rectangle", "color":"red", "location":"100 0" }
],
"linkDataArray": [
{ "from":1, "to":2 }
]}
</textarea>
</div>
</body>
</html>
+251
View File
@@ -0,0 +1,251 @@
<!DOCTYPE html>
<html>
<head>
<title>Selection Adornment Buttons</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="When a diagram node is selected show a selection Adornment holding buttons on which a click invokes a command or a drag starts a tool">
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
myDiagram = $(go.Diagram, "myDiagramDiv", // create a Diagram for the DIV HTML element
{
"linkingTool.isEnabled": false, // invoked explicitly by drawLink function, below
"linkingTool.direction": go.LinkingTool.ForwardsOnly, // only draw "from" towards "to"
"undoManager.isEnabled": true // enable undo & redo
});
myDiagram.linkTemplate =
$(go.Link,
{ routing: go.Link.AvoidsNodes, corner: 5 },
$(go.Shape, { strokeWidth: 1.5 }),
$(go.Shape, { toArrow: "OpenTriangle" })
);
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{
desiredSize: new go.Size(80, 80),
// rearrange the link points evenly along the sides of the nodes as links are
// drawn or reconnected -- these event handlers only make sense when the fromSpot
// and toSpot are Spot.xxxSides
linkConnected: function(node, link, port) {
if (link.fromNode !== null) link.fromNode.invalidateConnectedLinks();
if (link.toNode !== null) link.toNode.invalidateConnectedLinks();
},
linkDisconnected: function(node, link, port) {
if (link.fromNode !== null) link.fromNode.invalidateConnectedLinks();
if (link.toNode !== null) link.toNode.invalidateConnectedLinks();
},
locationSpot: go.Spot.Center
},
new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify),
$(go.Shape,
{
name: "SHAPE", // named so that changeColor can modify it
strokeWidth: 0, // no border
fill: "lightgray", // default fill color
portId: "",
// use the following property if you want users to draw new links
// interactively by dragging from the Shape, and re-enable the LinkingTool
// in the initialization of the Diagram
//cursor: "pointer",
fromSpot: go.Spot.AllSides, fromLinkable: true,
fromLinkableDuplicates: true, fromLinkableSelfNode: true,
toSpot: go.Spot.AllSides, toLinkable: true,
toLinkableDuplicates: true, toLinkableSelfNode: true
},
new go.Binding("fill", "color").makeTwoWay()),
$(go.TextBlock,
{
name: "TEXTBLOCK", // named so that editText can start editing it
margin: 3,
// use the following property if you want users to interactively start
// editing the text by clicking on it or by F2 if the node is selected:
//editable: true,
overflow: go.TextBlock.OverflowEllipsis,
maxLines: 5
},
new go.Binding("text").makeTwoWay())
);
// a selected node shows an Adornment that includes both a blue border
// and a row of Buttons above the node
myDiagram.nodeTemplate.selectionAdornmentTemplate =
$(go.Adornment, "Spot",
$(go.Panel, "Auto",
$(go.Shape, { stroke: "dodgerblue", strokeWidth: 2, fill: null }),
$(go.Placeholder)
),
$(go.Panel, "Horizontal",
{ alignment: go.Spot.Top, alignmentFocus: go.Spot.Bottom },
$("Button",
{ click: editText }, // defined below, to support editing the text of the node
$(go.TextBlock, "t",
{ font: "bold 10pt sans-serif", desiredSize: new go.Size(15, 15), textAlign: "center" })
),
$("Button",
{ click: changeColor, "_buttonFillOver": "transparent" }, // defined below, to support changing the color of the node
new go.Binding("ButtonBorder.fill", "color", nextColor),
$(go.Shape,
{ fill: null, stroke: null, desiredSize: new go.Size(14, 14) })
),
$("Button",
{ // drawLink is defined below, to support interactively drawing new links
click: drawLink, // click on Button and then click on target node
actionMove: drawLink // drag from Button to the target node
},
$(go.Shape,
{ geometryString: "M0 0 L8 0 8 12 14 12 M12 10 L14 12 12 14" })
),
$("Button",
{
actionMove: dragNewNode, // defined below, to support dragging from the button
_dragData: { text: "a Node", color: "lightgray" }, // node data to copy
click: clickNewNode // defined below, to support a click on the button
},
$(go.Shape,
{ geometryString: "M0 0 L3 0 3 10 6 10 x F1 M6 6 L14 6 14 14 6 14z", fill: "gray" })
)
)
);
function editText(e, button) {
var node = button.part.adornedPart;
e.diagram.commandHandler.editTextBlock(node.findObject("TEXTBLOCK"));
}
// used by nextColor as the list of colors through which we rotate
var myColors = ["lightgray", "lightblue", "lightgreen", "yellow", "orange", "pink"];
// used by both the Button Binding and by the changeColor click function
function nextColor(c) {
var idx = myColors.indexOf(c);
if (idx < 0) return "lightgray";
if (idx >= myColors.length - 1) idx = 0;
return myColors[idx + 1];
}
function changeColor(e, button) {
var node = button.part.adornedPart;
var shape = node.findObject("SHAPE");
if (shape === null) return;
node.diagram.startTransaction("Change color");
shape.fill = nextColor(shape.fill);
button["_buttonFillNormal"] = nextColor(shape.fill); // update the button too
node.diagram.commitTransaction("Change color");
}
function drawLink(e, button) {
var node = button.part.adornedPart;
var tool = e.diagram.toolManager.linkingTool;
tool.startObject = node.port;
e.diagram.currentTool = tool;
tool.doActivate();
}
// used by both clickNewNode and dragNewNode to create a node and a link
// from a given node to the new node
function createNodeAndLink(data, fromnode) {
var diagram = fromnode.diagram;
var model = diagram.model;
var nodedata = model.copyNodeData(data);
model.addNodeData(nodedata);
var newnode = diagram.findNodeForData(nodedata);
var linkdata = model.copyLinkData({});
model.setFromKeyForLinkData(linkdata, model.getKeyForNodeData(fromnode.data));
model.setToKeyForLinkData(linkdata, model.getKeyForNodeData(newnode.data));
model.addLinkData(linkdata);
diagram.select(newnode);
return newnode;
}
// the Button.click event handler, called when the user clicks the "N" button
function clickNewNode(e, button) {
var data = button._dragData;
if (!data) return;
e.diagram.startTransaction("Create Node and Link");
var fromnode = button.part.adornedPart;
var newnode = createNodeAndLink(button._dragData, fromnode);
newnode.location = new go.Point(fromnode.location.x + 200, fromnode.location.y);
e.diagram.commitTransaction("Create Node and Link");
}
// the Button.actionMove event handler, called when the user drags within the "N" button
function dragNewNode(e, button) {
var tool = e.diagram.toolManager.draggingTool;
if (tool.isBeyondDragSize()) {
var data = button._dragData;
if (!data) return;
e.diagram.startTransaction("button drag"); // see doDeactivate, below
var newnode = createNodeAndLink(data, button.part.adornedPart);
newnode.location = e.diagram.lastInput.documentPoint;
// don't commitTransaction here, but in tool.doDeactivate, after drag operation finished
// set tool.currentPart to a selected movable Part and then activate the DraggingTool
tool.currentPart = newnode;
e.diagram.currentTool = tool;
tool.doActivate();
}
}
// using dragNewNode also requires modifying the standard DraggingTool so that it
// only calls commitTransaction when dragNewNode started a "button drag" transaction;
// do this by overriding DraggingTool.doDeactivate:
var tool = myDiagram.toolManager.draggingTool;
tool.doDeactivate = function() {
// commit "button drag" transaction, if it is ongoing; see dragNewNode, above
if (tool.diagram.undoManager.nestedTransactionNames.elt(0) === "button drag") {
tool.diagram.commitTransaction();
}
go.DraggingTool.prototype.doDeactivate.call(tool); // call the base method
};
myDiagram.model = new go.GraphLinksModel(
[
{ key: 1, text: "Alpha", color: "lightblue", location: "0 0" },
{ key: 2, text: "Beta", color: "orange", location: "140 0" },
{ key: 3, text: "Gamma", color: "lightgreen", location: "0 140" },
{ key: 4, text: "Delta", color: "pink", location: "140 140" }
],
[
{ from: 1, to: 2 }
]);
myDiagram.findNodeForKey(4).isSelected = true;
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
<p>
The node template uses a custom <a>Part.selectionAdornmentTemplate</a> to
add a row of Buttons when the node is selected.
Select a node and you will see the Buttons for the node.
</p>
<p>
The first button, "T", when clicked, starts in-place editing of the text.
</p>
<p>
The second button, "C", when clicked, changes the color of the node,
rotating through a list of colors.
</p>
<p>
The third button, "L", when clicked or dragged, starts the <a>LinkingTool</a>,
drawing a new link starting at the selected node.
</p>
<p>
The fourth button, "N", when clicked, adds a new node and creates a link from
the selected node to the new node.
Dragging from the fourth button does the same thing as a click but also activates
the <a>DraggingTool</a>, allowing the user to drag the new node where they like.
</p>
</div>
</body>
</html>
+302
View File
@@ -0,0 +1,302 @@
<!DOCTYPE html>
<html>
<head>
<title>All GoJS Samples</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Alphabetical list of all GoJS samples." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="../assets/css/main.css" rel="stylesheet" type="text/css" /> <!-- you don't need to use this -->
</head>
<body>
<!-- When adding a new sample, add it to the samples/all.html list. -->
<!-- If you want the sample to show up at samples/index.html, add it to samples/indexList.js and add a screenshot in assets/images/screenshots. -->
<!-- Consider adding it to assets/js/goSamples.js, if you want a sample to show up in the left side navigation bar. -->
<!-- And don't forget to mention it in changelog.html. -->
<h3>Samples and Extensions</h3>
<p>
Below is a list of every <a href="../index.html">GoJS</a> sample and extension.
See the <a href="index.html">samples index</a> for a subset of this list with screenshots and short descriptions.
</p>
<h3>Samples</h3>
<p>
These are in the <code>samples/</code> directory.
Most are stand-alone apps, but a few depend on extensions and/or on common third-party libraries.
Remember that the GoJS library does not depend on any other library.
</p>
<ul>
<li><a href="absolute.html">absolute.html</a></li>
<li><a href="addRemoveColumns.html">addRemoveColumns.html</a></li>
<li><a href="addToPalette.html">addToPalette.html</a></li>
<li><a href="adornmentButtons.html">adornmentButtons.html</a></li>
<li><a href="animatedFocus.html">animatedFocus.html</a></li>
<li><a href="arrowheads.html">arrowheads.html</a></li>
<li><a href="barCharts.html">barCharts.html</a></li>
<li><a href="basic.html">basic.html</a></li>
<li><a href="beatPaths.html">beatPaths.html</a></li>
<li><a href="belts.html">belts.html</a></li>
<li><a href="blockEditor.html">blockEditor.html</a></li>
<li><a href="candlestickCharts.html">candlestickCharts.html</a></li>
<li><a href="canvases.html">canvases.html</a></li>
<li><a href="classHierarchy.html">classHierarchy.html</a></li>
<li><a href="cLayout.html">cLayout.html</a></li>
<li><a href="comments.html">comments.html</a></li>
<li><a href="conceptMap.html">conceptMap.html</a></li>
<li><a href="connectionBoxNode.html">connectionBoxNode.html</a></li>
<li><a href="constantSize.html">constantSize.html</a></li>
<li><a href="contentAlign.html">contentAlign.html</a></li>
<li><a href="controlGauges.html">controlGauges.html</a></li>
<li><a href="curviness.html">curviness.html</a></li>
<li><a href="customAnimations.html">customAnimations.html</a></li>
<li><a href="customContextMenu.html">customContextMenu.html</a></li>
<li><a href="customExpandCollapse.html">customExpandCollapse.html</a></li>
<li><a href="customTextEditingTool.html">customTextEditingTool.html</a></li>
<li><a href="dataFlow.html">dataFlow.html</a></li>
<li><a href="dataFlowVertical.html">dataFlowVertical.html</a></li>
<li><a href="dataVisualization.html">dataVisualization.html</a></li>
<li><a href="decisionTree.html">decisionTree.html</a></li>
<li><a href="distances.html">distances.html</a></li>
<li><a href="DOMTree.html">DOMTree.html</a></li>
<li><a href="donutCharts.html">donutCharts.html</a></li>
<li><a href="doubleCircle.html">doubleCircle.html</a></li>
<li><a href="doubleTree.html">doubleTree.html</a></li>
<li><a href="dragDropFields.html">dragDropFields.html</a></li>
<li><a href="draggableLink.html">draggableLink.html</a></li>
<li><a href="draggablePorts.html">draggablePorts.html</a></li>
<li><a href="dragOutFields.html">dragOutFields.html</a></li>
<li><a href="dragUnoccupied.html">dragUnoccupied.html</a></li>
<li><a href="dynamicPieChart.html">dynamicPieChart.html</a></li>
<li><a href="dynamicPorts.html">dynamicPorts.html</a></li>
<li><a href="entityRelationship.html">entityRelationship.html</a></li>
<li><a href="euler.html">euler.html</a></li>
<li><a href="familyTree.html">familyTree.html</a></li>
<li><a href="familyTreeJP.html">familyTreeJP.html</a></li>
<li><a href="faultTree.html">faultTree.html</a></li>
<li><a href="fdLayout.html">fdLayout.html</a></li>
<li><a href="flowBuilder.html">flowBuilder.html</a></li>
<li><a href="flowchart.html">flowchart.html</a></li>
<li><a href="flowgrammer.html">flowgrammer.html</a></li>
<li><a href="friendWheel.html">friendWheel.html</a></li>
<li><a href="gameOfLife.html">gameOfLife.html</a></li>
<li><a href="gantt.html">gantt.html</a></li>
<li><a href="genogram.html">genogram.html</a></li>
<li><a href="gestureBehavior.html">gestureBehavior.html</a></li>
<li><a href="gLayout.html">gLayout.html</a></li>
<li><a href="grafcet.html">grafcet.html</a></li>
<li><a href="grouping.html">grouping.html</a></li>
<li><a href="hoverButtons.html">hoverButtons.html</a></li>
<li><a href="htmlDragDrop.html">htmlDragDrop.html</a></li>
<li><a href="htmlInteraction.html">htmlInteraction.html</a></li>
<li><a href="htmlLightBoxContextMenu.html">htmlLightBoxContextMenu.html</a></li>
<li><a href="icons.html">icons.html</a></li>
<li><a href="incrementalTree.html">incrementalTree.html</a></li>
<li><a href="instrumentGauge.html">instrumentGauge.html</a></li>
<li><a href="interactiveForce.html">interactiveForce.html</a></li>
<li><a href="IVRtree.html">IVRtree.html</a></li>
<li><a href="jQueryDragDrop.html">jQueryDragDrop.html</a></li>
<li><a href="kanban.html">kanban.html</a></li>
<li><a href="kittenMonitor.html">kittenMonitor.html</a></li>
<li><a href="ldLayout.html">ldLayout.html</a></li>
<li><a href="leaflet.html">leaflet.html</a></li>
<li><a href="linksToLinks.html">linksToLinks.html</a></li>
<li><a href="localView.html">localView.html</a></li>
<li><a href="logicCircuit.html">logicCircuit.html</a></li>
<li><a href="macros.html">macros.html</a></li>
<li><a href="magnifier.html">magnifier.html</a></li>
<li><a href="mindMap.html">mindMap.html</a></li>
<li><a href="minimal.html">minimal.html</a></li>
<li><a href="minimalBlob.html">minimalBlob.html</a></li>
<li><a href="minimalSvg.html">minimalSvg.html</a></li>
<li><a href="multiArrow.html">multiArrow.html</a></li>
<li><a href="multiColorLinks.html">multiColorLinks.html</a></li>
<li><a href="multiNodePathLinks.html">multiNodePathLinks.html</a></li>
<li><a href="navigation.html">navigation.html</a></li>
<li><a href="network.html">network.html</a></li>
<li><a href="orgChartAssistants.html">orgChartAssistants.html</a></li>
<li><a href="orgChartEditor.html">orgChartEditor.html</a></li>
<li><a href="orgChartExtras.html">orgChartExtras.html</a></li>
<li><a href="orgChartStatic.html">orgChartStatic.html</a></li>
<li><a href="pageFlow.html">pageFlow.html</a></li>
<li><a href="panelLayout.html">panelLayout.html</a></li>
<li><a href="parseTree.html">parseTree.html</a></li>
<li><a href="pathAnimation.html">pathAnimation.html</a></li>
<li><a href="PERT.html">PERT.html</a></li>
<li><a href="pieCharts.html">pieCharts.html</a></li>
<li><a href="pinchResizing.html">pinchResizing.html</a></li>
<li><a href="pipes.html">pipes.html</a></li>
<li><a href="pipeTree.html">pipeTree.html</a></li>
<li><a href="planogram.html">planogram.html</a></li>
<li><a href="processFlow.html">processFlow.html</a></li>
<li><a href="productionEditor.html">productionEditor.html</a></li>
<li><a href="productionProcess.html">productionProcess.html</a></li>
<li><a href="radial.html">radial.html</a></li>
<li><a href="radialAdornment.html">radialAdornment.html</a></li>
<li><a href="radialPartition.html">radialPartition.html</a></li>
<li><a href="records.html">records.html</a></li>
<li><a href="regrouping.html">regrouping.html</a></li>
<li><a href="regroupingTreeView.html">regroupingTreeView.html</a></li>
<li><a href="relationships.html">relationships.html</a></li>
<li><a href="require.html">require.html</a></li>
<li><a href="roundedGroups.html">roundedGroups.html</a></li>
<li><a href="ruleredDiagram.html">ruleredDiagram.html</a></li>
<li><a href="sankey.html">sankey.html</a></li>
<li><a href="scrollModes.html">scrollModes.html</a></li>
<li><a href="seatingChart.html">seatingChart.html</a></li>
<li><a href="selectableFields.html">selectableFields.html</a></li>
<li><a href="selectablePorts.html">selectablePorts.html</a></li>
<li><a href="sequenceDiagram.html">sequenceDiagram.html</a></li>
<li><a href="sequentialFunction.html">sequentialFunction.html</a></li>
<li><a href="shapes.html">shapes.html</a></li>
<li><a href="sharedStates.html">sharedStates.html</a></li>
<li><a href="shopFloorMonitor.html">shopFloorMonitor.html</a></li>
<li><a href="singlePage.html">singlePage.html</a></li>
<li><a href="spacingZoom.html">spacingZoom.html</a></li>
<li><a href="sparklineGraphs.html">sparklineGraphs.html</a></li>
<li><a href="spreadsheet.html">spreadsheet.html</a></li>
<li><a href="stateChart.html">stateChart.html</a></li>
<li><a href="stateChartIncremental.html">stateChartIncremental.html</a></li>
<li><a href="svgDataUrl.html">svgDataUrl.html</a></li>
<li><a href="swimBands.html">swimBands.html</a></li>
<li><a href="swimLanes.html">swimLanes.html</a></li>
<li><a href="swimLanesVertical.html">swimLanesVertical.html</a></li>
<li><a href="systemDynamics.html">systemDynamics.html</a></li>
<li><a href="tabs.html">tabs.html</a></li>
<li><a href="taperedLinks.html">taperedLinks.html</a></li>
<li><a href="thermometer.html">thermometer.html</a></li>
<li><a href="tiger.html">tiger.html</a></li>
<li><a href="timeline.html">timeline.html</a></li>
<li><a href="tLayout.html">tLayout.html</a></li>
<li><a href="tournament.html">tournament.html</a></li>
<li><a href="treeLoadAnimation.html">treeLoadAnimation.html</a></li>
<li><a href="treeMapper.html">treeMapper.html</a></li>
<li><a href="treeView.html">treeView.html</a></li>
<li><a href="triStateCheckBoxTree.html">triStateCheckBoxTree.html</a></li>
<li><a href="twoDiagrams.html">twoDiagrams.html</a></li>
<li><a href="twoHalves.html">twoHalves.html</a></li>
<li><a href="umlClass.html">umlClass.html</a></li>
<li><a href="updateDemo.html">updateDemo.html</a></li>
<li><a href="virtualized.html">virtualized.html</a></li>
<li><a href="virtualizedForceLayout.html">virtualizedForceLayout.html</a></li>
<li><a href="virtualizedTree.html">virtualizedTree.html</a></li>
<li><a href="virtualizedTreeLayout.html">virtualizedTreeLayout.html</a></li>
<li><a href="visualTree.html">visualTree.html</a></li>
<li><a href="visualTreeGrouping.html">visualTreeGrouping.html</a></li>
<li><a href="vue.html">vue.html</a></li>
<li><a href="wordcloud.html">wordcloud.html</a></li>
</ul>
<h3>Extensions</h3>
<p>
There are three extension directories.
</p>
<ul>
<li><code>extensions/</code>, loadable in a simple &lt;script&gt; tag</li>
<li><code>extensionsTS/</code>, implemented in TypeScript, compiled as a UMD module</li>
<li><code>extensionsJSM/</code>, using <code>go.mjs</code>, compiled as an ES6 module</li>
</ul>
<p>
In all cases you should copy the extension code into your own project and make sure that any <code>require</code> or <code>import</code> is adjusted to fit your environment.
Subtle errors can occur if your app loads the GoJS library more than once, especially if they are of different versions.
</p>
<h4>Layout Extensions:</h4>
<ul>
<li><a href="../extensions/Arranging.html">Arranging.html</a>, using <a href="../extensions/ArrangingLayout.js">ArrangingLayout.js</a></li>
<li><a href="doubleTree.html">DoubleTree.html</a>, using <a href="../extensions/DoubleTreeLayout.js">DoubleTreeLayout.js</a></li>
<li><a href="../extensions/Fishbone.html">Fishbone.html</a>, using <a href="../extensions/FishboneLayout.js">FishboneLayout.js</a></li>
<li><a href="../extensions/PackedHierarchy.html">PackedHierarchy.html, using <a href="../extensionsTS/PackedLayout.js">PackedLayout.js</a></li>
<li><a href="../extensions/PackedLayout.html">PackedLayout.html</a>, using <a href="../extensionsTS/PackedLayout.js">PackedLayout.js</a></li>
<li><a href="../extensions/Parallel.html">Parallel.html</a>, using <a href="../extensions/ParallelLayout.js">ParallelLayout.js</a></li>
<li><a href="../extensions/Serpentine.html">Serpentine.html</a>, using <a href="../extensions/SerpentineLayout.js">SerpentineLayout.js</a></li>
<li><a href="../extensions/Spiral.html">Spiral.html</a>, using <a href="../extensions/SpiralLayout.js">SpiralLayout.js</a></li>
<li><a href="../extensions/SwimLaneLayout.html">SwimLaneLayout.html, using <a href="../extensionsTS/SwimLaneLayout.js">SwimLaneLayout.js</a></li>
<li><a href="../extensions/Table.html">Table.html</a>, using <a href="../extensions/TableLayout.js">TableLayout.js</a></li>
<li><a href="../extensions/TreeMap.html">TreeMap.html</a>, using <a href="../extensions/TreeMapLayout.js">TreeMapLayout.js</a></li>
<li><a href="../extensionsTS/VirtualizedPacked.html">VirtualizedPacked.html</a>, using <a href="../extensionsTS/VirtualizedPackedLayout.js">VirtualizedPackedLayout.js</a></li>
</ul>
<h4>Tool Extensions:</h4>
<ul>
<li><a href="../extensions/ColumnResizing.html">ColumnResizing.html</a>, using <a href="../extensions/ColumnResizingTool.js">ColumnResizingTool.js</a></li>
<li><a href="../extensions/CurvedLinkReshaping.html">CurvedLinkReshaping.html</a> using <a href="../extensions/CurvedLinkReshapingTool.js">CurvedLinkReshapingTool.js</a></li>
<li><a href="../extensions/DragCreating.html">DragCreating.html</a> using <a href="../extensions/DragCreatingTool.js">DragCreatingTool.js</a></li>
<li><a href="../extensions/DragZooming.html">DragZooming.html</a> using <a href="../extensions/DragZoomingTool.js">DragZoomingTool.js</a></li>
<li><a href="../extensions/FreehandDrawing.html">FreehandDrawing.html</a> using <a href="../extensions/FreehandDrawingTool.js">FreehandDrawingTool.js</a></li>
<li><a href="../extensions/GeometryReshaping.html">GeometryReshaping.html</a> using <a href="../extensions/GeometryReshapingTool.js">GeometryReshapingTool.js</a></li>
<li><a href="../extensions/GuidedDragging.html">GuidedDragging.html</a> using <a href="../extensions/GuidedDraggingTool.js">GuidedDraggingTool.js</a></li>
<li><a href="../extensions/LinkLabelDragging.html">LinkLabelDragging.html</a> using <a href="../extensions/LinkLabelDraggingTool.js">LinkLabelDraggingTool.js</a></li>
<li><a href="../extensions/LinkLabelOnPathDragging.html">LinkLabelOnPathDragging.html</a> using <a href="../extensions/LinkLabelOnPathDraggingTool.js">LinkLabelOnPathDraggingTool.js</a></li>
<li><a href="../extensions/LinkShifting.html">LinkShifting.html</a> using <a href="../extensions/LinkShiftingTool.js">LinkShiftingTool.js</a></li>
<li><a href="../extensions/NodeLabelDragging.html">NodeLabelDragging.html</a> using <a href="../extensions/NodeLabelDraggingTool.js">NodeLabelDraggingTool.js</a></li>
<li><a href="../extensions/NonRealtimeDragging.html">NonRealtimeDragging.html</a> using <a href="../extensions/NonRealtimeDraggingTool.js">NonRealtimeDraggingTool.js</a></li>
<li><a href="../extensions/OrthogonalLinkReshaping.html">OrthogonalLinkReshaping.html</a> using <a href="../extensions/OrthogonalLinkReshapingTool.js">OrthogonalLinkReshapingTool.js</a></li>
<li><a href="../extensions/OverviewResizing.html">OverviewResizing.html</a> using <a href="../extensions/OverviewResizingTool.js">OverviewResizingTool.js</a></li>
<li><a href="../extensions/PolygonDrawing.html">PolygonDrawing.html</a> using <a href="../extensions/PolygonDrawingTool.js">PolygonDrawingTool.js</a></li>
<li><a href="../extensions/PolylineLinking.html">PolylineLinking.html</a> using <a href="../extensions/PolylineLinkingTool.js">PolylineLinkingTool.js</a></li>
<li><a href="../extensions/PortShifting.html">PortShifting.html</a> using <a href="../extensions/PortShiftingTool.js">PortShiftingTool.js</a></li>
<li><a href="../extensions/RealtimeDragSelecting.html">RealtimeDragSelecting.html</a> using <a href="../extensions/RealtimeDragSelectingTool.js">RealtimeDragSelectingTool.js</a></li>
<li><a href="../extensions/Rescaling.html">Rescaling.html</a> using <a href="../extensions/RescalingTool.js">RescalingTool.js</a></li>
<li><a href="../extensions/ResizeMultiple.html">ResizeMultiple.html</a> using <a href="../extensions/ResizeMultipleTool.js">ResizeMultipleTool.js</a></li>
<li><a href="../extensions/RotateMultiple.html">RotateMultiple.html</a> using <a href="../extensions/RotateMultipleTool.js">RotateMultipleTool.js</a></li>
<li><a href="../extensions/SectorReshaping.html">SectorReshaping.html</a> using <a href="../extensions/SectorReshapingTool.js">SectorReshapingTool.js</a></li>
<li><a href="../extensions/SnapLinkReshaping.html">SnapLinkReshaping.html</a> using <a href="../extensions/SnapLinkReshapingTool.js">SnapLinkReshapingTool.js</a></li>
</ul>
<h4>CommandHandler Extensions:</h4>
<ul>
<li><a href="../extensions/DrawCommandHandler.html">DrawCommandHandler.html</a> using <a href="../extensions/DrawCommandHandler.js">DrawCommandHandler.js</a></li>
<li><a href="../extensions/LocalStorageCommandHandler.html">LocalStorageCommandHandler.html</a> using <a href="../extensions/LocalStorageCommandHandler.js">LocalStorageCommandHandler.js</a></li>
</ul>
<h4>Builder and Link Extensions:</h4>
<ul>
<li><a href="../extensions/BalloonLink.html">BalloonLink.html</a> using <a href="../extensions/BalloonLink.js">BalloonLink.js</a></li>
<li><a href="../extensions/CheckBoxes.html">CheckBoxes.html</a> using predefined "CheckBox"s shown in <a href="../extensions/Buttons.js">Buttons.js</a></li>
<li><a href="../extensions/Dimensioning.html">Dimensioning.html</a> using <a href="../extensions/DimensioningLink.js">DimensioningLink.js</a></li>
<li><a href="../extensions/Hyperlink.html">Hyperlink.html</a> using <a href="../extensions/HyperlinkText.js">HyperlinkText.js</a></li>
<li><a href="../extensions/ParallelRoute.html">ParallelRoute.html</a> using <a href="../extensions/ParallelRouteLink.js">ParallelRouteLink.js</a></li>
<li><a href="../extensions/ScrollingTable.html">ScrollingTable.html</a> using <a href="../extensions/ScrollingTable.js">ScrollingTable.js</a></li>
<li><a href="../extensions/TextEditor.html">TextEditor.html</a> using <a href="../extensions/TextEditor.js">TextEditor.js</a> <a href="../extensions/TextEditorRadioButtons.js">TextEditorRadioButtons.js</a> and <a href="../extensions/TextEditorSelectBox.js">TextEditorSelectBox.js</a></li>
</ul>
<h4>Miscellaneous Extensions:</h4>
<ul>
<li><a href="../extensions/DataInspector.html">DataInspector.html</a> using <a href="../extensions/DataInspector.js">DataInspector.js</a></li>
<li><a href="../extensions/Robot.html">Robot.html</a> using <a href="../extensions/Robot.js">Robot.js</a></li>
<li><a href="../extensions/ZoomSlider.html">ZoomSlider.html</a> using <a href="../extensions/ZoomSlider.js">ZoomSlider.js</a></li>
</ul>
<h4>Storage Extensions:</h4>
<ul>
<li><a href="../projects/storage/samples/GoCloudStorageManager.html">GoCloudStorageManager.html</a> using <a href="../projects/storage/lib/gcs.js">gcs.js</a>; sources at <a href="../projects/storage/src/GoCloudStorageManager.ts">../projects/storage/src/GoCloudStorageManager.ts</a></li>
<!--
<li><a href="../projects/storage/samples/GoDropBox.html">GoDropBox.html</a> source at: <a href="../projects/storage/src/GoDropBox.ts">../projects/storage/src/GoDropBox.ts</a></li>
<li><a href="../projects/storage/samples/GoGoogleDrive.html">GoGoogleDrive.html</a> source at: <a href="../projects/storage/src/GoGoogleDrive.ts">../projects/storage/src/GoGoogleDrive.ts</a></li>
<li><a href="../projects/storage/samples/GoOneDrive.html">GoOneDrive.html</a> source at: <a href="../projects/storage/src/GoOneDrive.ts">../projects/storage/src/GoOneDrive.ts</a></li>
<li><a href="../projects/storage/samples/GoLocalStorage.html">GoLocalStorage.html</a> source at: <a href="../projects/storage/src/GoLocalStorage.ts">../projects/storage/src/GoLocalStorage.ts</a></li>
-->
</ul>
<h4>Projects:</h4>
<ul>
<li><a href="../projects/floorplannerTS/index.html">new Floor Planner (TypeScript)</a>, in the <code>projects/floorplannerTS/</code> folder</li>
<li><a href="../projects/floorplanner/FloorPlanner.html">old Floor Planner (JavaScript)</a>, in the <code>projects/floorplanner/</code> folder</li>
<li><a href="../projects/bpmn/BPMN.html">BPMN Editor</a>, in the <code>projects/bpmn/</code> folder</li>
<li><a href="../projects/pdf/minimalPDF.html">PDF generator</a>, in the <code>projects/pdf</code> folder</li>
<li>See more at <a href="../projects/index.html">Projects</a></li>
</ul>
<h4>Predefined, built-in functionality:</h4>
<ul>
<li><a href="../extensions/Arrowheads.js">Arrowheads.js</a> all shown by <a href="arrowheads.html">arrowheads.html</a></li>
<li><a href="../extensions/Buttons.js">Buttons.js</a></li>
<li><a href="../extensions/Figures.js">Figures.js</a> all shown by <a href="shapes.html">shapes.html</a></li>
<li><a href="../extensions/Templates.js">Templates.js</a></li>
<li><a href="../extensions/TextEditor.js">TextEditor.js</a></li>
</ul>
<p>
See the <a href="index.html">samples index</a> for a subset of this list with screenshots and short descriptions.
</p>
</body>
</html>
+108
View File
@@ -0,0 +1,108 @@
<!DOCTYPE html>
<html>
<head>
<title>Drawing Attention to a Node</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="When focussing on a node, scroll with animation to it and show a magnified image of it shrinking in place." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
// allow some empty space to appear when scrolled to the edge of the document
scrollMargin: 200,
// the layout does not really matter for this sample
layout: $(go.GridLayout, { wrappingWidth: 4000 }),
"InitialLayoutCompleted": function(e) {
// wait until initial layout and initial animation are finished,
// then select the node and scroll to it with its own animation
var node = null; // you might choose a particular node in your app
setTimeout(function() { focusOnNode(node); },
e.diagram.animationManager.duration);
}
});
// the templates do not really matter for this sample
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{ width: 120, height: 60 },
$(go.Shape,
new go.Binding("fill")),
$(go.TextBlock,
new go.Binding("text"))
);
// create enough nodes so that only part of the document will fit in the viewport
var arr = [];
for (var i = 0; i < 1000; i++) {
var color = go.Brush.randomColor();
arr.push({ text: color, fill: color });
}
myDiagram.model = new go.GraphLinksModel(arr);
}
function focusOnNode(node) { // node is optional
// If no node is given, choose a node at random, and select it.
if (!node) {
var arr = myDiagram.model.nodeDataArray;
var data = arr[Math.floor(Math.random() * arr.length)];
node = myDiagram.findNodeForData(data);
}
if (!node) return;
myDiagram.select(node);
// Set up an Animation that shows the node significantly larger than normal
// and then scales it back down to normal.
// This intentionally does not operate on the selected node itself,
// but on a temporary copy of it, so that the node and the model are unaffected.
var focus1 = node.copy();
focus1.layerName = "Tool";
focus1.isInDocumentBounds = false;
focus1.locationSpot = go.Spot.Center;
focus1.location = node.actualBounds.center;
// Figure out how large to scale it initially; assume maximum is one third of the viewport size
var w = Math.max(node.actualBounds.width, 1);
var h = Math.max(node.actualBounds.height, 1);
var viewscale = Math.max(myDiagram.viewportBounds.width/w, myDiagram.viewportBounds.height/h) / 3;
// Now create the Animation showing the temporary node scaled initially at VIEWSCALE
var anim = new go.Animation();
anim.addTemporaryPart(focus1, myDiagram);
anim.add(focus1, "scale", viewscale, 1.0); // and animating down to scale 1.0
// This animation occurs concurrently with the scrolling animation.
anim.duration = myDiagram.animationManager.duration + 1000;
anim.start();
// Meanwhile, make sure that the node is in the viewport, so the user can see it
myDiagram.commandHandler.scrollToPart(node);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
<p>
Click on this button to select a node at random, scroll to it, and animate a copy of it -- all to
draw attention to it.<br/>
<button onclick="focusOnNode()">Focus on random Node</button>
</p>
<p>
This calls <a>CommandHandler.scrollToPart</a>, which conducts an animation to scroll
the viewport to where the node is. Note that if the node is close to the edge of the document,
the viewport cannot be scrolled so that the node is nearer to the center of the viewport unless you
increase the <a>Diagram.scrollMargin</a>.
</p>
<p>
This also creates an <a>Animation</a> that operates on a temporary copy of the selected node,
making it appear much larger but animating the scale so that it appears to shrink to be
the selected node where it is in the diagram.
</p>
</div>
</body>
</html>
+168
View File
@@ -0,0 +1,168 @@
<!DOCTYPE html>
<html>
<head>
<title>GoJS Arrowheads</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Show all of the predefined kinds of arrowheads for links, which can be used by setting Shape.toArrow or Shape.fromArrow." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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 myDiagram =
$(go.Diagram, "myDiagramDiv", // create a Diagram for the DIV HTML element
{
isReadOnly: true, // don't allow move or delete
layout: $(go.CircularLayout,
{
radius: 100, // minimum radius
spacing: 0, // circular nodes will touch each other
nodeDiameterFormula: go.CircularLayout.Circular, // assume nodes are circular
startAngle: 270 // first node will be at top
}),
// define a DiagramEvent listener
"LayoutCompleted": function(e) {
// now that the CircularLayout has finished, we know where its center is
var cntr = myDiagram.findNodeForKey("Center");
cntr.location = myDiagram.layout.actualCenter;
}
});
// construct a shared radial gradient brush
var radBrush = $(go.Brush, "Radial", { 0: "#550266", 1: "#80418C" });
// these are the nodes that are in a circle
myDiagram.nodeTemplate =
$(go.Node,
$(go.Shape, "Circle",
{
desiredSize: new go.Size(28, 28),
fill: radBrush, strokeWidth: 0, stroke: null
}), // no outline
{
locationSpot: go.Spot.Center,
click: showArrowInfo, // defined below
toolTip: // define a tooltip for each link that displays its information
$("ToolTip",
$(go.TextBlock, { margin: 4 },
new go.Binding("text", "", infoString).ofObject())
)
}
);
// use a special template for the center node
myDiagram.nodeTemplateMap.add("Center",
$(go.Node, "Spot",
{
selectable: false,
isLayoutPositioned: false, // the Diagram.layout will not position this node
locationSpot: go.Spot.Center
},
$(go.Shape, "Circle",
{ fill: radBrush, strokeWidth: 0, stroke: null, desiredSize: new go.Size(200, 200) }), // no outline
$(go.TextBlock, "Arrowheads",
{ margin: 1, stroke: "white", font: "bold 14px sans-serif" })
));
// all Links have both "toArrow" and "fromArrow" Shapes,
// where both arrow properties are data bound
myDiagram.linkTemplate =
$(go.Link, // the whole link panel
{ routing: go.Link.Normal },
$(go.Shape, // the link shape
// the first element is assumed to be main element: as if isPanelMain were true
{ stroke: "gray", strokeWidth: 2 }),
$(go.Shape, // the "from" arrowhead
new go.Binding("fromArrow", "fromArrow"),
{ scale: 2, fill: "#D4B52C" }),
$(go.Shape, // the "to" arrowhead
new go.Binding("toArrow", "toArrow"),
{ scale: 2, fill: "#D4B52C" }),
{
click: showArrowInfo,
toolTip: // define a tooltip for each link that displays its information
$("ToolTip",
$(go.TextBlock, { margin: 4 },
new go.Binding("text", "", infoString).ofObject())
)
}
);
// collect all of the predefined arrowhead names
var arrowheads = go.Shape.getArrowheadGeometries().toKeySet().toArray();
if (arrowheads.length % 2 === 1) arrowheads.push(""); // make sure there's an even number
// create all of the link data, two arrowheads per link
var linkdata = [];
var i = 0;
for (var j = 0; j < arrowheads.length; j = j + 2) {
linkdata.push({ from: "Center", to: i++, toArrow: arrowheads[j], fromArrow: arrowheads[j + 1] });
}
myDiagram.model =
$(go.GraphLinksModel,
{ // this gets copied automatically when there's a link data reference to a new node key
// and is then added to the nodeDataArray
archetypeNodeData: {},
// the node array starts with just the special Center node
nodeDataArray: [{ category: "Center", key: "Center" }],
// the link array was created above
linkDataArray: linkdata
});
}
// a conversion function used to get arrowhead information for a Part
function infoString(obj) {
var part = obj.part;
if (part instanceof go.Adornment) part = part.adornedPart;
var msg = "";
if (part instanceof go.Link) {
var link = part;
msg = "toArrow: " + link.data.toArrow + ";\nfromArrow: " + link.data.fromArrow;
} else if (part instanceof go.Node) {
var node = part;
var link = node.linksConnected.first();
msg = "toArrow: " + link.data.toArrow + ";\nfromArrow: " + link.data.fromArrow;
}
return msg;
}
// a GraphObject.click event handler to show arrowhead information
function showArrowInfo(e, obj) {
var msg = infoString(obj);
if (msg) {
var status = document.getElementById("myArrowheadInfo");
if (status) status.textContent = msg;
}
}
</script>
</head>
<body onload="init()">
<div id="sample">
<!-- The DIV for the Diagram needs an explicit size or else we won't see anything.
Also add a border to help see the edges. -->
<div id="myDiagramDiv" style="border: solid 1px black; width:600px; height:500px"></div>
<div id="myArrowheadInfo" style="color:red"></div>
<p>
This sample displays all predefined GoJS arrowheads.
Select or hover over a Node or its Link to see the names of the arrowheads on the Link.
</p>
<p>
Each Link shows two arrowheads.
The Link template has a Shape whose <a>Shape.toArrow</a> property is bound to an arrowhead name.
A different Shape in the template has its <a>Shape.fromArrow</a> property bound to a different arrowhead name.
Each arrowhead has been scaled up to make it more easily visible.
</p>
<p>
See the definitions of all these arrowheads in the file: <a href="../extensions/Arrowheads.js" target="_blank">Arrowheads.js</a>.
</p>
<p>
For predefined shape geometries, see the <a href="shapes.html">Shapes</a> sample.
</p>
</div>
</body>
</html>
File diff suppressed because one or more lines are too long
+97
View File
@@ -0,0 +1,97 @@
<!DOCTYPE html>
<html>
<head>
<title>Bar Charts</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="GoJS nodes containing simple bar charts." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
myDiagram =
$(go.Diagram, "myDiagramDiv");
// the template for each item in a node's array of item data
var itemTempl =
$(go.Panel, "TableColumn",
$(go.Shape,
{ row: 0, alignment: go.Spot.Bottom },
{ fill: "slateblue", stroke: null, width: 40 },
new go.Binding("height", "val"),
new go.Binding("fill", "color")),
$(go.TextBlock,
{ row: 1 },
new go.Binding("text")),
{
toolTip:
$("ToolTip",
$(go.TextBlock, { margin: 4 },
new go.Binding("text", "val"))
)
}
);
myDiagram.nodeTemplate =
$(go.Node, "Auto",
$(go.Shape,
{ fill: "white" }),
$(go.Panel, "Vertical",
$(go.Panel, "Table",
{ margin: 6, itemTemplate: itemTempl },
new go.Binding("itemArray", "items")),
$(go.TextBlock,
{ font: "bold 12pt sans-serif" },
new go.Binding("text"))
)
);
var nodeDataArray = [
{
key: 1,
text: "Before",
items: [{ text: "first", val: 50 },
{ text: "second", val: 70 },
{ text: "third", val: 60 },
{ text: "fourth", val: 80 }]
},
{
key: 2,
text: "After",
items: [{ text: "first", val: 50 },
{ text: "second", val: 70 },
{ text: "third", val: 75, color: "red" },
{ text: "fourth", val: 80 }]
}
];
var linkDataArray = [
{ from: 1, to: 2 }
];
myDiagram.model = $(go.GraphLinksModel,
{
copiesArrays: true,
copiesArrayObjects: true,
nodeDataArray: nodeDataArray,
linkDataArray: linkDataArray
});
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="background-color: white; border: solid 1px black; width: 100%; height: 500px"></div>
<p>
Each node contains a Table Panel whose <a>Panel.itemArray</a> is data bound to the "items" property which holds an Array of data objects.
That Table Panel has an <a>Panel.itemTemplate</a> which creates a bar (a rectangular Shape) and a TextBlock label for each item.
Each bar also has a tooltip showing the value.
</p>
<p>
For more sophisticated charts within nodes, see the <a href="canvases.html">Canvas Charts</a> sample.
</p>
</div>
</body>
</html>
+282
View File
@@ -0,0 +1,282 @@
<!DOCTYPE html>
<html>
<head>
<title>Basic GoJS Sample</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Interactive GoJS diagram demonstrating creating new nodes and links, reconnecting links, grouping and ungrouping, and context menus and tooltips for nodes, for links, and for the diagram background." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
myDiagram =
$(go.Diagram, "myDiagramDiv", // create a Diagram for the DIV HTML element
{
// allow double-click in background to create a new node
"clickCreatingTool.archetypeNodeData": { text: "Node", color: "white" },
// allow Ctrl-G to call groupSelection()
"commandHandler.archetypeGroupData": { text: "Group", isGroup: true, color: "blue" },
// enable undo & redo
"undoManager.isEnabled": true
});
// Define the appearance and behavior for Nodes:
// First, define the shared context menu for all Nodes, Links, and Groups.
// To simplify this code we define a function for creating a context menu button:
function makeButton(text, action, visiblePredicate) {
return $("ContextMenuButton",
$(go.TextBlock, text),
{ click: action },
// don't bother with binding GraphObject.visible if there's no predicate
visiblePredicate ? new go.Binding("visible", "", function(o, e) { return o.diagram ? visiblePredicate(o, e) : false; }).ofObject() : {});
}
// a context menu is an Adornment with a bunch of buttons in them
var partContextMenu =
$("ContextMenu",
makeButton("Properties",
function(e, obj) { // OBJ is this Button
var contextmenu = obj.part; // the Button is in the context menu Adornment
var part = contextmenu.adornedPart; // the adornedPart is the Part that the context menu adorns
// now can do something with PART, or with its data, or with the Adornment (the context menu)
if (part instanceof go.Link) alert(linkInfo(part.data));
else if (part instanceof go.Group) alert(groupInfo(contextmenu));
else alert(nodeInfo(part.data));
}),
makeButton("Cut",
function(e, obj) { e.diagram.commandHandler.cutSelection(); },
function(o) { return o.diagram.commandHandler.canCutSelection(); }),
makeButton("Copy",
function(e, obj) { e.diagram.commandHandler.copySelection(); },
function(o) { return o.diagram.commandHandler.canCopySelection(); }),
makeButton("Paste",
function(e, obj) { e.diagram.commandHandler.pasteSelection(e.diagram.toolManager.contextMenuTool.mouseDownPoint); },
function(o) { return o.diagram.commandHandler.canPasteSelection(o.diagram.toolManager.contextMenuTool.mouseDownPoint); }),
makeButton("Delete",
function(e, obj) { e.diagram.commandHandler.deleteSelection(); },
function(o) { return o.diagram.commandHandler.canDeleteSelection(); }),
makeButton("Undo",
function(e, obj) { e.diagram.commandHandler.undo(); },
function(o) { return o.diagram.commandHandler.canUndo(); }),
makeButton("Redo",
function(e, obj) { e.diagram.commandHandler.redo(); },
function(o) { return o.diagram.commandHandler.canRedo(); }),
makeButton("Group",
function(e, obj) { e.diagram.commandHandler.groupSelection(); },
function(o) { return o.diagram.commandHandler.canGroupSelection(); }),
makeButton("Ungroup",
function(e, obj) { e.diagram.commandHandler.ungroupSelection(); },
function(o) { return o.diagram.commandHandler.canUngroupSelection(); })
);
function nodeInfo(d) { // Tooltip info for a node data object
var str = "Node " + d.key + ": " + d.text + "\n";
if (d.group)
str += "member of " + d.group;
else
str += "top-level node";
return str;
}
// These nodes have text surrounded by a rounded rectangle
// whose fill color is bound to the node data.
// The user can drag a node by dragging its TextBlock label.
// Dragging from the Shape will start drawing a new link.
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{ locationSpot: go.Spot.Center },
$(go.Shape, "RoundedRectangle",
{
fill: "white", // the default fill, if there is no data bound value
portId: "", cursor: "pointer", // the Shape is the port, not the whole Node
// allow all kinds of links from and to this port
fromLinkable: true, fromLinkableSelfNode: true, fromLinkableDuplicates: true,
toLinkable: true, toLinkableSelfNode: true, toLinkableDuplicates: true
},
new go.Binding("fill", "color")),
$(go.TextBlock,
{
font: "bold 14px sans-serif",
stroke: '#333',
margin: 6, // make some extra space for the shape around the text
isMultiline: false, // don't allow newlines in text
editable: true // allow in-place editing by user
},
new go.Binding("text", "text").makeTwoWay()), // the label shows the node data's text
{ // this tooltip Adornment is shared by all nodes
toolTip:
$("ToolTip",
$(go.TextBlock, { margin: 4 }, // the tooltip shows the result of calling nodeInfo(data)
new go.Binding("text", "", nodeInfo))
),
// this context menu Adornment is shared by all nodes
contextMenu: partContextMenu
}
);
// Define the appearance and behavior for Links:
function linkInfo(d) { // Tooltip info for a link data object
return "Link:\nfrom " + d.from + " to " + d.to;
}
// The link shape and arrowhead have their stroke brush data bound to the "color" property
myDiagram.linkTemplate =
$(go.Link,
{ toShortLength: 3, relinkableFrom: true, relinkableTo: true }, // allow the user to relink existing links
$(go.Shape,
{ strokeWidth: 2 },
new go.Binding("stroke", "color")),
$(go.Shape,
{ toArrow: "Standard", stroke: null },
new go.Binding("fill", "color")),
{ // this tooltip Adornment is shared by all links
toolTip:
$("ToolTip",
$(go.TextBlock, { margin: 4 }, // the tooltip shows the result of calling linkInfo(data)
new go.Binding("text", "", linkInfo))
),
// the same context menu Adornment is shared by all links
contextMenu: partContextMenu
}
);
// Define the appearance and behavior for Groups:
function groupInfo(adornment) { // takes the tooltip or context menu, not a group node data object
var g = adornment.adornedPart; // get the Group that the tooltip adorns
var mems = g.memberParts.count;
var links = 0;
g.memberParts.each(function(part) {
if (part instanceof go.Link) links++;
});
return "Group " + g.data.key + ": " + g.data.text + "\n" + mems + " members including " + links + " links";
}
// Groups consist of a title in the color given by the group node data
// above a translucent gray rectangle surrounding the member parts
myDiagram.groupTemplate =
$(go.Group, "Vertical",
{
selectionObjectName: "PANEL", // selection handle goes around shape, not label
ungroupable: true // enable Ctrl-Shift-G to ungroup a selected Group
},
$(go.TextBlock,
{
//alignment: go.Spot.Right,
font: "bold 19px sans-serif",
isMultiline: false, // don't allow newlines in text
editable: true // allow in-place editing by user
},
new go.Binding("text", "text").makeTwoWay(),
new go.Binding("stroke", "color")),
$(go.Panel, "Auto",
{ name: "PANEL" },
$(go.Shape, "Rectangle", // the rectangular shape around the members
{
fill: "rgba(128,128,128,0.2)", stroke: "gray", strokeWidth: 3,
portId: "", cursor: "pointer", // the Shape is the port, not the whole Node
// allow all kinds of links from and to this port
fromLinkable: true, fromLinkableSelfNode: true, fromLinkableDuplicates: true,
toLinkable: true, toLinkableSelfNode: true, toLinkableDuplicates: true
}),
$(go.Placeholder, { margin: 10, background: "transparent" }) // represents where the members are
),
{ // this tooltip Adornment is shared by all groups
toolTip:
$("ToolTip",
$(go.TextBlock, { margin: 4 },
// bind to tooltip, not to Group.data, to allow access to Group properties
new go.Binding("text", "", groupInfo).ofObject())
),
// the same context menu Adornment is shared by all groups
contextMenu: partContextMenu
}
);
// Define the behavior for the Diagram background:
function diagramInfo(model) { // Tooltip info for the diagram's model
return "Model:\n" + model.nodeDataArray.length + " nodes, " + model.linkDataArray.length + " links";
}
// provide a tooltip for the background of the Diagram, when not over any Part
myDiagram.toolTip =
$("ToolTip",
$(go.TextBlock, { margin: 4 },
new go.Binding("text", "", diagramInfo))
);
// provide a context menu for the background of the Diagram, when not over any Part
myDiagram.contextMenu =
$("ContextMenu",
makeButton("Paste",
function(e, obj) { e.diagram.commandHandler.pasteSelection(e.diagram.toolManager.contextMenuTool.mouseDownPoint); },
function(o) { return o.diagram.commandHandler.canPasteSelection(o.diagram.toolManager.contextMenuTool.mouseDownPoint); }),
makeButton("Undo",
function(e, obj) { e.diagram.commandHandler.undo(); },
function(o) { return o.diagram.commandHandler.canUndo(); }),
makeButton("Redo",
function(e, obj) { e.diagram.commandHandler.redo(); },
function(o) { return o.diagram.commandHandler.canRedo(); })
);
// Create the Diagram's Model:
var nodeDataArray = [
{ key: 1, text: "Alpha", color: "lightblue" },
{ key: 2, text: "Beta", color: "orange" },
{ key: 3, text: "Gamma", color: "lightgreen", group: 5 },
{ key: 4, text: "Delta", color: "pink", group: 5 },
{ key: 5, text: "Epsilon", color: "green", isGroup: true }
];
var linkDataArray = [
{ from: 1, to: 2, color: "blue" },
{ from: 2, to: 2 },
{ from: 3, to: 4, color: "green" },
{ from: 3, to: 1, color: "purple" }
];
myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:400px; height:400px"></div>
<p>
This sample demonstrates tooltips and context menus for all parts and for the diagram background,
as well as several other powerful diagram editing abilities.
</p>
<p>
Unlike the <a href="minimal.html">Minimal</a> sample, this sample has templates for Links and for Groups,
plus tooltips and context menus for Nodes, for Links, for Groups, and for the Diagram.
</p>
<p>This sample has all of the functionality of the Minimal sample, but additionally allows the user to:</p>
<ul>
<li>create new nodes: double-click in the background of the diagram</li>
<li>edit text: select the node and then click on the text, or select the node and press F2</li>
<li>draw new links: drag from the inner edge of the node's or the group's shape</li>
<li>reconnect existing links: select the link and then drag the diamond-shaped handle at either end of the link</li>
<li>group nodes and links: select some nodes and links and then type Ctrl-G (or invoke via context menu)</li>
<li>ungroup an existing group: select a group and then type Ctrl-Shift-G (or invoke via context menu)</li>
</ul>
<p>
GoJS contains many other possible commands, which can be invoked by either mouse/keyboard/touch or programatically.
<a href="../intro/commands.html">See an overview of possible commands here.</a>
On a Mac, use CMD instead of Ctrl.
</p>
<p>
On touch devices, hold your finger stationary to bring up a context menu.
The default context menu supports most of the standard commands that are enabled at that time for that object.
</p>
</div>
</body>
</html>
+142
View File
@@ -0,0 +1,142 @@
<!DOCTYPE html>
<html>
<head>
<title>Beat Paths</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A precedence diagram showing a hierarchical relationship between nodes, using archetypeNodeData, LayeredDigraphLayout, and Bezier curved links." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
// Must name or refer to the DIV HTML element
myDiagram =
$(go.Diagram, "myDiagramDiv",
{ // automatically scale the diagram to fit the viewport's size
initialAutoScale: go.Diagram.Uniform,
// disable user copying of parts
allowCopy: false,
// position all of the nodes and route all of the links
layout:
$(go.LayeredDigraphLayout,
{
direction: 90,
layerSpacing: 10,
columnSpacing: 15,
setsPortSpots: false
})
});
// replace the default Node template in the nodeTemplateMap
myDiagram.nodeTemplate =
$(go.Node, "Vertical", // the whole node panel
$(go.TextBlock, // the text label
new go.Binding("text", "key")),
$(go.Picture, // the icon showing the logo
// You should set the desiredSize (or width and height)
// whenever you know what size the Picture should be.
{ desiredSize: new go.Size(75, 50) },
new go.Binding("source", "key", convertKeyImage))
);
// replace the default Link template in the linkTemplateMap
myDiagram.linkTemplate =
$(go.Link, // the whole link panel
{ curve: go.Link.Bezier, toShortLength: 2 },
$(go.Shape, // the link shape
{ strokeWidth: 1.5 }),
$(go.Shape, // the arrowhead
{ toArrow: "Standard", stroke: null })
);
// the array of link data objects: the relationships between the nodes
var linkDataArray = [
{ from: "CAR", to: "ARI" },
{ from: "ARI", to: "CIN" },
{ from: "ARI", to: "GB" },
{ from: "DEN", to: "GB" },
{ from: "DEN", to: "CIN" },
{ from: "DEN", to: "NE" },
{ from: "GB", to: "WAS" },
{ from: "WAS", to: "STL" },
{ from: "CIN", to: "STL" },
{ from: "STL", to: "SEA" },
{ from: "SEA", to: "SF" },
{ from: "SEA", to: "MIN" },
{ from: "NE", to: "NYG" },
{ from: "NE", to: "KC" },
{ from: "MIN", to: "DET" },
{ from: "MIN", to: "KC" },
{ from: "KC", to: "HOU" },
{ from: "KC", to: "BUF" },
{ from: "KC", to: "BAL" },
{ from: "KC", to: "OAK" },
{ from: "BUF", to: "NYJ" },
{ from: "BAL", to: "PIT" },
{ from: "DET", to: "NO" },
{ from: "DET", to: "PHI" },
{ from: "DET", to: "CHI" },
{ from: "HOU", to: "JAC" },
{ from: "HOU", to: "TEN" },
{ from: "PIT", to: "IND" },
{ from: "PIT", to: "SD" },
{ from: "OAK", to: "NYJ" },
{ from: "OAK", to: "SD" },
{ from: "NO", to: "ATL" },
{ from: "NO", to: "NYG" },
{ from: "PHI", to: "NYG" },
{ from: "CHI", to: "TB" },
{ from: "NYJ", to: "IND" },
{ from: "NYJ", to: "CLE" },
{ from: "IND", to: "TB" },
{ from: "TB", to: "ATL" },
{ from: "SD", to: "CLE" },
{ from: "ATL", to: "DAL" },
{ from: "ATL", to: "JAC" },
{ from: "CLE", to: "TEN" },
{ from: "DAL", to: "MIA" },
{ from: "MIA", to: "TEN" }
];
// create the model and assign it to the Diagram
myDiagram.model =
$(go.GraphLinksModel,
{ // automatically create node data objects for each "from" or "to" reference
// (set this property before setting the linkDataArray)
archetypeNodeData: {},
// process all of the link relationship data
linkDataArray: linkDataArray
});
}
function convertKeyImage(key) {
if (!key) key = "NE";
return "https://www.nwoods.com/go/beatpaths/" + key + "_logo-75x50.png";
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; margin: 10px; height: 700px"></div>
<p>
This sample demonstrates reading JSON data describing the relative rankings of NFL teams
during the 2015 season and generating a diagram from that data.
The ranking information came from beatgraphs.com.
</p>
<p>
The JSON data is basically just a list of relationships.
Unlike most model data, there are no elements describing the nodes --
the node definitions are implicit in the references from the links.
Hence the <a>Diagram.model</a> has <a>GraphLinksModel.archetypeNodeData</a> set to a JavaScript object.
</p>
<p>
The node template uses the <b>convertKeyImage</b> function to convert the team name
into a URI referring to an image on our web site.
</p>
</div>
</body>
</html>
+332
View File
@@ -0,0 +1,332 @@
<!DOCTYPE html>
<html>
<head>
<title>Belts</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Belts & gears: chains, pulleys, tensioners, conveyor belts, rollers." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
"InitialLayoutCompleted": function(e) {
updateBelts(); // changes the bounds of the "Belt"s
e.diagram.alignDocument(go.Spot.Center, go.Spot.Center);
},
"animationManager.isEnabled": false,
"undoManager.isEnabled": true,
allowCopy: false,
allowDelete: false,
"draggingTool.moveParts": function(parts, offset, check) {
go.DraggingTool.prototype.moveParts.call(this, parts, offset, check);
updateBelts(); // this is inefficient if there are a lot of belts
}
});
function commonStyle() {
return [
new go.Binding("location", "xy", go.Point.parse).makeTwoWay(go.Point.stringify),
{
locationSpot: go.Spot.Center, locationObjectName: "GUIDE",
selectionAdorned: false,
dragComputation: function(node, newloc, snaploc) {
// don't allow rollers or drums to overlap each other
var oldloc = node.location;
var noderad = node.findObject("GUIDE").actualBounds.width/2;
var ok = true;
var it = node.diagram.nodes.iterator;
while (it.next()) {
var n = it.value;
if (n === node || n.category === "Belt") continue;
var dist2 = newloc.distanceSquaredPoint(n.location);
var rad = n.findObject("GUIDE").actualBounds.width/2;
if (dist2 < (noderad+rad)*(noderad+rad)) { ok = false; break; }
}
return ok ? newloc : oldloc;
}
}
];
}
myDiagram.nodeTemplateMap.add("Roller",
$(go.Node, "Spot", commonStyle(),
$(go.Shape, "Circle",
{ name: "GUIDE", fill: "lightgray", strokeWidth: 0, width: 20, height: 20 },
new go.Binding("width", "diameter").makeTwoWay(),
new go.Binding("height", "diameter")),
$(go.TextBlock,
{ font: "6pt sans-serif", stroke: "black" },
new go.Binding("text", "key"))
));
myDiagram.nodeTemplateMap.add("Drum",
$(go.Node, "Spot", commonStyle(),
$(go.Shape, "Circle",
{ name: "GUIDE", fill: "lightgray", stroke: "gray", width: 80, height: 80 },
new go.Binding("fill", "color")),
$(go.TextBlock,
{ font: "6pt sans-serif", stroke: "darkblue" },
new go.Binding("text", "key"))
));
myDiagram.nodeTemplateMap.add("Belt",
$(go.Node,
{ selectionAdorned: false, layerName: "Foreground", copyable: false, movable: false },
$(go.Shape,
{ name: "BELT", fill: null, stroke: "gray", strokeWidth: 2, strokeDashArray: [4, 2] },
new go.Binding("stroke", "color"))
));
load();
} // end init
function updateBelts(coll) {
if (!coll) coll = myDiagram.nodes;
myDiagram.startTransaction();
coll.each(updateBelt);
myDiagram.commitTransaction("updated belts");
}
function updateBelt(node) {
if (node.category !== "Belt") return;
var belt = node.findObject("BELT");
var diagram = node.diagram;
var guideinfos = node.data.guides;
if (!Array.isArray(guideinfos)) throw new Error("data.guides is not an Array for Belt node: " + node.data.key);
// gather basic information about each guide node
var guides = []; // holds Objects with handy information
for (var i = 0; i < guideinfos.length; i++) {
var info = guideinfos[i];
var guidenode = diagram.findNodeForKey(info.k);
if (guidenode !== null && guidenode.location.isReal()) {
var loc = guidenode.location;
var cyl = guidenode.findObject("GUIDE");
var radius = (cyl !== null) ? cyl.measuredBounds.width / 2 : 10;
if (guides.length > 0) {
var prevguide = guides[guides.length - 1];
var prevloc = prevguide.location;
var prevradius = prevguide.radius;
var dist = Math.sqrt(prevloc.distanceSquaredPoint(loc));
if (dist < Math.abs(prevradius-radius)) { // one is completely inside the other
if (prevradius > radius) {
continue; // skip this smaller guide
} else {
guides.pop(); // skip the previous guide, which was smaller
}
}
}
guides.push({
node: guidenode,
location: guidenode.location.copy(),
radius: radius + belt.strokeWidth/2,
outside: !!info.outside,
from: null, // these Points will be computed by computeContacts
to: null
});
}
}
// handle some degenerate cases
if (guides.length < 2) {
if (guides.length === 1) {
node.location = guides[0].location;
}
if (belt !== null) {
belt.geometry = new go.Geometry(go.Geometry.Ellipse);
}
return;
}
// compute the contact points
// assume guides are listed in clockwise order
for (var i = 0; i < guides.length; i++) {
var guide = guides[i];
var next = guides[(i + 1) % guides.length];
computeContacts(guide, next);
}
// skip any guides that should not contact the Belt, because they're cannot touch the path
var i = 0;
while (guides.length > 2 && i < guides.length) {
var guide = guides[i];
var next = guides[(i + 1) % guides.length];
var follow = guides[(i + 2) % guides.length];
// is NEXT on the wrong side of the line from GUIDE to FOLLOW?
var wrongside = comparePointWithLine(guide.from.x, guide.from.y, follow.to.x, follow.to.y, next.to.x, next.to.y) < 0;
if (next.outside) wrongside = !wrongside;
if (wrongside) {
computeContacts(guide, follow);
// get rid of NEXT
if (i + 1 < guides.length) {
guides.splice(i + 1, 1);
} else {
guides.splice(0, 1);
}
// now also need to check whether GUIDE has become on the wrong side!
if (i > 0) i--;
} else {
i++;
}
}
// construct the Geometry for the belt Shape
var geo = new go.Geometry();
var fig = null;
for (var i = 0; i < guides.length; i++) {
var guide = guides[i];
var next = guides[(i + 1) % guides.length];
if (fig === null) {
fig = new go.PathFigure(guide.from.x, guide.from.y, true);
geo.add(fig);
}
fig.add(new go.PathSegment(go.PathSegment.Line, next.to.x, next.to.y));
var startang = next.location.directionPoint(next.to);
var endang = next.location.directionPoint(next.from);
var sweep = (endang > startang) ? endang-startang : (360 - startang) + endang;
if (next.outside) { // go counter-clockwise
fig.add(new go.PathSegment(go.PathSegment.Arc, startang, sweep - 360, next.location.x, next.location.y, next.radius, next.radius));
} else { // positive sweep angle
fig.add(new go.PathSegment(go.PathSegment.Arc, startang, sweep, next.location.x, next.location.y, next.radius, next.radius));
}
}
// update the Belt's Shape.geometry
if (belt !== null) {
var pos = geo.normalize();
belt.geometry = geo;
// account for the thickness of the belt shape's stroke
node.position = new go.Point(-pos.x - belt.strokeWidth / 2, -pos.y - belt.strokeWidth / 2);
node.ensureBounds();
}
} // end updateBelt
function comparePointWithLine(a1x, a1y, a2x, a2y, p1x, p1y) {
var x2 = a2x - a1x;
var y2 = a2y - a1y;
var px = p1x - a1x;
var py = p1y - a1y;
var ccw = px * y2 - py * x2;
if (ccw === 0) {
ccw = px * x2 + py * y2;
if (ccw > 0) {
px -= x2;
py -= y2;
ccw = px * x2 + py * y2;
if (ccw < 0) ccw = 0;
}
}
return (ccw < 0) ? -1 : ((ccw > 0) ? 1 : 0);
}
function computeContacts(guideA, guideB) {
var locA = guideA.location;
var x1 = locA.x;
var y1 = locA.y;
var r1 = guideA.radius;
var locB = guideB.location;
var x2 = locB.x;
var y2 = locB.y;
var r2 = guideB.radius;
// this assumes that belts only go clockwise
var g = Math.atan2(y2 - y1, x2 - x1);
var d = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
var bb = ((guideA.outside === guideB.outside) ? (r2 - r1) : (r2 + r1)) / d;
if (bb < -1) bb = -1; else if (bb > 1) bb = 1;
var b = Math.asin(bb);
if (guideB.outside) {
if (guideA.outside) { // both outside
var a = Math.PI / 2 - b - g;
var cosa = Math.cos(a);
var sina = Math.sin(a);
guideA.from = new go.Point(x1 - r1 * cosa, y1 + r1 * sina);
guideB.to = new go.Point(x2 - r2 * cosa, y2 + r2 * sina);
} else { // inside A, outside B
var a = Math.PI / 2 - Math.abs(b) - g;
var cosa = Math.cos(a);
var sina = Math.sin(a);
guideA.from = new go.Point(x1 + r1 * cosa, y1 - r1 * sina);
guideB.to = new go.Point(x2 - r2 * cosa, y2 + r2 * sina);
}
} else {
if (guideA.outside) { // outside A, inside B
var a = Math.abs(b) - Math.PI / 2 - g;
var cosa = Math.cos(a);
var sina = Math.sin(a);
guideA.from = new go.Point(x1 + r1 * cosa, y1 - r1 * sina);
guideB.to = new go.Point(x2 - r2 * cosa, y2 + r2 * sina);
} else { // both inside
var a = Math.PI / 2 + b - g;
var cosa = Math.cos(a);
var sina = Math.sin(a);
guideA.from = new go.Point(x1 + r1 * cosa, y1 - r1 * sina);
guideB.to = new go.Point(x2 + r2 * cosa, y2 - r2 * sina);
}
}
}
// Show the diagram's model in JSON format that the user may edit
function save() {
document.getElementById("mySavedModel").value = myDiagram.model.toJson();
myDiagram.isModified = false;
}
var beltAnimation = null;
function load() {
myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
// Animate the flow in the pipes
if (beltAnimation) beltAnimation.stop();
var animation = new go.Animation();
animation.easing = go.Animation.EaseLinear;
myDiagram.nodes.each(function(node) {
if (node.category !== "Belt") return;
animation.add(node.findObject("BELT"), "strokeDashOffset", 36, 0)
});
// Run indefinitely
animation.runCount = Infinity;
animation.start();
beltAnimation = animation;
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
<button id="SaveButton" onclick="save()">Save</button>
<button onclick="load()">Load</button>
Diagram Model saved in JSON format:
<textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "go.GraphLinksModel",
"nodeDataArray": [
{"key":"P111", "category":"Roller", "xy":"450 610"},
{"key":"P112", "category":"Roller", "xy":"400 660"},
{"key":"P113", "category":"Roller", "xy":"350 725"},
{"key":"P114", "category":"Roller", "xy":"305 800"},
{"key":"P115", "category":"Roller", "xy":"280 705"},
{"key":"P116", "category":"Roller", "xy":"200 720"},
{"key":"P117", "category":"Roller", "xy":"200 620"},
{"key":"D1", "category":"Drum", "xy":"300 540"},
{"key":"D2", "category":"Drum", "xy":"300 622"},
{"key":"B1", "category":"Belt", "color":"blue",
"guides":[ {"k":"D2"},{"k":"P111"},{"k":"P112", "outside":true},{"k":"P113", "outside":true},{"k":"P114"},{"k":"P115", "outside":true},{"k":"P116"},{"k":"P117"} ]},
{"key":"P211", "category":"Roller", "xy":"100 750"},
{"key":"P212", "category":"Roller", "xy":"150 800"},
{"key":"B2", "category":"Belt", "color":"green",
"guides":[ {"k":"P211"},{"k":"P116"},{"k":"P212"} ]}
],
"linkDataArray": []}
</textarea>
</div>
</body>
</html>
+589
View File
@@ -0,0 +1,589 @@
<!DOCTYPE html>
<html>
<head>
<title>Simple Block Editor</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<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">
<script src="../release/go.js"></script>
<script src="../extensions/Figures.js"></script>
<script src="../extensions/DrawCommandHandler.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;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
padding: 20, // extra space when scrolled all the way
grid: $(go.Panel, "Grid", // a simple 10x10 grid
$(go.Shape, "LineH", { stroke: "lightgray", strokeWidth: 0.5 }),
$(go.Shape, "LineV", { stroke: "lightgray", strokeWidth: 0.5 })
),
"draggingTool.isGridSnapEnabled": true,
handlesDragDropForTopLevelParts: true,
mouseDrop: function(e) {
// when the selection is dropped in the diagram's background,
// make sure the selected Parts no longer belong to any Group
var ok = e.diagram.commandHandler.addTopLevelParts(e.diagram.selection, true);
if (!ok) e.diagram.currentTool.doCancel();
},
commandHandler: $(DrawCommandHandler), // support offset copy-and-paste
"clickCreatingTool.archetypeNodeData": { text: "NEW NODE" }, // create a new node by double-clicking in background
"PartCreated": function(e) {
var node = e.subject; // the newly inserted Node -- now need to snap its location to the grid
node.location = node.location.copy().snapToGridPoint(e.diagram.grid.gridOrigin, e.diagram.grid.gridCellSize);
setTimeout(function() { // and have the user start editing its text
e.diagram.commandHandler.editTextBlock();
}, 20);
},
"commandHandler.archetypeGroupData": { isGroup: true, text: "NEW GROUP" },
"SelectionGrouped": function(e) {
var group = e.subject;
setTimeout(function() { // and have the user start editing its text
e.diagram.commandHandler.editTextBlock();
})
},
"LinkRelinked": function(e) {
// re-spread the connections of other links connected with both old and new nodes
var oldnode = e.parameter.part;
oldnode.invalidateConnectedLinks();
var link = e.subject;
if (e.diagram.toolManager.linkingTool.isForwards) {
link.toNode.invalidateConnectedLinks();
} else {
link.fromNode.invalidateConnectedLinks();
}
},
"undoManager.isEnabled": true
});
// Node template
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{
locationSpot: go.Spot.Center, locationObjectName: "SHAPE",
desiredSize: new go.Size(120, 60), minSize: new go.Size(40, 40),
resizable: true, resizeCellSize: new go.Size(20, 20)
},
// these Bindings are TwoWay because the DraggingTool and ResizingTool modify the target properties
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
$(go.Shape,
{ // the border
name: "SHAPE", fill: "white",
portId: "", cursor: "pointer",
fromLinkable: true, toLinkable: true,
fromLinkableDuplicates: true, toLinkableDuplicates: true,
fromSpot: go.Spot.AllSides, toSpot: go.Spot.AllSides
},
new go.Binding("figure"),
new go.Binding("fill"),
new go.Binding("stroke", "color"),
new go.Binding("strokeWidth", "thickness"),
new go.Binding("strokeDashArray", "dash")),
// this Shape prevents mouse events from reaching the middle of the port
$(go.Shape, { width: 100, height: 40, strokeWidth: 0, fill: "transparent" }),
$(go.TextBlock,
{ margin: 1, textAlign: "center", overflow: go.TextBlock.OverflowEllipsis, editable: true },
// this Binding is TwoWay due to the user editing the text with the TextEditingTool
new go.Binding("text").makeTwoWay(),
new go.Binding("stroke", "color"))
);
myDiagram.nodeTemplate.toolTip =
$("ToolTip", // show some detailed information
$(go.Panel, "Vertical",
{ maxSize: new go.Size(200, NaN) }, // limit width but not height
$(go.TextBlock,
{ font: "bold 10pt sans-serif", textAlign: "center" },
new go.Binding("text")),
$(go.TextBlock,
{ font: "10pt sans-serif", textAlign: "center" },
new go.Binding("text", "details"))
)
);
// Node selection adornment
// Include four large triangular buttons so that the user can easily make a copy
// of the node, move it to be in that direction relative to the original node,
// and add a link to the new node.
function makeArrowButton(spot, fig) {
var maker = function(e, shape) {
e.handled = true;
e.diagram.model.commit(function(m) {
var selnode = shape.part.adornedPart;
// create a new node in the direction of the spot
var p = new go.Point().setRectSpot(selnode.actualBounds, spot);
p.subtract(selnode.location);
p.scale(2, 2);
p.x += Math.sign(p.x) * 60;
p.y += Math.sign(p.y) * 60;
p.add(selnode.location);
p.snapToGridPoint(e.diagram.grid.gridOrigin, e.diagram.grid.gridCellSize);
// make the new node a copy of the selected node
var nodedata = m.copyNodeData(selnode.data);
// add to same group as selected node
m.setGroupKeyForNodeData(nodedata, m.getGroupKeyForNodeData(selnode.data));
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
// move the new node to the computed location, select it, and start to edit it
var newnode = e.diagram.findNodeForData(nodedata);
newnode.location = p;
e.diagram.select(newnode);
setTimeout(function() {
e.diagram.commandHandler.editTextBlock();
}, 20);
});
};
return $(go.Shape,
{
figure: fig,
alignment: spot, alignmentFocus: spot.opposite(),
width: (spot.equals(go.Spot.Top) || spot.equals(go.Spot.Bottom)) ? 36 : 18,
height: (spot.equals(go.Spot.Top) || spot.equals(go.Spot.Bottom)) ? 18 : 36,
fill: "orange", strokeWidth: 0,
isActionable: true, // needed because it's in an Adornment
click: maker, contextClick: maker
});
}
// create a button that brings up the context menu
function CMButton(options) {
return $(go.Shape,
{
fill: "orange", stroke: "gray", background: "transparent",
geometryString: "F1 M0 0 M0 4h4v4h-4z M6 4h4v4h-4z M12 4h4v4h-4z M0 12",
isActionable: true, cursor: "context-menu",
click: function(e, shape) {
e.diagram.commandHandler.showContextMenu(shape.part.adornedPart);
}
},
options || {});
}
myDiagram.nodeTemplate.selectionAdornmentTemplate =
$(go.Adornment, "Spot",
$(go.Placeholder, { padding: 10 }),
makeArrowButton(go.Spot.Top, "TriangleUp"),
makeArrowButton(go.Spot.Left, "TriangleLeft"),
makeArrowButton(go.Spot.Right, "TriangleRight"),
makeArrowButton(go.Spot.Bottom, "TriangleDown"),
CMButton({ alignment: new go.Spot(0.75, 0) })
);
// Common context menu button definitions
// All buttons in context menu work on both click and contextClick,
// in case the user context-clicks on the button.
// All buttons modify the node data, not the Node, so the Bindings need not be TwoWay.
// A button-defining helper function that returns a click event handler.
// PROPNAME is the name of the data property that should be set to the given VALUE.
function ClickFunction(propname, value) {
return function(e, obj) {
e.handled = true; // don't let the click bubble up
e.diagram.model.commit(function(m) {
m.set(obj.part.adornedPart.data, propname, value);
});
};
}
// Create a context menu button for setting a data property with a color value.
function ColorButton(color, propname) {
if (!propname) propname = "color";
return $(go.Shape,
{
width: 16, height: 16, stroke: "lightgray", fill: color,
margin: 1, background: "transparent",
mouseEnter: function(e, shape) { shape.stroke = "dodgerblue"; },
mouseLeave: function(e, shape) { shape.stroke = "lightgray"; },
click: ClickFunction(propname, color), contextClick: ClickFunction(propname, color)
});
}
function LightFillButtons() { // used by multiple context menus
return [
$("ContextMenuButton",
$(go.Panel, "Horizontal",
ColorButton("white", "fill"), ColorButton("beige", "fill"), ColorButton("aliceblue", "fill"), ColorButton("lightyellow", "fill")
)
),
$("ContextMenuButton",
$(go.Panel, "Horizontal",
ColorButton("lightgray", "fill"), ColorButton("lightgreen", "fill"), ColorButton("lightblue", "fill"), ColorButton("pink", "fill")
)
)
];
}
function DarkColorButtons() { // used by multiple context menus
return [
$("ContextMenuButton",
$(go.Panel, "Horizontal",
ColorButton("black"), ColorButton("green"), ColorButton("blue"), ColorButton("red")
)
),
$("ContextMenuButton",
$(go.Panel, "Horizontal",
ColorButton("brown"), ColorButton("magenta"), ColorButton("purple"), ColorButton("orange")
)
)
];
}
// Create a context menu button for setting a data property with a stroke width value.
function ThicknessButton(sw, propname) {
if (!propname) propname = "thickness";
return $(go.Shape, "LineH",
{
width: 16, height: 16, strokeWidth: sw,
margin: 1, background: "transparent",
mouseEnter: function(e, shape) { shape.background = "dodgerblue"; },
mouseLeave: function(e, shape) { shape.background = "transparent"; },
click: ClickFunction(propname, sw), contextClick: ClickFunction(propname, sw)
});
}
// Create a context menu button for setting a data property with a stroke dash Array value.
function DashButton(dash, propname) {
if (!propname) propname = "dash";
return $(go.Shape, "LineH",
{
width: 24, height: 16, strokeWidth: 2,
strokeDashArray: dash,
margin: 1, background: "transparent",
mouseEnter: function(e, shape) { shape.background = "dodgerblue"; },
mouseLeave: function(e, shape) { shape.background = "transparent"; },
click: ClickFunction(propname, dash), contextClick: ClickFunction(propname, dash)
});
}
function StrokeOptionsButtons() { // used by multiple context menus
return [
$("ContextMenuButton",
$(go.Panel, "Horizontal",
ThicknessButton(1), ThicknessButton(2), ThicknessButton(3), ThicknessButton(4)
)
),
$("ContextMenuButton",
$(go.Panel, "Horizontal",
DashButton(null), DashButton([2, 4]), DashButton([4, 4])
)
)
];
}
// Node context menu
function FigureButton(fig, propname) {
if (!propname) propname = "figure";
return $(go.Shape,
{
width: 32, height: 32, scale: 0.5, fill: "lightgray", figure: fig,
margin: 1, background: "transparent",
mouseEnter: function(e, shape) { shape.fill = "dodgerblue"; },
mouseLeave: function(e, shape) { shape.fill = "lightgray"; },
click: ClickFunction(propname, fig), contextClick: ClickFunction(propname, fig)
});
}
myDiagram.nodeTemplate.contextMenu =
$("ContextMenu",
$("ContextMenuButton",
$(go.Panel, "Horizontal",
FigureButton("Rectangle"), FigureButton("RoundedRectangle"), FigureButton("Ellipse"), FigureButton("Diamond")
)
),
$("ContextMenuButton",
$(go.Panel, "Horizontal",
FigureButton("Parallelogram2"), FigureButton("ManualOperation"), FigureButton("Procedure"), FigureButton("Cylinder1")
)
),
$("ContextMenuButton",
$(go.Panel, "Horizontal",
FigureButton("Terminator"), FigureButton("CreateRequest"), FigureButton("Document"), FigureButton("TriangleDown")
)
),
LightFillButtons(),
DarkColorButtons(),
StrokeOptionsButtons()
);
// Group template
myDiagram.groupTemplate =
$(go.Group, "Spot",
{
layerName: "Background",
ungroupable: true,
locationSpot: go.Spot.Center,
selectionObjectName: "BODY",
computesBoundsAfterDrag: true, // allow dragging out of a Group that uses a Placeholder
handlesDragDropForMembers: true, // don't need to define handlers on Nodes and Links
mouseDrop: function(e, grp) { // add dropped nodes as members of the group
var ok = grp.addMembers(grp.diagram.selection, true);
if (!ok) grp.diagram.currentTool.doCancel();
},
avoidable: false
},
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
$(go.Panel, "Auto",
{ name: "BODY" },
$(go.Shape,
{
parameter1: 10,
fill: "white", strokeWidth: 2,
portId: "", cursor: "pointer",
fromLinkable: true, toLinkable: true,
fromLinkableDuplicates: true, toLinkableDuplicates: true,
fromSpot: go.Spot.AllSides, toSpot: go.Spot.AllSides
},
new go.Binding("fill"),
new go.Binding("stroke", "color"),
new go.Binding("strokeWidth", "thickness"),
new go.Binding("strokeDashArray", "dash")),
$(go.Placeholder,
{ background: "transparent", margin: 10 })
),
$(go.TextBlock,
{
alignment: go.Spot.Top, alignmentFocus: go.Spot.Bottom,
font: "bold 12pt sans-serif", editable: true
},
new go.Binding("text"),
new go.Binding("stroke", "color"))
);
myDiagram.groupTemplate.selectionAdornmentTemplate =
$(go.Adornment, "Spot",
$(go.Panel, "Auto",
$(go.Shape, { fill: null, stroke: "dodgerblue", strokeWidth: 3 }),
$(go.Placeholder, { margin: 1.5 })
),
CMButton({ alignment: go.Spot.TopRight, alignmentFocus: go.Spot.BottomRight })
);
myDiagram.groupTemplate.contextMenu =
$("ContextMenu",
LightFillButtons(),
DarkColorButtons(),
StrokeOptionsButtons()
);
// Link template
myDiagram.linkTemplate =
$(go.Link,
{
layerName: "Foreground",
routing: go.Link.AvoidsNodes, corner: 10,
toShortLength: 4, // assume arrowhead at "to" end, need to avoid bad appearance when path is thick
relinkableFrom: true, relinkableTo: true,
reshapable: true, resegmentable: true
},
new go.Binding("fromSpot", "fromSpot", go.Spot.parse),
new go.Binding("toSpot", "toSpot", go.Spot.parse),
new go.Binding("fromShortLength", "dir", function(dir) { return dir === 2 ? 4 : 0; }),
new go.Binding("toShortLength", "dir", function(dir) { return dir >= 1 ? 4 : 0; }),
new go.Binding("points").makeTwoWay(), // TwoWay due to user reshaping with LinkReshapingTool
$(go.Shape, { strokeWidth: 2 },
new go.Binding("stroke", "color"),
new go.Binding("strokeWidth", "thickness"),
new go.Binding("strokeDashArray", "dash")),
$(go.Shape, { fromArrow: "Backward", strokeWidth: 0, scale: 4/3, visible: false },
new go.Binding("visible", "dir", function(dir) { return dir === 2; }),
new go.Binding("fill", "color"),
new go.Binding("scale", "thickness", function(t) { return (2+t)/3; })),
$(go.Shape, { toArrow: "Standard", strokeWidth: 0, scale: 4/3 },
new go.Binding("visible", "dir", function(dir) { return dir >= 1; }),
new go.Binding("fill", "color"),
new go.Binding("scale", "thickness", function(t) { return (2+t)/3; })),
$(go.TextBlock,
{ alignmentFocus: new go.Spot(0, 1, -4, 0), editable: true },
new go.Binding("text").makeTwoWay(), // TwoWay due to user editing with TextEditingTool
new go.Binding("stroke", "color"))
);
myDiagram.linkTemplate.selectionAdornmentTemplate =
$(go.Adornment, // use a special selection Adornment that does not obscure the link path itself
$(go.Shape,
{ // this uses a pathPattern with a gap in it, in order to avoid drawing on top of the link path Shape
isPanelMain: true,
stroke: "transparent", strokeWidth: 6,
pathPattern: makeAdornmentPathPattern(2) // == thickness or strokeWidth
},
new go.Binding("pathPattern", "thickness", makeAdornmentPathPattern)),
CMButton({ alignmentFocus: new go.Spot(0, 0, -6, -4) })
);
function makeAdornmentPathPattern(w) {
return $(go.Shape,
{
stroke: "dodgerblue", strokeWidth: 2, strokeCap: "square",
geometryString: "M0 0 M4 2 H3 M4 " + (w+4).toString() + " H3"
});
}
// Link context menu
// All buttons in context menu work on both click and contextClick,
// in case the user context-clicks on the button.
// All buttons modify the link data, not the Link, so the Bindings need not be TwoWay.
function ArrowButton(num) {
var geo = "M0 0 M16 16 M0 8 L16 8 M12 11 L16 8 L12 5";
if (num === 0) {
geo = "M0 0 M16 16 M0 8 L16 8";
} else if (num === 2) {
geo = "M0 0 M16 16 M0 8 L16 8 M12 11 L16 8 L12 5 M4 11 L0 8 L4 5";
}
return $(go.Shape,
{
geometryString: geo,
margin: 2, background: "transparent",
mouseEnter: function(e, shape) { shape.background = "dodgerblue"; },
mouseLeave: function(e, shape) { shape.background = "transparent"; },
click: ClickFunction("dir", num), contextClick: ClickFunction("dir", num)
});
}
function AllSidesButton(to) {
var setter = function(e, shape) {
e.handled = true;
e.diagram.model.commit(function(m) {
var link = shape.part.adornedPart;
m.set(link.data, (to ? "toSpot" : "fromSpot"), go.Spot.stringify(go.Spot.AllSides));
// re-spread the connections of other links connected with the node
(to ? link.toNode : link.fromNode).invalidateConnectedLinks();
});
};
return $(go.Shape,
{
width: 12, height: 12, fill: "transparent",
mouseEnter: function(e, shape) { shape.background = "dodgerblue"; },
mouseLeave: function(e, shape) { shape.background = "transparent"; },
click: setter, contextClick: setter
});
}
function SpotButton(spot, to) {
var ang = 0;
var side = go.Spot.RightSide;
if (spot.equals(go.Spot.Top)) { ang = 270; side = go.Spot.TopSide; }
else if (spot.equals(go.Spot.Left)) { ang = 180; side = go.Spot.LeftSide; }
else if (spot.equals(go.Spot.Bottom)) { ang = 90; side = go.Spot.BottomSide; }
if (!to) ang -= 180;
var setter = function(e, shape) {
e.handled = true;
e.diagram.model.commit(function(m) {
var link = shape.part.adornedPart;
m.set(link.data, (to ? "toSpot" : "fromSpot"), go.Spot.stringify(side));
// re-spread the connections of other links connected with the node
(to ? link.toNode : link.fromNode).invalidateConnectedLinks();
});
};
return $(go.Shape,
{
alignment: spot, alignmentFocus: spot.opposite(),
geometryString: "M0 0 M12 12 M12 6 L1 6 L4 4 M1 6 L4 8",
angle: ang,
background: "transparent",
mouseEnter: function(e, shape) { shape.background = "dodgerblue"; },
mouseLeave: function(e, shape) { shape.background = "transparent"; },
click: setter, contextClick: setter
});
}
myDiagram.linkTemplate.contextMenu =
$("ContextMenu",
DarkColorButtons(),
StrokeOptionsButtons(),
$("ContextMenuButton",
$(go.Panel, "Horizontal",
ArrowButton(0), ArrowButton(1), ArrowButton(2)
)
),
$("ContextMenuButton",
$(go.Panel, "Horizontal",
$(go.Panel, "Spot",
AllSidesButton(false),
SpotButton(go.Spot.Top, false), SpotButton(go.Spot.Left, false), SpotButton(go.Spot.Right, false), SpotButton(go.Spot.Bottom, false)
),
$(go.Panel, "Spot",
{ margin: new go.Margin(0, 0, 0, 2) },
AllSidesButton(true),
SpotButton(go.Spot.Top, true), SpotButton(go.Spot.Left, true), SpotButton(go.Spot.Right, true), SpotButton(go.Spot.Bottom, true)
)
)
)
);
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:600px"></div>
<p>
Double-click in the background to create a new node.
Create groups by selecting nodes and invoking Ctrl-G; Ctrl-Shift-G to ungroup a selected group.
A selected node will have four orange triangles that when clicked will automatically copy the node and link to it.
Use the context menu to change the shape, color, thickness, and dashed-ness.
</p>
<p>
Links can be drawn by dragging from the side of each node.
A selected link can be reconnected by dragging an end handle.
Use the context menu to change the color, thickness, dashed-ness, and which side the link should connect with.
</p>
<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, "loc":"0 0", "text":"Alpha", "details":"some information about Alpha and its importance"},
{"key":2, "loc":"170 0", "text":"Beta", "color":"blue", "thickness":2, "figure":"Procedure"},
{"key":3, "loc":"0 100", "text":"Gamma", "color":"green", "figure":"Cylinder1"},
{"key":4, "loc":"80 180", "text":"Delta", "color":"red", "figure":"Terminator", "size":"80 40"},
{"key":5, "loc":"350 -50", "text":"Zeta", "group":7, "color":"blue", "figure":"CreateRequest"},
{"key":6, "loc":"350 50", "text":"Eta", "group":7, "figure":"Document", "fill":"lightyellow"},
{"key":7, "isGroup":true, "text":"Theta", "color":"green", "fill":"lightgreen"},
{"key":8, "loc":"520 50", "text":"Iota", "fill":"pink"}
],
"linkDataArray": [
{"from":1, "to":2, "dash":[ 6,3 ], "thickness":4},
{"from":1, "to":3, "dash":[ 2,4 ], "color":"green", "text":"label"},
{"from":3, "to":4, "color":"red", "text":"a red label", "fromSpot":"RightSide"},
{"from":2, "to":1},
{"from":5, "to":6, "text":"in a group"},
{"from":2, "to":7},
{"from":6, "to":8, "dir":0},
{"from":6, "to":8, "dir":1},
{"from":6, "to":8, "dir":2}
]}
</textarea>
</div>
</body>
</html>
+293
View File
@@ -0,0 +1,293 @@
<!DOCTYPE html>
<html>
<head>
<title>Circular Layout</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Interactive demonstration of circular layout features by the CircularLayout class." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
myDiagram =
$(go.Diagram, "myDiagramDiv", // must be the ID or reference to div
{
initialAutoScale: go.Diagram.UniformToFill,
layout: $(go.CircularLayout)
// other properties are set by the layout function, defined below
});
// define the Node template
myDiagram.nodeTemplate =
$(go.Node, "Spot",
// make sure the Node.location is different from the Node.position
{ locationSpot: go.Spot.Center },
new go.Binding("text", "text"), // for sorting
$(go.Shape, "Ellipse", // the default value for the Shape.figure property
{
fill: "lightgray",
stroke: null,
desiredSize: new go.Size(30, 30)
},
new go.Binding("figure", "figure"),
new go.Binding("fill", "fill"),
new go.Binding("desiredSize", "size")),
$(go.TextBlock,
new go.Binding("text", "text"))
);
// define the Link template
myDiagram.linkTemplate =
$(go.Link,
{ selectable: false },
$(go.Shape,
{ strokeWidth: 3, stroke: "#333" }));
// generate a circle using the default values
rebuildGraph();
}
function rebuildGraph() {
var numNodes = document.getElementById("numNodes").value;
numNodes = parseInt(numNodes, 10);
if (isNaN(numNodes)) numNodes = 16;
var width = document.getElementById("width").value;
width = parseFloat(width, 10);
var height = document.getElementById("height").value;
height = parseFloat(height, 10);
var randSizes = document.getElementById("randSizes").checked;
var circ = document.getElementById("circ").checked;
var cyclic = document.getElementById("cyclic").checked;
var minLinks = document.getElementById("minLinks").value;
minLinks = parseInt(minLinks, 10);
var maxLinks = document.getElementById("maxLinks").value;
maxLinks = parseInt(maxLinks, 10);
generateCircle(numNodes, width, height, minLinks, maxLinks, randSizes, circ, cyclic);
}
function generateCircle(numNodes, width, height, minLinks, maxLinks, randSizes, circ, cyclic) {
myDiagram.startTransaction("generateCircle");
// replace the diagram's model's nodeDataArray
generateNodes(numNodes, width, height, randSizes, circ);
// replace the diagram's model's linkDataArray
generateLinks(minLinks, maxLinks, cyclic);
// force a diagram layout
layout();
myDiagram.commitTransaction("generateCircle");
}
function generateNodes(numNodes, width, height, randSizes, circ) {
var nodeArray = [];
for (var i = 0; i < numNodes; i++) {
var size;
if (randSizes) {
size = new go.Size(Math.floor(Math.random() * (65 - width + 1)) + width, Math.floor(Math.random() * (65 - height + 1)) + height);
} else {
size = new go.Size(width, height);
}
if (circ) size.height = size.width;
var figure = "Rectangle";
if (circ) figure = "Ellipse";
nodeArray.push({
key: i,
text: i.toString(),
figure: figure,
fill: go.Brush.randomColor(),
size: size
});
}
// randomize the data, to help demonstrate sorting
for (i = 0; i < nodeArray.length; i++) {
var swap = Math.floor(Math.random() * nodeArray.length);
var temp = nodeArray[swap];
nodeArray[swap] = nodeArray[i];
nodeArray[i] = temp;
}
// set the nodeDataArray to this array of objects
myDiagram.model.nodeDataArray = nodeArray;
}
function generateLinks(min, max, cyclic) {
if (myDiagram.nodes.count < 2) return;
var linkArray = [];
var nit = myDiagram.nodes;
var nodes = new go.List(/*go.Node*/);
nodes.addAll(nit);
var num = nodes.length;
if (cyclic) {
for (var i = 0; i < num; i++) {
if (i >= num - 1) {
linkArray.push({ from: i, to: 0 });
} else {
linkArray.push({ from: i, to: i + 1 });
}
}
} else {
if (isNaN(min) || min < 0) min = 0;
if (isNaN(max) || max < min) max = min;
for (var i = 0; i < num; i++) {
var next = nodes.get(i);
var children = Math.floor(Math.random() * (max - min + 1)) + min;
for (var j = 1; j <= children; j++) {
var to = nodes.get(Math.floor(Math.random() * num));
// get keys from the Node.text strings
var nextKey = parseInt(next.text, 10);
var toKey = parseInt(to.text, 10);
if (nextKey !== toKey) {
linkArray.push({ from: nextKey, to: toKey });
}
}
}
}
myDiagram.model.linkDataArray = linkArray;
}
// Update the layout from the controls, and then perform the layout again
function layout() {
myDiagram.startTransaction("change Layout");
var lay = myDiagram.layout;
var radius = document.getElementById("radius").value;
if (radius !== "NaN") radius = parseFloat(radius, 10);
else radius = NaN;
lay.radius = radius;
var aspectRatio = document.getElementById("aspectRatio").value;
aspectRatio = parseFloat(aspectRatio, 10);
lay.aspectRatio = aspectRatio;
var startAngle = document.getElementById("startAngle").value;
startAngle = parseFloat(startAngle, 10);
lay.startAngle = startAngle;
var sweepAngle = document.getElementById("sweepAngle").value;
sweepAngle = parseFloat(sweepAngle, 10);
lay.sweepAngle = sweepAngle;
var spacing = document.getElementById("spacing").value;
spacing = parseFloat(spacing, 10);
lay.spacing = spacing;
var arrangement = document.getElementById("arrangement").value;
if (arrangement === "ConstantDistance") lay.arrangement = go.CircularLayout.ConstantDistance;
else if (arrangement === "ConstantAngle") lay.arrangement = go.CircularLayout.ConstantAngle;
else if (arrangement === "ConstantSpacing") lay.arrangement = go.CircularLayout.ConstantSpacing;
else if (arrangement === "Packed") lay.arrangement = go.CircularLayout.Packed;
var diamFormula = getRadioValue("diamFormula");
if (diamFormula === "Pythagorean") lay.nodeDiameterFormula = go.CircularLayout.Pythagorean;
else if (diamFormula === "Circular") lay.nodeDiameterFormula = go.CircularLayout.Circular;
var direction = document.getElementById("direction").value;
if (direction === "Clockwise") lay.direction = go.CircularLayout.Clockwise;
else if (direction === "Counterclockwise") lay.direction = go.CircularLayout.Counterclockwise;
else if (direction === "BidirectionalLeft") lay.direction = go.CircularLayout.BidirectionalLeft;
else if (direction === "BidirectionalRight") lay.direction = go.CircularLayout.BidirectionalRight;
var sorting = document.getElementById("sorting").value;
if (sorting === "Forwards") lay.sorting = go.CircularLayout.Forwards;
else if (sorting === "Reverse") lay.sorting = go.CircularLayout.Reverse;
else if (sorting === "Ascending") lay.sorting = go.CircularLayout.Ascending;
else if (sorting === "Descending") lay.sorting = go.CircularLayout.Descending;
else if (sorting === "Optimized") lay.sorting = go.CircularLayout.Optimized;
myDiagram.commitTransaction("change Layout");
}
function getRadioValue(name) {
var radio = document.getElementsByName(name);
for (var i = 0; i < radio.length; i++)
if (radio[i].checked) return radio[i].value;
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div style="margin-bottom: 5px; padding: 5px; background-color: aliceblue">
<span style="display: inline-block; vertical-align: top; padding: 5px">
<b>New Graph</b><br />
# of nodes: <input type="text" size="3" id="numNodes" value="16" /><br />
Node Size: <input type="text" size="3" id="width" value="25" /><input type="text" size="3" id="height" value="25" /><br />
Random Sizes: <input type="checkbox" id="randSizes" checked="checked" /> (&lt;= 65) <br />
Circular Nodes: <input type="checkbox" id="circ" /><br />
Graph is simple ring: <input type="checkbox" id="cyclic" /><br />
Min Links from Node: <input type="text" size="2" id="minLinks" value="1" /><br />
Max Links from Node: <input type="text" size="2" id="maxLinks" value="2" /><br />
<button type="button" onclick="rebuildGraph()">Generate Circle</button>
</span>
<span style="display: inline-block; vertical-align: top; padding: 5px">
<b>CircularLayout Properties</b><br />
Radius:
<input type="text" size="3" id="radius" value="NaN" onchange="layout()" />
(along X axis; NaN or &gt; 0)
<br />
Aspect Ratio:
<input type="text" size="2" id="aspectRatio" value="1" onchange="layout()" />
(1 is circular; &gt; 0)
<br />
Start Angle:
<input type="text" size="3" id="startAngle" value="0" onchange="layout()" />
(angle at first element)
<br />
Sweep Angle:
<input type="text" size="3" id="sweepAngle" value="360" onchange="layout()" />
(degrees occupied; &gt;= 1, &lt;= 360)
<br />
Spacing:
<input type="text" size="2" id="spacing" value="6" onchange="layout()" />
(actual spacing also depends on radius)
<br />
Arrangement:
<select name="arrangement" id="arrangement" onchange="layout()">
<option value="ConstantDistance">ConstantDistance</option>
<option value="ConstantAngle">ConstantAngle</option>
<option value="ConstantSpacing" selected="selected">ConstantSpacing</option>
<option value="Packed">Packed</option>
</select>
<br />
Node Diameter:
<input type="radio" name="diamFormula" onclick="layout()" value="Pythagorean" checked="checked" /> Pythagorean
<input type="radio" name="diamFormula" onclick="layout()" value="Circular" /> Circular<br />
Direction:
<select name="direction" id="direction" onchange="layout()">
<option value="Clockwise" selected="selected">Clockwise</option>
<option value="Counterclockwise">Counterclockwise</option>
<option value="BidirectionalLeft">BidirectionalLeft</option>
<option value="BidirectionalRight">BidirectionalRight</option>
</select>
<br />
Sorting:
<select name="sorting" id="sorting" onchange="layout()">
<option value="Forwards" selected="selected">Forwards</option>
<option value="Reverse">Reverse</option>
<option value="Ascending">Ascending</option>
<option value="Descending">Descending</option>
<option value="Optimized">Optimized</option>
</select>
(use "Optimized" to reduce the number of link crossings)
</span>
</div>
<div id="myDiagramDiv" style="border: solid 1px black; background: white; width: 100%; height: 500px;"></div>
<p>
For information on <b>CircularLayout</b> and its properties, see the <a>CircularLayout</a> documentation page.
</p>
</div>
</body>
</html>
+83
View File
@@ -0,0 +1,83 @@
<!DOCTYPE html>
<html>
<head>
<title>Candlestick or Range Charts</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="GoJS nodes containing simple range charts." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
myDiagram =
$(go.Diagram, "myDiagramDiv");
// the template for each attribute in a node's array of item data
var itemTempl =
$(go.Panel, "TableRow",
$(go.TextBlock,
{ column: 0 },
new go.Binding("text")),
$(go.Shape,
{ column: 1, alignment: go.Spot.Left },
{ fill: "slateblue", stroke: "darkblue" },
new go.Binding("geometry", "", produceRange)));
function produceRange(d) {
var h = 12; // total height for the markers
var w = 3; // half width for the median marker
// using constructors is more efficient than calling go.GraphObject.make:
return new go.Geometry()
.add(new go.PathFigure(d.min, h / 2, false)
.add(new go.PathSegment(go.PathSegment.Line, d.max, h / 2)))
.add(new go.PathFigure(d.min, 0, false)
.add(new go.PathSegment(go.PathSegment.Line, d.min, h)))
.add(new go.PathFigure(d.max, 0, false)
.add(new go.PathSegment(go.PathSegment.Line, d.max, h)))
.add(new go.PathFigure(d.val - w, 0)
.add(new go.PathSegment(go.PathSegment.Line, d.val + w, 0))
.add(new go.PathSegment(go.PathSegment.Line, d.val + w, h))
.add(new go.PathSegment(go.PathSegment.Line, d.val - w, h).close()));
}
myDiagram.nodeTemplate =
$(go.Node, "Auto",
$(go.Shape,
{ fill: "white" }),
$(go.Panel, "Table",
{
margin: 6,
itemTemplate: itemTempl
},
new go.Binding("itemArray", "items")));
var nodeDataArray = [
{
items: [{ text: "first", min: 10, val: 50, max: 60 },
{ text: "second", min: 20, val: 70, max: 90 },
{ text: "third", min: 40, val: 60, max: 110 },
{ text: "fourth", min: 50, val: 80, max: 130 }]
}
];
myDiagram.model = $(go.GraphLinksModel,
{
copiesArrays: true,
copiesArrayObjects: true,
nodeDataArray: nodeDataArray
});
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="background-color: white; border: solid 1px black; width: 100%; height: 500px"></div>
</div>
<p>
For more sophisticated charts within nodes, see the <a href="canvases.html">Canvas Charts</a> sample.
</p>
</body>
</html>
+206
View File
@@ -0,0 +1,206 @@
<!DOCTYPE html>
<html>
<head>
<title>Various Charts in GoJS Nodes</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A diagram where each node contains a chart rendered by Chart.js." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.bundle.min.js"></script>
<script id="code">
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: $(go.TreeLayout)
});
myDiagram.nodeTemplate =
$(go.Node, "Vertical",
$(go.Panel, "Auto",
$(go.Shape, { fill: "transparent" },
new go.Binding("stroke", "color")),
$(go.Picture,
{ width: 300, height: 150, portId: "" },
new go.Binding("element", "datasets", makeLineChart))
),
$(go.TextBlock,
{ margin: 8 },
new go.Binding("text"))
);
// This Binding conversion function creates a Canvas element for a Picture
// that has a rendering of a line chart drawn by Chart.js.
function makeLineChart(datasets, picture) {
var canvases = document.getElementById("myCanvases");
canv = document.createElement("canvas");
canv.width = canv.style.width = "600px";
canv.height = canv.style.height = "300px";
// apparently Chart.js expects the Canvas to be in a DIV
var div = document.createElement("div");
div.style.position = "absolute";
div.appendChild(canv);
// add the DIV/Canvas to the DOM, temporarily
canvases.appendChild(div);
var config = { // Chart.js configuration, including the DATASETS data from the model data
type: "line",
data: {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: datasets
},
options: {
animation: {
onProgress: function() { picture.redraw(); },
onComplete: function() {
var canvases = document.getElementById("myCanvases");
if (canvases) { // remove the Canvas that was in the DOM for rendering
canvases.removeChild(div);
}
picture.redraw();
}
}
}
};
new Chart(canv, config);
return canv;
}
myDiagram.model = $(go.GraphLinksModel,
{
copiesArrays: true,
copiesArrayObjects: true,
nodeDataArray:
[
{
key: 1, text: "Alpha",
datasets: [{
label: "Random data",
borderColor: "black",
data: makeRandomPoints(8, 10)
}]
},
{
key: 2, text: "Beta",
datasets: [{
label: "First dataset",
fill: false,
backgroundColor: "red",
borderColor: "red",
data: makeRandomPoints(8)
}, {
label: "Second dataset",
fill: false,
backgroundColor: "blue",
borderColor: "blue",
data: makeRandomPoints(8)
}]
},
{
key: 3, text: "Gamma", color: "green",
datasets: [{
label: "some data",
fill: false,
backgroundColor: "green",
borderColor: "green",
data: makeRandomPoints()
}]
}
],
linkDataArray: [
{ from: 1, to: 2 },
{ from: 1, to: 3 }
]
});
}
function makeRandomPoints(num, range) {
if (!num) num = 20;
if (!range) range = 100;
var pts = [];
for (var i = 0; i < num; i++) {
pts.push(Math.random() * range);
}
return pts;
}
function addNode() {
myDiagram.model.commit(function(m) {
var firstnode = myDiagram.nodes.first();
var color = go.Brush.darken(go.Brush.randomColor());
var data = {
text: "Node " + (myDiagram.nodes.count + 1),
color: color,
datasets: [{
label: "some data",
fill: false,
backgroundColor: color,
borderColor: color,
data: makeRandomPoints()
}]
};
m.addNodeData(data);
if (firstnode) {
m.addLinkData({ from: firstnode.key, to: m.getKeyForNodeData(data) });
// new node starts off at same location as the parent node
var newnode = myDiagram.findNodeForData(data);
if (newnode) newnode.location = firstnode.location;
}
}, "added chart node");
}
function modifyNodes() {
myDiagram.commit(function(diag) {
diag.selection.each(function(node) {
var oldset = node.data.datasets;
if (!oldset) return; // if it's a link, there's no datasets property
diag.model.set(node.data, "datasets",
[{
label: oldset[0].label,
fill: false,
backgroundColor: oldset[0].backgroundColor,
borderColor: oldset[0].borderColor,
data: makeRandomPoints()
}]);
});
}, "modified selected nodes");
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
<button onclick="addNode()">Add Chart</button>
<button onclick="modifyNodes()">Modify Charts of Selected Nodes</button>
<p>
This app demonstrates using a popular charting library, <a href="https://chartjs.org">Chart.js</a>,
for rendering charts within nodes.
</p>
<p>
The data for each chart is stored on the node data in the model. In this case the <code>datasets</code>
property value has the same properties that are expected by the Chart.js configuration, but you could
organize the data however you want.
</p>
<p>
The <code>makeLineChart</code> conversion function is used by a <a>Binding</a> on <a>Picture.element</a>
to generate a Canvas element that can be shown in the node. Most of the implementation of that function
is specific to Chart.js. The rendering requires the Canvas to be in the HTML DOM. To avoid accumulating
resources, the configuration of the chart defines an <code>onComplete</code> event handler to
remove the Canvas element from the DOM. That allows any future removal of the Node from the Diagram not
to leave an unused Canvas element behind.
</p>
<!-- myCanvases is used by makeLineChart, but need not be seen by the user -->
<div id="myCanvases" style="position:absolute; top:0px; left:0px; width:0px; height:0px; opacity:0"></div>
</div>
</body>
</html>
+116
View File
@@ -0,0 +1,116 @@
<!DOCTYPE html>
<html>
<head>
<title>GoJS Class Hierarchy Tree</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="The JavaScript class hierarchy defined by the GoJS library, arranged as a tree." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../extensions/HyperlinkText.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 diagram = $(go.Diagram, "myDiagramDiv", // id of DIV
{ // Automatically lay out the diagram as a tree;
// separate trees are arranged vertically above each other.
layout: $(go.TreeLayout, { nodeSpacing: 3 })
});
// Define a node template showing class names.
// Clicking on the node opens up the documentation for that class.
diagram.nodeTemplate =
$(go.Node,
$("HyperlinkText",
// compute the URL to open for the documentation
function(node) { return "../api/symbols/" + node.data.key + ".html"; },
// define the visuals for the hyperlink, basically the whole node:
$(go.Panel, "Auto",
$(go.Shape, { fill: "#1F4963", stroke: null }),
$(go.TextBlock,
{
font: "bold 13px Helvetica, bold Arial, sans-serif",
stroke: "white", margin: 3
},
new go.Binding("text", "key"))
)
)
);
// Define a trivial link template with no arrowhead
diagram.linkTemplate =
$(go.Link,
{
curve: go.Link.Bezier,
toEndSegmentLength: 30, fromEndSegmentLength: 30
},
$(go.Shape, { strokeWidth: 1.5 }) // the link shape, with the default black stroke
);
// Collect all of the data for the model of the class hierarchy
var nodeDataArray = [];
// Iterate over all of the classes in "go"
for (k in go) {
var cls = go[k];
if (!cls) continue;
var proto = cls.prototype;
if (!proto) continue;
if (k === 'EnumValue' || k === 'TextBlockMetrics') continue; // undocumented classes
proto.constructor.className = k; // remember name
// find base class constructor
var base = Object.getPrototypeOf(proto).constructor;
if (base === Object) { // "root" node?
nodeDataArray.push({ key: k });
} else {
// add a node for this class and a tree-parent reference to the base class name
nodeDataArray.push({ key: k, parent: base.className });
}
}
// Create the model for the hierarchy diagram
diagram.model = new go.TreeModel(nodeDataArray);
// Now collect all node data that are singletons
var singlesArray = []; // for classes that don't inherit from another class
diagram.nodes.each(function(node) {
if (node.linksConnected.count === 0) {
singlesArray.push(node.data);
}
});
// Remove the unconnected class nodes from the main Diagram
diagram.model.removeNodeDataCollection(singlesArray);
// Display the unconnected classes in a separate Diagram
var singletons =
$(go.Diagram, "mySingletons",
{
nodeTemplate: diagram.nodeTemplate, // share the node template with the main Diagram
layout:
$(go.GridLayout,
{
wrappingColumn: 1, // put the unconnected nodes in a column
spacing: new go.Size(3, 3)
}),
model: new go.Model(singlesArray) // use a separate model
});
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div style="width: 100%; display: flex; justify-content: space-between">
<div id="myDiagramDiv" style="flex-grow: 1; height: 725px; margin-right: 4px; border: solid 1px black"></div>
<div id="mySingletons" style="width: 160px; background-color: whitesmoke; border: solid 1px black"></div>
</div>
<p>The JavaScript class hierarchy defined by the GoJS library, laid out by a <a>TreeLayout</a>.
Classes that do not have any inheritance relationship are shown at the right.</p>
<p>Because the node template uses a "HyperlinkText", clicking on a node will open the API reference for that class in a new window.</p>
<p>For more uses of the Tree Layout, see the <a href="DOMTree.html">DOM Tree</a> and <a href="visualTree.html">Visual Tree</a> samples.</p>
</div>
</body>
</html>
+136
View File
@@ -0,0 +1,136 @@
<!DOCTYPE html>
<html>
<head>
<title>Comments</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A tree-structured diagram annotated with balloon comments, automatically laid out." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../extensions/BalloonLink.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;
myDiagram =
$(go.Diagram, "myDiagramDiv", // create a Diagram for the DIV HTML element
{
layout: $(go.TreeLayout,
{
angle: 90,
setsPortSpot: false,
setsChildPortSpot: false
}),
"undoManager.isEnabled": true,
// When a Node is deleted by the user, also delete all of its Comment Nodes.
// When a Comment Link is deleted, also delete the corresponding Comment Node.
"SelectionDeleting": function(e) {
var parts = e.subject; // the collection of Parts to be deleted, the Diagram.selection
// iterate over a copy of this collection,
// because we may add to the collection by selecting more Parts
parts.copy().each(function(p) {
if (p instanceof go.Node) {
var node = p;
node.findNodesConnected().each(function(n) {
// remove every Comment Node that is connected with this node
if (n.category === "Comment") {
n.isSelected = true; // include in normal deletion process
}
});
} else if (p instanceof go.Link && p.category === "Comment") {
var comlink = p; // a "Comment" Link
var comnode = comlink.fromNode;
// remove the Comment Node that is associated with this Comment Link,
if (comnode.category === "Comment") {
comnode.isSelected = true; // include in normal deletion process
}
}
});
}
});
myDiagram.nodeTemplate =
$("Node", "Auto",
$("Shape",
{ fill: "white" },
new go.Binding("fill", "color")),
$("TextBlock",
{ margin: 6 },
new go.Binding("text", "key"))
);
myDiagram.linkTemplate =
$("Link",
$("Shape",
{ strokeWidth: 1.5 }),
$("Shape",
{ toArrow: "Standard", stroke: null })
);
myDiagram.nodeTemplateMap.add("Comment",
$(go.Node, // this needs to act as a rectangular shape for BalloonLink,
{ background: "transparent" }, // which can be accomplished by setting the background.
$(go.TextBlock,
{ stroke: "brown", margin: 3 },
new go.Binding("text"))
));
myDiagram.linkTemplateMap.add("Comment",
// if the BalloonLink class has been loaded from the Extensions directory, use it
$((typeof BalloonLink === "function" ? BalloonLink : go.Link),
$(go.Shape, // the Shape.geometry will be computed to surround the comment node and
// point all the way to the commented node
{ stroke: "brown", strokeWidth: 1, fill: "lightyellow" })
));
myDiagram.model =
$(go.GraphLinksModel,
{
nodeDataArray: [
{ key: "Alpha", color: "orange" },
{ key: "Beta", color: "lightgreen" },
{ key: "Gamma", color: "lightgreen" },
{ key: "Delta", color: "pink" },
{ key: "A comment", text: "comment\nabout Alpha", category: "Comment" },
{ key: "B comment", text: "comment\nabout Beta", category: "Comment" },
{ key: "G comment", text: "comment about Gamma", category: "Comment" }
],
linkDataArray: [
{ from: "Alpha", to: "Beta" },
{ from: "Alpha", to: "Gamma" },
{ from: "Alpha", to: "Delta" },
{ from: "A comment", to: "Alpha", category: "Comment" },
{ from: "B comment", to: "Beta", category: "Comment" },
{ from: "G comment", to: "Gamma", category: "Comment" }
]
});
// show the model in JSON format
document.getElementById("savedModel").textContent = myDiagram.model.toJson();
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:400px;"></div>
<p>
<b>GoJS</b> supports the notion of "Comment"s.
A "Comment" is a node that is linked with another node but is positioned by some layouts to go along with that other node,
rather than be laid out like a regular node and link.
</p>
<p>
In this sample there are three "Comment" nodes, connected with regular nodes by three "Comment" links.
Node and link data are marked as "Comment"s by specifying "Comment" as the category.
But the "Comment" nodes and links have a different default template, and thus a different appearance, than regular nodes and links.
You can specify your own templates for "Comment" nodes and "Comment" links.
The "Comment" link template defined here uses the <code>BalloonLink</code> class defined in <a href="../extensions/BalloonLink.js">BalloonLink.js</a> in the Extensions directory.
</p>
<div style="display: inline">
Initial Diagram.model saved in JSON format:<br />
<pre id="savedModel"></pre>
</div>
</div>
</body>
</html>
+125
View File
@@ -0,0 +1,125 @@
<!DOCTYPE html>
<html>
<head>
<title>Concept Map</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A concept map diagram implemented with labeled links and ForceDirectedLayout." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
myDiagram =
$(go.Diagram, "myDiagramDiv", // must name or refer to the DIV HTML element
{
initialAutoScale: go.Diagram.Uniform, // an initial automatic zoom-to-fit
contentAlignment: go.Spot.Center, // align document to the center of the viewport
layout:
$(go.ForceDirectedLayout, // automatically spread nodes apart
{ maxIterations: 200, defaultSpringLength: 30, defaultElectricalCharge: 100 })
});
// define each Node's appearance
myDiagram.nodeTemplate =
$(go.Node, "Auto", // the whole node panel
{ locationSpot: go.Spot.Center },
// define the node's outer shape, which will surround the TextBlock
$(go.Shape, "Rectangle",
{ fill: $(go.Brush, "Linear", { 0: "rgb(254, 201, 0)", 1: "rgb(254, 162, 0)" }), stroke: "black" }),
$(go.TextBlock,
{ font: "bold 10pt helvetica, bold arial, sans-serif", margin: 4 },
new go.Binding("text", "text"))
);
// replace the default Link template in the linkTemplateMap
myDiagram.linkTemplate =
$(go.Link, // the whole link panel
$(go.Shape, // the link shape
{ stroke: "black" }),
$(go.Shape, // the arrowhead
{ toArrow: "standard", stroke: null }),
$(go.Panel, "Auto",
$(go.Shape, // the label background, which becomes transparent around the edges
{
fill: $(go.Brush, "Radial", { 0: "rgb(240, 240, 240)", 0.3: "rgb(240, 240, 240)", 1: "rgba(240, 240, 240, 0)" }),
stroke: null
}),
$(go.TextBlock, // the label text
{
textAlign: "center",
font: "10pt helvetica, arial, sans-serif",
stroke: "#555555",
margin: 4
},
new go.Binding("text", "text"))
)
);
// create the model for the concept map
var nodeDataArray = [
{ key: 1, text: "Concept Maps" },
{ key: 2, text: "Organized Knowledge" },
{ key: 3, text: "Context Dependent" },
{ key: 4, text: "Concepts" },
{ key: 5, text: "Propositions" },
{ key: 6, text: "Associated Feelings or Affect" },
{ key: 7, text: "Perceived Regularities" },
{ key: 8, text: "Labeled" },
{ key: 9, text: "Hierarchically Structured" },
{ key: 10, text: "Effective Teaching" },
{ key: 11, text: "Crosslinks" },
{ key: 12, text: "Effective Learning" },
{ key: 13, text: "Events (Happenings)" },
{ key: 14, text: "Objects (Things)" },
{ key: 15, text: "Symbols" },
{ key: 16, text: "Words" },
{ key: 17, text: "Creativity" },
{ key: 18, text: "Interrelationships" },
{ key: 19, text: "Infants" },
{ key: 20, text: "Different Map Segments" }
];
var linkDataArray = [
{ from: 1, to: 2, text: "represent" },
{ from: 2, to: 3, text: "is" },
{ from: 2, to: 4, text: "is" },
{ from: 2, to: 5, text: "is" },
{ from: 2, to: 6, text: "includes" },
{ from: 2, to: 10, text: "necessary\nfor" },
{ from: 2, to: 12, text: "necessary\nfor" },
{ from: 4, to: 5, text: "combine\nto form" },
{ from: 4, to: 6, text: "include" },
{ from: 4, to: 7, text: "are" },
{ from: 4, to: 8, text: "are" },
{ from: 4, to: 9, text: "are" },
{ from: 5, to: 9, text: "are" },
{ from: 5, to: 11, text: "may be" },
{ from: 7, to: 13, text: "in" },
{ from: 7, to: 14, text: "in" },
{ from: 7, to: 19, text: "begin\nwith" },
{ from: 8, to: 15, text: "with" },
{ from: 8, to: 16, text: "with" },
{ from: 9, to: 17, text: "aids" },
{ from: 11, to: 18, text: "show" },
{ from: 12, to: 19, text: "begins\nwith" },
{ from: 17, to: 18, text: "needed\nto see" },
{ from: 18, to: 20, text: "between" }
];
myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="background-color: whitesmoke; border: solid 1px black; width: 100%; height: 700px"></div>
<p>
A concept map sample depicting various suggested relationships between different ideas.
See also the <a href="interactiveForce.html">Interactive Force</a> sample that uses the exact same data
but a different node template and an interactive <a>ForceDirectedLayout</a>.
</p>
</div>
</body>
</html>
+192
View File
@@ -0,0 +1,192 @@
<!DOCTYPE html>
<html>
<head>
<title>Connection Box Nodes</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Support link connections within a node as well as between nodes.">
<meta name="viewport" content="width=device-width, initial-scale=1">
<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;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
initialContentAlignment: go.Spot.Center, // for v1.*
initialScale: 1.4,
"undoManager.isEnabled": true,
"linkingTool.direction": go.LinkingTool.ForwardsOnly,
"ModelChanged": function(e) { // just for demonstration purposes,
if (e.isTransactionFinished) { // show the model data in the page's TextArea
document.getElementById("mySavedModel").textContent = e.model.toJson();
}
}
});
myDiagram.nodeTemplate =
$(go.Node, "Table",
new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify),
{
selectionObjectName: "BODY",
linkValidation: function(fromnode, fromport, tonode, toport, link) {
if (!fromport || !toport) return false;
if (fromnode === tonode) {
// inside a node must go from an input port to an output port
return fromport.portId[0] === "i" && toport.portId[0] === "o";
} else {
// between nodes the port colors must match
if (fromport.fill !== toport.fill) return false;
// between nodes must go from an output port to an input port
return fromport.portId[0] === "o" && toport.portId[0] === "i";
}
return true;
}
},
$(go.RowColumnDefinition, { column: 1, width: 70 }),
$(go.Shape,
{
name: "BODY",
row: 0, rowSpan: 99, column: 0, columnSpan: 3, stretch: go.GraphObject.Fill,
fill: "gray", strokeWidth: 0, margin: new go.Margin(0, 8)
},
),
$(go.TextBlock,
{ row: 0, columnSpan: 3, margin: new go.Margin(4, 2, 2, 2) },
new go.Binding("text")),
$(go.TextBlock,
{ row: 2, columnSpan: 3, margin: new go.Margin(4, 2, 2, 2) },
new go.Binding("text", "text2")),
$(go.Panel, "Table",
new go.Binding("itemArray", "inPorts"),
{
row: 1, column: 0,
defaultSeparatorPadding: new go.Margin(4, 0),
itemTemplate: // input ports
$(go.Panel, "TableRow",
{ background: "white" },
$(go.Shape,
{
width: 6, height: 6, strokeWidth: 0, margin: new go.Margin(2, 6, 2, 0),
fromSpot: go.Spot.Right, toSpot: go.Spot.Left,
fromLinkable: true, toLinkable: true,
fromLinkableSelfNode: true, toLinkableSelfNode: true, cursor: "pointer"
},
new go.Binding("portId", "row", function(r) { return "i" + r; }).ofObject(),
new go.Binding("fill", "", convertToColor))
)
}
),
$(go.Shape,
{
row: 1, column: 1, fill: "white", strokeWidth: 0,
stretch: go.GraphObject.Fill
}),
$(go.Panel, "Table",
new go.Binding("itemArray", "outPorts"),
{
row: 1, column: 2,
defaultSeparatorPadding: new go.Margin(4, 0),
itemTemplate: // output ports
$(go.Panel, "TableRow",
{ background: "white" },
$(go.Shape,
{
width: 6, height: 6, strokeWidth: 0, margin: new go.Margin(2, 0, 2, 6),
fromSpot: go.Spot.Right, toSpot: go.Spot.Left,
fromLinkable: true, toLinkable: true,
fromLinkableSelfNode: true, toLinkableSelfNode: true, cursor: "pointer"
},
new go.Binding("portId", "row", function(r) { return "o" + r; }).ofObject(),
new go.Binding("fill", "", convertToColor))
)
}
),
);
function convertToColor(n) {
switch (n) {
case "r": return "brown";
case "g": return "olivedrab";
case "b": return "cornflowerblue";
default: return "black";
}
}
myDiagram.linkTemplate =
$(go.Link,
{ relinkableFrom: true, relinkableTo: true },
$(go.Shape,
{ strokeWidth: 2 },
new go.Binding("stroke", "fromPort", function(p) {
return p.fill;
}).ofObject())
);
myDiagram.model = $(go.GraphLinksModel, {
linkFromPortIdProperty: "fpid",
linkToPortIdProperty: "tpid",
copiesArrays: true,
copiesArrayObjects: true,
nodeDataArray:
[
{
key: 1, text: "Alpha",
location: "0 0",
inPorts: ["r", "r", "r", "g", "b"],
outPorts: ["r", "g", "g", "b", "b"]
},
{
key: 2, text: "Beta",
location: "200 -80",
inPorts: ["r", "r", "g", "g", "b"],
outPorts: ["r", "r", "g", "g", "b"]
},
{
key: 3, text: "Gamma",
location: "200 80",
inPorts: ["r", "r", "g", "g", "b"],
outPorts: ["r", "r", "g", "g", "b"]
}
],
linkDataArray:
[
{ from: 1, fpid: "i0", to: 1, tpid: "o1" },
{ from: 1, fpid: "i1", to: 1, tpid: "o4" },
{ from: 1, fpid: "i2", to: 1, tpid: "o0" },
{ from: 1, fpid: "i3", to: 1, tpid: "o3" },
{ from: 1, fpid: "i4", to: 1, tpid: "o2" },
{ from: 1, fpid: "o0", to: 2, tpid: "i0" },
{ from: 1, fpid: "o2", to: 2, tpid: "i2" },
{ from: 1, fpid: "o2", to: 3, tpid: "i2" },
{ from: 1, fpid: "o3", to: 2, tpid: "i4" },
{ from: 1, fpid: "o3", to: 3, tpid: "i4" },
{ from: 2, fpid: "i0", to: 2, tpid: "o1" },
{ from: 2, fpid: "i2", to: 2, tpid: "o0" },
{ from: 2, fpid: "i4", to: 2, tpid: "o2" },
{ from: 3, fpid: "i2", to: 3, tpid: "o3" },
{ from: 3, fpid: "i1", to: 3, tpid: "o1" }
]
});
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
<p>
You can draw new links between ports, both between nodes and within a node.
Between nodes, the link validation predicate requires new link connections to connect ports of the same color.
However, within a node, links may connect ports of different colors.
</p>
<textarea id="mySavedModel" style="width:100%;height:250px"></textarea>
</div>
</body>
</html>
+116
View File
@@ -0,0 +1,116 @@
<!DOCTYPE html>
<html>
<head>
<title>Kitten Monitor</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A variation of a monitoring diagram where the objects of interest maintain a constant size while the user zooms in and out on the map." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
initialContentAlignment: go.Spot.TopLeft,
isReadOnly: true, // allow selection but not moving or copying or deleting
"toolManager.hoverDelay": 100, // how quickly tooltips are shown
"toolManager.mouseWheelBehavior": go.ToolManager.WheelZoom // mouse wheel zooms instead of scrolls
});
// the background image, a floor plan
myDiagram.add(
$(go.Part, // this Part is not bound to any model data
{
layerName: "Background", position: new go.Point(0, 0),
selectable: false, pickable: false
},
$(go.Picture, "https://upload.wikimedia.org/wikipedia/commons/9/9a/Sample_Floorplan.jpg")
));
// the template for each kitten, for now just a colored circle
myDiagram.nodeTemplate =
$(go.Node,
new go.Binding("location", "loc"), // specified by data
{ locationSpot: go.Spot.Center }, // at center of node
$(go.Shape, "Circle",
{ width: 12, height: 12, stroke: null },
new go.Binding("fill", "color")), // also specified by data
{ // this tooltip shows the name and picture of the kitten
toolTip:
$("ToolTip",
$(go.Panel, "Vertical",
$(go.Picture,
new go.Binding("source", "src", function(s) { return "images/" + s + ".png"; })),
$(go.TextBlock, { margin: 3 },
new go.Binding("text", "key"))
)
) // end Adornment
}
);
// pretend there are four kittens
myDiagram.model.nodeDataArray = [
{ key: "Alonzo", src: "50x40", loc: new go.Point(220, 130), color: "blue" },
{ key: "Coricopat", src: "55x55", loc: new go.Point(420, 250), color: "green" },
{ key: "Garfield", src: "60x90", loc: new go.Point(640, 450), color: "red" },
{ key: "Demeter", src: "80x50", loc: new go.Point(140, 350), color: "purple" }
];
// This code keeps all nodes at a constant size in the viewport,
// by adjusting for any scaling done by zooming in or out.
// This code ignores simple Parts;
// Links will automatically be rerouted as Nodes change size.
var origscale = NaN;
myDiagram.addDiagramListener("InitialLayoutCompleted", function(e) { origscale = myDiagram.scale; });
myDiagram.addDiagramListener("ViewportBoundsChanged", function(e) {
if (isNaN(origscale)) return;
var newscale = myDiagram.scale;
if (e.subject.scale === newscale) return; // optimization: don't scale Nodes when just scrolling/panning
myDiagram.skipsUndoManager = true;
myDiagram.startTransaction("scale Nodes");
myDiagram.nodes.each(function(node) {
node.scale = origscale / newscale;
});
myDiagram.commitTransaction("scale Nodes");
myDiagram.skipsUndoManager = false;
});
// simulate some real-time position monitoring, once every 2 seconds
function randomMovement() {
var model = myDiagram.model;
model.startTransaction("update locations");
var arr = model.nodeDataArray;
var picture = myDiagram.parts.first();
for (var i = 0; i < arr.length; i++) {
var data = arr[i];
var pt = data.loc;
var x = pt.x + 20 * Math.random() - 10;
var y = pt.y + 20 * Math.random() - 10;
// make sure the kittens stay inside the house
var b = picture.actualBounds;
if (x < b.x || x > b.right) x = pt.x;
if (y < b.y || y > b.bottom) y = pt.y;
model.setDataProperty(data, "loc", new go.Point(x, y));
}
model.commitTransaction("update locations");
}
function loop() {
setTimeout(function() { randomMovement(); loop(); }, 2000);
}
loop(); // start the simulation
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
<p>The tooltip for each kitten shows its name and photo.</p>
<p>When you zoom in or out the effective size of each Node is kept constant by changing its <a>GraphObject.scale</a>.</p>
</div>
</body>
</html>
+163
View File
@@ -0,0 +1,163 @@
<!DOCTYPE html>
<html>
<head>
<title>Content Alignment examples</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="An interactive GoJS Diagram demonstrating viewports and document bounds and alignment and scaling." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
myDiagram = $(go.Diagram, "myDiagramDiv", // Must be the ID or reference to div
{
"undoManager.isEnabled": true
});
for (var i = 0; i < 15; i++) {
myDiagram.add( // add an unbound Node to the diagram at a random position
$(go.Node,
{ position: new go.Point(Math.random() * 251, Math.random() * 251) },
$(go.Shape, "Circle",
{ fill: go.Brush.randomColor(150), strokeWidth: 2, desiredSize: new go.Size(30, 30) })
));
}
// automatically update information in the panel
myDiagram.addDiagramListener("DocumentBoundsChanged", updateDOM);
myDiagram.addDiagramListener("ViewportBoundsChanged", updateDOM);
var myPosX = document.getElementById("positionX");
var myPosY = document.getElementById("positionY");
var myScale = document.getElementById("scale");
var myDocBounds = document.getElementById("docBounds");
function updateDOM(e) {
var d = e.diagram;
var pos = d.position;
myPosX.value = Math.round(pos.x, 2);
myPosY.value = Math.round(pos.y, 2);
myScale.value = d.scale;
var b = d.documentBounds;
myDocBounds.textContent = b.x.toFixed(2) + ", " + b.y.toFixed(2) + " " + b.width.toFixed(2) + " x " + b.height.toFixed(2);
}
}
// occurs when one of the contentAlign radio buttons is clicked
function changeContentAlign(spot) {
myDiagram.startTransaction("");
myDiagram.contentAlignment = go.Spot[spot];
myDiagram.commitTransaction("");
}
function changePosition(posx, posy) {
myDiagram.startTransaction("");
var x = parseInt(posx);
var y = parseInt(posy);
myDiagram.position = new go.Point(x, y);
myDiagram.commitTransaction("");
}
function changeScale(scale) {
var scale = parseFloat(scale);
if (scale > 0) {
myDiagram.startTransaction("");
myDiagram.scale = scale;
myDiagram.commitTransaction("");
}
}
function changeFixedBounds(fx, fy, fw, fh) {
myDiagram.startTransaction("");
var x = parseFloat(fx);
var y = parseFloat(fy);
var h = parseFloat(fw);
var w = parseFloat(fh);
myDiagram.fixedBounds = new go.Rect(x, y, Math.max(1, w), Math.max(1, h));
myDiagram.commitTransaction("");
}
function changePadding(pt, pr, pb, pl) {
myDiagram.startTransaction("");
var t = parseFloat(pt);
var r = parseFloat(pr);
var b = parseFloat(pb);
var l = parseFloat(pl);
myDiagram.padding = new go.Margin(t, r, b, l);
myDiagram.commitTransaction("");
}
function changeAutoScale(scaleType) {
myDiagram.startTransaction("");
myDiagram.autoScale = go.Diagram[scaleType];
myDiagram.commitTransaction("");
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div style="width:100%; white-space: nowrap">
<div style="display: inline-block; vertical-align: top; width:50%">
<div id="myDiagramDiv" style="height: 400px; background: whitesmoke; border: solid 1px black"></div>
</div>
<div style="display: inline-block; vertical-align: top; width:50%">
<div style="border: solid 1px black; padding:5px">
Diagram.documentBounds: <div id="docBounds" style="display: inline-block"></div>
<p>Diagram.contentAlignment:<br />
<input type="radio" name="contentAlign" onclick="changeContentAlign(this.id)" id="None" checked/><label for="None">None</label>
<input type="radio" name="contentAlign" onclick="changeContentAlign(this.id)" id="Center"/><label for="Center">Center</label><br/>
<input type="radio" name="contentAlign" onclick="changeContentAlign(this.id)" id="Left"/><label for="Left">Left</label>
<input type="radio" name="contentAlign" onclick="changeContentAlign(this.id)" id="Right"/><label for="Right">Right</label><br/>
<input type="radio" name="contentAlign" onclick="changeContentAlign(this.id)" id="Top"/><label for="Top">Top</label>
<input type="radio" name="contentAlign" onclick="changeContentAlign(this.id)" id="Bottom"/><label for="Bottom">Bottom</label><br/>
</p>
<p>Diagram.position:<br />
<input type="text" size="3" id="positionX" value="NaN"/>
<input type="text" size="3" id="positionY" value="NaN"/>
<input type="button" onclick="changePosition(positionX.value, positionY.value)" value="Change"/>
</p>
<p>Diagram.scale:<br />
<input type="text" size="3" id="scale" value="1"/>
<input type="button" onclick="changeScale(scale.value)" value="Change"/>
</p>
<p>Diagram.fixedBounds (x, y, width, height):<br />
<input type="text" size="3" id="fixedX" value="NaN"/>
<input type="text" size="3" id="fixedY" value="NaN"/>
<input type="text" size="3" id="fixedW" value="NaN"/>
<input type="text" size="3" id="fixedH" value="NaN"/>
<input type="button" onclick="changeFixedBounds(fixedX.value, fixedY.value, fixedW.value, fixedH.value)" value="Set"/>
</p>
<p>Diagram.padding (top, right, bottom, left):<br />
<input type="text" size="3" id="padT" value="5"/>
<input type="text" size="3" id="padR" value="5"/>
<input type="text" size="3" id="padB" value="5"/>
<input type="text" size="3" id="padL" value="5"/>
<input type="button" onclick="changePadding(padT.value, padR.value, padB.value, padL.value)" value="Set"/>
</p>
<p>Diagram.autoScale:<br />
<input type="radio" name="autoScale" onclick="changeAutoScale(this.value)" id="DiagramNone" value="None" checked /><label for="DiagramNone">Diagram.None</label><br/>
<input type="radio" name="autoScale" onclick="changeAutoScale(this.id)" id="Uniform" /><label for="Uniform">Diagram.Uniform</label><br/>
<input type="radio" name="autoScale" onclick="changeAutoScale(this.id)" id="UniformToFill" /><label for="UniformToFill">Diagram.UniformToFill</label><br/>
(but no greater than CommandHandler.defaultScale)
</p>
<input type="button" onclick="myDiagram.commandHandler.zoomToFit()" value="Zoom to Fit"/>
</div>
</div>
<p>
A Diagram's <a>Diagram.contentAlignment</a> property determines how parts are positioned when the
<a>Diagram.viewportBounds</a> width or height is different than the <a>Diagram.documentBounds</a> width or height.
</p>
</div>
</div>
</body>
</html>
+471
View File
@@ -0,0 +1,471 @@
<!DOCTYPE html>
<html>
<head>
<title>Control Instruments: Gauges and Meters</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Gauges and Meters that allow the user to control the values that those instruments show" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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;
myDiagram = $(go.Diagram, "myDiagramDiv",
{ "undoManager.isEnabled": true });
// These properties are what change an object from being a value indicator,
// such as a needle or a bar or a thumb of a slider, to being a controller
// that the user can drag to change the value of the instrument.
// This assumes that the scale (a "Graduated" Panel) is named "SCALE".
// The alwaysVisible parameter determines whether the object's visibility
// is controlled by the "SCALE"'s Panel.isEnabled property.
function sliderActions(alwaysVisible) {
return [
{
isActionable: true,
actionDown: function(e, obj) {
obj._dragging = true;
obj._original = obj.part.data.value;
},
actionMove: function(e, obj) {
if (!obj._dragging) return;
var scale = obj.part.findObject("SCALE");
var pt = e.diagram.lastInput.documentPoint;
var loc = scale.getLocalPoint(pt);
var val = Math.round(scale.graduatedValueForPoint(loc));
// just set the data.value temporarily, not recorded in UndoManager
e.diagram.model.commit(function(m) {
m.set(obj.part.data, "value", val);
}, null); // null means skipsUndoManager
},
actionUp: function(e, obj) {
if (!obj._dragging) return;
obj._dragging = false;
var scale = obj.part.findObject("SCALE");
var pt = e.diagram.lastInput.documentPoint;
var loc = scale.getLocalPoint(pt);
var val = Math.round(scale.graduatedValueForPoint(loc));
e.diagram.model.commit(function(m) {
m.set(obj.part.data, "value", obj._original);
}, null); // null means skipsUndoManager
// now set the data.value for real
e.diagram.model.commit(function(m) {
m.set(obj.part.data, "value", val);
}, "dragged slider");
},
actionCancel: function(e, obj) {
obj._dragging = false;
e.diagram.model.commit(function(m) {
m.set(obj.part.data, "value", obj._original);
}, null); // null means skipsUndoManager
}
},
(alwaysVisible ? {} : new go.Binding("visible", "isEnabled").ofObject("SCALE")),
new go.Binding("cursor", "isEnabled", function(e) { return e ? "pointer" : ""; }).ofObject("SCALE")
];
}
// These helper functions simplify the node templates
function commonScaleBindings() {
return [
new go.Binding("graduatedMin", "min"),
new go.Binding("graduatedMax", "max"),
new go.Binding("graduatedTickUnit", "unit"),
new go.Binding("isEnabled", "editable")
];
}
function commonSlider(vert) {
return $(go.Shape, "RoundedRectangle",
{
name: "SLIDER",
fill: "white",
desiredSize: (vert ? new go.Size(20, 6) : new go.Size(6, 20)),
alignment: (vert ? go.Spot.Top : go.Spot.Right)
},
sliderActions(false)
);
}
function commonNodeStyle() {
return [
{ locationSpot: go.Spot.Center },
{ fromSpot: go.Spot.BottomRightSides, toSpot: go.Spot.TopLeftSides },
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
];
}
myDiagram.nodeTemplateMap.add("Horizontal",
$(go.Node, "Auto", commonNodeStyle(),
// {
// resizable: true,
// resizeObjectName: "PATH",
// resizeAdornmentTemplate:
// $(go.Adornment, "Spot",
// $(go.Placeholder),
// $(go.Shape, { fill: "dodgerblue", width: 8, height: 8, alignment: go.Spot.Right, cursor: "e-resize" }))
// },
$(go.Shape,
{ fill: "lightgray", stroke: "gray" }),
$(go.Panel, "Table",
{ margin: 1, stretch: go.GraphObject.Fill },
// header information
$(go.TextBlock,
{ row: 0, font: "bold 10pt sans-serif" },
new go.Binding("text")),
$(go.Panel, "Spot",
{ row: 1 },
$(go.Panel, "Graduated",
{ name: "SCALE", margin: new go.Margin(0, 6), graduatedTickUnit: 10, isEnabled: false },
commonScaleBindings(),
$(go.Shape, { geometryString: "M0 0 H200", height: 0, name: "PATH" }),
$(go.Shape, { geometryString: "M0 0 V16", alignmentFocus: go.Spot.Center, stroke: "gray" }),
$(go.Shape, { geometryString: "M0 0 V20", alignmentFocus: go.Spot.Center, interval: 5, strokeWidth: 1.5 })
),
$(go.Panel, "Spot",
{ alignment: go.Spot.Left, alignmentFocus: go.Spot.Left, alignmentFocusName: "BAR" },
// the indicator (a bar)
$(go.Shape,
{ name: "BAR", fill: "red", strokeWidth: 0, height: 8 },
new go.Binding("fill", "color"),
new go.Binding("desiredSize", "value", function(v, shp) {
var scale = shp.part.findObject("SCALE");
var path = scale.findMainElement();
var len = (v-scale.graduatedMin) / (scale.graduatedMax-scale.graduatedMin) * path.geometry.bounds.width;
return new go.Size(len, 10);
})),
commonSlider(false)
)
),
// state information
$(go.TextBlock, "0",
{ row: 2, alignment: go.Spot.Left },
new go.Binding("text", "min")),
$(go.TextBlock, "100",
{ row: 2, alignment: go.Spot.Right },
new go.Binding("text", "max")),
$(go.TextBlock,
{ row: 2, background: "white", font: "bold 10pt sans-serif", isMultiline: false, editable: true },
new go.Binding("text", "value", function(v) { return v.toString(); }).makeTwoWay(function(s) { return parseFloat(s); }))
)
));
myDiagram.nodeTemplateMap.add("Vertical",
$(go.Node, "Auto", commonNodeStyle(),
// {
// resizable: true,
// resizeObjectName: "PATH",
// resizeAdornmentTemplate:
// $(go.Adornment, "Spot",
// $(go.Placeholder),
// $(go.Shape, { fill: "dodgerblue", width: 8, height: 8, alignment: go.Spot.Top, cursor: "n-resize" }))
// },
$(go.Shape,
{ fill: "lightgray", stroke: "gray" }),
$(go.Panel, "Table",
{ margin: 1, stretch: go.GraphObject.Fill },
// header information
$(go.TextBlock,
{ row: 0, font: "bold 10pt sans-serif" },
new go.Binding("text")),
$(go.Panel, "Spot",
{ row: 1 },
$(go.Panel, "Graduated",
{ name: "SCALE", margin: new go.Margin(6, 0), graduatedTickUnit: 10, isEnabled: false },
commonScaleBindings(),
// NOTE: path goes upward!
$(go.Shape, { geometryString: "M0 0 V-200", width: 0, name: "PATH" }),
$(go.Shape, { geometryString: "M0 0 V16", alignmentFocus: go.Spot.Center, stroke: "gray" }),
$(go.Shape, { geometryString: "M0 0 V20", alignmentFocus: go.Spot.Center, interval: 5, strokeWidth: 1.5 })
),
$(go.Panel, "Spot",
{ alignment: go.Spot.Bottom, alignmentFocus: go.Spot.Bottom, alignmentFocusName: "BAR" },
// the indicator (a bar)
$(go.Shape,
{ name: "BAR", fill: "red", strokeWidth: 0, height: 8 },
new go.Binding("fill", "color"),
new go.Binding("desiredSize", "value", function(v, shp) {
var scale = shp.part.findObject("SCALE");
var path = scale.findMainElement();
var len = (v-scale.graduatedMin) / (scale.graduatedMax-scale.graduatedMin) * path.geometry.bounds.height;
return new go.Size(10, len);
})),
commonSlider(true)
)
),
// state information
$(go.TextBlock, "0",
{ row: 2, alignment: go.Spot.Left },
new go.Binding("text", "min")),
$(go.TextBlock, "100",
{ row: 2, alignment: go.Spot.Right },
new go.Binding("text", "max")),
$(go.TextBlock,
{ row: 2, background: "white", font: "bold 10pt sans-serif", isMultiline: false, editable: true },
new go.Binding("text", "value", function(v) { return v.toString(); }).makeTwoWay(function(s) { return parseFloat(s); }))
)
));
myDiagram.nodeTemplateMap.add("NeedleMeter",
$(go.Node, "Auto", commonNodeStyle(),
$(go.Shape, { fill: "darkslategray" }),
$(go.Panel, "Spot",
$(go.Panel, "Position",
$(go.Panel, "Graduated",
{ name: "SCALE", margin: 10 },
commonScaleBindings(),
$(go.Shape, { name: "PATH", geometryString: "M0 0 A120 120 0 0 1 200 0", stroke: "white" }),
$(go.Shape, { geometryString: "M0 0 V10", stroke: "white" }),
$(go.TextBlock,
{ segmentOffset: new go.Point(0, 12), segmentOrientation: go.Link.OrientAlong, stroke: "white" })
),
$(go.Shape,
{ stroke: "red", strokeWidth: 4, isGeometryPositioned: true },
new go.Binding("geometry", "value", function(v, shp) {
var scale = shp.part.findObject("SCALE");
var pt = scale.graduatedPointForValue(v);
var geo = new go.Geometry(go.Geometry.Line);
geo.startX = 100 + scale.margin.left;
geo.startY = 90 + scale.margin.top;
geo.endX = pt.x + scale.margin.left;
geo.endY = pt.y + scale.margin.top;
return geo;
}),
sliderActions(true))
),
$(go.TextBlock,
{ alignment: new go.Spot(0.5, 0.5, 0, 20), stroke: "white", font: "bold 10pt sans-serif" },
new go.Binding("text"),
new go.Binding("stroke", "color")),
$(go.TextBlock,
{ alignment: go.Spot.Top, margin: new go.Margin(4, 0, 0, 0) },
{ stroke: "white", font: "bold italic 13pt sans-serif", isMultiline: false, editable: true },
new go.Binding("text", "value", function(v) { return v.toString(); }).makeTwoWay(function(s) { return parseFloat(s); }),
new go.Binding("stroke", "color"))
)
));
myDiagram.nodeTemplateMap.add("CircularMeter",
$(go.Node, "Table", commonNodeStyle(),
$(go.Panel, "Auto",
{ row: 0 },
$(go.Shape, "Circle",
{ stroke: "orange", strokeWidth: 5, spot1: go.Spot.TopLeft, spot2: go.Spot.BottomRight },
new go.Binding("stroke", "color")),
$(go.Panel, "Spot",
$(go.Panel, "Graduated",
{
name: "SCALE", margin: 14,
graduatedTickUnit: 2.5, // tick marks at each multiple of 2.5
stretch: go.GraphObject.None // needed to avoid unnecessary re-measuring!!!
},
commonScaleBindings(),
// the main path of the graduated panel, an arc starting at 135 degrees and sweeping for 270 degrees
$(go.Shape, { name: "PATH", geometryString: "M-70.7 70.7 B135 270 0 0 100 100 M0 100", stroke: "white", strokeWidth: 4 }),
// three differently sized tick marks
$(go.Shape, { geometryString: "M0 0 V10", stroke: "white", strokeWidth: 1 }),
$(go.Shape, { geometryString: "M0 0 V12", stroke: "white", strokeWidth: 2, interval: 2 }),
$(go.Shape, { geometryString: "M0 0 V15", stroke: "white", strokeWidth: 3, interval: 4 }),
$(go.TextBlock,
{ // each tick label
interval: 4,
alignmentFocus: go.Spot.Center,
font: "bold italic 14pt sans-serif", stroke: "white",
segmentOffset: new go.Point(0, 30)
})
),
$(go.TextBlock,
{ alignment: new go.Spot(0.5, 0.9), stroke: "white", font: "bold italic 14pt sans-serif", editable: true },
new go.Binding("text", "value", function(v) { return v.toString(); }).makeTwoWay(function(s) { return parseFloat(s); }),
new go.Binding("stroke", "color")),
$(go.Shape, { fill: "red", strokeWidth: 0, geometryString: "F1 M-6 0 L0 -6 100 0 0 6z x M-100 0" },
new go.Binding("angle", "value", function(v, shp) {
// this determines the angle of the needle, based on the data.value argument
var scale = shp.part.findObject("SCALE");
var p = scale.graduatedPointForValue(v);
var path = shp.part.findObject("PATH");
var c = path.actualBounds.center;
return c.directionPoint(p);
}),
sliderActions(true)),
$(go.Shape, "Circle", { width: 2, height: 2, fill: "#444" })
)
),
$(go.TextBlock,
{ row: 1, font: "bold 11pt sans-serif" },
new go.Binding("text"))
));
myDiagram.nodeTemplateMap.add("BarMeter",
$(go.Node, "Table", commonNodeStyle(),
{ scale: 0.8 },
$(go.Panel, "Auto",
{ row: 0 },
$(go.Shape, "Circle",
{ stroke: "orange", strokeWidth: 5, spot1: go.Spot.TopLeft, spot2: go.Spot.BottomRight },
new go.Binding("stroke", "color")),
$(go.Panel, "Spot",
$(go.Panel, "Graduated",
{
name: "SCALE", margin: 14,
graduatedTickUnit: 2.5, // tick marks at each multiple of 2.5
stretch: go.GraphObject.None // needed to avoid unnecessary re-measuring!!!
},
commonScaleBindings(),
// the main path of the graduated panel, an arc starting at 135 degrees and sweeping for 270 degrees
$(go.Shape, { name: "PATH", geometryString: "M-70.7 70.7 B135 270 0 0 100 100 M0 100", stroke: "white", strokeWidth: 4 }),
// three differently sized tick marks
$(go.Shape, { geometryString: "M0 0 V10", stroke: "white", strokeWidth: 1 }),
$(go.Shape, { geometryString: "M0 0 V12", stroke: "white", strokeWidth: 2, interval: 2 }),
$(go.Shape, { geometryString: "M0 0 V15", stroke: "white", strokeWidth: 3, interval: 4 }),
$(go.TextBlock,
{ // each tick label
interval: 4,
alignmentFocus: go.Spot.Center,
font: "bold italic 14pt sans-serif", stroke: "white",
segmentOffset: new go.Point(0, 30)
})
),
$(go.TextBlock,
{ alignment: go.Spot.Center, stroke: "white", font: "bold italic 14pt sans-serif", editable: true },
new go.Binding("text", "value", function(v) { return v.toString(); }).makeTwoWay(function(s) { return parseFloat(s); }),
new go.Binding("stroke", "color")),
$(go.Shape, { fill: "red", strokeWidth: 0 },
new go.Binding("geometry", "value", function(v, shp) {
var scale = shp.part.findObject("SCALE");
var p0 = scale.graduatedPointForValue(scale.graduatedMin);
var pv = scale.graduatedPointForValue(v);
var path = shp.part.findObject("PATH");
var radius = path.actualBounds.width/2;
var c = path.actualBounds.center;
var a0 = c.directionPoint(p0);
var av = c.directionPoint(pv);
var sweep = av-a0;
if (sweep < 0) sweep += 360;
var layerThickness = 8;
return new go.Geometry()
.add(new go.PathFigure(-radius, -radius)) // always make sure the Geometry includes the top left corner
.add(new go.PathFigure(radius, radius)) // and the bottom right corner of the whole circular area
.add(new go.PathFigure(p0.x-radius, p0.y-radius)
.add(new go.PathSegment(go.PathSegment.Arc, a0, sweep, 0, 0, radius, radius))
.add(new go.PathSegment(go.PathSegment.Line, pv.x-radius, pv.y-radius))
.add(new go.PathSegment(go.PathSegment.Arc, av, -sweep, 0, 0, radius-layerThickness, radius-layerThickness).close()));
}),
sliderActions(true)),
$(go.Shape, "Circle", { width: 2, height: 2, fill: "#444" })
)
),
$(go.TextBlock,
{ row: 1, font: "bold 11pt sans-serif" },
new go.Binding("text"))
));
myDiagram.linkTemplate =
$(go.Link,
{ routing: go.Link.AvoidsNodes, corner: 12 },
$(go.Shape, { isPanelMain: true, stroke: "gray", strokeWidth: 9 }),
$(go.Shape, { isPanelMain: true, stroke: "lightgray", strokeWidth: 5 }),
$(go.Shape, { isPanelMain: true, stroke: "whitesmoke" })
)
myDiagram.model = new go.GraphLinksModel(
[
{ key: 1, value: 87, text: "Vertical", category: "Vertical", loc: "30 0", editable: true, color: "yellow" },
{ key: 2, value: 23, text: "Circular Meter", category: "CircularMeter", loc: "250 -120", editable: true, color: "skyblue" },
{ key: 3, value: 56, text: "Needle Meter", category: "NeedleMeter", loc: "250 110", editable: true, color: "lightsalmon" },
{ key: 4, value: 16, max: 120, text: "Horizontal", category: "Horizontal", loc: "550 0", editable: true, color: "green" },
{ key: 5, value: 23, max: 200, unit: 5, text: "Bar Meter", category: "BarMeter", loc: "550 200", editable: true, color: "orange" }
],
[
{ from: 1, to: 2 },
{ from: 1, to: 3 },
{ from: 2, to: 4 },
{ from: 3, to: 4 },
{ from: 4, to: 5 }
]);
loop(); // start a simple simulation
}
function loop() {
setTimeout(function() {
myDiagram.commit(function() {
myDiagram.links.each(function(l) {
if (Math.random() < 0.2) return;
var prev = l.fromNode.data.value;
var now = l.toNode.data.value;
if (prev > (l.fromNode.data.min || 0) && now < (l.toNode.data.max || 100)) {
myDiagram.model.set(l.fromNode.data, "value", prev-1);
myDiagram.model.set(l.toNode.data, "value", now+1);
}
});
})
loop();
}, 500);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:800px; height:600px"></div>
<p>
Instruments are Panels that include:
</p>
<ul>
<li>a scale which is a "Graduated" Panel showing a possible range of values</li>
<li>one or more indicators that show the instrument's value</li>
</ul>
<p>
Optionally there are other TextBlocks or Shapes that show additional information.
Indicators can be Shapes or TextBlocks or more complicated Panels.
For more about scales, please read <a href="../intro/graduatedPanels.html">Graduated Panels</a>.
For simplicity, all of these instruments only show one value.
But you could define instruments that show multiple values on the same scale,
or that have multiple scales.
</p>
<p>
When an instrument is also a control, the user can modify the instrument's value.
When the instrument is editable, there may be a handle that the user can drag.
This might be the same as the indicator or might be a different object.
</p>
<p>
This sample defines five different types of instruments.
<ul>
<li><b>Horizontal</b>, a horizontal scale with a bar indicator and a slider handle</li>
<li><b>Vertical</b>, a vertical scale with a bar indicator and a slider handle</li>
<li><b>NeedleMeter</b>, a curved scale with a straight needle indicator</li>
<li><b>CircularMeter</b>, a circular scale with a polygonal needle indicator</li>
<li><b>BarMeter</b>, a circular scale with an annular bar indicator</li>
</ul>
<p>
The value to be shown by the instrument is assumed to be the <code>data.value</code> property.
The value is shown both textually in a TextBlock and graphically using an indicator on the scale.
If the value of <code>data.editable</code> is true,
</p>
<ul>
<li>
the user can drag something to change the instrument's value --
the value is limited by the <a>Panel.graduatedMin</a> and <a>Panel.graduatedMax</a> values
</li>
<li>the user can in-place edit the TextBlock showing the value (if the node is selected, hit the F2 key)</li>
</ul>
<p>
Of course you can change the details of anything you want to use.
You might want to add more TextBlocks to show more information.
A few properties already have data Bindings, such as:
</p>
<ul>
<li><a>TextBlock.text</a> from <code>data.text</code>, for the name of the instrument</li>
<li><a>Panel.graduatedMin</a> from <code>data.min</code>, to control the range of the scale</li>
<li><a>Panel.graduatedMax</a> from <code>data.max</code>, to control the range of the scale</li>
<li>(various) from <code>data.color</code>, to control some colors used by the instrument</li>
</ul>
</div>
</body>
</html>
+83
View File
@@ -0,0 +1,83 @@
<!DOCTYPE html>
<html>
<head>
<title>Curviness</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Links with different amounts of curviness." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
myDiagram = $(go.Diagram, "myDiagramDiv", // create a Diagram for the DIV HTML element
{
"undoManager.isEnabled": true
});
// define a simple Node template
myDiagram.nodeTemplate =
$(go.Node, "Auto",
new go.Binding("position", "position"),
$(go.Shape, "RoundedRectangle",
// Shape.fill is bound to Node.data.color
new go.Binding("fill", "color")),
$(go.TextBlock,
{ margin: 3 }, // some room around the text
// TextBlock.text is bound to Node.data.key
new go.Binding("text", "key"))
);
myDiagram.linkTemplate =
$(go.Link, go.Link.Bezier,
// when using fromSpot/toSpot:
{ fromSpot: go.Spot.Left, toSpot: go.Spot.Left },
new go.Binding("fromEndSegmentLength", "curviness"),
new go.Binding("toEndSegmentLength", "curviness"),
// if not using fromSpot/toSpot, use a binding to curviness instead:
//new go.Binding("curviness", "curviness"),
$(go.Shape, // the link shape
{ stroke: "black", strokeWidth: 1.5 }),
$(go.Shape, // the arrowhead, at the mid point of the link
{ toArrow: "OpenTriangle", segmentIndex: -Infinity })
);
// create the model data that will be represented by Nodes and Links
myDiagram.model = new go.GraphLinksModel(
[
{ position: new go.Point(100, 100), key: "Alpha", color: "lightblue" },
{ position: new go.Point(100, 200), key: "Beta", color: "orange" },
{ position: new go.Point(100, 300), key: "Gamma", color: "lightgreen" },
{ position: new go.Point(100, 400), key: "Delta", color: "pink" }
],
[
// The links have different curviness values.
// Set by hand here, they are larger when the two nodes are farther away
{ from: "Alpha", to: "Beta", curviness: 20 },
{ from: "Alpha", to: "Gamma", curviness: 40 },
{ from: "Gamma", to: "Delta", curviness: 20 },
{ from: "Delta", to: "Alpha", curviness: 60 }
]);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:500px; height:500px"></div>
<p>
This sample explicitly binds the <a>Link.curviness</a> property, so that some links bend out farther than others.
</p>
<p>
The link template also places an arrowhead at the middle of the link,
by explicitly setting the arrowhead's <a>GraphObject.segmentIndex</a> to -Infinity
<i>after</i> setting <a>Shape.toArrow</a>.
</p>
</div>
</body>
</html>
+434
View File
@@ -0,0 +1,434 @@
<!DOCTYPE html>
<html>
<head>
<title>Custom Animation Demo</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Custom animation examples for GoJS." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<style>
.flex-container {
display: flex;
flex-wrap: nowrap;
flex-direction: column;
}
.flex-container>div {
margin-bottom: 10px;
}
</style>
<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
myDiagram = $(go.Diagram, "myDiagramDiv", // create a Diagram for the DIV HTML element
{
"clickCreatingTool.archetypeNodeData": {
color: "palegreen",
key: "node"
},
"undoManager.isEnabled": true,
"animationManager.isInitial": false, // To use custom initial animation instead
"InitialLayoutCompleted": animateFadeIn // animate with this function
});
function animateFadeIn(e) {
var diagram = e.diagram;
var animation = new go.Animation();
animation.isViewportUnconstrained = true;
animation.easing = go.Animation.EaseOutExpo; // Looks better for a fade in animation
animation.duration = 900;
animation.add(diagram, 'position', diagram.position.copy().offset(0, diagram instanceof go.Palette ? 200 : -200), diagram.position);
animation.add(diagram, 'opacity', 0, 1);
animation.start();
}
var addNodeAdornment =
$(go.Adornment, "Spot",
$(go.Panel, "Auto",
$(go.Shape, { fill: null, stroke: "dodgerblue", strokeWidth: 3 }),
$(go.Placeholder)),
// the button to create a "next" node, at the top-right corner
$("Button",
{
alignment: go.Spot.TopRight,
click: addNode // this function is defined below
},
$(go.Shape, "PlusLine", { desiredSize: new go.Size(6, 6) })
)
);
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{
selectionAdornmentTemplate: addNodeAdornment,
locationSpot: go.Spot.Center
},
new go.Binding("location", "loc").makeTwoWay(),
$(go.Shape, "RoundedRectangle", {
strokeWidth: 2,
portId: "", // this Shape is the Node's port, not the whole Node
fromLinkable: true, fromLinkableSelfNode: true, fromLinkableDuplicates: true,
toLinkable: true, toLinkableSelfNode: true, toLinkableDuplicates: true,
cursor: "pointer"
},
new go.Binding("fill", "color")),
$(go.TextBlock,
{ margin: 10, font: '14px sans-serif' },
new go.Binding("text", "key"))
);
myDiagram.model = new go.GraphLinksModel(
[
{ key: "Alpha", loc: new go.Point(0, 0), color: "lightblue" },
{ key: "Beta", loc: new go.Point(150, 0), color: "orange" },
{ key: "Gamma", loc: new go.Point(0, 150), color: "lightgreen" },
{ key: "Delta", loc: new go.Point(150, 150), color: "pink" }
],
[
// No links to start
]);
// This animation can be used in LinkDrawn Diagram listeners to animate
// from a straight temporary link to a Bezier finished link
// Custom animation for the curviness of a bezier link
go.AnimationManager.defineAnimationEffect('curviness',
function (obj, startValue, endValue, easing, currentTime, duration, animationState) {
if (isNaN(startValue)) startValue = 0;
if (isNaN(endValue)) endValue = 20;
obj.curviness = easing(currentTime, startValue, endValue - startValue, duration);
}
);
// This animation can be used in LinkDrawn Diagram listeners to animate
// from a straight temporary link to an Orthogonal finished link
go.AnimationManager.defineAnimationEffect('orthogonalLinkanim',
function (link, initPoints, tempPoints, easing, currentTime, duration, animation) {
var animationState = animation.getTemporaryState(link);
if (animationState.initial === undefined) {
// On the first animaiton tick, save the initial points
animationState.initial = true;
var pts = link.points.copy();
tempPoints.points = pts;
animationState.startPt = pts.first();
animationState.toPt1 = pts.elt(2);
animationState.toPt2 = pts.elt(3);
animationState.endPt = pts.last();
}
var newpt1 = new go.Point(
easing(currentTime, animationState.startPt.x, animationState.toPt1.x - animationState.startPt.x, duration),
easing(currentTime, animationState.startPt.y, animationState.toPt1.y - animationState.startPt.y, duration));
var newpt2 = new go.Point(
easing(currentTime, animationState.endPt.x, -(animationState.endPt.x - animationState.toPt2.x), duration),
easing(currentTime, animationState.endPt.y, -(animationState.endPt.y - animationState.toPt2.y), duration));
// Setting the array of points will automatically call makeGeometry which will redraw the segments of the line
link.points = [animationState.startPt, tempPoints.points.elt(1),
newpt1, newpt2,
tempPoints.points.elt(4), animationState.endPt];
}
);
go.AnimationManager.defineAnimationEffect('corner',
function (obj, startValue, endValue, easing, currentTime, duration, animation) {
// If no corner set, default to 0 -> 20
startValue = startValue || 0;
endValue = endValue || 20;
obj.corner = easing(currentTime, startValue, endValue - startValue, duration);
}
);
myDiagram.addDiagramListener('LinkDrawn', function (e) {
var link = e.subject;
var animation = new go.Animation();
var linkChoice = document.getElementById("links").value
if (linkChoice == "bezier") {
link.curve = go.Link.Bezier;
animation.easing = elasticEase;
animation.add(link, "curviness", 0, link.curviness);
animation.duration = 500;
} else if (linkChoice == "orthogonal") {
// The orthogonal animation is two animations, chained together. One to modify the points,
// and then another to modify the link.corner value.
// Store the initial link.corner value,
// then set it to 0 so that in between animations it does not revert back to the original state
var initCorner = link.corner;
link.corner = 0;
// Store the original points to this object
var tempPoints = {};
animation.add(link, "orthogonalLinkanim", link.points, tempPoints);
animation.duration = 300;
// Chain animation after first one is completed
animation.finished = function () {
// Set points back to what they were before the animation
myDiagram.startTransaction("Fix Points");
link.points = tempPoints.points;
myDiagram.commitTransaction("Fix Points");
// Need to make a new animation object
var animation2 = new go.Animation()
animation2.add(link, "corner", 0, initCorner);
animation2.duration = 250;
animation2.start();
}
animation.start();
link.routing = go.Link.Orthogonal;
// NYI ortho animation
link.ensureBounds();
}
animation.start();
});
go.AnimationManager.defineAnimationEffect('bounceDelete',
function (obj, startValue, endValue, easing, currentTime, duration, animation) {
var animationState = animation.getTemporaryState(obj);
if (animationState.initial === undefined) {
// Set the initial positions as part of the animationState data
animationState.yPos = obj.location.y;
animationState.xPos = obj.location.x;
animationState.yVelo = 0;
animationState.xVelo = 0;
animationState.newTime = 0;
animationState.oldTime = 0;
animationState.initial = true;
}
obj.location = getPointBounceDelete(currentTime, obj, animationState, obj.diagram);
}
);
// Get the point the object should be at based upon the time and original point
function getPointBounceDelete(currentTime, obj, animationState, diagram) {
if (diagram === null) return new go.Point(animationState.xPos, animationState.yPos);
let height = obj.actualBounds.height;
animationState.newTime = currentTime;
// Animation uses a change in time in order to be more consistant
let delTime = (animationState.newTime - animationState.oldTime) / 3;
animationState.yVelo += .05 * delTime;
// Add a slight easing to the x movement at the beginning of the animation
if (currentTime < 200) {
animationState.xVelo = currentTime / 300;
}
// Adjust positions based on the velocities and the change in time
animationState.yPos += animationState.yVelo * delTime;
animationState.xPos += animationState.xVelo * delTime;
// Check to see if the Y position will be less than the bottom of the diagram, if so,
// change the direction and scale down the velocity and set the position to the bottom of the diagram
if (animationState.yPos > diagram.viewportBounds.height / 2 - height) {
animationState.yVelo = -.75 * animationState.yVelo;
animationState.yPos = diagram.viewportBounds.height / 2 - height;
}
let myPoint = new go.Point(animationState.xPos, animationState.yPos)
// Get the new old time for use in the next iteration
animationState.oldTime = animationState.newTime;
return myPoint;
}
myDiagram.addDiagramListener('SelectionDeleting', function (e) {
var deletionSelection = document.getElementById('deletion');
var animation = new go.Animation();
var diagram = e.diagram;
e.subject.each(function (p) {
// Because we are deleting this part, we cannot animate it, instead we must animate a temporary copy
// The animation handles this via addTemporaryPart, which must be passed a copy
var part = p.copy();
animation.addTemporaryPart(part, diagram);
var initPosition = part.position.copy()
part.locationSpot = go.Spot.Center;
part.position = initPosition;
switch (deletionSelection.value) {
case "spinOut":
animation.add(part, "angle", part.angle, part.angle + 1000);
animation.add(part, "scale", part.scale, 0.01);
break;
case "fadeOut":
animation.add(part, "opacity", part.opacity, 0);
break;
case "zoomOut":
animation.add(part, "scale", part.scale, 0.01);
break;
case "floatOut":
animation.add(part, "opacity", part.opacity, 0);
animation.add(part, "position", part.position, part.position.copy().add(new go.Point(0, -80)));
break;
case "bounceOut":
animation.add(part, "bounceDelete", part.location); // does't need an end value, bounceDelete determines one
animation.add(part, "scale", part.scale, 0.01);
animation.duration = 1500;
animation.isViewportUnconstrained = true;
break;
default:
// nothing animates
break;
}
});
animation.start();
});
myDiagram.addDiagramListener('ClipboardPasted', function (e) {
var creationSelection = document.getElementById('creation');
// For best performance, be sure to use only one Animation for the entire selection
// instead of creating one animation for each object in the selection
var animation = new go.Animation();
e.subject.each(function (part) {
addCreatedPart(part, animation, creationSelection.value)
});
animation.start();
});
myDiagram.addDiagramListener('PartCreated', function (e) { // From ClickCreatingTool
var creationSelection = document.getElementById('creation');
var animation = new go.Animation();
addCreatedPart(e.subject, animation, creationSelection.value)
animation.start();
});
function addCreatedPart(part, animation, creationSelection) {
switch (creationSelection) {
case "spinIn":
animation.add(part, "angle", part.angle + 1000, part.angle);
animation.add(part, "scale", 0.01, part.scale);
break;
case "fadeIn":
animation.add(part, "opacity", 0, part.opacity);
break;
case "zoomIn":
animation.add(part, "scale", 0.01, part.scale);
break;
case "floatIn":
animation.add(part, "opacity", 0, part.opacity);
animation.add(part, "location", part.location.copy().add(new go.Point(0, -80)), part.location);
break;
default:
// nothing animates
break;
}
}
document.getElementById('addNode').addEventListener('click', function (e) { addNode(); });
function addNode() {
var diagram = myDiagram;
var tempNodes = new go.List();
diagram.startTransaction("Add States");
diagram.nodes.each(function (node) {
if (node.isSelected) {
tempNodes.push(node);
}
})
var animation = new go.Animation();
// Set the easing to a custom easing function
animation.easing = elasticEase;
// Add a new node to the right of each node
tempNodes.each(function (part) {
// get the node data for which the user clicked the button
var fromNode = part;
var fromData = part.data;
// create a new "State" data object, positioned off to the right of the adorned Node
var toData = { key: "new" };
toData.color = "purple";
var p = fromNode.location.copy();
// Place the new node randomly somewhere along a circular 200px radius
var angle = Math.random() * Math.PI * 2;
p.x += Math.cos(angle) * 200;
p.y += Math.sin(angle) * 200;
toData.loc = p;
// 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 = {
from: model.getKeyForNodeData(fromData), // or just: fromData.key
to: model.getKeyForNodeData(toData),
};
// and add the link data to the model
model.addLinkData(linkdata);
var newnode = diagram.findNodeForData(toData);
// Animate each newly created node
animation.add(newnode, "position", part.location, newnode.location);
});
animation.start();
diagram.commitTransaction("Add States");
};
document.getElementById('reloadModel').addEventListener('click', function (e) {
myDiagram.model = go.Model.fromJSON(myDiagram.model.toJSON())
});
// Custom easing function used in some animations
function elasticEase(currentTime, startValue, byValue, duration) {
var ts = (currentTime /= duration) * currentTime;
var tc = ts * currentTime;
return startValue + byValue * (56 * tc * ts + -175 * ts * ts + 200 * tc + -100 * ts + 20 * currentTime);
}
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width: 700px; height: 600px;"></div>
<div class="flex-container" style="width:700px">
<p style="margin-bottom: 0;">
This extension implements several custom animations within GoJS. It may be useful to copy some of them into your own project.
</p>
<ul>
<li>Double click in the Diagram background to create a node, or copy and paste nodes, to view creation animations.
<li>Delete a node to view deletion animations.
<li>Draw links to see new link creation animations.
<li>Select a node and press the + button or the button below to see a link-and-node creation animation.
<li>Reload the model using the button below to see the custom load animation
</ul>
</div>
<div class="flex-container">
<p><strong>Options:</strong></p>
<div>
Creation Animation
<select id="creation">
<option value="spinIn">Spin In</option>
<option value="fadeIn">Fade In</option>
<option value="floatIn">Float In</option>
<option value="zoomIn">Zoom In</option>
<option>--None--</option>
</select>
</div>
<div>
Deletion Animation
<select id="deletion">
<option value="spinOut">Spin Out</option>
<option value="fadeOut">Fade Out</option>
<option value="floatOut">Float Out</option>
<option value="zoomOut">Zoom Out</option>
<option value="bounceOut">Bounce Out</option>
<option>--None--</option>
</select>
</div>
<div>
Drawn Link Style
<select id="links">
<option value="bezier">Bezier Curve</option>
<option value="orthogonal">Orthogonal Curve</option>
<option>Linear (no animation)</option>
</select>
</div>
</div>
<button id="addNode">Add Node + Link from selected Node</button>
<button id="reloadModel">Reload model</button>
</div>
</body>
</html>
+239
View File
@@ -0,0 +1,239 @@
<!DOCTYPE html>
<html>
<head>
<title>HTML Context Menu</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Context menus implemented in HTML rather than as GoJS objects." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
/* CSS for the traditional context menu */
.menu {
display: none;
position: absolute;
opacity: 0;
margin: 0;
padding: 8px 0;
z-index: 999;
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, .2), 0 8px 10px 1px rgba(0, 0, 0, .14), 0 3px 14px 2px rgba(0, 0, 0, .12);
list-style: none;
background-color: #ffffff;
border-radius: 4px;
}
.menu-item {
display: block;
position: relative;
min-width: 60px;
margin: 0;
padding: 6px 16px;
font: bold 12px sans-serif;
color: rgba(0, 0, 0, .87);
cursor: pointer;
}
.menu-item::before {
position: absolute;
top: 0;
left: 0;
opacity: 0;
pointer-events: none;
content: "";
width: 100%;
height: 100%;
background-color: #000000;
}
.menu-item:hover::before {
opacity: .04;
}
.menu .menu {
top: -8px;
left: 100%;
}
.show-menu, .menu-item:hover > .menu {
display: block;
opacity: 1;
}
</style>
<script src="../release/go.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<script id="code">
var myDiagram = null;
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", // create a Diagram for the DIV HTML element
{
"undoManager.isEnabled": true
});
// This is the actual HTML context menu:
var cxElement = document.getElementById("contextMenu");
// Since we have only one main element, we don't have to declare a hide method,
// we can set mainElement and GoJS will hide it automatically
var myContextMenu = $(go.HTMLInfo, {
show: showContextMenu,
hide: hideContextMenu
});
// define a simple Node template (but use the default Link template)
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{ contextMenu: myContextMenu },
$(go.Shape, "RoundedRectangle",
// Shape.fill is bound to Node.data.color
new go.Binding("fill", "color")),
$(go.TextBlock,
{ margin: 3 }, // some room around the text
// TextBlock.text is bound to Node.data.key
new go.Binding("text", "key"))
);
// create the model data that will be represented by Nodes and Links
myDiagram.model = new go.GraphLinksModel(
[
{ key: "Alpha", color: "#f38181" },
{ key: "Beta", color: "#eaffd0" },
{ key: "Gamma", color: "#95e1d3" },
{ key: "Delta", color: "#fce38a" }
],
[
{ from: "Alpha", to: "Beta" },
{ from: "Alpha", to: "Gamma" },
{ from: "Beta", to: "Beta" },
{ from: "Gamma", to: "Delta" },
{ from: "Delta", to: "Alpha" }
]);
myDiagram.contextMenu = myContextMenu;
// We don't want the div acting as a context menu to have a (browser) context menu!
cxElement.addEventListener("contextmenu", function(e) {
e.preventDefault();
return false;
}, false);
function hideCX() {
if (myDiagram.currentTool instanceof go.ContextMenuTool) {
myDiagram.currentTool.doCancel();
}
}
function showContextMenu(obj, diagram, tool) {
// Show only the relevant buttons given the current state.
var cmd = diagram.commandHandler;
var hasMenuItem = false;
function maybeShowItem(elt, pred) {
if (pred) {
elt.style.display = "block";
hasMenuItem = true;
} else {
elt.style.display = "none";
}
}
maybeShowItem(document.getElementById("cut"), cmd.canCutSelection());
maybeShowItem(document.getElementById("copy"), cmd.canCopySelection());
maybeShowItem(document.getElementById("paste"), cmd.canPasteSelection(diagram.toolManager.contextMenuTool.mouseDownPoint));
maybeShowItem(document.getElementById("delete"), cmd.canDeleteSelection());
maybeShowItem(document.getElementById("color"), obj !== null);
// Now show the whole context menu element
if (hasMenuItem) {
cxElement.classList.add("show-menu");
// we don't bother overriding positionContextMenu, we just do it here:
var mousePt = diagram.lastInput.viewPoint;
cxElement.style.left = mousePt.x + 5 + "px";
cxElement.style.top = mousePt.y + "px";
}
// Optional: Use a `window` click listener with event capture to
// remove the context menu if the user clicks elsewhere on the page
window.addEventListener("click", hideCX, true);
}
function hideContextMenu() {
cxElement.classList.remove("show-menu");
// Optional: Use a `window` click listener with event capture to
// remove the context menu if the user clicks elsewhere on the page
window.removeEventListener("click", hideCX, true);
}
}
// This is the general menu command handler, parameterized by the name of the command.
function cxcommand(event, val) {
if (val === undefined) val = event.currentTarget.id;
var diagram = myDiagram;
switch (val) {
case "cut": diagram.commandHandler.cutSelection(); break;
case "copy": diagram.commandHandler.copySelection(); break;
case "paste": diagram.commandHandler.pasteSelection(diagram.toolManager.contextMenuTool.mouseDownPoint); break;
case "delete": diagram.commandHandler.deleteSelection(); break;
case "color": {
var color = window.getComputedStyle(event.target)['background-color'];
changeColor(diagram, color); break;
}
}
diagram.currentTool.stopTool();
}
// A custom command, for changing the color of the selected node(s).
function changeColor(diagram, color) {
// Always make changes in a transaction, except when initializing the diagram.
diagram.startTransaction("change color");
diagram.selection.each(function(node) {
if (node instanceof go.Node) { // ignore any selected Links and simple Parts
// Examine and modify the data, not the Node directly.
var data = node.data;
// Call setDataProperty to support undo/redo as well as
// automatically evaluating any relevant bindings.
diagram.model.setDataProperty(data, "color", color);
}
});
diagram.commitTransaction("change color");
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div style="display: inline-block;">
<!-- We make a div to contain both the Diagram div and the context menu (such that they are siblings)
so that absolute positioning works easily.
This DIV containing both MUST have a non-static CSS position (we use position: relative)
so that our context menu's absolute coordinates work correctly. -->
<div style="position: relative;" >
<div id="myDiagramDiv" style="border: solid 1px black; width:400px; height:400px"></div>
<ul id="contextMenu" class="menu">
<li id="cut" class="menu-item" onclick="cxcommand(event)">Cut</li>
<li id="copy" class="menu-item" onclick="cxcommand(event)">Copy</li>
<li id="paste" class="menu-item" onclick="cxcommand(event)">Paste</li>
<li id="delete" class="menu-item" onclick="cxcommand(event)">Delete</li>
<li id="color" class="menu-item">Color
<ul class="menu">
<li class="menu-item" style="background-color: #f38181;" onclick="cxcommand(event, 'color')">Red</li>
<li class="menu-item" style="background-color: #eaffd0;" onclick="cxcommand(event, 'color')">Green</li>
<li class="menu-item" style="background-color: #95e1d3;" onclick="cxcommand(event, 'color')">Blue</li>
<li class="menu-item" style="background-color: #fce38a;" onclick="cxcommand(event, 'color')">Yellow</li>
</ul>
</li>
</ul>
</div>
<div id="description">
<p>This demonstrates the implementation of a custom HTML context menu.</p>
<p>For a light-box style HTML context menu implementation, see the <a href="htmlLightBoxContextMenu.html">LightBox Context Menu</a> sample.</p>
<p>Right-click or tap-hold on a Node to bring up a context menu.
If you have a selection copied in the clipboard, you can bring up a context menu anywhere to paste.</p>
</div>
</div>
</div>
</body>
</html>
+133
View File
@@ -0,0 +1,133 @@
<!DOCTYPE html>
<html>
<head>
<title>Different Criteria for Hiding "Children" of Collapsed Nodes</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Custom policy for collapsing and expanding subtrees, different than TreeExpanderButton." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
padding: 10,
layout: $(go.LayeredDigraphLayout,
{ direction: 90, layeringOption: go.LayeredDigraphLayout.LayerLongestPathSource }),
"undoManager.isEnabled": true
});
myDiagram.nodeTemplate =
$(go.Node, go.Panel.Vertical,
{ portId: "", fromLinkable: true, toLinkable: true },
new go.Binding("visible"),
$(go.Panel, go.Panel.Auto,
$(go.Shape,
{ fill: "white", minSize: new go.Size(30, 30), strokeWidth: 0 },
{ cursor: "pointer" }, // indicate that linking may start here
new go.Binding("fill", "color")),
$(go.TextBlock,
{ margin: 2 },
{ fromLinkable: false, toLinkable: false }, // don't start drawing a link from the text
new go.Binding("text", "key"))),
$("Button", // a replacement for "TreeExpanderButton" that works for non-tree-structured graphs
// assume initially not visible because there are no links coming out
{ visible: false },
// bind the button visibility to whether it's not a leaf node
new go.Binding("visible", "isTreeLeaf",
function(leaf) { return !leaf; })
.ofObject(),
$(go.Shape,
{
name: "ButtonIcon",
figure: "MinusLine",
desiredSize: new go.Size(6, 6)
},
new go.Binding("figure", "isCollapsed", // data.isCollapsed remembers "collapsed" or "expanded"
function(collapsed) { return collapsed ? "PlusLine" : "MinusLine"; })),
{
click: function(e, obj) {
e.diagram.startTransaction();
var node = obj.part;
if (node.data.isCollapsed) {
expandFrom(node, node);
} else {
collapseFrom(node, node);
}
e.diagram.commitTransaction("toggled visibility of dependencies");
}
})
);
function collapseFrom(node, start) {
if (node.data.isCollapsed) return;
node.diagram.model.setDataProperty(node.data, "isCollapsed", true);
if (node !== start) node.diagram.model.setDataProperty(node.data, "visible", false);
node.findNodesOutOf().each(collapseFrom);
}
function expandFrom(node, start) {
if (!node.data.isCollapsed) return;
node.diagram.model.setDataProperty(node.data, "isCollapsed", false);
if (node !== start) node.diagram.model.setDataProperty(node.data, "visible", true);
node.findNodesOutOf().each(expandFrom);
}
myDiagram.linkTemplate =
$(go.Link,
{ relinkableFrom: true, relinkableTo: true, corner: 10 },
$(go.Shape),
$(go.Shape, { toArrow: "Standard" }));
myDiagram.model = new go.GraphLinksModel([
{ key: "A", color: "lightgreen" },
{ key: "B1", color: "yellow" },
{ key: "B2", color: "yellow" },
{ key: "C", color: "lightblue" },
{ key: "D1", color: "orange" },
{ key: "D2", color: "orange" },
{ key: "E", color: "pink" },
{ key: "F", color: "lightgreen" },
{ key: "Z1", color: "lightgreen" },
{ key: "Z2", color: "yellow" },
{ key: "Z3", color: "orange" },
{ key: "Z4", color: "pink" }
], [
{ from: "A", to: "B1" },
{ from: "B1", to: "C" },
{ from: "A", to: "B2" },
{ from: "B2", to: "D2" },
{ from: "C", to: "D1" },
{ from: "C", to: "D2" },
{ from: "D1", to: "E" },
{ from: "D2", to: "E" },
{ from: "D2", to: "F" },
{ from: "Z1", to: "Z2" },
{ from: "Z2", to: "Z3" },
{ from: "Z3", to: "Z4" },
{ from: "Z4", to: "Z1" }
]);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:600px; height:700px"></div>
<p>
The "TreeExpanderButton", which changes the <a>Node.isTreeExpanded</a> property, really only works with tree structures.
When you want to hide/show the "downstream" nodes from a given node, using the "TreeExpanderButton" might not do what you like,
especially when there are cycles in the graph structure.
</p>
<p>
Instead, this sample implements a "Button" with custom behavior to modify the visibility of each Node.
If this behavior is still not quite right for your app, you can adapt the behavior implemented in the
<code>collapseFrom</code> and <code>expandFrom</code> functions to use different criteria for when to stop recursion.
</p>
</div>
</body>
</html>
+121
View File
@@ -0,0 +1,121 @@
<!DOCTYPE html>
<html>
<head>
<title>Text Editing Examples</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Custom text editing using an HTML select box and some radio buttons." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<!-- custom text editors -->
<script src="../extensions/TextEditorSelectBox.js"></script>
<script src="../extensions/TextEditorRadioButtons.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;
myDiagram = $(go.Diagram, "myDiagramDiv", // must identify the DIV
{
// default text editor is now a SelectBox
"textEditingTool.defaultTextEditor": window.TextEditorSelectBox, // defined in textEditorSelectBox.js
"undoManager.isEnabled": true
});
var brush = new go.Brush(go.Brush.Linear);
brush.addColorStop(0, "rgb(255, 211, 89)");
brush.addColorStop(1, "rgb(255, 239, 113)");
myDiagram.nodeTemplate =
$(go.Node, "Vertical",
{
resizable: true,
rotatable: true,
locationSpot: go.Spot.Center
},
new go.Binding("location", "loc"),
$(go.TextBlock,
{
text: "Alpha",
editable: true,
font: "32pt Georgia, serif",
background: "lightblue"
},
new go.Binding("choices")),
$(go.TextBlock,
{
text: "Beta",
editable: true,
font: "22pt Georgia, serif",
background: "lightgreen",
scale: 2
},
new go.Binding("choices")),
$(go.TextBlock,
{
text: "Gamma",
editable: true,
font: "60pt Georgia, serif",
background: "orangered",
scale: 0.4
},
new go.Binding("choices")),
$(go.TextBlock,
{
text: "One",
editable: true,
font: "bold 16pt Arial, Helvetica, sans-serif",
background: brush,
scale: 2,
// this specific TextBlock uses a RadioButtons for editing text
textEditor: window.TextEditorRadioButtons, // defined in textEditorRadioButtons.js
// this specific TextBlock has its own choices:
choices: ['One', 'Two', 'Three', 'Four']
})
);
myDiagram.model = new go.GraphLinksModel(
[
{ key: 1, choices: ['Alpha', 'Beta', 'Gamma', 'Theta'], loc: new go.Point(250, 150) },
{ key: 2, choices: ['Alpha', 'Beta', 'Gamma', 'Theta'], loc: new go.Point(50, 50) }
],
[
{ from: 1, to: 2 }
]);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<!--
The div needs an explicit size or else we won't see anything.
Lets also add a border to help see the edges.
-->
<div id="myDiagramDiv"
style="border: solid 1px black; width:500px; height:400px; min-width: 200px"></div>
<p>
This example shows how create custom textEditors for the TextEditingTool.
</p>
<p>
Above is a Diagram with two nodes, each holding several TextBlocks.
The TextEditingTool on the diagram has a custom editor that consists of an HTML select box with several preset values.
This editor will change the text as soon as the user presses Enter, Tab, or clicks away from the select box.
</p>
<p>
TextBlocks can also have their own custom editors that override the TextEditingTool's editor, by setting <a>TextBlock.textEditor</a>.
The last TextBlock in each node has its own custom editor that consists of an HTML div with several radio buttons.
This editor will change the text as soon as an option is selected.
</p>
<p>
TextBlocks in this sample make use of <a>TextBlock.choices</a> to inform the custom text editing tools.
</p>
<p>The code for these text editors is in <a href="../extensions/TextEditorSelectBox.js" target="_blank">TextEditorSelectBox.js</a>
and <a href="../extensions/TextEditorRadioButtons.js" target="_blank">TextEditorRadioButtons.js</a>.
<p>You can see a re-implementation of the default text editors in the <a href="../extensions/TextEditor.html">Text Editor extension</a>.
</div>
</body>
</html>
+240
View File
@@ -0,0 +1,240 @@
<!DOCTYPE html>
<html>
<head>
<title>Data Flow Diagram</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Data flow or workflow graph of nodes with varying input and output ports with labels, oriented horizontally." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
initialContentAlignment: go.Spot.Left,
initialAutoScale: go.Diagram.UniformToFill,
layout: $(go.LayeredDigraphLayout,
{ direction: 0 }),
"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);
}
});
function makePort(name, leftside) {
var port = $(go.Shape, "Rectangle",
{
fill: "gray", stroke: null,
desiredSize: new go.Size(8, 8),
portId: name, // declare this object to be a "port"
toMaxLinks: 1, // don't allow more than one link into a port
cursor: "pointer" // show a different cursor to indicate potential link point
});
var lab = $(go.TextBlock, name, // the name of the port
{ font: "7pt sans-serif" });
var panel = $(go.Panel, "Horizontal",
{ margin: new go.Margin(2, 0) });
// set up the port/panel based on which side of the node it will be on
if (leftside) {
port.toSpot = go.Spot.Left;
port.toLinkable = true;
lab.margin = new go.Margin(1, 0, 0, 1);
panel.alignment = go.Spot.TopLeft;
panel.add(port);
panel.add(lab);
} else {
port.fromSpot = go.Spot.Right;
port.fromLinkable = true;
lab.margin = new go.Margin(1, 1, 0, 0);
panel.alignment = go.Spot.TopRight;
panel.add(lab);
panel.add(port);
}
return panel;
}
function makeTemplate(typename, icon, background, inports, outports) {
var node = $(go.Node, "Spot",
$(go.Panel, "Auto",
{ width: 100, height: 120 },
$(go.Shape, "Rectangle",
{
fill: background, stroke: null, strokeWidth: 0,
spot1: go.Spot.TopLeft, spot2: go.Spot.BottomRight
}),
$(go.Panel, "Table",
$(go.TextBlock, typename,
{
row: 0,
margin: 3,
maxSize: new go.Size(80, NaN),
stroke: "white",
font: "bold 11pt sans-serif"
}),
$(go.Picture, icon,
{ row: 1, width: 16, height: 16, scale: 3.0 }),
$(go.TextBlock,
{
row: 2,
margin: 3,
editable: true,
maxSize: new go.Size(80, 40),
stroke: "white",
font: "bold 9pt sans-serif"
},
new go.Binding("text", "name").makeTwoWay())
)
),
$(go.Panel, "Vertical",
{
alignment: go.Spot.Left,
alignmentFocus: new go.Spot(0, 0.5, 8, 0)
},
inports),
$(go.Panel, "Vertical",
{
alignment: go.Spot.Right,
alignmentFocus: new go.Spot(1, 0.5, -8, 0)
},
outports)
);
myDiagram.nodeTemplateMap.set(typename, node);
}
makeTemplate("Table", "images/table.svg", "forestgreen",
[],
[makePort("OUT", false)]);
makeTemplate("Join", "images/join.svg", "mediumorchid",
[makePort("L", true), makePort("R", true)],
[makePort("UL", false), makePort("ML", false), makePort("M", false), makePort("MR", false), makePort("UR", false)]);
makeTemplate("Project", "images/project.svg", "darkcyan",
[makePort("", true)],
[makePort("OUT", false)]);
makeTemplate("Filter", "images/filter.svg", "cornflowerblue",
[makePort("", true)],
[makePort("OUT", false), makePort("INV", false)]);
makeTemplate("Group", "images/group.svg", "mediumpurple",
[makePort("", true)],
[makePort("OUT", false)]);
makeTemplate("Sort", "images/sort.svg", "sienna",
[makePort("", true)],
[makePort("OUT", false)]);
makeTemplate("Export", "images/upload.svg", "darkred",
[makePort("", true)],
[]);
myDiagram.linkTemplate =
$(go.Link,
{
routing: go.Link.Orthogonal, corner: 5,
relinkableFrom: true, relinkableTo: true
},
$(go.Shape, { stroke: "gray", strokeWidth: 2 }),
$(go.Shape, { stroke: "gray", fill: "gray", toArrow: "Standard" })
);
load();
}
// Show the diagram's model in JSON format that the user may edit
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 id="myDiagramDiv" style="border: solid 1px black; width: 100%; height: 600px"></div>
<p>
This sample demonstrates labeled ports on nodes, arranged as a data flow or workflow. These ports are set up as panels, created within
the <b>makePort</b> function. This function sets various properties of the <a>Shape</a> and
<a>TextBlock</a> that make up the panel, and properties of the panel itself. Most notable are
<a>GraphObject.portId</a> to declare the shape as a port, and <a>GraphObject.fromLinkable</a> and
<a>GraphObject.toLinkable</a> to set the way the ports can be linked.
</p>
<p>
The diagram also uses the <b>makeTemplate</b> function to create the node templates with shared features.
This function takes a type, an image, a background color, and arrays of ports to create the node
to be added to the <a>Diagram.nodeTemplateMap</a>.
</p>
<p>
For the same data model rendered somewhat differently, see the <a href="dataFlowVertical.html">Data Flow (vertical)</a> sample.
</p>
<div>
<div>
<button id="SaveButton" onclick="save()">Save</button>
<button onclick="load()">Load</button>
Diagram Model saved in JSON format:
</div>
<textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "go.GraphLinksModel",
"nodeCategoryProperty": "type",
"linkFromPortIdProperty": "frompid",
"linkToPortIdProperty": "topid",
"nodeDataArray": [
{"key":1, "type":"Table", "name":"Product"},
{"key":2, "type":"Table", "name":"Sales"},
{"key":3, "type":"Table", "name":"Period"},
{"key":4, "type":"Table", "name":"Store"},
{"key":11, "type":"Join", "name":"Product, Class"},
{"key":12, "type":"Join", "name":"Period"},
{"key":13, "type":"Join", "name":"Store"},
{"key":21, "type":"Project", "name":"Product, Class"},
{"key":31, "type":"Filter", "name":"Boston, Jan2014"},
{"key":32, "type":"Filter", "name":"Boston, 2014"},
{"key":41, "type":"Group", "name":"Sales"},
{"key":42, "type":"Group", "name":"Total Sales"},
{"key":51, "type":"Join", "name":"Product Name"},
{"key":61, "type":"Sort", "name":"Product Name"},
{"key":71, "type":"Export", "name":"File"}
],
"linkDataArray": [
{"from":1, "frompid":"OUT", "to":11, "topid":"L"},
{"from":2, "frompid":"OUT", "to":11, "topid":"R"},
{"from":3, "frompid":"OUT", "to":12, "topid":"R"},
{"from":4, "frompid":"OUT", "to":13, "topid":"R"},
{"from":11, "frompid":"M", "to":12, "topid":"L"},
{"from":12, "frompid":"M", "to":13, "topid":"L"},
{"from":13, "frompid":"M", "to":21},
{"from":21, "frompid":"OUT", "to":31},
{"from":21, "frompid":"OUT", "to":32},
{"from":31, "frompid":"OUT", "to":41},
{"from":32, "frompid":"OUT", "to":42},
{"from":41, "frompid":"OUT", "to":51, "topid":"L"},
{"from":42, "frompid":"OUT", "to":51, "topid":"R"},
{"from":51, "frompid":"OUT", "to":61},
{"from":61, "frompid":"OUT", "to":71}
]}
</textarea>
</div>
</div>
</body>
</html>
+239
View File
@@ -0,0 +1,239 @@
<!DOCTYPE html>
<html>
<head>
<title>Data Flow Diagram</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Data flow or workflow graph of nodes with varying input and output ports with labels, oriented vertically." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
initialContentAlignment: go.Spot.Top,
initialAutoScale: go.Diagram.UniformToFill,
layout: $(go.LayeredDigraphLayout,
{ direction: 90 }),
"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);
}
});
// when the diagram is vertically oriented, "left" means "top" and "right" means "bottom"
function makePort(name, leftside) {
var port = $(go.Shape, "Circle",
{
fill: "black", stroke: null,
desiredSize: new go.Size(8, 8),
portId: name, // declare this object to be a "port"
toMaxLinks: 1, // don't allow more than one link into a port
cursor: "pointer" // show a different cursor to indicate potential link point
});
var lab = $(go.TextBlock, name, // the name of the port
{ font: "7pt sans-serif" });
var panel = $(go.Panel, "Vertical",
{ margin: new go.Margin(0, 2) });
if (leftside) {
port.toSpot = go.Spot.Top;
port.toLinkable = true;
lab.margin = new go.Margin(1, 0, 0, 1);
panel.alignment = go.Spot.TopLeft;
panel.add(port);
panel.add(lab);
} else {
port.fromSpot = go.Spot.Bottom;
port.fromLinkable = true;
lab.margin = new go.Margin(1, 1, 0, 0);
panel.alignment = go.Spot.TopRight;
panel.add(lab);
panel.add(port);
}
return panel;
}
function makeTemplate(typename, icon, background, inports, outports) {
var node = $(go.Node, "Spot",
$(go.Panel, "Auto",
{ width: 200, height: 90 },
$(go.Shape, "RoundedRectangle",
{
fill: background,
spot1: go.Spot.TopLeft, spot2: go.Spot.BottomRight
}),
$(go.Panel, "Table",
$(go.TextBlock, typename,
{
column: 0,
margin: 3,
maxSize: new go.Size(80, NaN),
stroke: "black",
font: "bold 10pt sans-serif"
}),
$(go.Picture, icon,
{ column: 1, width: 55, height: 55 }),
$(go.TextBlock,
{
column: 2,
margin: 3,
editable: true,
maxSize: new go.Size(80, 40),
stroke: "black",
font: "bold 9pt sans-serif"
},
new go.Binding("text", "name").makeTwoWay())
)
),
$(go.Panel, "Horizontal",
{
alignment: go.Spot.Top,
alignmentFocus: new go.Spot(0.5, 0, 0, 4)
},
inports),
$(go.Panel, "Horizontal",
{
alignment: go.Spot.Bottom,
alignmentFocus: new go.Spot(0.5, 1, 0, -4)
},
outports)
);
myDiagram.nodeTemplateMap.set(typename, node);
}
makeTemplate("Table", "images/table.svg", "forestgreen",
[],
[makePort("OUT", false)]);
makeTemplate("Join", "images/join.svg", "mediumorchid",
[makePort("L", true), makePort("R", true)],
[makePort("UL", false), makePort("ML", false), makePort("M", false), makePort("MR", false), makePort("UR", false)]);
makeTemplate("Project", "images/project.svg", "darkcyan",
[makePort("", true)],
[makePort("OUT", false)]);
makeTemplate("Filter", "images/filter.svg", "cornflowerblue",
[makePort("", true)],
[makePort("OUT", false), makePort("INV", false)]);
makeTemplate("Group", "images/group.svg", "mediumpurple",
[makePort("", true)],
[makePort("OUT", false)]);
makeTemplate("Sort", "images/sort.svg", "sienna",
[makePort("", true)],
[makePort("OUT", false)]);
makeTemplate("Export", "images/upload.svg", "darkred",
[makePort("", true)],
[]);
myDiagram.linkTemplate =
$(go.Link,
{
routing: go.Link.Orthogonal, corner: 5,
relinkableFrom: true, relinkableTo: true
},
$(go.Shape, { stroke: "black", strokeWidth: 2 })
);
load();
}
// Show the diagram's model in JSON format that the user may edit
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 id="myDiagramDiv" style="border: solid 1px black; width: 100%; height: 600px"></div>
<p>
This sample demonstrates a data flow or workflow graph with labeled ports on nodes. These ports are set up as panels, created within
the <b>makePort</b> function. This function sets various properties of the <a>Shape</a> and
<a>TextBlock</a> that make up the panel, and properties of the panel itself. Most notable are
<a>GraphObject.portId</a> to declare the shape as a port, and <a>GraphObject.fromLinkable</a> and
<a>GraphObject.toLinkable</a> to set the way the ports can be linked.
</p>
<p>
The diagram also uses the <b>makeTemplate</b> function to create the node templates with shared features.
This function takes a type, an image, a background color, and arrays of ports to create the node
to be added to the <a>Diagram.nodeTemplateMap</a>.
</p>
<p>
For the same data model rendered somewhat differently, see the <a href="dataFlow.html">Data Flow (horizontal)</a> sample.
</p>
<div>
<div>
<button id="SaveButton" onclick="save()">Save</button>
<button onclick="load()">Load</button>
Diagram Model saved in JSON format:
</div>
<textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "go.GraphLinksModel",
"nodeCategoryProperty": "type",
"linkFromPortIdProperty": "frompid",
"linkToPortIdProperty": "topid",
"nodeDataArray": [
{"key":1, "type":"Table", "name":"Product"},
{"key":2, "type":"Table", "name":"Sales"},
{"key":3, "type":"Table", "name":"Period"},
{"key":4, "type":"Table", "name":"Store"},
{"key":11, "type":"Join", "name":"Product, Class"},
{"key":12, "type":"Join", "name":"Period"},
{"key":13, "type":"Join", "name":"Store"},
{"key":21, "type":"Project", "name":"Product, Class"},
{"key":31, "type":"Filter", "name":"Boston, Jan2014"},
{"key":32, "type":"Filter", "name":"Boston, 2014"},
{"key":41, "type":"Group", "name":"Sales"},
{"key":42, "type":"Group", "name":"Total Sales"},
{"key":51, "type":"Join", "name":"Product Name"},
{"key":61, "type":"Sort", "name":"Product Name"},
{"key":71, "type":"Export", "name":"File"}
],
"linkDataArray": [
{"from":1, "frompid":"OUT", "to":11, "topid":"L"},
{"from":2, "frompid":"OUT", "to":11, "topid":"R"},
{"from":3, "frompid":"OUT", "to":12, "topid":"R"},
{"from":4, "frompid":"OUT", "to":13, "topid":"R"},
{"from":11, "frompid":"M", "to":12, "topid":"L"},
{"from":12, "frompid":"M", "to":13, "topid":"L"},
{"from":13, "frompid":"M", "to":21},
{"from":21, "frompid":"OUT", "to":31},
{"from":21, "frompid":"OUT", "to":32},
{"from":31, "frompid":"OUT", "to":41},
{"from":32, "frompid":"OUT", "to":42},
{"from":41, "frompid":"OUT", "to":51, "topid":"L"},
{"from":42, "frompid":"OUT", "to":51, "topid":"R"},
{"from":51, "frompid":"OUT", "to":61},
{"from":61, "frompid":"OUT", "to":71}
]}
</textarea>
</div>
</div>
</body>
</html>
+468
View File
@@ -0,0 +1,468 @@
<!DOCTYPE html>
<html>
<head>
<title>Data Visualization GoJS Sample</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Interactive visualization of multi-dimensional data with HTML tooltips." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<!--
The div needs an explicit size or else we won't see anything.
Lets also add a border to help see the edges.
-->
<style type="text/css">
#myDiagramDiv {
border: solid 1px black;
width: 400px;
height: 400px;
margin-right: 12px;
float: left;
background-color: whitesmoke;
}
#controls {
border: solid 1px gray;
width: 250px;
background: #ffffff;
padding: 5px;
float: left;
}
#infoBoxHolder {
z-index: 300;
position: absolute;
left: 5px;
}
#infoBox {
border: 1px solid #999;
padding: 8px;
background-color: whitesmoke;
opacity: 0.9;
position: relative;
width: 170px;
font-family: arial, helvetica, sans-serif;
font-weight: bold;
font-size: 11px;
}
/* this is known as the "clearfix" hack to allow
floated objects to add to the height of a div */
#infoBox:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
div.infoTitle {
width: 100px;
font-weight: normal;
color: #787878;
float: left;
margin-left: 4px;
}
div.infoValues {
width: 30px;
text-align: right;
float: right;
}
label:hover, label:focus {
background: #CEDFF2;
}
</style>
<script src="../release/go.js"></script>
<script src="../extensions/Figures.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<script id="code">
var myDiagram;
var myLocation = { // this controls the data properties used by data binding conversions
x: "sepalLength",
y: "sepalWidth"
}
var lastStroked = null; // this remembers the last highlight Shape
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 myToolTip = $(go.HTMLInfo, {
show: showToolTip,
// do nothing on hide: This tooltip doesn't hide unless the mouse leaves the diagram
})
myDiagram =
$(go.Diagram, "myDiagramDiv", // create a Diagram for the DIV HTML element
{
"animationManager.initialAnimationStyle": go.AnimationManager.AnimateLocations,
contentAlignment: go.Spot.Center, // content is always centered in the viewport
autoScale: go.Diagram.Uniform, // scale always has all content fitting in the viewport
"toolManager.hoverDelay": 10, // how quickly tooltips are shown
isReadOnly: true, // don't let users modify anything
mouseOver: doMouseOver, // this event handler is defined below
click: doMouseOver // this event handler is defined below
});
// define a simple Node template
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{
selectable: false,
toolTip: myToolTip
},
new go.Binding("location", "", function(nothing, elt) {
return new go.Point(elt.data[myLocation.x] * 200,
elt.data[myLocation.y] * 200)
}),
new go.AnimationTrigger("position", null, go.AnimationTrigger.Bundled),
$(go.Shape, "Hexagon",
{
name: "SHAPE",
width: 20, height: 20,
strokeWidth: 4, stroke: null
},
new go.Binding("fill", "species", function(name) {
switch (name) {
case "Iris-setosa": return "rgba(240, 120, 50, .6)";
case "Iris-versicolor": return "rgba(240, 230, 120, .6)";
case "Iris-virginica": return "rgba(125, 200, 120, .6)";
}
return "black";
}))
);
// This is the fundamental data set, taken from:
// https://en.wikipedia.org/wiki/Iris_flower_data_set
var irisData = [
[5.1, 3.5, 1.4, 0.2, "Iris-setosa"],
[4.9, 3.0, 1.4, 0.2, "Iris-setosa"],
[4.7, 3.2, 1.3, 0.2, "Iris-setosa"],
[4.6, 3.1, 1.5, 0.2, "Iris-setosa"],
[5.0, 3.6, 1.4, 0.2, "Iris-setosa"],
[5.4, 3.9, 1.7, 0.4, "Iris-setosa"],
[4.6, 3.4, 1.4, 0.3, "Iris-setosa"],
[5.0, 3.4, 1.5, 0.2, "Iris-setosa"],
[4.4, 2.9, 1.4, 0.2, "Iris-setosa"],
[4.9, 3.1, 1.5, 0.1, "Iris-setosa"],
[5.4, 3.7, 1.5, 0.2, "Iris-setosa"],
[4.8, 3.4, 1.6, 0.2, "Iris-setosa"],
[4.8, 3.0, 1.4, 0.1, "Iris-setosa"],
[4.3, 3.0, 1.1, 0.1, "Iris-setosa"],
[5.8, 4.0, 1.2, 0.2, "Iris-setosa"],
[5.7, 4.4, 1.5, 0.4, "Iris-setosa"],
[5.4, 3.9, 1.3, 0.4, "Iris-setosa"],
[5.1, 3.5, 1.4, 0.3, "Iris-setosa"],
[5.7, 3.8, 1.7, 0.3, "Iris-setosa"],
[5.1, 3.8, 1.5, 0.3, "Iris-setosa"],
[5.4, 3.4, 1.7, 0.2, "Iris-setosa"],
[5.1, 3.7, 1.5, 0.4, "Iris-setosa"],
[4.6, 3.6, 1.0, 0.2, "Iris-setosa"],
[5.1, 3.3, 1.7, 0.5, "Iris-setosa"],
[4.8, 3.4, 1.9, 0.2, "Iris-setosa"],
[5.0, 3.0, 1.6, 0.2, "Iris-setosa"],
[5.0, 3.4, 1.6, 0.4, "Iris-setosa"],
[5.2, 3.5, 1.5, 0.2, "Iris-setosa"],
[5.2, 3.4, 1.4, 0.2, "Iris-setosa"],
[4.7, 3.2, 1.6, 0.2, "Iris-setosa"],
[4.8, 3.1, 1.6, 0.2, "Iris-setosa"],
[5.4, 3.4, 1.5, 0.4, "Iris-setosa"],
[5.2, 4.1, 1.5, 0.1, "Iris-setosa"],
[5.5, 4.2, 1.4, 0.2, "Iris-setosa"],
[4.9, 3.1, 1.5, 0.2, "Iris-setosa"],
[5.0, 3.2, 1.2, 0.2, "Iris-setosa"],
[5.5, 3.5, 1.3, 0.2, "Iris-setosa"],
[4.9, 3.6, 1.4, 0.1, "Iris-setosa"],
[4.4, 3.0, 1.3, 0.2, "Iris-setosa"],
[5.1, 3.4, 1.5, 0.2, "Iris-setosa"],
[5.0, 3.5, 1.3, 0.3, "Iris-setosa"],
[4.5, 2.3, 1.3, 0.3, "Iris-setosa"],
[4.4, 3.2, 1.3, 0.2, "Iris-setosa"],
[5.0, 3.5, 1.6, 0.6, "Iris-setosa"],
[5.1, 3.8, 1.9, 0.4, "Iris-setosa"],
[4.8, 3.0, 1.4, 0.3, "Iris-setosa"],
[5.1, 3.8, 1.6, 0.2, "Iris-setosa"],
[4.6, 3.2, 1.4, 0.2, "Iris-setosa"],
[5.3, 3.7, 1.5, 0.2, "Iris-setosa"],
[5.0, 3.3, 1.4, 0.2, "Iris-setosa"],
[7.0, 3.2, 4.7, 1.4, "Iris-versicolor"],
[6.4, 3.2, 4.5, 1.5, "Iris-versicolor"],
[6.9, 3.1, 4.9, 1.5, "Iris-versicolor"],
[5.5, 2.3, 4.0, 1.3, "Iris-versicolor"],
[6.5, 2.8, 4.6, 1.5, "Iris-versicolor"],
[5.7, 2.8, 4.5, 1.3, "Iris-versicolor"],
[6.3, 3.3, 4.7, 1.6, "Iris-versicolor"],
[4.9, 2.4, 3.3, 1.0, "Iris-versicolor"],
[6.6, 2.9, 4.6, 1.3, "Iris-versicolor"],
[5.2, 2.7, 3.9, 1.4, "Iris-versicolor"],
[5.0, 2.0, 3.5, 1.0, "Iris-versicolor"],
[5.9, 3.0, 4.2, 1.5, "Iris-versicolor"],
[6.0, 2.2, 4.0, 1.0, "Iris-versicolor"],
[6.1, 2.9, 4.7, 1.4, "Iris-versicolor"],
[5.6, 2.9, 3.6, 1.3, "Iris-versicolor"],
[6.7, 3.1, 4.4, 1.4, "Iris-versicolor"],
[5.6, 3.0, 4.5, 1.5, "Iris-versicolor"],
[5.8, 2.7, 4.1, 1.0, "Iris-versicolor"],
[6.2, 2.2, 4.5, 1.5, "Iris-versicolor"],
[5.6, 2.5, 3.9, 1.1, "Iris-versicolor"],
[5.9, 3.2, 4.8, 1.8, "Iris-versicolor"],
[6.1, 2.8, 4.0, 1.3, "Iris-versicolor"],
[6.3, 2.5, 4.9, 1.5, "Iris-versicolor"],
[6.1, 2.8, 4.7, 1.2, "Iris-versicolor"],
[6.4, 2.9, 4.3, 1.3, "Iris-versicolor"],
[6.6, 3.0, 4.4, 1.4, "Iris-versicolor"],
[6.8, 2.8, 4.8, 1.4, "Iris-versicolor"],
[6.7, 3.0, 5.0, 1.7, "Iris-versicolor"],
[6.0, 2.9, 4.5, 1.5, "Iris-versicolor"],
[5.7, 2.6, 3.5, 1.0, "Iris-versicolor"],
[5.5, 2.4, 3.8, 1.1, "Iris-versicolor"],
[5.5, 2.4, 3.7, 1.0, "Iris-versicolor"],
[5.8, 2.7, 3.9, 1.2, "Iris-versicolor"],
[6.0, 2.7, 5.1, 1.6, "Iris-versicolor"],
[5.4, 3.0, 4.5, 1.5, "Iris-versicolor"],
[6.0, 3.4, 4.5, 1.6, "Iris-versicolor"],
[6.7, 3.1, 4.7, 1.5, "Iris-versicolor"],
[6.3, 2.3, 4.4, 1.3, "Iris-versicolor"],
[5.6, 3.0, 4.1, 1.3, "Iris-versicolor"],
[5.5, 2.5, 4.0, 1.3, "Iris-versicolor"],
[5.5, 2.6, 4.4, 1.2, "Iris-versicolor"],
[6.1, 3.0, 4.6, 1.4, "Iris-versicolor"],
[5.8, 2.6, 4.0, 1.2, "Iris-versicolor"],
[5.0, 2.3, 3.3, 1.0, "Iris-versicolor"],
[5.6, 2.7, 4.2, 1.3, "Iris-versicolor"],
[5.7, 3.0, 4.2, 1.2, "Iris-versicolor"],
[5.7, 2.9, 4.2, 1.3, "Iris-versicolor"],
[6.2, 2.9, 4.3, 1.3, "Iris-versicolor"],
[5.1, 2.5, 3.0, 1.1, "Iris-versicolor"],
[5.7, 2.8, 4.1, 1.3, "Iris-versicolor"],
[6.3, 3.3, 6.0, 2.5, "Iris-virginica"],
[5.8, 2.7, 5.1, 1.9, "Iris-virginica"],
[7.1, 3.0, 5.9, 2.1, "Iris-virginica"],
[6.3, 2.9, 5.6, 1.8, "Iris-virginica"],
[6.5, 3.0, 5.8, 2.2, "Iris-virginica"],
[7.6, 3.0, 6.6, 2.1, "Iris-virginica"],
[4.9, 2.5, 4.5, 1.7, "Iris-virginica"],
[7.3, 2.9, 6.3, 1.8, "Iris-virginica"],
[6.7, 2.5, 5.8, 1.8, "Iris-virginica"],
[7.2, 3.6, 6.1, 2.5, "Iris-virginica"],
[6.5, 3.2, 5.1, 2.0, "Iris-virginica"],
[6.4, 2.7, 5.3, 1.9, "Iris-virginica"],
[6.8, 3.0, 5.5, 2.1, "Iris-virginica"],
[5.7, 2.5, 5.0, 2.0, "Iris-virginica"],
[5.8, 2.8, 5.1, 2.4, "Iris-virginica"],
[6.4, 3.2, 5.3, 2.3, "Iris-virginica"],
[6.5, 3.0, 5.5, 1.8, "Iris-virginica"],
[7.7, 3.8, 6.7, 2.2, "Iris-virginica"],
[7.7, 2.6, 6.9, 2.3, "Iris-virginica"],
[6.0, 2.2, 5.0, 1.5, "Iris-virginica"],
[6.9, 3.2, 5.7, 2.3, "Iris-virginica"],
[5.6, 2.8, 4.9, 2.0, "Iris-virginica"],
[7.7, 2.8, 6.7, 2.0, "Iris-virginica"],
[6.3, 2.7, 4.9, 1.8, "Iris-virginica"],
[6.7, 3.3, 5.7, 2.1, "Iris-virginica"],
[7.2, 3.2, 6.0, 1.8, "Iris-virginica"],
[6.2, 2.8, 4.8, 1.8, "Iris-virginica"],
[6.1, 3.0, 4.9, 1.8, "Iris-virginica"],
[6.4, 2.8, 5.6, 2.1, "Iris-virginica"],
[7.2, 3.0, 5.8, 1.6, "Iris-virginica"],
[7.4, 2.8, 6.1, 1.9, "Iris-virginica"],
[7.9, 3.8, 6.4, 2.0, "Iris-virginica"],
[6.4, 2.8, 5.6, 2.2, "Iris-virginica"],
[6.3, 2.8, 5.1, 1.5, "Iris-virginica"],
[6.1, 2.6, 5.6, 1.4, "Iris-virginica"],
[7.7, 3.0, 6.1, 2.3, "Iris-virginica"],
[6.3, 3.4, 5.6, 2.4, "Iris-virginica"],
[6.4, 3.1, 5.5, 1.8, "Iris-virginica"],
[6.0, 3.0, 4.8, 1.8, "Iris-virginica"],
[6.9, 3.1, 5.4, 2.1, "Iris-virginica"],
[6.7, 3.1, 5.6, 2.4, "Iris-virginica"],
[6.9, 3.1, 5.1, 2.3, "Iris-virginica"],
[5.8, 2.7, 5.1, 1.9, "Iris-virginica"],
[6.8, 3.2, 5.9, 2.3, "Iris-virginica"],
[6.7, 3.3, 5.7, 2.5, "Iris-virginica"],
[6.7, 3.0, 5.2, 2.3, "Iris-virginica"],
[6.3, 2.5, 5.0, 1.9, "Iris-virginica"],
[6.5, 3.0, 5.2, 2.0, "Iris-virginica"],
[6.2, 3.4, 5.4, 2.3, "Iris-virginica"],
[5.9, 3.0, 5.1, 1.8, "Iris-virginica"]
];
// now convert that data into an Array of JavaScript Objects,
// to be used as the Model.nodeDataArray
var array = [];
for (var i = 0; i < irisData.length; i++) {
var line = irisData[i];
var data = {
sepalLength: line[0],
sepalWidth: line[1],
petalLength: line[2],
petalWidth: line[3],
species: line[4]
};
array.push(data);
}
// create the Model for the Diagram to display
myDiagram.model = new go.Model(array);
// Called when the mouse is over the diagram's background
function doMouseOver(e) {
if (e === undefined) e = myDiagram.lastInput;
var doc = e.documentPoint;
// find all Nodes that are within 100 units
var list = myDiagram.findObjectsNear(doc, 100, null, function(x) { return x instanceof go.Node; });
// now find the one that is closest to e.documentPoint
var closest = null;
var closestDist = 999999999;
list.each(function(node) {
var dist = doc.distanceSquaredPoint(node.getDocumentPoint(go.Spot.Center));
if (dist < closestDist) {
closestDist = dist;
closest = node;
}
});
showToolTip(closest, myDiagram);
}
// Called with a Node (or null) that the mouse is over or near
function showToolTip(obj, diagram) {
if (obj !== null) {
var node = obj.part;
var e = diagram.lastInput;
var shape = node.findObject("SHAPE");
shape.stroke = "white";
if (lastStroked !== null && lastStroked !== shape) lastStroked.stroke = null;
lastStroked = shape;
updateInfoBox(e.viewPoint, node.data);
} else {
if (lastStroked !== null) lastStroked.stroke = null;
lastStroked = null;
document.getElementById("infoBoxHolder").innerHTML = "";
}
}
// Make sure the infoBox is momentarily hidden if the user tries to mouse over it
var infoBoxH = document.getElementById("infoBoxHolder");
infoBoxH.addEventListener("mousemove", function() {
var box = document.getElementById("infoBoxHolder");
box.style.left = parseInt(box.style.left) + "px";
box.style.top = parseInt(box.style.top) + 30 + "px";
}, false);
var diagramDiv = document.getElementById("myDiagramDiv");
// Make sure the infoBox is hidden when the mouse is not over the Diagram
diagramDiv.addEventListener("mouseout", function(e) {
if (lastStroked !== null) lastStroked.stroke = null;
lastStroked = null;
var infoBox = document.getElementById("infoBox");
var elem = document.elementFromPoint(e.clientX, e.clientY);
if (elem !== null && (elem === infoBox || elem.parentNode === infoBox)) {
var box = document.getElementById("infoBoxHolder");
box.style.left = parseInt(box.style.left) + "px";
box.style.top = parseInt(box.style.top) + 30 + "px";
} else {
var box = document.getElementById("infoBoxHolder");
box.innerHTML = "";
}
}, false);
} // end init
// This function is called to update the tooltip information
// depending on the bound data of the Node that is closest to the pointer.
function updateInfoBox(mousePt, data) {
var box = document.getElementById("infoBoxHolder");
box.innerHTML = "";
var infobox = document.createElement("div");
infobox.id = "infoBox";
box.appendChild(infobox);
for (var i = 0; i < 9; i++) {
var child = document.createElement("div");
switch (i) {
case 0: child.textContent = data.species; break;
case 1: child.className = "infoTitle"; child.textContent = "Sepal Length"; break;
case 2: child.className = "infoValues"; child.textContent = data.sepalLength; break;
case 3: child.className = "infoTitle"; child.textContent = "Sepal Width"; break;
case 4: child.className = "infoValues"; child.textContent = data.sepalWidth; break;
case 5: child.className = "infoTitle"; child.textContent = "Petal Length"; break;
case 6: child.className = "infoValues"; child.textContent = data.petalLength; break;
case 7: child.className = "infoTitle"; child.textContent = "Petal Width"; break;
case 8: child.className = "infoValues"; child.textContent = data.petalWidth; break;
}
infobox.appendChild(child);
}
box.style.left = mousePt.x + 30 + "px";
box.style.top = mousePt.y + 20 + "px";
}
// This function is called which a radio button is pressed.
// It changes the value of a variable that the node template's location conversion function uses.
// It then updates all target bindings, causing all node locations to change.
function changeAxes(e) {
var value = e.value;
if (e.name === "X") {
myLocation.x = value;
} else {
myLocation.y = value;
}
myDiagram.startTransaction("updateBindings");
myDiagram.updateAllTargetBindings();
myDiagram.commitTransaction("updateBindings");
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div style="display: inline-block">
<!--The DIV for the Diagram needs an explicit size or else we won't see anything.
Also add a border to help see the edges. -->
<div id="myDiagramDiv" style="background-color: #3D3D3D; border: solid 1px black; width:500px; height:500px"></div>
<!-- This sibling of the Diagram provides information when the mouse is near a Diagram Node -->
<div id="infoBoxHolder">
<!-- Initially Empty, it is populated when updateInfoBox is called -->
</div>
<div id="controls">
<div style="border: solid 1px gray; float: left; padding: 2px;">
<p style="text-align: center;">X-axis</p>
<hr/>
<input type="radio" name="X" onclick="changeAxes(this)" value="sepalLength" id="SLx" checked/>
<label for="SLx">Sepal Length</label><br/>
<input type="radio" name="X" onclick="changeAxes(this)" value="sepalWidth" id="SWx" />
<label for="SWx">Sepal Width</label><br/>
<input type="radio" name="X" onclick="changeAxes(this)" value="petalLength" id="PLx"/>
<label for="PLx">Petal Length</label><br/>
<input type="radio" name="X" onclick="changeAxes(this)" value="petalWidth" id="PWx"/>
<label for="PWx">Petal Width</label><br/>
</div>
<div style="border: solid 1px gray; float: left; margin-left: 10px; padding: 2px;">
<p style="text-align: center;">Y-axis</p>
<hr/>
<input type="radio" name="Y" onclick="changeAxes(this)" value="sepalLength" id="SLy"/>
<label for="SLy">Sepal Length</label><br/>
<input type="radio" name="Y" onclick="changeAxes(this)" value="sepalWidth" id="SWy" checked/>
<label for="SWy">Sepal Width</label><br/>
<input type="radio" name="Y" onclick="changeAxes(this)" value="petalLength" id="PLy"/>
<label for="PLy">Petal Length</label><br/>
<input type="radio" name="Y" onclick="changeAxes(this)" value="petalWidth" id="PWy"/>
<label for="PWy">Petal Width</label><br/>
</div>
<div id="description" style="float: left;">
<p>This sample gives an example of a Diagram interacting with other HTML elements on the page.</p>
<p>As the mouse moves over the diagram, a formatted HTML DIV element displays information about the nearest Node.</p>
<p>The data displayed is from the <a href="https://en.wikipedia.org/wiki/Iris_flower_data_set">Iris flower data set</a>,
describing the variations in dimensions of three related flower species.</p>
</div>
</div>
</div>
</div>
</body>
</html>
+351
View File
@@ -0,0 +1,351 @@
<!DOCTYPE html>
<html>
<head>
<title>Decision Tree</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Interactive decision diagram with automatic expansion as the user makes choices." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<link href='https://fonts.googleapis.com/css?family=Roboto:400,500' rel='stylesheet' type='text/css'>
<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
myDiagram = $(go.Diagram, "myDiagramDiv", // must name or refer to the DIV HTML element
{
initialContentAlignment: go.Spot.Left,
allowSelect: false, // the user cannot select any part
// create a TreeLayout for the decision tree
layout: $(go.TreeLayout)
});
// custom behavior for expanding/collapsing half of the subtree from a node
function buttonExpandCollapse(e, port) {
var node = port.part;
node.diagram.startTransaction("expand/collapse");
var portid = port.portId;
node.findLinksOutOf(portid).each(function(l) {
if (l.visible) {
// collapse whole subtree recursively
collapseTree(node, portid);
} else {
// only expands immediate children and their links
l.visible = true;
var n = l.getOtherNode(node);
if (n !== null) {
n.location = node.getDocumentPoint(go.Spot.TopRight);
n.visible = true;
}
}
});
myDiagram.toolManager.hideToolTip();
node.diagram.commitTransaction("expand/collapse");
}
// recursive function for collapsing complete subtree
function collapseTree(node, portid) {
node.findLinksOutOf(portid).each(function(l) {
l.visible = false;
var n = l.getOtherNode(node);
if (n !== null) {
n.visible = false;
collapseTree(n, null); // null means all links, not just for a particular portId
}
});
}
// get the text for the tooltip from the data on the object being hovered over
function tooltipTextConverter(data) {
var str = "";
var e = myDiagram.lastInput;
var currobj = e.targetObject;
if (currobj !== null && (currobj.name === "ButtonA" ||
(currobj.panel !== null && currobj.panel.name === "ButtonA"))) {
str = data.aToolTip;
} else {
str = data.bToolTip;
}
return str;
}
// define tooltips for buttons
var tooltipTemplate =
$("ToolTip",
{ "Border.fill": "whitesmoke", "Border.stroke": "lightgray" },
$(go.TextBlock,
{
font: "8pt sans-serif",
wrap: go.TextBlock.WrapFit,
desiredSize: new go.Size(200, NaN),
alignment: go.Spot.Center,
margin: 6
},
new go.Binding("text", "", tooltipTextConverter))
);
// define the Node template for non-leaf nodes
myDiagram.nodeTemplateMap.add("decision",
$(go.Node, "Auto",
new go.Binding("text", "key"),
// define the node's outer shape, which will surround the Horizontal Panel
$(go.Shape, "Rectangle",
{ fill: "whitesmoke", stroke: "lightgray" }),
// define a horizontal Panel to place the node's text alongside the buttons
$(go.Panel, "Horizontal",
$(go.TextBlock,
{ font: "30px Roboto, sans-serif", margin: 5 },
new go.Binding("text", "key")),
// define a vertical panel to place the node's two buttons one above the other
$(go.Panel, "Vertical",
{ defaultStretch: go.GraphObject.Fill, margin: 3 },
$("Button", // button A
{
name: "ButtonA",
click: buttonExpandCollapse,
toolTip: tooltipTemplate
},
new go.Binding("portId", "a"),
$(go.TextBlock,
{ font: '500 16px Roboto, sans-serif' },
new go.Binding("text", "aText"))
), // end button A
$("Button", // button B
{
name: "ButtonB",
click: buttonExpandCollapse,
toolTip: tooltipTemplate
},
new go.Binding("portId", "b"),
$(go.TextBlock,
{ font: '500 16px Roboto, sans-serif' },
new go.Binding("text", "bText"))
) // end button B
) // end Vertical Panel
) // end Horizontal Panel
)); // end Node and call to add
// define the Node template for leaf nodes
myDiagram.nodeTemplateMap.add("personality",
$(go.Node, "Auto",
new go.Binding("text", "key"),
$(go.Shape, "Rectangle",
{ fill: "whitesmoke", stroke: "lightgray" }),
$(go.TextBlock,
{
font: '13px Roboto, sans-serif',
wrap: go.TextBlock.WrapFit, desiredSize: new go.Size(200, NaN), margin: 5
},
new go.Binding("text", "text"))
));
// define the only Link template
myDiagram.linkTemplate =
$(go.Link, go.Link.Orthogonal, // the whole link panel
{ fromPortId: "" },
new go.Binding("fromPortId", "fromport"),
$(go.Shape, // the link shape
{ stroke: "lightblue", strokeWidth: 2 })
);
// create the model for the decision tree
var model =
$(go.GraphLinksModel,
{ linkFromPortIdProperty: "fromport" });
// set up the model with the node and link data
makeNodes(model);
makeLinks(model);
myDiagram.model = model;
// make all but the start node invisible
myDiagram.nodes.each(function(n) {
if (n.text !== "Start") n.visible = false;
});
myDiagram.links.each(function(l) {
l.visible = false;
});
}
function makeNodes(model) {
var nodeDataArray = [
{ key: "Start" }, // the root node
// intermediate nodes: decisions on personality characteristics
{ key: "I" },
{ key: "E" },
{ key: "IN" },
{ key: "IS" },
{ key: "EN" },
{ key: "ES" },
{ key: "INT" },
{ key: "INF" },
{ key: "IST" },
{ key: "ISF" },
{ key: "ENT" },
{ key: "ENF" },
{ key: "EST" },
{ key: "ESF" },
// terminal nodes: the personality descriptions
{
key: "INTJ",
text: "INTJ: Scientist\nThe most self-confident of all types. They focus on possibilities and use empirical logic to think about the future. They prefer that events and people serve some positive use. 1% of population."
},
{
key: "INTP",
text: "INTP: Architect\nAn architect of ideas, number systems, computer languages, and many other concepts. They exhibit great precision in thought and language. 1% of the population."
},
{
key: "INFJ",
text: "INFJ: Author\nFocus on possibilities. Place emphasis on values and come to decisions easily. They have a strong drive to contribute to the welfare of others. 1% of population."
},
{
key: "INFP",
text: "INFP: Questor\nPresent a calm and pleasant face to the world. Although they seem reserved, they are actually very idealistic and care passionately about a few special people or a cause. 1% of the population."
},
{
key: "ISTJ",
text: "ISTJ: Trustee\nISTJs like organized lives. They are dependable and trustworthy, as they dislike chaos and work on a task until completion. They prefer to deal with facts rather than emotions. 6% of the population."
},
{
key: "ISTP",
text: "ISTP: Artisan\nISTPs are quiet people who are very capable at analyzing how things work. Though quiet, they can be influential, with their seclusion making them all the more skilled. 17% of the population."
},
{
key: "ISFJ",
text: "ISFJ: Conservator\nISFJs are not particularly social and tend to be most concerned with maintaining order in their lives. They are dutiful, respectful towards, and interested in others, though they are often shy. They are, therefore, trustworthy, but not bossy. 6% of the population."
},
{
key: "ISFP",
text: "ISFP: Author\nFocus on possibilities. Place emphasis on values and come to decisions easily. They have a strong drive to contribute to the welfare of others. 1% of population."
},
{
key: "ENTJ",
text: "ENTJ: Fieldmarshal\nThe driving force of this personality is to lead. They like to impose structure and harness people to work towards distant goals. They reject inefficiency. 5% of the population."
},
{
key: "ENTP",
text: "ENTP: Inventor\nExercise their ingenuity by dealing with social, physical, and mechanical relationships. They are always sensitive to future possibilities. 5% of the population."
},
{
key: "ENFJ",
text: "ENFJ: Pedagogue\nExcellent leaders; they are charismatic and never doubt that others will follow them and do as they ask. They place a high value on cooperation. 5% of the population."
},
{
key: "ENFP",
text: "ENFP: Journalist\nPlace significance in everyday occurrences. They have great ability to understand the motives of others. They see life as a great drama. They have a great impact on others. 5% of the population."
},
{
key: "ESTJ",
text: "ESTJ: Administrator\nESTJs are pragmatic, and thus well-suited for business or administrative roles. They are traditionalists and conservatives, believing in the status quo. 13% of the population."
},
{
key: "ESTP",
text: "ESTP: Promoter\nESTPs tend to manipulate others in order to attain access to the finer aspects of life. However, they enjoy heading to such places with others. They are social and outgoing and are well-connected. 13% of the population."
},
{
key: "ESFJ",
text: "ESFJ: Seller\nESFJs tend to be social and concerned for others. They follow tradition and enjoy a structured community environment. Always magnanimous towards others, they expect the same respect and appreciation themselves. 13% of the population."
},
{
key: "ESFP",
text: "ESFP: Entertainer\nThe mantra of the ESFP would be \"Carpe Diem.\" They enjoy life to the fullest. They do not, thus, like routines and long-term goals. In general, they are very concerned with others and tend to always try to help others, often perceiving well their needs. 13% of the population."
}
];
// Provide the same choice information for all of the nodes on each level.
// The level is implicit in the number of characters in the Key, except for the root node.
// In a different application, there might be different choices for each node, so the initialization would be above, where the Info's are created.
// But for this application, it makes sense to share the initialization code based on tree level.
for (var i = 0; i < nodeDataArray.length; i++) {
var d = nodeDataArray[i];
if (d.key === "Start") {
d.category = "decision";
d.a = "I";
d.aText = "Introversion";
d.aToolTip = "The Introvert is “territorial” and desires space and solitude to recover energy. Introverts enjoy solitary activities such as reading and meditating. 25% of the population.";
d.b = "E";
d.bText = "Extraversion";
d.bToolTip = "The Extravert is “sociable” and is energized by the presence of other people. Extraverts experience loneliness when not in contact with others. 75% of the population.";
} else {
switch (d.key.length) {
case 1:
d.category = "decision";
d.a = "N";
d.aText = "Intuition";
d.aToolTip = "The “intuitive” person bases their lives on predictions and ingenuity. They consider the future and enjoy planning ahead. 25% of the population.";
d.b = "S";
d.bText = "Sensing";
d.bToolTip = "The “sensing” person bases their life on facts, thinking primarily of their present situation. They are realistic and practical. 75% of the population.";
break;
case 2:
d.category = "decision";
d.a = "T";
d.aText = "Thinking";
d.aToolTip = "The “thinking” person bases their decisions on facts and without personal bias. They are more comfortable with making impersonal judgments. 50% of the population.";
d.b = "F";
d.bText = "Feeling";
d.bToolTip = "The “feeling” person bases their decisions on personal experience and emotion. They make their emotions very visible. 50% of the population.";
break;
case 3:
d.category = "decision";
d.a = "J";
d.aText = "Judgment";
d.aToolTip = "The “judging” person enjoys closure. They establish deadlines and take them seriously. They despise being late. 50% of the population.";
d.b = "P";
d.bText = "Perception";
d.bToolTip = "The “perceiving” person likes to keep options open and fluid. They have little regard for deadlines. Dislikes making decisions unless they are completely sure they are right. 50% of the population.";
break;
default:
d.category = "personality";
break;
}
}
}
model.nodeDataArray = nodeDataArray;
}
// The key strings implicitly hold the relationship information, based on their spellings.
// Other than the root node ("Start"), each node's key string minus its last letter is the
// key to the "parent" node.
function makeLinks(model) {
var linkDataArray = [];
var nda = model.nodeDataArray;
for (var i = 0; i < nda.length; i++) {
var key = nda[i].key;
if (key === "Start" || key.length === 0) continue;
// e.g., if key=="INTJ", we want: prefix="INT" and letter="J"
var prefix = key.slice(0, key.length - 1);
var letter = key.charAt(key.length - 1);
if (prefix.length === 0) prefix = "Start";
var obj = { from: prefix, fromport: letter, to: key };
linkDataArray.push(obj);
}
model.linkDataArray = linkDataArray;
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="background-color: white; border: solid 1px black; width: 100%; height: 500px"></div>
<p>
This sample allows a user to make progressive decisions about personality types.
</p>
<p>
There are two kinds of nodes, so there are two node templates ("decision" and "personality")
that determine the appearance and behavior of each <a>Node</a>.
</p>
<p>
The "decision" template displays the abbreviated personality type and two choice buttons, all surrounded by a figure.
Clicking a button will either expand the choice or will collapse all nodes leading from that choice.
</p>
<p>
The "personality" template displays the personality descriptions, as the "leaf" nodes for the tree.
</p>
</div>
</body>
</html>
+393
View File
@@ -0,0 +1,393 @@
<!DOCTYPE html>
<html>
<head>
<title>Graph Distances and Paths</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Interactive diagram showing all distances from a node, and highlighting all paths between two nodes." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
myDiagram =
$(go.Diagram, "myDiagramDiv", // must be the ID or reference to div
{
initialAutoScale: go.Diagram.UniformToFill,
padding: 10,
contentAlignment: go.Spot.Center,
layout: $(go.ForceDirectedLayout, { defaultSpringLength: 10 }),
maxSelectionCount: 2
});
// define the Node template
myDiagram.nodeTemplate =
$(go.Node, "Horizontal",
{
locationSpot: go.Spot.Center, // Node.location is the center of the Shape
locationObjectName: "SHAPE",
selectionAdorned: false,
selectionChanged: nodeSelectionChanged
},
$(go.Panel, "Auto",
$(go.Shape, "Ellipse",
{
name: "SHAPE",
fill: "lightgray", // default value, but also data-bound
stroke: "transparent", // modified by highlighting
strokeWidth: 2,
desiredSize: new go.Size(30, 30),
portId: ""
}, // so links will go to the shape, not the whole node
new go.Binding("fill", "isSelected", function(s, obj) { return s ? "red" : obj.part.data.color; }).ofObject()),
$(go.TextBlock,
new go.Binding("text", "distance", function(d) { if (d === Infinity) return "INF"; else return d | 0; }))),
$(go.TextBlock,
new go.Binding("text")));
// define the Link template
myDiagram.linkTemplate =
$(go.Link,
{
selectable: false, // links cannot be selected by the user
curve: go.Link.Bezier,
layerName: "Background" // don't cross in front of any nodes
},
$(go.Shape, // this shape only shows when it isHighlighted
{ isPanelMain: true, stroke: null, strokeWidth: 5 },
new go.Binding("stroke", "isHighlighted", function(h) { return h ? "red" : null; }).ofObject()),
$(go.Shape,
// mark each Shape to get the link geometry with isPanelMain: true
{ isPanelMain: true, stroke: "black", strokeWidth: 1 },
new go.Binding("stroke", "color")),
$(go.Shape, { toArrow: "Standard" })
);
// Override the clickSelectingTool's standardMouseSelect
// If less than 2 nodes are selected, always add to the selection
myDiagram.toolManager.clickSelectingTool.standardMouseSelect = function() {
var diagram = this.diagram;
if (diagram === null || !diagram.allowSelect) return;
var e = diagram.lastInput;
var count = diagram.selection.count;
var curobj = diagram.findPartAt(e.documentPoint, false);
if (curobj !== null) {
if (count < 2) { // add the part to the selection
if (!curobj.isSelected) {
var part = curobj;
if (part !== null) part.isSelected = true;
}
} else {
if (!curobj.isSelected) {
var part = curobj;
if (part !== null) diagram.select(part);
}
}
} else if (e.left && !(e.control || e.meta) && !e.shift) {
// left click on background with no modifier: clear selection
diagram.clearSelection();
}
}
generateGraph();
// select two nodes that connect from the first one to the second one
var num = myDiagram.model.nodeDataArray.length;
var node1 = null;
var node2 = null;
for (var i = 0; i < num; i++) {
node1 = myDiagram.findNodeForKey(i);
var distances = findDistances(node1);
for (var j = 0; j < num; j++) {
node2 = myDiagram.findNodeForKey(j);
var dist = distances.get(node2);
if (dist > 1 && dist < Infinity) {
node1.isSelected = true;
node2.isSelected = true;
break;
}
}
if (myDiagram.selection.count > 0) break;
}
}
function generateGraph() {
var names = [
"Joshua", "Kathryn", "Robert", "Jason", "Scott", "Betsy", "John",
"Walter", "Gabriel", "Simon", "Emily", "Tina", "Elena", "Samuel",
"Jacob", "Michael", "Juliana", "Natalie", "Grace", "Ashley", "Dylan"
];
var nodeDataArray = [];
for (var i = 0; i < names.length; i++) {
nodeDataArray.push({ key: i, text: names[i], color: go.Brush.randomColor(128, 240) });
}
var linkDataArray = [];
var num = nodeDataArray.length;
for (var i = 0; i < num * 2; i++) {
var a = Math.floor(Math.random() * num);
var b = Math.floor(Math.random() * num / 4) + 1;
linkDataArray.push({ from: a, to: (a + b) % num, color: go.Brush.randomColor(0, 127) });
}
myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
}
// There are three bits of functionality here:
// 1: findDistances(Node) computes the distance of each Node from the given Node.
// This function is used by showDistances to update the model data.
// 2: findShortestPath(Node, Node) finds a shortest path from one Node to another.
// This uses findDistances. This is used by highlightShortestPath.
// 3: collectAllPaths(Node, Node) produces a collection of all paths from one Node to another.
// This is used by listAllPaths. The result is remembered in a global variable
// which is used by highlightSelectedPath. This does not depend on findDistances.
// Returns a Map of Nodes with distance values from the given source Node.
// Assumes all links are unidirectional.
function findDistances(source) {
var diagram = source.diagram;
// keep track of distances from the source node
var distances = new go.Map(/*go.Node, "number"*/);
// all nodes start with distance Infinity
var nit = diagram.nodes;
while (nit.next()) {
var n = nit.value;
distances.set(n, Infinity);
}
// the source node starts with distance 0
distances.set(source, 0);
// keep track of nodes for which we have set a non-Infinity distance,
// but which we have not yet finished examining
var seen = new go.Set(/*go.Node*/);
seen.add(source);
// keep track of nodes we have finished examining;
// this avoids unnecessary traversals and helps keep the SEEN collection small
var finished = new go.Set(/*go.Node*/);
while (seen.count > 0) {
// look at the unfinished node with the shortest distance so far
var least = leastNode(seen, distances);
var leastdist = distances.get(least);
// by the end of this loop we will have finished examining this LEAST node
seen.delete(least);
finished.add(least);
// look at all Links connected with this node
var it = least.findLinksOutOf();
while (it.next()) {
var link = it.value;
var neighbor = link.getOtherNode(least);
// skip nodes that we have finished
if (finished.has(neighbor)) continue;
var neighbordist = distances.get(neighbor);
// assume "distance" along a link is unitary, but could be any non-negative number.
var dist = leastdist + 1; //Math.sqrt(least.location.distanceSquaredPoint(neighbor.location));
if (dist < neighbordist) {
// if haven't seen that node before, add it to the SEEN collection
if (neighbordist === Infinity) {
seen.add(neighbor);
}
// record the new best distance so far to that node
distances.set(neighbor, dist);
}
}
}
return distances;
}
// This helper function finds a Node in the given collection that has the smallest distance.
function leastNode(coll, distances) {
var bestdist = Infinity;
var bestnode = null;
var it = coll.iterator;
while (it.next()) {
var n = it.value;
var dist = distances.get(n);
if (dist < bestdist) {
bestdist = dist;
bestnode = n;
}
}
return bestnode;
}
// Find a path that is shortest from the BEGIN node to the END node.
// (There might be more than one, and there might be none.)
function findShortestPath(begin, end) {
// compute and remember the distance of each node from the BEGIN node
distances = findDistances(begin);
// now find a path from END to BEGIN, always choosing the adjacent Node with the lowest distance
var path = new go.List();
path.add(end);
while (end !== null) {
var next = leastNode(end.findNodesInto(), distances);
if (next !== null) {
if (distances.get(next) < distances.get(end)) {
path.add(next); // making progress towards the beginning
} else {
next = null; // nothing better found -- stop looking
}
}
end = next;
}
// reverse the list to start at the node closest to BEGIN that is on the path to END
// NOTE: if there's no path from BEGIN to END, the first node won't be BEGIN!
path.reverse();
return path;
}
// Recursively walk the graph starting from the BEGIN node;
// when reaching the END node remember the list of nodes along the current path.
// Finally return the collection of paths, which may be empty.
// This assumes all links are unidirectional.
function collectAllPaths(begin, end) {
var stack = new go.List(/*go.Node*/);
var coll = new go.List(/*go.List*/);
function find(source, end) {
source.findNodesOutOf().each(function(n) {
if (n === source) return; // ignore reflexive links
if (n === end) { // success
var path = stack.copy();
path.add(end); // finish the path at the end node
coll.add(path); // remember the whole path
} else if (!stack.has(n)) { // inefficient way to check having visited
stack.add(n); // remember that we've been here for this path (but not forever)
find(n, end);
stack.removeAt(stack.count - 1);
} // else might be a cycle
});
}
stack.add(begin); // start the path at the begin node
find(begin, end);
return coll;
}
// Return a string representation of a path for humans to read.
function pathToString(path) {
var s = path.length + ": ";
for (var i = 0; i < path.length; i++) {
if (i > 0) s += " -- ";
s += path.get(i).data.text;
}
return s;
}
// When a node is selected show distances from the first selected node.
// When a second node is selected, highlight the shortest path between two selected nodes.
// If a node is deselected, clear all highlights.
function nodeSelectionChanged(node) {
var diagram = node.diagram;
if (diagram === null) return;
diagram.clearHighlighteds();
if (node.isSelected) {
// when there is a selection made, always clear out the list of all paths
var sel = document.getElementById("myPaths");
sel.innerHTML = "";
// show the distance for each node from the selected node
var begin = diagram.selection.first();
showDistances(begin);
if (diagram.selection.count === 2) {
var end = node; // just became selected
// highlight the shortest path
highlightShortestPath(begin, end);
// list all paths
listAllPaths(begin, end);
}
}
}
// Have each node show how far it is from the BEGIN node.
function showDistances(begin) {
// compute and remember the distance of each node from the BEGIN node
distances = findDistances(begin);
// show the distance on each node
var it = distances.iterator;
while (it.next()) {
var n = it.key;
var dist = it.value;
myDiagram.model.setDataProperty(n.data, "distance", dist);
}
}
// Highlight links along one of the shortest paths between the BEGIN and the END nodes.
// Assume links are unidirectional.
function highlightShortestPath(begin, end) {
highlightPath(findShortestPath(begin, end));
}
// List all paths from BEGIN to END
function listAllPaths(begin, end) {
// compute and remember all paths from BEGIN to END: Lists of Nodes
paths = collectAllPaths(begin, end);
// update the Selection element with a bunch of Option elements, one per path
var sel = document.getElementById("myPaths");
sel.innerHTML = ""; // clear out any old Option elements
paths.each(function(p) {
var opt = document.createElement("option");
opt.text = pathToString(p);
sel.add(opt, null);
});
sel.onchange = highlightSelectedPath;
}
// A collection of all of the paths between a pair of nodes, a List of Lists of Nodes
var paths = null;
// This is only used for listing all paths for the selection onchange event.
// When the selected item changes in the Selection element,
// highlight the corresponding path of nodes.
function highlightSelectedPath() {
var sel = document.getElementById("myPaths");
var idx = sel.selectedIndex;
var opt = sel.options[idx];
var val = opt.value;
highlightPath(paths.get(sel.selectedIndex));
}
// Highlight a particular path, a List of Nodes.
function highlightPath(path) {
myDiagram.clearHighlighteds();
for (var i = 0; i < path.count - 1; i++) {
var f = path.get(i);
var t = path.get(i + 1);
f.findLinksTo(t).each(function(l) { l.isHighlighted = true; });
}
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; background: white; width: 100%; height: 700px"></div>
Click on a node to show distances from that node to each other node.
Click on a second node to show a shortest path from the first node to the second node.
(Note that there might not be any path between the nodes.)
<p>
Clicking on a third node will de-select the first two.
<p>
Here is a list of all paths between the first and second selected nodes.
Select a path to highlight it in the diagram.
</p>
<select id="myPaths" style="min-width:100px" size="10"></select>
</div>
</body>
</html>
+93
View File
@@ -0,0 +1,93 @@
<!DOCTYPE html>
<html>
<head>
<title>Donut Charts</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A donut chart in each node." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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;
myDiagram =
$(go.Diagram, "myDiagramDiv");
myDiagram.nodeTemplate =
$(go.Node, "Spot",
$(go.Panel,
$(go.Shape, "Circle", // provide a whole-circle background for the chart
{ width: 100, height: 100, strokeWidth: 0, fill: "transparent" }),
$(go.Shape, { fill: "transparent", stroke: "cyan", strokeWidth: 8 },
new go.Binding("geometry", "value", makeArc),
new go.Binding("stroke", "color1")),
$(go.Shape, { fill: "transparent", stroke: "gray", strokeWidth: 8 },
new go.Binding("geometry", "value", makeArcRest),
new go.Binding("stroke", "color2"))
),
$(go.TextBlock,
new go.Binding("text"))
);
// These arcs assume the angle starts at 270 degrees, at the top of the circle.
// They all assume the circle is 100x100 in size.
function makeArc(sweep) {
return new go.Geometry()
.add(new go.PathFigure(50, 0)
.add(new go.PathSegment(go.PathSegment.Arc, -90, sweep, 50, 50, 50, 50)));
}
function makeArcRest(sweep) {
var p = new go.Point(50, 0).rotate(-90+sweep).offset(50, 50);
return new go.Geometry()
.add(new go.PathFigure(p.x, p.y)
.add(new go.PathSegment(go.PathSegment.Arc, sweep-90, 360-sweep, 50, 50, 50, 50)));
}
myDiagram.model = new go.GraphLinksModel(
[
{ key: 1, text: "Alpha", value: 0 },
{ key: 2, text: "Beta", value: 90 },
{ key: 3, text: "Gamma", value: 135 },
{ key: 4, text: "Delta", value: 330, color1: "red", color2: "green" }
],
[
{ from: 1, to: 2 },
{ from: 1, to: 3 },
{ from: 3, to: 4 },
{ from: 4, to: 1 }
]);
}
function changeValue() {
var node = myDiagram.selection.first();
if (node instanceof go.Node) {
myDiagram.model.commit(function(m) {
var val = node.data.value;
val += Math.random() * 40 - 20;
if (val < 0) val = 20;
else if (val >= 360) val = 340;
m.set(node.data, "value", val);
}, "changed data value");
}
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
<button onclick="changeValue()">Change Selected Value</button>
<p>
Each node contains a Position Panel containing two Shape elements that get Geometry values
that show a data value as an annular bar in a circle. One can also specify the colors of
the two bars -- the bar showing the value and the rest of the circle.
</p>
<p>
For more sophisticated charts within nodes, see the <a href="canvases.html">Canvas Charts</a> sample.
</p>
</div>
</body>
</html>
+92
View File
@@ -0,0 +1,92 @@
<!DOCTYPE html>
<html>
<head>
<title>Double Circle</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Arrange nodes into concentric circles using CircularLayout." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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 in this function
myDiagram =
$(go.Diagram, "myDiagramDiv", // must be the ID or reference to div
{
initialAutoScale: go.Diagram.Uniform,
"animationManager.isEnabled": false
});
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{ locationSpot: go.Spot.Center },
$(go.Shape, "Circle",
{ fill: "gray", stroke: "#D8D8D8" },
new go.Binding("fill", "color")),
// define the node's text
$(go.TextBlock,
{ margin: 5, font: "bold 11px Helvetica, bold Arial, sans-serif" },
new go.Binding("text", "key"))
);
// create the model for the double circle
var data = [];
// if you want a node in the center, set its layer: 0
//data.push({ layer: 0, color: "red" });
for (var i = 0; i < 10; i++) data.push({ layer: 1, color: go.Brush.randomColor() });
for (var i = 0; i < 25; i++) data.push({ layer: 2, color: go.Brush.randomColor() });
myDiagram.model = new go.GraphLinksModel(data);
doubleCircleLayout(myDiagram);
}
function doubleCircleLayout(diagram) {
var $ = go.GraphObject.make; // for conciseness in defining templates
diagram.startTransaction("Multi Circle Layout");
var radius = 100;
var layer = 1;
var nodes = null;
while (nodes = nodesByLayer(diagram, layer), nodes.count > 0) {
var layout = $(go.CircularLayout,
{ radius: radius });
layout.doLayout(nodes);
// recenter at (0, 0)
var cntr = layout.actualCenter;
diagram.moveParts(nodes, new go.Point(-cntr.x, -cntr.y));
// next layout uses a larger radius
radius += 100;
layer++;
}
nodesByLayer(diagram, 0).each(function(n) { n.location = new go.Point(0, 0); });
diagram.commitTransaction("Multi Circle Layout");
}
function nodesByLayer(diagram, layer) {
var set = new go.Set(/*go.Node*/);
diagram.nodes.each(function(part) {
if (part instanceof go.Node && part.data.layer === layer) set.add(part);
});
return set;
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="background-color: white; border: solid 1px black; width: 100%; height: 500px"></div>
<p>
This sample displays a diagram of two sets of nodes intended to be arranged in different circles.
</p>
<p>
Unlike many <b>GoJS</b> apps, there is no <a>Diagram.layout</a> assigned.
Layouts are performed explicitly in code -- a separate <a>CircularLayout</a> for each subset of nodes.
The code will actually work with a variable number of layers/circles, not just two.
</p>
</div>
</body>
</html>
+95
View File
@@ -0,0 +1,95 @@
<!DOCTYPE html>
<html>
<head>
<title>Double Tree</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Layout of two trees in opposite directions, assuming a single root, using TreeLayout." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../extensions/DoubleTreeLayout.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 in this function
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
layout: $(DoubleTreeLayout,
{
//vertical: true, // default directions are horizontal
// choose whether this subtree is growing towards the right or towards the left:
directionFunction: function(n) { return n.data && n.data.dir !== "left"; }
// controlling the parameters of each TreeLayout:
//bottomRightOptions: { nodeSpacing: 0, layerSpacing: 20 },
//topLeftOptions: { alignment: go.TreeLayout.AlignmentStart },
})
});
// define all of the gradient brushes
var graygrad = $(go.Brush, "Linear", { 0: "#F5F5F5", 1: "#F1F1F1" });
var bluegrad = $(go.Brush, "Linear", { 0: "#CDDAF0", 1: "#91ADDD" });
var yellowgrad = $(go.Brush, "Linear", { 0: "#FEC901", 1: "#FEA200" });
var lavgrad = $(go.Brush, "Linear", { 0: "#EF9EFA", 1: "#A570AD" });
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{ isShadowed: true },
// define the node's outer shape
$(go.Shape, "RoundedRectangle",
{ fill: graygrad, stroke: "#D8D8D8" }, // default fill is gray
new go.Binding("fill", "color")),
// define the node's text
$(go.TextBlock,
{ margin: 5, font: "bold 11px Helvetica, bold Arial, sans-serif" },
new go.Binding("text", "key"))
);
myDiagram.linkTemplate =
$(go.Link, // the whole link panel
{ selectable: false },
$(go.Shape)); // the link shape
// create the model for the double tree; could be eiher TreeModel or GraphLinksModel
myDiagram.model = new go.TreeModel([
{ key: "Root", color: lavgrad },
{ key: "Left1", parent: "Root", dir: "left", color: bluegrad },
{ key: "leaf1", parent: "Left1" },
{ key: "leaf2", parent: "Left1" },
{ key: "Left2", parent: "Left1", color: bluegrad },
{ key: "leaf3", parent: "Left2" },
{ key: "leaf4", parent: "Left2" },
{ key: "leaf5", parent: "Left1" },
{ key: "Right1", parent: "Root", dir: "right", color: yellowgrad },
{ key: "Right2", parent: "Right1", color: yellowgrad },
{ key: "leaf11", parent: "Right2" },
{ key: "leaf12", parent: "Right2" },
{ key: "leaf13", parent: "Right2" },
{ key: "leaf14", parent: "Right1" },
{ key: "leaf15", parent: "Right1" },
{ key: "Right3", parent: "Root", dir: "right", color: yellowgrad },
{ key: "leaf16", parent: "Right3" },
{ key: "leaf17", parent: "Right3" }
]);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="background-color: white; border: solid 1px black; width: 100%; height: 500px"></div>
<p>
This sample displays a diagram of two trees sharing a single root node growing in opposite directions.
The immediate child data of the ROOT node have a "dir" property
that describes the direction that subtree should grow.
</p>
<p>
The <a>Diagram.layout</a> is an instance of the <a>DoubleTreeLayout</a> extension layout,
defined in <a href="../extensions/DoubleTreeLayout.js">extensions/DoubleTreeLayout.js</a>.
The layout requires a <a>DoubleTreeLayout.directionFunction</a> predicate to decide for a child node
of the root node which way the subtree should grow.
</p>
</div>
</body>
</html>
+234
View File
@@ -0,0 +1,234 @@
<!DOCTYPE html>
<html>
<head>
<title>Dragging Fields Between Records</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Drag and drop items between nodes." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<script id="code">
// Custom DraggingTool for dragging fields instead of whole Parts.
// FieldDraggingTool.fieldTemplate needs to be set to a template of the field that you want shown while dragging.
function FieldDraggingTool() {
go.DraggingTool.call(this);
this.fieldTemplate = null; // THIS NEEDS TO BE SET before a drag starts
this.temporaryPart = null;
}
go.Diagram.inherit(FieldDraggingTool, go.DraggingTool);
// override this method
FieldDraggingTool.prototype.findDraggablePart = function() {
var diagram = this.diagram;
var obj = diagram.findObjectAt(diagram.lastInput.documentPoint);
while (obj !== null && obj.type !== go.Panel.TableRow) obj = obj.panel;
if (obj !== null && obj.type === go.Panel.TableRow &&
this.fieldTemplate !== null && this.temporaryPart === null) {
var tempPart =
go.GraphObject.make(go.Node, "Table",
{ layerName: "Tool", locationSpot: go.Spot.Center },
this.fieldTemplate.copy()); // copy the template!
this.temporaryPart = tempPart;
// assume OBJ is now a Panel representing a field, bound to field data
// update the temporary Part via data binding
tempPart.location = diagram.lastInput.documentPoint; // need to set location explicitly
diagram.add(tempPart); // add to Diagram before setting data
tempPart.data = obj.data; // bind to the same field data as being dragged
return tempPart;
}
return go.DraggingTool.prototype.findDraggablePart.call(this);
};
FieldDraggingTool.prototype.doActivate = function() {
if (this.temporaryPart === null) return go.DraggingTool.prototype.doActivate.call(this);
var diagram = this.diagram;
this.standardMouseSelect();
this.isActive = true;
// instead of the usual result of computeEffectiveCollection, just use the temporaryPart alone
var map = new go.Map(/*go.Part, go.DraggingInfo*/);
map.set(this.temporaryPart, new go.DraggingInfo(diagram.lastInput.documentPoint.copy()));
this.draggedParts = map;
this.startTransaction("Drag Field");
diagram.isMouseCaptured = true;
};
FieldDraggingTool.prototype.doDeactivate = function() {
if (this.temporaryPart === null) return go.DraggingTool.prototype.doDeactivate.call(this);
var diagram = this.diagram;
// make sure the temporary Part is no longer in the Diagram
diagram.remove(this.temporaryPart);
this.temporaryPart = null;
// now do all the standard deactivation cleanup,
// including setting isActive = false, clearing out draggedParts, calling stopTransaction(),
// and setting diagram.isMouseCaptured = false
go.DraggingTool.prototype.doDeactivate.call(this);
};
FieldDraggingTool.prototype.doMouseMove = function() {
if (!this.isActive) return;
if (this.temporaryPart === null) return go.DraggingTool.prototype.doMouseMove.call(this);
var diagram = this.diagram;
// just move the temporaryPart (in draggedParts), without regard to moving or copying permissions of the Node
var offset = diagram.lastInput.documentPoint.copy().subtract(diagram.firstInput.documentPoint);
this.moveParts(this.draggedParts, offset, false);
};
FieldDraggingTool.prototype.doMouseUp = function() {
if (!this.isActive) return;
if (this.temporaryPart === null) return go.DraggingTool.prototype.doMouseUp.call(this);
var diagram = this.diagram;
var data = this.temporaryPart.data;
var dest = diagram.findPartAt(diagram.lastInput.documentPoint, false);
if (dest !== null && dest.data && dest.data.fields) {
var panel = dest.findObject("TABLE");
var idx = panel.findRowForLocalY(panel.getLocalPoint(diagram.lastInput.documentPoint).y);
diagram.model.insertArrayItem(dest.data.fields, idx + 1,
{ name: data.name, info: data.info, color: data.color, figure: data.figure });
}
var src = this.currentPart;
// whether or not there was a destination node, delete the original field
if (!(diagram.lastInput.control || diagram.lastInput.meta)) {
var sidx = src.data.fields.indexOf(data);
if (sidx >= 0) {
diagram.model.removeArrayItem(src.data.fields, sidx);
}
}
this.transactionResult = "Inserted Field";
this.stopTool();
};
// end of FieldDraggingTool
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",
{
validCycle: go.Diagram.CycleNotDirected, // don't allow loops
draggingTool: $(FieldDraggingTool), // use custom DraggingTool
// automatically update the model that is shown on this page
"ModelChanged": function(e) { if (e.isTransactionFinished) showModel(); },
"undoManager.isEnabled": true
});
// This template is a Panel that is used to represent each item in a Panel.itemArray.
// The Panel is data bound to the item object.
// This template needs to be used by the FieldDraggingTool as well as the Diagram.nodeTemplate.
var fieldTemplate =
$(go.Panel, "TableRow", // this Panel is a row in the containing Table
new go.Binding("portId", "name"), // this Panel is a "port"
{
background: "transparent", // so this port's background can be picked by the mouse
fromSpot: go.Spot.Right, // links only go from the right side to the left side
toSpot: go.Spot.Left
}, // allow drawing links from or to this port
$(go.Shape,
{ width: 12, height: 12, column: 0, strokeWidth: 2, margin: 4 },
new go.Binding("figure", "figure"),
new go.Binding("fill", "color")),
$(go.TextBlock,
{ margin: new go.Margin(0, 2), column: 1, font: "bold 13px sans-serif" },
new go.Binding("text", "name")),
$(go.TextBlock,
{ margin: new go.Margin(0, 2), column: 2, font: "13px sans-serif" },
new go.Binding("text", "info"))
);
// the FieldDraggingTool needs a template for what to show while dragging
myDiagram.toolManager.draggingTool.fieldTemplate = fieldTemplate;
// This template represents a whole "record".
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{
movable: false,
copyable: false,
deletable: false,
locationSpot: go.Spot.Center
},
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
// this rectangular shape surrounds the content of the node
$(go.Shape,
{ fill: "#EEEEEE" }),
// the content consists of a header and a list of items
$(go.Panel, "Vertical",
// this is the header for the whole node
$(go.Panel, "Auto",
{ stretch: go.GraphObject.Horizontal }, // as wide as the whole node
$(go.Shape,
{ fill: "#1570A6", stroke: null }),
$(go.TextBlock,
{
alignment: go.Spot.Center,
margin: 3,
stroke: "white",
textAlign: "center",
font: "bold 12pt sans-serif"
},
new go.Binding("text", "title"))),
// this Panel holds a Panel for each item object in the itemArray;
// each item Panel is defined by the itemTemplate to be a TableRow in this Table
$(go.Panel, "Table",
{
name: "TABLE",
padding: 2,
minSize: new go.Size(100, 10),
defaultStretch: go.GraphObject.Horizontal,
itemTemplate: fieldTemplate
},
new go.Binding("itemArray", "fields")
) // end Table Panel of items
) // end Vertical Panel
); // end Node
myDiagram.model =
$(go.GraphLinksModel,
{
linkFromPortIdProperty: "fromPort",
linkToPortIdProperty: "toPort",
copiesArrays: true,
copiesArrayObjects: true,
nodeDataArray: [
{
key: 1,
title: "Record1",
fields: [
{ name: "field1", info: "", color: "#F7B84B", figure: "Ellipse" },
{ name: "field2", info: "the second one", color: "#F25022", figure: "Ellipse" },
{ name: "fieldThree", info: "3rd", color: "#00BCF2" }
],
loc: "0 0"
},
{
key: 2,
title: "Record2",
fields: [
{ name: "fieldA", info: "", color: "#FFB900", figure: "Diamond" }
],
loc: "250 0"
}
]
});
showModel(); // show the diagram's initial model
function showModel() {
document.getElementById("mySavedModel").textContent = myDiagram.model.toJson();
}
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:300px"></div>
<p>Drag fields between records to move them; dragging within the same node can reorder them.
Fields can be copied when holding down the control key; they are deleted when dropped in the diagram's background.
The "record" Nodes are not movable or copyable or deletable.</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>
+217
View File
@@ -0,0 +1,217 @@
<!DOCTYPE html>
<html>
<head>
<title>Dragging a Field from a Record onto an HTML Element</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Drag an item from a node out of the diagram and onto another HTML element." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<script id="code">
// Custom DraggingTool for dragging fields instead of whole Parts.
// FieldDraggingTool.fieldTemplate needs to be set to a template of the field that you want shown while dragging.
function FieldDraggingTool() {
go.DraggingTool.call(this);
this.fieldTemplate = null; // THIS NEEDS TO BE SET before a drag starts
this.temporaryPart = null;
}
go.Diagram.inherit(FieldDraggingTool, go.DraggingTool);
// override this method
FieldDraggingTool.prototype.findDraggablePart = function() {
var diagram = this.diagram;
var obj = diagram.findObjectAt(diagram.lastInput.documentPoint);
while (obj !== null && obj.type !== go.Panel.TableRow) obj = obj.panel;
if (obj !== null && obj.type === go.Panel.TableRow &&
this.fieldTemplate !== null && this.temporaryPart === null) {
var tempPart =
go.GraphObject.make(go.Node, "Table",
{ layerName: "Tool", locationSpot: go.Spot.Center },
this.fieldTemplate.copy()); // copy the template!
this.temporaryPart = tempPart;
// assume OBJ is now a Panel representing a field, bound to field data
// update the temporary Part via data binding
tempPart.location = diagram.lastInput.documentPoint; // need to set location explicitly
diagram.add(tempPart); // add to Diagram before setting data
tempPart.data = obj.data; // bind to the same field data as being dragged
return tempPart;
}
return go.DraggingTool.prototype.findDraggablePart.call(this);
};
FieldDraggingTool.prototype.doActivate = function() {
if (this.temporaryPart === null) return go.DraggingTool.prototype.doActivate.call(this);
var diagram = this.diagram;
this.standardMouseSelect();
this.isActive = true;
// instead of the usual result of computeEffectiveCollection, just use the temporaryPart alone
var map = new go.Map(/*go.Part, go.DraggingInfo*/);
map.set(this.temporaryPart, new go.DraggingInfo(diagram.lastInput.documentPoint.copy()));
this.draggedParts = map;
this.startTransaction("Drag Field");
diagram.isMouseCaptured = true;
};
FieldDraggingTool.prototype.doDeactivate = function() {
if (this.temporaryPart === null) return go.DraggingTool.prototype.doDeactivate.call(this);
var diagram = this.diagram;
// make sure the temporary Part is no longer in the Diagram
diagram.remove(this.temporaryPart);
this.temporaryPart = null;
// now do all the standard deactivation cleanup,
// including setting isActive = false, clearing out draggedParts, calling stopTransaction(),
// and setting diagram.isMouseCaptured = false
go.DraggingTool.prototype.doDeactivate.call(this);
};
FieldDraggingTool.prototype.doMouseMove = function() {
if (!this.isActive) return;
if (this.temporaryPart === null) return go.DraggingTool.prototype.doMouseMove.call(this);
var diagram = this.diagram;
// just move the temporaryPart (in draggedParts), without regard to moving or copying permissions of the Node
var offset = diagram.lastInput.documentPoint.copy().subtract(diagram.firstInput.documentPoint);
this.moveParts(this.draggedParts, offset, false);
};
FieldDraggingTool.prototype.doMouseUp = function() {
if (!this.isActive) return;
if (this.temporaryPart === null) return go.DraggingTool.prototype.doMouseUp.call(this);
var diagram = this.diagram;
var data = this.temporaryPart.data;
var input = diagram.lastInput;
var id = input.event.target.id;
if (input.isTouchEvent) {
// Touch events always target the first object touched, we want the last.
// Determine if you are using Touch or Pointer:
var evt = input.event.changedTouches ? input.event.changedTouches[0] : input.event;
id = document.elementFromPoint(evt.clientX, evt.clientY).id;
}
if (input.event && id === "myDroppedFields") {
document.getElementById("myDroppedFields").textContent += data.name + " (" + data.info + ")\n";
}
this.transactionResult = "Dragged Field";
this.stopTool();
};
// end of FieldDraggingTool
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",
{
validCycle: go.Diagram.CycleNotDirected, // don't allow loops
draggingTool: $(FieldDraggingTool), // use custom DraggingTool
"undoManager.isEnabled": true
});
// This template is a Panel that is used to represent each item in a Panel.itemArray.
// The Panel is data bound to the item object.
var fieldTemplate =
$(go.Panel, "TableRow", // this Panel is a row in the containing Table
new go.Binding("portId", "name"), // this Panel is a "port"
{
background: "transparent", // so this port's background can be picked by the mouse
fromSpot: go.Spot.Right, // links only go from the right side to the left side
toSpot: go.Spot.Left
}, // allow drawing links from or to this port
$(go.Shape,
{ width: 12, height: 12, column: 0, strokeWidth: 2, margin: 4 },
new go.Binding("figure", "figure"),
new go.Binding("fill", "color")),
$(go.TextBlock,
{ margin: new go.Margin(0, 2), column: 1, font: "bold 13px sans-serif" },
new go.Binding("text", "name")),
$(go.TextBlock,
{ margin: new go.Margin(0, 2), column: 2, font: "13px sans-serif" },
new go.Binding("text", "info"))
);
// the FieldDraggingTool needs a template for what to show while dragging
myDiagram.toolManager.draggingTool.fieldTemplate = fieldTemplate;
// This template represents a whole "record".
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{
movable: false,
copyable: false,
deletable: false
},
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
// this rectangular shape surrounds the content of the node
$(go.Shape,
{ fill: "#EEEEEE" }),
// the content consists of a header and a list of items
$(go.Panel, "Vertical",
// this is the header for the whole node
$(go.Panel, "Auto",
{ stretch: go.GraphObject.Horizontal }, // as wide as the whole node
$(go.Shape,
{ fill: "#1570A6", stroke: null }),
$(go.TextBlock,
{
alignment: go.Spot.Center,
margin: 3,
stroke: "white",
textAlign: "center",
font: "bold 12pt sans-serif"
},
new go.Binding("text", "key"))),
// this Panel holds a Panel for each item object in the itemArray;
// each item Panel is defined by the itemTemplate to be a TableRow in this Table
$(go.Panel, "Table",
{
name: "TABLE",
padding: 2,
minSize: new go.Size(100, 10),
defaultStretch: go.GraphObject.Horizontal,
itemTemplate: fieldTemplate
},
new go.Binding("itemArray", "fields")
) // end Table Panel of items
) // end Vertical Panel
); // end Node
myDiagram.model =
$(go.GraphLinksModel,
{
linkFromPortIdProperty: "fromPort",
linkToPortIdProperty: "toPort",
copiesArrays: true,
copiesArrayObjects: true,
nodeDataArray: [
{
key: "Record1",
fields: [
{ name: "field1", info: "", color: "#F7B84B", figure: "Ellipse" },
{ name: "field2", info: "the second one", color: "#F25022", figure: "Ellipse" },
{ name: "fieldThree", info: "3rd", color: "#00BCF2" }
],
loc: "0 0"
},
{
key: "Record2",
fields: [
{ name: "fieldA", info: "", color: "#FFB900", figure: "Diamond" }
],
loc: "250 0"
}
]
});
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:300px"></div>
<p>Drag a field from one of the record nodes and drop onto the PRE element below.
The "record" Nodes are not movable or copyable or deletable.</p>
<p>Here you can drop a field from one of the records above:</p>
<pre id="myDroppedFields" style="width:200px;height:300px;border:dashed"></pre>
</div>
</body>
</html>
+135
View File
@@ -0,0 +1,135 @@
<!DOCTYPE html>
<html>
<head>
<title>Drag Unoccupied</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Limit the dragging of nodes to avoid any overlap with other nodes.">
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
// R is a Rect in document coordinates
// NODE is the Node being moved -- ignore when looking for Parts intersecting the Rect
function isUnoccupied(r, node) {
var diagram = node.diagram;
// nested function used by Layer.findObjectsIn, below
// only consider Parts, and ignore the given Node, any Links, and Group members
function navig(obj) {
var part = obj.part;
if (part === node) return null;
if (part instanceof go.Link) return null;
if (part.isMemberOf(node)) return null;
if (node.isMemberOf(part)) return null;
return part;
}
// only consider non-temporary Layers
var lit = diagram.layers;
while (lit.next()) {
var lay = lit.value;
if (lay.isTemporary) continue;
if (lay.findObjectsIn(r, navig, null, true).count > 0) return false;
}
return true;
}
// a Part.dragComputation function that prevents a Part from being dragged to overlap another Part
// use PT instead of GRIDPT if DraggingTool.isGridSnapEnabled but movement should not snap to grid
function avoidNodeOverlap(node, pt, gridpt) {
if (node.diagram instanceof go.Palette) return gridpt;
// this assumes each node is fully rectangular
var bnds = node.actualBounds;
var loc = node.location;
// use PT instead of GRIDPT if you want to ignore any grid snapping behavior
// see if the area at the proposed location is unoccupied
var r = new go.Rect(gridpt.x - (loc.x - bnds.x), gridpt.y - (loc.y - bnds.y), bnds.width, bnds.height);
// maybe inflate R if you want some space between the node and any other nodes
r.inflate(-0.5, -0.5); // by default, deflate to avoid edge overlaps with "exact" fits
// when dragging a node from another Diagram, choose an unoccupied area
if (!(node.diagram.currentTool instanceof go.DraggingTool) &&
(!node._temp || !node.layer.isTemporary)) { // in Temporary Layer during external drag-and-drop
node._temp = true; // flag to avoid repeated searches during external drag-and-drop
while (!isUnoccupied(r, node)) {
r.x += 10; // note that this is an unimaginative search algorithm --
r.y += 2; // you can improve the search here to be more appropriate for your app
}
r.inflate(0.5, 0.5); // restore to actual size
// return the proposed new location point
return new go.Point(r.x - (loc.x - bnds.x), r.y - (loc.y - bnds.y));
}
if (isUnoccupied(r, node)) return gridpt; // OK
return loc; // give up -- don't allow the node to be moved to the new location
}
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
"animationManager.isEnabled": false,
"undoManager.isEnabled": true
});
// Define the template for Nodes, just some text inside a colored rectangle
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{ dragComputation: avoidNodeOverlap },
{ minSize: new go.Size(50, 20), resizable: true },
new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
new go.Binding("position", "pos", go.Point.parse).makeTwoWay(go.Point.stringify),
// temporarily put selected nodes in Foreground layer
new go.Binding("layerName", "isSelected", function(s) { return s ? "Foreground" : ""; }).ofObject(),
$(go.Shape, "Rectangle",
new go.Binding("fill", "color")),
$(go.TextBlock,
new go.Binding("text", "color"))
);
myDiagram.model = new go.GraphLinksModel([
{ pos: "-30 0", size: "50 300", color: go.Brush.randomColor() },
{ pos: "120 20", size: "300 50", color: go.Brush.randomColor() },
{ pos: "100 200", size: "300 50", color: go.Brush.randomColor() },
{ pos: "500 50", size: "50 300", color: go.Brush.randomColor() },
{ key: 1, pos: "100 100", size: "50 50", color: "gray" },
{ key: 2, pos: "200 140", size: "50 50", color: "gray" }
]);
myDiagram.findNodeForKey(1).isSelected = true;
// initialize the Palette that is on the left side of the page
myPalette =
$(go.Palette, "myPaletteDiv", // must name or refer to the DIV HTML element
{
nodeTemplateMap: myDiagram.nodeTemplateMap, // share the templates used by myDiagram
model: new go.GraphLinksModel([ // specify the contents of the Palette
{ size: "50 50", color: go.Brush.randomColor() },
{ size: "60 40", color: go.Brush.randomColor() }
])
});
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div style="width: 100%; display: flex; justify-content: space-between">
<div id="myPaletteDiv" style="width: 100px; background-color: floralwhite; border: solid 1px black; margin-right: 2px"></div>
<div id="myDiagramDiv" style="background-color: white; border: solid 1px black; width: 100%; height: 400px"></div>
</div>
<p>
Drag a node around.
Notice how you cannot force the dragged node to overlap any other (stationary) node.
If you drag more than one node, notice how the relative positions of the dragged nodes are maintained
except when forced to be shifted in order to avoid overlapping other nodes.
</p>
<p>
This functionality is implemented by a custom <a>Part.dragComputation</a> property function,
which affects how the <a>DraggingTool</a> can move selected nodes.
You will want to adjust how it finds an empty spot for the dragged node when dragging from another Diagram.
</p>
</div>
</body>
</html>
+313
View File
@@ -0,0 +1,313 @@
<!DOCTYPE html>
<html>
<head>
<title>Draggable Link</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Drag a link to reconnect it. Nodes have custom Adornments for selection, resizing, and rotating. The Palette includes links." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../extensions/Figures.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
myDiagram =
$(go.Diagram, "myDiagramDiv", // must name or refer to the DIV HTML element
{
grid: $(go.Panel, "Grid",
$(go.Shape, "LineH", { stroke: "lightgray", strokeWidth: 0.5 }),
$(go.Shape, "LineH", { stroke: "gray", strokeWidth: 0.5, interval: 10 }),
$(go.Shape, "LineV", { stroke: "lightgray", strokeWidth: 0.5 }),
$(go.Shape, "LineV", { stroke: "gray", strokeWidth: 0.5, interval: 10 })
),
"draggingTool.dragsLink": true,
"draggingTool.isGridSnapEnabled": true,
"linkingTool.isUnconnectedLinkValid": true,
"linkingTool.portGravity": 20,
"relinkingTool.isUnconnectedLinkValid": true,
"relinkingTool.portGravity": 20,
"relinkingTool.fromHandleArchetype":
$(go.Shape, "Diamond", { segmentIndex: 0, cursor: "pointer", desiredSize: new go.Size(8, 8), fill: "tomato", stroke: "darkred" }),
"relinkingTool.toHandleArchetype":
$(go.Shape, "Diamond", { segmentIndex: -1, cursor: "pointer", desiredSize: new go.Size(8, 8), fill: "darkred", stroke: "tomato" }),
"linkReshapingTool.handleArchetype":
$(go.Shape, "Diamond", { desiredSize: new go.Size(7, 7), fill: "lightblue", stroke: "deepskyblue" }),
"rotatingTool.handleAngle": 270,
"rotatingTool.handleDistance": 30,
"rotatingTool.snapAngleMultiple": 15,
"rotatingTool.snapAngleEpsilon": 15,
"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);
}
});
// Define a function for creating a "port" that is normally transparent.
// The "name" is used as the GraphObject.portId, the "spot" is used to control how links connect
// and where the port is positioned on the node, and the boolean "output" and "input" arguments
// control whether the user can draw links from or to the port.
function makePort(name, spot, output, input) {
// the port is basically just a small transparent square
return $(go.Shape, "Circle",
{
fill: null, // not seen, by default; set to a translucent gray by showSmallPorts, defined below
stroke: null,
desiredSize: new go.Size(7, 7),
alignment: spot, // align the port on the main Shape
alignmentFocus: spot, // just inside the Shape
portId: name, // declare this object to be a "port"
fromSpot: spot, toSpot: spot, // declare where links may connect at this port
fromLinkable: output, toLinkable: input, // declare whether the user may draw links to/from here
cursor: "pointer" // show a different cursor to indicate potential link point
});
}
var nodeSelectionAdornmentTemplate =
$(go.Adornment, "Auto",
$(go.Shape, { fill: null, stroke: "deepskyblue", strokeWidth: 1.5, strokeDashArray: [4, 2] }),
$(go.Placeholder)
);
var nodeResizeAdornmentTemplate =
$(go.Adornment, "Spot",
{ locationSpot: go.Spot.Right },
$(go.Placeholder),
$(go.Shape, { alignment: go.Spot.TopLeft, cursor: "nw-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
$(go.Shape, { alignment: go.Spot.Top, cursor: "n-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
$(go.Shape, { alignment: go.Spot.TopRight, cursor: "ne-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
$(go.Shape, { alignment: go.Spot.Left, cursor: "w-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
$(go.Shape, { alignment: go.Spot.Right, cursor: "e-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
$(go.Shape, { alignment: go.Spot.BottomLeft, cursor: "se-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
$(go.Shape, { alignment: go.Spot.Bottom, cursor: "s-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" }),
$(go.Shape, { alignment: go.Spot.BottomRight, cursor: "sw-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue" })
);
var nodeRotateAdornmentTemplate =
$(go.Adornment,
{ locationSpot: go.Spot.Center, locationObjectName: "CIRCLE" },
$(go.Shape, "Circle", { name: "CIRCLE", cursor: "pointer", desiredSize: new go.Size(7, 7), fill: "lightblue", stroke: "deepskyblue" }),
$(go.Shape, { geometryString: "M3.5 7 L3.5 30", isGeometryPositioned: true, stroke: "deepskyblue", strokeWidth: 1.5, strokeDashArray: [4, 2] })
);
myDiagram.nodeTemplate =
$(go.Node, "Spot",
{ locationSpot: go.Spot.Center },
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
{ selectable: true, selectionAdornmentTemplate: nodeSelectionAdornmentTemplate },
{ resizable: true, resizeObjectName: "PANEL", resizeAdornmentTemplate: nodeResizeAdornmentTemplate },
{ rotatable: true, rotateAdornmentTemplate: nodeRotateAdornmentTemplate },
new go.Binding("angle").makeTwoWay(),
// the main object is a Panel that surrounds a TextBlock with a Shape
$(go.Panel, "Auto",
{ name: "PANEL" },
new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
$(go.Shape, "Rectangle", // default figure
{
portId: "", // the default port: if no spot on link data, use closest side
fromLinkable: true, toLinkable: true, cursor: "pointer",
fill: "white", // default color
strokeWidth: 2
},
new go.Binding("figure"),
new go.Binding("fill")),
$(go.TextBlock,
{
font: "bold 11pt Helvetica, Arial, sans-serif",
margin: 8,
maxSize: new go.Size(160, NaN),
wrap: go.TextBlock.WrapFit,
editable: true
},
new go.Binding("text").makeTwoWay())
),
// four small named ports, one on each side:
makePort("T", go.Spot.Top, false, true),
makePort("L", go.Spot.Left, true, true),
makePort("R", go.Spot.Right, true, true),
makePort("B", go.Spot.Bottom, true, false),
{ // handle mouse enter/leave events to show/hide the ports
mouseEnter: function(e, node) { showSmallPorts(node, true); },
mouseLeave: function(e, node) { showSmallPorts(node, false); }
}
);
function showSmallPorts(node, show) {
node.ports.each(function(port) {
if (port.portId !== "") { // don't change the default port, which is the big shape
port.fill = show ? "rgba(0,0,0,.3)" : null;
}
});
}
var linkSelectionAdornmentTemplate =
$(go.Adornment, "Link",
$(go.Shape,
// isPanelMain declares that this Shape shares the Link.geometry
{ isPanelMain: true, fill: null, stroke: "deepskyblue", strokeWidth: 0 }) // use selection object's strokeWidth
);
myDiagram.linkTemplate =
$(go.Link, // the whole link panel
{ selectable: true, selectionAdornmentTemplate: linkSelectionAdornmentTemplate },
{ relinkableFrom: true, relinkableTo: true, reshapable: true },
{
routing: go.Link.AvoidsNodes,
curve: go.Link.JumpOver,
corner: 5,
toShortLength: 4
},
new go.Binding("points").makeTwoWay(),
$(go.Shape, // the link path shape
{ isPanelMain: true, strokeWidth: 2 }),
$(go.Shape, // the arrowhead
{ toArrow: "Standard", stroke: null }),
$(go.Panel, "Auto",
new go.Binding("visible", "isSelected").ofObject(),
$(go.Shape, "RoundedRectangle", // the link shape
{ fill: "#F8F8F8", stroke: null }),
$(go.TextBlock,
{
textAlign: "center",
font: "10pt helvetica, arial, sans-serif",
stroke: "#919191",
margin: 2,
minSize: new go.Size(10, NaN),
editable: true
},
new go.Binding("text").makeTwoWay())
)
);
load(); // load an initial diagram from some JSON text
// initialize the Palette that is on the left side of the page
myPalette =
$(go.Palette, "myPaletteDiv", // must name or refer to the DIV HTML element
{
maxSelectionCount: 1,
nodeTemplateMap: myDiagram.nodeTemplateMap, // share the templates used by myDiagram
linkTemplate: // simplify the link template, just in this Palette
$(go.Link,
{ // because the GridLayout.alignment is Location and the nodes have locationSpot == Spot.Center,
// to line up the Link in the same manner we have to pretend the Link has the same location spot
locationSpot: go.Spot.Center,
selectionAdornmentTemplate:
$(go.Adornment, "Link",
{ locationSpot: go.Spot.Center },
$(go.Shape,
{ isPanelMain: true, fill: null, stroke: "deepskyblue", strokeWidth: 0 }),
$(go.Shape, // the arrowhead
{ toArrow: "Standard", stroke: null })
)
},
{
routing: go.Link.AvoidsNodes,
curve: go.Link.JumpOver,
corner: 5,
toShortLength: 4
},
new go.Binding("points"),
$(go.Shape, // the link path shape
{ isPanelMain: true, strokeWidth: 2 }),
$(go.Shape, // the arrowhead
{ toArrow: "Standard", stroke: null })
),
model: new go.GraphLinksModel([ // specify the contents of the Palette
{ text: "Start", figure: "Circle", fill: "#00AD5F" },
{ text: "Step" },
{ text: "DB", figure: "Database", fill: "lightgray" },
{ text: "???", figure: "Diamond", fill: "lightskyblue" },
{ text: "End", figure: "Circle", fill: "#CE0620" },
{ text: "Comment", figure: "RoundedRectangle", fill: "lightyellow" }
], [
// the Palette also has a disconnected Link, which the user can drag-and-drop
{ points: new go.List(/*go.Point*/).addAll([new go.Point(0, 0), new go.Point(30, 0), new go.Point(30, 40), new go.Point(60, 40)]) }
])
});
}
// Show the diagram's model in JSON format that the user may edit
function save() {
saveDiagramProperties(); // do this first, before writing to JSON
document.getElementById("mySavedModel").value = myDiagram.model.toJson();
myDiagram.isModified = false;
}
function load() {
myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
loadDiagramProperties(); // do this after the Model.modelData has been brought into memory
}
function saveDiagramProperties() {
myDiagram.model.modelData.position = go.Point.stringify(myDiagram.position);
}
function loadDiagramProperties(e) {
// set Diagram.initialPosition, not Diagram.position, to handle initialization side-effects
var pos = myDiagram.model.modelData.position;
if (pos) myDiagram.initialPosition = go.Point.parse(pos);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div style="width: 100%; display: flex; justify-content: space-between">
<div id="myPaletteDiv" style="width: 105px; margin-right: 2px; background-color: whitesmoke; border: solid 1px black"></div>
<div id="myDiagramDiv" style="flex-grow: 1; height: 620px; border: solid 1px black"></div>
</div>
<p>
This sample demonstrates the ability for the user to drag around a Link as if it were a Node.
When either end of the link passes over a valid port, the port is highlighted.
</p>
<p>
The link-dragging functionality is enabled by setting some or all of the following properties:
<a>DraggingTool.dragsLink</a>, <a>LinkingTool.isUnconnectedLinkValid</a>, and
<a>RelinkingTool.isUnconnectedLinkValid</a>.
</p>
<p>
Note that a Link is present in the <a>Palette</a> so that it too can be dragged out and onto
the main Diagram. Because links are not automatically routed when either end is not connected
with a Node, the route is provided explicitly when that Palette item is defined.
</p>
<p>
This also demonstrates several custom Adornments:
<a>Part.selectionAdornmentTemplate</a>, <a>Part.resizeAdornmentTemplate</a>, and
<a>Part.rotateAdornmentTemplate</a>.
</p>
<p>
Finally this sample demonstrates saving and restoring the <a>Diagram.position</a> as a property
on the <a>Model.modelData</a> object that is automatically saved and restored when calling <a>Model.toJson</a>
and <a>Model,fromJson</a>.
</p>
<div>
<div>
<button id="SaveButton" onclick="save()">Save</button>
<button onclick="load()">Load</button>
Diagram Model saved in JSON format:
</div>
<textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "go.GraphLinksModel",
"linkFromPortIdProperty": "fromPort",
"linkToPortIdProperty": "toPort",
"nodeDataArray": [
],
"linkDataArray": [
]}
</textarea>
</div>
</div>
</body>
</html>
+337
View File
@@ -0,0 +1,337 @@
<!DOCTYPE html>
<html>
<head>
<title>Movable, Copyable, Deletable Ports</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Nodes with selectable, movable, copyable, and deletable ports." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
"undoManager.isEnabled": true,
// don't allow links within a group
"linkingTool.linkValidation": differentGroups,
"relinkingTool.linkValidation": differentGroups,
mouseDrop: function(e) {
// when the selection is dropped in the diagram's background,
// and it includes any "port"s, cancel the drop
if (myDiagram.selection.any(selectionIncludesPorts)) {
myDiagram.currentTool.doCancel();
}
}
});
function differentGroups(fromnode, fromport, tonode, toport) {
return fromnode.containingGroup !== tonode.containingGroup;
}
function selectionIncludesPorts(n) {
return n.containingGroup !== null && !myDiagram.selection.has(n.containingGroup);
}
var UnselectedBrush = "lightgray"; // item appearance, if not "selected"
var SelectedBrush = "dodgerblue"; // item appearance, if "selected"
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{ selectionAdorned: false },
{
mouseDrop: function(e, n) {
// when the selection is entirely ports and is dropped onto a Group, transfer membership
if (n.containingGroup !== null && myDiagram.selection.all(selectionIncludesPorts)) {
myDiagram.selection.each(function(p) { p.containingGroup = n.containingGroup; });
} else {
myDiagram.currentTool.doCancel();
}
}
},
$(go.Shape,
{
name: "SHAPE",
fill: UnselectedBrush, stroke: "gray",
geometryString: "F1 m 0,0 l 5,0 1,4 -1,4 -5,0 1,-4 -1,-4 z",
spot1: new go.Spot(0, 0, 5, 1), // keep the text inside the shape
spot2: new go.Spot(1, 1, -5, 0),
// some port-related properties
portId: "",
toSpot: go.Spot.Left,
toLinkable: false,
fromSpot: go.Spot.Right,
fromLinkable: false,
cursor: "pointer"
},
new go.Binding("fill", "isSelected", function(s) { return s ? SelectedBrush : UnselectedBrush; }).ofObject(),
new go.Binding("toLinkable", "_in"),
new go.Binding("fromLinkable", "_in", function(b) { return !b; })),
$(go.TextBlock,
new go.Binding("text", "name"))
);
myDiagram.groupTemplate =
$(go.Group, "Auto",
{
selectionAdorned: false,
locationSpot: go.Spot.Center, locationObjectName: "ICON"
},
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
{
mouseDrop: function(e, g) {
// when the selection is entirely ports and is dropped onto a Group, transfer membership
if (myDiagram.selection.all(selectionIncludesPorts)) {
myDiagram.selection.each(function(p) { p.containingGroup = g; });
} else {
myDiagram.currentTool.doCancel();
}
},
layout: new InputOutputGroupLayout()
},
$(go.Shape, "RoundedRectangle",
{ stroke: "gray", strokeWidth: 2, fill: "transparent" },
new go.Binding("stroke", "isSelected", function(b) { return b ? SelectedBrush : UnselectedBrush; }).ofObject()),
$(go.Panel, "Vertical",
{ margin: 6 },
$(go.TextBlock,
new go.Binding("text", "name"),
{ alignment: go.Spot.Left }),
$(go.Panel, "Spot",
{ name: "ICON", height: 60 }, // an initial height; size will be set by InputOutputGroupLayout
$(go.Shape,
{ fill: null, stroke: null, stretch: go.GraphObject.Fill }),
$(go.Picture, "images/60x90.png",
{ width: 30, height: 45 })
)
)
);
myDiagram.linkTemplate =
$(go.Link,
{ routing: go.Link.Orthogonal, corner: 10, toShortLength: -3 },
{ relinkableFrom: true, relinkableTo: true },
$(go.Shape, { stroke: "gray", strokeWidth: 2.5 })
);
load(); // initialize myDiagram's model from the TextArea
}
function findPortNode(g, name, input) {
for (var it = g.memberParts; it.next();) {
var n = it.value;
if (!(n instanceof go.Node)) continue;
if (n.data.name === name && n.data._in === input) return n;
}
return null;
}
// Transform the given data to the data structures needed internally.
// For each data object in the "ins" Array of the node data, add a "port" Node to the Group.
// For each data object in the "outs" Array, add a "port" Node to the Group.
// For each link data, convert the "from" and "fromPort" information to the actual "port" Node,
// and then the same for "to" and "toPort".
// The internal model uses property names starting with "_" to avoid having Model.toJson() write them out.
function setupDiagram(nodes, links) {
var model = new go.GraphLinksModel();
model.linkFromKeyProperty = "_f";
model.linkToKeyProperty = "_t";
model.nodeIsGroupProperty = "_isg";
model.nodeGroupKeyProperty = "_g";
// first create all of the nodes, implemented as Groups
for (var i = 0; i < nodes.length; i++) {
var nodedata = nodes[i];
nodedata._isg = true;
}
model.addNodeDataCollection(nodes);
// now each node data will have a unique key, if not already specified
// then create all of the ports, implemented as Nodes that are members of those Groups
for (var i = 0; i < nodes.length; i++) {
var nodedata = nodes[i];
if (Array.isArray(nodedata.ins)) {
for (var j = 0; j < nodedata.ins.length; j++) {
var portdata = nodedata.ins[j];
portdata._in = true;
portdata._g = nodedata.key;
}
model.addNodeDataCollection(nodedata.ins);
nodedata.ins = undefined;
}
if (Array.isArray(nodedata.outs)) {
for (var j = 0; j < nodedata.outs.length; j++) {
var portdata = nodedata.outs[j];
portdata._in = false;
portdata._g = nodedata.key;
}
model.addNodeDataCollection(nodedata.outs);
nodedata.outs = undefined;
}
}
myDiagram.model = model;
// now Groups and Nodes exist, so can find the Node corresponding to a node's port
// finally process all of the links, to account for ports actually being member nodes
for (var i = 0; i < links.length; i++) {
var linkdata = links[i];
var fromNode = myDiagram.findNodeForKey(linkdata.from);
var toNode = myDiagram.findNodeForKey(linkdata.to);
if (fromNode !== null && toNode !== null) {
// look in the Group for a "port" Node with the right name and directionality
var fromPortNode = findPortNode(fromNode, linkdata.fromPort, false);
var toPortNode = findPortNode(toNode, linkdata.toPort, true);
if (fromPortNode !== null && toPortNode !== null) {
linkdata._f = fromPortNode.data.key;
linkdata._t = toPortNode.data.key;
linkdata.from = linkdata.fromPort = linkdata.to = linkdata.toPort = undefined;
}
}
}
model.addLinkDataCollection(links);
}
function save() {
// can't just call myDiagram.model.toJson() -- need to transform to external format
var m = new go.GraphLinksModel();
m.linkFromPortIdProperty = "fromPort";
m.linkToPortIdProperty = "toPort";
var arr = myDiagram.model.nodeDataArray;
myDiagram.nodes.each(function(g) {
if (g instanceof go.Group) {
g.data.ins = undefined;
g.data.outs = undefined;
m.addNodeData(g.data);
}
});
myDiagram.nodes.each(function(n) {
if (!(n instanceof go.Group)) {
var gd = n.containingGroup.data;
var a = n.data._in ? gd.ins : gd.outs;
if (!a) {
a = [];
if (n.data._in) gd.ins = a; else gd.outs = a;
}
a.push(n.data);
}
});
myDiagram.links.each(function(l) {
l.data.from = l.fromNode.containingGroup.data.key;
l.data.fromPort = l.fromNode.data.name;
l.data.to = l.toNode.containingGroup.data.key;
l.data.toPort = l.toNode.data.name;
m.addLinkData(l.data);
});
document.getElementById("mySavedModel").value = m.toJson();
myDiagram.isModified = false;
}
function load() {
var m = go.Model.fromJson(document.getElementById("mySavedModel").value);
setupDiagram(m.nodeDataArray, m.linkDataArray);
}
// The Group.layout, for arranging the "port" Nodes within the Group
function InputOutputGroupLayout() {
go.Layout.call(this);
}
go.Diagram.inherit(InputOutputGroupLayout, go.Layout);
InputOutputGroupLayout.prototype.doLayout = function(coll) {
coll = this.collectParts(coll);
var portSpacing = 2;
var iconAreaWidth = 60;
// compute the counts and areas of the inputs and the outputs
var left = 0;
var leftwidth = 0; // max
var leftheight = 0; // total
var right = 0;
var rightwidth = 0; // max
var rightheight = 0; // total
coll.each(function(n) {
if (n instanceof go.Link) return; // ignore Links
if (n.data._in) {
left++;
leftwidth = Math.max(leftwidth, n.actualBounds.width);
leftheight += n.actualBounds.height;
} else {
right++;
rightwidth = Math.max(rightwidth, n.actualBounds.width);
rightheight += n.actualBounds.height;
}
});
if (left > 0) leftheight += portSpacing * (left - 1);
if (right > 0) rightheight += portSpacing * (right - 1);
var loc = new go.Point(0, 0);
if (this.group !== null && this.group.location.isReal()) loc = this.group.location;
// first lay out the left side, the inputs
var y = loc.y - leftheight / 2;
coll.each(function(n) {
if (n instanceof go.Link) return; // ignore Links
if (!n.data._in) return; // ignore outputs
n.position = new go.Point(loc.x - iconAreaWidth / 2 - leftwidth, y);
y += n.actualBounds.height + portSpacing;
});
// now the right side, the outputs
y = loc.y - rightheight / 2;
coll.each(function(n) {
if (n instanceof go.Link) return; // ignore Links
if (n.data._in) return; // ignore inputs
n.position = new go.Point(loc.x + iconAreaWidth / 2 + rightwidth - n.actualBounds.width, y);
y += n.actualBounds.height + portSpacing;
});
// then position the group and size its icon area
if (this.group !== null) {
// position the group so that its ICON is in the middle, between the "ports"
this.group.location = loc;
// size the ICON so that it's wide enough to overlap the "ports" and tall enough to hold all of the "ports"
var icon = this.group.findObject("ICON");
if (icon !== null) icon.desiredSize = new go.Size(iconAreaWidth + leftwidth / 2 + rightwidth / 2, Math.max(leftheight, rightheight) + 10);
}
};
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
<p>
To allow ports to be selected, dragged, copied, and deleted, they are implemented as Nodes.
That means the nodes have to be implemented as Groups.
The user can delete selected ports.
The user cannot drop a port onto the background, but only onto a node.
</p>
<p>
There is a custom Layout used by such Group nodes, <code>InputOutputGroupLayout</code>,
to line up the input ports on the left side and the output ports on the right side.
</p>
<button id="SaveButton" onclick="save()">Save</button>
<button onclick="load()">Load</button>
The transformed model data (not the actual myDiagram.model):
<textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "go.GraphLinksModel",
"linkFromPortIdProperty": "fromPort",
"linkToPortIdProperty": "toPort",
"nodeDataArray": [
{"key":1, "name":"Server", "ins":[ {"name":"s1", "key":-3},{"name":"s2", "key":-4} ], "outs":[ {"name":"o1", "key":-5} ], "loc":"-80 -80"},
{"key":2, "name":"Other", "ins":[ {"name":"s1", "key":-6},{"name":"s2", "key":-7} ], "outs":[ {"name":"o1", "key":-8} ], "loc":"80 80"}
],
"linkDataArray": [
{"from":1, "fromPort":"o1", "to":2, "toPort":"s2"}
]
}
</textarea>
</div>
</body>
</html>
+344
View File
@@ -0,0 +1,344 @@
<!DOCTYPE html>
<html>
<head>
<title>Dynamic Pie Chart</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A GoJS pie chart that updates dynamically as counts change." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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;
var pieRadius = 100;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
"textEditingTool.starting": go.TextEditingTool.SingleClick,
"ModelChanged": onModelChanged,
"undoManager.isEnabled": true
}
);
// When a count changes in our model, ensure we trigger a redrawing of each slice in the pie
function onModelChanged(e) {
if (e.change === go.ChangedEvent.Property && e.propertyName === "count") {
var slicedata = e.object;
var nodedata = findNodeDataForSlice(slicedata);
if (nodedata) {
// Update the count binding to force makeGeo/positionSlice
myDiagram.model.updateTargetBindings(nodedata, "count");
// If the count went to 0, hide the slice
var sliceindex = nodedata.slices.indexOf(slicedata);
var slice = myDiagram.findNodeForKey(nodedata.key).findObject("PIE").elt(sliceindex);
var sliceshape = slice.findObject("SLICE");
if (slicedata.count === 0)
sliceshape.visible = false;
else
sliceshape.visible = true;
}
}
}
var sliceTemplate =
$(go.Panel,
$(go.Shape,
{
name: "SLICE",
strokeWidth: 2, stroke: "transparent",
isGeometryPositioned: true
},
new go.Binding("fill", "color"),
new go.Binding("geometry", "", makeGeo)
),
new go.Binding("position", "", positionSlice),
{ // Allow the user to "select" slices when clicking them
click: function(e, slice) {
var sliceShape = slice.findObject("SLICE");
var oldskips = slice.diagram.skipsUndoManager;
slice.diagram.skipsUndoManager = true;
if (sliceShape.stroke === "transparent") {
sliceShape.stroke = go.Brush.darkenBy(slice.data.color, 0.4);
// Move the slice out from the pie when selected
var nodedata = findNodeDataForSlice(slice.data);
if (nodedata) {
var sliceindex = nodedata.slices.indexOf(slice.data);
var angles = getAngles(nodedata, sliceindex);
if (angles.sweep !== 360) {
var angle = angles.start + angles.sweep / 2;
var offsetPoint = new go.Point(pieRadius / 10, 0);
slice.position = offsetPoint.rotate(angle).offset(pieRadius / 10, pieRadius / 10);
}
}
} else {
sliceShape.stroke = "transparent";
slice.position = new go.Point(pieRadius / 10, pieRadius / 10);
}
slice.diagram.skipsUndoManager = oldskips;
}
},
{
toolTip:
$("ToolTip",
{ "Border.fill": "lightgray" },
$(go.TextBlock,
{ font: "10pt Verdana, sans-serif", margin: 4 },
new go.Binding("text", "", function(data) {
// Display text and percentage rounded to 2 decimals
var nodedata = findNodeDataForSlice(data);
if (nodedata) {
var percent = Math.round((data.count / getTotalCount(nodedata) * 100) * 100) / 100;
return data.text + ": " + percent + "%";
}
return "";
}))
)
}
);
var optionTemplate =
$(go.Panel, "TableRow",
$(go.TextBlock,
{
column: 0,
font: "10pt Verdana, sans-serif", alignment: go.Spot.Left,
margin: 5
},
new go.Binding("text")
),
$(go.Panel, "Auto",
{ column: 1 },
$(go.Shape, { fill: "#F2F2F2" }),
$(go.TextBlock,
{
font: "10pt Verdana, sans-serif",
textAlign: "right", margin: 2,
wrap: go.TextBlock.None, width: 40,
editable: true, isMultiline: false,
textValidation: isValidCount
},
new go.Binding("text", "count").makeTwoWay(function(count) { return parseInt(count, 10); })
)
),
$(go.Panel, "Horizontal",
{ column: 2 },
$("Button",
{
click: incrementCount
},
$(go.Shape, "PlusLine", { margin: 3, desiredSize: new go.Size(7, 7) })
),
$("Button",
{
click: decrementCount
},
$(go.Shape, "MinusLine", { margin: 3, desiredSize: new go.Size(7, 7) })
)
)
);
myDiagram.nodeTemplate =
$(go.Node, "Vertical",
{ deletable: false },
$(go.TextBlock,
{ font: "11pt Verdana, sans-serif", margin: 5 },
new go.Binding("text")
),
$(go.Panel, "Horizontal",
$(go.Panel, "Position",
{
name: "PIE",
// account for slices offsetting when selected so the node won't change size
desiredSize: new go.Size(pieRadius * 2.2 + 5, pieRadius * 2.2 + 5),
itemTemplate: sliceTemplate
},
new go.Binding("itemArray", "slices")
),
$(go.Panel, "Table",
{
margin: 5,
itemTemplate: optionTemplate
},
new go.Binding("itemArray", "slices")
)
)
);
myDiagram.model = $(go.Model,
{
copiesArrays: true,
copiesArrayObjects: true,
nodeDataArray:
[
{
key: 0,
text: "Sample Poll",
slices: [
{ text: "Option 1", count: 21, color: "#B378C1" },
{ text: "Option 2", count: 11, color: "#F25F5C" },
{ text: "Option 3", count: 5, color: "#FFE066" },
{ text: "Option 4", count: 2, color: "#2B98C5" },
{ text: "Option 5", count: 1, color: "#70C1B3" }
]
}
]
});
// Validation function for editing text
function isValidCount(textblock, oldstr, newstr) {
if (newstr === "") return false;
var num = +newstr; // quick way to convert a string to a number
return !isNaN(num) && Number.isInteger(num) && num >= 0;
}
// Given some slice data, find the corresponding node data
function findNodeDataForSlice(slice) {
var arr = myDiagram.model.nodeDataArray;
for (var i = 0; i < arr.length; i++) {
var data = arr[i];
if (data.slices.indexOf(slice) >= 0) {
return data;
}
}
}
function makeGeo(data) {
var nodedata = findNodeDataForSlice(data);
var sliceindex = nodedata.slices.indexOf(data);
var angles = getAngles(nodedata, sliceindex);
// Constructing the Geomtery this way is much more efficient than calling go.GraphObject.make:
return new go.Geometry()
.add(new go.PathFigure(pieRadius, pieRadius) // start point
.add(new go.PathSegment(go.PathSegment.Arc,
angles.start, angles.sweep, // angles
pieRadius, pieRadius, // center
pieRadius, pieRadius) // radius
.close()));
}
// Ensure slices get the proper positioning after we update any counts
function positionSlice(data, obj) {
var nodedata = findNodeDataForSlice(data);
var sliceindex = nodedata.slices.indexOf(data);
var angles = getAngles(nodedata, sliceindex);
var selected = obj.findObject("SLICE").stroke !== "transparent";
if (selected && angles.sweep !== 360) {
var offsetPoint = new go.Point(pieRadius / 10, 0); // offset by 1/10 the radius
offsetPoint = offsetPoint.rotate(angles.start + angles.sweep / 2); // rotate to the correct angle
offsetPoint = offsetPoint.offset(pieRadius / 10, pieRadius / 10); // translate center toward middle of pie panel
return offsetPoint;
}
return new go.Point(pieRadius / 10, pieRadius / 10);
}
// This is a bit inefficient, but should be OK for normal-sized graphs with reasonable numbers of slices per node
function findAllSelectedItems() {
var slices = [];
for (var nit = myDiagram.nodes; nit.next();) {
var node = nit.value;
var pie = node.findObject("PIE");
if (pie) {
for (var sit = pie.elements; sit.next();) {
var slicepanel = sit.value;
if (slicepanel.findObject("SLICE").stroke !== "transparent") slices.push(slicepanel);
}
}
}
return slices;
}
// Override the standard CommandHandler deleteSelection behavior.
// If there are any selected slices, delete them instead of deleting any selected nodes or links.
myDiagram.commandHandler.canDeleteSelection = function() {
// True if there are any selected deletable nodes or links,
// or if there are any selected slices within nodes
return go.CommandHandler.prototype.canDeleteSelection.call(myDiagram.commandHandler) ||
findAllSelectedItems().length > 0;
};
myDiagram.commandHandler.deleteSelection = function() {
var slices = findAllSelectedItems();
if (slices.length > 0) { // if there are any selected slices, delete them
myDiagram.startTransaction("delete slices");
var nodeset = new go.Set();
for (var i = 0; i < slices.length; i++) {
var panel = slices[i];
var nodedata = panel.part.data;
var slicearray = nodedata.slices;
var slicedata = panel.data;
var sliceindex = slicearray.indexOf(slicedata);
// Remove the slice from the model
myDiagram.model.removeArrayItem(slicearray, sliceindex);
nodeset.add(nodedata);
}
// Force geometries to be redrawn on any node that had slices deleted
nodeset.each(function(data) {
myDiagram.model.updateTargetBindings(data, "count");
});
myDiagram.commitTransaction("delete slices");
} else { // otherwise just delete nodes and/or links, as usual
go.CommandHandler.prototype.deleteSelection.call(myDiagram.commandHandler);
}
};
// Return total count of a given node
function getTotalCount(nodedata) {
var totCount = 0;
for (var i = 0; i < nodedata.slices.length; i++) {
totCount += nodedata.slices[i].count;
}
return totCount;
}
// Determine start and sweep angles given some node data and the index of the slice
function getAngles(nodedata, index) {
var totCount = getTotalCount(nodedata);
var startAngle = -90;
for (var i = 0; i < index; i++) {
startAngle += 360 * nodedata.slices[i].count / totCount;
}
return { "start": startAngle, "sweep": 360 * nodedata.slices[index].count / totCount };
}
// When user hits + button, increment count on that option
function incrementCount(e, obj) {
myDiagram.model.startTransaction("increment count");
var slicedata = obj.panel.panel.data;
myDiagram.model.setDataProperty(slicedata, "count", slicedata.count + 1);
myDiagram.model.commitTransaction("increment count");
}
// When user hits - button, decrement count on that option
function decrementCount(e, obj) {
myDiagram.model.startTransaction("decrement count");
var slicedata = obj.panel.panel.data;
if (slicedata.count > 0)
myDiagram.model.setDataProperty(slicedata, "count", slicedata.count - 1);
myDiagram.model.commitTransaction("decrement count");
}
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width: 100%; height: 500px;"></div>
<p>
This sample demonstrates the ability to build an updateable pie chart with selectable slices.
The Geometry for each slice is built using a <a>PathFigure</a> with a <a>PathSegment,Arc</a>.
Slices use a custom <b>click</b> function, which sets a stroke and offsets slices as they are selected.
Functionality for "selection" and deletion of these slices is similar to the <a href="selectableFields.html">Selectable Fields sample</a>,
using some overridden <a>CommandHandler</a> functions.
Each slice also has a tooltip showing the text and percentage of votes.
</p>
<p>
Poll results can be adjusted and the pie chart will automatically update to reflect any changes.
This includes deleting selected slices, updating the count using a TextBlock, or using the +/- buttons.
</p>
</div>
</body>
</html>
+518
View File
@@ -0,0 +1,518 @@
<!DOCTYPE html>
<html>
<head>
<title>Dynamic Ports</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Nodes with varying lists of ports on each of four sides." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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 node templates
myDiagram =
$(go.Diagram, "myDiagramDiv", //Diagram refers to its DIV HTML element by id
{ "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);
}
});
// To simplify this code we define a function for creating a context menu button:
function makeButton(text, action, visiblePredicate) {
return $("ContextMenuButton",
$(go.TextBlock, text),
{ click: action },
// don't bother with binding GraphObject.visible if there's no predicate
visiblePredicate ? new go.Binding("visible", "", function(o, e) { return o.diagram ? visiblePredicate(o, e) : false; }).ofObject() : {});
}
var nodeMenu = // context menu for each Node
$("ContextMenu",
makeButton("Copy",
function(e, obj) { e.diagram.commandHandler.copySelection(); }),
makeButton("Delete",
function(e, obj) { e.diagram.commandHandler.deleteSelection(); }),
$(go.Shape, "LineH", { strokeWidth: 2, height: 1, stretch: go.GraphObject.Horizontal }),
makeButton("Add top port",
function(e, obj) { addPort("top"); }),
makeButton("Add left port",
function(e, obj) { addPort("left"); }),
makeButton("Add right port",
function(e, obj) { addPort("right"); }),
makeButton("Add bottom port",
function(e, obj) { addPort("bottom"); })
);
var portSize = new go.Size(8, 8);
var portMenu = // context menu for each port
$("ContextMenu",
makeButton("Swap order",
function(e, obj) { swapOrder(obj.part.adornedObject); }),
makeButton("Remove port",
// in the click event handler, the obj.part is the Adornment;
// its adornedObject is the port
function(e, obj) { removePort(obj.part.adornedObject); }),
makeButton("Change color",
function(e, obj) { changeColor(obj.part.adornedObject); }),
makeButton("Remove side ports",
function(e, obj) { removeAll(obj.part.adornedObject); })
);
// the node template
// includes a panel on each side with an itemArray of panels containing ports
myDiagram.nodeTemplate =
$(go.Node, "Table",
{
locationObjectName: "BODY",
locationSpot: go.Spot.Center,
selectionObjectName: "BODY",
contextMenu: nodeMenu
},
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
// the body
$(go.Panel, "Auto",
{
row: 1, column: 1, name: "BODY",
stretch: go.GraphObject.Fill
},
$(go.Shape, "Rectangle",
{
fill: "#dbf6cb", stroke: null, strokeWidth: 0,
minSize: new go.Size(60, 60)
}),
$(go.TextBlock,
{ margin: 10, textAlign: "center", font: "bold 14px Segoe UI,sans-serif", stroke: "#484848", editable: true },
new go.Binding("text", "name").makeTwoWay())
), // end Auto Panel body
// the Panel holding the left port elements, which are themselves Panels,
// created for each item in the itemArray, bound to data.leftArray
$(go.Panel, "Vertical",
new go.Binding("itemArray", "leftArray"),
{
row: 1, column: 0,
itemTemplate:
$(go.Panel,
{
_side: "left", // internal property to make it easier to tell which side it's on
fromSpot: go.Spot.Left, toSpot: go.Spot.Left,
fromLinkable: true, toLinkable: true, cursor: "pointer",
contextMenu: portMenu
},
new go.Binding("portId", "portId"),
$(go.Shape, "Rectangle",
{
stroke: null, strokeWidth: 0,
desiredSize: portSize,
margin: new go.Margin(1, 0)
},
new go.Binding("fill", "portColor"))
) // end itemTemplate
}
), // end Vertical Panel
// the Panel holding the top port elements, which are themselves Panels,
// created for each item in the itemArray, bound to data.topArray
$(go.Panel, "Horizontal",
new go.Binding("itemArray", "topArray"),
{
row: 0, column: 1,
itemTemplate:
$(go.Panel,
{
_side: "top",
fromSpot: go.Spot.Top, toSpot: go.Spot.Top,
fromLinkable: true, toLinkable: true, cursor: "pointer",
contextMenu: portMenu
},
new go.Binding("portId", "portId"),
$(go.Shape, "Rectangle",
{
stroke: null, strokeWidth: 0,
desiredSize: portSize,
margin: new go.Margin(0, 1)
},
new go.Binding("fill", "portColor"))
) // end itemTemplate
}
), // end Horizontal Panel
// the Panel holding the right port elements, which are themselves Panels,
// created for each item in the itemArray, bound to data.rightArray
$(go.Panel, "Vertical",
new go.Binding("itemArray", "rightArray"),
{
row: 1, column: 2,
itemTemplate:
$(go.Panel,
{
_side: "right",
fromSpot: go.Spot.Right, toSpot: go.Spot.Right,
fromLinkable: true, toLinkable: true, cursor: "pointer",
contextMenu: portMenu
},
new go.Binding("portId", "portId"),
$(go.Shape, "Rectangle",
{
stroke: null, strokeWidth: 0,
desiredSize: portSize,
margin: new go.Margin(1, 0)
},
new go.Binding("fill", "portColor"))
) // end itemTemplate
}
), // end Vertical Panel
// the Panel holding the bottom port elements, which are themselves Panels,
// created for each item in the itemArray, bound to data.bottomArray
$(go.Panel, "Horizontal",
new go.Binding("itemArray", "bottomArray"),
{
row: 2, column: 1,
itemTemplate:
$(go.Panel,
{
_side: "bottom",
fromSpot: go.Spot.Bottom, toSpot: go.Spot.Bottom,
fromLinkable: true, toLinkable: true, cursor: "pointer",
contextMenu: portMenu
},
new go.Binding("portId", "portId"),
$(go.Shape, "Rectangle",
{
stroke: null, strokeWidth: 0,
desiredSize: portSize,
margin: new go.Margin(0, 1)
},
new go.Binding("fill", "portColor"))
) // end itemTemplate
}
) // end Horizontal Panel
); // end Node
// an orthogonal link template, reshapable and relinkable
myDiagram.linkTemplate =
$(CustomLink, // defined below
{
routing: go.Link.AvoidsNodes,
corner: 4,
curve: go.Link.JumpGap,
reshapable: true,
resegmentable: true,
relinkableFrom: true,
relinkableTo: true
},
new go.Binding("points").makeTwoWay(),
$(go.Shape, { stroke: "#2F4F4F", strokeWidth: 2 })
);
// support double-clicking in the background to add a copy of this data as a node
myDiagram.toolManager.clickCreatingTool.archetypeNodeData = {
name: "Unit",
leftArray: [],
rightArray: [],
topArray: [],
bottomArray: []
};
myDiagram.contextMenu =
$("ContextMenu",
makeButton("Paste",
function(e, obj) { e.diagram.commandHandler.pasteSelection(e.diagram.toolManager.contextMenuTool.mouseDownPoint); },
function(o) { return o.diagram.commandHandler.canPasteSelection(o.diagram.toolManager.contextMenuTool.mouseDownPoint); }),
makeButton("Undo",
function(e, obj) { e.diagram.commandHandler.undo(); },
function(o) { return o.diagram.commandHandler.canUndo(); }),
makeButton("Redo",
function(e, obj) { e.diagram.commandHandler.redo(); },
function(o) { return o.diagram.commandHandler.canRedo(); })
);
// load the diagram from JSON data
load();
}
// This custom-routing Link class tries to separate parallel links from each other.
// This assumes that ports are lined up in a row/column on a side of the node.
function CustomLink() {
go.Link.call(this);
};
go.Diagram.inherit(CustomLink, go.Link);
CustomLink.prototype.findSidePortIndexAndCount = function(node, port) {
var nodedata = node.data;
if (nodedata !== null) {
var portdata = port.data;
var side = port._side;
var arr = nodedata[side + "Array"];
var len = arr.length;
for (var i = 0; i < len; i++) {
if (arr[i] === portdata) return [i, len];
}
}
return [-1, len];
};
CustomLink.prototype.computeEndSegmentLength = function(node, port, spot, from) {
var esl = go.Link.prototype.computeEndSegmentLength.call(this, node, port, spot, from);
var other = this.getOtherPort(port);
if (port !== null && other !== null) {
var thispt = port.getDocumentPoint(this.computeSpot(from));
var otherpt = other.getDocumentPoint(this.computeSpot(!from));
if (Math.abs(thispt.x - otherpt.x) > 20 || Math.abs(thispt.y - otherpt.y) > 20) {
var info = this.findSidePortIndexAndCount(node, port);
var idx = info[0];
var count = info[1];
if (port._side == "top" || port._side == "bottom") {
if (otherpt.x < thispt.x) {
return esl + 4 + idx * 8;
} else {
return esl + (count - idx - 1) * 8;
}
} else { // left or right
if (otherpt.y < thispt.y) {
return esl + 4 + idx * 8;
} else {
return esl + (count - idx - 1) * 8;
}
}
}
}
return esl;
};
CustomLink.prototype.hasCurviness = function() {
if (isNaN(this.curviness)) return true;
return go.Link.prototype.hasCurviness.call(this);
};
CustomLink.prototype.computeCurviness = function() {
if (isNaN(this.curviness)) {
var fromnode = this.fromNode;
var fromport = this.fromPort;
var fromspot = this.computeSpot(true);
var frompt = fromport.getDocumentPoint(fromspot);
var tonode = this.toNode;
var toport = this.toPort;
var tospot = this.computeSpot(false);
var topt = toport.getDocumentPoint(tospot);
if (Math.abs(frompt.x - topt.x) > 20 || Math.abs(frompt.y - topt.y) > 20) {
if ((fromspot.equals(go.Spot.Left) || fromspot.equals(go.Spot.Right)) &&
(tospot.equals(go.Spot.Left) || tospot.equals(go.Spot.Right))) {
var fromseglen = this.computeEndSegmentLength(fromnode, fromport, fromspot, true);
var toseglen = this.computeEndSegmentLength(tonode, toport, tospot, false);
var c = (fromseglen - toseglen) / 2;
if (frompt.x + fromseglen >= topt.x - toseglen) {
if (frompt.y < topt.y) return c;
if (frompt.y > topt.y) return -c;
}
} else if ((fromspot.equals(go.Spot.Top) || fromspot.equals(go.Spot.Bottom)) &&
(tospot.equals(go.Spot.Top) || tospot.equals(go.Spot.Bottom))) {
var fromseglen = this.computeEndSegmentLength(fromnode, fromport, fromspot, true);
var toseglen = this.computeEndSegmentLength(tonode, toport, tospot, false);
var c = (fromseglen - toseglen) / 2;
if (frompt.x + fromseglen >= topt.x - toseglen) {
if (frompt.y < topt.y) return c;
if (frompt.y > topt.y) return -c;
}
}
}
}
return go.Link.prototype.computeCurviness.call(this);
};
// end CustomLink class
// Add a port to the specified side of the selected nodes.
function addPort(side) {
myDiagram.startTransaction("addPort");
myDiagram.selection.each(function(node) {
// skip any selected Links
if (!(node instanceof go.Node)) return;
// compute the next available index number for the side
var i = 0;
while (node.findPort(side + i.toString()) !== node) i++;
// now this new port name is unique within the whole Node because of the side prefix
var name = side + i.toString();
// get the Array of port data to be modified
var arr = node.data[side + "Array"];
if (arr) {
// create a new port data object
var newportdata = {
portId: name,
portColor: getPortColor()
// if you add port data properties here, you should copy them in copyPortData above
};
// and add it to the Array of port data
myDiagram.model.insertArrayItem(arr, -1, newportdata);
}
});
myDiagram.commitTransaction("addPort");
}
// Exchange the position/order of the given port with the next one.
// If it's the last one, swap with the previous one.
function swapOrder(port) {
var arr = port.panel.itemArray;
if (arr.length >= 2) { // only if there are at least two ports!
for (var i = 0; i < arr.length; i++) {
if (arr[i].portId === port.portId) {
myDiagram.startTransaction("swap ports");
if (i >= arr.length - 1) i--; // now can swap I and I+1, even if it's the last port
var newarr = arr.slice(0); // copy Array
newarr[i] = arr[i + 1]; // swap items
newarr[i + 1] = arr[i];
// remember the new Array in the model
myDiagram.model.setDataProperty(port.part.data, port._side + "Array", newarr);
myDiagram.commitTransaction("swap ports");
break;
}
}
}
}
// Remove the clicked port from the node.
// Links to the port will be redrawn to the node's shape.
function removePort(port) {
myDiagram.startTransaction("removePort");
var pid = port.portId;
var arr = port.panel.itemArray;
for (var i = 0; i < arr.length; i++) {
if (arr[i].portId === pid) {
myDiagram.model.removeArrayItem(arr, i);
break;
}
}
myDiagram.commitTransaction("removePort");
}
// Remove all ports from the same side of the node as the clicked port.
function removeAll(port) {
myDiagram.startTransaction("removePorts");
var nodedata = port.part.data;
var side = port._side; // there are four property names, all ending in "Array"
myDiagram.model.setDataProperty(nodedata, side + "Array", []); // an empty Array
myDiagram.commitTransaction("removePorts");
}
// Change the color of the clicked port.
function changeColor(port) {
myDiagram.startTransaction("colorPort");
var data = port.data;
myDiagram.model.setDataProperty(data, "portColor", getPortColor());
myDiagram.commitTransaction("colorPort");
}
// Use some pastel colors for ports
function getPortColor() {
var portColors = ["#fae3d7", "#d6effc", "#ebe3fc", "#eaeef8", "#fadfe5", "#6cafdb", "#66d6d1"]
return portColors[Math.floor(Math.random() * portColors.length)];
}
// Save the model to / load it from JSON text shown on the page itself, not in a database.
function save() {
document.getElementById("mySavedModel").value = myDiagram.model.toJson();
myDiagram.isModified = false;
}
function load() {
myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
// When copying a node, we need to copy the data that the node is bound to.
// This JavaScript object includes properties for the node as a whole, and
// four properties that are Arrays holding data for each port.
// Those arrays and port data objects need to be copied too.
// Thus Model.copiesArrays and Model.copiesArrayObjects both need to be true.
// Link data includes the names of the to- and from- ports;
// so the GraphLinksModel needs to set these property names:
// linkFromPortIdProperty and linkToPortIdProperty.
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="width:600px; height:500px; border:1px solid black"></div>
Add port to selected nodes:
<button onclick="addPort('top')">Top</button>
<button onclick="addPort('bottom')">Bottom</button>
<button onclick="addPort('left')">Left</button>
<button onclick="addPort('right')">Right</button>
<p>
Double-click in the diagram background in order to add a new node there.
In this sample you can add ports to a selected node by clicking the above buttons or by using the context menu.
Draw links between ports by dragging between ports.
If you select a link you can relink or reshape it.
Right-click or touch-hold on a port to bring up a context menu that allows you to remove it or change its color.
</p>
<p>
The diagram also uses a custom link to allow for special routing to help parallel links avoid each other
using overridden <a>Link.computeEndSegmentLength</a>, <a>Link.hasCurviness</a>, and <a>Link.computeCurviness</a>
functions.
</p>
<p>
See the <a href="../intro/ports.html">Ports Intro page</a> for an explanation of GoJS ports.
</p>
<div>
<div>
<button id="SaveButton" onclick="save()">Save</button>
<button onclick="load()">Load</button>
Diagram Model saved in JSON format:
</div>
<textarea id="mySavedModel" style="width:100%;height:250px">
{ "class": "go.GraphLinksModel",
"copiesArrays": true,
"copiesArrayObjects": true,
"linkFromPortIdProperty": "fromPort",
"linkToPortIdProperty": "toPort",
"nodeDataArray": [
{"key":1, "name":"Unit One", "loc":"101 204",
"leftArray":[ {"portColor":"#fae3d7", "portId":"left0"} ],
"topArray":[ {"portColor":"#d6effc", "portId":"top0"} ],
"bottomArray":[ {"portColor":"#ebe3fc", "portId":"bottom0"} ],
"rightArray":[ {"portColor":"#eaeef8", "portId":"right0"},{"portColor":"#fadfe5", "portId":"right1"} ] },
{"key":2, "name":"Unit Two", "loc":"320 152",
"leftArray":[ {"portColor":"#6cafdb", "portId":"left0"},{"portColor":"#66d6d1", "portId":"left1"},{"portColor":"#fae3d7", "portId":"left2"} ],
"topArray":[ {"portColor":"#d6effc", "portId":"top0"} ],
"bottomArray":[ {"portColor":"#eaeef8", "portId":"bottom0"},{"portColor":"#eaeef8", "portId":"bottom1"},{"portColor":"#6cafdb", "portId":"bottom2"} ],
"rightArray":[ ] },
{"key":3, "name":"Unit Three", "loc":"384 319",
"leftArray":[ {"portColor":"#66d6d1", "portId":"left0"},{"portColor":"#fadfe5", "portId":"left1"},{"portColor":"#6cafdb", "portId":"left2"} ],
"topArray":[ {"portColor":"#66d6d1", "portId":"top0"} ],
"bottomArray":[ {"portColor":"#6cafdb", "portId":"bottom0"} ],
"rightArray":[ ] },
{"key":4, "name":"Unit Four", "loc":"138 351",
"leftArray":[ {"portColor":"#fae3d7", "portId":"left0"} ],
"topArray":[ {"portColor":"#6cafdb", "portId":"top0"} ],
"bottomArray":[ {"portColor":"#6cafdb", "portId":"bottom0"} ],
"rightArray":[ {"portColor":"#6cafdb", "portId":"right0"},{"portColor":"#66d6d1", "portId":"right1"} ] }
],
"linkDataArray": [
{"from":4, "to":2, "fromPort":"top0", "toPort":"bottom0"},
{"from":4, "to":2, "fromPort":"top0", "toPort":"bottom0"},
{"from":3, "to":2, "fromPort":"top0", "toPort":"bottom1"},
{"from":4, "to":3, "fromPort":"right0", "toPort":"left0"},
{"from":4, "to":3, "fromPort":"right1", "toPort":"left2"},
{"from":1, "to":2, "fromPort":"right0", "toPort":"left1"},
{"from":1, "to":2, "fromPort":"right1", "toPort":"left2"}
]}
</textarea>
</div>
</div>
</body>
</html>
+194
View File
@@ -0,0 +1,194 @@
<!DOCTYPE html>
<html>
<head>
<title>Entity Relationship</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Interactive entity-relationship diagram or data model diagram implemented by GoJS in JavaScript for HTML." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../extensions/Figures.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
myDiagram =
$(go.Diagram, "myDiagramDiv", // must name or refer to the DIV HTML element
{
allowDelete: false,
allowCopy: false,
layout: $(go.ForceDirectedLayout),
"undoManager.isEnabled": true
});
var colors = {
'red': '#be4b15',
'green': '#52ce60',
'blue': '#6ea5f8',
'lightred': '#fd8852',
'lightblue': '#afd4fe',
'lightgreen': '#b9e986',
'pink': '#faadc1',
'purple': '#d689ff',
'orange': '#fdb400',
}
// the template for each attribute in a node's array of item data
var itemTempl =
$(go.Panel, "Horizontal",
$(go.Shape,
{ desiredSize: new go.Size(15, 15), strokeJoin: "round", strokeWidth: 3, stroke: null, margin: 2 },
new go.Binding("figure", "figure"),
new go.Binding("fill", "color"),
new go.Binding("stroke", "color")),
$(go.TextBlock,
{
stroke: "#333333",
font: "bold 14px sans-serif"
},
new go.Binding("text", "name"))
);
// define the Node template, representing an entity
myDiagram.nodeTemplate =
$(go.Node, "Auto", // the whole node panel
{
selectionAdorned: true,
resizable: true,
layoutConditions: go.Part.LayoutStandard & ~go.Part.LayoutNodeSized,
fromSpot: go.Spot.AllSides,
toSpot: go.Spot.AllSides,
isShadowed: true,
shadowOffset: new go.Point(3, 3),
shadowColor: "#C5C1AA"
},
new go.Binding("location", "location").makeTwoWay(),
// whenever the PanelExpanderButton changes the visible property of the "LIST" panel,
// clear out any desiredSize set by the ResizingTool.
new go.Binding("desiredSize", "visible", function(v) { return new go.Size(NaN, NaN); }).ofObject("LIST"),
// define the node's outer shape, which will surround the Table
$(go.Shape, "RoundedRectangle",
{ fill: 'white', stroke: "#eeeeee", strokeWidth: 3 }),
$(go.Panel, "Table",
{ margin: 8, stretch: go.GraphObject.Fill },
$(go.RowColumnDefinition, { row: 0, sizing: go.RowColumnDefinition.None }),
// the table header
$(go.TextBlock,
{
row: 0, alignment: go.Spot.Center,
margin: new go.Margin(0, 24, 0, 2), // leave room for Button
font: "bold 16px sans-serif"
},
new go.Binding("text", "key")),
// the collapse/expand button
$("PanelExpanderButton", "LIST", // the name of the element whose visibility this button toggles
{ row: 0, alignment: go.Spot.TopRight }),
// the list of Panels, each showing an attribute
$(go.Panel, "Vertical",
{
name: "LIST",
row: 1,
padding: 3,
alignment: go.Spot.TopLeft,
defaultAlignment: go.Spot.Left,
stretch: go.GraphObject.Horizontal,
itemTemplate: itemTempl
},
new go.Binding("itemArray", "items"))
) // end Table Panel
); // end Node
// define the Link template, representing a relationship
myDiagram.linkTemplate =
$(go.Link, // the whole link panel
{
selectionAdorned: true,
layerName: "Foreground",
reshapable: true,
routing: go.Link.AvoidsNodes,
corner: 5,
curve: go.Link.JumpOver
},
$(go.Shape, // the link shape
{ stroke: "#303B45", strokeWidth: 2.5 }),
$(go.TextBlock, // the "from" label
{
textAlign: "center",
font: "bold 14px sans-serif",
stroke: "#1967B3",
segmentIndex: 0,
segmentOffset: new go.Point(NaN, NaN),
segmentOrientation: go.Link.OrientUpright
},
new go.Binding("text", "text")),
$(go.TextBlock, // the "to" label
{
textAlign: "center",
font: "bold 14px sans-serif",
stroke: "#1967B3",
segmentIndex: -1,
segmentOffset: new go.Point(NaN, NaN),
segmentOrientation: go.Link.OrientUpright
},
new go.Binding("text", "toText"))
);
// create the model for the E-R diagram
var nodeDataArray = [
{
key: "Products",
items: [{ name: "ProductID", iskey: true, figure: "Decision", color: colors.red },
{ name: "ProductName", iskey: false, figure: "Hexagon", color: colors.blue },
{ name: "SupplierID", iskey: false, figure: "Decision", color: "purple" },
{ name: "CategoryID", iskey: false, figure: "Decision", color: "purple" }]
},
{
key: "Suppliers",
items: [{ name: "SupplierID", iskey: true, figure: "Decision", color: colors.red },
{ name: "CompanyName", iskey: false, figure: "Hexagon", color: colors.blue },
{ name: "ContactName", iskey: false, figure: "Hexagon", color: colors.blue },
{ name: "Address", iskey: false, figure: "Hexagon", color: colors.blue }]
},
{
key: "Categories",
items: [{ name: "CategoryID", iskey: true, figure: "Decision", color: colors.red },
{ name: "CategoryName", iskey: false, figure: "Hexagon", color: colors.blue },
{ name: "Description", iskey: false, figure: "Hexagon", color: colors.blue },
{ name: "Picture", iskey: false, figure: "TriangleUp", color: colors.pink }]
},
{
key: "Order Details",
items: [{ name: "OrderID", iskey: true, figure: "Decision", color: colors.red },
{ name: "ProductID", iskey: true, figure: "Decision", color: colors.red },
{ name: "UnitPrice", iskey: false, figure: "Circle", color: colors.green },
{ name: "Quantity", iskey: false, figure: "Circle", color: colors.green },
{ name: "Discount", iskey: false, figure: "Circle", color: colors.green }]
},
];
var linkDataArray = [
{ from: "Products", to: "Suppliers", text: "0..N", toText: "1" },
{ from: "Products", to: "Categories", text: "0..N", toText: "1" },
{ from: "Order Details", to: "Products", text: "0..N", toText: "1" }
];
myDiagram.model = $(go.GraphLinksModel,
{
copiesArrays: true,
copiesArrayObjects: true,
nodeDataArray: nodeDataArray,
linkDataArray: linkDataArray
});
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="background-color: white; border: solid 1px black; width: 100%; height: 700px"></div>
<p>Sample for representing the relationship between various entities. Try dragging the nodes -- their links will avoid other nodes, by virtue of the <a>Link,AvoidsNodes</a> property assigned to the
custom link template's <a>Link.routing</a>. Also note the use of <a href="../intro/buttons.html" target="_blank">Panel Expander Buttons</a> to allow for expandable/collapsible node data.
</p>
<p>Buttons are defined in <a href="../extensions/Buttons.js">Buttons.js</a>.</p>
</div>
</body>
</html>
+120
View File
@@ -0,0 +1,120 @@
<!DOCTYPE html>
<html>
<head>
<title>Euler Diagram</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A diagram showing nodes connected by different kinds of links with concentric circular backgrounds. Clicking on a node opens a window to a Wikipedia page." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../extensions/HyperlinkText.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;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{ isReadOnly: true, allowSelect: false, contentAlignment: go.Spot.Center });
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{ locationSpot: go.Spot.Center },
new go.Binding("location", "loc", go.Point.parse),
$(go.Shape, "Ellipse",
{ fill: "transparent" },
new go.Binding("stroke", "color"),
new go.Binding("strokeWidth", "width"),
new go.Binding("strokeDashArray", "dash")),
$("HyperlinkText",
function(node) { return "https://en.wikipedia.org/w/index.php?search=" + encodeURIComponent(node.data.text); },
function(node) { return node.data.text; },
{ margin: 1, maxSize: new go.Size(80, 80), textAlign: "center" })
);
myDiagram.nodeTemplateMap.add("center",
$(go.Node, "Spot",
{ locationSpot: go.Spot.Center },
new go.Binding("location", "loc", go.Point.parse),
$(go.Shape, "Circle",
{
fill: "rgba(128,128,128,0.1)", stroke: null,
width: 550, height: 550
}),
$(go.Shape, "Circle",
{
fill: "rgba(128,128,128,0.05)", stroke: null,
width: 400, height: 400
}),
$(go.Shape, "Circle",
{
fill: "rgba(128,128,128,0.033)", stroke: null,
width: 250, height: 250
}),
$(go.Panel, "Spot",
$(go.Shape, "Circle",
{ isPanelMain: true, fill: "transparent", portId: "" },
new go.Binding("stroke", "hicolor"),
new go.Binding("strokeWidth", "hiwidth")),
$(go.Shape, "Circle",
{ isPanelMain: true, fill: "transparent" },
new go.Binding("stroke", "color"),
new go.Binding("strokeWidth", "width"),
new go.Binding("strokeDashArray", "dash")),
$("HyperlinkText",
function(node) { return "https://en.wikipedia.org/w/index.php?search=" + encodeURIComponent(node.data.text); },
function(node) { return node.data.text; },
{ margin: 1, maxSize: new go.Size(80, 80), textAlign: "center" })
)
));
myDiagram.linkTemplate =
$(go.Link,
$(go.Shape,
new go.Binding("stroke", "color"),
new go.Binding("strokeWidth", "width"),
new go.Binding("strokeDashArray", "dash"))
);
var nodeDataArray = [
{ key: 1, text: "Cognitive Procedural", loc: "300 300", category: "center" },
{ key: 2, text: "Cognitive Problem Solving", loc: "600 300", category: "center", hicolor: "lightblue", hiwidth: 7 },
{ key: 11, text: "Logical Reasoning", loc: "450 275" },
{ key: 12, text: "Scaffolding", loc: "450 325" },
{ key: 13, text: "Part Task Training", loc: "425 400" },
{ key: 21, text: "Training Wheels", loc: "325 125" },
{ key: 22, text: "Exploratory Learning", loc: "250 150" },
{ key: 23, text: "Learner Control", loc: "650 150" },
{ key: 31, text: "Overlearning", loc: "450 475" }
];
var linkDataArray = [
{ from: 1, to: 11, color: "gray" },
{ from: 1, to: 12, color: "gray", dash: [3, 2] },
{ from: 1, to: 13, color: "olive", width: 2 },
{ from: 1, to: 21, color: "olive", width: 3 },
{ from: 1, to: 22, color: "olive", width: 2 },
{ from: 1, to: 23, color: "crimson", width: 2 },
{ from: 1, to: 31 },
{ from: 2, to: 11, color: "gray" },
{ from: 2, to: 12, color: "olive", width: 2 },
{ from: 2, to: 13, color: "gray", dash: [3, 2] },
{ from: 2, to: 21, color: "crimson", width: 2 },
{ from: 2, to: 22, color: "crimson", width: 2 },
{ from: 2, to: 23, color: "black", width: 3 },
{ from: 2, to: 31, color: "black", dash: [3, 2] }
];
myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
<p> A sample of a Euler diagram: that is, a means of representing various sets and their relationships with one another. Euler diagrams have much in common with Venn diagrams.
This diagram is read-only, but clicking on a node will search Wikipedia
with a query string generated from the "text" property of the node data.
</p>
</div>
</body>
</html>
+221
View File
@@ -0,0 +1,221 @@
<!DOCTYPE html>
<html>
<head>
<title>Family Tree (British)</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A family tree diagram of British royalty." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<link href='https://fonts.googleapis.com/css?family=Droid+Serif:400,700' rel='stylesheet' type='text/css'>
<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
myDiagram =
$(go.Diagram, "myDiagramDiv", // must be the ID or reference to div
{
"toolManager.hoverDelay": 100, // 100 milliseconds instead of the default 850
allowCopy: false,
layout: // create a TreeLayout for the family tree
$(go.TreeLayout,
{ angle: 90, nodeSpacing: 10, layerSpacing: 40, layerStyle: go.TreeLayout.LayerUniform })
});
var bluegrad = '#90CAF9';
var pinkgrad = '#F48FB1';
// Set up a Part as a legend, and place it directly on the diagram
myDiagram.add(
$(go.Part, "Table",
{ position: new go.Point(300, 10), selectable: false },
$(go.TextBlock, "Key",
{ row: 0, font: "700 14px Droid Serif, sans-serif" }), // end row 0
$(go.Panel, "Horizontal",
{ row: 1, alignment: go.Spot.Left },
$(go.Shape, "Rectangle",
{ desiredSize: new go.Size(30, 30), fill: bluegrad, margin: 5 }),
$(go.TextBlock, "Males",
{ font: "700 13px Droid Serif, sans-serif" })
), // end row 1
$(go.Panel, "Horizontal",
{ row: 2, alignment: go.Spot.Left },
$(go.Shape, "Rectangle",
{ desiredSize: new go.Size(30, 30), fill: pinkgrad, margin: 5 }),
$(go.TextBlock, "Females",
{ font: "700 13px Droid Serif, sans-serif" })
) // end row 2
));
// get tooltip text from the object's data
function tooltipTextConverter(person) {
var str = "";
str += "Born: " + person.birthYear;
if (person.deathYear !== undefined) str += "\nDied: " + person.deathYear;
if (person.reign !== undefined) str += "\nReign: " + person.reign;
return str;
}
// define tooltips for nodes
var tooltiptemplate =
$("ToolTip",
{ "Border.fill": "whitesmoke", "Border.stroke": "black" },
$(go.TextBlock,
{
font: "bold 8pt Helvetica, bold Arial, sans-serif",
wrap: go.TextBlock.WrapFit,
margin: 5
},
new go.Binding("text", "", tooltipTextConverter))
);
// define Converters to be used for Bindings
function genderBrushConverter(gender) {
if (gender === "M") return bluegrad;
if (gender === "F") return pinkgrad;
return "orange";
}
// replace the default Node template in the nodeTemplateMap
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{ deletable: false, toolTip: tooltiptemplate },
new go.Binding("text", "name"),
$(go.Shape, "Rectangle",
{
fill: "lightgray",
stroke: null, strokeWidth: 0,
stretch: go.GraphObject.Fill,
alignment: go.Spot.Center
},
new go.Binding("fill", "gender", genderBrushConverter)),
$(go.TextBlock,
{
font: "700 12px Droid Serif, sans-serif",
textAlign: "center",
margin: 10, maxSize: new go.Size(80, NaN)
},
new go.Binding("text", "name"))
);
// define the Link template
myDiagram.linkTemplate =
$(go.Link, // the whole link panel
{ routing: go.Link.Orthogonal, corner: 5, selectable: false },
$(go.Shape, { strokeWidth: 3, stroke: '#424242' })); // the gray link shape
// here's the family data
var nodeDataArray = [
{ key: 0, name: "George V", gender: "M", birthYear: "1865", deathYear: "1936", reign: "1910-1936" },
{ key: 1, parent: 0, name: "Edward VIII", gender: "M", birthYear: "1894", deathYear: "1972", reign: "1936" },
{ key: 2, parent: 0, name: "George VI", gender: "M", birthYear: "1895", deathYear: "1952", reign: "1936-1952" },
{ key: 7, parent: 2, name: "Elizabeth II", gender: "F", birthYear: "1926", reign: "1952-" },
{ key: 16, parent: 7, name: "Charles, Prince of Wales", gender: "M", birthYear: "1948" },
{ key: 38, parent: 16, name: "Prince William", gender: "M", birthYear: "1982" },
{ key: 39, parent: 16, name: "Prince Harry of Wales", gender: "M", birthYear: "1984" },
{ key: 17, parent: 7, name: "Anne, Princess Royal", gender: "F", birthYear: "1950" },
{ key: 40, parent: 17, name: "Peter Phillips", gender: "M", birthYear: "1977" },
{ key: 82, parent: 40, name: "Savannah Phillips", gender: "F", birthYear: "2010" },
{ key: 41, parent: 17, name: "Zara Phillips", gender: "F", birthYear: "1981" },
{ key: 18, parent: 7, name: "Prince Andrew", gender: "M", birthYear: "1960" },
{ key: 42, parent: 18, name: "Princess Beatrice of York", gender: "F", birthYear: "1988" },
{ key: 43, parent: 18, name: "Princess Eugenie of York", gender: "F", birthYear: "1990" },
{ key: 19, parent: 7, name: "Prince Edward", gender: "M", birthYear: "1964" },
{ key: 44, parent: 19, name: "Lady Louise Windsor", gender: "F", birthYear: "2003" },
{ key: 45, parent: 19, name: "James, Viscount Severn", gender: "M", birthYear: "2007" },
{ key: 8, parent: 2, name: "Princess Margaret", gender: "F", birthYear: "1930", deathYear: "2002" },
{ key: 20, parent: 8, name: "David Armstrong-Jones", gender: "M", birthYear: "1961" },
{ key: 21, parent: 8, name: "Lady Sarah Chatto", gender: "F", birthYear: "1964" },
{ key: 46, parent: 21, name: "Samuel Chatto", gender: "M", birthYear: "1996" },
{ key: 47, parent: 21, name: "Arthur Chatto", gender: "M", birthYear: "1999" },
{ key: 3, parent: 0, name: "Mary, Princess Royal", gender: "F", birthYear: "1897", deathYear: "1965" },
{ key: 9, parent: 3, name: "George Lascelles", gender: "M", birthYear: "1923", deathYear: "2011" },
{ key: 22, parent: 9, name: "David Lascelles", gender: "M", birthYear: "1950" },
{ key: 48, parent: 22, name: "Emily Shard", gender: "F", birthYear: "1975" },
{ key: 49, parent: 22, name: "Benjamin Lascelles", gender: "M", birthYear: "1978" },
{ key: 50, parent: 22, name: "Alexander Lascelles", gender: "M", birthYear: "1980" },
{ key: 51, parent: 22, name: "Edward Lascelles", gender: "M", birthYear: "1982" },
{ key: 23, parent: 9, name: "James Lascelles", gender: "M", birthYear: "1953" },
{ key: 52, parent: 23, name: "Sophie Lascelles", gender: "F", birthYear: "1973" },
{ key: 53, parent: 23, name: "Rowan Lascelles", gender: "M", birthYear: "1977" },
{ key: 54, parent: 23, name: "Tanit Lascelles", gender: "F", birthYear: "1981" },
{ key: 55, parent: 23, name: "Tewa Lascelles", gender: "M", birthYear: "1985" },
{ key: 24, parent: 9, name: "Jeremy Lascelles", gender: "M", birthYear: "1955" },
{ key: 56, parent: 24, name: "Thomas Lascelles", gender: "M", birthYear: "1982" },
{ key: 57, parent: 24, name: "Ellen Lascelles", gender: "F", birthYear: "1984" },
{ key: 58, parent: 24, name: "Amy Lascelles", gender: "F", birthYear: "1986" },
{ key: 59, parent: 24, name: "Tallulah Lascelles", gender: "F", birthYear: "2005" },
{ key: 25, parent: 9, name: "Mark Lascelles", gender: "M", birthYear: "1964" },
{ key: 60, parent: 25, name: "Charlotte Lascelles", gender: "F", birthYear: "1996" },
{ key: 61, parent: 25, name: "Imogen Lascelles", gender: "F", birthYear: "1998" },
{ key: 62, parent: 25, name: "Miranda Lascelles", gender: "F", birthYear: "2000" },
{ key: 10, parent: 3, name: "Gerald Lascelles", gender: "M", birthYear: "1924", deathYear: "1998" },
{ key: 26, parent: 10, name: "Henry Lascelles", gender: "M", birthYear: "1953" },
{ key: 63, parent: 26, name: "Maximilian Lascelles", gender: "M", birthYear: "1991" },
{ key: 27, parent: 10, name: "Martin David Lascelles", gender: "M", birthYear: "1962" },
{ key: 64, parent: 27, name: "Alexander Lascelles", gender: "M", birthYear: "2002" },
{ key: 4, parent: 0, name: "Prince Henry", gender: "M", birthYear: "1900", deathYear: "1974" },
{ key: 11, parent: 4, name: "Prince William of Gloucester", gender: "M", birthYear: "1941", deathYear: "1972" },
{ key: 12, parent: 4, name: "Prince Richard", gender: "M", birthYear: "1944" },
{ key: 28, parent: 12, name: "Alexander Windsor", gender: "M", birthYear: "1974" },
{ key: 65, parent: 28, name: "Xan Windsor", gender: "M", birthYear: "2007" },
{ key: 66, parent: 28, name: "Lady Cosima Windsor", gender: "F", birthYear: "2010" },
{ key: 29, parent: 12, name: "Lady Davina Lewis", gender: "F", birthYear: "1977" },
{ key: 67, parent: 29, name: "Senna Lewis", gender: "F", birthYear: "2010" },
{ key: 30, parent: 12, name: "Lady Rose Gilman", gender: "F", birthYear: "1980" },
{ key: 68, parent: 30, name: "Lyla Gilman", gender: "F", birthYear: "2010" },
{ key: 5, parent: 0, name: "Prince George", gender: "M", birthYear: "1902", deathYear: "1942" },
{ key: 13, parent: 5, name: "Prince Edward", gender: "M", birthYear: "1935" },
{ key: 31, parent: 13, name: "George Windsor", gender: "M", birthYear: "1962" },
{ key: 69, parent: 31, name: "Edward Windsor", gender: "M", birthYear: "1988" },
{ key: 70, parent: 31, name: "Lady Marina-Charlotte Windsor", gender: "F", birthYear: "1992" },
{ key: 71, parent: 31, name: "Lady Amelia Windsor", gender: "F", birthYear: "1995" },
{ key: 32, parent: 13, name: "Lady Helen Taylor", gender: "F", birthYear: "1964" },
{ key: 72, parent: 32, name: "Columbus Taylor", gender: "M", birthYear: "1994" },
{ key: 73, parent: 32, name: "Cassius Taylor", gender: "M", birthYear: "1996" },
{ key: 74, parent: 32, name: "Eloise Taylor", gender: "F", birthYear: "2003" },
{ key: 75, parent: 32, name: "Estella Taylor", gender: "F", birthYear: "2004" },
{ key: 33, parent: 13, name: "Lord Nicholas Windsor", gender: "M", birthYear: "1970" },
{ key: 76, parent: 33, name: "Albert Windsor", gender: "M", birthYear: "2007" },
{ key: 77, parent: 33, name: "Leopold Windsor", gender: "M", birthYear: "2009" },
{ key: 14, parent: 5, name: "Princess Alexandra", gender: "F", birthYear: "1936" },
{ key: 34, parent: 14, name: "James Ogilvy", gender: "M", birthYear: "1964" },
{ key: 78, parent: 34, name: "Flora Ogilvy", gender: "F", birthYear: "1994" },
{ key: 79, parent: 34, name: "Alexander Ogilvy", gender: "M", birthYear: "1996" },
{ key: 35, parent: 14, name: "Marina Ogilvy", gender: "F", birthYear: "1966" },
{ key: 80, parent: 35, name: "Zenouska Mowatt", gender: "F", birthYear: "1990" },
{ key: 81, parent: 35, name: "Christian Mowatt", gender: "M", birthYear: "1993" },
{ key: 15, parent: 5, name: "Prince Michael of Kent", gender: "M", birthYear: "1942" },
{ key: 36, parent: 15, name: "Lord Frederick Windsor", gender: "M", birthYear: "1979" },
{ key: 37, parent: 15, name: "Lady Gabriella Windsor", gender: "F", birthYear: "1981" },
{ key: 6, parent: 0, name: "Prince John", gender: "M", birthYear: "1905", deathYear: "1919" }
];
// create the model for the family tree
myDiagram.model = new go.TreeModel(nodeDataArray);
document.getElementById('zoomToFit').addEventListener('click', function() {
myDiagram.commandHandler.zoomToFit();
});
document.getElementById('centerRoot').addEventListener('click', function() {
myDiagram.scale = 1;
myDiagram.scrollToRect(myDiagram.findNodeForKey(0).actualBounds);
});
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="background-color: white; border: solid 1px black; width: 100%; height: 550px"></div>
<p><button id="zoomToFit">Zoom to Fit</button> <button id="centerRoot">Center on root</button></p>
<p>This family tree diagram shows several generations of British royalty beginning with George V (1865-1936).</p>
<p><a>Node</a> data contains information about gender, and a data binding assigns a corresponding color. Additional data is displayed with a tooltip. A key is placed on the diagram using a <a>Panel,Table</a>.</p>
<p>For a variation of this tree, see the <a href="familyTreeJP.html">Japanese family tree sample</a>.</p>
<p>For a more complex family tree see the <a href="genogram.html">genogram sample</a>.</p>
</div>
</body>
</html>
+207
View File
@@ -0,0 +1,207 @@
<!DOCTYPE html>
<html>
<head>
<title>Family Tree (Japanese)</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A family tree diagram showing Japanese royalty." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
myDiagram =
$(go.Diagram, "myDiagramDiv", // must be the ID or reference to div
{
allowCopy: false,
layout: // create a TreeLayout for the family tree
$(go.TreeLayout,
{ angle: 90, nodeSpacing: 5 })
});
var bluegrad = $(go.Brush, "Linear", { 0: "rgb(60, 204, 254)", 1: "rgb(70, 172, 254)" });
var pinkgrad = $(go.Brush, "Linear", { 0: "rgb(255, 192, 203)", 1: "rgb(255, 142, 203)" });
// Set up a Part as a legend, and place it directly on the diagram
myDiagram.add(
$(go.Part, "Table",
{ position: new go.Point(10, 10), selectable: false },
$(go.TextBlock, "Key",
{ row: 0, font: "bold 10pt Helvetica, Arial, sans-serif" }), // end row 0
$(go.Panel, "Horizontal",
{ row: 1, alignment: go.Spot.Left },
$(go.Shape, "Rectangle",
{ desiredSize: new go.Size(30, 30), fill: bluegrad, margin: 5 }),
$(go.TextBlock, "Males",
{ font: "bold 8pt Helvetica, bold Arial, sans-serif" })
), // end row 1
$(go.Panel, "Horizontal",
{ row: 2, alignment: go.Spot.Left },
$(go.Shape, "Rectangle",
{ desiredSize: new go.Size(30, 30), fill: pinkgrad, margin: 5 }),
$(go.TextBlock, "Females",
{ font: "bold 8pt Helvetica, bold Arial, sans-serif" })
) // end row 2
));
// get tooltip text from the object's data
function tooltipTextConverter(person) {
var str = "";
str += "Born: " + person.birthYear;
if (person.deathYear !== undefined) str += "\nDied: " + person.deathYear;
if (person.reign !== undefined) str += "\nReign: " + person.reign;
return str;
}
// define tooltips for nodes
var tooltiptemplate =
$("ToolTip",
{ "Border.fill": "whitesmoke", "Border.stroke": "black" },
$(go.TextBlock,
{
font: "bold 8pt Helvetica, bold Arial, sans-serif",
wrap: go.TextBlock.WrapFit,
margin: 5
},
new go.Binding("text", "", tooltipTextConverter))
);
// define Converters to be used for Bindings
function genderBrushConverter(gender) {
if (gender === "M") return bluegrad;
if (gender === "F") return pinkgrad;
return "orange";
}
// replace the default Node template in the nodeTemplateMap
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{ deletable: false, toolTip: tooltiptemplate },
new go.Binding("text", "name"),
$(go.Shape, "Rectangle",
{
fill: "orange",
stroke: "black",
stretch: go.GraphObject.Fill,
alignment: go.Spot.Center
},
new go.Binding("fill", "gender", genderBrushConverter)),
$(go.Panel, "Vertical",
$(go.TextBlock,
{
font: "bold 8pt Helvetica, bold Arial, sans-serif",
alignment: go.Spot.Center,
margin: 6
},
new go.Binding("text", "name")),
$(go.TextBlock,
new go.Binding("text", "kanjiName"))
)
);
// define the Link template
myDiagram.linkTemplate =
$(go.Link, // the whole link panel
{ routing: go.Link.Orthogonal, corner: 5, selectable: false },
$(go.Shape)); // the default black link shape
// here's the family data
var nodeDataArray = [
{ key: 0, name: "Osahito", gender: "M", fullTitle: "Emperor Kōmei", kanjiName: "統仁 孝明天皇", posthumousName: "Komei", birthYear: "1831", deathYear: "1867" },
{ key: 1, parent: 0, name: "Matsuhito", gender: "M", fullTitle: "Emperor Meiji", kanjiName: "睦仁 明治天皇", posthumousName: "Meiji", birthYear: "1852", deathYear: "1912" },
{ key: 2, parent: 1, name: "Toshiko", gender: "F", fullTitle: "Princess Yasu-no-Miya Toshiko", birthYear: "1896", deathYear: "1978", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 3, parent: 2, name: "Higashikuni Morihiro", gender: "M", fullTitle: "Prince Higashikuni Morihiro", kanjiName: "東久邇宮 盛厚王", birthYear: "1916", deathYear: "1969", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 4, parent: 3, name: "See spouse for descendants" },
{ key: 5, parent: 2, name: "Moromasa", gender: "M", fullTitle: "Prince Moromasa", kanjiName: "師正王", birthYear: "1917", deathYear: "1923" },
{ key: 6, parent: 2, name: "Akitsune", gender: "M", fullTitle: "Prince Akitsune", kanjiName: "彰常王", birthYear: "1920", deathYear: "2006", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 7, parent: 2, name: "Toshihiko", gender: "M", fullTitle: "Prince Toshihiko", kanjiName: "俊彦王", birthYear: "1929", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 8, parent: 1, name: "Yoshihito", gender: "M", fullTitle: "Emperor Taishō", kanjiName: "嘉仁 大正天皇,", posthumousName: "Taisho", birthYear: "1879", deathYear: "1926" },
{ key: 9, parent: 8, name: "Hirohito", gender: "M", fullTitle: "Emperor Showa", kanjiName: "裕仁 昭和天皇", posthumousName: "Showa", birthYear: "1901", deathYear: "1989" },
{ key: 10, parent: 9, name: "Higashikuni Shigeko", gender: "F", spouse: "Higashikuni Morihiro", spouseKanji: "東久邇宮 盛厚王", fullTitle: "Princess Shigeko Higashikuni", kanjiName: "東久邇成子", birthYear: "1925", deathYear: "1961", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 11, parent: 10, name: "Higashikuni Nobuhiko", gender: "M", fullTitle: "Prince Higashikuni Nobuhiko", kanjiName: "東久邇宮 信彦王", birthYear: "1945", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 12, parent: 11, name: "Higashikuni Yukihiko", gender: "M", fullTitle: "No Title", birthYear: "1974" },
{ key: 13, parent: 10, name: "Higashikuni Fumiko", gender: "F", fullTitle: "Princess Higashikuni Fumiko", kanjiName: "文子女王", birthYear: "1946", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 14, parent: 10, name: "Higashikuni Naohiko", gender: "M", fullTitle: "No Title", kanjiName: "東久邇真彦", birthYear: "1948" },
{ key: 15, parent: 14, name: "Higashikuni Teruhiko", gender: "M", fullTitle: "No Title" },
{ key: 16, parent: 14, name: "Higashikuni Matsuhiko", gender: "M", fullTitle: "No Title" },
{ key: 17, parent: 10, name: "Higashikuni Hidehiko", gender: "M", fullTitle: "No Title", kanjiName: "東久邇基博", birthYear: "1949" },
{ key: 18, parent: 10, name: "Higashikuni Yuko", gender: "F", fullTitle: "No Title", kanjiName: "東久邇優子", birthYear: "1950" },
{ key: 19, parent: 9, name: "Sachiko", gender: "F", fullTitle: "Princess Sachiko", kanjiName: "久宮祐子", birthYear: "1927", deathYear: "1928" },
{ key: 20, parent: 9, name: "Kazuko Takatsukasa", gender: "F", fullTitle: "Kazuko, Princess Taka", kanjiName: "鷹司 和子", birthYear: "1929", deathYear: "1989", statusChange: "In 1950, lost imperial family status by marrying a commoner" },
{ key: 21, parent: 9, name: "Atsuko Ikeda", gender: "F", fullTitle: "Atsuko, Princess Yori", kanjiName: "池田厚子", birthYear: "1931", statusChange: "In 1952, lost imperial family status by marrying a commoner" },
{ key: 22, parent: 9, name: "Akihito", gender: "M", fullTitle: "Reigning Emperor of Japan; Tennō", kanjiName: "明仁 今上天皇", posthumousName: "Heisei", birthYear: "1933" },
{ key: 23, parent: 22, name: "Naruhito", gender: "M", fullTitle: "Naruhito, Crown Prince of Japan", kanjiName: "皇太子徳仁親王", orderInSuccession: "1", birthYear: "1960" },
{ key: 24, parent: 23, name: "Aiko", gender: "F", fullTitle: "Aiko, Princess Toshi", kanjiName: "敬宮愛子内親王", birthYear: "2001" },
{ key: 25, parent: 22, name: "Fumihito", gender: "M", fullTitle: "Fumihito, Prince Akishino", kanjiName: "秋篠宮文仁親王", orderInSuccession: "2", birthYear: "1965" },
{ key: 26, parent: 25, name: "Mako", gender: "F", fullTitle: "Princess Mako of Akishino", kanjiName: "眞子内親王", birthYear: "1991" },
{ key: 27, parent: 25, name: "Kako", gender: "F", fullTitle: "Princess Kako of Akishino", kanjiName: "佳子内親王", birthYear: "1994" },
{ key: 28, parent: 25, name: "Hisahito", gender: "M", fullTitle: "Prince Hisahito of Akishino", kanjiName: "悠仁親王", orderInSuccession: "3", birthYear: "2006" },
{ key: 29, parent: 22, name: "Sayako Kuroda", gender: "F", fullTitle: "Princess Sayako of Japan", kanjiName: "黒田清子", birthYear: "1969", statusChange: "In 2005, lost imperial family status by marrying a commoner" },
{ key: 30, parent: 9, name: "Masahito", gender: "M", fullTitle: "Masahito, Prince Hitachi", kanjiName: "常陸宮正仁親王", orderInSuccession: "4", birthYear: "1935" },
{ key: 31, parent: 9, name: "Takako Shimazu", gender: "F", fullTitle: "Princess Takako", kanjiName: "島津貴子", birthYear: "1939", statusChange: "In 1960, lost imperial family status by marrying a commoner" },
{ key: 32, parent: 31, name: "Yorihisa Shimazu", gender: "M", fullTitle: "No Title", birthYear: "1962" },
{ key: 33, parent: 8, name: "Yasuhito", gender: "M", fullTitle: "Yasuhito, Prince Chichibu of Japan", kanjiName: "秩父宮 雍仁", birthYear: "1902", deathYear: "1953" },
{ key: 34, parent: 8, name: "Nobuhito", gender: "M", fullTitle: "Nobuhito, Prince Takamatsu", kanjiName: "高松宮宣仁親王", birthYear: "1905", deathYear: "1987" },
{ key: 35, parent: 8, name: "Takahito", gender: "M", fullTitle: "Takahito, Prince Mikasa", kanjiName: "三笠宮崇仁親王", orderInSuccession: "5", birthYear: "1915" },
{ key: 36, parent: 35, name: "Yasuko Konoe", gender: "F", fullTitle: "Princess Yasuko of Mikasa", kanjiName: "甯子内親王", birthYear: "1944", statusChange: "In 1966, lost imperial family stutus by marrying a commoner" },
{ key: 37, parent: 36, name: "Tadahiro", gender: "M", fullTitle: "None" },
{ key: 38, parent: 35, name: "Tomihito", gender: "M", fullTitle: "Prince Tomohito of Mikasa", kanjiName: "三笠宮寬仁", orderInSuccession: "6", birthYear: "1946" },
{ key: 39, parent: 38, name: "Akiko", gender: "F", fullTitle: "Princess Akiko of Mikasa", kanjiName: "彬子女王", birthYear: "1981" },
{ key: 40, parent: 38, name: "Yoko", gender: "F", fullTitle: "Princess Yoko of Mikasa", kanjiName: "瑶子女王", birthYear: "1983" },
{ key: 41, parent: 35, name: "Yoshihito", gender: "M", fullTitle: "Yoshihito, Prince Katsura", kanjiName: "桂宮 宜仁親王", orderInSuccession: "7", birthYear: "1948" },
{ key: 42, parent: 35, name: "Masako Sen", gender: "F", fullTitle: "Princess Masako of Mikasa", kanjiName: "容子内親王", birthYear: "1951", statusChange: "In 1983, lost imperial family status by marrying a commoner" },
{ key: 43, parent: 42, name: "Akifumi", gender: "M", fullTitle: "No Title" },
{ key: 44, parent: 42, name: "Takafumi", gender: "M", fullTitle: "No Title" },
{ key: 45, parent: 42, name: "Makiko", gender: "F", fullTitle: "No Title" },
{ key: 46, parent: 35, name: "Norihito", gender: "M", fullTitle: "Norihito, Prince Takamado", kanjiName: "高円宮憲仁親王", birthYear: "1954", deathYear: "2002" },
{ key: 47, parent: 46, name: "Tsuguko", gender: "F", fullTitle: "Princess Tsuguko of Takamado", kanjiName: "承子女王", birthYear: "1986" },
{ key: 48, parent: 46, name: "Noriko", gender: "F", fullTitle: "Princess Noriko of Takamado", kanjiName: "典子女王", birthYear: "1988" },
{ key: 49, parent: 46, name: "Ayako", gender: "F", fullTitle: "Princess Ayako of Takamado", kanjiName: "絢子女王", birthYear: "1990" },
{ key: 50, parent: 1, name: "Masako", gender: "F", fullTitle: "Princess Masako of Tsune", birthYear: "1888", deathYear: "1940" },
{ key: 51, parent: 50, name: "Takeda Tsuneyoshi", gender: "M", fullTitle: "Prince Takeda Tsunehisa", kanjiName: "竹田宮恒徳王", birthYear: "1909", deathYear: "1992", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 52, parent: 51, name: "Takeda Tsunetada", gender: "M", fullTitle: "Prince Takeda Tsunetada", kanjiName: "竹田恒正王", birthYear: "1940", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 53, parent: 52, name: "Takeda Tsunetaka", gender: "M", fullTitle: "No Title", birthYear: "1967" },
{ key: 54, parent: 52, name: "Takeda Hiroko", gender: "M", fullTitle: "No Title", birthYear: "1971" },
{ key: 55, parent: 51, name: "Takeda Motoko", gender: "F", fullTitle: "Princess Takeda Motoko", kanjiName: "素子女王", birthYear: "1942", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 56, parent: 51, name: "Takeda Tsunekazu", gender: "M", fullTitle: "No Title", kanjiName: "竹田恒和王", birthYear: "1944", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 57, parent: 51, name: "Takeda Noriko", gender: "F", fullTitle: "Princess Takeda Noriko", kanjiName: "紀子女王", birthYear: "1943", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 58, parent: 51, name: "Tsuneharu Takeda", gender: "M", fullTitle: "Prince Tsuneharu Takeda", kanjiName: "竹田恒治王", birthYear: "1945", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 59, parent: 50, name: "Takeda Ayako", gender: "F", fullTitle: "Princess Tsune-no-Miya Takeda Ayako", kanjiName: "禮子女王", birthYear: "1911", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 60, parent: 1, name: "Fusako", gender: "F", fullTitle: "Princess Fusako of Kane", birthYear: "1890", deathYear: "1974" },
{ key: 61, parent: 60, name: "Kitashirakawa Nagahisa", gender: "M", fullTitle: "Prince Kitashirakawa Nagahisa", kanjiName: "北白川宮永久王", birthYear: "1910", deathYear: "1940" },
{ key: 62, parent: 61, name: "Kitashirakawa Michihisa", gender: "M", fullTitle: "Prince Kitashirakawa Michihisa", birthYear: "1937", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 63, parent: 62, name: "Kitashirakawa Naoko", gender: "F", fullTitle: "No Title", birthYear: "1969" },
{ key: 64, parent: 62, name: "Kitashirakawa Nobuko", gender: "F", fullTitle: "No Title", birthYear: "1971" },
{ key: 65, parent: 62, name: "Kitashirakawa Akiko", gender: "F", fullTitle: "No Title", birthYear: "1973" },
{ key: 66, parent: 61, name: "Hatsuko", gender: "F", fullTitle: "Princess Hatsuko", birthYear: "1939", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 67, parent: 60, name: "Kitashirakawa Mineko", gender: "F", fullTitle: "Princess Kitashirakawa Mineko", kanjiName: "美年子女王", birthYear: "1910", deathYear: "1970", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 68, parent: 60, name: "Kitashirakawa Sawako", gender: "F", fullTitle: "Princess Kitashirakawa Sawako", kanjiName: "佐和子女王", birthYear: "1913", deathYear: "2001", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 69, parent: 60, name: "Kitashirakawa Taeko", gender: "F", fullTitle: "Princess Kitashirakawa Taeko", kanjiName: "多惠子女王", birthYear: "1920", deathYear: "1954", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 70, parent: 1, name: "Nobuko", gender: "F", fullTitle: "Princess Fumi-no-Miya Nobuko", birthYear: "1891", deathYear: "1933" },
{ key: 71, parent: 70, name: "Asaka Kikuko", gender: "F", fullTitle: "Princess Asaka Kikuko", kanjiName: "紀久子", birthYear: "1911", deathYear: "1989", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 72, parent: 70, name: "Asaka Takahiko", gender: "M", fullTitle: "Prince Asaka Takahiko", kanjiName: "朝香 孚彦", birthYear: "1913", deathYear: "1994", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 73, parent: 72, name: "Fukuko", gender: "F", fullTitle: "No Title" },
{ key: 74, parent: 72, name: "Minoko", gender: "F", fullTitle: "No Title" },
{ key: 75, parent: 72, name: "Tomohiko", gender: "M", fullTitle: "No Title" },
{ key: 76, parent: 70, name: "Asaka Tadahito", gender: "M", fullTitle: "Prince Asaka Tadahito", kanjiName: "朝香正彦", birthYear: "1914", deathYear: "1944" },
{ key: 77, parent: 70, name: "Asaka Kiyoko", gender: "F", fullTitle: "Princess Asaka Kiyoko", kanjiName: "湛子", birthYear: "1919", statusChange: "In 1947, lost imperial family status due to American abrogation of Japanese nobility" },
{ key: 78, parent: 1, name: "Ten Other Children Not Surviving Infancy" },
{ key: 79, parent: 0, name: "Five Other Children Not Surviving Infancy" }
];
// create the model for the family tree
myDiagram.model = new go.TreeModel(nodeDataArray);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="background-color: white; border: solid 1px black; width: 100%; height: 600px"></div>
<p>For a variation of this tree, see the <a href="familyTree.html">British family tree sample</a>.</p>
<p>For a more complex family tree see the <a href="genogram.html">genogram sample</a>.</p>
</div>
</body>
</html>
+158
View File
@@ -0,0 +1,158 @@
<!DOCTYPE html>
<html>
<head>
<title>Fault Tree</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A Fault Tree diagram showing gate shapes at each non-root node." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../extensions/Figures.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
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
allowCopy: false,
allowDelete: false,
"draggingTool.dragsTree": true,
layout:
$(go.TreeLayout,
{ angle: 90, layerSpacing: 30 }),
"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);
}
});
function nodeFillConverter(figure) {
switch (figure) {
case "AndGate":
// right to left so when it's rotated, it goes from top to bottom
return $(go.Brush, "Linear", { 0: "#EA8100", 1: "#C66D00", start: go.Spot.Right, end: go.Spot.Left });
case "OrGate":
return $(go.Brush, "Linear", { 0: "#0058D3", 1: "#004FB7", start: go.Spot.Right, end: go.Spot.Left });
case "Circle":
return $(go.Brush, "Linear", { 0: "#009620", 1: "#007717" });
case "Triangle":
return $(go.Brush, "Linear", { 0: "#7A0099", 1: "#63007F" });
default:
return "whitesmoke";
}
}
myDiagram.nodeTemplate = // the default node template
$(go.Node, "Spot",
{ selectionObjectName: "BODY", locationSpot: go.Spot.Center, locationObjectName: "BODY" },
// the main "BODY" consists of a Rectangle surrounding some text
$(go.Panel, "Auto",
{ name: "BODY", portId: "" },
$(go.Shape,
{ fill: $(go.Brush, "Linear", { 0: "#770000", 1: "#600000" }), stroke: null }),
$(go.TextBlock,
{
margin: new go.Margin(2, 10, 1, 10), maxSize: new go.Size(100, NaN),
stroke: "whitesmoke", font: "10pt Segoe UI, sans-serif"
},
new go.Binding("text"))
), // end "BODY", an Auto Panel
$("TreeExpanderButton", { alignment: go.Spot.Right, alignmentFocus: go.Spot.Left, "ButtonBorder.figure": "Rectangle" }),
$(go.Shape, "LineV",
new go.Binding("visible", "figure", function(f) { return f !== "None"; }),
{ strokeWidth: 1.5, height: 20, alignment: new go.Spot(0.5, 1, 0, -1), alignmentFocus: go.Spot.Top }),
$(go.Shape,
new go.Binding("visible", "figure", function(f) { return f !== "None"; }),
{
alignment: new go.Spot(0.5, 1, 0, 5), alignmentFocus: go.Spot.Top, width: 30, height: 30,
stroke: null
},
new go.Binding("figure"),
new go.Binding("fill", "figure", nodeFillConverter),
new go.Binding("angle", "figure", function(f) { return (f === "OrGate" || f === "AndGate") ? -90 : 0; })), // ORs and ANDs should point upwards
$(go.TextBlock,
new go.Binding("visible", "figure", function(f) { return f !== "None"; }), // if we don't have a figure, don't display any choice text
{
alignment: new go.Spot(0.5, 1, 20, 20), alignmentFocus: go.Spot.Left,
stroke: "black", font: "10pt Segoe UI, sans-serif"
},
new go.Binding("text", "choice"))
);
myDiagram.linkTemplate =
$(go.Link, go.Link.Orthogonal,
{ layerName: "Background", curviness: 20, corner: 5 },
$(go.Shape,
{ strokeWidth: 1.5 })
);
load();
}
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 id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
<p>
<em>Fault trees</em> are used to conduct deductive failure analysis in which an undesired state of a
system is analyzed using Boolean logic to combine a series of lower-level events.
</p>
<p>
This diagram uses a basic <a>TreeModel</a> and <a>TreeLayout</a> to layout nodes in a tree structure.
The <a>Diagram.nodeTemplate</a> definition allows for text describing the undesirable states and,
when necessary, a figure indicating an event/gate.
</p>
<p>
The <b>visible</b> property on some of the node template's <a>Shape</a>s is set based on
whether a figure is chosen for the node in the <a>Model.nodeDataArray</a>. The nodes also
display a <b>TreeExpanderButton</b> allowing for expanding/collapsing of subtrees.
See the <a href="../intro/buttons.html">Intro page on Buttons</a> for more GoJS button information.
</p>
<p>
Related to deductive failure analysis is root cause analysis, or RCA. See the <a href="../extensions/Fishbone.html">fishbone layout</a>
extension page for a diagram format typically used in root cause analysis.
</p>
<div>
<div>
<button id="SaveButton" onclick="save()">Save</button>
<button onclick="load()">Load</button>
Diagram Model saved in JSON format:
</div>
<textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "go.TreeModel",
"nodeDataArray": [
{"key":1, "text":"No flow to receiver", "figure":"None"},
{"key":2, "text":"No flow from Component B", "parent":1, "figure":"OrGate", "choice":"G02"},
{"key":3, "text":"No flow into Component B", "parent":2, "figure":"AndGate", "choice":"G03"},
{"key":4, "text":"Component B blocks flow", "parent":2, "figure":"Circle", "choice":"B01"},
{"key":5, "text":"No flow from Component A1", "parent":3, "figure":"OrGate", "choice":"G04"},
{"key":6, "text":"No flow from Component A2", "parent":3, "figure":"OrGate", "choice":"G05"},
{"key":7, "text":"No flow from source1", "parent":5, "figure":"Triangle", "choice":"T01"},
{"key":8, "text":"Component A1 blocks flow", "parent":5, "figure":"Circle", "fill":"green", "choice":"B02"},
{"key":9, "text":"No flow from source2", "parent":6, "figure":"Triangle", "choice":"T02"},
{"key":10, "text":"Component A2 blocks flow", "parent":6, "figure":"Circle", "choice":"B03"}
]}
</textarea>
</div>
</div>
</body>
</html>
+236
View File
@@ -0,0 +1,236 @@
<!DOCTYPE html>
<html>
<head>
<title>Force Directed Layout</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Interactive demonstration of physics layout features by the ForceDirectedLayout class." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<script id="code">
// define a custom ForceDirectedLayout for this sample
function DemoForceDirectedLayout() {
go.ForceDirectedLayout.call(this);
}
go.Diagram.inherit(DemoForceDirectedLayout, go.ForceDirectedLayout);
// Override the makeNetwork method to also initialize
// ForceDirectedVertex.isFixed from the corresponding Node.isSelected.
DemoForceDirectedLayout.prototype.makeNetwork = function(coll) {
// call base method for standard behavior
var net = go.ForceDirectedLayout.prototype.makeNetwork.call(this, coll);
net.vertexes.each(function(vertex) {
var node = vertex.node;
if (node !== null) vertex.isFixed = node.isSelected;
});
return net;
};
// end DemoForceDirectedLayout class
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", // must be the ID or reference to div
{
initialAutoScale: go.Diagram.Uniform, // zoom to make everything fit in the viewport
layout: new DemoForceDirectedLayout() // use custom layout
// other Layout properties are set by the layout function, defined below
});
// define the Node template
myDiagram.nodeTemplate =
$(go.Node, "Spot",
// make sure the Node.location is different from the Node.position
{ locationSpot: go.Spot.Center },
new go.Binding("text", "text"), // for sorting
$(go.Shape, "Ellipse",
{
fill: "lightgray",
stroke: null,
desiredSize: new go.Size(30, 30)
},
new go.Binding("fill", "fill")),
$(go.TextBlock,
new go.Binding("text", "text"))
);
// define the Link template
myDiagram.linkTemplate =
$(go.Link,
{ selectable: false },
$(go.Shape,
{ strokeWidth: 3, stroke: "#333" }));
// generate a tree using the default values
rebuildGraph();
}
function rebuildGraph() {
var minNodes = document.getElementById("minNodes").value;
minNodes = parseInt(minNodes, 10);
var maxNodes = document.getElementById("maxNodes").value;
maxNodes = parseInt(maxNodes, 10);
var minChil = document.getElementById("minChil").value;
minChil = parseInt(minChil, 10);
var maxChil = document.getElementById("maxChil").value;
maxChil = parseInt(maxChil, 10);
generateTree(minNodes, maxNodes, minChil, maxChil);
}
function generateTree(minNodes, maxNodes, minChil, maxChil) {
myDiagram.startTransaction("generateTree");
// replace the diagram's model's nodeDataArray
generateNodes(minNodes, maxNodes);
// replace the diagram's model's linkDataArray
generateLinks(minChil, maxChil);
// perform a diagram layout with the latest parameters
layout();
myDiagram.commitTransaction("generateTree");
}
// Creates a random number of randomly colored nodes.
function generateNodes(min, max) {
var nodeArray = [];
if (isNaN(min) || min < 0) min = 0;
if (isNaN(max) || max < min) max = min;
var numNodes = Math.floor(Math.random() * (max - min + 1)) + min;
for (var i = 0; i < numNodes; i++) {
nodeArray.push({
key: i,
text: i.toString(),
fill: go.Brush.randomColor()
});
}
// randomize the node data
for (i = 0; i < nodeArray.length; i++) {
var swap = Math.floor(Math.random() * nodeArray.length);
var temp = nodeArray[swap];
nodeArray[swap] = nodeArray[i];
nodeArray[i] = temp;
}
// set the nodeDataArray to this array of objects
myDiagram.model.nodeDataArray = nodeArray;
}
// Takes the random collection of nodes and creates a random tree with them.
// Respects the minimum and maximum number of links from each node.
// (The minimum can be disregarded if we run out of nodes to link to)
function generateLinks(min, max) {
if (myDiagram.nodes.count < 2) return;
if (isNaN(min) || min < 1) min = 1;
if (isNaN(max) || max < min) max = min;
var linkArray = [];
// make two Lists of nodes to keep track of where links already exist
var nit = myDiagram.nodes;
var nodes = new go.List(/*go.Node*/);
nodes.addAll(nit);
var available = new go.List(/*go.Node*/);
available.addAll(nodes);
for (var i = 0; i < nodes.length; i++) {
var next = nodes.get(i);
available.remove(next)
var children = Math.floor(Math.random() * (max - min + 1)) + min;
for (var j = 1; j <= children; j++) {
if (available.length === 0) break;
var to = available.get(0);
available.remove(to);
// get keys from the Node.text strings
var nextKey = parseInt(next.text, 10);
var toKey = parseInt(to.text, 10);
linkArray.push({ from: nextKey, to: toKey });
}
}
myDiagram.model.linkDataArray = linkArray;
}
// Update the layout from the controls.
// Changing the properties will invalidate the layout.
function layout() {
myDiagram.startTransaction("changed Layout");
var lay = myDiagram.layout;
var maxIter = document.getElementById("maxIter").value;
maxIter = parseInt(maxIter, 10);
lay.maxIterations = maxIter;
var epsilon = document.getElementById("epsilon").value;
epsilon = parseFloat(epsilon, 10);
lay.epsilonDistance = epsilon;
var infinity = document.getElementById("infinity").value;
infinity = parseFloat(infinity, 10);
lay.infinityDistance = infinity;
var arrangement = document.getElementById("arrangement").value;
var arrangementSpacing = new go.Size();
arrangement = arrangement.split(" ", 2);
arrangementSpacing.width = parseFloat(arrangement[0], 10);
arrangementSpacing.height = parseFloat(arrangement[1], 10);
lay.arrangementSpacing = arrangementSpacing;
var charge = document.getElementById("charge").value;
charge = parseFloat(charge, 10);
lay.defaultElectricalCharge = charge;
var mass = document.getElementById("mass").value;
mass = parseFloat(mass, 10);
lay.defaultGravitationalMass = mass;
var stiffness = document.getElementById("stiffness").value;
stiffness = parseFloat(stiffness, 10);
lay.defaultSpringStiffness = stiffness;
var length = document.getElementById("length").value;
length = parseFloat(length, 10);
lay.defaultSpringLength = length;
myDiagram.commitTransaction("changed Layout");
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div style="margin-bottom: 5px; padding: 5px; background-color: aliceblue">
<span style="display: inline-block; vertical-align: top; padding: 5px">
<b>New Tree</b><br />
MinNodes: <input type="text" size="3" id="minNodes" value="20" /><br />
MaxNodes: <input type="text" size="3" id="maxNodes" value="100" /><br />
MinChildren: <input type="text" size="3" id="minChil" value="1" /><br />
MaxChildren: <input type="text" size="3" id="maxChil" value="10" /><br />
<button type="button" onclick="rebuildGraph()">Generate Tree</button>
</span>
<span style="display: inline-block; vertical-align: top; padding: 5px">
<b>ForceDirectedLayout Properties</b><br />
Max Iterations: <input type="text" size="5" id="maxIter" value="100" onchange="layout()" /><br />
Epsilon: <input type="text" size="5" id="epsilon" value="1" onchange="layout()" /><br />
Infinity: <input type="text" size="5" id="infinity" value="1000" onchange="layout()" /><br />
ArrangementSpacing: <input type="text" size="8" id="arrangement" value="100 100" onchange="layout()" /><br />
</span>
<span style="display: inline-block; vertical-align: top; padding: 5px">
<b>Vertex Properties</b><br />
Electrical Charge: <input type="text" size="5" id="charge" value="150" onchange="layout()" /><br />
Gravitational Mass: <input type="text" size="5" id="mass" value="0" onchange="layout()" /><br />
</span>
<span style="display: inline-block; vertical-align: top; padding: 5px">
<b>Edge Properties</b><br />
Spring Stiffness: <input type="text" size="5" id="stiffness" value="0.05" onchange="layout()" /><br />
Spring Length: <input type="text" size="5" id="length" value="50" onchange="layout()" /><br />
</span>
</div>
<div id="myDiagramDiv" style="background: white; border: solid 1px black; width: 100%; height: 500px"></div>
<p>
For information on <b>ForceDirectedLayout</b> and its properties, see the <a>ForceDirectedLayout</a> documentation page.
</p>
</div>
</body>
</html>
+389
View File
@@ -0,0 +1,389 @@
<!DOCTYPE html>
<html>
<head>
<title>Flow Builder</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="An editor of flow diagrams that supports deletion by dropping onto a particular node and relinking by dragging a link." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
allowCopy: false,
layout:
$(go.LayeredDigraphLayout,
{
setsPortSpots: false, // Links already know their fromSpot and toSpot
columnSpacing: 5,
isInitial: false,
isOngoing: false
}),
validCycle: go.Diagram.CycleNotDirected,
"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 graygrad = $(go.Brush, "Linear",
{ 0: "white", 0.1: "whitesmoke", 0.9: "whitesmoke", 1: "lightgray" });
myDiagram.nodeTemplate = // the default node template
$(go.Node, "Spot",
{ selectionAdorned: false, textEditable: true, locationObjectName: "BODY" },
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
// the main body consists of a Rectangle surrounding the text
$(go.Panel, "Auto",
{ name: "BODY" },
$(go.Shape, "Rectangle",
{ fill: graygrad, stroke: "gray", minSize: new go.Size(120, 21) },
new go.Binding("fill", "isSelected", function(s) { return s ? "dodgerblue" : graygrad; }).ofObject()),
$(go.TextBlock,
{
stroke: "black", font: "12px sans-serif", editable: true,
margin: new go.Margin(3, 3 + 11, 3, 3 + 4), alignment: go.Spot.Left
},
new go.Binding("text").makeTwoWay())
),
// output port
$(go.Panel, "Auto",
{ alignment: go.Spot.Right, portId: "from", fromLinkable: true, cursor: "pointer", click: addNodeAndLink },
$(go.Shape, "Circle",
{ width: 22, height: 22, fill: "white", stroke: "dodgerblue", strokeWidth: 3 }),
$(go.Shape, "PlusLine",
{ width: 11, height: 11, fill: null, stroke: "dodgerblue", strokeWidth: 3 })
),
// input port
$(go.Panel, "Auto",
{ alignment: go.Spot.Left, portId: "to", toLinkable: true },
$(go.Shape, "Circle",
{ width: 8, height: 8, fill: "white", stroke: "gray" }),
$(go.Shape, "Circle",
{ width: 4, height: 4, fill: "dodgerblue", stroke: null })
)
);
myDiagram.nodeTemplate.contextMenu =
$("ContextMenu",
$("ContextMenuButton",
$(go.TextBlock, "Rename"),
{ click: function(e, obj) { e.diagram.commandHandler.editTextBlock(); } },
new go.Binding("visible", "", function(o) { return o.diagram && o.diagram.commandHandler.canEditTextBlock(); }).ofObject()),
// add one for Editing...
$("ContextMenuButton",
$(go.TextBlock, "Delete"),
{ click: function(e, obj) { e.diagram.commandHandler.deleteSelection(); } },
new go.Binding("visible", "", function(o) { return o.diagram && o.diagram.commandHandler.canDeleteSelection(); }).ofObject())
);
myDiagram.nodeTemplateMap.add("Loading",
$(go.Node, "Spot",
{ selectionAdorned: false, textEditable: true, locationObjectName: "BODY" },
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
// the main body consists of a Rectangle surrounding the text
$(go.Panel, "Auto",
{ name: "BODY" },
$(go.Shape, "Rectangle",
{ fill: graygrad, stroke: "gray", minSize: new go.Size(120, 21) },
new go.Binding("fill", "isSelected", function(s) { return s ? "dodgerblue" : graygrad; }).ofObject()),
$(go.TextBlock,
{
stroke: "black", font: "12px sans-serif", editable: true,
margin: new go.Margin(3, 3 + 11, 3, 3 + 4), alignment: go.Spot.Left
},
new go.Binding("text", "text"))
),
// output port
$(go.Panel, "Auto",
{ alignment: go.Spot.Right, portId: "from", fromLinkable: true, click: addNodeAndLink },
$(go.Shape, "Circle",
{ width: 22, height: 22, fill: "white", stroke: "dodgerblue", strokeWidth: 3 }),
$(go.Shape, "PlusLine",
{ width: 11, height: 11, fill: null, stroke: "dodgerblue", strokeWidth: 3 })
)
));
myDiagram.nodeTemplateMap.add("End",
$(go.Node, "Spot",
{ selectionAdorned: false, textEditable: true, locationObjectName: "BODY" },
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
// the main body consists of a Rectangle surrounding the text
$(go.Panel, "Auto",
{ name: "BODY" },
$(go.Shape, "Rectangle",
{ fill: graygrad, stroke: "gray", minSize: new go.Size(120, 21) },
new go.Binding("fill", "isSelected", function(s) { return s ? "dodgerblue" : graygrad; }).ofObject()),
$(go.TextBlock,
{
stroke: "black", font: "12px sans-serif", editable: true,
margin: new go.Margin(3, 3 + 11, 3, 3 + 4), alignment: go.Spot.Left
},
new go.Binding("text", "text"))
),
// input port
$(go.Panel, "Auto",
{ alignment: go.Spot.Left, portId: "to", toLinkable: true },
$(go.Shape, "Circle",
{ width: 8, height: 8, fill: "white", stroke: "gray" }),
$(go.Shape, "Circle",
{ width: 4, height: 4, fill: "dodgerblue", stroke: null })
)
));
// dropping a node on this special node will cause the selection to be deleted;
// linking or relinking to this special node will cause the link to be deleted
myDiagram.nodeTemplateMap.add("Recycle",
$(go.Node, "Auto",
{
portId: "to", toLinkable: true, deletable: false,
layerName: "Background", locationSpot: go.Spot.Center
},
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
{ dragComputation: function(node, pt, gridpt) { return pt; } },
{ mouseDrop: function(e, obj) { myDiagram.commandHandler.deleteSelection(); } },
$(go.Shape,
{ fill: "lightgray", stroke: "gray" }),
$(go.TextBlock, "Drop Here\nTo Delete",
{ margin: 5, textAlign: "center" })
));
// this is a click event handler that adds a node and a link to the diagram,
// connecting with the node on which the click occurred
function addNodeAndLink(e, obj) {
var fromNode = obj.part;
var diagram = fromNode.diagram;
diagram.startTransaction("Add State");
// get the node data for which the user clicked the button
var fromData = fromNode.data;
// create a new "State" data object, positioned off to the right of the fromNode
var p = fromNode.location.copy();
p.x += diagram.toolManager.draggingTool.gridSnapCellSize.width;
var toData = {
text: "new",
loc: go.Point.stringify(p)
};
// 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 = {
from: model.getKeyForNodeData(fromData),
to: 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);
// snap the new node to a valid location
newnode.location = diagram.toolManager.draggingTool.computeMove(newnode, p);
// then account for any overlap
shiftNodesToEmptySpaces();
diagram.commitTransaction("Add State");
}
// Highlight ports when they are targets for linking or relinking.
var OldTarget = null; // remember the last highlit port
function highlight(port) {
if (OldTarget !== port) {
lowlight(); // remove highlight from any old port
OldTarget = port;
port.scale = 1.3; // highlight by enlarging
}
}
function lowlight() { // remove any highlight
if (OldTarget) {
OldTarget.scale = 1.0;
OldTarget = null;
}
}
// Connecting a link with the Recycle node removes the link
myDiagram.addDiagramListener("LinkDrawn", function(e) {
var link = e.subject;
if (link.toNode.category === "Recycle") myDiagram.remove(link);
lowlight();
});
myDiagram.addDiagramListener("LinkRelinked", function(e) {
var link = e.subject;
if (link.toNode.category === "Recycle") myDiagram.remove(link);
lowlight();
});
myDiagram.linkTemplate =
$(go.Link,
{ selectionAdorned: false, fromPortId: "from", toPortId: "to", relinkableTo: true },
$(go.Shape,
{ stroke: "gray", strokeWidth: 2 },
{
mouseEnter: function(e, obj) { obj.strokeWidth = 5; obj.stroke = "dodgerblue"; },
mouseLeave: function(e, obj) { obj.strokeWidth = 2; obj.stroke = "gray"; }
})
);
function commonLinkingToolInit(tool) {
// the temporary link drawn during a link drawing operation (LinkingTool) is thick and blue
tool.temporaryLink =
$(go.Link, { layerName: "Tool" },
$(go.Shape, { stroke: "dodgerblue", strokeWidth: 5 }));
// change the standard proposed ports feedback from blue rectangles to transparent circles
tool.temporaryFromPort.figure = "Circle";
tool.temporaryFromPort.stroke = null;
tool.temporaryFromPort.strokeWidth = 0;
tool.temporaryToPort.figure = "Circle";
tool.temporaryToPort.stroke = null;
tool.temporaryToPort.strokeWidth = 0;
// provide customized visual feedback as ports are targeted or not
tool.portTargeted = function(realnode, realport, tempnode, tempport, toend) {
if (realport === null) { // no valid port nearby
lowlight();
} else if (toend) {
highlight(realport);
}
};
}
var ltool = myDiagram.toolManager.linkingTool;
commonLinkingToolInit(ltool);
// do not allow links to be drawn starting at the "to" port
ltool.direction = go.LinkingTool.ForwardsOnly;
var rtool = myDiagram.toolManager.relinkingTool;
commonLinkingToolInit(rtool);
// change the standard relink handle to be a shape that takes the shape of the link
rtool.toHandleArchetype =
$(go.Shape,
{ isPanelMain: true, fill: null, stroke: "dodgerblue", strokeWidth: 5 });
// use a special DraggingTool to cause the dragging of a Link to start relinking it
myDiagram.toolManager.draggingTool = new DragLinkingTool();
// detect when dropped onto an occupied cell
myDiagram.addDiagramListener("SelectionMoved", shiftNodesToEmptySpaces);
function shiftNodesToEmptySpaces() {
myDiagram.selection.each(function(node) {
if (!(node instanceof go.Node)) return;
// look for Parts overlapping the node
while (true) {
var exist = myDiagram.findObjectsIn(node.actualBounds,
// only consider Parts
function(obj) { return obj.part; },
// ignore Links and the dropped node itself
function(part) { return part instanceof go.Node && part !== node; },
// check for any overlap, not complete containment
true).first();
if (exist === null) break;
// try shifting down beyond the existing node to see if there's empty space
node.position = new go.Point(node.actualBounds.x, exist.actualBounds.bottom + 10);
}
});
}
// prevent nodes from being dragged to the left of where the layout placed them
myDiagram.addDiagramListener("LayoutCompleted", function(e) {
myDiagram.nodes.each(function(node) {
if (node.category === "Recycle") return;
node.minLocation = new go.Point(node.location.x, -Infinity);
});
});
load(); // load initial diagram from the mySavedModel textarea
}
function save() {
document.getElementById("mySavedModel").value = myDiagram.model.toJson();
myDiagram.isModified = false;
}
function load() {
myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
// if any nodes don't have a real location, explicitly do a layout
if (myDiagram.nodes.any(function(n) { return !n.location.isReal(); })) layout();
}
function layout() {
myDiagram.layoutDiagram(true);
}
// Define a custom tool that changes a drag operation on a Link to a relinking operation,
// but that operates like a normal DraggingTool otherwise.
function DragLinkingTool() {
go.DraggingTool.call(this);
this.isGridSnapEnabled = true;
this.isGridSnapRealtime = false;
this.gridSnapCellSize = new go.Size(182, 1);
this.gridSnapOrigin = new go.Point(5.5, 0);
}
go.Diagram.inherit(DragLinkingTool, go.DraggingTool);
// Handle dragging a link specially -- by starting the RelinkingTool on that Link
DragLinkingTool.prototype.doActivate = function() {
var diagram = this.diagram;
if (diagram === null) return;
this.standardMouseSelect();
var main = this.currentPart; // this is set by the standardMouseSelect
if (main instanceof go.Link) { // maybe start relinking instead of dragging
var relinkingtool = diagram.toolManager.relinkingTool;
// tell the RelinkingTool to work on this Link, not what is under the mouse
relinkingtool.originalLink = main;
// start the RelinkingTool
diagram.currentTool = relinkingtool;
// can activate it right now, because it already has the originalLink to reconnect
relinkingtool.doActivate();
relinkingtool.doMouseMove();
} else {
go.DraggingTool.prototype.doActivate.call(this);
}
};
// end DragLinkingTool
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:500px"></div>
<button id="SaveButton" onclick="save()">Save</button>
<button onclick="load()">Load</button>
<button onclick="layout()">Do Layout</button>
<br />
<textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "go.GraphLinksModel",
"nodeDataArray": [
{ "key":1, "text":"Loading Screen", "category":"Loading" },
{ "key":2, "text":"Beginning" },
{ "key":3, "text":"Segment 1" },
{ "key":4, "text":"Segment 2" },
{ "key":5, "text":"Segment 3"},
{ "key":6, "text":"End Screen", "category":"End" },
{ "key":-2, "category": "Recycle" }
],
"linkDataArray": [
{ "from":1, "to":2 },
{ "from":2, "to":3 },
{ "from":2, "to":5 },
{ "from":3, "to":4 },
{ "from":4, "to":6 }
]
}
</textarea>
</div>
</body>
</html>
+381
View File
@@ -0,0 +1,381 @@
<!DOCTYPE html>
<html>
<head>
<title>Flowchart</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Interactive flowchart diagram implemented by GoJS in JavaScript for HTML." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<link href='https://fonts.googleapis.com/css?family=Lato:300,400,700' rel='stylesheet' type='text/css'>
<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
myDiagram =
$(go.Diagram, "myDiagramDiv", // must name or refer to the DIV HTML element
{
"LinkDrawn": showLinkLabel, // this DiagramEvent listener is defined below
"LinkRelinked": showLinkLabel,
"undoManager.isEnabled": true // enable undo & redo
});
// 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);
}
});
// helper definitions for node templates
function nodeStyle() {
return [
// The Node.location comes from the "loc" property of the node data,
// converted by the Point.parse static method.
// If the Node.location is changed, it updates the "loc" property of the node data,
// converting back using the Point.stringify static method.
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
{
// the Node.location is at the center of each node
locationSpot: go.Spot.Center
}
];
}
// Define a function for creating a "port" that is normally transparent.
// The "name" is used as the GraphObject.portId,
// the "align" is used to determine where to position the port relative to the body of the node,
// the "spot" is used to control how links connect with the port and whether the port
// stretches along the side of the node,
// and the boolean "output" and "input" arguments control whether the user can draw links from or to the port.
function makePort(name, align, spot, output, input) {
var horizontal = align.equals(go.Spot.Top) || align.equals(go.Spot.Bottom);
// the port is basically just a transparent rectangle that stretches along the side of the node,
// and becomes colored when the mouse passes over it
return $(go.Shape,
{
fill: "transparent", // changed to a color in the mouseEnter event handler
strokeWidth: 0, // no stroke
width: horizontal ? NaN : 8, // if not stretching horizontally, just 8 wide
height: !horizontal ? NaN : 8, // if not stretching vertically, just 8 tall
alignment: align, // align the port on the main Shape
stretch: (horizontal ? go.GraphObject.Horizontal : go.GraphObject.Vertical),
portId: name, // declare this object to be a "port"
fromSpot: spot, // declare where links may connect at this port
fromLinkable: output, // declare whether the user may draw links from here
toSpot: spot, // declare where links may connect at this port
toLinkable: input, // declare whether the user may draw links to here
cursor: "pointer", // show a different cursor to indicate potential link point
mouseEnter: function(e, port) { // the PORT argument will be this Shape
if (!e.diagram.isReadOnly) port.fill = "rgba(255,0,255,0.5)";
},
mouseLeave: function(e, port) {
port.fill = "transparent";
}
});
}
function textStyle() {
return {
font: "bold 11pt Lato, Helvetica, Arial, sans-serif",
stroke: "#F8F8F8"
}
}
// define the Node templates for regular nodes
myDiagram.nodeTemplateMap.add("", // the default category
$(go.Node, "Table", nodeStyle(),
// the main object is a Panel that surrounds a TextBlock with a rectangular Shape
$(go.Panel, "Auto",
$(go.Shape, "Rectangle",
{ fill: "#282c34", stroke: "#00A9C9", strokeWidth: 3.5 },
new go.Binding("figure", "figure")),
$(go.TextBlock, textStyle(),
{
margin: 8,
maxSize: new go.Size(160, NaN),
wrap: go.TextBlock.WrapFit,
editable: true
},
new go.Binding("text").makeTwoWay())
),
// four named ports, one on each side:
makePort("T", go.Spot.Top, go.Spot.TopSide, false, true),
makePort("L", go.Spot.Left, go.Spot.LeftSide, true, true),
makePort("R", go.Spot.Right, go.Spot.RightSide, true, true),
makePort("B", go.Spot.Bottom, go.Spot.BottomSide, true, false)
));
myDiagram.nodeTemplateMap.add("Conditional",
$(go.Node, "Table", nodeStyle(),
// the main object is a Panel that surrounds a TextBlock with a rectangular Shape
$(go.Panel, "Auto",
$(go.Shape, "Diamond",
{ fill: "#282c34", stroke: "#00A9C9", strokeWidth: 3.5 },
new go.Binding("figure", "figure")),
$(go.TextBlock, textStyle(),
{
margin: 8,
maxSize: new go.Size(160, NaN),
wrap: go.TextBlock.WrapFit,
editable: true
},
new go.Binding("text").makeTwoWay())
),
// four named ports, one on each side:
makePort("T", go.Spot.Top, go.Spot.Top, false, true),
makePort("L", go.Spot.Left, go.Spot.Left, true, true),
makePort("R", go.Spot.Right, go.Spot.Right, true, true),
makePort("B", go.Spot.Bottom, go.Spot.Bottom, true, false)
));
myDiagram.nodeTemplateMap.add("Start",
$(go.Node, "Table", nodeStyle(),
$(go.Panel, "Spot",
$(go.Shape, "Circle",
{ desiredSize: new go.Size(70, 70), fill: "#282c34", stroke: "#09d3ac", strokeWidth: 3.5 }),
$(go.TextBlock, "Start", textStyle(),
new go.Binding("text"))
),
// three named ports, one on each side except the top, all output only:
makePort("L", go.Spot.Left, go.Spot.Left, true, false),
makePort("R", go.Spot.Right, go.Spot.Right, true, false),
makePort("B", go.Spot.Bottom, go.Spot.Bottom, true, false)
));
myDiagram.nodeTemplateMap.add("End",
$(go.Node, "Table", nodeStyle(),
$(go.Panel, "Spot",
$(go.Shape, "Circle",
{ desiredSize: new go.Size(60, 60), fill: "#282c34", stroke: "#DC3C00", strokeWidth: 3.5 }),
$(go.TextBlock, "End", textStyle(),
new go.Binding("text"))
),
// three named ports, one on each side except the bottom, all input only:
makePort("T", go.Spot.Top, go.Spot.Top, false, true),
makePort("L", go.Spot.Left, go.Spot.Left, false, true),
makePort("R", go.Spot.Right, go.Spot.Right, false, true)
));
// taken from ../extensions/Figures.js:
go.Shape.defineFigureGenerator("File", function(shape, w, h) {
var geo = new go.Geometry();
var fig = new go.PathFigure(0, 0, true); // starting point
geo.add(fig);
fig.add(new go.PathSegment(go.PathSegment.Line, .75 * w, 0));
fig.add(new go.PathSegment(go.PathSegment.Line, w, .25 * h));
fig.add(new go.PathSegment(go.PathSegment.Line, w, h));
fig.add(new go.PathSegment(go.PathSegment.Line, 0, h).close());
var fig2 = new go.PathFigure(.75 * w, 0, false);
geo.add(fig2);
// The Fold
fig2.add(new go.PathSegment(go.PathSegment.Line, .75 * w, .25 * h));
fig2.add(new go.PathSegment(go.PathSegment.Line, w, .25 * h));
geo.spot1 = new go.Spot(0, .25);
geo.spot2 = go.Spot.BottomRight;
return geo;
});
myDiagram.nodeTemplateMap.add("Comment",
$(go.Node, "Auto", nodeStyle(),
$(go.Shape, "File",
{ fill: "#282c34", stroke: "#DEE0A3", strokeWidth: 3 }),
$(go.TextBlock, textStyle(),
{
margin: 8,
maxSize: new go.Size(200, NaN),
wrap: go.TextBlock.WrapFit,
textAlign: "center",
editable: true
},
new go.Binding("text").makeTwoWay())
// no ports, because no links are allowed to connect with a comment
));
// replace the default Link template in the linkTemplateMap
myDiagram.linkTemplate =
$(go.Link, // the whole link panel
{
routing: go.Link.AvoidsNodes,
curve: go.Link.JumpOver,
corner: 5, toShortLength: 4,
relinkableFrom: true,
relinkableTo: true,
reshapable: true,
resegmentable: true,
// mouse-overs subtly highlight links:
mouseEnter: function(e, link) { link.findObject("HIGHLIGHT").stroke = "rgba(30,144,255,0.2)"; },
mouseLeave: function(e, link) { link.findObject("HIGHLIGHT").stroke = "transparent"; },
selectionAdorned: false
},
new go.Binding("points").makeTwoWay(),
$(go.Shape, // the highlight shape, normally transparent
{ isPanelMain: true, strokeWidth: 8, stroke: "transparent", name: "HIGHLIGHT" }),
$(go.Shape, // the link path shape
{ isPanelMain: true, stroke: "gray", strokeWidth: 2 },
new go.Binding("stroke", "isSelected", function(sel) { return sel ? "dodgerblue" : "gray"; }).ofObject()),
$(go.Shape, // the arrowhead
{ toArrow: "standard", strokeWidth: 0, fill: "gray" }),
$(go.Panel, "Auto", // the link label, normally not visible
{ visible: false, name: "LABEL", segmentIndex: 2, segmentFraction: 0.5 },
new go.Binding("visible", "visible").makeTwoWay(),
$(go.Shape, "RoundedRectangle", // the label shape
{ fill: "#F8F8F8", strokeWidth: 0 }),
$(go.TextBlock, "Yes", // the label
{
textAlign: "center",
font: "10pt helvetica, arial, sans-serif",
stroke: "#333333",
editable: true
},
new go.Binding("text").makeTwoWay())
)
);
// Make link labels visible if coming out of a "conditional" node.
// This listener is called by the "LinkDrawn" and "LinkRelinked" DiagramEvents.
function showLinkLabel(e) {
var label = e.subject.findObject("LABEL");
if (label !== null) label.visible = (e.subject.fromNode.data.category === "Conditional");
}
// temporary links used by LinkingTool and RelinkingTool are also orthogonal:
myDiagram.toolManager.linkingTool.temporaryLink.routing = go.Link.Orthogonal;
myDiagram.toolManager.relinkingTool.temporaryLink.routing = go.Link.Orthogonal;
load(); // load an initial diagram from some JSON text
// initialize the Palette that is on the left side of the page
myPalette =
$(go.Palette, "myPaletteDiv", // must name or refer to the DIV HTML element
{
// Instead of the default animation, use a custom fade-down
"animationManager.initialAnimationStyle": go.AnimationManager.None,
"InitialAnimationStarting": animateFadeDown, // Instead, animate with this function
nodeTemplateMap: myDiagram.nodeTemplateMap, // share the templates used by myDiagram
model: new go.GraphLinksModel([ // specify the contents of the Palette
{ category: "Start", text: "Start" },
{ text: "Step" },
{ category: "Conditional", text: "???" },
{ category: "End", text: "End" },
{ category: "Comment", text: "Comment" }
])
});
// This is a re-implementation of the default animation, except it fades in from downwards, instead of upwards.
function animateFadeDown(e) {
var diagram = e.diagram;
var animation = new go.Animation();
animation.isViewportUnconstrained = true; // So Diagram positioning rules let the animation start off-screen
animation.easing = go.Animation.EaseOutExpo;
animation.duration = 900;
// Fade "down", in other words, fade in from above
animation.add(diagram, 'position', diagram.position.copy().offset(0, 200), diagram.position);
animation.add(diagram, 'opacity', 0, 1);
animation.start();
}
} // end init
// Show the diagram's model in JSON format that the user may edit
function save() {
document.getElementById("mySavedModel").value = myDiagram.model.toJson();
myDiagram.isModified = false;
}
function load() {
myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
}
// print the diagram by opening a new window holding SVG images of the diagram contents for each page
function printDiagram() {
var svgWindow = window.open();
if (!svgWindow) return; // failure to open a new Window
var printSize = new go.Size(700, 960);
var bnds = myDiagram.documentBounds;
var x = bnds.x;
var y = bnds.y;
while (y < bnds.bottom) {
while (x < bnds.right) {
var svg = myDiagram.makeSvg({ scale: 1.0, position: new go.Point(x, y), size: printSize });
svgWindow.document.body.appendChild(svg);
x += printSize.width;
}
x = bnds.x;
y += printSize.height;
}
setTimeout(function() { svgWindow.print(); }, 1);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div style="width: 100%; display: flex; justify-content: space-between">
<div id="myPaletteDiv" style="width: 100px; margin-right: 2px; background-color: #282c34;"></div>
<div id="myDiagramDiv" style="flex-grow: 1; height: 750px; background-color: #282c34;"></div>
</div>
<p>
The FlowChart sample demonstrates several key features of GoJS,
namely <a href="../intro/palette.html">Palette</a>s,
<a href="../intro/links.html">Linkable nodes</a>, Drag/Drop behavior,
<a href="../intro/textBlocks.html">Text Editing</a>, and the use of
<a href="../intro/templateMaps.html">Node Template Maps</a> in Diagrams.
</p>
<p>
Mouse-over a Node to view its ports.
Drag from these ports to create new Links.
Selecting Links allows you to re-shape and re-link them.
Selecting a Node and then clicking its TextBlock will allow
you to edit text (except on the Start and End Nodes).
</p>
<button id="SaveButton" onclick="save()">Save</button>
<button onclick="load()">Load</button>
Diagram Model saved in JSON format:
<textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "go.GraphLinksModel",
"linkFromPortIdProperty": "fromPort",
"linkToPortIdProperty": "toPort",
"nodeDataArray": [
{"category":"Comment", "loc":"360 -10", "text":"Kookie Brittle", "key":-13},
{"key":-1, "category":"Start", "loc":"175 0", "text":"Start"},
{"key":0, "loc":"-5 75", "text":"Preheat oven to 375 F"},
{"key":1, "loc":"175 100", "text":"In a bowl, blend: 1 cup margarine, 1.5 teaspoon vanilla, 1 teaspoon salt"},
{"key":2, "loc":"175 200", "text":"Gradually beat in 1 cup sugar and 2 cups sifted flour"},
{"key":3, "loc":"175 290", "text":"Mix in 6 oz (1 cup) Nestle's Semi-Sweet Chocolate Morsels"},
{"key":4, "loc":"175 380", "text":"Press evenly into ungreased 15x10x1 pan"},
{"key":5, "loc":"355 85", "text":"Finely chop 1/2 cup of your choice of nuts"},
{"key":6, "loc":"175 450", "text":"Sprinkle nuts on top"},
{"key":7, "loc":"175 515", "text":"Bake for 25 minutes and let cool"},
{"key":8, "loc":"175 585", "text":"Cut into rectangular grid"},
{"key":-2, "category":"End", "loc":"175 660", "text":"Enjoy!"}
],
"linkDataArray": [
{"from":1, "to":2, "fromPort":"B", "toPort":"T"},
{"from":2, "to":3, "fromPort":"B", "toPort":"T"},
{"from":3, "to":4, "fromPort":"B", "toPort":"T"},
{"from":4, "to":6, "fromPort":"B", "toPort":"T"},
{"from":6, "to":7, "fromPort":"B", "toPort":"T"},
{"from":7, "to":8, "fromPort":"B", "toPort":"T"},
{"from":8, "to":-2, "fromPort":"B", "toPort":"T"},
{"from":-1, "to":0, "fromPort":"B", "toPort":"T"},
{"from":-1, "to":1, "fromPort":"B", "toPort":"T"},
{"from":-1, "to":5, "fromPort":"B", "toPort":"T"},
{"from":5, "to":4, "fromPort":"B", "toPort":"T"},
{"from":0, "to":4, "fromPort":"B", "toPort":"T"}
]}
</textarea>
<button onclick="printDiagram()">Print Diagram Using SVG</button>
</div>
</body>
</html>
+601
View File
@@ -0,0 +1,601 @@
<!DOCTYPE html>
<html>
<head>
<title>Flowgrammer</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="An editor for a flowchart-like diagram with a restricted syntax -- add nodes by dropping them onto existing nodes or links." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../extensions/ParallelLayout.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<script>
// two custom figures, for "For Each" loops
go.Shape.defineFigureGenerator("ForEach", function(shape, w, h) {
var param1 = shape ? shape.parameter1 : NaN; // length of triangular area in direction that it is pointing
if (isNaN(param1)) param1 = 10;
var d = Math.min(h/2, param1);
var geo = new go.Geometry();
var fig = new go.PathFigure(w, h-d, true);
geo.add(fig);
fig.add(new go.PathSegment(go.PathSegment.Line, w/2, h));
fig.add(new go.PathSegment(go.PathSegment.Line, 0, h-d));
fig.add(new go.PathSegment(go.PathSegment.Line, 0, 0));
fig.add(new go.PathSegment(go.PathSegment.Line, w, 0).close());
geo.spot1 = go.Spot.TopLeft;
geo.spot2 = new go.Spot(1, 1, 0, Math.min(-d+2, 0));
return geo;
});
go.Shape.defineFigureGenerator("EndForEach", function(shape, w, h) {
var param1 = shape ? shape.parameter1 : NaN; // length of triangular area in direction that it is pointing
if (isNaN(param1)) param1 = 10;
var d = Math.min(h/2, param1);
var geo = new go.Geometry();
var fig = new go.PathFigure(w, d, true);
geo.add(fig);
fig.add(new go.PathSegment(go.PathSegment.Line, w, h));
fig.add(new go.PathSegment(go.PathSegment.Line, 0, h));
fig.add(new go.PathSegment(go.PathSegment.Line, 0, d));
fig.add(new go.PathSegment(go.PathSegment.Line, w/2, 0).close());
geo.spot1 = new go.Spot(0, 0, 0, Math.min(d, 0));
geo.spot2 = go.Spot.BottomRight;
return geo;
});
function init() {
if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this
var $ = go.GraphObject.make;
// initialize main Diagram
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
allowMove: false,
allowCopy: false,
"SelectionDeleting": function(e) { // before a delete happens
// handle deletions by excising the node and reconnecting the link where the node had been
new go.List(e.diagram.selection).each(function(part) { deletingNode(part); });
},
layout: $(ParallelLayout, { angle: 90, layerSpacing: 21, nodeSpacing: 30 }),
"ExternalObjectsDropped": function(e) { // handle drops from the Palette
var newnode = e.diagram.selection.first();
if (!newnode) return;
if (!(newnode instanceof go.Group) && newnode.linksConnected.count === 0) {
// when the selection is dropped but not hooked up to the rest of the graph, delete it
e.diagram.removeParts(e.diagram.selection, false);
} else {
e.diagram.commandHandler.scrollToPart(newnode);
}
},
"undoManager.isEnabled": true
});
// dragged nodes are translucent so that the user can see highlighting of links and nodes
myDiagram.findLayer("Tool").opacity = 0.5;
// some common styles for most of the node templates
function nodeStyle() {
return {
deletable: false,
locationSpot: go.Spot.Center,
mouseDragEnter: function(e, node) {
var sh = node.findObject("SHAPE");
if (sh) sh.fill = "lime";
},
mouseDragLeave: function(e, node) {
var sh = node.findObject("SHAPE");
if (sh) sh.fill = "white";
},
mouseDrop: dropOntoNode
};
}
function shapeStyle() {
return { name: "SHAPE", fill: "white" };
}
function textStyle() {
return [
{ name: "TEXTBLOCK", textAlign: "center", editable: true },
new go.Binding("text").makeTwoWay()
];
}
// define the Node templates
myDiagram.nodeTemplate = // regular action steps
$(go.Node, "Auto", nodeStyle(),
{ deletable: true }, // override nodeStyle()
{ minSize: new go.Size(10, 20) },
$(go.Shape, shapeStyle()),
$(go.TextBlock, textStyle(),
{ margin: 4 })
);
myDiagram.nodeTemplateMap.add("Start",
$(go.Node, "Auto", nodeStyle(),
{ desiredSize: new go.Size(32, 32) },
$(go.Shape, "Circle", shapeStyle()),
$(go.TextBlock, textStyle(), "Start")
));
myDiagram.nodeTemplateMap.add("End",
$(go.Node, "Auto", nodeStyle(),
{ desiredSize: new go.Size(32, 32) },
$(go.Shape, "Circle", shapeStyle()),
$(go.TextBlock, textStyle(), "End")
));
myDiagram.nodeTemplateMap.add("For",
$(go.Node, "Auto", nodeStyle(),
{ minSize: new go.Size(64, 32) },
$(go.Shape, "ForEach", shapeStyle()),
$(go.TextBlock, textStyle(), "For Each",
{ margin: 4 })
));
myDiagram.nodeTemplateMap.add("EndFor",
$(go.Node, nodeStyle(),
$(go.Shape, "EndForEach", shapeStyle(),
{ desiredSize: new go.Size(4, 4) })
));
myDiagram.nodeTemplateMap.add("While",
$(go.Node, "Auto", nodeStyle(),
{ minSize: new go.Size(32, 32) },
$(go.Shape, "ForEach", shapeStyle(),
{ angle: -90, spot2: new go.Spot(1, 1, -6, 0) }),
$(go.TextBlock, textStyle(), "While",
{ margin: 4 })
));
myDiagram.nodeTemplateMap.add("EndWhile",
$(go.Node, nodeStyle(),
$(go.Shape, "Circle", shapeStyle(),
{ desiredSize: new go.Size(4, 4) })
));
myDiagram.nodeTemplateMap.add("If",
$(go.Node, "Auto", nodeStyle(),
{ minSize: new go.Size(64, 32) },
$(go.Shape, "Diamond", shapeStyle()),
$(go.TextBlock, textStyle(), "If")
));
myDiagram.nodeTemplateMap.add("EndIf",
$(go.Node, nodeStyle(),
$(go.Shape, "Diamond", shapeStyle(),
{ desiredSize: new go.Size(4, 4) })
));
myDiagram.nodeTemplateMap.add("Switch",
$(go.Node, "Auto", nodeStyle(),
{ minSize: new go.Size(64, 32) },
$(go.Shape, "TriangleUp", shapeStyle()),
$(go.TextBlock, textStyle(), "Switch")
));
myDiagram.nodeTemplateMap.add("Merge",
$(go.Node, nodeStyle(),
$(go.Shape, "TriangleDown", shapeStyle(),
{ desiredSize: new go.Size(4, 4) })
));
function groupColor(cat) {
switch (cat) {
case "If": return "rgba(255,0,0,0.05)";
case "For": return "rgba(0,255,0,0.05)";
case "While": return "rgba(0,0,255,0.05)";
default: return "rgba(0,0,0,0.05)";
}
}
// define the Group template, required but unseen
myDiagram.groupTemplate =
$(go.Group, "Auto",
{
locationSpot: go.Spot.Center,
avoidableMargin: 10, // extra space on the sides
layout: $(ParallelLayout, { angle: 90, layerSpacing: 24, nodeSpacing: 30 }),
mouseDragEnter: function(e, group) {
var sh = group.findObject("SHAPE");
if (sh) { sh.width = Math.max(20, group.actualBounds.width-20); sh.stroke = "lime"; }
},
mouseDragLeave: function(e, group) {
var sh = group.findObject("SHAPE");
if (sh) sh.stroke = null;
},
mouseDrop: dropOntoNode
},
$(go.Shape, "RoundedRectangle",
{ fill: "rgba(0,0,0,0.05)", strokeWidth: 0, spot1: go.Spot.TopLeft, spot2: go.Spot.BottomRight },
new go.Binding("fill", "cat", groupColor)),
$(go.Placeholder),
$(go.Shape, "LineH",
{
name: "SHAPE",
height: 0, alignment: go.Spot.Bottom,
stroke: null, strokeWidth: 8
})
);
myDiagram.linkTemplate =
$(go.Link,
{
selectable: false,
deletable: false,
routing: go.Link.Orthogonal, corner: 5,
toShortLength: 2,
// links cannot be deleted
// If a node from the Palette is dragged over this node, its outline will turn green
mouseDragEnter: function(e, link) { if (!isLoopBack(link)) link.isHighlighted = true; },
mouseDragLeave: function(e, link) { link.isHighlighted = false; },
// if a node from the Palette is dropped on a link, the link is replaced by links to and from the new node
mouseDrop: dropOntoLink
},
$(go.Shape, { isPanelMain: true, stroke: "transparent", strokeWidth: 8 },
new go.Binding("stroke", "isHighlighted", function(h) { return h ? "lime" : "transparent"; }).ofObject()),
$(go.Shape, { isPanelMain: true, stroke: "black", strokeWidth: 1.5 }),
$(go.Shape, { toArrow: "Standard", strokeWidth: 0 }),
// $(go.TextBlock, { segmentIndex: -2, segmentFraction: 0.75, editable: true },
// new go.Binding("text").makeTwoWay(),
// new go.Binding("background", "text", function(t) { return t ? "white" : null; }))
);
function isLoopBack(link) {
if (!link) return false;
if (link.fromNode.containingGroup !== link.toNode.containingGroup) return false;
var cat = link.fromNode.category;
return (cat === "EndFor" || cat === "EndWhile" || cat === "EndIf");
}
// A node dropped onto a Merge node is spliced into a link coming into that node;
// otherwise it is spliced into a link that is coming out of that node.
function dropOntoNode(e, oldnode) {
if (oldnode instanceof go.Group) {
var merge = oldnode.layout.mergeNode;
if (merge) {
var it = merge.findLinksOutOf();
while (it.next()) {
var link = it.value;
if (link.fromNode.containingGroup !== link.toNode.containingGroup) {
dropOntoLink(e, link);
break;
}
}
}
} else if (oldnode instanceof go.Node) {
var cat = oldnode.category;
if (cat === "Merge" || cat === "End" || cat === "EndFor" || cat === "EndWhile" || cat === "EndIf") {
var link = oldnode.findLinksInto().first();
if (link) dropOntoLink(e, link);
} else {
var link = oldnode.findLinksOutOf().first();
if (link) dropOntoLink(e, link);
}
}
}
// Splice a node into a link.
// If the new node is of category "For" or "While" or "If", create a Group and splice it in,
// and add the new node to that group, and add any other desired nodes and links to that group.
function dropOntoLink(e, oldlink) {
if (!(oldlink instanceof go.Link)) return;
var diagram = e.diagram;
var newnode = diagram.selection.first();
if (!(newnode instanceof go.Node)) return;
if (!newnode.isTopLevel) return;
if (isLoopBack(oldlink)) {
// can't add nodes into links going back to the "For" node
diagram.remove(newnode);
return;
}
var fromnode = oldlink.fromNode;
var tonode = oldlink.toNode;
if (newnode.category === "") { // add simple step into chain of actions
newnode.containingGroup = oldlink.containingGroup;
// Reconnect the existing link to the new node
oldlink.toNode = newnode;
// Then add links from the new node to the old node
if (newnode.category === "If") {
diagram.model.addLinkData({ from: newnode.key, to: tonode.key });
diagram.model.addLinkData({ from: newnode.key, to: tonode.key });
} else {
diagram.model.addLinkData({ from: newnode.key, to: tonode.key });
}
} else if (newnode.category === "For" || newnode.category === "While") { // add loop group
// add group for loop
var groupdata = { isGroup: true, cat: newnode.category };
diagram.model.addNodeData(groupdata);
var group = diagram.findNodeForData(groupdata);
group.containingGroup = oldlink.containingGroup;
diagram.select(group);
newnode.containingGroup = group;
var enddata = { category: "End" + newnode.category };
diagram.model.addNodeData(enddata);
var endnode = diagram.findNodeForData(enddata);
endnode.containingGroup = group;
endnode.location = e.documentPoint;
diagram.model.addLinkData({ from: newnode.key, to: endnode.key });
diagram.model.addLinkData({ from: endnode.key, to: newnode.key });
// Reconnect the existing link to the new node
oldlink.toNode = newnode;
// Then add a link from the end node to the old node
diagram.model.addLinkData({ from: endnode.key, to: tonode.key });
} else if (newnode.category === "If") { // add Conditional group
// add group for conditional
var groupdata = { isGroup: true, cat: newnode.category };
diagram.model.addNodeData(groupdata);
var group = diagram.findNodeForData(groupdata);
group.containingGroup = oldlink.containingGroup;
diagram.select(group);
newnode.containingGroup = group;
var enddata = { category: "EndIf" };
diagram.model.addNodeData(enddata);
var endnode = diagram.findNodeForData(enddata);
endnode.containingGroup = group;
endnode.location = e.documentPoint;
var truedata = { from: newnode.key, to: endnode.key, text: "true" };
diagram.model.addLinkData(truedata);
var truelink = diagram.findLinkForData(truedata);
var falsedata = { from: newnode.key, to: endnode.key, text: "false" };
diagram.model.addLinkData(falsedata);
var falselink = diagram.findLinkForData(falsedata);
// Reconnect the existing link to the new node
oldlink.toNode = newnode;
// Then add a link from the new node to the old node
diagram.model.addLinkData({ from: endnode.key, to: tonode.key });
} else if (newnode.category === "Switch") { // add multi-way Switch group
// add group for loop
var groupdata = { isGroup: true, cat: newnode.category };
diagram.model.addNodeData(groupdata);
var group = diagram.findNodeForData(groupdata);
group.containingGroup = oldlink.containingGroup;
diagram.select(group);
newnode.containingGroup = group;
var enddata = { category: "Merge" };
diagram.model.addNodeData(enddata);
var endnode = diagram.findNodeForData(enddata);
endnode.containingGroup = group;
endnode.location = e.documentPoint;
var yesdata = { text: "yes,\ndo it" };
diagram.model.addNodeData(yesdata);
var yesnode = diagram.findNodeForData(yesdata);
yesnode.containingGroup = group;
yesnode.location = e.documentPoint;
diagram.model.addLinkData({ from: newnode.key, to: yesnode.key, text: "yes" });
diagram.model.addLinkData({ from: yesnode.key, to: endnode.key });
var nodata = { text: "no,\ndon't" };
diagram.model.addNodeData(nodata);
var nonode = diagram.findNodeForData(nodata);
nonode.containingGroup = group;
nonode.location = e.documentPoint;
diagram.model.addLinkData({ from: newnode.key, to: nonode.key, text: "no" });
diagram.model.addLinkData({ from: nonode.key, to: endnode.key });
var maybedata = { text: "??" };
diagram.model.addNodeData(maybedata);
var maybenode = diagram.findNodeForData(maybedata);
maybenode.containingGroup = group;
maybenode.location = e.documentPoint;
diagram.model.addLinkData({ from: newnode.key, to: maybenode.key, text: "maybe" });
diagram.model.addLinkData({ from: maybenode.key, to: endnode.key });
// Reconnect the existing link to the new node
oldlink.toNode = newnode;
// Then add a link from the end node to the old node
diagram.model.addLinkData({ from: endnode.key, to: tonode.key });
}
diagram.layoutDiagram(true);
}
function deletingNode(node) { // excise node from the chain that it is in
if (!(node instanceof go.Node)) return;
if (node instanceof go.Group) {
var externals = node.findExternalLinksConnected();
var next = null;
externals.each(function(link) {
if (link.fromNode.isMemberOf(node)) next = link.toNode;
});
if (next) {
externals.each(function(link) {
if (link.toNode.isMemberOf(node)) link.toNode = next;
});
}
} else if (node.category === "") {
var next = node.findNodesOutOf().first();
if (next) {
new go.List(node.findLinksInto()).each(function(link) { link.toNode = next; });
}
}
}
// initialize Palette
myPalette =
$(go.Palette, "myPaletteDiv",
{
maxSelectionCount: 1,
nodeTemplateMap: myDiagram.nodeTemplateMap,
model: new go.GraphLinksModel([
{ text: "Action" },
{ text: "For Each", category: "For" },
{ text: "While", category: "While" },
{ text: "If", category: "If" },
{ text: "Switch", category: "Switch" }
])
});
// initialize Overview
myOverview =
$(go.Overview, "myOverviewDiv",
{
observed: myDiagram,
contentAlignment: go.Spot.Center
});
load();
}
// save a model to and load a model from Json 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);
}
function newDiagram() {
myDiagram.model = go.GraphObject.make(go.GraphLinksModel,
{
nodeDataArray:
[
{"key":1, "text":"S", "category":"Start"},
{"key":2, "text":"E", "category":"End"}
],
linkDataArray:
[
{"from":1, "to":2}
]
});
}
</script>
<style>
/* Use a Flexbox to make the Palette/Overview/Diagram responsive and size things relatively */
#myFlexDiv {
display: flex;
width: 100%;
height: 600px;
}
#myPODiv {
display: flex;
}
@media (min-width: 768px) {
#myFlexDiv {
flex-flow: row;
}
#myPODiv {
width: 105px;
height: 100%;
margin-right: 3px;
flex-flow: column;
}
#myPaletteDiv {
height: 80%;
}
#myOverviewDiv {
margin-top: 3px;
flex: 1;
}
#myDiagramDiv {
flex: 1;
}
}
@media (max-width: 767px) {
#myFlexDiv {
flex-flow: column;
align-items: center;
}
#myPODiv {
width: 90%;
height: 105px;
margin-bottom: 3px;
flex-flow: row;
}
#myPaletteDiv {
width: 75%;
}
#myOverviewDiv {
margin-left: 3px;
flex: 1;
}
#myDiagramDiv {
width: 90%;
flex: 1;
}
}
</style>
</head>
<body onload="init()">
<div id="sample">
<div id="myFlexDiv">
<div id="myPODiv">
<div id="myPaletteDiv" style="background-color: floralwhite; border: solid 1px black"></div>
<div id="myOverviewDiv" style="background-color: whitesmoke; border: solid 1px black"></div>
</div>
<div id="myDiagramDiv" style="border: solid 1px black"></div>
</div>
<p>
The Flowgrammer sample demonstrates how one can build a flowchart with a constrained syntax.
You can drag and drop Nodes onto Links and Nodes in the diagram in order to splice them into the graph.
There is visual feedback during the dragging process.
Nodes dropped onto the diagram's background are automatically deleted.
Edit text by clicking on the text of selected nodes.
Deleting an action or step Node excises it from the chain of steps that it is in.
The "For", "While", and "If" are not deletable, but you can select and delete the Group holding the
whole body of the loop or conditional.
The "Start" and "End" nodes and Links are not deletable.
</p>
<p>
The automatic layout of the diagram is accomplished with the <a>ParallelLayout</a> extension.
</p>
<div id="buttons">
<button id="loadModel" onclick="load()">Load</button>
<button id="saveModel" onclick="save()">Save</button>
<button onclick="newDiagram()">New Diagram</button>
</div>
<textarea id="mySavedModel" style="width:100%;height:200px">
{ "class": "GraphLinksModel",
"nodeDataArray": [
{"key":1, "text":"S", "category":"Start"},
{"key":-1, "isGroup":true, "cat":"For"},
{"key":2, "text":"For Each", "category":"For", "group":-1},
{"key":3, "text":"Action 1", "group":-1},
{"key":-2, "isGroup":true, "cat":"If", "group":-1},
{"key":4, "text":"If", "category":"If", "group":-2},
{"key":5, "text":"Action 2", "group":-2},
{"key":6, "text":"Action 3", "group":-2},
{"key":-3, "isGroup":true, "cat":"For", "group":-2},
{"key":7, "text":"For Each\n(nested)", "category":"For", "group":-3},
{"key":8, "text":"Action 4", "group":-3},
{"key":9, "text":"", "category":"EndFor", "group":-3},
{"key":10, "text":"", "category":"EndIf", "group":-2},
{"key":11, "text":"Action 5", "group":-1},
{"key":12, "text":"", "category":"EndFor", "group":-1},
{"key":13, "text":"E", "category":"End"}
],
"linkDataArray": [
{"from":1, "to":2},
{"from":2, "to":3},
{"from":3, "to":4},
{"from":4, "to":5, "text":"true"},
{"from":4, "to":6, "text":"false"},
{"from":6, "to":7},
{"from":7, "to":8},
{"from":8, "to":9},
{"from":5, "to":10},
{"from":9, "to":10},
{"from":9, "to":7},
{"from":10, "to":11},
{"from":11, "to":12},
{"from":12, "to":2},
{"from":12, "to":13}
]}
</textarea>
</div>
</body>
</html>
+218
View File
@@ -0,0 +1,218 @@
<!DOCTYPE html>
<html>
<head>
<title>Friend Wheel</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Show the relationships between people using a friend wheel diagram, implemented using circular layout." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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 WheelLayout() {
go.CircularLayout.call(this);
}
go.Diagram.inherit(WheelLayout, go.CircularLayout);
// override makeNetwork to set the diameter of each node and ignore the TextBlock label
WheelLayout.prototype.makeNetwork = function(coll) {
var net = go.CircularLayout.prototype.makeNetwork.call(this, coll);
net.vertexes.each(function(cv) {
cv.diameter = 20; // because our desiredSize for nodes is (20, 20)
});
return net;
}
// override commitNodes to rotate nodes so the text goes away from the center,
// and flip text if it would be upside-down
WheelLayout.prototype.commitNodes = function() {
go.CircularLayout.prototype.commitNodes.call(this);
this.network.vertexes.each(function(v) {
var node = v.node;
if (node === null) return;
// get the angle of the node towards the center, and rotate it accordingly
var a = v.actualAngle;
if (a > 90 && a < 270) { // make sure the text isn't upside down
var textBlock = node.findObject("TEXTBLOCK");
textBlock.angle = 180;
}
node.angle = a;
});
};
// override commitLinks in order to make sure all of the Bezier links are "inside" the ellipse;
// this helps avoid links crossing over any other nodes
WheelLayout.prototype.commitLinks = function() {
go.CircularLayout.prototype.commitLinks.call(this);
if (this.network.vertexes.count > 4) {
this.network.vertexes.each(function(v) {
v.destinationEdges.each(function(de) {
var dv = de.toVertex;
var da = dv.actualAngle;
var sa = v.actualAngle;
if (da - sa > 180) da -= 360;
else if (sa - da > 180) sa -= 360;
de.link.curviness = (sa > da) ? 15 : -15;
})
})
}
}
// end WheelLayout class
var highlightColor = "red"; // color parameterization
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", // must be the ID or reference to div
{
initialAutoScale: go.Diagram.Uniform,
padding: 10,
contentAlignment: go.Spot.Center,
layout:
$(WheelLayout, // set up a custom CircularLayout
// set some properties appropriate for this sample
{
arrangement: go.CircularLayout.ConstantDistance,
nodeDiameterFormula: go.CircularLayout.Circular,
spacing: 10,
aspectRatio: 0.7,
sorting: go.CircularLayout.Optimized
}),
isReadOnly: true,
click: function(e) { // background click clears any remaining highlighteds
e.diagram.startTransaction("clear");
e.diagram.clearHighlighteds();
e.diagram.commitTransaction("clear");
}
});
// define the Node template
myDiagram.nodeTemplate =
$(go.Node, "Horizontal",
{
selectionAdorned: false,
locationSpot: go.Spot.Center, // Node.location is the center of the Shape
locationObjectName: "SHAPE",
mouseEnter: function(e, node) {
node.diagram.clearHighlighteds();
node.linksConnected.each(function(l) { highlightLink(l, true); });
node.isHighlighted = true;
var tb = node.findObject("TEXTBLOCK");
if (tb !== null) tb.stroke = highlightColor;
},
mouseLeave: function(e, node) {
node.diagram.clearHighlighteds();
var tb = node.findObject("TEXTBLOCK");
if (tb !== null) tb.stroke = "black";
}
},
new go.Binding("text", "text"), // for sorting the nodes
$(go.Shape, "Ellipse",
{
name: "SHAPE",
fill: "lightgray", // default value, but also data-bound
stroke: "transparent", // modified by highlighting
strokeWidth: 2,
desiredSize: new go.Size(20, 20),
portId: ""
}, // so links will go to the shape, not the whole node
new go.Binding("fill", "color"),
new go.Binding("stroke", "isHighlighted",
function(h) { return h ? highlightColor : "transparent"; })
.ofObject()),
$(go.TextBlock,
{ name: "TEXTBLOCK" }, // for search
new go.Binding("text", "text"))
);
function highlightLink(link, show) {
link.isHighlighted = show;
link.fromNode.isHighlighted = show;
link.toNode.isHighlighted = show;
}
// define the Link template
myDiagram.linkTemplate =
$(go.Link,
{
routing: go.Link.Normal,
curve: go.Link.Bezier,
selectionAdorned: false,
mouseEnter: function(e, link) { highlightLink(link, true); },
mouseLeave: function(e, link) { highlightLink(link, false); }
},
$(go.Shape,
new go.Binding("stroke", "isHighlighted",
function(h, shape) { return h ? highlightColor : shape.part.data.color; })
.ofObject(),
new go.Binding("strokeWidth", "isHighlighted",
function(h) { return h ? 2 : 1; })
.ofObject())
// no arrowhead -- assume directionality of relationship need not be shown
);
generateGraph();
}
function generateGraph() {
var names = [
"Joshua", "Daniel", "Robert", "Noah", "Anthony",
"Elizabeth", "Addison", "Alexis", "Ella", "Samantha",
"Joseph", "Scott", "James", "Ryan", "Benjamin",
"Walter", "Gabriel", "Christian", "Nathan", "Simon",
"Isabella", "Emma", "Olivia", "Sophia", "Ava",
"Emily", "Madison", "Tina", "Elena", "Mia",
"Jacob", "Ethan", "Michael", "Alexander", "William",
"Natalie", "Grace", "Lily", "Alyssa", "Ashley",
"Sarah", "Taylor", "Hannah", "Brianna", "Hailey",
"Christopher", "Aiden", "Matthew", "David", "Andrew",
"Kaylee", "Juliana", "Leah", "Anna", "Allison",
"John", "Samuel", "Tyler", "Dylan", "Jonathan",
];
var nodeDataArray = [];
for (var i = 0; i < names.length; i++) {
nodeDataArray.push({ key: i, text: names[i], color: go.Brush.randomColor(128, 240) });
}
var linkDataArray = [];
var num = nodeDataArray.length;
for (var i = 0; i < num * 2; i++) {
var a = Math.floor(Math.random() * num);
var b = Math.floor(Math.random() * num / 4) + 1;
linkDataArray.push({ from: a, to: (a + b) % num, color: go.Brush.randomColor(0, 127) });
}
myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; background: white; width: 100%; height: 600px" ></div>
<p>
This "friend wheel" demonstrates the use of <a>CircularLayout</a>.
The layout has been customized to make sure each node is considered to have a fixed diameter,
ignoring the size of any <a>TextBlock</a>.
</p>
<p>
The custom layout also rotates each <a>Node</a> according to the actual angle at which the node was positioned.
This information is available on the <a>CircularVertex</a> used by the <a>LayoutNetwork</a> that
the <a>CircularLayout</a> constructs from the nodes and links of the diagram.
Furthermore, when laying out the nodes it also flips the angle of the <a>TextBlock</a> so that the
text is not upside-down.
</p>
<p>
<a>GraphObject.mouseEnter</a> and <a>GraphObject.mouseLeave</a> event handlers on the <a>Node</a> template
highlight both the Node and all of the Links that connect with the Node.
The same event handlers on the <a>Link</a>s highlight that Link and both connected Nodes.
Changes made in these event handlers automatically are not recorded in the <a>UndoManager</a>,
although this sample does not enable the UndoManager anyway.
</p>
</div>
</body>
</html>
+165
View File
@@ -0,0 +1,165 @@
<!DOCTYPE html>
<html>
<head>
<title>Grid Layout</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Interactive demonstration of layout-on-a-grid features by the GridLayout class." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
myDiagram =
$(go.Diagram, "myDiagramDiv", // must be the ID or reference to div
{
layout: $(go.GridLayout,
{ comparer: go.GridLayout.smartComparer })
// other properties are set by the layout function, defined below
});
// define the Node template
myDiagram.nodeTemplate =
$(go.Node, "Spot",
// make sure the Node.location is different from the Node.position
{ locationSpot: go.Spot.Center },
new go.Binding("text", "text"), // for sorting
$(go.Shape, "Ellipse",
{
fill: "lightgray",
stroke: null,
desiredSize: new go.Size(30, 30)
},
new go.Binding("fill", "fill"),
new go.Binding("desiredSize", "size")),
$(go.TextBlock,
// the default alignment is go.Spot.Center
new go.Binding("text", "text"))
);
// create an array of data describing randomly colored and sized nodes
var nodeDataArray = [];
for (var i = 0; i < 100; i++) {
nodeDataArray.push({
key: i,
text: i.toString(),
fill: go.Brush.randomColor(),
size: new go.Size(30 + Math.floor(Math.random() * 50), 30 + Math.floor(Math.random() * 50))
});
}
// randomize the data
for (i = 0; i < nodeDataArray.length; i++) {
var swap = Math.floor(Math.random() * nodeDataArray.length);
var temp = nodeDataArray[swap];
nodeDataArray[swap] = nodeDataArray[i];
nodeDataArray[i] = temp;
}
// create a Model that does not know about link or group relationships
myDiagram.model = new go.Model(nodeDataArray);
// layout using the latest parameters
layout();
}
// Update the layout from the controls, and then perform the layout again
function layout() {
myDiagram.startTransaction("change Layout");
var lay = myDiagram.layout;
var wrappingColumn = document.getElementById("wrappingColumn").value;
lay.wrappingColumn = parseFloat(wrappingColumn, 10);
var wrappingWidth = document.getElementById("wrappingWidth").value;
lay.wrappingWidth = parseFloat(wrappingWidth, 10);
var cellSize = document.getElementById("cellSize").value;
lay.cellSize = go.Size.parse(cellSize);
var spacing = document.getElementById("spacing").value;
lay.spacing = go.Size.parse(spacing);
var alignment = getRadioValue("alignment");
if (alignment === "Position") {
lay.alignment = go.GridLayout.Position;
} else {
lay.alignment = go.GridLayout.Location;
}
var arrangement = getRadioValue("arrangement");
if (arrangement === "LeftToRight") {
lay.arrangement = go.GridLayout.LeftToRight;
} else {
lay.arrangement = go.GridLayout.RightToLeft;
}
var sorting = document.getElementById("sorting").value;
switch (sorting) {
default:
case "Forward": lay.sorting = go.GridLayout.Forward; break;
case "Reverse": lay.sorting = go.GridLayout.Reverse; break;
case "Ascending": lay.sorting = go.GridLayout.Ascending; break;
case "Descending": lay.sorting = go.GridLayout.Descending; break;
}
myDiagram.commitTransaction("change Layout");
}
function getRadioValue(name) {
var radio = document.getElementsByName(name);
for (var i = 0; i < radio.length; i++) {
if (radio[i].checked) return radio[i].value;
}
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div style="margin-bottom: 5px; padding: 5px; background-color: aliceblue">
<span style="display: inline-block; vertical-align: top; padding: 5px">
<b>GridLayout Properties</b>
<br />
Wrapping Column:
<input type="text" size="3" id="wrappingColumn" value="NaN" onchange="layout()" />
(NaN means there's no limit)
<br />
Wrapping Width:
<input type="text" size="3" id="wrappingWidth" value="NaN" onchange="layout()" />
(NaN means use the diagram's viewport width)
<br />
Cell Size:
<input type="text" size="8" id="cellSize" value="NaN NaN" onchange="layout()" />
(NaN x NaN means use a cell size big enough to hold any node)
<br />
Spacing:
<input type="text" size="8" id="spacing" value="10 10" onchange="layout()" />
(the minimum space between the nodes)
<br />
Alignment:
<input type="radio" name="alignment" onclick="layout()" value="Position" /> Position
<input type="radio" name="alignment" onclick="layout()" value="Location" checked="checked" /> Location
<br />
Arrangement:
<input type="radio" name="arrangement" onclick="layout()" value="LeftToRight" checked="checked" /> LeftToRight
<input type="radio" name="arrangement" onclick="layout()" value="RightToLeft" /> RightToLeft
<br />
Sorting:
<select name="sorting" id="sorting" onchange="layout()">
<option value="Forward" selected="selected">Forward</option>
<option value="Reverse">Reverse</option>
<option value="Ascending">Ascending</option>
<option value="Descending">Descending</option>
</select>
</span>
</div>
<div id="myDiagramDiv" style="background-color: white; border: solid 1px black; width: 100%; height: 500px"></div>
<p>
For information on <b>GridLayout</b> and its properties, see the <a>GridLayout</a> documentation page.
</p>
</div>
</body>
</html>
+406
View File
@@ -0,0 +1,406 @@
<!DOCTYPE html>
<html>
<head>
<title>Conway's Game of Life</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A cellular automation simulation in GoJS">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<script id="code">
// two dimensional array which will represent the board state
// this will be filled with Nodes once they are created
var goLGrid = [];
// the size of the board
var rows = 40;
var cols = 40;
var interval = 15; // the interval between steps in ms when the simulation is enabled
var initializing = true;
var enabled = false; // flag to turn the simulation on or off
var myDiagram;
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",
{
"animationManager.isEnabled": false,
// disable movement/edit controls, since the myDiagram is a static grid
"panningTool.isEnabled": false,
isReadOnly: true,
allowZoom: false,
allowSelect: false,
hasHorizontalScrollbar: false,
hasVerticalScrollbar: false,
initialAutoScale: go.Diagram.Uniform
});
var nodeSize = 25;
var nodeDataArray = []; // array to hold Node data for the model
// populate data array by initializing Nodes in a grid and setting their "isAlive" state to false
for (var i = 0; i < rows; i++) {
var row = new Array(cols);
for (var j = 0; j < cols; j++) {
nodeDataArray.push({ location: new go.Point(j * nodeSize, i * nodeSize), row: i, col: j, isAlive: false });
}
goLGrid.push(row);
}
// stroke color and width of the grid lines
var gridStroke = "#A2A2A2";
var gridStrokeWidth = 1;
// define the node template, which also includes interactive functionality, such as clicking to toggle a node's state
myDiagram.nodeTemplate =
$(go.Part, {
isLayoutPositioned: false,
mouseEnter: function(e, part, prev) { // set mouseover border
if (!initializing && !enabled) {
var shape = part.elt(0);
if (shape) {
shape.stroke = "steelblue";
shape.strokeWidth = 3;
}
part.zOrder = 2; // ensure that the selection border is in front of all other Nodes by increasing its zOrder
// drag with a button down to add or erase cells
if ((e.buttons === 1 && !part.data.isAlive) || (e.buttons === 2 && part.data.isAlive)) {
select(part);
}
}
},
mouseLeave: function(e, part, next) { // restore to original borders
var shape = part.elt(0);
if (shape) {
shape.stroke = gridStroke;
shape.strokeWidth = gridStrokeWidth
}
part.zOrder = 1;
},
click: function(e, part) { // left click to toggle cell
initializing = false;
if (!enabled) {
select(part);
}
},
contextClick: function(e, part) { // right click to clear cell
initializing = false;
if (!enabled && part.data.isAlive) {
select(part);
}
},
zOrder: 1
},
$(go.Shape,
{
figure: "Rectangle",
fill: "white",
stroke: gridStroke,
strokeWidth: gridStrokeWidth,
width: nodeSize,
height: nodeSize
}),
new go.Binding("location")
);
// use a simple model for our node data
myDiagram.model = new go.Model(nodeDataArray);
// myDiagram.parts is populated after a model is assigned;
// populate the internal gamestate array with the newly created nodes
for (var it = myDiagram.parts.iterator; it.next(); ) {
var part = it.value;
goLGrid[part.data.row][part.data.col] = part;
}
// load the default sample
var e = document.getElementById("samplePatterns");
loadSample(e.options[e.selectedIndex].value);
}
// toggles a given node's state, both visually and in the internal gamestate
function select(part) {
var shape = part.elt(0);
if (shape) {
if (shape.fill === "white") {
shape.fill = "steelblue";
} else {
shape.fill = "white";
}
};
part.data.isAlive = !part.data.isAlive;
}
// toggles the state of the simulation, changing the button text from "Start" to "Pause" or back again
function toggleSimulation() {
initializing = false;
var button = document.getElementById("start");
if (!enabled) {
button.value = "Pause";
enabled = true;
goLStep();
} else {
button.value = "Start";
enabled = false;
}
}
// the callback for the step button, only steps forward if the simulation is currently stopped
function stepOnclick() {
initializing = false;
if (!enabled) {
goLStep(true);
}
}
// performs a single step in the Game of Life
function goLStep(isManualStep) {
if (goLGrid.length === 0) {
return; // don't do anything if things aren't initialized yet
}
var isAlive = false;
var toSelect = [];
var liveCellCount = 0; // count the number of live cells to determine if there are no more left
for (var i = 0; i < rows; i++) {
for (var j = 0; j < cols; j++) {
if (isAlive) {
liveCellCount++;
}
// count the number of cells in the 8 squares adjacent to this one
var total = 0;
var above = goLGrid[i > 0 ? i - 1 : rows - 1];
var below = goLGrid[i + 1 < rows ? i + 1 : 0];
var left = j > 0 ? j - 1 : cols - 1;
var right = j + 1 < cols ? j + 1 : 0;
total += above[left].data.isAlive;
total += above[j].data.isAlive;
total += above[right].data.isAlive;
total += goLGrid[i][left].data.isAlive;
total += goLGrid[i][right].data.isAlive;
total += below[left].data.isAlive;
total += below[j].data.isAlive;
total += below[right].data.isAlive;
// toggle the cell if necessary according to the three rules
var part = goLGrid[i][j];
isAlive = part.data.isAlive;
if ((total <= 1 && isAlive)
|| (total > 3 && isAlive)
|| (total === 3 && !isAlive)) {
if (!isAlive) {
liveCellCount++;
} else {
liveCellCount--;
}
toSelect.push(part); // don't actually toggle the cell yet, this happens all at once after everything is done
}
}
}
// change the board state according the earlier loop
if (enabled || isManualStep) {
for (var i = 0; i < toSelect.length; i++) {
select(toSelect[i]);
}
}
if (enabled) {
if (liveCellCount === 0) {
toggleSimulation(); // stop the simulation if there are no more live cells
} else {
setTimeout(goLStep, interval); // queue another step if the simuation is still enabled
}
}
}
// clear the board of all live cells, stopping the simulation if it's currently enabled
function goLClear() {
if (enabled) {
toggleSimulation();
}
for (var i = 0; i < rows; i++) {
for (var j = 0; j < cols; j++) {
var part = goLGrid[i][j];
if (part.data.isAlive) {
select(part);
}
}
}
}
// this function contains all of the data for the four included sample patterns as well as the logic for drawing them
function loadSample(value) {
goLClear(); // clear the board first, stopping the simulation if it's enabled
// select the correct sample data based on the option value passed to the function
var sampleData = [];
switch (value) {
case "symm4":
sampleData = [
[ , , , , , , , , , , 1, 1, 1, , , , , , , , 0],
[ , , , , , , , , , , 1, , , 1, , , , , , , 0],
[ , , , , , , , , , , 1, , , 1, , , , , , , 0],
[ , , , , , , , , , , , , 1, 1, , , , , , , 0],
[ , , , , , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , , , , , 0],
[ , 1, 1, 1, , , , , , , , , , , , , , , , , 0],
[1, , , 1, , , , , , , , , , , , , , , , , 0],
[1, , , , , , , , , , , , , , , , , , , , 0],
[1, 1, 1, , , , , , , , , , , , , , , , 1, 1, 1],
[ , , , , , , , , , , , , , , , , , , , , 1],
[ , , , , , , , , , , , , , , , , , 1, , , 1],
[ , , , , , , , , , , , , , , , , , 1, 1, 1, 0],
[ , , , , , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , , , , , 0],
[ , , , , , , , 1, 1, , , , , , , , , , , , 0],
[ , , , , , , , 1, , , 1, , , , , , , , , , 0],
[ , , , , , , , 1, , , 1, , , , , , , , , , 0],
[ , , , , , , , , 1, 1, 1, , , , , , , , , , 0]
];
break;
case "pulsar":
sampleData = [
[ , 1, 1, 1, , , , 1, 1, 1, 0],
[1, , , , 1, , 1, , , , 1],
[1, , , , 1, , 1, , , , 1],
[1, , , , 1, , 1, , , , 1],
[ , 1, 1, 1, , , , 1, 1, 1, 0],
[ , , , , , , , , , , 0],
[ , 1, 1, 1, , , , 1, 1, 1, 0],
[1, , , , 1, , 1, , , , 1],
[1, , , , 1, , 1, , , , 1],
[1, , , , 1, , 1, , , , 1],
[ , 1, 1, 1, , , , 1, 1, 1, 0]
]
break;
case "spaceships":
sampleData = [
[1, , , 1, , , , , , , , , , , , , 0],
[ , , , , 1, , , , , , , , , , , , 0],
[1, , , , 1, , , , , , , , , , , , 0],
[ , 1, 1, 1, 1, , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , 1, , 0],
[ , , , , , , , , , , , , 1, , , , 1],
[ , , , , , , , , , , , 1, , , , , 0],
[ , , , , , , , , , , , 1, , , , , 1],
[ , , , , , , , , , , , 1, 1, 1, 1, 1, 0],
[ , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , 0],
[ , , , , , 1, , 1, 1, , , , , , , , 0],
[ , , , , 1, , , , , , , 1, , , , , 0],
[ , , , 1, 1, , , , 1, , , 1, , , , , 0],
[1, 1, , 1, , , , , , 1, 1, , , , , , 0],
[1, 1, , 1, , , , , , 1, 1, , , , , , 0],
[ , , , 1, 1, , , , 1, , , 1, , , , , 0],
[ , , , , 1, , , , , , , 1, , , , , 0],
[ , , , , , 1, , 1, 1, , , , , , , , 0]
]
break;
case "bigGliders":
sampleData = [
[ , , , , , , , , , , , , , 1, 1, 1, , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , 1, , , 1, 1, 1, , , , , , , , , 0],
[ , , , , , , , , , , , , , , 1, , 1, , , , , , , , , , , 0],
[ , , , , , , , , , , 1, 1, , , , , , , , 1, , , , , , , , 0],
[ , , , , , , , , , , 1, , 1, , , , , 1, , , 1, , , , , , , 0],
[ , , , , , , , , , , 1, , , , , , , , , 1, 1, , , , , , , 0],
[ , , , , , , , , , , , 1, 1, , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , 1, , , 1, , , , , , 1, , 1, 1, , , , 0],
[ , , , , , , , , , , , 1, , , , , , , , , , 1, 1, , 1, , , 0],
[ , , , , , , , , , , , , , 1, , 1, , , , , , , 1, 1, , , 1, 0],
[ , , , , , , , , , , , , , , 1, 1, , 1, , , , , 1, 1, , , , 1],
[ , , , , , , , , , , , , , , , , , , 1, , , , , , , , 1, 0],
[ , , , , , , , , , , , , , , , , , 1, 1, 1, 1, , , , 1, , 1, 0],
[ , , , , , , , , , , , , , , , , , 1, , 1, 1, , , , 1, 1, 1, 1],
[ , , , , , , , , , , , , , , , , , , 1, , , , 1, 1, , 1, , 0],
[ , , , , , , , , 1, 1, , , , , , , , , , , , , , 1, 1, , , 0],
[ , , , , , , , 1, 1, , , , , , , , , , , 1, , 1, 1, 1, , , , 0],
[ , , , , , , , , , 1, , , , , , , , , , , 1, , , 1, , , , 0],
[ , , , , , , , , , , , 1, 1, , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , 1, , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , , , , , , , , , , , , , , , , , , , 0],
[ , , , , , , , , , 1, , , 1, , , , , , , , , , , , , , , 0],
[ , 1, 1, , , , , , 1, 1, , , , , , , , , , , , , , , , , , 0],
[1, 1, , , , , , 1, , , , , , , , , , , , , , , , , , , , 0],
[ , , 1, , , , , 1, , 1, , , , , , , , , , , , , , , , , , 0],
[ , , , , 1, 1, , , 1, , , , , , , , , , , , , , , , , , , 0],
[ , , , , 1, 1, , , , , , , , , , , , , , , , , , , , , , 0],
]
break;
}
// draw the sample pattern in the middle of the board
var startRow = Math.floor((rows / 2) - (sampleData.length / 2));
var startCol = Math.floor((cols / 2) - (sampleData[0].length / 2));
for (var i = startRow; i < startRow + sampleData.length; i++) {
for (var j = startCol; j < startCol + sampleData[0].length; j++) {
if (sampleData[i - startRow][j - startCol] === 1) {
select(goLGrid[i][j]);
}
}
}
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:500px; height:500px"></div>
<p>
Game Controls:
<input id="start" onclick="toggleSimulation()" type="button" value="Start" />
<input id="step" onclick="stepOnclick()" type="button" value="Step" />
<input id="clear" onclick="goLClear()" type="button" value="Clear" style="margin-bottom: 10px" />
Sample patterns:
<select id="samplePatterns" onchange="loadSample(this.options[this.selectedIndex].value)" style="margin-bottom: 10px">
<option value="symm4">Symmetry</option>
<option value="pulsar">Pulsar</option>
<option value="spaceships">Spaceships</option>
<option value="bigGliders">Big gliders</option>
</select>
</p>
<p>
This sample shows an implementation of <a href="https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life">Conway's Game of Life</a> in GoJS.
Conway's Game of Life is a simple cellular automaton devised by British mathematician John Horton Conway in 1970. To start or advance the simulation,
use the controls above.
</p>
<p>
Whether or not a given cell lives, dies, or is born in a step is determined by the number of live cells in the 8 squares adjacent to it. For a cell <i>x</i> with <i>n</i> adjacent live cells:
</p>
<ul>
<li>If <i>n</i> &lt;= 1, cell <i>x</i> dies or stays dead (from underpopulation).</li>
<li>If <i>n</i> &gt; 3, cell <i>x</i> dies or stays dead (from overpopulation).</li>
<li>If <i>n</i> = 3, then <i>x</i> is born or stays alive.</li>
<li>If <i>n</i> = 2, then <i>x</i> maintains its status.</li>
</ul>
<p>
Though the rules are simple, they can produce complex patterns, some of which are shown in the dropdown above.
To create your own patterns, click or drag anywhere on the grid when the simulation is not running.
</p>
<p>
Each cell is implemented by a simple <a>Part</a> holding a small square <a>Shape</a>.
</p>
</div>
</body>
</html>
+177
View File
@@ -0,0 +1,177 @@
<!DOCTYPE html>
<html>
<head>
<title>Gantt chart</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A Gantt chart that supports zooming into the timeline." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
myDiagram =
$(go.Diagram, "myDiagramDiv", // Diagram refers to its DIV HTML element by id
{
_widthFactor: 1, // a scale for the nodes' positions and widths
isReadOnly: true, // deny the user permission to alter the diagram or zoom in or out
allowZoom: false,
"grid.visible": true, // display a grid in the background of the diagram
"grid.gridCellSize": new go.Size(30, 150)
});
// create the template for the standard nodes
myDiagram.nodeTemplateMap.add("",
$(go.Node, "Auto",
// links come from the right and go to the left side of the top of the node
{ fromSpot: go.Spot.Right, toSpot: new go.Spot(0.001, 0, 11, 0) },
$(go.Shape, "Rectangle",
{ height: 15 },
new go.Binding("fill", "color"),
new go.Binding("width", "width", function(w) { return scaleWidth(w); })),
$(go.TextBlock,
{ margin: 2, alignment: go.Spot.Left },
new go.Binding("text", "key")),
// using a function in the Binding allows the value to
// change when Diagram.updateAllTargetBindings is called
new go.Binding("location", "loc",
function(l) { return new go.Point(scaleWidth(l.x), l.y); })
));
// create the template for the start node
myDiagram.nodeTemplateMap.add("start",
$(go.Node,
{ fromSpot: go.Spot.Right, toSpot: go.Spot.Top, selectable: false },
$(go.Shape, "Diamond",
{ height: 15, width: 15 }),
// make the location of the start node is not scalable
new go.Binding("location", "loc")
));
// create the template for the end node
myDiagram.nodeTemplateMap.add("end",
$(go.Node,
{ fromSpot: go.Spot.Right, toSpot: go.Spot.Top, selectable: false },
$(go.Shape, "Diamond",
{ height: 15, width: 15 }),
// make the location of the end node (with location.x < 0) scalable
new go.Binding("location", "loc",
function(l) {
if (l.x >= 0) return new go.Point(scaleWidth(l.x), l.y);
else return l;
})
));
// create the link template
myDiagram.linkTemplate =
$(go.Link,
{
routing: go.Link.Orthogonal,
corner: 3, toShortLength: 2,
selectable: false
},
$(go.Shape,
{ strokeWidth: 2 }),
$(go.Shape,
{ toArrow: "OpenTriangle" })
);
// add the nodes and links to the model
myDiagram.model = new go.GraphLinksModel(
[ // node data
{ key: "a", color: "coral", width: 120, loc: new go.Point(scaleWidth(0), 40) },
{ key: "b", color: "turquoise", width: 160, loc: new go.Point(scaleWidth(0), 60) },
{ key: "c", color: "coral", width: 150, loc: new go.Point(scaleWidth(120), 80) },
{ key: "d", color: "turquoise", width: 190, loc: new go.Point(scaleWidth(120), 100) },
{ key: "e", color: "coral", width: 150, loc: new go.Point(scaleWidth(270), 120) },
{ key: "f", color: "turquoise", width: 130, loc: new go.Point(scaleWidth(310), 140) },
{ key: "g", color: "coral", width: 155, loc: new go.Point(scaleWidth(420), 160) },
{ key: "begin", category: "start", loc: new go.Point(-15, 20) },
{ key: "end", category: "end", loc: new go.Point(scaleWidth(575), 180) }
],
[ // link data
{ from: "begin", to: "a" },
{ from: "begin", to: "b" },
{ from: "a", to: "c" },
{ from: "a", to: "d" },
{ from: "b", to: "e" },
{ from: "c", to: "e" },
{ from: "d", to: "f" },
{ from: "e", to: "g" },
{ from: "f", to: "end" },
{ from: "g", to: "end" }
]);
// add a Graduated panel to show the dates, globally scoped
dateScale =
$(go.Part, "Graduated",
{
graduatedTickUnit: 1, graduatedMin: 0, graduatedMax: 3,
pickable: false, location: new go.Point(0, 0)
},
$(go.Shape,
{ name: "line", strokeWidth: 0, geometryString: "M0 0 H" + scaleWidth(450) }
),
$(go.TextBlock,
{
name: "labels",
font: "10pt sans-serif",
alignmentFocus: new go.Spot(0, 0, -3, -3),
graduatedFunction: function(v) {
var d = new Date(2017, 6, 23);
d.setDate(d.getDate() + v * 7);
// format date output to string
var options = { month: "short", day: "2-digit" };
return d.toLocaleDateString("en-US", options);
}
}
)
);
myDiagram.add(dateScale);
}
// scale the number according to the current widthFactor
function scaleWidth(num) {
return num * myDiagram._widthFactor;
}
// change the grid's cell size and the widthFactor,
// then update Bindings to scale the widths and positions of nodes,
// as well as the width of the date scale
function rescale() {
var val = parseFloat(document.getElementById("widthSlider").value);
myDiagram.startTransaction("rescale");
myDiagram.grid.gridCellSize = new go.Size(val, 150);
myDiagram._widthFactor = val / 30;
myDiagram.updateAllTargetBindings();
// update width of date scale and maybe change interval of labels if too small
var width = scaleWidth(450);
dateScale.findObject("line").geometryString = "M0 0 H" + width;
if (width >= 140) dateScale.findObject("labels").interval = 1;
if (width < 140) dateScale.findObject("labels").interval = 2;
if (width < 70) dateScale.findObject("labels").interval = 4;
myDiagram.commitTransaction("rescale");
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="height:600px;width:100%;border:1px solid black"></div>
<div id="slider">
<label>Spacing:</label>
<input id="widthSlider" type="range" min="2" max="90" value="30" onchange="rescale()"/>
</div>
<p>
This sample demonstrates a simple Gantt chart. Gantt charts are used to illustrate project schedules, denoting the start and end dates for terminal and summary elements of the project.
</p>
<p>
You can zoom in on the diagram by changing the "Spacing" value,
which scales the diagram using a data binding function for nodes' widths and locations.
This is in place of changing the <a>Diagram.scale</a>.
</p>
</div>
</body>
</html>
+661
View File
@@ -0,0 +1,661 @@
<!DOCTYPE html>
<html>
<head>
<title>Genogram</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A genogram is a family tree diagram for visualizing hereditary patterns." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
initialAutoScale: go.Diagram.Uniform,
"undoManager.isEnabled": true,
// 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, "Circle", { fill: "#c1cee3", stroke: null }),
$(go.Placeholder, { margin: 2 })
),
layout: // use a custom layout, defined below
$(GenogramLayout, { direction: 90, layerSpacing: 30, columnSpacing: 10 })
});
// determine the color for each attribute shape
function attrFill(a) {
switch (a) {
case "A": return "#00af54"; // green
case "B": return "#f27935"; // orange
case "C": return "#d4071c"; // red
case "D": return "#70bdc2"; // cyan
case "E": return "#fcf384"; // gold
case "F": return "#e69aaf"; // pink
case "G": return "#08488f"; // blue
case "H": return "#866310"; // brown
case "I": return "#9270c2"; // purple
case "J": return "#a3cf62"; // chartreuse
case "K": return "#91a4c2"; // lightgray bluish
case "L": return "#af70c2"; // magenta
case "S": return "#d4071c"; // red
default: return "transparent";
}
}
// determine the geometry for each attribute shape in a male;
// except for the slash these are all squares at each of the four corners of the overall square
var tlsq = go.Geometry.parse("F M1 1 l19 0 0 19 -19 0z");
var trsq = go.Geometry.parse("F M20 1 l19 0 0 19 -19 0z");
var brsq = go.Geometry.parse("F M20 20 l19 0 0 19 -19 0z");
var blsq = go.Geometry.parse("F M1 20 l19 0 0 19 -19 0z");
var slash = go.Geometry.parse("F M38 0 L40 0 40 2 2 40 0 40 0 38z");
function maleGeometry(a) {
switch (a) {
case "A": return tlsq;
case "B": return tlsq;
case "C": return tlsq;
case "D": return trsq;
case "E": return trsq;
case "F": return trsq;
case "G": return brsq;
case "H": return brsq;
case "I": return brsq;
case "J": return blsq;
case "K": return blsq;
case "L": return blsq;
case "S": return slash;
default: return tlsq;
}
}
// determine the geometry for each attribute shape in a female;
// except for the slash these are all pie shapes at each of the four quadrants of the overall circle
var tlarc = go.Geometry.parse("F M20 20 B 180 90 20 20 19 19 z");
var trarc = go.Geometry.parse("F M20 20 B 270 90 20 20 19 19 z");
var brarc = go.Geometry.parse("F M20 20 B 0 90 20 20 19 19 z");
var blarc = go.Geometry.parse("F M20 20 B 90 90 20 20 19 19 z");
function femaleGeometry(a) {
switch (a) {
case "A": return tlarc;
case "B": return tlarc;
case "C": return tlarc;
case "D": return trarc;
case "E": return trarc;
case "F": return trarc;
case "G": return brarc;
case "H": return brarc;
case "I": return brarc;
case "J": return blarc;
case "K": return blarc;
case "L": return blarc;
case "S": return slash;
default: return tlarc;
}
}
// 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", selectionObjectName: "ICON" },
$(go.Panel,
{ name: "ICON" },
$(go.Shape, "Square",
{ width: 40, height: 40, strokeWidth: 2, fill: "white", stroke: "#919191", portId: "" }),
$(go.Panel,
{ // for each attribute show a Shape at a particular place in the overall square
itemTemplate:
$(go.Panel,
$(go.Shape,
{ stroke: null, strokeWidth: 0 },
new go.Binding("fill", "", attrFill),
new go.Binding("geometry", "", maleGeometry))
),
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", selectionObjectName: "ICON" },
$(go.Panel,
{ name: "ICON" },
$(go.Shape, "Circle",
{ width: 40, height: 40, strokeWidth: 2, fill: "white", stroke: "#a1a1a1", portId: "" }),
$(go.Panel,
{ // for each attribute show a Shape at a particular place in the overall circle
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.linkTemplate = // for parent-child relationships
$(go.Link,
{
routing: go.Link.Orthogonal, corner: 5,
layerName: "Background", selectable: false,
fromSpot: go.Spot.Bottom, toSpot: go.Spot.Top
},
$(go.Shape, { stroke: "#424242", strokeWidth: 2 })
);
myDiagram.linkTemplateMap.add("Marriage", // for marriage relationships
$(go.Link,
{ selectable: false },
$(go.Shape, { strokeWidth: 2.5, stroke: "#5d8cc1" /* blue */ })
));
// n: name, s: sex, m: mother, f: father, ux: wife, vir: husband, a: attributes/markers
setupDiagram(myDiagram, [
{ key: 0, n: "Aaron", s: "M", m: -10, f: -11, ux: 1, a: ["C", "F", "K"] },
{ key: 1, n: "Alice", s: "F", m: -12, f: -13, a: ["B", "H", "K"] },
{ key: 2, n: "Bob", s: "M", m: 1, f: 0, ux: 3, a: ["C", "H", "L"] },
{ key: 3, n: "Barbara", s: "F", a: ["C"] },
{ key: 4, n: "Bill", s: "M", m: 1, f: 0, ux: 5, a: ["E", "H"] },
{ key: 5, n: "Brooke", s: "F", a: ["B", "H", "L"] },
{ key: 6, n: "Claire", s: "F", m: 1, f: 0, a: ["C"] },
{ key: 7, n: "Carol", s: "F", m: 1, f: 0, a: ["C", "I"] },
{ key: 8, n: "Chloe", s: "F", m: 1, f: 0, vir: 9, a: ["E"] },
{ key: 9, n: "Chris", s: "M", a: ["B", "H"] },
{ key: 10, n: "Ellie", s: "F", m: 3, f: 2, a: ["E", "G"] },
{ key: 11, n: "Dan", s: "M", m: 3, f: 2, a: ["B", "J"] },
{ key: 12, n: "Elizabeth", s: "F", vir: 13, a: ["J"] },
{ key: 13, n: "David", s: "M", m: 5, f: 4, a: ["B", "H"] },
{ key: 14, n: "Emma", s: "F", m: 5, f: 4, a: ["E", "G"] },
{ key: 15, n: "Evan", s: "M", m: 8, f: 9, a: ["F", "H"] },
{ key: 16, n: "Ethan", s: "M", m: 8, f: 9, a: ["D", "K"] },
{ key: 17, n: "Eve", s: "F", vir: 16, a: ["B", "F", "L"] },
{ key: 18, n: "Emily", s: "F", m: 8, f: 9 },
{ key: 19, n: "Fred", s: "M", m: 17, f: 16, a: ["B"] },
{ key: 20, n: "Faith", s: "F", m: 17, f: 16, a: ["L"] },
{ key: 21, n: "Felicia", s: "F", m: 12, f: 13, a: ["H"] },
{ key: 22, n: "Frank", s: "M", m: 12, f: 13, a: ["B", "H"] },
// "Aaron"'s ancestors
{ key: -10, n: "Paternal Grandfather", s: "M", m: -33, f: -32, ux: -11, a: ["A", "S"] },
{ key: -11, n: "Paternal Grandmother", s: "F", a: ["E", "S"] },
{ key: -32, n: "Paternal Great", s: "M", ux: -33, a: ["F", "H", "S"] },
{ key: -33, n: "Paternal Great", s: "F", a: ["S"] },
{ key: -40, n: "Great Uncle", s: "M", m: -33, f: -32, a: ["F", "H", "S"] },
{ key: -41, n: "Great Aunt", s: "F", m: -33, f: -32, a: ["B", "I", "S"] },
{ key: -20, n: "Uncle", s: "M", m: -11, f: -10, a: ["A", "S"] },
// "Alice"'s ancestors
{ key: -12, n: "Maternal Grandfather", s: "M", ux: -13, a: ["D", "L", "S"] },
{ key: -13, n: "Maternal Grandmother", s: "F", m: -31, f: -30, a: ["H", "S"] },
{ key: -21, n: "Aunt", s: "F", m: -13, f: -12, a: ["C", "I"] },
{ key: -22, n: "Uncle", s: "M", ux: -21 },
{ key: -23, n: "Cousin", s: "M", m: -21, f: -22 },
{ key: -30, n: "Maternal Great", s: "M", ux: -31, a: ["D", "J", "S"] },
{ key: -31, n: "Maternal Great", s: "F", m: -50, f: -51, a: ["B", "H", "L", "S"] },
{ key: -42, n: "Great Uncle", s: "M", m: -30, f: -31, a: ["C", "J", "S"] },
{ key: -43, n: "Great Aunt", s: "F", m: -30, f: -31, a: ["E", "G", "S"] },
{ key: -50, n: "Maternal Great Great", s: "F", ux: -51, a: ["D", "I", "S"] },
{ key: -51, n: "Maternal Great Great", s: "M", a: ["B", "H", "S"] }
],
4 /* focus on this person */);
}
// create and initialize the Diagram.model given an array of node data representing people
function setupDiagram(diagram, array, focusId) {
diagram.model =
go.GraphObject.make(go.GraphLinksModel,
{ // declare support for link label nodes
linkLabelKeysProperty: "labelKeys",
// this property determines which template is used
nodeCategoryProperty: "s",
// if a node data object is copied, copy its data.a Array
copiesArrays: true,
// create all of the nodes for people
nodeDataArray: array
});
setupMarriages(diagram);
setupParents(diagram);
var node = diagram.findNodeForKey(focusId);
if (node !== null) {
diagram.select(node);
// remove any spouse for the person under focus:
//node.linksConnected.each(function(l) {
// if (!l.isLabeledLink) return;
// l.opacity = 0;
// var spouse = l.getOtherNode(node);
// spouse.opacity = 0;
// spouse.pickable = false;
//});
}
}
function findMarriage(diagram, a, b) { // A and B are node keys
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);
}
}
}
// A custom layout that shows the two families related to a person's parents
function GenogramLayout() {
go.LayeredDigraphLayout.call(this);
this.initializeOption = go.LayeredDigraphLayout.InitDepthFirstIn;
this.spouseSpacing = 30; // minimum space between spouses
}
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) {
var multiSpousePeople = new go.Set();
// 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 + this.spouseSpacing + spouseB.actualBounds.width;
vertex.height = Math.max(spouseA.actualBounds.height, spouseB.actualBounds.height);
vertex.focus = new go.Point(spouseA.actualBounds.width + this.spouseSpacing / 2, vertex.height / 2);
} else {
// don't add a vertex for any married person!
// instead, code above adds label node for marriage link
// assume a marriage Link has a label Node
var marriages = 0;
node.linksConnected.each(function(l) { if (l.isLabeledLink) marriages++; });
if (marriages === 0) {
var vertex = net.addNode(node);
} else if (marriages > 1) {
multiSpousePeople.add(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
link.toNode.linksConnected.each(function(l) {
if (!l.isLabeledLink) return; // if it has no label node, it's a parent-child link
// 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);
}
});
}
}
}
while (multiSpousePeople.count > 0) {
// find all collections of people that are indirectly married to each other
var node = multiSpousePeople.first();
var cohort = new go.Set();
this.extendCohort(cohort, node);
// then encourage them all to be the same generation by connecting them all with a common vertex
var dummyvert = net.createVertex();
net.addVertex(dummyvert);
var marriages = new go.Set();
cohort.each(function(n) {
n.linksConnected.each(function(l) {
marriages.add(l);
})
});
marriages.each(function(link) {
// find the vertex for the marriage link (i.e. for the label node)
var mlab = link.labelNodes.first()
var v = net.findVertex(mlab);
if (v !== null) {
net.linkVertexes(dummyvert, v, null);
}
});
// done with these people, now see if there are any other multiple-married people
multiSpousePeople.removeAll(cohort);
}
};
// collect all of the people indirectly married with a person
GenogramLayout.prototype.extendCohort = function(coll, node) {
if (coll.has(node)) return;
coll.add(node);
var lay = this;
node.linksConnected.each(function(l) {
if (l.isLabeledLink) { // if it's a marriage link, continue with both spouses
lay.extendCohort(coll, l.fromNode);
lay.extendCohort(coll, l.toNode);
}
});
};
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 = [];
this.network.vertexes.each(function(v) {
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)
this.network.vertexes.each(function(v) {
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
this.network.vertexes.each(function(v) {
if (v.node !== null && !v.node.isLinkLabel) {
v.node.position = new go.Point(v.x, v.y);
}
});
// position the spouses of each marriage vertex
var layout = this;
this.network.vertexes.each(function(v) {
if (v.node === null) return;
if (!v.node.isLinkLabel) return;
var labnode = v.node;
var lablink = labnode.labeledLink;
// In case the spouses are not actually moved, we need to have the marriage link
// position the label node, because LayoutVertex.commit() was called above on these vertexes.
// Alternatively we could override LayoutVetex.commit to be a no-op for label node vertexes.
lablink.invalidateRoute();
var spouseA = lablink.fromNode;
var spouseB = lablink.toNode;
// prefer fathers on the left, mothers on the right
if (spouseA.data.s === "F") { // sex is female
var temp = spouseA;
spouseA = spouseB;
spouseB = temp;
}
// see if the parents are on the desired sides, to avoid a link crossing
var aParentsNode = layout.findParentsMarriageLabelNode(spouseA);
var bParentsNode = layout.findParentsMarriageLabelNode(spouseB);
if (aParentsNode !== null && bParentsNode !== null && aParentsNode.position.x > bParentsNode.position.x) {
// swap the spouses
var temp = spouseA;
spouseA = spouseB;
spouseB = temp;
}
spouseA.position = new go.Point(v.x, v.y);
spouseB.position = new go.Point(v.x + spouseA.actualBounds.width + layout.spouseSpacing, 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;
}
});
// position only-child nodes to be under the marriage label node
this.network.vertexes.each(function(v) {
if (v.node === null || v.node.linksConnected.count > 1) return;
var mnode = layout.findParentsMarriageLabelNode(v.node);
if (mnode !== null && mnode.linksConnected.count === 1) { // if only one child
var mvert = layout.network.findVertex(mnode);
var newbnds = v.node.actualBounds.copy();
newbnds.x = mvert.centerX - v.node.actualBounds.width / 2;
// see if there's any empty space at the horizontal mid-point in that layer
var overlaps = layout.diagram.findObjectsIn(newbnds, function(x) { return x.part; }, function(p) { return p !== v.node; }, true);
if (overlaps.count === 0) {
v.node.move(newbnds.position);
}
}
});
};
GenogramLayout.prototype.findParentsMarriageLabelNode = function(node) {
var it = node.findNodesInto();
while (it.next()) {
var n = it.value;
if (n.isLinkLabel) return n;
}
return null;
};
// end GenogramLayout class
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="background-color: #F8F8F8; border: solid 1px black; width:100%; height:600px;"></div>
<p>A <em>genogram</em> or <em>pedigree chart</em> is an extended family tree diagram that displays information about each person or each relationship.</p>
<p>
There are functions that convert an attribute value into a brush color or Shape geometry,
to be added to the Node representing the person.
</p>
<p>
A custom <a>LayeredDigraphLayout</a> does the layout, assuming there is a central person whose mother and father
each have their own ancestors. In this case we focus on "Bill", but any of the children of "Alice" and "Aaron" would work.
The overridden <b>add</b> function allows husband/wife pairs to be represented by a single <a>LayeredDigraphVertex</a>.
</p>
<p>For a simpler family tree, see the <a href="familyTree.html">family tree sample</a>.</p>
<p>
The node data representing the people, processed by the <code>setupDiagram</code> function is below.
The properties are:
<ul>
<li><b>key</b>, the unique ID of the person</li>
<li><b>n</b>, the person's name</li>
<li><b>s</b>, the person's sex</li>
<li><b>m</b>, the person's mother's key</li>
<li><b>f</b>, the person's father's key</li>
<li><b>ux</b>, the person's wife</li>
<li><b>vir</b>, the person's husband</li>
<li><b>a</b>, an Array of the attributes or markers that the person has</li>
</ul>
</p>
<pre id="peopleData">
[
{ key: 0, n: "Aaron", s: "M", m:-10, f:-11, ux: 1, a: ["C", "F", "K"] },
{ key: 1, n: "Alice", s: "F", m:-12, f:-13, a: ["B", "H", "K"] },
{ key: 2, n: "Bob", s: "M", m: 1, f: 0, ux: 3, a: ["C", "H", "L"] },
{ key: 3, n: "Barbara", s: "F", a: ["C"] },
{ key: 4, n: "Bill", s: "M", m: 1, f: 0, ux: 5, a: ["E", "H"] },
{ key: 5, n: "Brooke", s: "F", a: ["B", "H", "L"] },
{ key: 6, n: "Claire", s: "F", m: 1, f: 0, a: ["C"] },
{ key: 7, n: "Carol", s: "F", m: 1, f: 0, a: ["C", "I"] },
{ key: 8, n: "Chloe", s: "F", m: 1, f: 0, vir: 9, a: ["E"] },
{ key: 9, n: "Chris", s: "M", a: ["B", "H"] },
{ key: 10, n: "Ellie", s: "F", m: 3, f: 2, a: ["E", "G"] },
{ key: 11, n: "Dan", s: "M", m: 3, f: 2, a: ["B", "J"] },
{ key: 12, n: "Elizabeth", s: "F", vir: 13, a: ["J"] },
{ key: 13, n: "David", s: "M", m: 5, f: 4, a: ["B", "H"] },
{ key: 14, n: "Emma", s: "F", m: 5, f: 4, a: ["E", "G"] },
{ key: 15, n: "Evan", s: "M", m: 8, f: 9, a: ["F", "H"] },
{ key: 16, n: "Ethan", s: "M", m: 8, f: 9, a: ["D", "K", "S"] },
{ key: 17, n: "Eve", s: "F", vir: 16, a: ["B", "F", "L", "S"] },
{ key: 18, n: "Emily", s: "F", m: 8, f: 9 },
{ key: 19, n: "Fred", s: "M", m: 17, f: 16, a: ["B"] },
{ key: 20, n: "Faith", s: "F", m: 17, f: 16, a: ["L"] },
{ key: 21, n: "Felicia", s: "F", m: 12, f: 13, a: ["H"] },
{ key: 22, n: "Frank", s: "M", m: 12, f: 13, a: ["B", "H"] },
// "Aaron"'s ancestors
{ key: -10, n: "Paternal Grandfather", s: "M", m: -33, f: -32, ux: -11, a: ["A"] },
{ key: -11, n: "Paternal Grandmother", s: "F", a: ["E"] },
{ key: -32, n: "Paternal Great", s: "M", ux: -33, a: ["F", "H"] },
{ key: -33, n: "Paternal Great", s: "F" },
{ key: -40, n: "Great Uncle", s: "M", m: -33, f: -32, a: ["F", "H"] },
{ key: -41, n: "Great Aunt", s: "F", m: -33, f: -32, a: ["B", "I"] },
{ key: -20, n: "Uncle", s: "M", m: -11, f: -10, a: ["A"] },
// "Alice"'s ancestors
{ key: -12, n: "Maternal Grandfather", s: "M", ux: -13, a: ["D", "L"] },
{ key: -13, n: "Maternal Grandmother", s: "F", m: -31, f: -30, a: ["H"] },
{ key: -21, n: "Aunt", s: "F", m: -13, f: -12, a: ["C", "I"] },
{ key: -22, n: "uncle", s: "M", ux: -21 },
{ key: -23, n: "cousin", s: "M", m: -21, f: -22 },
{ key: -30, n: "Maternal Great", s: "M", ux: -31, a: ["D", "J"] },
{ key: -31, n: "Maternal Great", s: "F", m: -50, f: -51, a: ["B", "H", "L"] },
{ key: -42, n: "Great Uncle", s: "M", m: -30, f: -31, a: ["C", "J"] },
{ key: -43, n: "Great Aunt", s: "F", m: -30, f: -31, a: ["E", "G"] },
{ key: -50, n: "Maternal Great Great", s: "F", ux: -51, a: ["D", "I"] },
{ key: -51, n: "Maternal Great Great", s: "M", a: ["B", "H"] }
]
</pre>
</div>
</body>
</html>
+94
View File
@@ -0,0 +1,94 @@
<!DOCTYPE html>
<html>
<head>
<title>ToolManager.gestureBehavior Sample</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Example of ToolManager.gestureBehavior property." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
// define a simple Node template
var myNodeTemplate =
$(go.Node, "Auto",
$(go.Shape, "RoundedRectangle", { strokeWidth: 0 },
new go.Binding("fill", "color")),
$(go.TextBlock,
{ margin: 8 },
new go.Binding("text", "key"))
);
var myModel = new go.GraphLinksModel(
[
{ key: "Alpha", color: "lightblue" },
{ key: "Beta", color: "orange" },
{ key: "Gamma", color: "lightgreen" },
{ key: "Delta", color: "pink" }
],
[
{ from: "Alpha", to: "Beta" },
{ from: "Alpha", to: "Gamma" },
{ from: "Beta", to: "Beta" },
{ from: "Gamma", to: "Delta" },
{ from: "Delta", to: "Alpha" }
]);
myDiagram = $(go.Diagram, "myDiagramDiv", // create a Diagram for the DIV HTML element
{
allowHorizontalScroll: false, allowVerticalScroll: false,
"panningTool.isEnabled": false,
"toolManager.gestureBehavior": go.ToolManager.GestureCancel,
model: myModel,
nodeTemplate: myNodeTemplate
});
}
function changegestureBehavior(id) {
switch (id) {
case "GestureZoom":
myDiagram.toolManager.gestureBehavior = go.ToolManager.GestureZoom;
break;
case "GestureCancel":
myDiagram.toolManager.gestureBehavior = go.ToolManager.GestureCancel;
break;
case "GestureNone":
myDiagram.toolManager.gestureBehavior = go.ToolManager.GestureNone;
break;
}
}
</script>
</head>
<body onload="init()">
<div id="sample">
<p>
This sample demonstrates the different values of <a>ToolManager.gestureBehavior</a>.
</p>
<p>
<ul>
<li><a>ToolManager,GestureZoom</a> is the default value: Pinch gestures will zoom the Diagram.
<li><a>ToolManager,GestureNone</a>: Pinch gestures zoom the browser page instead of the Diagram.
<li><a>ToolManager,GestureCancel</a>: Pinch gestures will do nothing.
</ul>
</p>
<p>
Set the value for the Diagram below:
</p>
<p><label><input type="radio" id="GestureZoom" onclick="changegestureBehavior(this.id)" name="group1"><code>go.ToolManager.GestureZoom;</code></label>
<p><label><input type="radio" id="GestureCancel" onclick="changegestureBehavior(this.id)" name="group1" checked="checked"><code>go.ToolManager.GestureCancel;</code></label>
<p><label><input type="radio" id="GestureNone" onclick="changegestureBehavior(this.id)" name="group1"><code>go.ToolManager.GestureNone;</code></label>
<div id="myDiagramDiv" style="border: solid 1px black; width:400px; height:400px"></div>
</div>
</body>
</html>
+487
View File
@@ -0,0 +1,487 @@
<!DOCTYPE html>
<html>
<head>
<title>Grafcet Diagrams</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A Grafcet diagram editor, showing buttons for creating new nodes and links related to the selected node." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
allowLink: false, // linking is only started via buttons, not modelessly;
// see the "startLink..." functions and CustomLinkingTool defined below
// double-click in the background creates a new "Start" node
"clickCreatingTool.archetypeNodeData": { category: "Start", step: 1, text: "Action" },
linkingTool: new CustomLinkingTool(), // defined below to automatically turn on allowLink
"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("saveModel");
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);
}
});
// This implements a selection Adornment that is a horizontal bar of command buttons
// that appear when the user selects a node.
// Each button has a click function to execute the command, a tooltip for a textual description,
// and a Binding of "visible" to hide the button if it cannot be executed for that particular node.
var commandsAdornment =
$("ContextMenu",
$(go.Panel, "Auto",
$(go.Shape, { fill: null, stroke: "deepskyblue", strokeWidth: 2, shadowVisible: false }),
$(go.Placeholder)
),
$(go.Panel, "Horizontal",
{ defaultStretch: go.GraphObject.Vertical },
$("Button",
$(go.Shape,
{
geometryString: "M0 0 L10 0",
fill: null, stroke: "red", margin: 3
}),
{ click: addExclusive, toolTip: makeTooltip("Add Exclusive") },
new go.Binding("visible", "", canAddSplit).ofObject()),
$("Button",
$(go.Shape,
{
geometryString: "M0 0 L10 0 M0 3 10 3",
fill: null, stroke: "red", margin: 3
}),
{ click: addParallel, toolTip: makeTooltip("Add Parallel") },
new go.Binding("visible", "", canAddSplit).ofObject()),
$("Button",
$(go.Shape,
{
geometryString: "M0 0 L10 0 10 6 0 6z",
fill: "lightyellow", margin: 3
}),
{ click: addStep, toolTip: makeTooltip("Add Step") },
new go.Binding("visible", "", canAddStep).ofObject()),
$("Button",
$(go.Shape,
{
geometryString: "M0 0 M5 0 L5 10 M3 8 5 10 7 8 M10 0",
fill: null, margin: 3
}),
{ click: startLinkDown, toolTip: makeTooltip("Draw Link Down") },
new go.Binding("visible", "", canStartLink).ofObject()),
$("Button",
$(go.Shape,
{
geometryString: "M0 0 M3 0 L3 2 7 2 7 6 3 6 3 10 M1 8 3 10 5 8 M10 0",
fill: null, margin: 3
}),
{ click: startLinkAround, toolTip: makeTooltip("Draw Link Skip") },
new go.Binding("visible", "", canStartLink).ofObject()),
$("Button",
$(go.Shape,
{
geometryString: "M0 0 M3 2 L3 0 7 0 7 10 3 10 3 8 M5 6 7 4 9 6 M10 0",
fill: null, margin: 3
}),
{ click: startLinkUp, toolTip: makeTooltip("Draw Link Repeat") },
new go.Binding("visible", "", canStartLink).ofObject())
)
);
function makeTooltip(str) { // a helper function for defining tooltips for buttons
return $("ToolTip",
$(go.TextBlock, str));
}
// Commands for adding new Nodes
function addStep(e, obj) {
var node = obj.part.adornedPart;
var model = myDiagram.model;
model.startTransaction("add Step");
var loc = node.location.copy();
loc.y += 50;
var nodedata = { location: go.Point.stringify(loc) };
model.addNodeData(nodedata);
var nodekey = model.getKeyForNodeData(nodedata);
var linkdata = { from: model.getKeyForNodeData(node.data), to: nodekey, text: "c" };
model.addLinkData(linkdata);
var newnode = myDiagram.findNodeForData(nodedata);
myDiagram.select(newnode);
model.commitTransaction("add Step");
}
function canAddStep(adorn) {
var node = adorn.adornedPart;
if (node.category === "" || node.category === "Start") {
return node.findLinksOutOf().count === 0;
} else if (node.category === "Parallel" || node.category === "Exclusive") {
return true;
}
return false;
}
function addParallel(e, obj) { addSplit(obj.part.adornedPart, "Parallel"); }
function addExclusive(e, obj) { addSplit(obj.part.adornedPart, "Exclusive"); }
function addSplit(node, type) {
var model = myDiagram.model;
model.startTransaction("add " + type);
var loc = node.location.copy();
loc.y += 50;
var nodedata = { category: type, location: go.Point.stringify(loc) };
model.addNodeData(nodedata);
var nodekey = model.getKeyForNodeData(nodedata);
var linkdata = { from: model.getKeyForNodeData(node.data), to: nodekey };
model.addLinkData(linkdata);
var newnode = myDiagram.findNodeForData(nodedata);
myDiagram.select(newnode);
model.commitTransaction("add " + type);
}
function canAddSplit(adorn) {
var node = adorn.adornedPart;
if (node.category === "" || node.category === "Start") {
return node.findLinksOutOf().count === 0;
} else if (node.category === "Parallel" || node.category === "Exclusive") {
return false;
}
return false;
}
// Commands for starting drawing new Links
function startLinkDown(e, obj) { startLink(obj.part.adornedPart, "", "c"); }
function startLinkAround(e, obj) { startLink(obj.part.adornedPart, "Skip", "s"); }
function startLinkUp(e, obj) { startLink(obj.part.adornedPart, "Repeat", "r"); }
function startLink(node, category, condition) {
var tool = myDiagram.toolManager.linkingTool;
// to control what kind of Link is created,
// change the LinkingTool.archetypeLinkData's category
myDiagram.model.setCategoryForLinkData(tool.archetypeLinkData, category);
// also change the text indicating the condition, which the user can edit
tool.archetypeLinkData.text = condition;
tool.startObject = node.port;
myDiagram.currentTool = tool;
tool.doActivate();
}
function canStartLink(adorn) {
var node = adorn.adornedPart;
return true; // this could be smarter
}
// The various kinds of Nodes
// a helper function that declares common properties for all kinds of nodes
function commonNodeStyle() {
return [
{
locationSpot: go.Spot.Center,
selectionAdornmentTemplate: commandsAdornment // shared selection Adornment
},
new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify),
];
}
myDiagram.nodeTemplateMap.add("Start",
$(go.Node, "Horizontal", commonNodeStyle(),
{ locationObjectName: "STEPPANEL", selectionObjectName: "STEPPANEL" },
$(go.Panel, "Auto",
{ // this is the port element, not the whole Node
name: "STEPPANEL", portId: "",
fromSpot: go.Spot.Bottom, fromLinkable: true
},
$(go.Shape, { fill: "lightgreen" }),
$(go.Panel, "Auto",
{ margin: 3 },
$(go.Shape, { fill: null, minSize: new go.Size(20, 20) }),
$(go.TextBlock, "Start",
{ margin: 3, editable: true },
new go.Binding("text", "step").makeTwoWay())
)
),
// a connector line between the texts
$(go.Shape, "LineH", { width: 10, height: 1 }),
// the boxed, editable text on the side
$(go.Panel, "Auto",
$(go.Shape, { fill: "white" }),
$(go.TextBlock, "Action",
{ margin: 3, editable: true },
new go.Binding("text", "text").makeTwoWay())
)
));
myDiagram.nodeTemplateMap.add("",
$(go.Node, "Horizontal", commonNodeStyle(),
{ locationObjectName: "STEPPANEL", selectionObjectName: "STEPPANEL" },
$(go.Panel, "Auto",
{ // this is the port element, not the whole Node
name: "STEPPANEL", portId: "",
fromSpot: go.Spot.Bottom, fromLinkable: true,
toSpot: go.Spot.Top, toLinkable: true
},
$(go.Shape, { fill: "lightyellow", minSize: new go.Size(20, 20) }),
$(go.TextBlock, "Step",
{ margin: 3, editable: true },
new go.Binding("text", "step").makeTwoWay())
),
$(go.Shape, "LineH", { width: 10, height: 1 }),
$(go.Panel, "Auto",
$(go.Shape, { fill: "white" }),
$(go.TextBlock, "Action",
{ margin: 3, editable: true },
new go.Binding("text", "text").makeTwoWay())
)
));
var resizeAdornment =
$(go.Adornment, go.Panel.Spot,
$(go.Placeholder),
$(go.Shape, // left resize handle
{
alignment: go.Spot.Left, cursor: "col-resize",
desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "dodgerblue"
}),
$(go.Shape, // right resize handle
{
alignment: go.Spot.Right, cursor: "col-resize",
desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "dodgerblue"
})
);
myDiagram.nodeTemplateMap.add("Parallel",
$(go.Node, commonNodeStyle(),
{ // special resizing: just at the ends
resizable: true, resizeObjectName: "SHAPE", resizeAdornmentTemplate: resizeAdornment,
fromLinkable: true, toLinkable: true
},
$(go.Shape,
{ // horizontal pair of lines stretched to an initial width of 200
name: "SHAPE", geometryString: "M0 0 L100 0 M0 4 L100 4",
fill: "transparent", stroke: "red", width: 200
},
new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify))
));
myDiagram.nodeTemplateMap.add("Exclusive",
$(go.Node, commonNodeStyle(),
{ // special resizing: just at the ends
resizable: true, resizeObjectName: "SHAPE", resizeAdornmentTemplate: resizeAdornment,
fromLinkable: true, toLinkable: true
},
$(go.Shape,
{ // horizontal line stretched to an initial width of 200
name: "SHAPE", geometryString: "M0 0 L100 0",
fill: "transparent", stroke: "red", width: 200
},
new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify))
));
// the various kinds of Links
myDiagram.linkTemplateMap.add("",
$(BarLink, // subclass defined below
{ routing: go.Link.Orthogonal },
$(go.Shape,
{ strokeWidth: 1.5 }),
$(go.Shape, "LineH", // only visible when there is text
{ width: 20, height: 1, visible: false },
new go.Binding("visible", "text", function(t) { return t !== ""; })),
$(go.TextBlock, // only visible when there is text
{ alignmentFocus: new go.Spot(0, 0.5, -12, 0), editable: true },
new go.Binding("text", "text").makeTwoWay(),
new go.Binding("visible", "text", function(t) { return t !== ""; }))
));
myDiagram.linkTemplateMap.add("Skip",
$(go.Link,
{
routing: go.Link.AvoidsNodes,
fromSpot: go.Spot.Bottom, toSpot: go.Spot.Top,
fromEndSegmentLength: 4, toEndSegmentLength: 4
},
$(go.Shape,
{ strokeWidth: 1.5 }),
$(go.Shape, "LineH", // only visible when there is text
{ width: 20, height: 1, visible: false },
new go.Binding("visible", "text", function(t) { return t !== ""; })),
$(go.TextBlock, // only visible when there is text
{ alignmentFocus: new go.Spot(1, 0.5, 12, 0), editable: true },
new go.Binding("text", "text").makeTwoWay(),
new go.Binding("visible", "text", function(t) { return t !== ""; }))
));
myDiagram.linkTemplateMap.add("Repeat",
$(go.Link,
{
routing: go.Link.AvoidsNodes,
fromSpot: go.Spot.Bottom, toSpot: go.Spot.Top,
fromEndSegmentLength: 4, toEndSegmentLength: 4
},
$(go.Shape,
{ strokeWidth: 1.5 }),
$(go.Shape,
{ toArrow: "OpenTriangle", segmentIndex: 3, segmentFraction: 0.75 }),
$(go.Shape,
{ toArrow: "OpenTriangle", segmentIndex: 3, segmentFraction: 0.25 }),
$(go.Shape, "LineH", // only visible when there is text
{ width: 20, height: 1, visible: false },
new go.Binding("visible", "text", function(t) { return t !== ""; })),
$(go.TextBlock, // only visible when there is text
{ alignmentFocus: new go.Spot(1, 0.5, 12, 0), editable: true },
new go.Binding("text", "text").makeTwoWay(),
new go.Binding("visible", "text", function(t) { return t !== ""; }))
));
// start off with a simple diagram
load();
}
// This custom LinkingTool just turns on Diagram.allowLink when it starts,
// and turns it off again when it stops so that users cannot draw new links modelessly.
function CustomLinkingTool() {
go.LinkingTool.call(this);
}
go.Diagram.inherit(CustomLinkingTool, go.LinkingTool);
// user-drawn linking is normally disabled,
// but needs to be turned on when using this tool
CustomLinkingTool.prototype.doStart = function() {
this.diagram.allowLink = true;
go.LinkingTool.prototype.doStart.call(this);
};
CustomLinkingTool.prototype.doStop = function() {
go.LinkingTool.prototype.doStop.call(this);
this.diagram.allowLink = false;
};
// end CustomLinkingTool
// This custom Link class is smart about computing the link point and direction
// at "Parallel" and "Exclusive" nodes.
function BarLink() {
go.Link.call(this);
}
go.Diagram.inherit(BarLink, go.Link);
BarLink.prototype.getLinkPoint = function(node, port, spot, from, ortho, othernode, otherport) {
var r = new go.Rect(port.getDocumentPoint(go.Spot.TopLeft),
port.getDocumentPoint(go.Spot.BottomRight));
var op = otherport.getDocumentPoint(go.Spot.Center);
var below = op.y > r.centerY;
var y = below ? r.bottom : r.top;
if (node.category === "Parallel" || node.category === "Exclusive") {
if (op.x < r.left) return new go.Point(r.left, y);
if (op.x > r.right) return new go.Point(r.right, y);
return new go.Point(op.x, y);
} else {
return new go.Point(r.centerX, y);
}
};
BarLink.prototype.getLinkDirection = function(node, port, linkpoint, spot, from, ortho, othernode, otherport) {
var p = port.getDocumentPoint(go.Spot.Center);
var op = otherport.getDocumentPoint(go.Spot.Center);
var below = op.y > p.y;
return below ? 90 : 270;
};
// end BarLink class
// save a model to and load a model from JSON text, displayed below the Diagram
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 id="myDiagramDiv" style="border: solid 1px black; width: 800px; height: 600px"></div>
<p>
A grafcet diagram is similar to a <a href="sequentialFunction.html">sequential function chart</a>.
</p>
<p>
Select a Node to show a list of Buttons that enable creating new Nodes or drawing new Links.
These buttons are defined as an adornment that is used in a common <a>Part.selectionAdornmentTemplate</a>.
This diagram uses many custom functions, including an overridden <a>LinkingTool</a> and a special
Link class, <b>BarLink</b>.
</p>
<div id="buttons">
<button id="saveModel" onclick="save()">Save</button>
<button id="loadModel" onclick="load()">Load</button>
Diagram Model saved in JSON format:
</div>
<textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "go.GraphLinksModel",
"nodeDataArray": [
{"key":1, "category":"Start", "location":"300 50", "step":"1", "text":"Action 1"},
{"key":2, "category":"Parallel", "location":"300 100"},
{"key":3, "location":"225 125", "step":"3", "text":"Action 2"},
{"key":4, "location":"325 150", "step":"4", "text":"Action 3"},
{"key":5, "location":"225 175", "step":"5", "text":"Action 4"},
{"key":6, "category":"Parallel", "location":"300 200"},
{"key":7, "location":"300 250", "step":"7", "text":"Action 6"},
{"key":11, "category":"Start", "location":"300 350", "step":"11", "text":"Action 1"},
{"key":12, "category":"Exclusive", "location":"300 400"},
{"key":13, "location":"225 450", "step":"13", "text":"Action 2"},
{"key":14, "location":"325 475", "step":"14", "text":"Action 3"},
{"key":15, "location":"225 500", "step":"15", "text":"Action 4"},
{"key":16, "category":"Exclusive", "location":"300 550"},
{"key":17, "location":"300 600", "step":"17", "text":"Action 6"},
{"key":21, "location":"500 50", "step":"21", "text":"Act 1"},
{"key":22, "location":"500 100", "step":"22", "text":"Act 2"},
{"key":23, "location":"500 150", "step":"23", "text":"Act 3"},
{"key":24, "location":"500 200", "step":"24", "text":"Act 4"},
{"key":31, "location":"500 400", "step":"31", "text":"Act 1"},
{"key":32, "location":"500 450", "step":"32", "text":"Act 2"},
{"key":33, "location":"500 500", "step":"33", "text":"Act 3"},
{"key":34, "location":"500 550", "step":"34", "text":"Act 4"}
],
"linkDataArray": [
{"from":1, "to":2, "text":"condition 1"},
{"from":2, "to":3},
{"from":2, "to":4},
{"from":3, "to":5, "text":"condition 2"},
{"from":4, "to":6},
{"from":5, "to":6},
{"from":6, "to":7, "text":"condition 5"},
{"from":11, "to":12, "text":"condition 1"},
{"from":12, "to":13, "text":"condition 12"},
{"from":12, "to":14, "text":"condition 13"},
{"from":13, "to":15, "text":"condition 2"},
{"from":14, "to":16, "text":"condition 14"},
{"from":15, "to":16, "text":"condition 15"},
{"from":16, "to":17, "text":"condition 5"},
{"from":21, "to":22, "text":"c1"},
{"from":22, "to":23, "text":"c2"},
{"from":23, "to":24, "text":"c3"},
{"from":21, "to":24, "text":"c14", "category":"Skip"},
{"from":31, "to":32, "text":"c1"},
{"from":32, "to":33, "text":"c2"},
{"from":33, "to":34, "text":"c3"},
{"from":33, "to":32, "text":"c14", "category":"Repeat"}
]}
</textarea>
</div>
</body>
</html>
+146
View File
@@ -0,0 +1,146 @@
<!DOCTYPE html>
<html>
<head>
<title>Grouping</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="A diagram holding groups that incrementally grow the diagram as groups are expanded." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
myDiagram =
$(go.Diagram, "myDiagramDiv", // Diagram refers to its DIV HTML element by id
{
layout: $(go.TreeLayout, // the layout for the entire diagram
{
angle: 90,
arrangement: go.TreeLayout.ArrangementHorizontal,
isRealtime: false
})
});
// define the node template for non-groups
myDiagram.nodeTemplate =
$(go.Node, "Auto",
$(go.Shape, "Rectangle",
{ stroke: null, strokeWidth: 0 },
new go.Binding("fill", "key")),
$(go.TextBlock,
{ margin: 7, font: "Bold 14px Sans-Serif" },
//the text, color, and key are all bound to the same property in the node data
new go.Binding("text", "key"))
);
myDiagram.linkTemplate =
$(go.Link,
{ routing: go.Link.Orthogonal, corner: 10 },
$(go.Shape, { strokeWidth: 2 }),
$(go.Shape, { toArrow: "OpenTriangle" })
);
// define the group template
myDiagram.groupTemplate =
$(go.Group, "Auto",
{ // define the group's internal layout
layout: $(go.TreeLayout,
{ angle: 90, arrangement: go.TreeLayout.ArrangementHorizontal, isRealtime: false }),
// the group begins unexpanded;
// upon expansion, a Diagram Listener will generate contents for the group
isSubGraphExpanded: false,
// when a group is expanded, if it contains no parts, generate a subGraph inside of it
subGraphExpandedChanged: function(group) {
if (group.memberParts.count === 0) {
randomGroup(group.data.key);
}
}
},
$(go.Shape, "Rectangle",
{ fill: null, stroke: "gray", strokeWidth: 2 }),
$(go.Panel, "Vertical",
{ defaultAlignment: go.Spot.Left, margin: 4 },
$(go.Panel, "Horizontal",
{ defaultAlignment: go.Spot.Top },
// the SubGraphExpanderButton is a panel that functions as a button to expand or collapse the subGraph
$("SubGraphExpanderButton"),
$(go.TextBlock,
{ font: "Bold 18px Sans-Serif", margin: 4 },
new go.Binding("text", "key"))
),
// create a placeholder to represent the area where the contents of the group are
$(go.Placeholder,
{ padding: new go.Margin(0, 10) })
) // end Vertical Panel
); // end Group
// generate the initial model
randomGroup();
}
// Generate a random number of nodes, including groups.
// If a group's key is given as a parameter, put these nodes inside it
function randomGroup(group) {
// all modification to the diagram is within this transaction
myDiagram.startTransaction("addGroupContents");
var addedKeys = []; // this will contain the keys of all nodes created
var groupCount = 0; // the number of groups in the diagram, to determine the numbers in the keys of new groups
myDiagram.nodes.each(function(node) {
if (node instanceof go.Group) groupCount++;
});
// create a random number of groups
// ensure there are at least 10 groups in the diagram
var groups = Math.floor(Math.random() * 2);
if (groupCount < 10) groups += 1;
for (var i = 0; i < groups; i++) {
var name = "group" + (i + groupCount);
myDiagram.model.addNodeData({ key: name, isGroup: true, group: group });
addedKeys.push(name);
}
var nodes = Math.floor(Math.random() * 3) + 2;
// create a random number of non-group nodes
for (var i = 0; i < nodes; i++) {
var color = go.Brush.randomColor();
// make sure the color, which will be the node's key, is unique in the diagram before adding the new node
if (myDiagram.findPartForKey(color) === null) {
myDiagram.model.addNodeData({ key: color, group: group });
addedKeys.push(color);
}
}
// add at least one link from each node to another
// this could result in clusters of nodes unreachable from each other, but no lone nodes
var arr = [];
for (var x in addedKeys) arr.push(addedKeys[x]);
arr.sort(function(x, y) { return Math.random(2) - 1; });
for (var i = 0; i < arr.length; i++) {
var from = Math.floor(Math.random() * (arr.length - i)) + i;
if (from !== i) {
myDiagram.model.addLinkData({ from: arr[from], to: arr[i] });
}
}
myDiagram.commitTransaction("addGroupContents");
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="height:600px;width:100%;border:1px solid black"></div>
<p>
This sample demonstrates subgraphs that are created only as groups are expanded.
</p>
<p>
The model is initially a random number of nodes, including some groups, in a tree layout.
When a group is expanded, the <a>Group.subGraphExpandedChanged</a> event handler calls a function to generate a random number of nodes
in a tree layout inside the group if it did not contain none any.
Each non-group node added has a unique random color, and links are added by giving each node one link to another node.
</p>
<p>
The addition of nodes and links is performed within a transaction to ensure that the diagram updates itself properly.
The diagram's tree layout and the tree layouts within each group are performed again when a sub-graph is expanded or collapsed.
</p>
</div>
</body>
</html>
+112
View File
@@ -0,0 +1,112 @@
<!DOCTYPE html>
<html>
<head>
<title>Buttons that show on Hover</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="When the mouse hovers over a node, show a set of Buttons that could perform various actions." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<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
myDiagram = $(go.Diagram, "myDiagramDiv", // create a Diagram for the DIV HTML element
{
hoverDelay: 200, // controls how long to wait motionless (msec) before showing Adornment
"undoManager.isEnabled": true // enable undo & redo
});
// this is shown by the mouseHover event handler
var nodeHoverAdornment =
$(go.Adornment, "Spot",
{
background: "transparent",
// hide the Adornment when the mouse leaves it
mouseLeave: function(e, obj) {
var ad = obj.part;
ad.adornedPart.removeAdornment("mouseHover");
}
},
$(go.Placeholder,
{
background: "transparent", // to allow this Placeholder to be "seen" by mouse events
isActionable: true, // needed because this is in a temporary Layer
click: function(e, obj) {
var node = obj.part.adornedPart;
node.diagram.select(node);
}
}),
$("Button",
{ alignment: go.Spot.Left, alignmentFocus: go.Spot.Right },
{ click: function(e, obj) { alert("Hi!"); } },
$(go.TextBlock, "Hi!")),
$("Button",
{ alignment: go.Spot.Right, alignmentFocus: go.Spot.Left },
{ click: function(e, obj) { alert("Bye"); } },
$(go.TextBlock, "Bye"))
);
// define a simple Node template
myDiagram.nodeTemplate =
$(go.Node, "Auto", // the Shape will go around the TextBlock
$(go.Shape, "RoundedRectangle", { strokeWidth: 0 },
// Shape.fill is bound to Node.data.color
new go.Binding("fill", "color")),
$(go.TextBlock,
{ margin: 8 }, // some room around the text
// TextBlock.text is bound to Node.data.key
new go.Binding("text", "key")),
{ // show the Adornment when a mouseHover event occurs
mouseHover: function(e, obj) {
var node = obj.part;
nodeHoverAdornment.adornedObject = node;
node.addAdornment("mouseHover", nodeHoverAdornment);
}
}
);
// but use the default Link template, by not setting Diagram.linkTemplate
// create the model data that will be represented by Nodes and Links
myDiagram.model = new go.GraphLinksModel(
[
{ key: "Alpha", color: "lightblue" },
{ key: "Beta", color: "orange" },
{ key: "Gamma", color: "lightgreen" },
{ key: "Delta", color: "pink" }
],
[
{ from: "Alpha", to: "Beta" },
{ from: "Alpha", to: "Gamma" },
{ from: "Beta", to: "Beta" },
{ from: "Gamma", to: "Delta" },
{ from: "Delta", to: "Alpha" }
]);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:400px; height:400px"></div>
<p>
This sample demonstrates buttons that appear when the user hovers over a node with the mouse.
The advantage of using an <a>Adornment</a> is that it keeps the Node template simpler.
That means there are less resources used to create nodes -- only that one adornment can be shown.
</p>
<p>
However, using a template as the <a>Part.selectionAdornmentTemplate</a> would allow for more
than one set of buttons to be shown simultaneously, one set for each selected node.
</p>
<p>
This technique does not work on touch devices.
</p>
<p>
If you want to show such an Adornment on mouseEnter and mouseLeave, rather than on mouseHover,
the code is given in the documentation for the <a>GraphObject.mouseEnter</a> property.
</p>
</div>
</body>
</html>
+246
View File
@@ -0,0 +1,246 @@
<!DOCTYPE html>
<html>
<head>
<title>Drag and Drop Example</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Use HTML5 drag-and-drop to implement dragging HTML elements onto a GoJS Diagram to create new nodes." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.draggable {
font: bold 16px sans-serif;
width: 140px;
height: 20px;
text-align: center;
background: white;
cursor: move;
margin-top: 20px;
}
.palettezone {
width: 160px;
height: 400px;
background: lightblue;
padding: 10px;
padding-top: 1px;
float: left;
}
</style>
<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
// *********************************************************
// First, set up the infrastructure to do HTML drag-and-drop
// *********************************************************
var dragged = null; // A reference to the element currently being dragged
// highlight stationary nodes during an external drag-and-drop into a Diagram
function highlight(node) { // may be null
var oldskips = myDiagram.skipsUndoManager;
myDiagram.skipsUndoManager = true;
myDiagram.startTransaction("highlight");
if (node !== null) {
myDiagram.highlight(node);
} else {
myDiagram.clearHighlighteds();
}
myDiagram.commitTransaction("highlight");
myDiagram.skipsUndoManager = oldskips;
}
// This event should only fire on the drag targets.
// Instead of finding every drag target,
// we can add the event to the document and disregard
// all elements that are not of class "draggable"
document.addEventListener("dragstart", function(event) {
if (event.target.className !== "draggable") return;
// Some data must be set to allow drag
event.dataTransfer.setData("text", event.target.textContent);
// store a reference to the dragged element and the offset of the mouse from the center of the element
dragged = event.target;
dragged.offsetX = event.offsetX - dragged.clientWidth / 2;
dragged.offsetY = event.offsetY - dragged.clientHeight / 2;
// Objects during drag will have a red border
event.target.style.border = "2px solid red";
}, false);
// This event resets styles after a drag has completed (successfully or not)
document.addEventListener("dragend", function(event) {
// reset the border of the dragged element
dragged.style.border = "";
highlight(null);
}, false);
// Next, events intended for the drop target - the Diagram div
var div = document.getElementById("myDiagramDiv");
div.addEventListener("dragenter", function(event) {
// Here you could also set effects on the Diagram,
// such as changing the background color to indicate an acceptable drop zone
// Requirement in some browsers, such as Internet Explorer
event.preventDefault();
}, false);
div.addEventListener("dragover", function(event) {
// We call preventDefault to allow a drop
// But on divs that already contain an element,
// we want to disallow dropping
if (this === myDiagram.div) {
var can = event.target;
var pixelratio = window.PIXELRATIO;
// if the target is not the canvas, we may have trouble, so just quit:
if (!(can instanceof HTMLCanvasElement)) return;
var bbox = can.getBoundingClientRect();
var bbw = bbox.width;
if (bbw === 0) bbw = 0.001;
var bbh = bbox.height;
if (bbh === 0) bbh = 0.001;
var mx = event.clientX - bbox.left * ((can.width / pixelratio) / bbw);
var my = event.clientY - bbox.top * ((can.height / pixelratio) / bbh);
var point = myDiagram.transformViewToDoc(new go.Point(mx, my));
var curnode = myDiagram.findPartAt(point, true);
if (curnode instanceof go.Node) {
highlight(curnode);
} else {
highlight(null);
}
}
if (event.target.className === "dropzone") {
// Disallow a drop by returning before a call to preventDefault:
return;
}
// Allow a drop on everything else
event.preventDefault();
}, false);
div.addEventListener("dragleave", function(event) {
// reset background of potential drop target
if (event.target.className == "dropzone") {
event.target.style.background = "";
}
highlight(null);
}, false);
// handle the user option for removing dragged items from the Palette
var remove = document.getElementById('remove');
div.addEventListener("drop", function(event) {
// prevent default action
// (open as link for some elements in some browsers)
event.preventDefault();
// Dragging onto a Diagram
if (this === myDiagram.div) {
var can = event.target;
var pixelratio = window.PIXELRATIO;
// if the target is not the canvas, we may have trouble, so just quit:
if (!(can instanceof HTMLCanvasElement)) return;
var bbox = can.getBoundingClientRect();
var bbw = bbox.width;
if (bbw === 0) bbw = 0.001;
var bbh = bbox.height;
if (bbh === 0) bbh = 0.001;
var mx = event.clientX - bbox.left * ((can.width / pixelratio) / bbw) - dragged.offsetX;
var my = event.clientY - bbox.top * ((can.height / pixelratio) / bbh) - dragged.offsetY;
var point = myDiagram.transformViewToDoc(new go.Point(mx, my));
myDiagram.startTransaction('new node');
myDiagram.model.addNodeData({
location: point,
text: event.dataTransfer.getData('text'),
color: "lightyellow"
});
myDiagram.commitTransaction('new node');
// remove dragged element from its old location
if (remove.checked) dragged.parentNode.removeChild(dragged);
}
// If we were using drag data, we could get it here, ie:
// var data = event.dataTransfer.getData('text');
}, false);
// *********************************************************
// Second, set up a GoJS Diagram
// *********************************************************
var $ = go.GraphObject.make; // for conciseness in defining templates
myDiagram = $(go.Diagram, "myDiagramDiv", // create a Diagram for the DIV HTML element
{
"undoManager.isEnabled": true
});
window.PIXELRATIO = myDiagram.computePixelRatio(); // constant needed to determine mouse coordinates on the canvas
// define a simple Node template
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{ locationSpot: go.Spot.Center },
new go.Binding('location'),
$(go.Shape, "Rectangle",
{ fill: 'white' },
// Shape.fill is bound to Node.data.color
new go.Binding("fill", "color"),
// this binding changes the Shape.fill when Node.isHighlighted changes value
new go.Binding("fill", "isHighlighted", function(h, shape) {
if (h) return "red";
var c = shape.part.data.color;
return c ? c : "white";
}).ofObject()), // binding source is Node.isHighlighted
$(go.TextBlock,
{ margin: 3, font: "bold 16px sans-serif", width: 140, textAlign: 'center' },
// TextBlock.text is bound to Node.data.key
new go.Binding("text"))
);
// but use the default Link template, by not setting Diagram.linkTemplate
// create the model data that will be represented by Nodes and Links
myDiagram.model = new go.GraphLinksModel(
[
{ text: "Alpha", color: "lightblue" },
{ text: "Beta", color: "orange" },
{ text: "Gamma", color: "lightgreen" },
{ text: "Delta", color: "pink" }
],
[]);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div style="width: 100%; display: flex; justify-content: space-between">
<div id="paletteZone" style="width: 160px; height: 400px; margin-right: 2px; background-color: lightblue; padding: 10px;">
<div class="draggable" draggable="true">Water</div>
<div class="draggable" draggable="true">Coffee</div>
<div class="draggable" draggable="true">Tea</div>
</div>
<div id="myDiagramDiv" style="flex-grow: 1; height: 400px; border: solid 1px black"></div>
</div>
<input id="remove" type="checkbox" /><label for="remove">Remove HTML item after drag</label>
<p>
The "Palette" in this sample is not a Palette (or GoJS control) at all.
It is a collection of HTML elements with draggable attributes using the
<a href="https://developer.mozilla.org/en-US/docs/DragDrop/Drag_and_Drop">HTML Drag and Drop API</a>.
</p>
<p>
This sample lets you drag these HTML elements onto the Diagram to create GoJS nodes.
As the mouse passes over stationary nodes in the Diagram, they are highlighted.
</p>
</div>
</body>
</html>
+198
View File
@@ -0,0 +1,198 @@
<!DOCTYPE html>
<html>
<head>
<title>HTML Interaction</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Show a GoJS Palette in a floating window and use an Inspector for changing the appearance of the selected node." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../assets/css/jquery-ui.min.css" />
<script src="../assets/js/jquery.min.js"></script>
<script src="../assets/js/jquery-ui.min.js"></script>
<script src="../release/go.js"></script>
<script src="../extensions/Figures.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<link rel='stylesheet' href='../extensions/DataInspector.css' />
<script src="../extensions/DataInspector.js"></script>
<script id="code">
function init() {
if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this
// Note that we do not use $ here as an alias for go.GraphObject.make because we are using $ for jQuery
var GO = go.GraphObject.make; // for conciseness in defining templates
myDiagram =
GO(go.Diagram, "myDiagramDiv",
{ "undoManager.isEnabled": true });
// define several shared Brushes
var fill1 = "rgb(105,210,231)"
var brush1 = "rgb(65,180,181)";
var fill2 = "rgb(167,219,216)"
var brush2 = "rgb(127,179,176)";
var fill3 = "rgb(224,228,204)"
var brush3 = "rgb(184,188,164)";
var fill4 = "rgb(243,134,48)"
var brush4 = "rgb(203,84,08)";
myDiagram.nodeTemplateMap.add("", // default category
GO(go.Node, "Auto",
{ locationSpot: go.Spot.Center },
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
GO(go.Shape, "Ellipse",
{ strokeWidth: 2, fill: fill1, name: "SHAPE" },
new go.Binding("figure", "figure"),
new go.Binding("fill", "fill"),
new go.Binding("stroke", "stroke")
),
GO(go.TextBlock,
{
margin: 5,
maxSize: new go.Size(200, NaN),
wrap: go.TextBlock.WrapFit,
textAlign: "center",
editable: true,
font: "bold 9pt Helvetica, Arial, sans-serif",
name: "TEXT"
},
new go.Binding("text", "text").makeTwoWay())));
// On selection changed, make sure infoDraggable will resize as necessary
myDiagram.addDiagramListener("ChangedSelection", function(diagramEvent) {
var idrag = document.getElementById("infoDraggable");
idrag.style.width = "";
idrag.style.height = "";
});
// initialize the Palette that is in a floating, draggable HTML container
myPalette = new go.Palette("myPaletteDiv"); // must name or refer to the DIV HTML element
myPalette.nodeTemplateMap = myDiagram.nodeTemplateMap;
myPalette.model = new go.GraphLinksModel([
{ text: "Lake", fill: fill1, stroke: brush1, figure: "Hexagon" },
{ text: "Ocean", fill: fill2, stroke: brush2, figure: "Rectangle" },
{ text: "Sand", fill: fill3, stroke: brush3, figure: "Diamond" },
{ text: "Goldfish", fill: fill4, stroke: brush4, figure: "Octagon" }
]);
myPalette.addDiagramListener("InitialLayoutCompleted", function(diagramEvent) {
var pdrag = document.getElementById("paletteDraggable");
var palette = diagramEvent.diagram;
pdrag.style.width = palette.documentBounds.width + 28 + "px"; // account for padding/borders
pdrag.style.height = palette.documentBounds.height + 38 + "px";
});
$(function() {
$("#paletteDraggable").draggable({ handle: "#paletteDraggableHandle" }).resizable({
// After resizing, perform another layout to fit everything in the palette's viewport
stop: function() { myPalette.layoutDiagram(true); }
});
$("#infoDraggable").draggable({ handle: "#infoDraggableHandle" });
var inspector = new Inspector('myInfo', myDiagram,
{
properties: {
// key would be automatically added for nodes, but we want to declare it read-only also:
"key": { readOnly: true, show: Inspector.showIfPresent },
// fill and stroke would be automatically added for nodes, but we want to declare it a color also:
"fill": { show: Inspector.showIfPresent, type: 'color' },
"stroke": { show: Inspector.showIfPresent, type: 'color' }
}
});
});
}
</script>
<style type="text/css">
.draggable {
display: inline-block;
vertical-align: top;
border: 4px solid #BBB;
border-radius: 4px;
background-color: #F5F5F5;
position: absolute;
top: 20px;
left: 20px;
z-index: 500;
}
.handle {
background-color: lightblue;
cursor: move;
text-align: center;
font: bold 12px sans-serif;
}
#infoDraggable {
font: 12px helvetica, sans-serif;
min-width: 213px;
}
#myInfo {
width: 100%;
overflow: hidden;
}
#myPaletteDiv {
background-color: #F5F5F5;
width: 100%;
height: 100%;
}
/*
One simple way of making a div fill its space,
with allowances for the title (top) and the resize handle (bottom)
*/
#paletteContainer {
position: absolute;
bottom: 14px;
left: 0px;
right: 0px;
top: 14px;
}
</style>
</head>
<body onload="init()">
<div id="sample">
<div id="paletteDraggable" class="draggable" style="height: 300px;">
<div id="paletteDraggableHandle" class="handle">Palette</div>
<div id="paletteContainer">
<div id="myPaletteDiv"></div>
</div>
</div>
<div id="infoDraggable" class="draggable" style="display: inline-block; vertical-align: top; padding: 5px; top: 20px; left: 380px;">
<div id="infoDraggableHandle" class="handle">Info</div>
<div>
<div id="myInfo"></div>
</div>
</div>
<div style="display: inline-block; vertical-align: top; width:400px">
<div id="myDiagramDiv" style="background-color: whitesmoke; border: solid 1px black; height: 400px"></div>
</div>
<p>
This sample contains a draggable HTML element (using jQuery UI), which houses a GoJS Palette.
</p>
<p>
A DIV to the right of the diagram houses the <a href="../extensions/DataInspector.html">GoJS Data inspector extension</a>,
which displays some editable information about each Node.
</p>
</div>
</body>
</html>
+88
View File
@@ -0,0 +1,88 @@
<!DOCTYPE html>
<html>
<head>
<title>LightBox Style HTML Custom Context Menu</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Demonstrate context menus implemented in HTML covering the whole window." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<link rel='stylesheet' href='../extensions/LightBoxContextMenu.css' />
<script src="../extensions/LightBoxContextMenu.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<script id="code">
var myDiagram = null;
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
// Initialize the Diagram
myDiagram =
$(go.Diagram, "myDiagramDiv", // create a Diagram for the DIV HTML element
{ "undoManager.isEnabled": true });
// define a simple Node template (but use the default Link template)
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{ contextMenu: window.myHTMLLightBox }, // window.myHTMLLightBox is defined in extensions/LightBoxContextMenu.js
$(go.Shape, "RoundedRectangle", { strokeWidth: 0 },
// Shape.fill is bound to Node.data.color
new go.Binding("fill", "color")),
$(go.TextBlock,
{ margin: 8 }, // some room around the text
// TextBlock.text is bound to Node.data.key
new go.Binding("text", "key"))
);
// create the model data that will be represented by Nodes and Links
myDiagram.model = new go.GraphLinksModel(
[
{ key: "Alpha", color: "lightblue" },
{ key: "Beta", color: "orange" },
{ key: "Gamma", color: "lightgreen" },
{ key: "Delta", color: "pink" }
],
[
{ from: "Alpha", to: "Beta" },
{ from: "Alpha", to: "Gamma" },
{ from: "Beta", to: "Beta" },
{ from: "Gamma", to: "Delta" },
{ from: "Delta", to: "Alpha" }
]);
myDiagram.contextMenu = window.myHTMLLightBox; // window.myHTMLLightBox is defined in extensions/LightBoxContextMenu.js
} // end init
</script>
</head>
<body onload="init()">
<div id="sample">
<div style="display: inline-block;">
<div style="position: relative">
<div id="myDiagramDiv" style="border: solid 1px black; width:400px; height:400px"></div>
</div>
<div id="description">
<p>
This demonstrates the implementation of a custom HTML context menu using <a>HTMLInfo</a>.
This sample is also a re-implementation of the built-in <a>ContextMenuTool.defaultTouchContextMenu</a>.
</p>
<p>The implementation is contained in the files <a href="../extensions/LightBoxContextMenu.js">LightBoxContextMenu.js</a> and <a href="../extensions/LightBoxContextMenu.css">LightBoxContextMenu.css</a>. The JavaScript file exposes <code>window.myHTMLLightBox</code>, which is used in this file as the value of <code>myDiagram.contextMenu</code>.
<p>For a more regular HTML context menu implementation, see the <a href="customContextMenu.html">Custom Context Menu</a> sample.</p>
<p>Right-click or tap-hold (mobile) on a Node to bring up a context menu.
If you have a selection copied in the clipboard,
you can bring up a context menu anywhere to paste.</p>
</div>
</div>
</div>
</body>
</html>
+128
View File
@@ -0,0 +1,128 @@
<!DOCTYPE html>
<html>
<head>
<title>Icons GoJS Sample</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Use SVG geometry path strings to create vector icons, rather than using images." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../release/go.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<script src="icons.js"></script> <!-- load SVG definitions for many icons in the "icons" variable -->
<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
// a collection of colors
var colors = {
blue: "#2a6dc0",
orange: "#ea2857",
green: "#1cc1bc",
gray: "#5b5b5b",
white: "#F5F5F5"
}
// The first Diagram showcases what the Nodes might look like "in action"
myDiagram = $(go.Diagram, "myDiagramDiv",
{
"undoManager.isEnabled": true,
layout: $(go.TreeLayout)
});
// "icons" is defined in icons.js
// A data binding conversion function. Given an icon name, return a Geometry.
// This assumes that all icons want to be filled.
// This caches the Geometry, because the Geometry may be shared by multiple Shapes.
function geoFunc(geoname) {
var geo = icons[geoname];
if (geo === undefined) geo = icons["heart"]; // use this for an unknown icon name
if (typeof geo === "string") {
geo = icons[geoname] = go.Geometry.parse(geo, true); // fill each geometry
}
return geo;
}
// Define a simple template consisting of the icon surrounded by a filled circle
myDiagram.nodeTemplate =
$(go.Node, "Auto",
$(go.Shape, "Circle",
{ fill: "lightcoral", strokeWidth: 0, width: 65, height: 65 },
new go.Binding("fill", "color")),
$(go.Shape,
{ margin: 3, fill: colors["white"], strokeWidth: 0 },
new go.Binding("geometry", "geo", geoFunc)),
// Each node has a tooltip that reveals the name of its icon
{
toolTip:
$("ToolTip",
{ "Border.stroke": colors["gray"], "Border.strokeWidth": 2 },
$(go.TextBlock, { margin: 8, stroke: colors["gray"], font: "bold 16px sans-serif" },
new go.Binding("text", "geo")))
}
);
// Define a Link template that routes orthogonally, with no arrowhead
myDiagram.linkTemplate =
$(go.Link,
{ routing: go.Link.Orthogonal, corner: 5 },
$(go.Shape, { strokeWidth: 3.5, stroke: colors["gray"] })); // the link shape
// Create the model data that will be represented by Nodes and Links
myDiagram.model = new go.GraphLinksModel(
[
{ key: 1, geo: "file", color: colors["blue"] },
{ key: 2, geo: "alarm", color: colors["orange"] },
{ key: 3, geo: "lab", color: colors["blue"] },
{ key: 4, geo: "earth", color: colors["blue"] },
{ key: 5, geo: "heart", color: colors["green"] },
{ key: 6, geo: "arrow-up-right", color: colors["blue"] },
{ key: 7, geo: "html5", color: colors["orange"] },
{ key: 8, geo: "twitter", color: colors["orange"] }
],
[
{ from: 1, to: 2 },
{ from: 1, to: 3 },
{ from: 3, to: 4 },
{ from: 4, to: 5 },
{ from: 4, to: 6 },
{ from: 3, to: 7 },
{ from: 3, to: 8 }
]);
// The second Diagram showcases every icon in icons.js
myDiagram2 = $(go.Diagram, "myDiagramDiv2",
{ // share node templates between both Diagrams
nodeTemplate: myDiagram.nodeTemplate,
// simple grid layout
layout: $(go.GridLayout)
});
// Convert the icons collection into an Array of JavaScript objects
var nodeArray = [];
for (var k in icons) {
nodeArray.push({ geo: k, color: colors["blue"] });
}
myDiagram2.model.nodeDataArray = nodeArray;
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:450px; height:300px"></div>
<p>This sample shows several "icons" that were originally SVG paths, used as Shapes in GoJS.</p>
<p>Above some icons are shown in a Tree-like Diagram, below a larger selection is shown.</p>
<p>You can easily add your own shapes to GoJS by writing your own geometry strings, or by copying SVG path strings, as is done in this sample. The icons for this sample are defined in <a href="icons.js">icons.js</a>.</p>
<p><a href="../intro/geometry.html">Read more about GoJS path syntax here.</a></p>
<div id="myDiagramDiv2" style="border: solid 1px black; width:700px; height:500px"></div>
<p>The icons in this sample are from a selection of free icons at <a href="https://icomoon.io" target = "blank">icomoon.io</a></p>
</div>
</body>
</html>
+160
View File
@@ -0,0 +1,160 @@
// a selection of free icons from https://icomoon.io/
// the following paths are all pure SVG path strings that GoJS parses as Geometry strings.
var icons = {
"home":
"M32 18.451l-16-12.42-16 12.42v-5.064l16-12.42 16 12.42zM28 18v12h-8v-8h-8v8h-8v-12l12-9z",
"droplet":
"M27.998 19.797c-0-0.022-0.001-0.044-0.001-0.066-0.001-0.045-0.002-0.089-0.004-0.134-0.313-9.864-11.993-19.597-11.993-19.597s-11.68 9.733-11.993 19.598c-0.001 0.045-0.003 0.089-0.004 0.134-0 0.022-0.001 0.044-0.001 0.066-0.001 0.067-0.002 0.135-0.002 0.203 0 0.074 0.001 0.148 0.002 0.221 0 0.006 0 0.012 0 0.018 0.127 6.517 5.45 11.76 11.997 11.76s11.87-5.244 11.997-11.761c0-0.006 0-0.012 0-0.018 0.001-0.074 0.002-0.147 0.002-0.221 0-0.068-0.001-0.136-0.002-0.203zM23.998 20.148l-0 0.013c-0.041 2.103-0.892 4.073-2.395 5.548-1.504 1.477-3.495 2.291-5.604 2.291-0.389 0-0.775-0.028-1.154-0.082 4.346-2.589 7.257-7.335 7.257-12.76 0-0.608-0.037-1.207-0.108-1.796 1.259 2.311 1.939 4.462 2 6.363l0 0.005c0.001 0.030 0.002 0.059 0.002 0.089l0.001 0.045c0.001 0.046 0.002 0.091 0.002 0.137 0 0.049-0.001 0.099-0.002 0.148z",
"camera":
"M9.5 19c0 3.59 2.91 6.5 6.5 6.5s6.5-2.91 6.5-6.5-2.91-6.5-6.5-6.5-6.5 2.91-6.5 6.5zM30 8h-7c-0.5-2-1-4-3-4h-8c-2 0-2.5 2-3 4h-7c-1.1 0-2 0.9-2 2v18c0 1.1 0.9 2 2 2h28c1.1 0 2-0.9 2-2v-18c0-1.1-0.9-2-2-2zM16 27.875c-4.902 0-8.875-3.973-8.875-8.875s3.973-8.875 8.875-8.875c4.902 0 8.875 3.973 8.875 8.875s-3.973 8.875-8.875 8.875zM30 14h-4v-2h4v2z",
"pacman":
"M30.148 5.588c-2.934-3.42-7.288-5.588-12.148-5.588-8.837 0-16 7.163-16 16s7.163 16 16 16c4.86 0 9.213-2.167 12.148-5.588l-10.148-10.412 10.148-10.412zM22 3.769c1.232 0 2.231 0.999 2.231 2.231s-0.999 2.231-2.231 2.231-2.231-0.999-2.231-2.231c0-1.232 0.999-2.231 2.231-2.231z",
"spades":
"M25.549 10.88c-6.049-4.496-8.133-8.094-9.549-10.88v0c-0 0-0-0-0-0v0c-1.415 2.785-3.5 6.384-9.549 10.88-10.314 7.665-0.606 18.365 7.93 12.476-0.556 3.654-2.454 6.318-4.381 7.465v1.179h12.001v-1.179c-1.928-1.147-3.825-3.811-4.382-7.465 8.535 5.889 18.244-4.811 7.93-12.476z",
"clubs":
"M24.588 12.274c-1.845 0-3.503 0.769-4.683 2.022-0.5 0.531-1.368 1.16-2.306 1.713 0.441-1.683 1.834-3.803 2.801-4.733 1.239-1.193 2-2.87 2-4.734 0-3.59-2.859-6.503-6.4-6.541-3.541 0.038-6.4 2.951-6.4 6.541 0 1.865 0.761 3.542 2 4.734 0.967 0.93 2.36 3.050 2.801 4.733-0.939-0.553-1.806-1.182-2.306-1.713-1.18-1.253-2.838-2.022-4.683-2.022-3.575 0-6.471 2.927-6.471 6.541s2.897 6.542 6.471 6.542c1.845 0 3.503-0.792 4.683-2.045 0.525-0.558 1.451-1.254 2.447-1.832-0.094 4.615-2.298 8.005-4.541 9.341v1.179h12v-1.179c-2.244-1.335-4.448-4.726-4.541-9.341 0.995 0.578 1.922 1.274 2.447 1.832 1.18 1.253 2.838 2.045 4.683 2.045 3.575 0 6.471-2.928 6.471-6.542s-2.897-6.541-6.471-6.541z",
"diamonds":
"M16 0l-10 16 10 16 10-16z",
"connection":
"M20 18c3.308 0 6.308 1.346 8.481 3.519l-2.827 2.827c-1.449-1.449-3.449-2.346-5.654-2.346s-4.206 0.897-5.654 2.346l-2.827-2.827c2.173-2.173 5.173-3.519 8.481-3.519zM5.858 15.858c3.777-3.777 8.8-5.858 14.142-5.858s10.365 2.080 14.142 5.858l-2.828 2.828c-3.022-3.022-7.040-4.686-11.314-4.686s-8.292 1.664-11.314 4.686l-2.828-2.828zM30.899 4.201c3.334 1.41 6.329 3.429 8.899 6v0l-2.828 2.828c-4.533-4.533-10.56-7.029-16.971-7.029s-12.438 2.496-16.971 7.029l-2.828-2.828c2.571-2.571 5.565-4.589 8.899-6 3.453-1.461 7.12-2.201 10.899-2.201s7.446 0.741 10.899 2.201zM18 28c0-1.105 0.895-2 2-2s2 0.895 2 2c0 1.105-0.895 2-2 2s-2-0.895-2-2z",
"feed":
"M12 16c0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.209-1.791 4-4 4s-4-1.791-4-4zM20.761 7.204c3.12 1.692 5.239 4.997 5.239 8.796s-2.119 7.104-5.239 8.796c1.377-2.191 2.239-5.321 2.239-8.796s-0.862-6.605-2.239-8.796zM9 16c0 3.475 0.862 6.605 2.239 8.796-3.12-1.692-5.239-4.997-5.239-8.796s2.119-7.104 5.239-8.796c-1.377 2.191-2.239 5.321-2.239 8.796zM3 16c0 5.372 1.7 10.193 4.395 13.491-4.447-2.842-7.395-7.822-7.395-13.491s2.948-10.649 7.395-13.491c-2.695 3.298-4.395 8.119-4.395 13.491zM24.605 2.509c4.447 2.842 7.395 7.822 7.395 13.491s-2.948 10.649-7.395 13.491c2.695-3.298 4.395-8.119 4.395-13.491s-1.7-10.193-4.395-13.491z",
"file":
"M27.879 5.879l-3.757-3.757c-1.167-1.167-3.471-2.121-5.121-2.121h-14c-1.65 0-3 1.35-3 3v26c0 1.65 1.35 3 3 3h22c1.65 0 3-1.35 3-3v-18c0-1.65-0.955-3.955-2.121-5.121zM20 4.236c0.069 0.025 0.139 0.053 0.211 0.082 0.564 0.234 0.956 0.505 1.082 0.631l3.757 3.757c0.126 0.126 0.397 0.517 0.631 1.082 0.030 0.072 0.057 0.143 0.082 0.211h-5.764v-5.764zM26 28h-20v-24h12v8h8v16zM8 16h16v2h-16zM8 20h16v2h-16zM8 24h16v2h-16z",
"history":
"M18 2c7.732 0 14 6.268 14 14s-6.268 14-14 14v-3c2.938 0 5.701-1.144 7.778-3.222s3.222-4.84 3.222-7.778c0-2.938-1.144-5.701-3.222-7.778s-4.84-3.222-7.778-3.222c-2.938 0-5.701 1.144-7.778 3.222-1.598 1.598-2.643 3.601-3.041 5.778h5.819l-7 8-7-8h5.143c0.971-6.784 6.804-12 13.857-12zM24 14v4h-8v-10h4v6z",
"alarm":
"M16 4c-7.732 0-14 6.268-14 14s6.268 14 14 14 14-6.268 14-14-6.268-14-14-14zM16 29.25c-6.213 0-11.25-5.037-11.25-11.25s5.037-11.25 11.25-11.25c6.213 0 11.25 5.037 11.25 11.25s-5.037 11.25-11.25 11.25zM29.212 8.974c0.501-0.877 0.788-1.892 0.788-2.974 0-3.314-2.686-6-6-6-1.932 0-3.65 0.913-4.747 2.331 4.121 0.851 7.663 3.287 9.96 6.643zM12.748 2.331c-1.097-1.418-2.816-2.331-4.748-2.331-3.314 0-6 2.686-6 6 0 1.082 0.287 2.098 0.788 2.974 2.297-3.356 5.838-5.792 9.96-6.643zM14 10h2v10h-2v-10zM16 18h6v2h-6v-2z",
"box-add":
"M26 2h-20l-6 6v21c0 0.552 0.448 1 1 1h30c0.552 0 1-0.448 1-1v-21l-6-6zM16 26l-10-8h6v-6h8v6h6l-10 8zM4.828 6l2-2h18.343l2 2h-22.343z",
"box-remove":
"M26 2h-20l-6 6v21c0 0.552 0.448 1 1 1h30c0.552 0 1-0.448 1-1v-21l-6-6zM20 20v6h-8v-6h-6l10-8 10 8h-6zM4.828 6l2-2h18.343l2 2h-22.343z",
"user":
"M8 10c0-4.418 3.582-8 8-8s8 3.582 8 8c0 4.418-3.582 8-8 8s-8-3.582-8-8zM24 20h-16c-4.418 0-8 3.582-8 8v2h32v-2c0-4.418-3.582-8-8-8z",
"zoomin":
"M31.008 27.231l-7.58-6.447c-0.784-0.705-1.622-1.029-2.299-0.998 1.789-2.096 2.87-4.815 2.87-7.787 0-6.627-5.373-12-12-12s-12 5.373-12 12c0 6.627 5.373 12 12 12 2.972 0 5.691-1.081 7.787-2.87-0.031 0.677 0.293 1.515 0.998 2.299l6.447 7.58c1.104 1.226 2.907 1.33 4.007 0.23s0.997-2.903-0.23-4.007zM12 20c-4.418 0-8-3.582-8-8s3.582-8 8-8 8 3.582 8 8-3.582 8-8 8zM14 6h-4v4h-4v4h4v4h4v-4h4v-4h-4z",
"zoomout":
"M31.008 27.231l-7.58-6.447c-0.784-0.705-1.622-1.029-2.299-0.998 1.789-2.096 2.87-4.815 2.87-7.787 0-6.627-5.373-12-12-12s-12 5.373-12 12c0 6.627 5.373 12 12 12 2.972 0 5.691-1.081 7.787-2.87-0.031 0.677 0.293 1.515 0.998 2.299l6.447 7.58c1.104 1.226 2.907 1.33 4.007 0.23s0.997-2.903-0.23-4.007zM12 20c-4.418 0-8-3.582-8-8s3.582-8 8-8 8 3.582 8 8-3.582 8-8 8zM6 10h12v4h-12z",
"cog":
"M29.181 19.070c-1.679-2.908-0.669-6.634 2.255-8.328l-3.145-5.447c-0.898 0.527-1.943 0.829-3.058 0.829-3.361 0-6.085-2.742-6.085-6.125h-6.289c0.008 1.044-0.252 2.103-0.811 3.070-1.679 2.908-5.411 3.897-8.339 2.211l-3.144 5.447c0.905 0.515 1.689 1.268 2.246 2.234 1.676 2.903 0.672 6.623-2.241 8.319l3.145 5.447c0.895-0.522 1.935-0.82 3.044-0.82 3.35 0 6.067 2.725 6.084 6.092h6.289c-0.003-1.034 0.259-2.080 0.811-3.038 1.676-2.903 5.399-3.894 8.325-2.219l3.145-5.447c-0.899-0.515-1.678-1.266-2.232-2.226zM16 22.479c-3.578 0-6.479-2.901-6.479-6.479s2.901-6.479 6.479-6.479c3.578 0 6.479 2.901 6.479 6.479s-2.901 6.479-6.479 6.479z",
"cogs":
"M11.366 22.564l1.291-1.807-1.414-1.414-1.807 1.291c-0.335-0.187-0.694-0.337-1.071-0.444l-0.365-2.19h-2l-0.365 2.19c-0.377 0.107-0.736 0.256-1.071 0.444l-1.807-1.291-1.414 1.414 1.291 1.807c-0.187 0.335-0.337 0.694-0.443 1.071l-2.19 0.365v2l2.19 0.365c0.107 0.377 0.256 0.736 0.444 1.071l-1.291 1.807 1.414 1.414 1.807-1.291c0.335 0.187 0.694 0.337 1.071 0.444l0.365 2.19h2l0.365-2.19c0.377-0.107 0.736-0.256 1.071-0.444l1.807 1.291 1.414-1.414-1.291-1.807c0.187-0.335 0.337-0.694 0.444-1.071l2.19-0.365v-2l-2.19-0.365c-0.107-0.377-0.256-0.736-0.444-1.071zM7 27c-1.105 0-2-0.895-2-2s0.895-2 2-2 2 0.895 2 2-0.895 2-2 2zM32 12v-2l-2.106-0.383c-0.039-0.251-0.088-0.499-0.148-0.743l1.799-1.159-0.765-1.848-2.092 0.452c-0.132-0.216-0.273-0.426-0.422-0.629l1.219-1.761-1.414-1.414-1.761 1.219c-0.203-0.149-0.413-0.29-0.629-0.422l0.452-2.092-1.848-0.765-1.159 1.799c-0.244-0.059-0.492-0.109-0.743-0.148l-0.383-2.106h-2l-0.383 2.106c-0.251 0.039-0.499 0.088-0.743 0.148l-1.159-1.799-1.848 0.765 0.452 2.092c-0.216 0.132-0.426 0.273-0.629 0.422l-1.761-1.219-1.414 1.414 1.219 1.761c-0.149 0.203-0.29 0.413-0.422 0.629l-2.092-0.452-0.765 1.848 1.799 1.159c-0.059 0.244-0.109 0.492-0.148 0.743l-2.106 0.383v2l2.106 0.383c0.039 0.251 0.088 0.499 0.148 0.743l-1.799 1.159 0.765 1.848 2.092-0.452c0.132 0.216 0.273 0.426 0.422 0.629l-1.219 1.761 1.414 1.414 1.761-1.219c0.203 0.149 0.413 0.29 0.629 0.422l-0.452 2.092 1.848 0.765 1.159-1.799c0.244 0.059 0.492 0.109 0.743 0.148l0.383 2.106h2l0.383-2.106c0.251-0.039 0.499-0.088 0.743-0.148l1.159 1.799 1.848-0.765-0.452-2.092c0.216-0.132 0.426-0.273 0.629-0.422l1.761 1.219 1.414-1.414-1.219-1.761c0.149-0.203 0.29-0.413 0.422-0.629l2.092 0.452 0.765-1.848-1.799-1.159c0.059-0.244 0.109-0.492 0.148-0.743l2.106-0.383zM21 15.35c-2.402 0-4.35-1.948-4.35-4.35s1.948-4.35 4.35-4.35 4.35 1.948 4.35 4.35c0 2.402-1.948 4.35-4.35 4.35z",
"stats":
"M4 28h28v4h-32v-32h4zM9 26c-1.657 0-3-1.343-3-3s1.343-3 3-3c0.088 0 0.176 0.005 0.262 0.012l3.225-5.375c-0.307-0.471-0.487-1.033-0.487-1.638 0-1.657 1.343-3 3-3s3 1.343 3 3c0 0.604-0.179 1.167-0.487 1.638l3.225 5.375c0.086-0.007 0.174-0.012 0.262-0.012 0.067 0 0.133 0.003 0.198 0.007l5.324-9.316c-0.329-0.482-0.522-1.064-0.522-1.691 0-1.657 1.343-3 3-3s3 1.343 3 3c0 1.657-1.343 3-3 3-0.067 0-0.133-0.003-0.198-0.007l-5.324 9.316c0.329 0.481 0.522 1.064 0.522 1.691 0 1.657-1.343 3-3 3s-3-1.343-3-3c0-0.604 0.179-1.167 0.487-1.638l-3.225-5.375c-0.086 0.007-0.174 0.012-0.262 0.012s-0.176-0.005-0.262-0.012l-3.225 5.375c0.307 0.471 0.487 1.033 0.487 1.637 0 1.657-1.343 3-3 3z",
"bars":
"M0 26h32v4h-32zM4 18h4v6h-4zM10 10h4v14h-4zM16 16h4v8h-4zM22 4h4v20h-4z",
"bars2":
"M9 12h-6c-0.55 0-1 0.45-1 1v18c0 0.55 0.45 1 1 1h6c0.55 0 1-0.45 1-1v-18c0-0.55-0.45-1-1-1zM9 30h-6v-8h6v8zM19 8h-6c-0.55 0-1 0.45-1 1v22c0 0.55 0.45 1 1 1h6c0.55 0 1-0.45 1-1v-22c0-0.55-0.45-1-1-1zM19 30h-6v-10h6v10zM29 4h-6c-0.55 0-1 0.45-1 1v26c0 0.55 0.45 1 1 1h6c0.55 0 1-0.45 1-1v-26c0-0.55-0.45-1-1-1zM29 30h-6v-12h6v12z",
"lab":
"M29.884 25.14l-9.884-16.47v-6.671h1c0.55 0 1-0.45 1-1s-0.45-1-1-1h-10c-0.55 0-1 0.45-1 1s0.45 1 1 1h1v6.671l-9.884 16.47c-2.264 3.773-0.516 6.86 3.884 6.86h20c4.4 0 6.148-3.087 3.884-6.86zM7.532 20l6.468-10.779v-7.221h4v7.221l6.468 10.779h-16.935z",
"remove":
"M6 32h20l2-22h-24zM20 4v-4h-8v4h-10v6l2-2h24l2 2v-6h-10zM18 4h-4v-2h4v2z",
"switch":
"M20 4.581v4.249c1.131 0.494 2.172 1.2 3.071 2.099 1.889 1.889 2.929 4.4 2.929 7.071s-1.040 5.182-2.929 7.071c-1.889 1.889-4.4 2.929-7.071 2.929s-5.182-1.040-7.071-2.929c-1.889-1.889-2.929-4.4-2.929-7.071s1.040-5.182 2.929-7.071c0.899-0.899 1.94-1.606 3.071-2.099v-4.249c-5.783 1.721-10 7.077-10 13.419 0 7.732 6.268 14 14 14s14-6.268 14-14c0-6.342-4.217-11.698-10-13.419zM14 0h4v16h-4z",
"tree":
"M30.5 24h-0.5v-6.5c0-1.93-1.57-3.5-3.5-3.5h-8.5v-4h0.5c0.825 0 1.5-0.675 1.5-1.5v-5c0-0.825-0.675-1.5-1.5-1.5h-5c-0.825 0-1.5 0.675-1.5 1.5v5c0 0.825 0.675 1.5 1.5 1.5h0.5v4h-8.5c-1.93 0-3.5 1.57-3.5 3.5v6.5h-0.5c-0.825 0-1.5 0.675-1.5 1.5v5c0 0.825 0.675 1.5 1.5 1.5h5c0.825 0 1.5-0.675 1.5-1.5v-5c0-0.825-0.675-1.5-1.5-1.5h-0.5v-6h8v6h-0.5c-0.825 0-1.5 0.675-1.5 1.5v5c0 0.825 0.675 1.5 1.5 1.5h5c0.825 0 1.5-0.675 1.5-1.5v-5c0-0.825-0.675-1.5-1.5-1.5h-0.5v-6h8v6h-0.5c-0.825 0-1.5 0.675-1.5 1.5v5c0 0.825 0.675 1.5 1.5 1.5h5c0.825 0 1.5-0.675 1.5-1.5v-5c0-0.825-0.675-1.5-1.5-1.5zM6 30h-4v-4h4v4zM18 30h-4v-4h4v4zM14 8v-4h4v4h-4zM30 30h-4v-4h4v4z",
"cloud":
"M32 20.548c0-2.565-1.771-4.716-4.156-5.296-0.101-4.022-3.389-7.252-7.433-7.252-2.369 0-4.477 1.109-5.839 2.835-0.764-0.987-1.959-1.624-3.303-1.624-2.307 0-4.176 1.871-4.176 4.179 0 0.201 0.015 0.399 0.043 0.592-0.351-0.063-0.711-0.098-1.080-0.098-3.344-0-6.054 2.712-6.054 6.058s2.71 6.058 6.054 6.058l20.508-0c3.004-0.006 5.438-2.444 5.438-5.451z",
"download":
"M23 14l-8 8-8-8h5v-12h6v12zM15 22h-15v8h30v-8h-15zM28 26h-4v-2h4v2z",
"earth":
"M27.314 4.686c3.022 3.022 4.686 7.040 4.686 11.314s-1.664 8.292-4.686 11.314c-3.022 3.022-7.040 4.686-11.314 4.686s-8.292-1.664-11.314-4.686c-3.022-3.022-4.686-7.040-4.686-11.314s1.664-8.292 4.686-11.314c3.022-3.022 7.040-4.686 11.314-4.686s8.292 1.664 11.314 4.686zM25.899 25.9c1.971-1.971 3.281-4.425 3.821-7.096-0.421 0.62-0.824 0.85-1.073-0.538-0.257-2.262-2.335-0.817-3.641-1.621-1.375 0.927-4.466-1.802-3.941 1.276 0.81 1.388 4.375-1.858 2.598 1.079-1.134 2.050-4.145 6.592-3.753 8.946 0.049 3.43-3.504 0.715-4.729-0.422-0.824-2.279-0.281-6.262-2.434-7.378-2.338-0.102-4.344-0.314-5.25-2.927-0.545-1.87 0.58-4.653 2.584-5.083 2.933-1.843 3.98 2.158 6.731 2.232 0.854-0.894 3.182-1.178 3.375-2.18-1.805-0.318 2.29-1.517-0.173-2.199-1.358 0.16-2.234 1.409-1.512 2.467-2.632 0.614-2.717-3.809-5.247-2.414-0.064 2.206-4.132 0.715-1.407 0.268 0.936-0.409-1.527-1.594-0.196-1.379 0.654-0.036 2.854-0.807 2.259-1.325 1.225-0.761 2.255 1.822 3.454-0.059 0.866-1.446-0.363-1.713-1.448-0.98-0.612-0.685 1.080-2.165 2.573-2.804 0.497-0.213 0.973-0.329 1.336-0.296 0.752 0.868 2.142 1.019 2.215-0.104-1.862-0.892-3.915-1.363-6.040-1.363-3.051 0-5.952 0.969-8.353 2.762 0.645 0.296 1.012 0.664 0.39 1.134-0.483 1.439-2.443 3.371-4.163 3.098-0.893 1.54-1.482 3.238-1.733 5.017 1.441 0.477 1.773 1.42 1.464 1.736-0.734 0.64-1.185 1.548-1.418 2.541 0.469 2.87 1.818 5.515 3.915 7.612 2.644 2.644 6.16 4.1 9.899 4.1s7.255-1.456 9.899-4.1z",
"heart":
"M32 11.192c0 2.699-1.163 5.126-3.015 6.808h0.015l-10 10c-1 1-2 2-3 2s-2-1-3-2l-9.985-10c-1.852-1.682-3.015-4.109-3.015-6.808 0-5.077 4.116-9.192 9.192-9.192 2.699 0 5.126 1.163 6.808 3.015 1.682-1.852 4.109-3.015 6.808-3.015 5.077 0 9.192 4.116 9.192 9.192z",
"smiley":
"M16 32c8.837 0 16-7.163 16-16s-7.163-16-16-16-16 7.163-16 16 7.163 16 16 16zM16 3c7.18 0 13 5.82 13 13s-5.82 13-13 13-13-5.82-13-13 5.82-13 13-13zM8 10c0-1.105 0.895-2 2-2s2 0.895 2 2c0 1.105-0.895 2-2 2s-2-0.895-2-2zM20 10c0-1.105 0.895-2 2-2s2 0.895 2 2c0 1.105-0.895 2-2 2s-2-0.895-2-2zM22.003 19.602l2.573 1.544c-1.749 2.908-4.935 4.855-8.576 4.855s-6.827-1.946-8.576-4.855l2.573-1.544c1.224 2.036 3.454 3.398 6.003 3.398s4.779-1.362 6.003-3.398z",
"close":
"M31.708 25.708c-0-0-0-0-0-0l-9.708-9.708 9.708-9.708c0-0 0-0 0-0 0.105-0.105 0.18-0.227 0.229-0.357 0.133-0.356 0.057-0.771-0.229-1.057l-4.586-4.586c-0.286-0.286-0.702-0.361-1.057-0.229-0.13 0.048-0.252 0.124-0.357 0.228 0 0-0 0-0 0l-9.708 9.708-9.708-9.708c-0-0-0-0-0-0-0.105-0.104-0.227-0.18-0.357-0.228-0.356-0.133-0.771-0.057-1.057 0.229l-4.586 4.586c-0.286 0.286-0.361 0.702-0.229 1.057 0.049 0.13 0.124 0.252 0.229 0.357 0 0 0 0 0 0l9.708 9.708-9.708 9.708c-0 0-0 0-0 0-0.104 0.105-0.18 0.227-0.229 0.357-0.133 0.355-0.057 0.771 0.229 1.057l4.586 4.586c0.286 0.286 0.702 0.361 1.057 0.229 0.13-0.049 0.252-0.124 0.357-0.229 0-0 0-0 0-0l9.708-9.708 9.708 9.708c0 0 0 0 0 0 0.105 0.105 0.227 0.18 0.357 0.229 0.356 0.133 0.771 0.057 1.057-0.229l4.586-4.586c0.286-0.286 0.362-0.702 0.229-1.057-0.049-0.13-0.124-0.252-0.229-0.357z",
"checkmark":
"M27 4l-15 15-7-7-5 5 12 12 20-20z",
"play":
"M6 4l20 12-20 12z",
"pause":
"M4 4h10v24h-10zM18 4h10v24h-10z",
"stop":
"M4 4h24v24h-24z",
"shuffle":
"M32 8l-8-8v6c-4.087 0-7.211 0.975-9.552 2.982-0.164 0.141-0.321 0.284-0.474 0.43 0.86 1.193 1.522 2.422 2.118 3.597 1.51-1.825 3.689-3.008 7.907-3.008v12c-6.764 0-8.285-3.043-10.211-6.894-1.072-2.144-2.181-4.361-4.237-6.124-2.341-2.006-5.465-2.982-9.552-2.982v4c6.764 0 8.285 3.043 10.211 6.894 1.072 2.144 2.181 4.361 4.237 6.124 2.341 2.006 5.465 2.982 9.552 2.982v6l8-8-8-8 8-8zM0 22v4c4.087 0 7.211-0.975 9.552-2.982 0.164-0.141 0.321-0.284 0.474-0.43-0.86-1.193-1.522-2.422-2.118-3.597-1.51 1.825-3.689 3.009-7.907 3.009z",
"arrow-up-right":
"M16 0c-8.837 0-16 7.163-16 16s7.163 16 16 16 16-7.163 16-16-7.163-16-16-16zM16 29c-7.18 0-13-5.82-13-13s5.82-13 13-13 13 5.82 13 13-5.82 13-13 13zM12 8c-1.105 0-2 0.895-2 2s0.895 2 2 2h5.172l-8.586 8.586c-0.781 0.781-0.781 2.047 0 2.829 0.39 0.39 0.902 0.586 1.414 0.586s1.024-0.195 1.414-0.586l8.586-8.586v5.172c0 1.105 0.895 2 2 2s2-0.895 2-2v-12h-12z",
"googleplus":
"M0.025 27.177c-0.008-0.079-0.014-0.158-0.018-0.238 0.004 0.080 0.011 0.159 0.018 0.238zM7.372 17.661c2.875 0.086 4.804-2.897 4.308-6.662s-3.231-6.787-6.106-6.873c-2.876-0.085-4.804 2.796-4.308 6.562 0.496 3.765 3.23 6.887 6.106 6.973zM32 8v-2.666c0-2.934-2.399-5.334-5.333-5.334h-21.333c-2.884 0-5.25 2.32-5.33 5.185 1.824-1.606 4.354-2.947 6.965-2.947 2.791 0 11.164 0 11.164 0l-2.498 2.113h-3.54c2.348 0.9 3.599 3.629 3.599 6.429 0 2.351-1.307 4.374-3.153 5.812-1.801 1.403-2.143 1.991-2.143 3.184 0 1.018 1.93 2.75 2.938 3.462 2.949 2.079 3.904 4.010 3.904 7.233 0 0.513-0.064 1.026-0.19 1.53h9.617c2.934 0 5.333-2.398 5.333-5.334v-16.666h-6v6h-2v-6h-6v-2h6v-6h2v6h6zM5.809 23.936c0.675 0 1.294-0.018 1.936-0.018-0.848-0.823-1.52-1.831-1.52-3.074 0-0.738 0.236-1.448 0.567-2.079-0.337 0.024-0.681 0.031-1.035 0.031-2.324 0-4.297-0.752-5.756-1.995v2.101l0 6.304c1.67-0.793 3.653-1.269 5.809-1.269zM0.107 27.727c-0.035-0.171-0.061-0.344-0.079-0.52 0.018 0.176 0.045 0.349 0.079 0.52zM14.233 29.776c-0.471-1.838-2.139-2.749-4.465-4.361-0.846-0.273-1.778-0.434-2.778-0.444-2.801-0.030-5.41 1.092-6.882 2.762 0.498 2.428 2.657 4.267 5.226 4.267h8.951c0.057-0.348 0.084-0.707 0.084-1.076 0-0.392-0.048-0.775-0.137-1.148z",
"facebook":
"M17.996 32h-5.996v-16h-4v-5.514l4-0.002-0.007-3.248c0-4.498 1.22-7.236 6.519-7.236h4.412v5.515h-2.757c-2.064 0-2.163 0.771-2.163 2.209l-0.008 2.76h4.959l-0.584 5.514-4.37 0.002-0.004 16z",
"twitter":
"M26.667 0h-21.333c-2.934 0-5.334 2.4-5.334 5.334v21.332c0 2.936 2.4 5.334 5.334 5.334h21.333c2.934 0 5.333-2.398 5.333-5.334v-21.332c0-2.934-2.399-5.334-5.333-5.334zM26.189 10.682c0.010 0.229 0.015 0.46 0.015 0.692 0 7.069-5.288 15.221-14.958 15.221-2.969 0-5.732-0.886-8.059-2.404 0.411 0.050 0.83 0.075 1.254 0.075 2.463 0 4.73-0.855 6.529-2.29-2.3-0.043-4.242-1.59-4.911-3.715 0.321 0.063 0.65 0.096 0.989 0.096 0.479 0 0.944-0.066 1.385-0.188-2.405-0.492-4.217-2.654-4.217-5.245 0-0.023 0-0.045 0-0.067 0.709 0.401 1.519 0.641 2.381 0.669-1.411-0.959-2.339-2.597-2.339-4.453 0-0.98 0.259-1.899 0.712-2.689 2.593 3.237 6.467 5.366 10.836 5.589-0.090-0.392-0.136-0.8-0.136-1.219 0-2.954 2.354-5.349 5.257-5.349 1.512 0 2.879 0.65 3.838 1.689 1.198-0.24 2.323-0.685 3.338-1.298-0.393 1.249-1.226 2.298-2.311 2.96 1.063-0.129 2.077-0.417 3.019-0.842-0.705 1.073-1.596 2.015-2.623 2.769z",
"github":
"M16 0c-8.837 0-16 7.163-16 16s7.163 16 16 16 16-7.163 16-16-7.163-16-16-16zM25.502 25.502c-1.235 1.235-2.672 2.204-4.272 2.881-0.406 0.172-0.819 0.323-1.238 0.453v-2.398c0-1.26-0.432-2.188-1.297-2.781 0.542-0.052 1.039-0.125 1.492-0.219s0.932-0.229 1.438-0.406 0.958-0.388 1.359-0.633 0.786-0.563 1.156-0.953 0.68-0.833 0.93-1.328 0.448-1.089 0.594-1.781 0.219-1.456 0.219-2.289c0-1.615-0.526-2.99-1.578-4.125 0.479-1.25 0.427-2.609-0.156-4.078l-0.391-0.047c-0.271-0.031-0.758 0.083-1.461 0.344s-1.492 0.688-2.367 1.281c-1.24-0.344-2.526-0.516-3.859-0.516-1.344 0-2.625 0.172-3.844 0.516-0.552-0.375-1.075-0.685-1.57-0.93s-0.891-0.411-1.188-0.5-0.573-0.143-0.828-0.164-0.419-0.026-0.492-0.016-0.125 0.021-0.156 0.031c-0.583 1.479-0.635 2.839-0.156 4.078-1.052 1.135-1.578 2.51-1.578 4.125 0 0.833 0.073 1.596 0.219 2.289s0.344 1.286 0.594 1.781 0.56 0.938 0.93 1.328 0.755 0.708 1.156 0.953 0.854 0.456 1.359 0.633 0.984 0.313 1.438 0.406 0.95 0.167 1.492 0.219c-0.854 0.583-1.281 1.51-1.281 2.781v2.445c-0.472-0.14-0.937-0.306-1.394-0.5-1.6-0.677-3.037-1.646-4.272-2.881s-2.204-2.672-2.881-4.272c-0.7-1.655-1.055-3.414-1.055-5.23s0.355-3.575 1.055-5.23c0.677-1.6 1.646-3.037 2.881-4.272s2.672-2.204 4.272-2.881c1.655-0.7 3.415-1.055 5.23-1.055s3.575 0.355 5.23 1.055c1.6 0.677 3.037 1.646 4.272 2.881s2.204 2.672 2.881 4.272c0.7 1.655 1.055 3.415 1.055 5.23s-0.355 3.575-1.055 5.23c-0.677 1.6-1.646 3.037-2.881 4.272z",
"tumblr":
"M26.668 0h-21.334c-2.934 0-5.334 2.4-5.334 5.334v21.332c0 2.936 2.4 5.334 5.334 5.334h21.334c2.933 0 5.332-2.398 5.332-5.334v-21.332c-0-2.933-2.399-5.334-5.332-5.334zM22.866 25.771c-0.942 0.443-1.798 0.756-2.563 0.936-0.765 0.178-1.593 0.267-2.481 0.267-1.010 0-1.605-0.127-2.381-0.381-0.775-0.256-1.438-0.621-1.984-1.090-0.549-0.473-0.928-0.975-1.14-1.506s-0.317-1.303-0.317-2.313v-7.744h-3v-3.127c0.867-0.281 1.873-0.685 2.49-1.211 0.62-0.527 1.116-1.158 1.49-1.896 0.375-0.736 0.633-1.676 0.774-2.815h3.141v5.108h5.105v3.941h-5.106v5.662c0 1.281-0.017 2.020 0.119 2.383 0.135 0.361 0.473 0.736 0.841 0.953 0.489 0.293 1.047 0.439 1.676 0.439 1.118 0 2.231-0.363 3.336-1.090v3.482z",
"apple":
"M24.734 17.003c-0.040-4.053 3.305-5.996 3.454-6.093-1.88-2.751-4.808-3.127-5.851-3.171-2.492-0.252-4.862 1.467-6.127 1.467-1.261 0-3.213-1.43-5.28-1.392-2.716 0.040-5.221 1.579-6.619 4.012-2.822 4.897-0.723 12.151 2.028 16.123 1.344 1.944 2.947 4.127 5.051 4.049 2.026-0.081 2.793-1.311 5.242-1.311s3.138 1.311 5.283 1.271c2.18-0.041 3.562-1.981 4.897-3.931 1.543-2.255 2.179-4.439 2.216-4.551-0.048-0.022-4.252-1.632-4.294-6.473zM20.705 5.11c1.117-1.355 1.871-3.235 1.665-5.11-1.609 0.066-3.559 1.072-4.713 2.423-1.036 1.199-1.942 3.113-1.699 4.951 1.796 0.14 3.629-0.913 4.747-2.264z",
"android":
"M27 10c-1.1 0-2 0.9-2 2v8c0 1.1 0.9 2 2 2s2-0.9 2-2v-8c0-1.1-0.9-2-2-2zM3 10c-1.1 0-2 0.9-2 2v8c0 1.1 0.9 2 2 2s2-0.9 2-2v-8c0-1.1-0.9-2-2-2zM6 23c0 1.657 1.343 3 3 3h1v4c0 1.1 0.9 2 2 2s2-0.9 2-2v-4h2v4c0 1.1 0.9 2 2 2s2-0.9 2-2v-4h1c1.657 0 3-1.343 3-3v-11h-18v11zM18.706 2.797l1.266-2.431c0.064-0.122 0.016-0.274-0.106-0.337s-0.274-0.016-0.337 0.106l-1.285 2.468c-1.006-0.389-2.1-0.603-3.244-0.603s-2.237 0.214-3.244 0.603l-1.285-2.468c-0.063-0.122-0.215-0.17-0.337-0.106s-0.17 0.215-0.106 0.337l1.266 2.432c-2.832 1.282-4.883 3.987-5.238 7.203h17.889c-0.355-3.216-2.406-5.921-5.238-7.203zM11 8.45c-0.801 0-1.45-0.649-1.45-1.45s0.649-1.45 1.45-1.45 1.45 0.649 1.45 1.45c-0 0.801-0.649 1.45-1.45 1.45zM19 8.45c-0.801 0-1.45-0.649-1.45-1.45s0.649-1.45 1.45-1.45 1.45 0.649 1.45 1.45c0 0.801-0.649 1.45-1.45 1.45z",
"windows8":
"M0.011 16l-0.011-9.752 12-1.63v11.382zM14 4.328l15.996-2.328v14h-15.996zM30 18l-0.004 14-15.996-2.25v-11.75zM12 29.495l-11.99-1.644-0.001-9.851h11.991z",
"html5":
"M1.892 0l2.567 28.801 11.524 3.199 11.554-3.204 2.572-28.796h-28.216zM24.52 9.42h-13.517l0.322 3.617h12.874l-0.97 10.844-7.245 2.008-7.237-2.008-0.495-5.547h3.547l0.252 2.82 3.933 1.060 0.009-0.002 3.935-1.062 0.408-4.58h-12.242l-0.953-10.681h17.694l-0.316 3.532z",
"chrome":
"M8.071 13.954l-4.579-7.931c2.932-3.671 7.445-6.023 12.508-6.023 5.857 0 10.978 3.148 13.767 7.844h-13.055c-0.235-0.020-0.472-0.031-0.711-0.031-3.809 0-7.018 2.614-7.929 6.142zM21.728 10.156h9.171c0.711 1.81 1.101 3.781 1.101 5.844 0 8.776-7.066 15.9-15.818 15.998l6.544-11.334c0.921-1.324 1.462-2.932 1.462-4.664 0-2.287-0.943-4.357-2.459-5.844zM10.188 16c0-3.205 2.607-5.813 5.813-5.813s5.813 2.607 5.813 5.813c0 3.205-2.608 5.813-5.813 5.813s-5.813-2.608-5.813-5.813zM18.193 23.889l-4.581 7.934c-7.704-1.153-13.613-7.797-13.613-15.822 0-2.851 0.746-5.526 2.053-7.845l6.532 11.314c1.308 2.785 4.14 4.718 7.415 4.718 0.759 0 1.495-0.104 2.193-0.299z",
"firefox":
"M31.954 10.442l-0.371 2.377c0 0-0.53-4.402-1.179-6.047-0.995-2.521-1.438-2.501-1.441-2.498 0.667 1.694 0.546 2.604 0.546 2.604s-1.181-3.219-4.303-4.243c-3.459-1.134-5.33-0.824-5.547-0.765-0.033-0-0.064-0-0.095-0 0.026 0.002 0.050 0.005 0.076 0.007-0.001 0.001-0.003 0.001-0.003 0.002 0.014 0.017 3.822 0.666 4.497 1.594 0 0-1.617 0-3.227 0.464-0.073 0.021 5.923 0.749 7.148 6.74 0 0-0.657-1.371-1.47-1.604 0.535 1.626 0.397 4.712-0.112 6.245-0.066 0.197-0.133-0.853-1.135-1.305 0.321 2.301-0.019 5.952-1.616 6.957-0.124 0.078 1.001-3.603 0.226-2.18-4.46 6.838-9.731 3.155-12.101 1.535 1.215 0.264 3.52-0.041 4.541-0.8 0.001-0.001 0.002-0.002 0.004-0.003 1.108-0.758 1.765-1.311 2.354-1.18s0.982-0.46 0.524-0.985c-0.459-0.526-1.572-1.249-3.079-0.855-1.063 0.278-2.379 1.454-4.389 0.264-1.543-0.914-1.688-1.673-1.702-2.199 0.038-0.186 0.086-0.361 0.143-0.52 0.178-0.496 0.716-0.646 1.015-0.764 0.508 0.087 0.946 0.246 1.405 0.481 0.006-0.153 0.008-0.356-0.001-0.586 0.044-0.088 0.017-0.352-0.054-0.674-0.041-0.322-0.107-0.655-0.211-0.959 0-0 0.001-0 0.001-0 0.002-0.001 0.003-0.001 0.005-0.002s0.005-0.004 0.007-0.006c0-0.001 0.001-0.001 0.001-0.002 0.003-0.004 0.005-0.008 0.006-0.015 0.032-0.144 0.376-0.423 0.804-0.722 0.383-0.268 0.834-0.553 1.19-0.774 0.314-0.195 0.554-0.34 0.605-0.378 0.019-0.015 0.042-0.032 0.068-0.051 0.005-0.004 0.009-0.007 0.014-0.011 0.003-0.002 0.006-0.005 0.009-0.007 0.169-0.135 0.421-0.389 0.474-0.924 0-0.001 0-0.002 0-0.004 0.002-0.016 0.003-0.032 0.004-0.048 0.001-0.011 0.002-0.023 0.002-0.034 0-0.009 0.001-0.018 0.001-0.027 0.001-0.021 0.002-0.043 0.002-0.065 0-0.001 0-0.002 0-0.004 0.001-0.052-0-0.106-0.003-0.163-0.002-0.032-0.004-0.060-0.009-0.086-0-0.001-0.001-0.003-0.001-0.004-0.001-0.003-0.001-0.005-0.002-0.008-0.001-0.005-0.002-0.009-0.004-0.013-0-0.001-0-0.001-0.001-0.001-0.002-0.005-0.004-0.010-0.005-0.014-0-0-0-0-0-0.001-0.055-0.128-0.26-0.177-1.108-0.191-0.001-0-0.002-0-0.002-0v0c-0.346-0.006-0.798-0.006-1.391-0.004-1.039 0.004-1.613-1.016-1.797-1.41 0.251-1.389 0.977-2.379 2.17-3.051 0.023-0.013 0.018-0.023-0.009-0.031 0.233-0.141-2.82-0.004-4.225 1.782-1.247-0.31-2.333-0.289-3.269-0.069-0.18-0.005-0.404-0.027-0.67-0.083-0.623-0.564-1.514-1.606-1.562-2.85 0 0-0.003 0.002-0.008 0.006-0.001-0.012-0.002-0.024-0.002-0.036 0 0-1.897 1.458-1.613 5.434-0.001 0.064-0.002 0.125-0.004 0.184-0.514 0.696-0.768 1.282-0.787 1.411-0.455 0.926-0.917 2.32-1.292 4.437 0 0 0.263-0.833 0.79-1.777-0.388 1.188-0.693 3.036-0.514 5.808 0 0 0.047-0.615 0.215-1.5 0.131 1.719 0.704 3.841 2.152 6.337 2.78 4.791 7.052 7.211 11.775 7.582 0.839 0.069 1.689 0.071 2.544 0.006 0.079-0.006 0.157-0.011 0.236-0.018 0.968-0.068 1.942-0.214 2.914-0.449 13.287-3.212 11.842-19.256 11.842-19.256z",
"IE":
"M22.944 19.651h7.377c0.057-0.512 0.080-1.034 0.080-1.569 0-2.507-0.673-4.858-1.848-6.883 1.215-3.228 1.172-5.968-0.455-7.606-1.547-1.54-5.697-1.29-10.388 0.787-0.347-0.026-0.697-0.040-1.051-0.040-6.439 0-11.841 4.431-13.335 10.402 2.020-2.586 4.145-4.461 6.984-5.826-0.258 0.242-1.764 1.739-2.018 1.993-7.486 7.484-9.847 17.26-7.306 19.8 1.931 1.93 5.43 1.604 9.449-0.364 1.869 0.952 3.984 1.488 6.226 1.488 6.035 0 11.15-3.885 13.003-9.295h-7.433c-1.023 1.887-3.023 3.171-5.319 3.171s-4.296-1.284-5.319-3.171c-0.455-0.852-0.716-1.83-0.716-2.864v-0.023h12.071zM10.884 16.025c0.171-3.035 2.694-5.456 5.774-5.456s5.604 2.421 5.774 5.456h-11.548zM28.030 5.119c1.048 1.059 1.021 3.007 0.125 5.438-1.535-2.341-3.766-4.186-6.4-5.239 2.816-1.207 5.106-1.367 6.274-0.199zM2.921 30.227c-1.337-1.337-0.934-4.144 0.788-7.526 1.072 3.008 3.161 5.534 5.854 7.161-2.982 1.354-5.423 1.584-6.643 0.365z",
"opera":
"M15.939 0c-8.741 0-15.023 6.34-15.023 15.851 0 8.463 6.109 16.149 15.024 16.149 9.002 0 15.144-7.684 15.144-16.149 0-9.586-6.483-15.851-15.146-15.851zM21.511 15.579c-0.002 5.292-0.284 12.828-5.571 12.828v0.001c-5.212 0-5.42-7.54-5.42-12.822 0-6.199 0.58-12.143 5.42-12.143s5.571 6.018 5.571 12.136z",
"safari":
"M16 2c-0.752 0-1.491 0.056-2.213 0.163-0.009-0.024-0.020-0.048-0.031-0.072 0.401-0.313 0.611-0.745 0.52-1.173-0.117-0.549-0.7-0.918-1.451-0.918-0.162 0-0.327 0.018-0.49 0.052-0.943 0.2-1.567 0.894-1.421 1.58 0.089 0.417 0.447 0.729 0.943 0.856-0.001 0.030-0.001 0.061 0.001 0.092-6.269 1.798-10.857 7.573-10.857 14.42 0 8.284 6.716 15 15 15s15-6.716 15-15c0-8.284-6.716-15-15-15zM11.403 1.528c-0.085-0.398 0.39-0.85 1.036-0.987 0.129-0.027 0.259-0.041 0.386-0.041 0.501 0 0.897 0.215 0.962 0.522 0.050 0.235-0.096 0.488-0.356 0.684-0.22-0.155-0.501-0.223-0.784-0.162-0.283 0.060-0.511 0.235-0.649 0.464-0.318-0.077-0.547-0.253-0.595-0.48zM24.043 25.043c-1.374 1.374-3.038 2.352-4.853 2.88l-1.325-2.149-0.336 2.499c-0.503 0.067-1.013 0.102-1.529 0.102-3.038 0-5.895-1.183-8.043-3.332-1.374-1.374-2.352-3.038-2.88-4.853l2.149-1.325-2.499-0.336c-0.067-0.503-0.102-1.013-0.102-1.529 0-3.038 1.183-5.895 3.332-8.043 1.374-1.374 3.038-2.352 4.853-2.88l1.325 2.149 0.336-2.499c0.503-0.067 1.013-0.102 1.529-0.102 3.038 0 5.895 1.183 8.043 3.332 1.374 1.374 2.352 3.038 2.88 4.853l-2.149 1.325 2.499 0.336c0.067 0.503 0.102 1.013 0.102 1.529 0 3.038-1.183 5.895-3.332 8.043zM23.778 9.222l-6.156 4.678c-0.458-0.239-0.965-0.377-1.482-0.398l-1.596-3.349-0.096 3.708c-0.415 0.204-0.784 0.489-1.090 0.842l-1.972-0.699 1.44 1.517c-0.198 0.422-0.308 0.876-0.326 1.338l-3.349 1.596 3.711 0.096c0.012 0.024 0.024 0.048 0.037 0.071l-4.678 6.156 6.156-4.678c0.458 0.239 0.965 0.377 1.482 0.398l1.596 3.349 0.096-3.708c0.415-0.204 0.785-0.489 1.090-0.842l1.972 0.699-1.44-1.517c0.198-0.422 0.308-0.876 0.326-1.338l3.349-1.596-3.71-0.096c-0.012-0.024-0.024-0.048-0.037-0.071l4.678-6.156zM16.003 13.892v0zM15.376 14.066c0.21-0.045 0.42-0.066 0.627-0.066 0.13 0 0.258 0.008 0.384 0.025l0.003 0.001-0-0.001c0.273 0.036 0.537 0.109 0.785 0.216l-2.236 1.699-1.699 2.236c-0.074-0.175-0.133-0.359-0.174-0.552-0.345-1.621 0.69-3.214 2.311-3.558zM17.566 19.559v0 0c-0.283 0.173-0.599 0.303-0.942 0.376-0.21 0.045-0.42 0.066-0.627 0.066-0.13 0-0.258-0.009-0.385-0.025l-0.002-0c-0.273-0.036-0.537-0.109-0.785-0.215l2.236-1.699 1.699-2.236c0.074 0.175 0.133 0.359 0.174 0.552 0.272 1.278-0.314 2.538-1.368 3.183z"
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16" version="1.1" width="18px" height="18px">
<g id="surface1">
<path style=" " d="M 2.5 2 C 1.675781 2 1 2.675781 1 3.5 L 1 12.5 C 1 13.324219 1.675781 14 2.5 14 L 13.5 14 C 14.324219 14 15 13.324219 15 12.5 L 15 5.5 C 15 4.675781 14.324219 4 13.5 4 L 6.796875 4 L 6.144531 2.789063 C 5.882813 2.300781 5.375 2 4.824219 2 Z M 2.5 3 L 4.824219 3 C 5.007813 3 5.175781 3.101563 5.265625 3.261719 L 5.664063 4 L 2 4 L 2 3.5 C 2 3.21875 2.21875 3 2.5 3 Z M 2 5 L 13.5 5 C 13.78125 5 14 5.21875 14 5.5 L 14 12.5 C 14 12.78125 13.78125 13 13.5 13 L 2.5 13 C 2.21875 13 2 12.78125 2 12.5 Z "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 738 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16" version="1.1" width="18px" height="18px">
<g id="surface1">
<path style=" " d="M 4.5 2 C 3.675781 2 3 2.675781 3 3.5 L 3 12.5 C 3 13.324219 3.675781 14 4.5 14 L 11.5 14 C 12.324219 14 13 13.324219 13 12.5 L 13 5.292969 L 9.707031 2 Z M 4.5 3 L 9 3 L 9 6 L 12 6 L 12 12.5 C 12 12.78125 11.78125 13 11.5 13 L 4.5 13 C 4.21875 13 4 12.78125 4 12.5 L 4 3.5 C 4 3.21875 4.21875 3 4.5 3 Z M 10 3.707031 L 11.292969 5 L 10 5 Z M 6 8 L 6 9 L 10 9 L 10 8 Z M 6 10 L 6 11 L 9 11 L 9 10 Z "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 636 B

+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by IcoMoon.io -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
<path fill="#000000" d="M8 0c-4.418 0-8 1.119-8 2.5v1.5l6 6v5c0 0.552 0.895 1 2 1s2-0.448 2-1v-5l6-6v-1.5c0-1.381-3.582-2.5-8-2.5zM1.475 2.169c0.374-0.213 0.9-0.416 1.52-0.586 1.374-0.376 3.152-0.583 5.005-0.583s3.631 0.207 5.005 0.583c0.62 0.17 1.146 0.372 1.52 0.586 0.247 0.141 0.38 0.26 0.442 0.331-0.062 0.071-0.195 0.19-0.442 0.331-0.374 0.213-0.9 0.416-1.52 0.586-1.374 0.376-3.152 0.583-5.005 0.583s-3.631-0.207-5.005-0.583c-0.62-0.17-1.146-0.372-1.52-0.586-0.247-0.141-0.38-0.26-0.442-0.331 0.062-0.071 0.195-0.19 0.442-0.331z"></path>
</svg>

After

Width:  |  Height:  |  Size: 864 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by IcoMoon.io -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
<path fill="#000000" d="M5 2h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1z"></path>
<path fill="#000000" d="M11 6h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1zM11 3h2v2h-2v-2z"></path>
<path fill="#000000" d="M5 10h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1zM5 13h-2v-2h2v2z"></path>
<path fill="#000000" d="M13 10h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1z"></path>
<path fill="#000000" d="M14 8h-1c-1.336 0-2.591-0.52-3.536-1.464s-1.464-2.2-1.464-3.536v-1c0-1.1-0.9-2-2-2h-4c-1.1 0-2 0.9-2 2v4c0 1.1 0.9 2 2 2h1c1.336 0 2.591 0.52 3.536 1.464s1.464 2.2 1.464 3.536v1c0 1.1 0.9 2 2 2h4c1.1 0 2-0.9 2-2v-4c0-1.1-0.9-2-2-2zM15 14c0 0.265-0.105 0.515-0.295 0.705s-0.44 0.295-0.705 0.295h-4c-0.265 0-0.515-0.105-0.705-0.295s-0.295-0.44-0.295-0.705v-1c0-3.314-2.686-6-6-6h-1c-0.265 0-0.515-0.105-0.705-0.295s-0.295-0.441-0.295-0.705v-4c0-0.265 0.105-0.515 0.295-0.705s0.44-0.295 0.705-0.295h4c0.265 0 0.515 0.105 0.705 0.295s0.295 0.44 0.295 0.705v1c0 3.314 2.686 6 6 6h1c0.265 0 0.515 0.105 0.705 0.295s0.295 0.44 0.295 0.705v4z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Some files were not shown because too many files have changed in this diff Show More