1/**
2 * Copyright (c) 2019-2020, JGraph Ltd
3 */
4/**
5 * Class: mxOrgChartLayout
6 *
7 * Extends <mxGraphLayout> to implement organization chart layout algorithm.
8 * The vertices need to be connected for this layout to work, vertices
9 * with no connections are ignored.
10 *
11 * Example:
12 *
13 * (code)
14 * var layout = new mxOrgChartLayout(graph);
15 * layout.execute(graph.getDefaultParent());
16 * (end)
17 *
18 */
19function mxOrgChartLayout(graph, branchOptimizer, parentChildSpacing, siblingSpacing)
20{
21	mxGraphLayout.call(this, graph);
22	this.correctY = false;
23
24	switch(parseInt(branchOptimizer))
25	{
26		case 0:
27			this.branchOptimizer = mxOrgChartLayout.prototype.BRANCH_OPT_LINEAR;
28			this.correctY = true;
29			break;
30		case 1:
31			this.branchOptimizer = mxOrgChartLayout.prototype.BRANCH_OPT_HANGER2;
32			this.correctY = true;
33			break;
34		case 3:
35			this.branchOptimizer = mxOrgChartLayout.prototype.BRANCH_OPT_FISHBONE1;
36			break;
37		case 4:
38			this.branchOptimizer = mxOrgChartLayout.prototype.BRANCH_OPT_FISHBONE2;
39			break;
40		case 5:
41			this.branchOptimizer = mxOrgChartLayout.prototype.BRANCH_OPT_1COLUMN_L;
42			break;
43		case 6:
44			this.branchOptimizer = mxOrgChartLayout.prototype.BRANCH_OPT_1COLUMN_R;
45			break;
46		case 7:
47			this.branchOptimizer = mxOrgChartLayout.prototype.BRANCH_OPT_SMART;
48			break;
49		default: //and case 2
50			this.branchOptimizer = mxOrgChartLayout.prototype.BRANCH_OPT_HANGER4;
51			this.correctY = true;
52	}
53
54	this.parentChildSpacing = parentChildSpacing > 0 ? parentChildSpacing : 20;
55	this.siblingSpacing = siblingSpacing > 0 ? siblingSpacing : 20;
56};
57
58/**
59 * Extends mxGraphLayout.
60 */
61mxOrgChartLayout.prototype = new mxGraphLayout();
62mxOrgChartLayout.prototype.constructor = mxOrgChartLayout;
63
64//Branch Optimizers
65mxOrgChartLayout.prototype.BRANCH_OPT_LINEAR = 'branchOptimizerAllLinear';
66mxOrgChartLayout.prototype.BRANCH_OPT_HANGER2 = 'branchOptimizerAllHanger2';
67mxOrgChartLayout.prototype.BRANCH_OPT_HANGER4 = 'branchOptimizerAllHanger4';
68mxOrgChartLayout.prototype.BRANCH_OPT_FISHBONE1 = 'branchOptimizerAllFishbone1';
69mxOrgChartLayout.prototype.BRANCH_OPT_FISHBONE2 = 'branchOptimizerAllFishbone2';
70mxOrgChartLayout.prototype.BRANCH_OPT_1COLUMN_L = 'branchOptimizerAllSingleColumnLeft';
71mxOrgChartLayout.prototype.BRANCH_OPT_1COLUMN_R = 'branchOptimizerAllSingleColumnRight';
72mxOrgChartLayout.prototype.BRANCH_OPT_SMART = 'branchOptimizerSmart';
73
74/**
75 * Function: execute
76 *
77 * Implements <mxGraphLayout.execute>. This operates on all children of the
78 * given parent.
79 */
80mxOrgChartLayout.prototype.execute = function(parent)
81{
82	this.graph.model.beginUpdate();
83	try
84	{
85		RPOrgChart.main(this.graph, parent, this.branchOptimizer, this.parentChildSpacing, this.siblingSpacing, this.correctY);
86	}
87	finally
88	{
89		this.graph.model.endUpdate();
90	}
91}
92
93Bridge.define('RPOrgChart',
94{
95    statics: {
96        config: {
97            init: function() {
98
99            }
100        },
101        main: function (graph, parent, branchOptimizer, parentChildSpacing, siblingSpacing, correctY) {
102            Bridge.Console.log = console.log;
103            Bridge.Console.error = console.error;
104            Bridge.Console.debug = console.debug;
105
106            RPOrgChart.graph = graph;
107            RPOrgChart.parent = parent;
108			RPOrgChart.dx = 0;
109			RPOrgChart.dy = 0;
110
111			if (parent.style == 'group' && parent.geometry)
112			{
113				RPOrgChart.dx = parent.geometry.x;
114				RPOrgChart.dy = parent.geometry.y;
115			}
116
117            RPOrgChart.branchOptimizer = branchOptimizer;
118			RPOrgChart.correctY = correctY;
119            RPOrgChart.parentChildSpacing = parseInt(parentChildSpacing);
120            RPOrgChart.siblingSpacing = parseInt(siblingSpacing);
121            RPOrgChart.buildChart(true);
122        },
123
124        diagram: {},
125        dataSource: {},
126
127        buildChart: function (initData) {
128            if (initData) {
129                RPOrgChart.initDiagram();
130            }
131            RPOrgChart.positionBoxes();
132        },
133
134        collapseAllBoxes: function(boxContainer, isCollapsed) {
135            var en = boxContainer.getBoxesById().getValues().getEnumerator();
136            while (en.moveNext()) {
137                var box = en.getCurrent();
138                if (!box.IsSpecial) {
139                    box.IsCollapsed = isCollapsed;
140                }
141            }
142        },
143
144        generateData: function ()
145        {
146        	var dataSource = new OrgChart.Test.TestDataSource();
147
148            var graph = RPOrgChart.graph;
149            var cells = graph.getChildVertices(RPOrgChart.parent);
150
151            for (var i = 0; i < cells.length; i++)
152        	{
153            	var cell = cells[i];
154
155            	if (cell.geometry != null && cell.vertex && cell.parent == RPOrgChart.parent) //Vertices and first level children only
156            	{
157            		// Find cell parent. If it has more than one parent, take first parent (should be an error?)
158            		var parentId = null;
159
160            		var incomingEdge = graph.getIncomingEdges(cell)[0];
161
162            		if (incomingEdge != null && incomingEdge.source != null)
163           			{
164            			parentId = incomingEdge.source.id;
165           			}
166
167            		var item = new OrgChart.Test.TestDataItem();
168            		item.Id = cell.id;
169            		item.ParentId = parentId;
170            		dataSource.Items.add(item.getId(), item);
171            	}
172           	}
173
174            return dataSource;
175        },
176
177        initDiagram: function () {
178            var dataSource = RPOrgChart.generateData();
179
180            RPOrgChart.dataSource = dataSource;
181
182            var boxContainer = new OrgChart.Layout.BoxContainer.$ctor1(dataSource);
183            RPOrgChart.diagram = new OrgChart.Layout.Diagram();
184
185            var diagram = RPOrgChart.diagram;
186            diagram.setBoxes(boxContainer);
187
188            var linearLayoutStrategy = new OrgChart.Layout.LinearLayoutStrategy();
189            linearLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.Center;
190            linearLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
191            linearLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
192            diagram.LayoutSettings.LayoutStrategies.add("linear", linearLayoutStrategy);
193
194            var multiLineHangerLayoutStrategy = new OrgChart.Layout.MultiLineHangerLayoutStrategy();
195            multiLineHangerLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.Center;
196            multiLineHangerLayoutStrategy.MaxSiblingsPerRow = 2;
197            multiLineHangerLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
198            multiLineHangerLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
199            diagram.LayoutSettings.LayoutStrategies.add("hanger2", multiLineHangerLayoutStrategy);
200
201            multiLineHangerLayoutStrategy = new OrgChart.Layout.MultiLineHangerLayoutStrategy();
202            multiLineHangerLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.Center;
203            multiLineHangerLayoutStrategy.MaxSiblingsPerRow = 4;
204            multiLineHangerLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
205            multiLineHangerLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
206            diagram.LayoutSettings.LayoutStrategies.add("hanger4", multiLineHangerLayoutStrategy);
207
208            var singleColumnLayoutStrategy = new OrgChart.Layout.SingleColumnLayoutStrategy();
209            singleColumnLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.Right;
210            singleColumnLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
211            singleColumnLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
212            diagram.LayoutSettings.LayoutStrategies.add("singleColumnRight", singleColumnLayoutStrategy);
213
214            singleColumnLayoutStrategy = new OrgChart.Layout.SingleColumnLayoutStrategy();
215            singleColumnLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.Left;
216            singleColumnLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
217            singleColumnLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
218            diagram.LayoutSettings.LayoutStrategies.add("singleColumnLeft", singleColumnLayoutStrategy);
219
220            var fishboneLayoutStrategy = new OrgChart.Layout.MultiLineFishboneLayoutStrategy();
221            fishboneLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.Center;
222            fishboneLayoutStrategy.MaxGroups = 1;
223            fishboneLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
224            fishboneLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
225            diagram.LayoutSettings.LayoutStrategies.add("fishbone1", fishboneLayoutStrategy);
226
227            fishboneLayoutStrategy = new OrgChart.Layout.MultiLineFishboneLayoutStrategy();
228            fishboneLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.Center;
229            fishboneLayoutStrategy.MaxGroups = 2;
230            fishboneLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
231            fishboneLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
232            diagram.LayoutSettings.LayoutStrategies.add("fishbone2", fishboneLayoutStrategy);
233
234            var hstackLayoutStrategy = new OrgChart.Layout.StackingLayoutStrategy();
235            hstackLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.InvalidValue;
236            hstackLayoutStrategy.Orientation = OrgChart.Layout.StackOrientation.SingleRowHorizontal;
237            hstackLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
238            hstackLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
239            diagram.LayoutSettings.LayoutStrategies.add("hstack", hstackLayoutStrategy);
240
241            var vstackLayoutStrategy = new OrgChart.Layout.StackingLayoutStrategy();
242            vstackLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.InvalidValue;
243            vstackLayoutStrategy.Orientation = OrgChart.Layout.StackOrientation.SingleColumnVertical;
244            vstackLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
245            vstackLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
246            diagram.LayoutSettings.LayoutStrategies.add("vstack", vstackLayoutStrategy);
247
248            vstackLayoutStrategy = new OrgChart.Layout.StackingLayoutStrategy();
249            vstackLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.InvalidValue;
250            vstackLayoutStrategy.Orientation = OrgChart.Layout.StackOrientation.SingleColumnVertical;
251            vstackLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
252            vstackLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
253            diagram.LayoutSettings.LayoutStrategies.add("vstackMiddle", vstackLayoutStrategy);
254
255            vstackLayoutStrategy = new OrgChart.Layout.StackingLayoutStrategy();
256            vstackLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.InvalidValue;
257            vstackLayoutStrategy.Orientation = OrgChart.Layout.StackOrientation.SingleColumnVertical;
258            vstackLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
259            vstackLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
260            diagram.LayoutSettings.LayoutStrategies.add("vstackTop", vstackLayoutStrategy);
261
262            var assistantsLayoutStrategy = new OrgChart.Layout.FishboneAssistantsLayoutStrategy();
263            assistantsLayoutStrategy.ParentAlignment = OrgChart.Layout.BranchParentAlignment.Center;
264            assistantsLayoutStrategy.ParentChildSpacing = RPOrgChart.parentChildSpacing;
265            assistantsLayoutStrategy.SiblingSpacing = RPOrgChart.siblingSpacing;
266            diagram.LayoutSettings.LayoutStrategies.add("assistants", assistantsLayoutStrategy);
267
268            diagram.LayoutSettings.DefaultLayoutStrategyId = "vstack";
269            diagram.LayoutSettings.DefaultAssistantLayoutStrategyId = "assistants";
270            //diagram.LayoutSettings.setBranchSpacing(5);
271        },
272
273        getBoxLevel: function(boxContainer, box) {
274            var level = 0;
275            var obj = {};
276            while (box.ParentId > 0) {
277                if (!boxContainer.getBoxesById().tryGetValue(box.ParentId, obj)) {
278                    break;
279                }
280                box = obj.v;
281                level++;
282            }
283
284            return level;
285        },
286
287        onLayoutStateChanged: function (sender, args) {
288            if (args.State.getCurrentOperation() === OrgChart.Layout.LayoutState.Operation.PreprocessVisualTree) {
289                // When layout algorithm is ready to preprocess the tree,
290                // we need to have box sizes ready -> hence have to render visible boxes in HTML.
291                // Rendering can happen at earlier time, but it's just more convenient to do it here,
292                // to utilize some readily available information about visual tree.
293                RPOrgChart.renderBoxes();
294            }
295        },
296
297        renderBoxes: function () {
298            var visitorFunc = function (node) {
299                var box = node.Element;
300
301                if (box.getIsDataBound()) {
302                    // we're being run when nodes have already been marked as visible or hidden,
303                    // based on IsCollapsed attribute of each Box
304                    // so use this knowledge to prevent unnecessary rendering of invisible branches
305                    if (node.State.IsHidden) {
306                        return true;
307                    }
308
309                    box.Size = RPOrgChart.getBoxElementSize(box.DataId);
310                }
311
312                return true;
313            }
314
315            RPOrgChart.diagram.getVisualTree().IterateParentFirst(visitorFunc);
316        },
317
318        getBranchOptimizerFunc: function () {
319            return RPOrgChart[RPOrgChart.branchOptimizer];
320        },
321
322        branchOptimizerAllLinear: function(node) {
323            return node.getIsAssistantRoot() ? null : "linear";
324        },
325
326        branchOptimizerAllHanger2: function(node) {
327            return node.getIsAssistantRoot() ? null : "hanger2";
328        },
329
330        branchOptimizerAllHanger4: function(node) {
331            return node.getIsAssistantRoot() ? null : "hanger4";
332        },
333
334        branchOptimizerAllFishbone1: function(node) {
335            return node.getIsAssistantRoot() ? null : "fishbone1";
336        },
337
338        branchOptimizerAllFishbone2: function (node) {
339            return node.getIsAssistantRoot() ? null : "fishbone2";
340        },
341
342        branchOptimizerAllSingleColumnLeft: function (node) {
343            return node.getIsAssistantRoot() ? null : "singleColumnRight";
344        },
345
346        branchOptimizerAllSingleColumnRight: function (node) {
347            return node.getIsAssistantRoot() ? null : "singleColumnLeft";
348        },
349
350        branchOptimizerStackers: function(node) {
351            if (node.getIsAssistantRoot()) {
352                return null;
353            }
354            return node.Level === 0 // this is Node for boxContainer.SystemRoot, which is not visible itself
355                ? "vstackTop"
356                : node.Level === 1 // this is children of SystemRoot - they appear as roots in the diagram
357                ? "vstackMiddle"
358                : "hstack";
359
360        },
361
362        branchOptimizerSmart: function(node) {
363            if (node.getIsAssistantRoot()) {
364                return null;
365            }
366
367            var childCount = node.getChildCount();
368
369            if (childCount <= 1) {
370                return "vstack";
371            }
372
373            var nonLeafChildren = 0;
374            for (var i = 0; i < childCount; i++) {
375                if (node.Children.getItem(i).getChildCount() > 0) {
376                    nonLeafChildren++;
377                }
378            }
379
380            if (nonLeafChildren <= 1) {
381                if (childCount <= 4) {
382                    return "vstack";
383                }
384                if (childCount <= 8) {
385                    return "fishbone1";
386                }
387                return "fishbone2";
388            }
389
390            return "hanger4";
391        },
392
393        boxSizeFunc: function (dataId) {
394            // ChartLayoutAlgorithm requires this function to accept data ID
395            // so have to convert it to Box ID first, to get rendered visual element
396            var boxId = RPOrgChart.diagram.getBoxes().getBoxesByDataId().getItem(dataId).Id;
397            return RPOrgChart.diagram.getBoxes().getBoxesById().getItem(boxId).Size;
398        },
399
400        getBoxElementSize: function (boxId) {
401    		var geo = RPOrgChart.graph.model.cells[boxId].geometry;
402    		return new OrgChart.Layout.Size.$ctor1(geo.width, geo.height);
403        },
404
405        positionBoxes: function () {
406            var diagram = RPOrgChart.diagram;
407
408            var state = new OrgChart.Layout.LayoutState(diagram);
409
410            state.addOperationChanged(RPOrgChart.onLayoutStateChanged);
411            state.BoxSizeFunc = Bridge.fn.bind(this, RPOrgChart.boxSizeFunc, null, true);
412            state.LayoutOptimizerFunc = Bridge.fn.bind(this, RPOrgChart.getBranchOptimizerFunc(), null, true);
413
414            OrgChart.Layout.LayoutAlgorithm.Apply(state);
415
416            var diagramBoundary = OrgChart.Layout.LayoutAlgorithm.ComputeBranchVisualBoundingRect(diagram.getVisualTree());
417
418			var offsetx = -diagramBoundary.getLeft() + diagramBoundary.getTop();
419
420			var graph = RPOrgChart.graph;
421            var cells = graph.model.cells;
422			var pointsList = [];
423
424            var visitorVertexFunc = function (node)
425            {
426                if (node.State.IsHidden) {
427                    return false;
428                }
429
430                var box = node.Element;
431
432                if (box.getIsDataBound()) {
433                    var cell = cells[box.DataId];
434                    var geo = cell.geometry.clone();
435                    geo.x = node.State.TopLeft.X + offsetx;
436                    geo.y = node.State.TopLeft.Y;
437                    graph.model.setGeometry(cell, geo);
438                }
439
440                return true;
441            }
442
443            var visitorEdgeFunc = function (node)
444            {
445				//The algorithm default is 5 px only above the node, this centers it
446				var yCorrection = RPOrgChart.correctY? Math.min(0, -(RPOrgChart.parentChildSpacing / 2) + 5) : 0;
447                // Render connectors
448                if (node.State.Connector != null) {
449
450                    var cell = cells[node.Element.DataId];
451
452                	var outgoingEdge = graph.getOutgoingEdges(cell);
453
454                	var uniquePoints = {};
455
456                	//Sort segments points from top to bottom or left to right + add offset
457                	for (var ix = 0; ix < node.State.Connector.Segments.length; ix++)
458                    {
459                		var edge = node.State.Connector.Segments[ix];
460                		edge.mark = 1 << ix; //TODO Support up to 31 segments. In this a limit?
461                        edge.From.X += offsetx;
462                        edge.To.X += offsetx;
463                        var fx = edge.From.X, fy = edge.From.Y, tx = edge.To.X, ty = edge.To.Y;
464
465                        if ((fx == tx && fy > ty) || (fy == ty && fx > tx))
466                    	{
467                        	var tmp = edge.From;
468                        	edge.From = edge.To;
469                        	edge.To = tmp;
470                    	}
471                    }
472
473                	//Collecting points including intersection of segments
474                    for (var ix = 0; ix < node.State.Connector.Segments.length; ix++)
475                    {
476                    	var edge = node.State.Connector.Segments[ix];
477                        var fx = edge.From.X, fy = edge.From.Y, tx = edge.To.X, ty = edge.To.Y;
478                        var fp = new mxPoint(fx, fy);
479						pointsList.push(fp);
480                        fp.mark = edge.mark;
481                        var up = uniquePoints[fx + ',' + fy];
482
483                        if (up != null)
484                    	{
485                        	up.mark |= fp.mark;
486                    	}
487                        else
488                        {
489                        	uniquePoints[fx + ',' + fy] = fp;
490                        }
491
492                        var tp = new mxPoint(tx, ty);
493						pointsList.push(tp);
494                        tp.mark = edge.mark;
495                        var up = uniquePoints[tx + ',' + ty];
496
497                        if (up != null)
498                    	{
499                        	up.mark |= tp.mark;
500                    	}
501                        else
502                        {
503                        	uniquePoints[tx + ',' + ty] = tp;
504                        }
505
506                        //Find intersections
507                        for (var j = ix + 1; j < node.State.Connector.Segments.length; j++)
508                        {
509                        	var e2 = node.State.Connector.Segments[j];
510                        	var fx2 = e2.From.X, fy2 = e2.From.Y, tx2 = e2.To.X, ty2 = e2.To.Y;
511
512                        	if (fx == tx && fy <= fy2 && ty >= fy2 && fx2 <= fx && tx2 >= fx) //Ver |_ Hor
513                    		{
514                        		var ip = new mxPoint(fx, fy2);
515								pointsList.push(ip);
516                        		ip.mark = edge.mark | e2.mark;
517                        		var up = uniquePoints[fx + ',' + fy2];
518
519                                if (up != null)
520                            	{
521                                	up.mark |= ip.mark;
522                            	}
523                                else
524                                {
525                                	uniquePoints[fx + ',' + fy2] = ip;
526                                }
527                    		}
528                        	else if (fy == ty && fx <= fx2 && tx >= fx2 && fy2 <= fy && ty2 >= fy) //Hor _| Ver
529                    		{
530                        		var ip = new mxPoint(fx2, fy);
531								pointsList.push(ip);
532                        		ip.mark = edge.mark | e2.mark;
533                        		var up = uniquePoints[fx2 + ',' + fy]
534
535                                if (up != null)
536                            	{
537                                	up.mark |= ip.mark;
538                            	}
539                                else
540                                {
541                                	uniquePoints[fx2 + ',' + fy] = ip;
542                                }
543                    		}
544                        }
545                    }
546
547                    //Sort points on y then x
548                    var pointsArr = [];
549
550                    for (var k in uniquePoints)
551                	{
552                    	pointsArr.push(uniquePoints[k]);
553                	}
554
555                    pointsArr.sort(function(a, b)
556                   	{
557                    	var dy = a.y - b.y;
558
559                    	return dy == 0? a.x - b.x : dy;
560                    });
561
562                    function pointOnCell(geo, p)
563                    {
564                    	return p.x >= geo.x && p.x <= geo.x + geo.width && p.y >= geo.y && p.y <= geo.y + geo.height;
565                    };
566
567                    function adjustEdgeGeoAndStyle(edge, edgePoints)
568                    {
569                        var eGeo = edge.geometry.clone();
570
571						for (var i = 0; edgePoints && i < edgePoints.length; i++)
572						{
573							if (!edgePoints[i].corrected)
574							{
575								edgePoints[i].y += yCorrection;
576								edgePoints[i].corrected = true
577							}
578						}
579
580                        eGeo.points = edgePoints;
581                        graph.model.setGeometry(edge, eGeo);
582
583                        //Remove entry and exit points
584                        graph.setCellStyles('entryX', null, [edge]);
585                        graph.setCellStyles('entryY', null, [edge]);
586                        graph.setCellStyles('exitX', null, [edge]);
587                        graph.setCellStyles('exitY', null, [edge]);
588                        //Set type orthogonal
589                        graph.setCellStyles('edgeStyle', 'orthogonalEdgeStyle', [edge]);
590                    };
591
592                    var outgoingEdge = graph.getOutgoingEdges(cell);
593
594                    //Simple case of a single segment. TODO Handle this case earlier
595                    if (pointsArr.length == 2 && outgoingEdge.length == 1)
596                	{
597                        adjustEdgeGeoAndStyle(outgoingEdge[0], pointsArr);
598                	}
599                    else
600                	{
601                        var srcGeo = cell.geometry;
602                        var srcP;
603
604                        //Find src starting point //TODO It should be first point always?
605                        for (var i = 0; i < pointsArr.length; i++)
606                    	{
607                        	if (pointOnCell(srcGeo, pointsArr[i]))
608                    		{
609                        		srcP = pointsArr[i];
610                        		break;
611                    		}
612                    	}
613
614                        var selected;
615
616                        function getNextPoint(lp)
617                        {
618                        	for (var i = 0; i < pointsArr.length; i++)
619                        	{
620                        		var p = pointsArr[i];
621                        		if (selected[p.x + ',' + p.y]) continue;
622
623                        		if (p.mark & lp.mark)
624                    			{
625                        			selected[p.x + ',' + p.y] = true;
626                        			return p;
627                                }
628                        	}
629                        }
630
631                    	for (var j = 0; j < outgoingEdge.length; j++)
632                		{
633                    		if (outgoingEdge[j].target != null)
634                			{
635                    			selected = {};
636                    			selected[srcP.x + ',' + srcP.y] = true;
637                                var trgGeo = outgoingEdge[j].target.geometry;
638
639                                var edgePoints = [srcP];
640                                var lp = srcP;
641                                var safeGuard = 0;
642                                //Is BFS better?
643                                while (safeGuard < 1000)
644                            	{
645                                	safeGuard++;
646                                	var np = getNextPoint(lp);
647
648                                	//retract, then remove this point
649                                	if (np == null)
650                            		{
651                                		edgePoints.pop();
652                                		lp = edgePoints[edgePoints.length - 1];
653                            		}
654                                	else
655                            		{
656                                		edgePoints.push(np);
657                                		lp = np;
658                                		if (pointOnCell(trgGeo, np)) break;
659                            		}
660                            	}
661
662                                //Remove retracted points TODO can we do it in a better way?
663                                if (edgePoints.length > 2)
664                                {
665	                                var spX = edgePoints[0].x;
666	                                var lpX = edgePoints[edgePoints.length - 1].x;
667
668	                                for (var i = edgePoints.length - 2; i > 0; i--)
669	                            	{
670	                                	if ((spX > lpX && edgePoints[i].x < lpX) || (spX < lpX && edgePoints[i].x < spX))
671	                            		{
672	                                		edgePoints.splice(i, 1);
673	                            		}
674	                            	}
675                                }
676
677                                var eGeo = outgoingEdge[j].geometry.clone();
678                                eGeo.points = edgePoints;
679                                RPOrgChart.graph.model.setGeometry(outgoingEdge[j], eGeo);
680
681                                //Fix edge points and style
682                                adjustEdgeGeoAndStyle(outgoingEdge[j], edgePoints);
683                			}
684                		}
685            		}
686                }
687
688                return true;
689            }
690
691            diagram.getVisualTree().IterateParentFirst(visitorVertexFunc);
692            diagram.getVisualTree().IterateParentFirst(visitorEdgeFunc);
693
694			//Cleanup
695			for (var i = 0; i < pointsList.length; i++)
696			{
697				delete pointsList[i].mark;
698				delete pointsList[i].corrected;
699			}
700        }
701
702    }
703});
704
705Bridge.init();
706