UNPKG

@syncfusion/ej2-diagrams

Version:

Feature-rich diagram control to create diagrams like flow charts, organizational charts, mind maps, and BPMN diagrams. Its rich feature set includes built-in shapes, editing, serializing, exporting, printing, overview, data binding, and automatic layouts.

1,052 lines 85.8 kB
import { DiagramAction } from '../enum/enum'; import { Rect } from '../primitives/rect'; import { BezierSegment, OrthogonalSegment } from '../objects/connector'; import { getFunction } from '../utility/base-util'; import { Point } from '../primitives/point'; import { updateLayoutValue } from '../utility/diagram-util'; /** * Hierarchical Tree and Organizational Chart */ var HierarchicalTree = /** @class */ (function () { /** * Constructor for the organizational chart module. * * @private */ function HierarchicalTree() { /** * Defines the layout animation * */ this.isAnimation = false; //constructs the layout module } /** * To destroy the organizational chart * * @returns {void} * @private */ HierarchicalTree.prototype.destroy = function () { /** * Destroy method performed here */ }; /** * Get module name. */ HierarchicalTree.prototype.getModuleName = function () { /** * Returns the module name of the layout */ return 'OrganizationalChart'; }; /** * @param nodes * @param nameTable * @param layoutProp * @param viewport * @param uniqueId * @param action * @private */ HierarchicalTree.prototype.updateLayout = function (nodes, nameTable, layoutProp, viewport, uniqueId, action) { var layout = { type: layoutProp.type, connectionPointOrigin: layoutProp.connectionPointOrigin, nameTable: nameTable, anchorX: 0, anchorY: 0, firstLevelNodes: [], centerNode: null, levels: [], maxLevel: 0, graphNodes: {}, orientation: layoutProp.orientation, horizontalSpacing: layoutProp.horizontalSpacing, verticalSpacing: layoutProp.verticalSpacing, verticalAlignment: layoutProp.verticalAlignment, horizontalAlignment: layoutProp.horizontalAlignment, fixedNode: layoutProp.fixedNode, getLayoutInfo: getFunction(layoutProp.getLayoutInfo), layoutInfo: layoutProp.layoutInfo, margin: layoutProp.margin, bounds: layoutProp.bounds, objects: [], root: layoutProp.root }; this.doLayout(layout, nodes, viewport, uniqueId, action); return layout; }; /** * Adjusts layout bounds to maintain proper spacing. * This method updates the positions of the layout, including the root node and its children, * If the space between the current and previous bounds is smaller than the required spacing. * * @param {Object} layout - The layout object containing node positions and structure. * @param {Object} rootNode - The root node of the current layout. * @param {Array} boundsArray - Array of bounds for all layouts. * @param {number} index - Index of the current layout. * @returns {void} */ HierarchicalTree.prototype.updateLayoutBounds = function (layout, rootNode, boundsArray, index) { // Get the current and previous layouts bounds var currentBounds = boundsArray[parseInt(index.toString(), 10)]; var previousBounds = boundsArray[parseInt(index.toString(), 10) - 1]; // Calculate the difference needed to maintain horizontal spacing var diff = layout.horizontalSpacing - (currentBounds.x - previousBounds.right); var traverseNodes = function (nodeId) { var node = layout.nameTable["" + nodeId]; if (!node) { return; } var layoutInfo = layout.graphNodes["" + nodeId]; layoutInfo.x += diff; // Shift the node's x position if (layoutInfo.mid !== undefined) { layoutInfo.mid += diff; // Shift the midpoint if defined } if (layoutInfo.firstChild && layoutInfo.firstChild.x !== undefined) { layoutInfo.firstChild.x += diff; // Shift the first child if its position is defined } // Recursively traverse child nodes if (layoutInfo.tree.children && layoutInfo.tree.children.length > 0) { for (var i = 0; i < layoutInfo.tree.children.length; i++) { traverseNodes(layoutInfo.tree.children[parseInt(i.toString(), 10)]); } } // Recursively traverse assistant nodes if present if (layoutInfo.tree.assistants && layoutInfo.tree.assistants.length > 0) { for (var j = 0; j < layoutInfo.tree.assistants.length; j++) { traverseNodes(layoutInfo.tree.assistants[parseInt(j.toString(), 10)]); } } }; // Start traversal from the root node of the current layout traverseNodes(rootNode.id); // Update the bounds of the current layout to reflect the shift currentBounds.x += diff; currentBounds.right += diff; }; HierarchicalTree.prototype.doLayout = function (layout, nodes, viewport, uniqueId, action) { var node; var i; var layoutInfo = {}; var shape; var rootNodes = []; if (layout.nameTable[layout.root]) { layout.firstLevelNodes.push(layout.nameTable[layout.root]); } for (i = 0; i < nodes.length; i++) { node = nodes[parseInt(i.toString(), 10)]; if (!node.excludeFromLayout) { layoutInfo = layout.graphNodes[node.id] = this.setUpLayoutInfo(layout, node); layoutInfo.tree.hasSubTree = false; if (!layout.nameTable[layout.root]) { if (!node.inEdges || !node.inEdges.length) { var parentId = 'parentId'; var processId = 'processId'; if (!node["" + parentId] && !node["" + processId]) { rootNodes.push(node); } if (node.data && String(node.data["" + uniqueId]) === layout.root) { layout.firstLevelNodes.push(node); } } } } } if (layout.firstLevelNodes.length === 0) { layout.firstLevelNodes = rootNodes; } //Update relationship(parent and children) for (i = 0; i < layout.firstLevelNodes.length; i++) { node = layout.firstLevelNodes[parseInt(i.toString(), 10)]; //let check: boolean; this.updateEdges(layout, node, 1, action, nodes); } // 965868: Layout Overlaps with other layout, when child node has larger size var layoutBoundsArray = []; if (layout.firstLevelNodes.length > 0) { layout.rootNode = layout.firstLevelNodes[0]; var x = 0; var y = 0; var minX = void 0; var maxY = void 0; var maxX = void 0; var minY = void 0; //let j: number; var bounds = void 0; for (i = 0; i < layout.firstLevelNodes.length; i++) { bounds = this.updateTree(layout, x, y, layout.firstLevelNodes[parseInt(i.toString(), 10)], 0, layout.firstLevelNodes[i - 1]); layoutBoundsArray.push(bounds); // 984193 - Nodes Overlap in Org Chart Layout with Multiple Roots if (layout.firstLevelNodes.length > 1 && i > 0 && i < layout.firstLevelNodes.length) { this.updateLayoutBounds(layout, layout.firstLevelNodes[parseInt(i.toString(), 10)], layoutBoundsArray, i); } var rootInfo = layout.graphNodes[layout.firstLevelNodes[parseInt(i.toString(), 10)].id]; bounds.y = Math.min(bounds.y, rootInfo.y); bounds.x = Math.min(bounds.x, rootInfo.x); if (layout.orientation.indexOf('Left') !== -1) { y = bounds.right + layout.horizontalSpacing; } else { x = bounds.right + layout.horizontalSpacing; } if (i === 0) { minX = bounds.x; minY = bounds.y; maxX = bounds.right; maxY = bounds.bottom; } else { minX = Math.min(minX, bounds.x); minY = Math.min(minY, bounds.y); maxX = Math.max(maxX, bounds.right); maxY = Math.max(maxY, bounds.bottom); } //Bug 924568: Hierarchical tree child nodes are positioned unevenly when rendering with multiple root nodes. //Added below code to empty the layout levels after processing the first root node. layout.levels = []; layout.maxLevel = undefined; } this.updateAnchor(layout, { x: minX, y: minY, right: maxX, bottom: maxY }, viewport); for (i = 0; i < layout.firstLevelNodes.length; i++) { this.updateNodes(layout, layout.firstLevelNodes[parseInt(i.toString(), 10)], 0); } for (i = 0; i < layout.firstLevelNodes.length; i++) { this.updateConnectors(layout, layout.firstLevelNodes[parseInt(i.toString(), 10)], 1); } } }; HierarchicalTree.prototype.getBounds = function (node) { var x = node.offsetX - node.actualSize.width * node.pivot.x; var y = node.offsetY - node.actualSize.height * node.pivot.y; return new Rect(x, y, node.actualSize.width, node.actualSize.height); }; HierarchicalTree.prototype.updateTree = function (layout, x, y, shape, level, prev, dontupdate) { //let dimensions: Dimensions; var info = {}; var lev; var obj; //let hasChild: number; var dimensions = this.getDimensions(layout, shape, x, y, level); info = layout.graphNodes[shape.id]; var firstChild; //Set maximum level of layout layout.maxLevel = Math.max(layout.maxLevel, level); lev = level; var hasChild = this.hasChild(layout, shape); if (!hasChild && !info.tree.assistants.length) { //update leaf nodes shape.treeBounds = this.updateLeafNode(layout, shape, prev, dimensions, level, dontupdate); return shape.treeBounds; } else { var treeBounds = void 0; var shapeBounds = void 0; var levelBounds = void 0; var d = void 0; var asstBounds = void 0; var space = void 0; var bottom = void 0; bottom = dimensions.y + dimensions.height + layout.verticalSpacing; if (info.tree.assistants.length) { //Vertically place assistants obj = this.setDepthSpaceForAssitants(layout, shape, bottom, dimensions.height, level, layout.verticalSpacing); lev = obj.level; bottom = obj.bottom; } if (!info.tree.assistants.length && info.tree.orientation !== 'Horizontal') { bottom = dimensions.y + dimensions.height + layout.verticalSpacing / 2; } if (info.tree.children.length) { if (info.tree.orientation === 'Horizontal' && (info.tree.type !== 'Balanced' || info.tree.children.length === 1)) { treeBounds = this.updateHorizontalTree(layout, shape, prev, dimensions.x, bottom, lev); } else if (info.tree.type === 'Balanced') { treeBounds = this.updateHorizontalTreeWithMultipleRows(layout, shape, prev, dimensions.x, bottom, lev); } else { treeBounds = this.updateVerticalTree(layout, shape, dimensions.x, bottom, lev, dontupdate); } } if (!(info.y && info.y > dimensions.y)) { info.y = dimensions.y; } // 919520: Leaf node position does not align correctly for subTreeAlignment 'Center' if (info.mid !== undefined) { x = info.mid; } if (info.tree.assistants.length) { //Set breadth space for assistants space = x !== undefined ? x : dimensions.x; asstBounds = this.setBreadthSpaceForAssistants(layout, shape, dimensions, space, bottom, level); if (!hasChild) { levelBounds = treeBounds = asstBounds; x = (levelBounds.x + levelBounds.right) / 2 - dimensions.width / 2; treeBounds = levelBounds; } d = asstBounds ? asstBounds.canMoveBy : undefined; } info.x = x; if (!info.translate) { info.treeWidth = treeBounds.right - treeBounds.x; } { shapeBounds = { x: x, y: dimensions.y, right: x + dimensions.width, bottom: dimensions.y + dimensions.height }; } var translateInfo = { layout: layout, shape: shape, shapeBounds: shapeBounds, treeBounds: treeBounds, dim: dimensions, level: level }; this.translateSubTree(translateInfo, d, prev !== undefined, dontupdate); if (info.firstChild && typeof info.firstChild !== 'string') { info.firstChild.x += info.subTreeTranslation; } shape.treeBounds = treeBounds; return treeBounds; } }; HierarchicalTree.prototype.updateLeafNode = function (layout, shape, prev, dimensions, level, dontupdate) { //let bounds: Bounds; var info = layout.graphNodes[shape.id]; info.x = dimensions.x; if (!(info.y && info.y > dimensions.y)) { info.y = dimensions.y; info.maxLevel = Math.max(level, info.maxLevel || 0); } // eslint-disable-next-line max-len var bounds = { x: dimensions.x, y: dimensions.y, right: dimensions.x + dimensions.width, bottom: dimensions.y + dimensions.height }; info.maxLevel = Math.max(info.maxLevel || 0, level); var translateInfo = { layout: layout, shape: shape, shapeBounds: bounds, treeBounds: bounds, dim: dimensions, level: level }; this.translateSubTree(translateInfo, undefined, prev !== undefined, dontupdate); return { x: info.x, y: info.y, right: info.x + dimensions.width, bottom: info.y + dimensions.height }; }; HierarchicalTree.prototype.setUpLayoutInfo = function (layout, item) { var info = {}; info.subTreeTranslation = 0; if (layout.type === 'OrganizationalChart') { info.tree = { orientation: 'Vertical', type: 'Alternate', offset: 20, enableRouting: true }; } else { info.tree = { orientation: 'Horizontal', type: 'Center', enableRouting: true }; } info.tree.children = []; info.tree.assistants = []; info.tree.level = 0; info.translate = true; return info; }; HierarchicalTree.prototype.translateSubTree = function (translateInfo, asstDif, translate, dontupdate) { var layout = translateInfo.layout; var shape = translateInfo.shape; var shapeBounds = translateInfo.shapeBounds; var treeBounds = translateInfo.treeBounds; var level = translateInfo.level; var dim = translateInfo.dim; var info = layout.graphNodes[shape.id]; var firstChild = layout.nameTable[info.firstChild ? info.firstChild.child : info.tree.children[0]]; var firstChildInfo = firstChild ? layout.graphNodes[firstChild.id] : null; var hasChild = this.hasChild(layout, shape); var intersect = this.findIntersectingLevels(layout, shapeBounds, level, info.actualLevel); var treeIntersect = this.findIntersectingLevels(layout, treeBounds, level, info.actualLevel); var levelBounds = []; //const diff: number; if (intersect.length && info.translate) { info.intersect = intersect; this.spaceLeftFromPrevSubTree(layout, shape, shapeBounds); info.canMoveBy = info.diff; if (asstDif !== undefined) { info.canMoveBy = Math.min(asstDif, info.canMoveBy); } if (firstChild && firstChildInfo.canMoveBy !== undefined) { if (firstChildInfo.canMoveBy >= info.canMoveBy) { info.translated = true; } info.canMoveBy = Math.min(info.canMoveBy, firstChildInfo.canMoveBy); } if (translate) { info.x -= info.canMoveBy; info.subTreeTranslation -= info.canMoveBy; if (hasChild) { this.shiftSubordinates(layout, treeIntersect, info.canMoveBy); treeBounds.x = Math.min(treeBounds.x, info.x); treeBounds.right = Math.max(treeBounds.right, info.x + dim.width); treeBounds.bottom = Math.max(treeBounds.bottom, info.y + dim.height); treeBounds.x -= info.canMoveBy; treeBounds.right -= info.canMoveBy; } if (firstChild && firstChildInfo.canMoveBy > info.canMoveBy) { info.canMoveBy = firstChildInfo.canMoveBy - info.canMoveBy; } else if (firstChild && info.canMoveBy !== undefined) { info.canMoveBy = 0; } } else if (info.canMoveBy !== undefined) { // 984193 - Nodes Overlap in Org Chart Layout with Multiple Roots if (hasChild) { treeBounds.x = Math.min(treeBounds.x, info.x); treeBounds.right = Math.max(treeBounds.right, info.x + dim.width); } } } else { if (hasChild) { treeBounds.x = Math.min(treeBounds.x, shapeBounds.x); treeBounds.right = Math.max(treeBounds.right, shapeBounds.x + dim.width); treeBounds.bottom = Math.max(treeBounds.bottom, info.y + dim.height); } if (!info.translate) { info.canMoveBy = 0; info.subTreeTranslation = 0; } } if (!dontupdate) { shapeBounds = { x: info.x, y: dim.y, right: info.x + dim.width, bottom: dim.y + dim.height }; levelBounds.push({ rBounds: shapeBounds }); this.updateRearBounds(layout, shape, levelBounds, level); } }; HierarchicalTree.prototype.updateRearBounds = function (layout, shape, levelBounds, level, intersect) { var bnds; var index; var isLastLeaf = true; var i; var info = {}; //let firstLevel: Bounds; //let lastLevel: Bounds; var bottom; if (shape) { info = layout.graphNodes[shape.id]; intersect = info.intersect; isLastLeaf = !info.tree.children.length && !info.tree.assistants.length; } var firstLevel = levelBounds[0].rBounds; var lastLevel = levelBounds[levelBounds.length - 1].rBounds; if (intersect && intersect.length) { bnds = layout.levels[intersect[0]].rBounds; bottom = bnds.bottom; if (bnds.y < firstLevel.y) { bnds.bottom = firstLevel.y; levelBounds.splice(0, 0, { rBounds: bnds }); } if (bottom > lastLevel.bottom) { levelBounds.push({ rBounds: { x: bnds.x, right: bnds.right, y: firstLevel.bottom, bottom: bottom } }); } else { bnds = layout.levels[intersect[intersect.length - 1]].rBounds; if (isLastLeaf && bnds.bottom > lastLevel.bottom) { bnds.y = lastLevel.bottom; levelBounds.push({ rBounds: bnds }); } } index = intersect[0]; for (i = levelBounds.length - 1; i >= 0; i--) { layout.levels.splice(index, 0, levelBounds[parseInt(i.toString(), 10)]); } index += levelBounds.length; layout.levels.splice(index, intersect.length); } else { index = this.findLevel(layout, levelBounds[levelBounds.length - 1].rBounds, level); for (i = levelBounds.length - 1; i >= 0; i--) { layout.levels.splice(index, 0, levelBounds[parseInt(i.toString(), 10)]); } } }; HierarchicalTree.prototype.shiftSubordinates = function (layout, intersect, diff) { var i; //Shift the sublevels by the distance diff if (diff !== 0) { for (i = 0; i < intersect.length; i++) { if (layout.levels[intersect[parseInt(i.toString(), 10)]].rBounds) { layout.levels[intersect[parseInt(i.toString(), 10)]].rBounds.x -= diff; layout.levels[intersect[parseInt(i.toString(), 10)]].rBounds.right -= diff; } } } }; HierarchicalTree.prototype.setDepthSpaceForAssitants = function (layout, shape, bottom, height, lev, vSpace) { var info = layout.graphNodes[shape.id]; var asst = {}; var asstHeight; var i; var asstElement; var max; max = bottom; //Vertically place the assistants as alternate layout(alternatively at both right and left sides of parent) for (i = 0; i < info.tree.assistants.length; i++) { asst = layout.graphNodes[info.tree.assistants[parseInt(i.toString(), 10)]]; if (asst) { asst.tree.children = asst.tree.assistants = []; asst.y = bottom; asstElement = layout.nameTable[info.tree.assistants[parseInt(i.toString(), 10)]]; asstHeight = asstElement.actualSize.height; if (layout.orientation.indexOf('Left') !== -1) { asstHeight = asstElement.actualSize.width; } max = bottom + asstHeight + vSpace / 2; layout.maxLevel = lev + 1; if (i % 2 === 1 && i !== info.tree.assistants.length - 1) { bottom = max; lev++; } } } return { level: layout.maxLevel, bottom: bottom + asstHeight + vSpace }; }; HierarchicalTree.prototype.setBreadthSpaceForAssistants = function (layout, shape, dim, space, bottom, level) { var asst = {}; var asstWidth; //let prevBounds: number; var bounds; var asstElement; var i; var info = layout.graphNodes[shape.id]; //let max: number = bottom; var lev = level; var left; var diff; var intersect; var levelBounds = { x: 0, y: 0, right: 0, bottom: 0 }; for (i = 0; i < info.tree.assistants.length; i++) { asst = layout.graphNodes[info.tree.assistants[parseInt(i.toString(), 10)]]; //Arrange assistants at both left and right sides of parent(like alternate layout) //Check - By default, distance to be left between parent and child nodes is assumed as 20. //It can be modified/customized later. if (asst) { asstElement = layout.nameTable[info.tree.assistants[parseInt(i.toString(), 10)]]; asstWidth = asstElement.actualSize.width; if (layout.orientation.indexOf('Left') !== -1) { asstWidth = asstElement.actualSize.height; } if (i % 2 === 0) { left = space + dim.width / 2 - 20 - asstWidth; } else { left = space + dim.width / 2 + 20; } //Check - What will happen if update leaf node is called? Since assistants don't have children bounds = this.updateTree(layout, left, asst.y, layout.nameTable[info.tree.assistants[parseInt(i.toString(), 10)]], lev + 1); if (!this.hasChild(layout, shape)) { if (i === 0) { levelBounds = bounds; } else { this.uniteRects(levelBounds, bounds); } } if (i % 2 === 0 && asst.prevBounds) { if (diff === undefined) { diff = asst.canMoveBy; } else { diff = Math.min(diff, asst.canMoveBy); } } if (i % 2 === 1 || i === info.tree.assistants.length - 1) { intersect = this.findIntersectingLevels(layout, bounds, lev + 1); //Update rightmost positions of known layout levels this.updateRearBounds(layout, null, [{ rBounds: bounds }], lev + 1, intersect); lev++; } } } if (levelBounds) { levelBounds.canMoveBy = diff; } return levelBounds; }; HierarchicalTree.prototype.getDimensions = function (layout, shape, x, y, level) { var width; width = shape.actualSize.width; var height; height = shape.actualSize.height; layout.orientation = layout.orientation || 'TopToBottom'; if (layout.orientation.indexOf('Left') !== -1) { if (!level) { //let temp: number; var temp = x; x = y; y = temp; } height = shape.actualSize.width; width = shape.actualSize.height; } return { x: x, y: y, width: width, height: height }; }; HierarchicalTree.prototype.hasChild = function (layout, shape) { //Check whether the node has children var shape1 = layout.graphNodes[shape.id]; return shape1 ? shape1.tree.children && shape1.tree.children.length : 0; }; HierarchicalTree.prototype.updateHorizontalTree = function (layout, shape, prev, x, y, level) { //Get dimensions with respect to layout orientations //let dimensions: Dimensions; var dimensions = this.getDimensions(layout, shape, x, y, level); var info = {}; info = layout.graphNodes[shape.id]; var side = info.tree.type; //let lev: number; var lev = level; var right = 0; right = x; var bottom = y; var width; var height; var child; var childBounds; var childWidth; var childHeight; //let prevBounds: Bounds; var bounds; var actBounds; var maxLevel; var translateSibilingsBy; var canMoveBy; var oldActBounds; var i; var childInfo; var firstChildInfo; var prevLayoutLevels = layout.levels.slice(0, layout.levels.length); if (this.hasChild(layout, shape)) { //let h: boolean; var h = layout.orientation.indexOf('Left') !== -1 ? true : false; for (i = 0; i < info.tree.children.length; i++) { child = layout.nameTable[info.tree.children[parseInt(i.toString(), 10)]]; width = child.actualSize.width; height = child.actualSize.height; childWidth = h ? height : width; childHeight = h ? width : height; var prevBounds = layout.levels[lev + 1] ? layout.levels[lev + 1].rBounds : null; //Update sub tree childBounds = this.updateTree(layout, right, bottom, child, lev + 1, layout.nameTable[info.tree.children[i - 1]]); childInfo = layout.graphNodes[child.id]; info.maxLevel = Math.max(info.maxLevel || 0, childInfo.maxLevel || 0); actBounds = { x: childInfo.x, y: childInfo.y, right: childInfo.x + childWidth, bottom: childInfo.y + childHeight }; if (i === 0) { //Compare with previous(right most) subtree bounds = { x: Math.min(childInfo.x, childBounds.x), y: Math.min(childInfo.y, childBounds.y), right: childBounds.right, bottom: childBounds.bottom }; firstChildInfo = childInfo; } if (!oldActBounds) { oldActBounds = actBounds; } else { oldActBounds.x = actBounds.x; oldActBounds.y = actBounds.y; if (actBounds.right > oldActBounds.right) { oldActBounds.right = actBounds.right; } oldActBounds.bottom = actBounds.bottom; //oldActBounds = actBounds; } //Compare with previous subtree if level of the child is greater than the level of previous sub tree //Check - what will happen if level of second child is greater than current child if (i === 0) { info.firstChild = { x: childInfo.x, canMoveBy: childInfo.canMoveBy, child: child.id }; } if (this.hasChild(layout, child)) { if (!info.firstChild || info.firstChild.x >= childInfo.firstChild.x) { if (childInfo.firstChild && info.firstChild.canMoveBy < childInfo.canMoveBy) { canMoveBy = info.firstChild.canMoveBy; childInfo.canMoveBy = canMoveBy; layout.graphNodes[info.firstChild.child].canMoveBy = canMoveBy; info.firstChild.canMoveBy = canMoveBy; } var canMoveValue = canMoveBy !== undefined ? canMoveBy : childInfo.canMoveBy; info.firstChild = { x: childInfo.firstChild.x, canMoveBy: canMoveValue, child: child.id }; } else if (childInfo.firstChild && childInfo.translated && info.firstChild.canMoveBy > childInfo.canMoveBy) { info.firstChild.canMoveBy = layout.graphNodes[info.firstChild.child].canMoveBy = childInfo.canMoveBy; } } maxLevel = maxLevel ? Math.max(childInfo.maxLevel, maxLevel) : childInfo.maxLevel; this.uniteRects(bounds, childBounds); if (i !== 0 && !this.hasChild(layout, child) && childInfo.subTreeTranslation < 0) { right = childBounds.right - childInfo.subTreeTranslation + layout.horizontalSpacing; } else { right = childBounds.right + layout.horizontalSpacing; } } if (!isNaN(translateSibilingsBy)) { firstChildInfo.canMoveBy = translateSibilingsBy; } info.mid = (firstChildInfo.x + oldActBounds.right) / 2 - dimensions.width / 2; //Set parent based on the chart type if (side === 'Left') { info.mid = actBounds.right - dimensions.width; } else if (side === 'Right') { info.mid = x; } } return bounds; }; /* eslint-disable */ HierarchicalTree.prototype.updateHorizontalTreeWithMultipleRows = function (layout, shape, prev, x, y, level) { //declarations var child; var childInfo; var childBounds; var childWidth; var childHeight; var firstChildInfo; var maxLevel; var bounds; var rowBounds; var width; var height; var diff; var translateSibilingsBy; var fchild; var maxRowWidth; var j; var i; var k; var max; var leftCenter; var rightCenter; //Get dimensions with respect to layout orientations var dimensions = this.getDimensions(layout, shape, x, y, level); var info = layout.graphNodes[shape.id]; var side = info.tree.type; var lev = level; var right = x; var bottom = y; var prevLayoutLevels = layout.levels.slice(0, layout.levels.length); var minTranslation = 0; if (this.hasChild(layout, shape)) { var h = layout.orientation.indexOf('Left') !== -1 ? true : false; var align = void 0; var rows = this.splitChildrenInRows(layout, shape); var unique = info.tree.children.length === 5 && rows[0].length === 3; var leftTree = []; var rightTree = []; if (!unique) { this.splitRows(rows, leftTree, rightTree); } else { rightTree = rows; } var treeInfo = { leftTree: leftTree, rows: rows, rightTree: rightTree, dimensions: dimensions }; var rightMost = this.updateLeftTree(layout, treeInfo, shape, x, bottom, lev); bounds = treeInfo.bounds; var rightX = void 0; var center = (rightMost || 0) + (rightMost !== undefined ? (layout.horizontalSpacing / 2) : 0); if (rightMost !== undefined) { info.mid = center - dimensions.width / 2; rightX = rightMost + layout.horizontalSpacing; } bottom = y; var rightBounds = void 0; rightBounds = []; for (i = 0; i < rightTree.length; i++) { if (rows[i].length % 2 === 1 && i === rightTree.length - 1 || unique) { right = x; } else { right = rightX || x; } if (i !== 0) { bottom = rightBounds[i - 1].bottom + layout.verticalSpacing; } for (j = 0; j < rightTree[i].length; j++) { child = layout.nameTable[rightTree[i][j]]; width = child.actualSize.width; height = child.actualSize.height; childWidth = h ? height : width; childHeight = h ? width : height; //Update sub tree childInfo = layout.graphNodes[child.id]; childInfo.actualLevel = lev + 1 + i; if (j === 0 && leftTree[i] && leftTree[i].length) { childInfo.translate = false; } if (unique && i === 1) { if (j === 0 && leftCenter + childWidth + layout.horizontalSpacing <= rightCenter) { align = true; right = leftCenter - childWidth / 2; } if (align && j === 1) { right = rightCenter - childWidth / 2; } } childBounds = this.updateTree(layout, right, bottom, child, lev + 1, layout.nameTable[rightTree[i][j - 1]]); if (unique && j <= 2 && i === 0) { if (j === 1) { leftCenter = childBounds.x - layout.horizontalSpacing / 2; rightCenter = childBounds.x + childWidth + layout.horizontalSpacing / 2; } } if (j === 0) { rightBounds[i] = { x: childBounds.x, y: childBounds.y, right: childBounds.right, bottom: childBounds.bottom }; } else { this.uniteRects(rightBounds[i], childBounds); } if (!bounds) { bounds = { x: rightBounds[i].x, y: rightBounds[i].y, right: rightBounds[i].right, bottom: rightBounds[i].bottom }; } this.uniteRects(bounds, rightBounds[i]); right = childBounds.right + layout.horizontalSpacing; if (!info.firstChild || ((i === rightTree.length - 1 && rows[i].length % 2 === 1) || unique) && j === 0 && childInfo.canMoveBy !== undefined && minTranslation > childInfo.canMoveBy) { minTranslation = Math.min(minTranslation, childInfo.canMoveBy || 0); info.firstChild = { x: childInfo.x, child: child.id, canMoveBy: childInfo.canMoveBy }; } treeInfo.leftCenter = leftCenter; treeInfo.rightCenter = rightCenter; treeInfo.align = align; treeInfo.level = lev; treeInfo.rightBounds = rightBounds; this.alignRowsToCenter(layout, i, shape, treeInfo, rightX); } } } return bounds; }; /* eslint-enable */ HierarchicalTree.prototype.updateLeftTree = function (layout, treeInfo, shape, x, bottom, lev) { var leftTree = treeInfo.leftTree; var info = layout.graphNodes[shape.id]; var right; var leftBounds = []; var minTranslation; var rightMost; var childBounds; var bounds; var h = layout.orientation.indexOf('Left') !== -1 ? true : false; //Arrange left side for (var i = 0; i < leftTree.length && leftTree[parseInt(i.toString(), 10)].length; i++) { right = x; if (leftBounds[i - 1]) { bottom = leftBounds[i - 1].bottom + layout.verticalSpacing; } for (var j = 0; j < leftTree[parseInt(i.toString(), 10)].length; j++) { var child = layout.nameTable[leftTree[parseInt(i.toString(), 10)][parseInt(j.toString(), 10)]]; var childWidth = h ? child.actualSize.height : child.actualSize.width; var childHeight = h ? child.actualSize.width : child.actualSize.height; //Update sub tree var childInfo = layout.graphNodes[child.id]; childInfo.actualLevel = lev + 1 + i; childBounds = this.updateTree(layout, right, bottom, child, lev + 1, layout.nameTable[leftTree[parseInt(i.toString(), 10)][j - 1]]); if (j === 0) { leftBounds[parseInt(i.toString(), 10)] = { x: childBounds.x, y: childBounds.y, right: childBounds.right, bottom: childBounds.bottom }; } else { this.uniteRects(leftBounds[parseInt(i.toString(), 10)], childBounds); } if (i === 0 && j === 0) { minTranslation = childInfo.canMoveBy; info.firstChild = { x: childInfo.x, child: child.id, canMoveBy: childInfo.canMoveBy }; } else if (j === 0 && childInfo.canMoveBy !== undefined && minTranslation > childInfo.canMoveBy) { minTranslation = Math.min(minTranslation, childInfo.canMoveBy || 0); info.firstChild = { x: childInfo.x, child: child.id, canMoveBy: childInfo.canMoveBy }; } right = childBounds.right + layout.horizontalSpacing; } if (i === 0) { rightMost = leftBounds[parseInt(i.toString(), 10)].right; } else { rightMost = Math.max(rightMost, leftBounds[parseInt(i.toString(), 10)].right); } } //Translate to same positions for (var i = 0; i < leftTree.length && leftTree[parseInt(i.toString(), 10)].length; i++) { if (rightMost !== leftBounds[parseInt(i.toString(), 10)].right) { var diff = rightMost - leftBounds[parseInt(i.toString(), 10)].right; for (var j = 0; j < leftTree[parseInt(i.toString(), 10)].length; j++) { var element = layout.nameTable[leftTree[parseInt(i.toString(), 10)][parseInt(j.toString(), 10)]]; var elementInfo = layout.graphNodes[leftTree[parseInt(i.toString(), 10)][parseInt(j.toString(), 10)]]; elementInfo.x += diff; } //leftBounds[i].x += diff; //leftBounds[i].right += diff; } if (i === 0) { bounds = { x: leftBounds[0].x, y: leftBounds[0].y, right: leftBounds[0].right, bottom: leftBounds[0].bottom }; } else { this.uniteRects(bounds, leftBounds[parseInt(i.toString(), 10)]); } } treeInfo.bounds = bounds; return rightMost; }; HierarchicalTree.prototype.alignRowsToCenter = function (layout, i, shape, treeInfo, rightX) { var max; var centered; var diff; var info = layout.graphNodes[shape.id]; var rows = treeInfo.rows; var rightTree = treeInfo.rightTree; var leftCenter = treeInfo.leftCenter; var rightCenter = treeInfo.rightCenter; var align = treeInfo.align; var rightBounds = treeInfo.rightBounds; var dimensions = treeInfo.dimensions; var lev = treeInfo.level; var unique = info.tree.children.length === 5 && rows[0].length === 3; if (unique && i === 1) { max = (rightBounds[0].right - rightBounds[0].x) >= (rightBounds[1].right - rightBounds[1].x) ? 0 : 1; } if (i === rows.length - 1) { if (rows[parseInt(i.toString(), 10)].length % 2 === 1 || unique && i === 1) { centered = rightTree[parseInt(i.toString(), 10)][Math.floor(rightTree[parseInt(i.toString(), 10)].length / 2)]; //let centerObjct: INode; var centerObjct = layout.nameTable["" + centered]; //let childDimension: Dimensions; var centeredX = layout.graphNodes["" + centered].x; var centeredY = layout.graphNodes["" + centered].y; var childDimension = this.getDimensions(layout, centerObjct, centeredX, centeredY, lev + 1); diff = undefined; if (!align && unique) { if (max === 1) { i = 0; } diff = (rightBounds[parseInt(max.toString(), 10)].x + rightBounds[parseInt(max.toString(), 10)].right) / 2 - (rightBounds[parseInt(i.toString(), 10)].x + rightBounds[parseInt(i.toString(), 10)].right) / 2; if (i === 0) { info.mid += diff; } } else if (!unique && rightX !== undefined) { diff = rightX - layout.horizontalSpacing / 2 - (centeredX + childDimension.width / 2); } if (diff !== undefined) { this.updateRearBoundsOfTree(layout, rightTree[parseInt(i.toString(), 10)], diff, dimensions); } if (unique) { info.mid = (rightCenter + leftCenter) / 2 + (i === 0 ? diff : 0) - dimensions.width / 2; } if (info.mid === undefined && layout.graphNodes["" + centered]) { info.mid = centeredX; } align = false; i++; } } }; HierarchicalTree.prototype.updateRearBoundsOfTree = function (layout, rightTree, diff, dimensions) { for (var j = 0; j < rightTree.length; j++) { var childInfo = layout.graphNodes[rightTree[parseInt(j.toString(), 10)]]; //let child: INode = layout.nameTable[rightTree[j]]; childInfo.x += diff; childInfo.canMoveBy += diff; if (j === rightTree.length - 1) { //removed child dimensions call calculation, since that is not used var childBnds = { x: childInfo.x, y: childInfo.y, right: childInfo.x + dimensions.width, bottom: childInfo.y + dimensions.height }; var intersect = this.findIntersectingLevels(layout, childBnds, childInfo.actualLevel); this.updateRearBounds(layout, null, [{ rBounds: childBnds }], childInfo.actualLevel, intersect); } } }; HierarchicalTree.prototype.splitRows = function (rows, leftTree, rightTree) { for (var i = 0; i < rows.length; i++) { leftTree[parseInt(i.toString(), 10)] = []; rightTree[parseInt(i.toString(), 10)] = []; var half = void 0; half = rows[parseInt(i.toString(), 10)].length; if (rows[parseInt(i.toString(), 10)].length % 2 !== 1) { half = Math.ceil(rows[parseInt(i.toString(), 10)].length / 2); for (var k = 0; k < half; k++) { leftTree[parseInt(i.toString(), 10)].push(rows[parseInt(i.toString(), 10)][parseInt(k.toString(), 10)]); } } for (var j = leftTree[parseInt(i.toString(), 10)].length; j < rows[parseInt(i.toString(), 10)].length; j++) { rightTree[parseInt(i.toString(), 10)].push(rows[parseInt(i.toString(), 10)][parseInt(j.toString(), 10)]); } } }; HierarchicalTree.prototype.updateVerticalTree = function (layout, shape, x, y, level, dontUpdate) { //declarations var child; var childInfo; var childBounds; var childWidth; var childHeight; var prevBounds; var bounds; var actBounds; var oddBounds; var evenBounds; //let dimensions: Dimensions = this.getDimensions(layout, shape, x, y, level); var info = layout.graphNodes[shape.id]; var firstChild = layout.nameTable[info.tree.children[0]]; var h = layout.orientation.indexOf('Left') !== -1 ? true : false; var factor = info.tree.type === 'Left' ? -1 : 0; var right = x; var bottom = y; var lev = level; var i; var intersect; var type; var levels = []; var oddLevels = []; var canMoveBy; //let diff: number; for (i = 0; i < info.tree.children.length; i++) { if (info.tree.type === 'Alternate') { //arrange at both left and right type = (i % 2 === 0 && info.tree.children.length > 2) ? 'Left' : 'Right'; factor = (i % 2 === 0 && info.tree.children.length > 2) ? -1 : 0; } right = x + this.findOffset(layout, shape, info, type); child = layout.nameTable[info.tree.children[parseInt(i.toString(), 10)]]; childWidth = h ? child.actualSize.height : child.actualSize.width; childHeight = h ? child.actualSize.width : child.actualSize.height; //Update sub tree childBounds = this.updateTree(layout, right + factor * childWidth, bottom, child, level + 1, undefined, true); childInfo = layout.graphNodes[child.id]; actBounds = { x: childInfo.x, y: childInfo.y, right: childInfo.x + childWidth, bottom: childInfo.y + childHeight }; if (i === 0) { this.uniteRects(childBounds, actBounds); bounds = childBounds; } else { this.uniteRects(bounds, childBounds); } //Check and adjust the space left from previous subtree/sibling if (childInfo.prevBounds && !(info.tree.type === 'Alternate' && i % 2 === 1 && info.tree.children.length > 2)) { canMoveBy = canMoveBy !== undefined ? Math.min(childInfo.canMoveBy, canMoveBy) : childInfo.canMoveBy; } //Max level of the subtree node info.maxLevel = Math.max(info.maxLevel || 0, childInfo.maxLevel || 0); if (!(info.tree.type === 'Alternate' && info.tree.children.length > 2 && i % 2 === 0)) { if (info.tree.type === 'Alternate' && info.tree.children.length > 2) { //alternate - arrange children with even index(0,2,4,6,..) at the next level bottom = Math.max(childBounds.bottom, prevBounds.bottom) + layout.verticalSpacing / 2; } else { // left/right - arrange next child at the nect level(bottom) bottom = childBounds.bottom + layout.verticalSpacing / 2; } level = info.maxLevel; levels.push({ rBounds: actBounds }); if (!evenBounds) { evenBounds = { x: childInfo.x, y: childInfo.y, right: childInfo.x + childWidth, bottom: childInfo.y + childHeight }; } else { this.uniteRects(evenBounds, actBounds); } if (childInfo.levelBounds) { levels = levels.concat(childInfo.levelBounds); } } else { if (i !== 0) { bottom = prevBounds.bottom + layout.verticalSpacing / 2; } oddLevels.push({ rBounds: actBounds }); if (childInfo.levelBounds) { oddLevels =