maxGraph/javascript/examples/swimlanes.html

378 lines
13 KiB
HTML
Raw Normal View History

2012-05-21 20:32:26 +00:00
<!--
2012-12-18 13:09:38 +00:00
$Id: swimlanes.html,v 1.50 2012-11-23 21:07:10 gaudenz Exp $
2012-05-21 20:32:26 +00:00
Copyright (c) 2006-2010, JGraph Ltd
Swimlanes example for mxGraph. This example demonstrates using
swimlanes for pools and lanes and adding cells and edges between
them. This also demonstrates using the stack layout as an
automatic layout.
-->
<html>
<head>
<title>Swimlanes example for mxGraph</title>
<!-- Sets the basepath for the library if not in same directory -->
<script type="text/javascript">
mxBasePath = '../src';
</script>
<!-- Loads and initializes the library -->
<script type="text/javascript" src="../src/js/mxClient.js"></script>
<!-- Example code -->
<script type="text/javascript">
// Defines an icon for creating new connections in the connection handler.
// This will automatically disable the highlighting of the source vertex.
mxConnectionHandler.prototype.connectImage = new mxImage('images/connector.gif', 16, 16);
// Program starts here. Creates a sample graph in the
// DOM node with the specified ID. This function is invoked
// from the onLoad event handler of the document (see below).
function main(container)
{
// Checks if browser is supported
if (!mxClient.isBrowserSupported())
{
// Displays an error message if the browser is
// not supported.
mxUtils.error('Browser is not supported!', 200, false);
}
else
{
// Enables crisp rendering of swimlanes in SVG
mxSwimlane.prototype.crisp = true;
// Creates a wrapper editor around a new graph inside
// the given container using an XML config for the
// keyboard bindings
var config = mxUtils.load(
'editors/config/keyhandler-commons.xml').
getDocumentElement();
var editor = new mxEditor(config);
editor.setGraphContainer(container);
var graph = editor.graph;
var model = graph.getModel();
// Auto-resizes the container
graph.border = 80;
graph.getView().translate = new mxPoint(graph.border/2, graph.border/2);
graph.setResizeContainer(true);
graph.graphHandler.setRemoveCellsFromParent(false);
// Changes the default vertex style in-place
var style = graph.getStylesheet().getDefaultVertexStyle();
style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_SWIMLANE;
2012-12-18 13:09:38 +00:00
style[mxConstants.STYLE_VERTICAL_ALIGN] = 'middle';
style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR] = 'white';
2012-05-21 20:32:26 +00:00
style[mxConstants.STYLE_FONTSIZE] = 11;
style[mxConstants.STYLE_STARTSIZE] = 22;
style[mxConstants.STYLE_HORIZONTAL] = false;
style[mxConstants.STYLE_FONTCOLOR] = 'black';
style[mxConstants.STYLE_STROKECOLOR] = 'black';
delete style[mxConstants.STYLE_FILLCOLOR];
style = mxUtils.clone(style);
style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_RECTANGLE;
style[mxConstants.STYLE_FONTSIZE] = 10;
style[mxConstants.STYLE_ROUNDED] = true;
style[mxConstants.STYLE_HORIZONTAL] = true;
style[mxConstants.STYLE_VERTICAL_ALIGN] = 'middle';
delete style[mxConstants.STYLE_STARTSIZE];
2012-12-18 13:09:38 +00:00
style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR] = 'none';
2012-05-21 20:32:26 +00:00
graph.getStylesheet().putCellStyle('process', style);
style = mxUtils.clone(style);
style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_ELLIPSE;
style[mxConstants.STYLE_PERIMETER] = mxPerimeter.EllipsePerimeter;
delete style[mxConstants.STYLE_ROUNDED];
graph.getStylesheet().putCellStyle('state', style);
style = mxUtils.clone(style);
style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_RHOMBUS;
style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RhombusPerimeter;
style[mxConstants.STYLE_VERTICAL_ALIGN] = 'top';
style[mxConstants.STYLE_SPACING_TOP] = 40;
style[mxConstants.STYLE_SPACING_RIGHT] = 64;
graph.getStylesheet().putCellStyle('condition', style);
style = mxUtils.clone(style);
style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_DOUBLE_ELLIPSE;
style[mxConstants.STYLE_PERIMETER] = mxPerimeter.EllipsePerimeter;
style[mxConstants.STYLE_SPACING_TOP] = 28;
style[mxConstants.STYLE_FONTSIZE] = 14;
style[mxConstants.STYLE_FONTSTYLE] = 1;
delete style[mxConstants.STYLE_SPACING_RIGHT];
graph.getStylesheet().putCellStyle('end', style);
style = graph.getStylesheet().getDefaultEdgeStyle();
style[mxConstants.STYLE_EDGE] = mxEdgeStyle.ElbowConnector;
style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_BLOCK;
style[mxConstants.STYLE_ROUNDED] = true;
style[mxConstants.STYLE_FONTCOLOR] = 'black';
style[mxConstants.STYLE_STROKECOLOR] = 'black';
style = mxUtils.clone(style);
style[mxConstants.STYLE_DASHED] = true;
style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_OPEN;
style[mxConstants.STYLE_STARTARROW] = mxConstants.ARROW_OVAL;
graph.getStylesheet().putCellStyle('crossover', style);
// Installs double click on middle control point and
// changes style of edges between empty and this value
graph.alternateEdgeStyle = 'elbow=vertical';
// Adds automatic layout and various switches if the
// graph is enabled
if (graph.isEnabled())
{
// Allows new connections but no dangling edges
graph.setConnectable(true);
graph.setAllowDanglingEdges(false);
// End-states are no valid sources
var previousIsValidSource = graph.isValidSource;
graph.isValidSource = function(cell)
{
if (previousIsValidSource.apply(this, arguments))
{
var style = this.getModel().getStyle(cell);
return style == null ||
!(style == 'end' ||
style.indexOf('end') == 0);
}
return false;
};
// Start-states are no valid targets, we do not
// perform a call to the superclass function because
// this would call isValidSource
// Note: All states are start states in
// the example below, so we use the state
// style below
graph.isValidTarget = function(cell)
{
var style = this.getModel().getStyle(cell);
return !this.getModel().isEdge(cell) &&
!this.isSwimlane(cell) &&
(style == null ||
!(style == 'state' ||
style.indexOf('state') == 0));
};
// Allows dropping cells into new lanes and
// lanes into new pools, but disallows dropping
// cells on edges to split edges
graph.setDropEnabled(true);
graph.setSplitEnabled(false);
// Returns true for valid drop operations
graph.isValidDropTarget = function(target, cells, evt)
{
if (this.isSplitEnabled() &&
this.isSplitTarget(target, cells, evt))
{
return true;
}
var model = this.getModel();
var lane = false;
var pool = false;
var cell = false;
// Checks if any lanes or pools are selected
for (var i = 0; i < cells.length; i++)
{
var tmp = model.getParent(cells[i]);
lane = lane || this.isPool(tmp);
pool = pool || this.isPool(cells[i]);
cell = cell || !(lane || pool);
}
return !pool &&
cell != lane &&
((lane && this.isPool(target)) ||
(cell && this.isPool(model.getParent(target))));
};
// Adds new method for identifying a pool
graph.isPool = function(cell)
{
var model = this.getModel();
var parent = model.getParent(cell);
return parent != null &&
model.getParent(parent) == model.getRoot();
};
// Changes swimlane orientation while collapsed
graph.model.getStyle = function(cell)
{
var style = mxGraphModel.prototype.getStyle.apply(this, arguments);
if (graph.isCellCollapsed(cell))
{
if (style != null)
{
style += ';';
}
else
{
style = '';
}
style += 'horizontal=1;align=left;spacingLeft=14;';
}
return style;
};
// Keeps widths on collapse/expand
var foldingHandler = function(sender, evt)
{
var cells = evt.getProperty('cells');
for (var i = 0; i < cells.length; i++)
{
var geo = graph.model.getGeometry(cells[i]);
if (geo.alternateBounds != null)
{
geo.width = geo.alternateBounds.width;
}
}
};
graph.addListener(mxEvent.FOLD_CELLS, foldingHandler);
}
// Applies size changes to siblings and parents
new mxSwimlaneManager(graph);
// Creates a stack depending on the orientation of the swimlane
var layout = new mxStackLayout(graph, false);
// Makes sure all children fit into the parent swimlane
layout.resizeParent = true;
// Applies the size to children if parent size changes
layout.fill = true;
// Only update the size of swimlanes
layout.isVertexIgnored = function(vertex)
{
return !graph.isSwimlane(vertex);
}
// Keeps the lanes and pools stacked
var layoutMgr = new mxLayoutManager(graph);
layoutMgr.getLayout = function(cell)
{
if (!model.isEdge(cell) && graph.getModel().getChildCount(cell) > 0 &&
(model.getParent(cell) == model.getRoot() || graph.isPool(cell)))
{
layout.fill = graph.isPool(cell);
return layout;
}
return null;
};
// Gets the default parent for inserting new cells. This
// is normally the first child of the root (ie. layer 0).
var parent = graph.getDefaultParent();
// Adds cells to the model in a single step
model.beginUpdate();
try
{
var pool1 = graph.insertVertex(parent, null, 'Pool 1', 0, 0, 640, 0);
pool1.setConnectable(false);
var lane1a = graph.insertVertex(pool1, null, 'Lane A', 0, 0, 640, 110);
lane1a.setConnectable(false);
var lane1b = graph.insertVertex(pool1, null, 'Lane B', 0, 0, 640, 110);
lane1b.setConnectable(false);
var pool2 = graph.insertVertex(parent, null, 'Pool 2', 0, 0, 640, 0);
pool2.setConnectable(false);
var lane2a = graph.insertVertex(pool2, null, 'Lane A', 0, 0, 640, 140);
lane2a.setConnectable(false);
var lane2b = graph.insertVertex(pool2, null, 'Lane B', 0, 0, 640, 110);
lane2b.setConnectable(false);
var start1 = graph.insertVertex(lane1a, null, null, 40, 40, 30, 30, 'state');
var end1 = graph.insertVertex(lane1a, null, 'A', 560, 40, 30, 30, 'end');
var step1 = graph.insertVertex(lane1a, null, 'Contact\nProvider', 90, 30, 80, 50, 'process');
var step11 = graph.insertVertex(lane1a, null, 'Complete\nAppropriate\nRequest', 190, 30, 80, 50, 'process');
var step111 = graph.insertVertex(lane1a, null, 'Receive and\nAcknowledge', 385, 30, 80, 50, 'process');
var start2 = graph.insertVertex(lane2b, null, null, 40, 40, 30, 30, 'state');
var step2 = graph.insertVertex(lane2b, null, 'Receive\nRequest', 90, 30, 80, 50, 'process');
var step22 = graph.insertVertex(lane2b, null, 'Refer to Tap\nSystems\nCoordinator', 190, 30, 80, 50, 'process');
var step3 = graph.insertVertex(lane1b, null, 'Request 1st-\nGate\nInformation', 190, 30, 80, 50, 'process');
var step33 = graph.insertVertex(lane1b, null, 'Receive 1st-\nGate\nInformation', 290, 30, 80, 50, 'process');
var step4 = graph.insertVertex(lane2a, null, 'Receive and\nAcknowledge', 290, 20, 80, 50, 'process');
var step44 = graph.insertVertex(lane2a, null, 'Contract\nConstraints?', 400, 20, 50, 50, 'condition');
var step444 = graph.insertVertex(lane2a, null, 'Tap for gas\ndelivery?', 480, 20, 50, 50, 'condition');
var end2 = graph.insertVertex(lane2a, null, 'B', 560, 30, 30, 30, 'end');
var end3 = graph.insertVertex(lane2a, null, 'C', 560, 84, 30, 30, 'end');
var e = null;
graph.insertEdge(lane1a, null, null, start1, step1);
graph.insertEdge(lane1a, null, null, step1, step11);
graph.insertEdge(lane1a, null, null, step11, step111);
graph.insertEdge(lane2b, null, null, start2, step2);
graph.insertEdge(lane2b, null, null, step2, step22);
graph.insertEdge(parent, null, null, step22, step3);
graph.insertEdge(lane1b, null, null, step3, step33);
graph.insertEdge(lane2a, null, null, step4, step44);
graph.insertEdge(lane2a, null, 'No', step44, step444, 'verticalAlign=bottom');
graph.insertEdge(parent, null, 'Yes', step44, step111, 'verticalAlign=bottom;horizontal=0');
graph.insertEdge(lane2a, null, 'Yes', step444, end2, 'verticalAlign=bottom');
e = graph.insertEdge(lane2a, null, 'No', step444, end3, 'verticalAlign=top');
e.geometry.points = [new mxPoint(step444.geometry.x + step444.geometry.width / 2,
end3.geometry.y + end3.geometry.height / 2)];
graph.insertEdge(parent, null, null, step1, step2, 'crossover');
graph.insertEdge(parent, null, null, step3, step11, 'crossover');
e = graph.insertEdge(lane1a, null, null, step11, step33, 'crossover');
e.geometry.points = [new mxPoint(step33.geometry.x + step33.geometry.width / 2 + 20,
step11.geometry.y + step11.geometry.height * 4 / 5)];
graph.insertEdge(parent, null, null, step33, step4);
graph.insertEdge(lane1a, null, null, step111, end1);
}
finally
{
// Updates the display
model.endUpdate();
}
}
};
</script>
</head>
<body onload="main(document.getElementById('graphContainer'))">
<div id="graphContainer"
style="position:absolute;overflow:hidden;top:40px;left:40px;width:600px;height:400px;border: gray dotted 1px;cursor:default;">
</div>
</body>
</html>