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:
Executable
+43
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
Executable
+80
@@ -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>
|
||||
Executable
+245
@@ -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);
|
||||
});
|
||||
Executable
+310
@@ -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);
|
||||
Executable
+101
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>GoJS® Extensions in TypeScript of Diagramming for HTML5/Canvas by Northwoods Software®</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>
|
||||
|
||||
Executable
+46
@@ -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>
|
||||
Executable
+51
@@ -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;
|
||||
});
|
||||
Executable
+50
@@ -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
@@ -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><script type="module"></script></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
@@ -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
@@ -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;
|
||||
}
|
||||
Executable
+17
@@ -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
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "es2015",
|
||||
"target": "es2015",
|
||||
"strict": true
|
||||
},
|
||||
"include": [
|
||||
"minimalModule.ts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user