var $ = go.GraphObject.make; // for conciseness in defining templates
var portSize = new go.Size(5, 5);
myDiagram =
$(go.Diagram, “myDiagramDiv”, // refers to its DIV HTML element by id
{
layout:
$(FishboneLayout,
{
angle: 180,
layerSpacing: 10,
nodeSpacing: 20,
rowSpacing: 10
}),
isReadOnly: true
}); // do not allow the user to modify the diagram
// define the normal node template, just some text
myDiagram.nodeTemplate =
$(go.Node,
$(go.TextBlock,
new go.Binding(“text”),
new go.Binding(“font”, “”, convertFont))
);
myDiagram.nodeTemplateMap.add(“Procedure”, // the default category
$(go.Node, “Spot”,
// nodeStyle(),
{
minSize: new go.Size(100, 40),
resizeObjectName: "proNode",
},
// new go.Binding("location", "", toLocation).makeTwoWay(fromLocation),
new go.Binding("movable", "isMovable"),
new go.Binding("resizable", "isResizable", function (data) {
if (data == true && isEditEnabled) {
return true;
} else {
return false;
}
}),
$(go.Panel, "Auto", {
name: "proNode",
},
new go.Binding("desiredSize", "nodeSize", go.Size.parse).makeTwoWay(go.Size.stringify),
$(go.Shape, "Rectangle",
{
fill: "#9dc8eb",
stroke: 'gray',
strokeWidth: 0.5,
portId: "",
},
),
$(go.Panel, "Table",{
stretch: go.GraphObject.Fill,
},
$(go.TextBlock, "Procedure",
{
//textEditor: window.TextEditorSelectBox,
name: "OBJTEXT",
stroke: "black",
editable: true,
margin: 8, textAlign: "center",
wrap: go.TextBlock.WrapFit,
//textValidation: isNameOK,
row: 0, column: 0,columnSpan:5
},
new go.Binding("text", "text").makeTwoWay(),
new go.Binding("choices"),
),
$(go.TextBlock, {row: 2, column:3,font: "8px Tahoma",alignment: go.Spot.Center,margin: new go.Margin(1, 1, 1, 1),},
new go.Binding("text", "reference")
),
)
),
$(go.Panel, "Vertical",
new go.Binding("itemArray", "leftArray"),
{
alignment: go.Spot.Left,
alignmentFocus: new go.Spot(0, 0.5, 8, 0),
itemTemplate: makeItemTemplate('left'),
}
),
$(go.Panel, "Vertical",
new go.Binding("itemArray", "rightArray"),
{
alignment: go.Spot.Right,
alignmentFocus: new go.Spot(1, 0.5, -8, 0),
itemTemplate: makeItemTemplate('right'),
},
),
$(go.Panel, "Horizontal",
new go.Binding("itemArray", "topArray"),
{
alignment: go.Spot.Top,
alignmentFocus: new go.Spot(0, 0.9, 8, 0),
itemTemplate: makeItemTemplate('top'),
},
),
$(go.Panel, "Horizontal",
new go.Binding("itemArray", "bottomArray"),
{
alignment: go.Spot.Bottom,
alignmentFocus: new go.Spot(1, 0.6, -10, 0),
itemTemplate: makeItemTemplate('bottom'),
},
)
));
function makeItemTemplate(side) {
if (side == 'left') {
return $(go.Panel,
{
_side: "left", // internal property to make it easier to tell which side it's on
fromSpot: go.Spot.Left, toSpot: go.Spot.Left,
fromLinkable: true, toLinkable: true, cursor: "pointer",
//contextMenu: portMenu
},
new go.Binding("portId", "portId"),
$(go.Shape, "Rectangle",
{
stroke: null, strokeWidth: 0,
desiredSize: portSize,
margin: new go.Margin(1, 0)
},
new go.Binding("fill", "portColor"))
);
// end itemTemplate
}
if (side == 'top') {
return $(go.Panel,
{
_side: "top",
fromSpot: go.Spot.Top, toSpot: go.Spot.Top,
fromLinkable: true, toLinkable: true, cursor: "pointer",
//contextMenu: portMenu
},
new go.Binding("portId", "portId"),
$(go.Shape, "Rectangle",
{
stroke: null, strokeWidth: 0,
desiredSize: portSize,
margin: new go.Margin(0, 1)
},
new go.Binding("fill", "portColor"))
) // end itemTemplate
}
if (side == 'right') {
return $(go.Panel,
{
_side: "right",
fromSpot: go.Spot.Right, toSpot: go.Spot.Right,
fromLinkable: true, toLinkable: true, cursor: "pointer",
// contextMenu: portMenu
},
new go.Binding("portId", "portId"),
$(go.Shape, "Rectangle",
{
stroke: null, strokeWidth: 0,
desiredSize: portSize,
margin: new go.Margin(1, 0)
},
new go.Binding("fill", "portColor"))
) // end itemTemplate
}
if (side == 'bottom') {
return $(go.Panel,
{
_side: "bottom",
fromSpot: go.Spot.Bottom, toSpot: go.Spot.Bottom,
fromLinkable: true, toLinkable: true, cursor: "pointer",
//contextMenu: portMenu
},
new go.Binding("portId", "portId"),
$(go.Shape, "Rectangle",
{
stroke: null, strokeWidth: 0,
desiredSize: portSize,
margin: new go.Margin(0, 1)
},
new go.Binding("fill", "portColor"))
) // end itemTemplate
}
}
function convertFont(data) {
var size = data.size;
if (size === undefined) size = 13;
var weight = data.weight;
if (weight === undefined) weight = “”;
return weight + " " + size + “px sans-serif”;
}
go.Shape.defineFigureGenerator(“HexArrow”, function(shape, w, h) {
var param1 = shape ? shape.parameter1 : NaN; // pointiness of arrow, lower is more pointy
if (isNaN(param1)) param1 = .05;
var geo = new go.Geometry();
var fig = new go.PathFigure(w, h/2, true);
geo.add(fig);
fig.add(new go.PathSegment(go.PathSegment.Line, (1-param1) * w, h));
fig.add(new go.PathSegment(go.PathSegment.Line, 0, h));
fig.add(new go.PathSegment(go.PathSegment.Line, param1 * w, h/2));
fig.add(new go.PathSegment(go.PathSegment.Line, 0, 0));
fig.add(new go.PathSegment(go.PathSegment.Line, (1-param1) * w, 0).close());
geo.spot1 = new go.Spot(param1, 0);
geo.spot2 = new go.Spot(1-param1, 1);
return geo;
});
myDiagram.groupTemplate=
$(go.Group, “Auto”,
{
layout:
$(FishboneLayout,
{
angle: 180,
layerSpacing: 10,
nodeSpacing: 20,
rowSpacing: 10
})
},
$(go.Shape, “HexArrow”, { fill: “rgba(0,0,0,0.1)”, strokeWidth: 0 }),
$(go.Placeholder, { padding: 4 }));
myDiagram.groupTemplateMap.add("Parallel",
$(go.Group, "Vertical",
new go.Binding("location", "", toLocation).makeTwoWay(fromLocation),
$(go.Panel, "Auto",
$(go.Shape, "RoundedRectangle", // surrounds the Placeholder
{ parameter1: 14,
fill: "rgba(128,128,128,0.33)" }),
),
//$(go.Placeholder, { padding: 4 })
/*$(go.TextBlock, // group title
{ alignment: go.Spot.Right, font: "Bold 12pt Sans-Serif" },
new go.Binding("text", "text")) */
));
myDiagram.groupTemplateMap.add("ProcessGroup",
$(go.Group, "Spot",
{
ungroupable: true,
},
new go.Binding("location", "", toLocation).makeTwoWay(fromLocation),
$(go.Panel, "Auto", {
//name: "processGroupNode"
},
$(go.Shape,"HexArrow",
{
name: "processGroupNode",
minSize: new go.Size(300, 100),
// geometryString: geoString,
fill: "red",
portId: "",
}
),
$("SubGraphExpanderButton",
{
alignment: go.Spot.TopLeft,
name: "EXPANDBUTTON", width: 10, height: 10, margin: 2,
}),
$(go.TextBlock,
{
width: 180, height: 20,
alignment: go.Spot.TopLeft,
editable: false,
margin: new go.Margin(4,0,0,12),
opacity: 0.95, // allow some color to show through
//stroke: "#404040",
font: "bold 8px Tahoma",
}
, new go.Binding("text", "text")
))
));
// This demo switches the Diagram.linkTemplate between the “normal” and the “fishbone” templates.
// If you are only doing a FishboneLayout, you could just set Diagram.linkTemplate
// to the template named “fishbone” here, and not switch templates dynamically.
// use this link template for fishbone layouts
myDiagram.linkTemplate =
$(FishboneLink, // defined above
$(go.Shape)
);
myDiagram.linkTemplateMap.add(“SimpleLink”,
$(go.Link, // the whole link panel
{
routing: go.Link.AvoidsNodes,
curve: go.Link.JumpOver,
corner: 5,
relinkableFrom: true,
relinkableTo: true,
reshapable: true,
resegmentable: true,
fromLinkable: true,
toLinkable: true,
selectionAdorned: false
},
//new go.Binding(“points”).makeTwoWay(),
new go.Binding(“fromSpot”, “fromSpot”, go.Spot.parse),
new go.Binding(“toSpot”, “toSpot”, go.Spot.parse),
$(go.Shape, // the link path shape
{isPanelMain: true, stroke: “gray”, strokeWidth: 1, name: “OBJSHAPE”},
),
$(go.Shape, // the arrowhead
{toArrow: “standard”, strokeWidth: 0, fill: “gray”, name: “ARWSHAPE”})
));
var json = {
“id”: 298,
“category”: “MainNodeFishbone”,
“key”: 1,
“reference”: “P-FIS-01”,
“text”: “Fishbone-01”,
“choices”: [],
“isGroup”: false,
“group”: 0,
“nodeType”: “Node”,
“url”: null,
“angle”: 0,
“topArray”: [],
“leftArray”: [],
“rightArray”: [],
“bottomArray”: [],
“extraNodeData”: null,
“size”: “16”,
“weight”: “bold”,
“isFishbone”: false,
“members”: [],
“causes”: [
{
"fishbone": false,
"id": 308,
"category": "ProcessGroup",
"key": 2,
"reference": "ddd",
"text": "ddddd",
"choices": [],
"isGroup": true,
"group": 0,
"location": "205.73 0.00",
"nodeSize": "300 157",
"nodeType": "Node",
"isMovable": true,
"isResizable": true,
"angle": 0,
"topArray": [],
"leftArray": [],
"rightArray": [],
"bottomArray": [],
"extraNodeData": {
"nodeShape": null,
"backgroundColor": "#d3d3d3",
"borderColor": "#000000",
"fontColor": "#000000",
"borderWidth": 1,
"borderDashLink": null
},
"size": null,
"weight": null,
"isFishbone": false,
"members": [
{
"fishbone": false,
"allNodePorts": [
{
"id": 16,
"portId": "right0",
"portColor": "#000000",
"portOpacity": "1.0"
}
],
"id": 302,
"category": "Procedure",
"key": "PROC_303_308_121_PROS_308_298_122_298",
"reference": "P-05",
"text": "Procedure-05",
"choices": [
"Procedure-05"
],
"isGroup": false,
"group": 2,
"horiz": false,
"location": "48.91 21.09",
"nodeSize": "114 40",
"nodeType": "SubNode",
"url": "/cnm/proc/procedure/v/view/15",
"angle": 0,
"topArray": [],
"leftArray": [],
"rightArray": [
{
"id": 16,
"portId": "right0",
"portColor": "#000000",
"portOpacity": "1.0"
}
],
"bottomArray": [],
"extraNodeData": null,
"size": null,
"weight": null,
"isFishbone": false,
"members": [],
"causes": [],
},
{
"fishbone": false,
"allNodePorts": [
{
"id": 15,
"portId": "right0",
"portColor": "#000000",
"portOpacity": "1.0"
}
],
"id": 303,
"category": "Procedure",
"key": "PROC_302_308_121_PROS_308_298_122_298",
"reference": "P-1.2",
"text": "Procedure-1.2",
"choices": [
"Procedure-1.2"
],
"showIO": null,
"isGroup": false,
"group": 2,
"location": "62.91 96.09",
"nodeSize": null,
"nodeType": "SubNode",
"isMovable": false,
"isResizable": false,
"angle": 0,
"topArray": [],
"leftArray": [],
"rightArray": [
{
"id": 15,
"portId": "right0",
"portColor": "#000000",
"portOpacity": "1.0",
}
],
"bottomArray": [],
"extraNodeData": null,
"size": null,
"weight": null,
"isFishbone": false,
"members": [],
"causes": [],
}
],
"causes": []
},
{
"fishbone": false,
"allNodePorts": [],
"id": 309,
"category": "Procedure",
"key": 5,
"reference": "P-1.3",
"text": "Procedure-1.3",
"choices": [],
"showIO": null,
"isGroup": false,
"group": 0,
"location": "436.23 178.00",
"nodeSize": null,
"nodeType": "Node",
"isMovable": true,
"isResizable": true,
"url": null,
"angle": 0,
"topArray": [],
"leftArray": [],
"rightArray": [],
"bottomArray": [],
"extraNodeData": null,
"size": null,
"weight": null,
"isFishbone": false,
"members": [],
"causes": []
},
{
"fishbone": false,
"allNodePorts": [],
"id": 305,
"category": "Procedure",
"key": 6,
"reference": "P-02",
"text": "Procedure-02",
"choices": [],
"showIO": null,
"isGroup": false,
"group": 0,
"horiz": false,
"location": "99.23 118.00",
"nodeSize": null,
"nodeType": "Node",
"isMovable": true,
"isResizable": true,
"url": null,
"angle": 0,
"topArray": [],
"leftArray": [],
"rightArray": [],
"bottomArray": [],
"extraNodeData": null,
"size": null,
"weight": null,
"isFishbone": false,
"members": [],
"causes": []
}
],
};
// the model is now a GraphLinksModel, not a TreeModel, so that we can use Groups
function walkJson(obj, narr, larr, grpkey) {
var key = narr.length;
obj.key = key;
if (grpkey !== undefined) obj.group = grpkey;
narr.push(obj);
var children = obj.causes;
if (Array.isArray(children)) {
for (var i = 0; i < children.length; i++) {
var o = children[i];
// instead of a reference to parent node data, add a link data object
larr.push({ from: key, to: narr.length });
if (o.isGroup) {
o.key = narr.length;
if (grpkey !== undefined) o.group = grpkey;
narr.push(o);
var members = o.members;
if (Array.isArray(members)) {
members.forEach(m => walkJson(m, narr, larr, o.key));
}
} else {
walkJson(o, narr, larr, grpkey);
}
}
}
}
function toLocation(data) {
var loc = go.Point.parse(data.loc);
if (data.group !== undefined) {
var groupdata = myDiagram.model.findNodeDataForKey(data.group);
if (groupdata) {
loc.add(toLocation(groupdata));
}
}
return loc;
};
// fromLocation just saves in data.loc either the absolute location if there's no containing Group,
// or the relative location with its containing Group.
function fromLocation(location, data) {
if (data.group !== undefined) {
var group = myDiagram.findNodeForKey(data.group);
if (group) {
var loc = location.copy().subtract(group.location);
data.loc = loc.x.toFixed(2) + " " + loc.y.toFixed(2); //go.Point.stringify(loc);
}
} else {
data.loc = go.Point.stringify(location);
}
};
myDiagram.linkTemplateMap.add("SimpleLink",
$(go.Link, // the whole link panel
{
routing: go.Link.AvoidsNodes,
curve: go.Link.JumpOver,
corner: 5,
relinkableFrom: true,
relinkableTo: true,
reshapable: true,
resegmentable: true,
fromLinkable: true,
toLinkable: true,
selectionAdorned: false
},
//new go.Binding("points").makeTwoWay(),
new go.Binding("fromSpot", "fromSpot", go.Spot.parse),
new go.Binding("toSpot", "toSpot", go.Spot.parse),
$(go.Shape, // the link path shape
{isPanelMain: true, stroke: "gray", strokeWidth: 1, name: "OBJSHAPE"}),
$(go.Shape, // the arrowhead
{toArrow: "standard", strokeWidth: 0, fill: "gray", name: "ARWSHAPE"}),
));
var nodeDataArray = [{ isGroup: true, key: 0 }
];
var linkDataArray = [
{
"from": "PROC_303_308_121_PROS_308_298_122_298",
"to": "PROC_302_308_121_PROS_308_298_122_298",
"fromPort": "right0",
"toPort": "right0",
"category": "SimpleLink"
}
];
walkJson(json, nodeDataArray, linkDataArray, 0);
console.log(nodeDataArray);
console.log(linkDataArray);
myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);