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
+43
View File
@@ -0,0 +1,43 @@
<!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." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../samples/assets/require.js"></script>
<script src="../assets/js/goSamples.js"></script>
<script>
function init() {
require(["arrowheadsScript"], function(app) {
app.init();
});
}
</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="../extensionsTS/Arrowheads.ts" target="_blank">Arrowheads.ts</a>.
</p>
<p>
For predefined shape geometries, see the <a href="shapes.html">Shapes</a> sample.
</p>
</div>
</body>
</html>
+122
View File
@@ -0,0 +1,122 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "../release/go.js"], factory);
}
})(function (require, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.showArrowInfo = exports.infoString = exports.init = void 0;
/*
* Copyright (C) 1998-2020 by Northwoods Software Corporation. All Rights Reserved.
*/
var go = require("../release/go.js");
function init() {
if (window.goSamples)
window.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,
layout: $(go.CircularLayout, {
radius: 100,
spacing: 0,
nodeDiameterFormula: go.CircularLayout.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');
if (cntr !== null)
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,
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,
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, {
// 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
});
}
exports.init = init;
// 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();
if (link)
msg = 'toArrow: ' + link.data.toArrow + ';\nfromArrow: ' + link.data.fromArrow;
}
return msg;
}
exports.infoString = infoString;
// a GraphObject.click event handler to show arrowhead information
function showArrowInfo(e, obj) {
var msg = infoString(obj);
if (msg) {
var status_1 = document.getElementById('myArrowheadInfo');
if (status_1)
status_1.textContent = msg;
}
}
exports.showArrowInfo = showArrowInfo;
});
+138
View File
@@ -0,0 +1,138 @@
'use strict';
/*
* Copyright (C) 1998-2020 by Northwoods Software Corporation. All Rights Reserved.
*/
import * as go from '../release/go.js';
export function init() {
if ((window as any).goSamples) (window as any).goSamples(); // init for these samples -- you don't need to call this
const $ = go.GraphObject.make; // for conciseness in defining templates
const 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: go.DiagramEvent) {
// now that the CircularLayout has finished, we know where its center is
const cntr = myDiagram.findNodeForKey('Center');
if (cntr !== null) cntr.location = (myDiagram.layout as go.CircularLayout).actualCenter;
}
});
// construct a shared radial gradient brush
const 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
$<go.Adornment>('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
$<go.Adornment>('ToolTip',
$(go.TextBlock, { margin: 4 },
new go.Binding('text', '', infoString).ofObject())
)
}
);
// collect all of the predefined arrowhead names
const 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
const linkdata = [];
let i = 0;
for (let 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
export function infoString(obj: go.GraphObject) {
let part = obj.part;
if (part instanceof go.Adornment) part = part.adornedPart;
let msg = '';
if (part instanceof go.Link) {
const link = part;
msg = 'toArrow: ' + link.data.toArrow + ';\nfromArrow: ' + link.data.fromArrow;
} else if (part instanceof go.Node) {
const node = part;
const link = node.linksConnected.first();
if (link) msg = 'toArrow: ' + link.data.toArrow + ';\nfromArrow: ' + link.data.fromArrow;
}
return msg;
}
// a GraphObject.click event handler to show arrowhead information
export function showArrowInfo(e: go.InputEvent, obj: go.GraphObject) {
const msg = infoString(obj);
if (msg) {
const status = document.getElementById('myArrowheadInfo');
if (status) status.textContent = msg;
}
}
+80
View File
@@ -0,0 +1,80 @@
<!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="../samples/assets/require.js"></script>
<script src="../assets/js/goSamples.js"></script>
<!-- requires minimal.js, built from minimal.ts -->
<script>
function init() {
require(["flowchart"], function(app) {
app.init();
});
}
</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: whitesmoke; border: solid 1px black"></div>
<div id="myDiagramDiv" style="flex-grow: 1; height: 750px; border: solid 1px black"></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">Save</button>
<button id="LoadButton">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":"0 77", "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 190", "text":"Gradually beat in 1 cup sugar and 2 cups sifted flour"},
{"key":3, "loc":"175 270", "text":"Mix in 6 oz (1 cup) Nestle's Semi-Sweet Chocolate Morsels"},
{"key":4, "loc":"175 370", "text":"Press evenly into ungreased 15x10x1 pan"},
{"key":5, "loc":"352 85", "text":"Finely chop 1/2 cup of your choice of nuts"},
{"key":6, "loc":"175 440", "text":"Sprinkle nuts on top"},
{"key":7, "loc":"175 500", "text":"Bake for 25 minutes and let cool"},
{"key":8, "loc":"175 570", "text":"Cut into rectangular grid"},
{"key":-2, "category":"End", "loc":"175 640", "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 id="SVGButton">Print Diagram Using SVG</button>
</div>
</body>
</html>
+245
View File
@@ -0,0 +1,245 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "../release/go.js"], factory);
}
})(function (require, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.init = void 0;
/*
* Copyright (C) 1998-2020 by Northwoods Software Corporation. All Rights Reserved.
*/
var go = require("../release/go.js");
var myDiagram;
var myPalette;
function init() {
if (window.goSamples)
window.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,
'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",
strokeWidth: 0,
width: horizontal ? NaN : 8,
height: !horizontal ? NaN : 8,
alignment: align,
stretch: (horizontal ? go.GraphObject.Horizontal : go.GraphObject.Vertical),
portId: name,
fromSpot: spot,
fromLinkable: output,
toSpot: spot,
toLinkable: input,
cursor: "pointer",
mouseEnter: function (e, port) {
if (!e.diagram.isReadOnly && port instanceof go.Shape)
port.fill = "rgba(255,0,255,0.5)";
},
mouseLeave: function (e, port) {
if (port instanceof go.Shape)
port.fill = "transparent";
}
});
}
function textStyle() {
return {
font: "bold 11pt Helvetica, Arial, sans-serif",
stroke: "whitesmoke"
};
}
// 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: "#00A9C9", strokeWidth: 0 }, 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: "#00A9C9", strokeWidth: 0 }, 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, "Auto", $(go.Shape, "Circle", { minSize: new go.Size(40, 40), fill: "#79C900", strokeWidth: 0 }), $(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, "Auto", $(go.Shape, "Circle", { minSize: new go.Size(40, 40), fill: "#DC3C00", strokeWidth: 0 }), $(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.ts:
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: "#DEE0A3", strokeWidth: 0 }), $(go.TextBlock, textStyle(), {
margin: 5,
maxSize: new go.Size(200, NaN),
wrap: go.TextBlock.WrapFit,
textAlign: "center",
editable: true,
font: "bold 12pt Helvetica, Arial, sans-serif",
stroke: '#454545'
}, 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) { if (link instanceof go.Link)
link.findObject("HIGHLIGHT").stroke = "rgba(30,144,255,0.2)"; },
mouseLeave: function (e, link) { if (link instanceof go.Link)
link.findObject("HIGHLIGHT").stroke = "transparent"; }
}, 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 }), $(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.figure === 'Diamond');
}
// 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
{
nodeTemplateMap: myDiagram.nodeTemplateMap,
model: new go.GraphLinksModel([
{ category: 'Start', text: 'Start' },
{ text: 'Step' },
{ category: 'Conditional', text: '???' },
{ category: 'End', text: 'End' },
{ category: 'Comment', text: 'Comment' }
])
});
// Attach to the window so you can manipulate in the console
window.myDiagram = myDiagram;
window.myPalette = myPalette;
} // end init
exports.init = init;
// Show the diagram's model in JSON format that the user may edit
var mySavedModel = document.getElementById('mySavedModel');
function save() {
mySavedModel.value = myDiagram.model.toJson();
myDiagram.isModified = false;
}
function load() {
myDiagram.model = go.Model.fromJson(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 = window.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);
}
// Add listeners for the buttons:
document.getElementById('SaveButton').addEventListener('click', save);
document.getElementById('LoadButton').addEventListener('click', load);
document.getElementById('SVGButton').addEventListener('click', printDiagram);
});
+310
View File
@@ -0,0 +1,310 @@
'use strict';
/*
* Copyright (C) 1998-2020 by Northwoods Software Corporation. All Rights Reserved.
*/
import * as go from '../release/go.js';
let myDiagram: go.Diagram;
let myPalette: go.Palette;
export function init() {
if ((window as any).goSamples) (window as any).goSamples(); // init for these samples -- you don't need to call this
const $ = 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', (e) => {
const button = document.getElementById('SaveButton') as HTMLButtonElement;
if (button) button.disabled = !myDiagram.isModified;
const idx = document.title.indexOf('*');
if (myDiagram.isModified) {
if (idx < 0) document.title += '*';
} else {
if (idx >= 0) document.title = document.title.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: string, align: go.Spot, spot: go.Spot, output: boolean, input: boolean) {
const 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: go.InputEvent, port: go.GraphObject) {
if (!e.diagram.isReadOnly && port instanceof go.Shape) port.fill = "rgba(255,0,255,0.5)";
},
mouseLeave: function(e: go.InputEvent, port: go.GraphObject) {
if (port instanceof go.Shape) port.fill = "transparent";
}
});
}
function textStyle() {
return {
font: "bold 11pt Helvetica, Arial, sans-serif",
stroke: "whitesmoke"
}
}
// 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: "#00A9C9", strokeWidth: 0 },
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: "#00A9C9", strokeWidth: 0 },
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, "Auto",
$(go.Shape, "Circle",
{ minSize: new go.Size(40, 40), fill: "#79C900", strokeWidth: 0 }),
$(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, "Auto",
$(go.Shape, "Circle",
{ minSize: new go.Size(40, 40), fill: "#DC3C00", strokeWidth: 0 }),
$(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.ts:
go.Shape.defineFigureGenerator('File', (shape, w, h) => {
const geo = new go.Geometry();
const 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());
const 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: "#DEE0A3", strokeWidth: 0 }),
$(go.TextBlock, textStyle(),
{
margin: 5,
maxSize: new go.Size(200, NaN),
wrap: go.TextBlock.WrapFit,
textAlign: "center",
editable: true,
font: "bold 12pt Helvetica, Arial, sans-serif",
stroke: '#454545'
},
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: go.InputEvent, link: go.GraphObject) { if (link instanceof go.Link) (link.findObject("HIGHLIGHT") as go.Shape).stroke = "rgba(30,144,255,0.2)"; },
mouseLeave: function(e: go.InputEvent, link: go.GraphObject) { if (link instanceof go.Link) (link.findObject("HIGHLIGHT") as go.Shape).stroke = "transparent"; }
},
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 }),
$(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: go.DiagramEvent) {
const label = e.subject.findObject('LABEL');
if (label !== null) label.visible = (e.subject.fromNode.data.figure === 'Diamond');
}
// 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
{
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' }
])
});
// Attach to the window so you can manipulate in the console
(window as any).myDiagram = myDiagram;
(window as any).myPalette = myPalette;
} // end init
// Show the diagram's model in JSON format that the user may edit
const mySavedModel = (document.getElementById('mySavedModel') as HTMLTextAreaElement);
function save() {
mySavedModel.value = myDiagram.model.toJson();
myDiagram.isModified = false;
}
function load() {
myDiagram.model = go.Model.fromJson(mySavedModel.value);
}
// print the diagram by opening a new window holding SVG images of the diagram contents for each page
function printDiagram() {
const svgWindow = window.open();
if (!svgWindow) return; // failure to open a new Window
const printSize = new go.Size(700, 960);
const bnds = myDiagram.documentBounds;
let x = bnds.x;
let y = bnds.y;
while (y < bnds.bottom) {
while (x < bnds.right) {
const svg = (window as any).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);
}
// Add listeners for the buttons:
(document.getElementById('SaveButton') as HTMLButtonElement).addEventListener('click', save);
(document.getElementById('LoadButton') as HTMLButtonElement).addEventListener('click', load);
(document.getElementById('SVGButton') as HTMLButtonElement).addEventListener('click', printDiagram);
+101
View File
@@ -0,0 +1,101 @@
<!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="../samples/assets/require.js"></script>
<script src="../assets/js/goSamples.js"></script>
<!-- requires minimal.js, built from minimal.ts -->
<script>
function init() {
require(["genogramScript"], function(app) {
app.init();
});
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="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>
+334
View File
@@ -0,0 +1,334 @@
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "../release/go.js"], factory);
}
})(function (require, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.GenogramLayout = void 0;
/*
* Copyright (C) 1998-2020 by Northwoods Software Corporation. All Rights Reserved.
*/
var go = require("../release/go.js");
// A custom layout that shows the two families related to a person's parents
var GenogramLayout = /** @class */ (function (_super) {
__extends(GenogramLayout, _super);
function GenogramLayout() {
var _this = _super.call(this) || this;
_this.initializeOption = go.LayeredDigraphLayout.InitDepthFirstIn;
_this.spouseSpacing = 30; // minimum space between spouses
return _this;
}
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;
var _loop_1 = function () {
var node = it.value;
if (!(node instanceof go.Node))
return "continue";
if (!node.isLayoutPositioned || !node.isVisible())
return "continue";
if (nonmemberonly && node.containingGroup !== null)
return "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;
if (link) {
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
if (spouseA && spouseB) {
vertex.width = spouseA.actualBounds.width + this_1.spouseSpacing + spouseB.actualBounds.width;
vertex.height = Math.max(spouseA.actualBounds.height, spouseB.actualBounds.height);
vertex.focus = new go.Point(spouseA.actualBounds.width + this_1.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_1 = 0;
node.linksConnected.each(function (l) { if (l.isLabeledLink)
marriages_1++; });
if (marriages_1 === 0) {
var vertex = net.addNode(node);
}
else if (marriages_1 > 1) {
multiSpousePeople.add(node);
}
}
};
var this_1 = this;
while (it.next()) {
_loop_1();
}
// now do all Links
it.reset();
var _loop_2 = function () {
var link = it.value;
if (!(link instanceof go.Link))
return "continue";
if (!link.isLayoutPositioned || !link.isVisible())
return "continue";
if (nonmemberonly && link.containingGroup !== null)
return "continue";
// if it's a parent-child link, add a LayoutEdge for it
if (!link.isLabeledLink) {
var fromNode = link.fromNode;
var toNode = link.toNode;
if (fromNode !== null && toNode !== null) {
var parent_1 = net.findVertex(fromNode); // should be a label node
var child = net.findVertex(toNode);
if (parent_1 !== null && child !== null) { // an unmarried child
net.linkVertexes(parent_1, child, link);
}
else if (parent_1 !== null) { // a married child
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
if (mlab !== null) {
var mlabvert = net.findVertex(mlab);
if (mlabvert !== null) {
net.linkVertexes(parent_1, mlabvert, link);
}
}
});
}
}
}
};
while (it.next()) {
_loop_2();
}
var _loop_3 = function () {
// find all collections of people that are indirectly married to each other
var node = multiSpousePeople.first();
var cohort = new go.Set();
this_2.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();
if (mlab !== null) {
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);
};
var this_2 = this;
while (multiSpousePeople.count > 0) {
_loop_3();
}
};
// collect all of the people indirectly married with a person
GenogramLayout.prototype.extendCohort = function (coll, node) {
if (coll.contains(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
if (l.fromNode !== null)
lay.extendCohort(coll, l.fromNode);
if (l.toNode !== null)
lay.extendCohort(coll, l.toNode);
}
});
};
GenogramLayout.prototype.assignLayers = function () {
_super.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 = [];
var net = this.network;
if (net !== null) {
var vit = net.vertexes.iterator;
while (vit.next()) {
var v = vit.value;
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;
}
vit.reset();
// 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)
while (vit.next()) {
var v = vit.value;
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 () {
_super.prototype.commitNodes.call(this);
var net = this.network;
// position regular nodes
if (net !== null) {
var vit = net.vertexes.iterator;
while (vit.next()) {
var v = vit.value;
if (v.node !== null && !v.node.isLinkLabel) {
v.node.position = new go.Point(v.x, v.y);
}
}
vit.reset();
// position the spouses of each marriage vertex
var layout = this;
while (vit.next()) {
var v = vit.value;
if (v.node === null)
continue;
if (!v.node.isLinkLabel)
continue;
var labnode = v.node;
var lablink = labnode.labeledLink;
if (lablink !== null) {
// 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;
if (spouseA !== null && spouseB != null) {
// 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;
}
}
}
}
vit.reset();
var _loop_4 = function () {
var v = vit.value;
if (v.node === null || v.node.linksConnected.count > 1)
return "continue";
var mnode = layout.findParentsMarriageLabelNode(v.node);
if (mnode !== null && mnode.linksConnected.count === 1) { // if only one child
if (layout.network === null)
return "continue";
var mvert = layout.network.findVertex(mnode);
if (mvert !== null) {
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
if (layout.diagram !== null) {
var overlaps = layout.diagram.findObjectsIn(newbnds, function (x) { var p = x.part; return (p instanceof go.Part) ? p : null; }, function (p) { return p !== v.node; }, true);
if (overlaps.count === 0) {
v.node.move(newbnds.position);
}
}
}
}
};
// position only-child nodes to be under the marriage label node
while (vit.next()) {
_loop_4();
}
}
};
GenogramLayout.prototype.findParentsMarriageLabelNode = function (node) {
var it = node.findNodesInto();
while (it.next()) {
var n = it.value;
if (n.isLinkLabel)
return n;
}
return null;
};
return GenogramLayout;
}(go.LayeredDigraphLayout));
exports.GenogramLayout = GenogramLayout;
});
// end GenogramLayout class
+281
View File
@@ -0,0 +1,281 @@
'use strict';
/*
* Copyright (C) 1998-2020 by Northwoods Software Corporation. All Rights Reserved.
*/
import * as go from '../release/go.js';
// A custom layout that shows the two families related to a person's parents
export class GenogramLayout extends go.LayeredDigraphLayout {
public spouseSpacing: number;
public constructor() {
super();
this.initializeOption = go.LayeredDigraphLayout.InitDepthFirstIn;
this.spouseSpacing = 30; // minimum space between spouses
}
public makeNetwork(coll: go.Diagram | go.Group | go.Iterable<go.Part>) {
// generate LayoutEdges for each parent-child Link
const 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
protected add(net: go.LayeredDigraphNetwork, coll: go.Iterable<go.Part>, nonmemberonly: boolean) {
const multiSpousePeople = new go.Set() as go.Set<go.Node>;
// consider all Nodes in the given collection
const it = coll.iterator;
while (it.next()) {
const node = it.value as go.Node;
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
const link = node.labeledLink;
if (link) {
const spouseA = link.fromNode;
const spouseB = link.toNode;
// create vertex representing both husband and wife
const vertex = net.addNode(node);
// now define the vertex size to be big enough to hold both spouses
if (spouseA && spouseB) {
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
let marriages = 0;
node.linksConnected.each(function(l) { if (l.isLabeledLink) marriages++; });
if (marriages === 0) {
const vertex = net.addNode(node);
} else if (marriages > 1) {
multiSpousePeople.add(node);
}
}
}
// now do all Links
it.reset();
while (it.next()) {
const link = it.value as go.Link;
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) {
const fromNode = link.fromNode;
const toNode = link.toNode;
if (fromNode !== null && toNode !== null) {
const parent = net.findVertex(fromNode); // should be a label node
const child = net.findVertex(toNode);
if (parent !== null && child !== null) { // an unmarried child
net.linkVertexes(parent, child, link);
} else if (parent !== null) { // a married child
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
const 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
if (mlab !== null) {
const 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
const node = multiSpousePeople.first() as go.Node;
const cohort = new go.Set() as go.Set<go.Node>;
this.extendCohort(cohort, node);
// then encourage them all to be the same generation by connecting them all with a common vertex
const dummyvert = net.createVertex();
net.addVertex(dummyvert);
const marriages = new go.Set() as go.Set<go.Link>;
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)
const mlab = link.labelNodes.first();
if (mlab !== null) {
const 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
protected extendCohort(coll: go.Set<go.Node>, node: go.Node) {
if (coll.contains(node)) return;
coll.add(node);
const lay = this;
node.linksConnected.each(function(l) {
if (l.isLabeledLink) { // if it's a marriage link, continue with both spouses
if (l.fromNode !== null) lay.extendCohort(coll, l.fromNode);
if (l.toNode !== null) lay.extendCohort(coll, l.toNode);
}
});
}
public assignLayers() {
super.assignLayers();
const horiz = this.direction === 0.0 || this.direction === 180.0;
// for every vertex, record the maximum vertex width or height for the vertex's layer
const maxsizes = [] as Array<number>;
const net = this.network;
if (net !== null) {
const vit = net.vertexes.iterator;
while (vit.next()) {
const v = vit.value as go.LayeredDigraphVertex;
const lay = v.layer;
let max = maxsizes[lay];
if (max === undefined) max = 0;
const sz = (horiz ? v.width : v.height);
if (sz > max) maxsizes[lay] = sz;
}
vit.reset();
// 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)
while (vit.next()) {
const v = vit.value as go.LayeredDigraphVertex;
const lay = v.layer;
const 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).
}
}
public commitNodes() {
super.commitNodes();
const net = this.network;
// position regular nodes
if (net !== null) {
const vit = net.vertexes.iterator;
while (vit.next()) {
const v = vit.value as go.LayeredDigraphVertex;
if (v.node !== null && !v.node.isLinkLabel) {
v.node.position = new go.Point(v.x, v.y);
}
}
vit.reset();
// position the spouses of each marriage vertex
const layout = this;
while (vit.next()) {
const v = vit.value as go.LayeredDigraphVertex;
if (v.node === null) continue;
if (!v.node.isLinkLabel) continue;
const labnode = v.node;
const lablink = labnode.labeledLink;
if (lablink !== null) {
// 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();
let spouseA = lablink.fromNode;
let spouseB = lablink.toNode;
if (spouseA !== null && spouseB != null) {
// prefer fathers on the left, mothers on the right
if (spouseA.data.s === 'F') { // sex is female
const temp = spouseA;
spouseA = spouseB;
spouseB = temp;
}
// see if the parents are on the desired sides, to avoid a link crossing
const aParentsNode = layout.findParentsMarriageLabelNode(spouseA);
const bParentsNode = layout.findParentsMarriageLabelNode(spouseB);
if (aParentsNode !== null && bParentsNode !== null && aParentsNode.position.x > bParentsNode.position.x) {
// swap the spouses
const 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) {
const pos = new go.Point(v.centerX - spouseA.actualBounds.width / 2, v.y);
spouseA.position = pos;
spouseB.position = pos;
} else if (spouseB.opacity === 0) {
const pos = new go.Point(v.centerX - spouseB.actualBounds.width / 2, v.y);
spouseA.position = pos;
spouseB.position = pos;
}
}
}
}
vit.reset();
// position only-child nodes to be under the marriage label node
while (vit.next()) {
const v = vit.value as go.LayeredDigraphVertex;
if (v.node === null || v.node.linksConnected.count > 1) continue;
const mnode = layout.findParentsMarriageLabelNode(v.node);
if (mnode !== null && mnode.linksConnected.count === 1) { // if only one child
if (layout.network === null) continue;
const mvert = layout.network.findVertex(mnode);
if (mvert !== null) {
const 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
if (layout.diagram !== null) {
const overlaps = layout.diagram.findObjectsIn(newbnds,
(x) => { const p = x.part; return (p instanceof go.Part) ? p : null; },
(p) => p !== v.node,
true
);
if (overlaps.count === 0) {
v.node.move(newbnds.position);
}
}
}
}
}
}
}
public findParentsMarriageLabelNode(node: go.Node) {
const it = node.findNodesInto();
while (it.next()) {
const n = it.value;
if (n.isLinkLabel) return n;
}
return null;
}
}
// end GenogramLayout class
+285
View File
@@ -0,0 +1,285 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "../release/go.js", "./GenogramLayout.js"], factory);
}
})(function (require, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupParents = exports.setupMarriages = exports.findMarriage = exports.setupDiagram = exports.init = void 0;
/*
* Copyright (C) 1998-2020 by Northwoods Software Corporation. All Rights Reserved.
*/
var go = require("../release/go.js");
var GenogramLayout_js_1 = require("./GenogramLayout.js");
function init() {
if (window.goSamples)
window.goSamples(); // init for these samples -- you don't need to call this
var $ = go.GraphObject.make;
var 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: 'yellow', stroke: null }), $(go.Placeholder)),
layout: // use a custom layout, defined below
$(GenogramLayout_js_1.GenogramLayout, { direction: 90, layerSpacing: 30, columnSpacing: 10 })
});
// determine the color for each attribute shape
function attrFill(a) {
switch (a) {
case 'A': return 'green';
case 'B': return 'orange';
case 'C': return 'red';
case 'D': return 'cyan';
case 'E': return 'gold';
case 'F': return 'pink';
case 'G': return 'blue';
case 'H': return 'brown';
case 'I': return 'purple';
case 'J': return 'chartreuse';
case 'K': return 'lightgray';
case 'L': return 'magenta';
case 'S': return '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' }, $(go.Panel, { name: 'ICON' }, $(go.Shape, 'Square', { width: 40, height: 40, strokeWidth: 2, fill: 'white', portId: '' }), $(go.Panel, {
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' }, $(go.Panel, { name: 'ICON' }, $(go.Shape, 'Circle', { width: 40, height: 40, strokeWidth: 2, fill: 'white', portId: '' }), $(go.Panel, {
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, curviness: 15,
layerName: 'Background', selectable: false,
fromSpot: go.Spot.Bottom, toSpot: go.Spot.Top
}, $(go.Shape, { strokeWidth: 2 }));
myDiagram.linkTemplateMap.add('Marriage', // for marriage relationships
$(go.Link, { selectable: false }, $(go.Shape, { strokeWidth: 2, stroke: '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 */);
}
exports.init = init;
// 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, {
linkLabelKeysProperty: 'labelKeys',
// this property determines which template is used
nodeCategoryProperty: 's',
// 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;
// });
}
}
exports.setupDiagram = setupDiagram;
function findMarriage(diagram, a, b) {
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;
}
exports.findMarriage = findMarriage;
// 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;
if (data.ux !== undefined) {
var uxs = [];
if (typeof data.ux === 'number')
uxs = [data.ux];
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);
}
}
}
if (data.vir !== undefined) {
var virs = (typeof data.vir === 'number') ? [data.vir] : data.vir;
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);
}
}
}
}
}
exports.setupMarriages = setupMarriages;
// 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 };
model.addLinkData(cdata);
}
}
}
exports.setupParents = setupParents;
});
+352
View File
@@ -0,0 +1,352 @@
'use strict';
/*
* Copyright (C) 1998-2020 by Northwoods Software Corporation. All Rights Reserved.
*/
import * as go from '../release/go.js';
import { GenogramLayout } from './GenogramLayout.js';
export function init() {
if ((window as any).goSamples) (window as any).goSamples(); // init for these samples -- you don't need to call this
const $ = go.GraphObject.make;
const 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: 'yellow', stroke: null }),
$(go.Placeholder)
),
layout: // use a custom layout, defined below
$(GenogramLayout, { direction: 90, layerSpacing: 30, columnSpacing: 10 })
});
// determine the color for each attribute shape
function attrFill(a: string): string {
switch (a) {
case 'A': return 'green';
case 'B': return 'orange';
case 'C': return 'red';
case 'D': return 'cyan';
case 'E': return 'gold';
case 'F': return 'pink';
case 'G': return 'blue';
case 'H': return 'brown';
case 'I': return 'purple';
case 'J': return 'chartreuse';
case 'K': return 'lightgray';
case 'L': return 'magenta';
case 'S': return '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
const tlsq = go.Geometry.parse('F M1 1 l19 0 0 19 -19 0z');
const trsq = go.Geometry.parse('F M20 1 l19 0 0 19 -19 0z');
const brsq = go.Geometry.parse('F M20 20 l19 0 0 19 -19 0z');
const blsq = go.Geometry.parse('F M1 20 l19 0 0 19 -19 0z');
const slash = go.Geometry.parse('F M38 0 L40 0 40 2 2 40 0 40 0 38z');
function maleGeometry(a: string): go.Geometry {
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
const tlarc = go.Geometry.parse('F M20 20 B 180 90 20 20 19 19 z');
const trarc = go.Geometry.parse('F M20 20 B 270 90 20 20 19 19 z');
const brarc = go.Geometry.parse('F M20 20 B 0 90 20 20 19 19 z');
const blarc = go.Geometry.parse('F M20 20 B 90 90 20 20 19 19 z');
function femaleGeometry(a: string): go.Geometry {
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' },
$(go.Panel,
{ name: 'ICON' },
$(go.Shape, 'Square',
{ width: 40, height: 40, strokeWidth: 2, fill: 'white', 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' },
$(go.Panel,
{ name: 'ICON' },
$(go.Shape, 'Circle',
{ width: 40, height: 40, strokeWidth: 2, fill: 'white', 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, curviness: 15,
layerName: 'Background', selectable: false,
fromSpot: go.Spot.Bottom, toSpot: go.Spot.Top
},
$(go.Shape, { strokeWidth: 2 })
);
myDiagram.linkTemplateMap.add('Marriage', // for marriage relationships
$(go.Link,
{ selectable: false },
$(go.Shape, { strokeWidth: 2, stroke: '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 */);
}
interface Data {
key: number;
// n: name, s: sex, m: mother, f: father, ux: wife, vir: husband, a: attributes/markers
n: string;
s: string;
m: number;
f: number;
ux: number;
vir: number;
a: string;
}
// create and initialize the Diagram.model given an array of node data representing people
export function setupDiagram(diagram: go.Diagram, array: Array<Object>, focusId: number) {
diagram.model =
go.GraphObject.make(go.GraphLinksModel,
{ // declare support for link label nodes
linkLabelKeysProperty: 'labelKeys',
// this property determines which template is used
nodeCategoryProperty: 's',
// create all of the nodes for people
nodeDataArray: array
});
setupMarriages(diagram);
setupParents(diagram);
const 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;
// });
}
}
export function findMarriage(diagram: go.Diagram, a: number, b: number) { // A and B are node keys
const nodeA = diagram.findNodeForKey(a);
const nodeB = diagram.findNodeForKey(b);
if (nodeA !== null && nodeB !== null) {
const it = nodeA.findLinksBetween(nodeB); // in either direction
while (it.next()) {
const 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
export function setupMarriages(diagram: go.Diagram) {
const model = diagram.model as go.GraphLinksModel;
const nodeDataArray = model.nodeDataArray;
for (let i = 0; i < nodeDataArray.length; i++) {
const data = nodeDataArray[i] as Data;
const key = data.key;
if (data.ux !== undefined) {
let uxs: Array<number> = [];
if (typeof data.ux === 'number') uxs = [data.ux] as Array<number>;
for (let j = 0; j < uxs.length; j++) {
const wife = uxs[j];
if (key === wife) {
// or warn no reflexive marriages
continue;
}
const link = findMarriage(diagram, key, wife);
if (link === null) {
// add a label node for the marriage link
const mlab = { s: 'LinkLabel' } as Data;
model.addNodeData(mlab);
// add the marriage link itself, also referring to the label node
const mdata = { from: key, to: wife, labelKeys: [mlab.key], category: 'Marriage' };
model.addLinkData(mdata);
}
}
}
if (data.vir !== undefined) {
const virs: Array<number> = (typeof data.vir === 'number') ? [data.vir] : data.vir as Array<number>;
for (let j = 0; j < virs.length; j++) {
const husband = virs[j];
if (key === husband) {
// or warn no reflexive marriages
continue;
}
const link = findMarriage(diagram, key, husband);
if (link === null) {
// add a label node for the marriage link
const mlab = { s: 'LinkLabel' } as Data;
model.addNodeData(mlab);
// add the marriage link itself, also referring to the label node
const mdata = { from: key, to: husband, labelKeys: [mlab.key], category: 'Marriage' };
model.addLinkData(mdata);
}
}
}
}
}
// process parent-child relationships once all marriages are known
export function setupParents(diagram: go.Diagram) {
const model = diagram.model as go.GraphLinksModel;
const nodeDataArray = model.nodeDataArray;
for (let i = 0; i < nodeDataArray.length; i++) {
const data = nodeDataArray[i] as Data;
const key = data.key;
const mother = data.m;
const father = data.f;
if (mother !== undefined && father !== undefined) {
const 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;
}
const mdata = link.data;
const mlabkey = mdata.labelKeys[0];
const cdata = { from: mlabkey, to: key };
model.addLinkData(cdata);
}
}
}
+44
View File
@@ -0,0 +1,44 @@
<!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">
<link rel='stylesheet' href='../extensions/LightBoxContextMenu.css' />
<script src="../samples/assets/require.js"></script>
<script src="../assets/js/goSamples.js"></script>
<!-- requires minimal.js, built from minimal.ts -->
<script>
function init() {
require(["htmlLightBoxContextMenuScript", "../extensionsTS/LightBoxContextMenu"], function(app) {
app.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="../extensionsTS/LightBoxContextMenu.ts">LightBoxContextMenu.ts</a> and <a href="../extensionsTS/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>
@@ -0,0 +1,50 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "../release/go.js"], factory);
}
})(function (require, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.init = void 0;
/*
* Copyright (C) 1998-2020 by Northwoods Software Corporation. All Rights Reserved.
*/
var go = require("../release/go.js");
var myDiagram = null;
function init() {
if (window.goSamples)
window.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
exports.init = init;
});
@@ -0,0 +1,50 @@
'use strict';
/*
* Copyright (C) 1998-2020 by Northwoods Software Corporation. All Rights Reserved.
*/
import * as go from '../release/go.js';
let myDiagram = null;
export function init() {
if ((window as any).goSamples) (window as any).goSamples(); // init for these samples -- you don't need to call this
const $ = 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 as any).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 as any).myHTMLLightBox; // window.myHTMLLightBox is defined in extensions/LightBoxContextMenu.js
} // end init
+17
View File
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>GoJS&reg; Extensions in TypeScript of Diagramming for HTML5/Canvas by Northwoods Software&reg;</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="Some GoJS samples written in TypeScript." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
window.location = "minimal.html"
</script>
</head>
<body>
You should be redirected to the Minimal TypeScript sample. If not, please <a href="minimal.html">click here.</a>
</body>
</html>
+46
View File
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html>
<head>
<title>Minimal GoJS Sample</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="An almost minimal diagram using a very simple node template and the default link template." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../samples/assets/require.js"></script>
<script src="../assets/js/goSamples.js"></script>
<!-- requires minimal.js, built from minimal.ts -->
<script>
function init() {
require(["minimal"], function(app) {
app.init();
});
}
</script>
</head>
<body onload="init()">
<div id="sample">
<!-- The DIV for the Diagram needs an explicit size or else we won't see anything.
This also adds a border to help see the edges of the viewport. -->
<div id="myDiagramDiv" style="border: solid 1px black; width:400px; height:400px"></div>
<p>
This isn't a truly <i>minimal</i> demonstration of <b>GoJS</b>,
because we do specify a custom Node template, but it's pretty simple.
The whole source for the sample is shown below if you click on the link.
</p>
<p>
This sample sets the <a>Diagram.nodeTemplate</a>, with a <a>Node</a> template that data binds both the text string and the shape's fill color.
For an overview of building your own templates and model data, see the <a href="../learn/index.html">Getting Started tutorial.</a>
</p>
<p>
Using the mouse and common keyboard commands, you can pan, select, move, copy, delete, and undo/redo.
On touch devices, use your finger to act as the mouse, and 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 the selected object.
</p>
<p>
For a more elaborate and capable sample, see the <a href="basic.html">Basic</a> sample.
</p>
</div>
</body>
</html>
+51
View File
@@ -0,0 +1,51 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "../release/go.js"], factory);
}
})(function (require, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.init = void 0;
/*
* Copyright (C) 1998-2020 by Northwoods Software Corporation. All Rights Reserved.
*/
var go = require("../release/go.js");
function init() {
if (window.goSamples)
window.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
{
'undoManager.isEnabled': true // enable undo & redo
});
// 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')));
// 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' }
]);
// Attach to the window for console manipulation
window.myDiagram = myDiagram;
}
exports.init = init;
});
+50
View File
@@ -0,0 +1,50 @@
'use strict';
/*
* Copyright (C) 1998-2020 by Northwoods Software Corporation. All Rights Reserved.
*/
import * as go from '../release/go.js';
export function init() {
if ((window as any).goSamples) (window as any).goSamples(); // init for these samples -- you don't need to call this
const $ = go.GraphObject.make; // for conciseness in defining templates
const myDiagram = $(go.Diagram, 'myDiagramDiv', // create a Diagram for the DIV HTML element
{
'undoManager.isEnabled': true // enable undo & redo
});
// 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'))
);
// 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' }
]);
// Attach to the window for console manipulation
(window as any).myDiagram = myDiagram;
}
+37
View File
@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html>
<head>
<title>Minimal GoJS Sample</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta name="description" content="An almost minimal diagram using a very simple node template and the default link template." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../assets/js/goSamples.js"></script>
<!-- requires minimalModule.js, built from minimalModule.ts -->
<script type="module">
import { init } from "./minimalModule.js";
// lib needs: export const go = self.go;
window.onload = function() {
init();
}
</script>
</head>
<body>
<div id="sample">
<!-- The DIV for the Diagram needs an explicit size or else we won't see anything.
This also adds a border to help see the edges of the viewport. -->
<div id="myDiagramDiv" style="border: solid 1px black; width:400px; height:400px"></div>
<p>
This sample uses <code>&lt;script type="module"&gt;&lt;/script&gt;</code>
to import from <code>minimalModule.js</code>, which imports GoJS from <code>go.mjs</code>
</p>
<p>
In some browsers, you may need to serve this file from localhost to allow ES6 imports.
</p>
<p>
There are many more samples using <code>go.mjs</code> in the <code>../extensionsJSM</code> directory.
</p>
</body>
</html>
+41
View File
@@ -0,0 +1,41 @@
'use strict';
/*
* Copyright (C) 1998-2020 by Northwoods Software Corporation. All Rights Reserved.
*/
// Unlike the other files in this directory,
// you must compile this file with:
// tsc -p .\tsconfigModule.json
import * as go from '../release/go.mjs';
export function init() {
if (window.goSamples)
window.goSamples(); // init for these samples -- you don't need to call this
const $ = go.GraphObject.make; // for conciseness in defining templates
const myDiagram = $(go.Diagram, 'myDiagramDiv', // create a Diagram for the DIV HTML element
{
'undoManager.isEnabled': true // enable undo & redo
});
// 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')));
// 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' }
]);
// Attach to the window for console manipulation
window.myDiagram = myDiagram;
}
+56
View File
@@ -0,0 +1,56 @@
'use strict';
/*
* Copyright (C) 1998-2020 by Northwoods Software Corporation. All Rights Reserved.
*/
// Unlike the other files in this directory,
// you must compile this file with:
// tsc -p .\tsconfigModule.json
import * as go from '../release/go.mjs';
export function init() {
if ((window as any).goSamples) (window as any).goSamples(); // init for these samples -- you don't need to call this
const $ = go.GraphObject.make; // for conciseness in defining templates
const myDiagram = $(go.Diagram, 'myDiagramDiv', // create a Diagram for the DIV HTML element
{
'undoManager.isEnabled': true // enable undo & redo
});
// 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'))
);
// 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' }
]);
// Attach to the window for console manipulation
(window as any).myDiagram = myDiagram;
}
+17
View File
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"module": "umd",
"target": "es5",
"strict": true
},
// For ES6 modules you may wish to use a configuration like this instead:
// "compilerOptions": {
// "target": "es6",
// "strict": true
// }
"exclude": [
"minimalModule.ts"
]
}
+10
View File
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"module": "es2015",
"target": "es2015",
"strict": true
},
"include": [
"minimalModule.ts"
]
}