932 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			932 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<html>
 | 
						|
<!--
 | 
						|
README
 | 
						|
******
 | 
						|
- Edge-to-edge connections: We store the point where the mouse
 | 
						|
was released in the terminal points of the edge geometry and
 | 
						|
use that point to find the nearest segment on the target edge
 | 
						|
and the connection point between the two edges in
 | 
						|
mxGraphView.updateFixedTerminalPoint.
 | 
						|
 | 
						|
- The orthogonal router, which is implemented as an edge style,
 | 
						|
computes its result based on the output of mxGraphView.
 | 
						|
updateFixedTerminalPoint, which computes all connection points
 | 
						|
for edge-to-edge connections and constrained ports and vertices
 | 
						|
and stores them in state.absolutePoints. 
 | 
						|
 | 
						|
- Routing directions are stored in the 'portConstraint' style.
 | 
						|
Possible values for this style horizontal and vertical. Note
 | 
						|
that this may have other values depending on the edge style.
 | 
						|
 | 
						|
- For edge-to-edge connections, a 'source-/targetConstraint'
 | 
						|
style is added in updateFixedTerminalPoint that contains the
 | 
						|
orientation of the segment that the edge connects to. Possible
 | 
						|
values are horizontal, vertical.
 | 
						|
 | 
						|
- An alternative solution for connection points via connection
 | 
						|
constraints is demonstrated. In this setup, the edge is connected
 | 
						|
to the parent cell directly. There are no child cells that act
 | 
						|
as "ports". Instead, the connection information is stored as a
 | 
						|
relative point in the connecting edge. (See also: portrefs.html
 | 
						|
for storing references to ports.)
 | 
						|
 | 
						|
-->
 | 
						|
<head>
 | 
						|
	<title>Wires</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>
 | 
						|
	<script type="text/javascript">
 | 
						|
		// If connect preview is not moved away then getCellAt is used to detect the cell under
 | 
						|
		// the mouse if the mouse is over the preview shape in IE (no event transparency), ie.
 | 
						|
		// the built-in hit-detection of the HTML document will not be used in this case.
 | 
						|
		mxConnectionHandler.prototype.movePreviewAway = false;
 | 
						|
		mxConnectionHandler.prototype.waypointsEnabled = true;
 | 
						|
		mxGraph.prototype.resetEdgesOnConnect = false;
 | 
						|
		mxConstants.SHADOWCOLOR = '#C0C0C0';
 | 
						|
		var joinNodeSize = 7;
 | 
						|
		var strokeWidth = 2;
 | 
						|
		
 | 
						|
		// Replaces the port image
 | 
						|
		mxConstraintHandler.prototype.pointImage = new mxImage('images/dot.gif', 10, 10);
 | 
						|
		
 | 
						|
		// Enables guides
 | 
						|
		mxGraphHandler.prototype.guidesEnabled = true;
 | 
						|
		
 | 
						|
	    // Alt disables guides
 | 
						|
	    mxGuide.prototype.isEnabledForEvent = function(evt)
 | 
						|
	    {
 | 
						|
	    	return !mxEvent.isAltDown(evt);
 | 
						|
	    };
 | 
						|
		
 | 
						|
		// Enables snapping waypoints to terminals
 | 
						|
		mxEdgeHandler.prototype.snapToTerminals = true;
 | 
						|
		
 | 
						|
		function main(container)
 | 
						|
		{
 | 
						|
			var graph = new mxGraph(container);
 | 
						|
			graph.view.scale = 1;
 | 
						|
			graph.setPanning(true);
 | 
						|
			graph.setConnectable(true);
 | 
						|
			graph.setConnectableEdges(true);
 | 
						|
			graph.setDisconnectOnMove(false);
 | 
						|
			graph.foldingEnabled = false;
 | 
						|
			
 | 
						|
			//Maximum size
 | 
						|
			graph.maximumGraphBounds = new mxRectangle(0, 0, 800, 600)
 | 
						|
			graph.border = 50;
 | 
						|
 | 
						|
			// Panning handler consumed right click so this must be
 | 
						|
			// disabled if right click should stop connection handler.
 | 
						|
			graph.panningHandler.isPopupTrigger = function() { return false; };
 | 
						|
			
 | 
						|
			// Enables return key to stop editing (use shift-enter for newlines)
 | 
						|
			graph.setEnterStopsCellEditing(true);
 | 
						|
 | 
						|
			// Adds rubberband selection
 | 
						|
			new mxRubberband(graph);
 | 
						|
			
 | 
						|
			// Alternative solution for implementing connection points without child cells.
 | 
						|
			// This can be extended as shown in portrefs.html example to allow for per-port
 | 
						|
			// incoming/outgoing direction.
 | 
						|
			graph.getAllConnectionConstraints = function(terminal)
 | 
						|
			{
 | 
						|
			   var geo = (terminal != null) ? this.getCellGeometry(terminal.cell) : null;
 | 
						|
 | 
						|
			   if ((geo != null ? !geo.relative : false) &&
 | 
						|
				   this.getModel().isVertex(terminal.cell) &&
 | 
						|
				   this.getModel().getChildCount(terminal.cell) == 0)
 | 
						|
			   {
 | 
						|
					return [new mxConnectionConstraint(new mxPoint(0, 0.5), false),
 | 
						|
				    	new mxConnectionConstraint(new mxPoint(1, 0.5), false)];
 | 
						|
			    }
 | 
						|
 | 
						|
				return null;
 | 
						|
			};
 | 
						|
 | 
						|
			// Makes sure non-relative cells can only be connected via constraints
 | 
						|
			graph.connectionHandler.isConnectableCell = function(cell)
 | 
						|
			{
 | 
						|
				if (this.graph.getModel().isEdge(cell))
 | 
						|
				{
 | 
						|
					return true;
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					var geo = (cell != null) ? this.graph.getCellGeometry(cell) : null;
 | 
						|
					
 | 
						|
					return (geo != null) ? geo.relative : false;
 | 
						|
				}
 | 
						|
			};
 | 
						|
			mxEdgeHandler.prototype.isConnectableCell = function(cell)
 | 
						|
			{
 | 
						|
				return graph.connectionHandler.isConnectableCell(cell);
 | 
						|
			};
 | 
						|
			
 | 
						|
			// Adds a special tooltip for edges
 | 
						|
			graph.setTooltips(true);
 | 
						|
			
 | 
						|
			var getTooltipForCell = graph.getTooltipForCell;
 | 
						|
			graph.getTooltipForCell = function(cell)
 | 
						|
			{
 | 
						|
				var tip = '';
 | 
						|
				
 | 
						|
				if (cell != null)
 | 
						|
				{
 | 
						|
					var src = this.getModel().getTerminal(cell, true);
 | 
						|
					
 | 
						|
					if (src != null)
 | 
						|
					{
 | 
						|
						tip += this.getTooltipForCell(src) + ' ';
 | 
						|
					}
 | 
						|
					
 | 
						|
					var parent = this.getModel().getParent(cell);
 | 
						|
					
 | 
						|
					if (this.getModel().isVertex(parent))
 | 
						|
					{
 | 
						|
						tip += this.getTooltipForCell(parent) + '.';
 | 
						|
					}
 | 
						|
	
 | 
						|
					tip += getTooltipForCell.apply(this, arguments);
 | 
						|
					
 | 
						|
					var trg = this.getModel().getTerminal(cell, false);
 | 
						|
					
 | 
						|
					if (trg != null)
 | 
						|
					{
 | 
						|
						tip += ' ' + this.getTooltipForCell(trg);
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				return tip;
 | 
						|
			};
 | 
						|
 | 
						|
			// Switch for black background and bright styles
 | 
						|
			var invert = false;
 | 
						|
			
 | 
						|
			if (invert)
 | 
						|
			{
 | 
						|
				container.style.backgroundColor = 'black';
 | 
						|
				
 | 
						|
				// White in-place editor text color
 | 
						|
				mxCellEditorStartEditing = mxCellEditor.prototype.startEditing;
 | 
						|
				mxCellEditor.prototype.startEditing = function (cell, trigger)
 | 
						|
				{
 | 
						|
					mxCellEditorStartEditing.apply(this, arguments);
 | 
						|
					
 | 
						|
					if (this.textarea != null)
 | 
						|
					{
 | 
						|
						this.textarea.style.color = '#FFFFFF';					
 | 
						|
					}
 | 
						|
				};
 | 
						|
				
 | 
						|
				mxGraphHandler.prototype.previewColor = 'white';
 | 
						|
			}
 | 
						|
			
 | 
						|
			var labelBackground = (invert) ? '#000000' : '#FFFFFF';
 | 
						|
			var fontColor = (invert) ? '#FFFFFF' : '#000000';
 | 
						|
			var strokeColor = (invert) ? '#C0C0C0' : '#000000';
 | 
						|
			var fillColor = (invert) ? 'none' : '#FFFFFF';
 | 
						|
			
 | 
						|
			var style = graph.getStylesheet().getDefaultEdgeStyle();
 | 
						|
			delete style['endArrow'];
 | 
						|
			style['strokeColor'] = strokeColor;
 | 
						|
			style['labelBackgroundColor'] = labelBackground;
 | 
						|
			style['edgeStyle'] = 'wireEdgeStyle';
 | 
						|
			style['fontColor'] = fontColor;
 | 
						|
			style['fontSize'] = '9';
 | 
						|
			style['movable'] = '0';
 | 
						|
			style['strokeWidth'] = strokeWidth;
 | 
						|
			//style['rounded'] = '1';
 | 
						|
			
 | 
						|
			// Sets join node size
 | 
						|
			style['startSize'] = joinNodeSize;
 | 
						|
			style['endSize'] = joinNodeSize;
 | 
						|
			
 | 
						|
			style = graph.getStylesheet().getDefaultVertexStyle();
 | 
						|
			style['gradientDirection'] = 'south';
 | 
						|
			//style['gradientColor'] = '#909090';
 | 
						|
			style['strokeColor'] = strokeColor;
 | 
						|
			//style['fillColor'] = '#e0e0e0';
 | 
						|
			style['fillColor'] = 'none';
 | 
						|
			style['fontColor'] = fontColor;
 | 
						|
			style['fontStyle'] = '1';
 | 
						|
			style['fontSize'] = '12';
 | 
						|
			style['resizable'] = '0';
 | 
						|
			style['rounded'] = '1';
 | 
						|
			style['strokeWidth'] = strokeWidth;
 | 
						|
 | 
						|
			var parent = graph.getDefaultParent();
 | 
						|
			
 | 
						|
			graph.getModel().beginUpdate();
 | 
						|
			try
 | 
						|
			{
 | 
						|
				var v1 = graph.insertVertex(parent, null, 'J1', 80, 40, 40, 80,
 | 
						|
						'verticalLabelPosition=top;verticalAlign=bottom;shadow=1;fillColor=' + fillColor);
 | 
						|
				v1.setConnectable(false);
 | 
						|
 | 
						|
				var v11 = graph.insertVertex(v1, null, '1', 0, 0, 10, 16,
 | 
						|
						'shape=line;align=left;verticalAlign=middle;fontSize=10;routingCenterX=-0.5;'+
 | 
						|
						'spacingLeft=12;fontColor=' + fontColor + ';strokeColor=' + strokeColor);
 | 
						|
				v11.geometry.relative = true;
 | 
						|
				v11.geometry.offset = new mxPoint(-v11.geometry.width, 2);
 | 
						|
				var v12 = v11.clone();
 | 
						|
				v12.value = '2';
 | 
						|
				v12.geometry.offset = new mxPoint(-v11.geometry.width, 22);
 | 
						|
				v1.insert(v12);
 | 
						|
				var v13 = v11.clone();
 | 
						|
				v13.value = '3';
 | 
						|
				v13.geometry.offset = new mxPoint(-v11.geometry.width, 42);
 | 
						|
				v1.insert(v13);
 | 
						|
				var v14 = v11.clone();
 | 
						|
				v14.value = '4';
 | 
						|
				v14.geometry.offset = new mxPoint(-v11.geometry.width, 62);
 | 
						|
				v1.insert(v14);
 | 
						|
 | 
						|
				var v15 = v11.clone();
 | 
						|
				v15.value = '5';
 | 
						|
				v15.geometry.x = 1;
 | 
						|
				v15.style =	'shape=line;align=right;verticalAlign=middle;fontSize=10;routingCenterX=0.5;'+
 | 
						|
					'spacingRight=12;fontColor=' + fontColor + ';strokeColor=' + strokeColor;
 | 
						|
				v15.geometry.offset = new mxPoint(0, 2);
 | 
						|
				v1.insert(v15);
 | 
						|
				var v16 = v15.clone();
 | 
						|
				v16.value = '6';
 | 
						|
				v16.geometry.offset = new mxPoint(0, 22);
 | 
						|
				v1.insert(v16);
 | 
						|
				var v17 = v15.clone();
 | 
						|
				v17.value = '7';
 | 
						|
				v17.geometry.offset = new mxPoint(0, 42);
 | 
						|
				v1.insert(v17);
 | 
						|
				var v18 = v15.clone();
 | 
						|
				v18.value = '8';
 | 
						|
				v18.geometry.offset = new mxPoint(0, 62);
 | 
						|
				v1.insert(v18);
 | 
						|
				
 | 
						|
				var v19 = v15.clone();
 | 
						|
				v19.value = 'clk';
 | 
						|
				v19.geometry.x = 0.5;
 | 
						|
				v19.geometry.y = 1;
 | 
						|
				v19.geometry.width = 10;
 | 
						|
				v19.geometry.height = 4;
 | 
						|
				// NOTE: portConstraint is defined for east direction, so must be inverted here
 | 
						|
				v19.style = 'shape=triangle;direction=north;spacingBottom=12;align=center;portConstraint=horizontal;'+
 | 
						|
					'fontSize=8;strokeColor=' + strokeColor + ';routingCenterY=0.5;';
 | 
						|
				v19.geometry.offset = new mxPoint(-4, -4);
 | 
						|
				v1.insert(v19);
 | 
						|
				
 | 
						|
				var v2 = graph.insertVertex(parent, null, 'R1', 220, 220, 80, 20,
 | 
						|
					'shape=resistor;verticalLabelPosition=top;verticalAlign=bottom;');
 | 
						|
				
 | 
						|
				// Uses implementation of connection points via constraints (see above)
 | 
						|
				//v2.setConnectable(false);
 | 
						|
 | 
						|
 				/*var v21 = graph.insertVertex(v2, null, 'A', 0, 0.5, 10, 1,
 | 
						|
 					'shape=none;spacingBottom=11;spacingLeft=1;align=left;fontSize=8;'+
 | 
						|
 					'fontColor=#4c4c4c;strokeColor=#909090;');
 | 
						|
 				v21.geometry.relative = true;
 | 
						|
 				v21.geometry.offset = new mxPoint(0, -1);
 | 
						|
				
 | 
						|
 				var v22 = graph.insertVertex(v2, null, 'B', 1, 0.5, 10, 1,
 | 
						|
 					'spacingBottom=11;spacingLeft=1;align=left;fontSize=8;'+
 | 
						|
 					'fontColor=#4c4c4c;strokeColor=#909090;');
 | 
						|
 				v22.geometry.relative = true;
 | 
						|
 				v22.geometry.offset = new mxPoint(-10, -1);*/
 | 
						|
 | 
						|
				var v3 = graph.addCell(graph.getModel().cloneCell(v1));
 | 
						|
				v3.value = 'J3';
 | 
						|
				v3.geometry.x = 420;
 | 
						|
				v3.geometry.y = 340;
 | 
						|
				
 | 
						|
				// Connection constraints implemented in edges, alternatively this
 | 
						|
				// can be implemented using references, see: portrefs.html
 | 
						|
				var e1 = graph.insertEdge(parent, null, 'e1', v1.getChildAt(7), v2,
 | 
						|
					'entryX=0;entryY=0.5;entryPerimeter=0;');
 | 
						|
				e1.geometry.points = [new mxPoint(180, 110)];
 | 
						|
				
 | 
						|
				var e2 = graph.insertEdge(parent, null, 'e2', v1.getChildAt(4), v2,
 | 
						|
					'entryX=1;entryY=0.5;entryPerimeter=0;');
 | 
						|
				e2.geometry.points = [new mxPoint(320, 50), new mxPoint(320, 230)];
 | 
						|
				
 | 
						|
				var e3 = graph.insertEdge(parent, null, 'crossover', e1, e2);
 | 
						|
				e3.geometry.setTerminalPoint(new mxPoint(180, 140), true);
 | 
						|
				e3.geometry.setTerminalPoint(new mxPoint(320, 140), false);
 | 
						|
				
 | 
						|
// 				var e1 = graph.insertEdge(parent, null, 'e1', v1.getChildAt(7), v2.getChildAt(0));
 | 
						|
// 				e1.geometry.points = [new mxPoint(180, 140)];
 | 
						|
				
 | 
						|
// 				var e2 = graph.insertEdge(parent, null, '', v1.getChildAt(4), v2.getChildAt(1));
 | 
						|
// 				e2.geometry.points = [new mxPoint(320, 80)];
 | 
						|
				
 | 
						|
// 				var e3 = graph.insertEdge(parent, null, 'crossover', e1, e2);
 | 
						|
// 				e3.geometry.setTerminalPoint(new mxPoint(180, 160), true);
 | 
						|
// 				e3.geometry.setTerminalPoint(new mxPoint(320, 160), false);
 | 
						|
				
 | 
						|
				var e4 = graph.insertEdge(parent, null, 'e4', v2, v3.getChildAt(0),
 | 
						|
					'exitX=1;exitY=0.5;entryPerimeter=0;');
 | 
						|
				e4.geometry.points = [new mxPoint(380, 230)];
 | 
						|
				
 | 
						|
				var e5 = graph.insertEdge(parent, null, 'e5', v3.getChildAt(5), v1.getChildAt(0));
 | 
						|
				e5.geometry.points = [new mxPoint(500, 310), new mxPoint(500, 20), new mxPoint(50, 20)];
 | 
						|
				
 | 
						|
				var e6 = graph.insertEdge(parent, null, '');
 | 
						|
				e6.geometry.setTerminalPoint(new mxPoint(100, 500), true);
 | 
						|
				e6.geometry.setTerminalPoint(new mxPoint(600, 500), false);
 | 
						|
				
 | 
						|
				var e7 = graph.insertEdge(parent, null, 'e7', v3.getChildAt(7), e6);
 | 
						|
				e7.geometry.setTerminalPoint(new mxPoint(500, 500), false);
 | 
						|
				e7.geometry.points = [new mxPoint(500, 350)];
 | 
						|
			}
 | 
						|
			finally
 | 
						|
			{
 | 
						|
				graph.getModel().endUpdate();
 | 
						|
			}
 | 
						|
			
 | 
						|
			document.body.appendChild(mxUtils.button('Zoom In', function()
 | 
						|
			{
 | 
						|
				graph.zoomIn();
 | 
						|
			}));
 | 
						|
			
 | 
						|
			document.body.appendChild(mxUtils.button('Zoom Out', function()
 | 
						|
			{
 | 
						|
				graph.zoomOut();
 | 
						|
			}));
 | 
						|
			
 | 
						|
			// Undo/redo
 | 
						|
			var undoManager = new mxUndoManager();
 | 
						|
			var listener = function(sender, evt)
 | 
						|
			{
 | 
						|
				undoManager.undoableEditHappened(evt.getProperty('edit'));
 | 
						|
			};
 | 
						|
			graph.getModel().addListener(mxEvent.UNDO, listener);
 | 
						|
			graph.getView().addListener(mxEvent.UNDO, listener);
 | 
						|
			
 | 
						|
			document.body.appendChild(mxUtils.button('Undo', function()
 | 
						|
			{
 | 
						|
				undoManager.undo();
 | 
						|
			}));
 | 
						|
			
 | 
						|
			document.body.appendChild(mxUtils.button('Redo', function()
 | 
						|
			{
 | 
						|
				undoManager.redo();
 | 
						|
			}));
 | 
						|
 | 
						|
			// Shows XML for debugging the actual model
 | 
						|
			document.body.appendChild(mxUtils.button('Delete', function()
 | 
						|
			{
 | 
						|
				graph.removeCells();
 | 
						|
			}));
 | 
						|
			
 | 
						|
			// Wire-mode
 | 
						|
			var checkbox = document.createElement('input');
 | 
						|
			checkbox.setAttribute('type', 'checkbox');
 | 
						|
			
 | 
						|
			document.body.appendChild(checkbox);
 | 
						|
			mxUtils.write(document.body, 'Wire Mode');
 | 
						|
			
 | 
						|
			// Starts connections on the background in wire-mode
 | 
						|
			var connectionHandlerIsStartEvent = graph.connectionHandler.isStartEvent;
 | 
						|
			graph.connectionHandler.isStartEvent = function(me)
 | 
						|
			{
 | 
						|
				return checkbox.checked || connectionHandlerIsStartEvent.apply(this, arguments);
 | 
						|
			};
 | 
						|
			
 | 
						|
			// Avoids any connections for gestures within tolerance except when in wire-mode
 | 
						|
			// or when over a port
 | 
						|
			var connectionHandlerMouseUp = graph.connectionHandler.mouseUp;
 | 
						|
			graph.connectionHandler.mouseUp = function(sender, me)
 | 
						|
			{
 | 
						|
				if (this.first != null && this.previous != null)
 | 
						|
				{
 | 
						|
					var point = mxUtils.convertPoint(this.graph.container, me.getX(), me.getY());
 | 
						|
					var dx = Math.abs(point.x - this.first.x);
 | 
						|
					var dy = Math.abs(point.y - this.first.y);
 | 
						|
 | 
						|
					if (dx < this.graph.tolerance && dy < this.graph.tolerance)
 | 
						|
					{
 | 
						|
						// Selects edges in non-wire mode for single clicks, but starts
 | 
						|
						// connecting for non-edges regardless of wire-mode
 | 
						|
						if (!checkbox.checked && this.graph.getModel().isEdge(this.previous.cell))
 | 
						|
						{
 | 
						|
							this.reset();
 | 
						|
						}
 | 
						|
						
 | 
						|
						return;
 | 
						|
					}
 | 
						|
				}
 | 
						|
				
 | 
						|
				connectionHandlerMouseUp.apply(this, arguments);
 | 
						|
			};
 | 
						|
			
 | 
						|
			// Grid
 | 
						|
			var checkbox2 = document.createElement('input');
 | 
						|
			checkbox2.setAttribute('type', 'checkbox');
 | 
						|
			checkbox2.setAttribute('checked', 'true');
 | 
						|
			
 | 
						|
			document.body.appendChild(checkbox2);
 | 
						|
			mxUtils.write(document.body, 'Grid');
 | 
						|
			
 | 
						|
			mxEvent.addListener(checkbox2, 'click', function(evt)
 | 
						|
			{
 | 
						|
				if (checkbox2.checked)
 | 
						|
				{
 | 
						|
					container.style.background = 'url(\'images/wires-grid.gif\')';
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					container.style.background = '';
 | 
						|
				}
 | 
						|
				
 | 
						|
				container.style.backgroundColor = (invert) ? 'black' : 'white';
 | 
						|
			});
 | 
						|
			
 | 
						|
			mxEvent.disableContextMenu(container);
 | 
						|
		};
 | 
						|
	</script>
 | 
						|
<!--  
 | 
						|
		Updates connection points before the routing is called.
 | 
						|
-->
 | 
						|
	<script type="text/javascript">
 | 
						|
		// Computes the position of edge to edge connection points.
 | 
						|
		mxGraphView.prototype.updateFixedTerminalPoint = function(edge, terminal, source, constraint)
 | 
						|
		{
 | 
						|
			var pt = null;
 | 
						|
			
 | 
						|
			if (constraint != null)
 | 
						|
			{
 | 
						|
				pt = this.graph.getConnectionPoint(terminal, constraint);
 | 
						|
			}
 | 
						|
 | 
						|
			if (source)
 | 
						|
			{
 | 
						|
				edge.sourceSegment = null;
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				edge.targetSegment = null;
 | 
						|
			}
 | 
						|
			
 | 
						|
			if (pt == null)
 | 
						|
			{
 | 
						|
				var s = this.scale;
 | 
						|
				var tr = this.translate;
 | 
						|
				var orig = edge.origin;
 | 
						|
				var geo = this.graph.getCellGeometry(edge.cell);
 | 
						|
				pt = geo.getTerminalPoint(source);
 | 
						|
 | 
						|
				// Computes edge-to-edge connection point
 | 
						|
				if (pt != null)
 | 
						|
				{
 | 
						|
					pt = new mxPoint(s * (tr.x + pt.x + orig.x),
 | 
						|
									 s * (tr.y + pt.y + orig.y));
 | 
						|
					
 | 
						|
					// Finds nearest segment on edge and computes intersection
 | 
						|
					if (terminal != null && terminal.absolutePoints != null)
 | 
						|
					{
 | 
						|
						var seg = mxUtils.findNearestSegment(terminal, pt.x, pt.y);
 | 
						|
 | 
						|
						// Finds orientation of the segment
 | 
						|
						var p0 = terminal.absolutePoints[seg];
 | 
						|
						var pe = terminal.absolutePoints[seg + 1];
 | 
						|
						var horizontal = (p0.x - pe.x == 0);
 | 
						|
						
 | 
						|
						// Stores the segment in the edge state
 | 
						|
						var key = (source) ? 'sourceConstraint' : 'targetConstraint';
 | 
						|
						var value = (horizontal) ? 'horizontal' : 'vertical';
 | 
						|
						edge.style[key] = value;
 | 
						|
						
 | 
						|
						// Keeps the coordinate within the segment bounds
 | 
						|
						if (horizontal)
 | 
						|
						{
 | 
						|
							pt.x = p0.x;
 | 
						|
							pt.y = Math.min(pt.y, Math.max(p0.y, pe.y));
 | 
						|
							pt.y = Math.max(pt.y, Math.min(p0.y, pe.y));
 | 
						|
						}
 | 
						|
						else
 | 
						|
						{
 | 
						|
							pt.y = p0.y;
 | 
						|
							pt.x = Math.min(pt.x, Math.max(p0.x, pe.x));
 | 
						|
							pt.x = Math.max(pt.x, Math.min(p0.x, pe.x));
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
				// Computes constraint connection points on vertices and ports
 | 
						|
				else if (terminal != null && terminal.cell.geometry.relative)
 | 
						|
				{
 | 
						|
					pt = new mxPoint(this.getRoutingCenterX(terminal),
 | 
						|
						this.getRoutingCenterY(terminal));
 | 
						|
				}
 | 
						|
 | 
						|
				// Snaps point to grid
 | 
						|
				/*if (pt != null)
 | 
						|
				{
 | 
						|
					var tr = this.graph.view.translate;
 | 
						|
					var s = this.graph.view.scale;
 | 
						|
					
 | 
						|
					pt.x = (this.graph.snap(pt.x / s - tr.x) + tr.x) * s;
 | 
						|
					pt.y = (this.graph.snap(pt.y / s - tr.y) + tr.y) * s;
 | 
						|
				}*/
 | 
						|
			}
 | 
						|
 | 
						|
			edge.setAbsoluteTerminalPoint(pt, source);
 | 
						|
		};
 | 
						|
	</script>
 | 
						|
<!--  
 | 
						|
	Overrides methods to preview and create new edges.
 | 
						|
-->
 | 
						|
	<script type="text/javascript">
 | 
						|
		// Sets source terminal point for edge-to-edge connections.
 | 
						|
		mxConnectionHandler.prototype.createEdgeState = function(me)
 | 
						|
		{
 | 
						|
			var edge = this.graph.createEdge();
 | 
						|
			
 | 
						|
			if (this.sourceConstraint != null && this.previous != null)
 | 
						|
			{
 | 
						|
				edge.style = mxConstants.STYLE_EXIT_X+'='+this.sourceConstraint.point.x+';'+
 | 
						|
					mxConstants.STYLE_EXIT_Y+'='+this.sourceConstraint.point.y+';';
 | 
						|
			}
 | 
						|
			else if (this.graph.model.isEdge(me.getCell()))
 | 
						|
			{
 | 
						|
				var scale = this.graph.view.scale;
 | 
						|
				var tr = this.graph.view.translate;
 | 
						|
				var pt = new mxPoint(this.graph.snap(me.getGraphX() / scale) - tr.x,
 | 
						|
						this.graph.snap(me.getGraphY() / scale) - tr.y);
 | 
						|
				edge.geometry.setTerminalPoint(pt, true);
 | 
						|
			}
 | 
						|
			
 | 
						|
			return this.graph.view.createState(edge);
 | 
						|
		};
 | 
						|
		
 | 
						|
		// Uses right mouse button to create edges on background (see also: lines 67 ff)
 | 
						|
		mxConnectionHandler.prototype.isStopEvent = function(me)
 | 
						|
		{
 | 
						|
			return me.getState() != null || mxEvent.isRightMouseButton(me.getEvent());
 | 
						|
		};
 | 
						|
		
 | 
						|
		// Updates target terminal point for edge-to-edge connections.
 | 
						|
		mxConnectionHandlerUpdateCurrentState = mxConnectionHandler.prototype.updateCurrentState;
 | 
						|
		mxConnectionHandler.prototype.updateCurrentState = function(me)
 | 
						|
		{
 | 
						|
			mxConnectionHandlerUpdateCurrentState.apply(this, arguments);
 | 
						|
 | 
						|
			if (this.edgeState != null)
 | 
						|
			{
 | 
						|
				this.edgeState.cell.geometry.setTerminalPoint(null, false);
 | 
						|
			
 | 
						|
				if (this.shape != null && this.currentState != null &&
 | 
						|
					this.currentState.view.graph.model.isEdge(this.currentState.cell))
 | 
						|
				{
 | 
						|
					var scale = this.graph.view.scale;
 | 
						|
					var tr = this.graph.view.translate;
 | 
						|
					var pt = new mxPoint(this.graph.snap(me.getGraphX() / scale) - tr.x,
 | 
						|
							this.graph.snap(me.getGraphY() / scale) - tr.y);
 | 
						|
					this.edgeState.cell.geometry.setTerminalPoint(pt, false);
 | 
						|
				}
 | 
						|
			}
 | 
						|
		};
 | 
						|
 | 
						|
		// Updates the terminal and control points in the cloned preview.
 | 
						|
		mxEdgeSegmentHandler.prototype.clonePreviewState = function(point, terminal)
 | 
						|
		{
 | 
						|
			var clone = mxEdgeHandler.prototype.clonePreviewState.apply(this, arguments);
 | 
						|
			clone.cell = clone.cell.clone();
 | 
						|
			
 | 
						|
			if (this.isSource || this.isTarget)
 | 
						|
			{
 | 
						|
				clone.cell.geometry = clone.cell.geometry.clone();
 | 
						|
				
 | 
						|
				// Sets the terminal point of an edge if we're moving one of the endpoints
 | 
						|
				if (this.graph.getModel().isEdge(clone.cell))
 | 
						|
				{
 | 
						|
					// TODO: Only set this if the target or source terminal is an edge
 | 
						|
					clone.cell.geometry.setTerminalPoint(point, this.isSource);
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					clone.cell.geometry.setTerminalPoint(null, this.isSource);				
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			return clone;
 | 
						|
		};
 | 
						|
		
 | 
						|
		var mxEdgeHandlerConnect = mxEdgeHandler.prototype.connect;
 | 
						|
		mxEdgeHandler.prototype.connect = function(edge, terminal, isSource, isClone, me)
 | 
						|
		{
 | 
						|
			var result = null;
 | 
						|
			var model = this.graph.getModel();
 | 
						|
			var parent = model.getParent(edge);
 | 
						|
			
 | 
						|
			model.beginUpdate();
 | 
						|
			try
 | 
						|
			{
 | 
						|
				result = mxEdgeHandlerConnect.apply(this, arguments);
 | 
						|
				var geo = model.getGeometry(result);
 | 
						|
				
 | 
						|
				if (geo != null)
 | 
						|
				{
 | 
						|
					geo = geo.clone();
 | 
						|
					var pt = null;
 | 
						|
					
 | 
						|
					if (model.isEdge(terminal))
 | 
						|
					{
 | 
						|
						pt = this.abspoints[(this.isSource) ? 0 : this.abspoints.length - 1];
 | 
						|
						pt.x = pt.x / this.graph.view.scale - this.graph.view.translate.x;
 | 
						|
						pt.y = pt.y / this.graph.view.scale - this.graph.view.translate.y;
 | 
						|
			
 | 
						|
						var pstate = this.graph.getView().getState(
 | 
						|
								this.graph.getModel().getParent(edge));
 | 
						|
								
 | 
						|
						if (pstate != null)
 | 
						|
						{
 | 
						|
							pt.x -= pstate.origin.x;
 | 
						|
							pt.y -= pstate.origin.y;
 | 
						|
						}
 | 
						|
						
 | 
						|
						pt.x -= this.graph.panDx / this.graph.view.scale;
 | 
						|
						pt.y -= this.graph.panDy / this.graph.view.scale;
 | 
						|
					}
 | 
						|
				
 | 
						|
					geo.setTerminalPoint(pt, isSource);
 | 
						|
					model.setGeometry(edge, geo);
 | 
						|
				}
 | 
						|
			}
 | 
						|
			finally
 | 
						|
			{
 | 
						|
				model.endUpdate();
 | 
						|
			}
 | 
						|
			
 | 
						|
			return result;
 | 
						|
		};
 | 
						|
	</script>
 | 
						|
<!--  
 | 
						|
	Adds in-place highlighting for complete cell area (no hotspot).
 | 
						|
-->
 | 
						|
	<script type="text/javascript">
 | 
						|
		mxConnectionHandlerCreateMarker = mxConnectionHandler.prototype.createMarker;
 | 
						|
		mxConnectionHandler.prototype.createMarker = function()
 | 
						|
		{
 | 
						|
			var marker = mxConnectionHandlerCreateMarker.apply(this, arguments);
 | 
						|
			
 | 
						|
			// Uses complete area of cell for new connections (no hotspot)
 | 
						|
			marker.intersects = function(state, evt)
 | 
						|
			{
 | 
						|
				return true;
 | 
						|
			};
 | 
						|
			
 | 
						|
			// Adds in-place highlighting
 | 
						|
			mxCellHighlightHighlight = mxCellHighlight.prototype.highlight;
 | 
						|
			marker.highlight.highlight = function(state)
 | 
						|
			{
 | 
						|
				if (this.state != state)
 | 
						|
				{
 | 
						|
					if (this.state != null)
 | 
						|
					{
 | 
						|
						this.state.style = this.lastStyle;
 | 
						|
						
 | 
						|
						// Workaround for shape using current stroke width if no strokewidth defined
 | 
						|
						this.state.style['strokeWidth'] = this.state.style['strokeWidth'] || '1';
 | 
						|
						this.state.style['strokeColor'] = this.state.style['strokeColor'] || 'none';
 | 
						|
						
 | 
						|
						if (this.state.shape != null)
 | 
						|
						{
 | 
						|
							this.state.view.graph.cellRenderer.configureShape(this.state);
 | 
						|
							this.state.shape.redraw();
 | 
						|
						}
 | 
						|
					}
 | 
						|
					
 | 
						|
					if (state != null)
 | 
						|
					{
 | 
						|
						this.lastStyle = state.style;
 | 
						|
						state.style = mxUtils.clone(state.style);
 | 
						|
						state.style['strokeColor'] = '#00ff00';
 | 
						|
						state.style['strokeWidth'] = '3';
 | 
						|
						
 | 
						|
						if (state.shape != null)
 | 
						|
						{
 | 
						|
							state.view.graph.cellRenderer.configureShape(state);
 | 
						|
							state.shape.redraw();
 | 
						|
						}
 | 
						|
					}
 | 
						|
					
 | 
						|
					this.state = state;
 | 
						|
				}
 | 
						|
			};
 | 
						|
			
 | 
						|
			return marker;
 | 
						|
		};
 | 
						|
		
 | 
						|
		mxEdgeHandlerCreateMarker = mxEdgeHandler.prototype.createMarker;
 | 
						|
		mxEdgeHandler.prototype.createMarker = function()
 | 
						|
		{
 | 
						|
			var marker = mxEdgeHandlerCreateMarker.apply(this, arguments);
 | 
						|
			
 | 
						|
			// Adds in-place highlighting when reconnecting existing edges
 | 
						|
			marker.highlight.highlight = this.graph.connectionHandler.marker.highlight.highlight;
 | 
						|
			
 | 
						|
			return marker;
 | 
						|
		}
 | 
						|
	</script>
 | 
						|
<!--  
 | 
						|
	Adds oval markers for edge-to-edge connections.
 | 
						|
-->
 | 
						|
	<script type="text/javascript">
 | 
						|
		mxGraphGetCellStyle = mxGraph.prototype.getCellStyle;
 | 
						|
		mxGraph.prototype.getCellStyle = function(cell)
 | 
						|
		{
 | 
						|
			var style = mxGraphGetCellStyle.apply(this, arguments);
 | 
						|
			
 | 
						|
			if (style != null && this.model.isEdge(cell))
 | 
						|
			{
 | 
						|
				style = mxUtils.clone(style);
 | 
						|
				
 | 
						|
				if (this.model.isEdge(this.model.getTerminal(cell, true)))
 | 
						|
				{
 | 
						|
					style['startArrow'] = 'oval';
 | 
						|
				}
 | 
						|
		
 | 
						|
				if (this.model.isEdge(this.model.getTerminal(cell, false)))
 | 
						|
				{
 | 
						|
					style['endArrow'] = 'oval';
 | 
						|
				}
 | 
						|
			}
 | 
						|
			
 | 
						|
			return style;
 | 
						|
		};
 | 
						|
	</script>
 | 
						|
<!--  
 | 
						|
	Imlements a custom resistor shape. Direction currently ignored here.
 | 
						|
-->
 | 
						|
	<script type="text/javascript">
 | 
						|
		function ResistorShape() { };
 | 
						|
		ResistorShape.prototype = new mxCylinder();
 | 
						|
		ResistorShape.prototype.constructor = ResistorShape;
 | 
						|
 | 
						|
		ResistorShape.prototype.redrawPath = function(path, x, y, w, h, isForeground)
 | 
						|
		{
 | 
						|
			var dx = w / 16;
 | 
						|
 | 
						|
			if (isForeground)
 | 
						|
			{
 | 
						|
				path.moveTo(0, h / 2);
 | 
						|
				path.lineTo(2 * dx, h / 2);
 | 
						|
				path.lineTo(3 * dx, 0);
 | 
						|
				path.lineTo(5 * dx, h);
 | 
						|
				path.lineTo(7 * dx, 0);
 | 
						|
				path.lineTo(9 * dx, h);
 | 
						|
				path.lineTo(11 * dx, 0);
 | 
						|
				path.lineTo(13 * dx, h);
 | 
						|
				path.lineTo(14 * dx, h / 2);
 | 
						|
				path.lineTo(16 * dx, h / 2);
 | 
						|
 | 
						|
				path.end();
 | 
						|
			}
 | 
						|
		};
 | 
						|
 | 
						|
		mxCellRenderer.registerShape('resistor', ResistorShape);
 | 
						|
	</script>
 | 
						|
<!--  
 | 
						|
	Imlements a custom resistor shape. Direction currently ignored here.
 | 
						|
-->
 | 
						|
	<script type="text/javascript">
 | 
						|
	mxEdgeStyle.WireConnector = function(state, source, target, hints, result)
 | 
						|
	{
 | 
						|
		// Creates array of all way- and terminalpoints
 | 
						|
		var pts = state.absolutePoints;
 | 
						|
		var horizontal = true;
 | 
						|
		var hint = null;
 | 
						|
		
 | 
						|
		// Gets the initial connection from the source terminal or edge
 | 
						|
		if (source != null && state.view.graph.model.isEdge(source.cell))
 | 
						|
		{
 | 
						|
			horizontal = state.style['sourceConstraint'] == 'horizontal';
 | 
						|
		}
 | 
						|
		else if (source != null)
 | 
						|
		{
 | 
						|
			horizontal = source.style['portConstraint'] != 'vertical';
 | 
						|
			
 | 
						|
			// Checks the direction of the shape and rotates
 | 
						|
			var direction = source.style[mxConstants.STYLE_DIRECTION];
 | 
						|
			
 | 
						|
			if (direction == 'north' || direction == 'south')
 | 
						|
			{
 | 
						|
				horizontal = !horizontal;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		
 | 
						|
		// Adds the first point
 | 
						|
		// TODO: Should move along connected segment
 | 
						|
		var pt = pts[0];
 | 
						|
		
 | 
						|
		if (pt == null && source != null)
 | 
						|
		{
 | 
						|
			pt = new mxPoint(state.view.getRoutingCenterX(source), state.view.getRoutingCenterY(source));
 | 
						|
		}
 | 
						|
		else if (pt != null)
 | 
						|
		{
 | 
						|
			pt = pt.clone();
 | 
						|
		}
 | 
						|
		
 | 
						|
		var first = pt;
 | 
						|
 | 
						|
		// Adds the waypoints
 | 
						|
		if (hints != null && hints.length > 0)
 | 
						|
		{
 | 
						|
			// FIXME: First segment not movable
 | 
						|
			/*hint = state.view.transformControlPoint(state, hints[0]);
 | 
						|
			mxLog.show();
 | 
						|
			mxLog.debug(hints.length,'hints0.y='+hint.y, pt.y)
 | 
						|
			
 | 
						|
			if (horizontal && Math.floor(hint.y) != Math.floor(pt.y))
 | 
						|
			{
 | 
						|
				mxLog.show();
 | 
						|
				mxLog.debug('add waypoint');
 | 
						|
 | 
						|
				pt = new mxPoint(pt.x, hint.y);
 | 
						|
				result.push(pt);
 | 
						|
				pt = pt.clone();
 | 
						|
				//horizontal = !horizontal;
 | 
						|
			}*/
 | 
						|
			
 | 
						|
			for (var i = 0; i < hints.length; i++)
 | 
						|
			{
 | 
						|
				horizontal = !horizontal;
 | 
						|
				hint = state.view.transformControlPoint(state, hints[i]);
 | 
						|
 | 
						|
				if (horizontal)
 | 
						|
				{
 | 
						|
					if (pt.y != hint.y)
 | 
						|
					{
 | 
						|
						pt.y = hint.y;
 | 
						|
						result.push(pt.clone());
 | 
						|
					}
 | 
						|
				}
 | 
						|
				else if (pt.x != hint.x)
 | 
						|
				{
 | 
						|
					pt.x = hint.x;
 | 
						|
					result.push(pt.clone());
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			hint = pt;
 | 
						|
		}
 | 
						|
 | 
						|
		// Adds the last point
 | 
						|
		pt = pts[pts.length - 1];
 | 
						|
 | 
						|
		// TODO: Should move along connected segment
 | 
						|
		if (pt == null && target != null)
 | 
						|
		{
 | 
						|
			pt = new mxPoint(state.view.getRoutingCenterX(target), state.view.getRoutingCenterY(target));
 | 
						|
		}
 | 
						|
 | 
						|
		if (horizontal)
 | 
						|
		{
 | 
						|
			if (pt.y != hint.y && first.x != pt.x)
 | 
						|
			{
 | 
						|
				result.push(new mxPoint(pt.x, hint.y));
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else if (pt.x != hint.x && first.y != pt.y)
 | 
						|
		{
 | 
						|
			result.push(new mxPoint(hint.x, pt.y));
 | 
						|
		}
 | 
						|
	};
 | 
						|
	
 | 
						|
	mxStyleRegistry.putValue('wireEdgeStyle', mxEdgeStyle.WireConnector);
 | 
						|
	
 | 
						|
	// This connector needs an mxEdgeSegmentHandler
 | 
						|
	mxGraphCreateHandler = mxGraph.prototype.createHandler;
 | 
						|
	mxGraph.prototype.createHandler = function(state)
 | 
						|
	{
 | 
						|
		var result = null;
 | 
						|
		
 | 
						|
		if (state != null)
 | 
						|
		{
 | 
						|
			if (this.model.isEdge(state.cell))
 | 
						|
			{
 | 
						|
				var style = this.view.getEdgeStyle(state);
 | 
						|
				
 | 
						|
				if (style == mxEdgeStyle.WireConnector)
 | 
						|
				{
 | 
						|
					return new mxEdgeSegmentHandler(state);
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		
 | 
						|
		return mxGraphCreateHandler.apply(this, arguments);
 | 
						|
	};
 | 
						|
	</script>
 | 
						|
</head>
 | 
						|
<body onload="main(document.getElementById('graphContainer'))">
 | 
						|
	<div id="graphContainer"
 | 
						|
		style="overflow:auto;position:relative;width:800px;height:600px;border:1px solid gray;background:url('images/wires-grid.gif');background-position:-1px 0px;cursor:crosshair;">
 | 
						|
	</div>
 | 
						|
</body>
 | 
						|
</html>
 |