basicprimitives
Version:
Basic Primitives Diagrams for JavaScript - data visualization components library that implements organizational chart and multi-parent dependency diagrams, contains implementations of JavaScript Controls and PDF rendering plugins.
851 lines (790 loc) • 36 kB
JavaScript
import Tree from '../../../algorithms/Tree';
import { SideFlag, Visibility, HorizontalAlignmentType, AdviserPlacementType,
Enabled, ChildrenPlacementType, ItemType } from '../../../enums';
import TreeItem from '../../../models/TreeItem';
import { RowType, GroupType } from './enums';
import NodeTypeSorter from './NodeTypeSorter';
import NodeGroupSorter from './NodeGroupSorter';
import BranchAligner from './BranchAligner';
import NavigationalFamily from './NavigationalFamily';
function NodeProps() {
this.hasVisibleChildren = false;
this.hasPartners = false;
this.isPartner = false;
this.hasLeavesOnly = true;
this.hasChildren = false;
this.typeSorter = NodeTypeSorter();
this.groupSorter = NodeGroupSorter();
}
/* method uses structures created in orgTreeTask to create visual tree used to render chart
It populates visualTree structure with TreeItem objects.
*/
export default function VisualTreeBuilder() {
var _treeItemCounter,
_activeItems;
function build(orgTree, maximumId, activeItems, options) {
_treeItemCounter = maximumId;
_activeItems = activeItems;
var {visualTree, navigationFamily, branchAligner} = createVisualTreeItems(orgTree, options, activeItems);
return {
visualTree: visualTree,
navigationFamily: navigationFamily.getFamily(),
branchAligner: branchAligner,
treeItemCounter: _treeItemCounter
};
}
function createVisualTreeItems(orgTree, options, activeItems) {
var index,
leftSiblingOffset,
rightSiblingOffset,
orgTreeProps = {},
visualTree = Tree(), /* Tree(); key: TreeItem.id value: TreeItem */
navigationFamily = NavigationalFamily(activeItems); /* Family structure where key: TreeItem.id and value: TreeItem */
/* stage 1: orgTreeProps hash, find and set actualItemType, hasPartners, hasVisibleChildren, hasLeavesOnly */
orgTree.loopPostOrder(this, function (nodeId, node, parentId, parent) {
if(!orgTreeProps.hasOwnProperty(nodeId)) {
orgTreeProps[nodeId] = new NodeProps();
}
var nodeProps = orgTreeProps[nodeId];
nodeProps.actualItemType = node.itemType;
if (parent != null) {
if(!orgTreeProps.hasOwnProperty(parentId)) {
orgTreeProps[parentId] = new NodeProps();
}
var parentProps = orgTreeProps[parentId];
parentProps.hasVisibleChildren = parentProps.hasVisibleChildren || node.isVisible || nodeProps.hasVisibleChildren;
parentProps.hasChildren = true;
parentProps.hasLeavesOnly = parentProps.hasLeavesOnly && !nodeProps.hasChildren;
}
});
/* stage 2: convert not supported combinations of child and parent items */
orgTree.loopPreOrder(this, function (nodeId, node, parentId, parent) {
var nodeProps = orgTreeProps[nodeId];
var parentProps = orgTreeProps[parentId];
if(!parentProps) {
parentProps = new NodeProps();
parentProps.actualItemType = ItemType.Regular;
orgTreeProps[parentId] = parentProps;
}
switch (nodeProps.actualItemType) {
case ItemType.LimitedPartner:
case ItemType.AdviserPartner:
case ItemType.GeneralPartner:
/* Don't support partner of partner */
if(parentProps.isPartner) {
nodeProps.actualItemType = ItemType.Adviser;
} else {
nodeProps.isPartner = true;
parentProps.hasPartners = true;
}
break;
case ItemType.Regular:
/* Don't support regular children of partner */
if(parentProps.isPartner) {
nodeProps.actualItemType = ItemType.Assistant;
}
break;
}
});
/* stage 3: find nodes alignment levels, so nodes from different branches of the hierarchy will be placed at the same level */
var branchAligner = BranchAligner();
orgTree.loopLevels(this, function (parentOrgItemId, parentOrgItem, levelid) {
var parentProps = orgTreeProps[parentOrgItemId];
if (!parentProps.hasVisibleChildren) {
return orgTree.SKIP;
}
orgTree.loopChildren(this, parentOrgItemId, function (orgItemId, orgItem, index) {
var treeItemProps = orgTreeProps[orgItemId];
parentProps.typeSorter.addChild(treeItemProps.actualItemType, orgItem.levelOffset, orgItem);
parentProps.groupSorter.addChild(treeItemProps.actualItemType, orgItem.levelOffset, orgItem);
});
var partners = [];
if(parentProps.hasPartners) {
partners = [...parentProps.typeSorter.getRow(ItemType.AdviserPartner), ...parentProps.typeSorter.getRow(ItemType.LimitedPartner), ...parentProps.typeSorter.getRow(ItemType.GeneralPartner)];
}
var advisers = parentProps.typeSorter.getRow(ItemType.Adviser);
if(advisers.length > 0) {
/* extend advisers level */
var extendChildren = partners.length > 0;
if(!extendChildren) {
switch (parentOrgItem.placeAdvisersAboveChildren) {
case Enabled.Auto:
extendChildren = options.placeAdvisersAboveChildren;
break;
case Enabled.True:
extendChildren = true;
break;
}
}
if(options.alignBranches) {
if(parentProps.isPartner) {
extendChildren = true;
}
if(extendChildren) {
branchAligner.mergeToChild(parentOrgItemId, advisers, RowType.Advisers, 0, 0, extendChildren);
} else {
branchAligner.mergeToParent(parentOrgItemId, advisers);
}
} else {
branchAligner.addChild(parentOrgItemId, advisers, RowType.Advisers, 0, 0, extendChildren);
}
}
var subAdvisers = parentProps.typeSorter.getRow(ItemType.SubAdviser);
if(subAdvisers.length > 0) {
/* extend advisers level */
var extendChildren = partners.length > 0;
if(!extendChildren) {
switch (parentOrgItem.placeAdvisersAboveChildren) {
case Enabled.Auto:
extendChildren = options.placeAdvisersAboveChildren;
break;
case Enabled.True:
extendChildren = true;
break;
}
}
if(options.alignBranches) {
if(parentProps.isPartner) {
extendChildren = true;
}
branchAligner.mergeToChild(parentOrgItemId, subAdvisers, RowType.SubAdvisers, 0, 1, extendChildren);
} else {
branchAligner.addChild(parentOrgItemId, subAdvisers, RowType.SubAdvisers, 0, 1, extendChildren);
}
}
var assistants = parentProps.typeSorter.getRows(ItemType.Assistant);
/* create assistants levels */
if(assistants.length > 0) {
/* extend assistants levels */
var extendChildren = partners.length > 0;
if(!extendChildren) {
switch (parentOrgItem.placeAssistantsAboveChildren) {
case Enabled.Auto:
extendChildren = options.placeAssistantsAboveChildren;
break;
case Enabled.True:
extendChildren = true;
break;
}
}
assistants.forEach((nodes, index) => {
if(options.alignBranches) {
branchAligner.mergeToChild(parentOrgItemId, nodes, RowType.Assistants, index, 0, extendChildren);
} else {
branchAligner.addChild(parentOrgItemId, nodes, RowType.Assistants, index, 0, extendChildren);
}
});
}
var subAssistants = parentProps.typeSorter.getRows(ItemType.SubAssistant);
/* create assistants levels */
if(subAssistants.length > 0) {
/* extend assistants levels */
var extendChildren = partners.length > 0;
if(!extendChildren) {
switch (parentOrgItem.placeAssistantsAboveChildren) {
case Enabled.Auto:
extendChildren = options.placeAssistantsAboveChildren;
break;
case Enabled.True:
extendChildren = true;
break;
}
}
subAssistants.forEach((nodes, index) => {
if(options.alignBranches) {
branchAligner.mergeToChild(parentOrgItemId, nodes, RowType.SubAssistants, index, 1, extendChildren);
} else {
branchAligner.addChild(parentOrgItemId, nodes, RowType.SubAssistants, index, 1, extendChildren);
}
});
}
if(partners.length > 0) {
branchAligner.mergeToParent(parentOrgItemId, partners);
}
var rowChildren = parentProps.groupSorter.getRows(GroupType.RowChildren);
/* create row children levels */
if(rowChildren.length > 0) {
rowChildren.forEach((nodes, index) => {
if(options.alignBranches) {
branchAligner.mergeToChild(parentOrgItemId, nodes, RowType.RowChildren, index, 0, true);
} else {
branchAligner.addChild(parentOrgItemId, nodes, RowType.RowChildren, index, 0, true);
}
});
}
/* add remaining children in formation */
var children = parentProps.groupSorter.getRow(GroupType.Children);
if(children.length > 0) {
var props = orgTreeProps[parentOrgItemId];
var childrenRows = getRegularChildrenRows(options, children, parentOrgItem.childrenPlacementType, props.hasLeavesOnly);
childrenRows.forEach((nodes, index) => {
if(options.alignBranches) {
branchAligner.mergeToChild(parentOrgItemId, nodes, RowType.Children, index, 0, true);
} else {
if(index == childrenRows.length - 1) {
branchAligner.addSplitChildren(parentOrgItemId, nodes, RowType.Children, index, 0);
} else {
branchAligner.addChild(parentOrgItemId, nodes, RowType.Children, index, 0, true);
}
}
});
}
});
/* stage 4: measure depth of alignment levels */
branchAligner.align();
/* stage 5: create visual tree */
var visualPartners = {};
orgTree.loopLevels(this, function (parentOrgItemId, parentOrgItem, levelid) {
var parentProps = orgTreeProps[parentOrgItemId];
var logicalParentItem = visualTree.node(parentOrgItemId);
if (!logicalParentItem) {
logicalParentItem = getNewTreeItem({
visibility: Visibility.Invisible,
connectorPlacement: 0,
parentId: null,
actualItemType: ItemType.Regular
}, parentOrgItem);
visualTree.add(null, parentOrgItemId, logicalParentItem);
}
/* find left and right siblings margins of logical parent item
they are needed to properly place GeneralPartner & LimitedPartner nodes. */
leftSiblingOffset = 0;
rightSiblingOffset = 0;
if ((index = visualTree.indexOf(parentOrgItemId)) != null) {
leftSiblingOffset = index;
rightSiblingOffset = visualTree.countSiblings(parentOrgItemId) - index - 1;
}
var partners = [];
if(parentProps.hasPartners) {
partners = [
...parentProps.typeSorter.getRow(ItemType.AdviserPartner),
...parentProps.typeSorter.getRow(ItemType.LimitedPartner),
...parentProps.typeSorter.getRow(ItemType.GeneralPartner)
];
}
var visualParent = logicalParentItem;
var visualParent2 = null;
var flagPartners = true;
branchAligner.loopGroupTypes(this, parentOrgItemId, function(groupType, len) {
if(!(parentProps.hasPartners || parentProps.isPartner) && groupType > parentProps.groupSorter.getLength() - 1) {
return true;
}
if(groupType > GroupType.Assistants && flagPartners) {
flagPartners = false;
if(parentProps.hasPartners) {
visualPartners[parentOrgItemId] = [visualParent.id];
visualParent = visualParent2 || visualParent;
visualParent2 = null;
visualParent.partners = visualPartners[parentOrgItemId];
}
if(parentProps.isPartner) {
visualPartners[parentOrgItem.parent].push(visualParent.id);
}
}
var fillEmptyLevels = ((parentProps.isPartner || parentProps.hasPartners) && groupType <= GroupType.Assistants);
fillEmptyLevels = fillEmptyLevels || groupType < parentProps.groupSorter.getLength() - 1;
var rows = [];
switch(groupType) {
case GroupType.Items:
var row = parentProps.groupSorter.getRows(GroupType.Items)[0] || [];
var depth = branchAligner.getRowDepth(parentOrgItemId, GroupType.Items, 0);
addAdvisers(visualTree, orgTreeProps, visualParent, row, leftSiblingOffset, rightSiblingOffset);
row.forEach(item => navigationFamily.define(parentOrgItem, item));
if(partners.length > 0) {
visualParent2 = addPartners(visualTree, orgTreeProps, visualParent, partners, leftSiblingOffset, rightSiblingOffset);
/* every child logically belongs to every partner */
partners.forEach( partner => {
navigationFamily.define(parentOrgItem, partner, true);
if(partner.id != logicalParentItem.id) {
var rowChildren = parentProps.groupSorter.getRows(GroupType.RowChildren);
rowChildren.forEach(row => row.forEach(child => navigationFamily.define(partner, child)));
var regularChildren = parentProps.groupSorter.getRow(GroupType.Children);
regularChildren.forEach(child => navigationFamily.define(partner, child));
}
})
}
if(parentProps.hasPartners || parentProps.isPartner || groupType < parentProps.groupSorter.getLength() - 1) {
while(depth > 1) {
visualParent = createNewVisualAggregator(visualTree, visualParent, false);
if(visualParent2) {
visualParent2 = createNewVisualAggregator(visualTree, visualParent2, false);
}
depth-=1;
}
}
break;
case GroupType.Assistants:
var rows = parentProps.groupSorter.getRows(GroupType.Assistants);
branchAligner.loopRows(this, parentOrgItemId, RowType.Assistants, function(depth, rowIndex) {
var row = rows[rowIndex] || [];
if(!fillEmptyLevels && rowIndex > rows.length - 1) {
return true;
}
visualParent = addAssistants(visualTree, orgTreeProps, visualParent, row);
if(visualParent2) {
visualParent2 = createNewVisualAggregator(visualTree, visualParent2, false);
}
row.forEach(item => navigationFamily.define(parentOrgItem, item));
if(parentProps.hasPartners || parentProps.isPartner || rowIndex < rows.length - 1 || groupType < parentProps.groupSorter.getLength() - 1) {
while(depth > 1) {
visualParent = createNewVisualAggregator(visualTree, visualParent, false);
if(visualParent2) {
visualParent2 = createNewVisualAggregator(visualTree, visualParent2, false);
}
depth-=1;
}
}
});
break;
case GroupType.RowChildren:
var rows = parentProps.groupSorter.getRows(GroupType.RowChildren);
branchAligner.loopRows(this, parentOrgItemId, RowType.RowChildren, function(depth, rowIndex) {
var row = rows[rowIndex] || [];
if(!fillEmptyLevels && rowIndex > rows.length - 1) {
return true;
}
var hideChildConnector = (logicalParentItem.visibility == Visibility.Invisible) && (logicalParentItem.connectorPlacement === 0);
visualParent = addRowChildren(visualTree, visualParent, row, fillEmptyLevels || rowIndex < rows.length - 1, hideChildConnector, options.horizontalAlignment);
row.forEach(item => navigationFamily.define(parentOrgItem, item));
if(rowIndex < rows.length - 1 || groupType < parentProps.groupSorter.getLength() - 1) {
while(depth > 1) {
visualParent = createNewVisualAggregator(visualTree, visualParent, false);
depth-=1;
}
}
});
break;
case GroupType.Children:
var regularChildren = parentProps.groupSorter.getRow(GroupType.Children); /* children added after all other custom item types */
/* add remaining children in formation */
if(regularChildren.length > 0) {
var props = orgTreeProps[logicalParentItem.id];
var depths = branchAligner.getRowsDepth(parentOrgItemId, GroupType.Children);
addChildren(orgTree, visualTree, depths, options, logicalParentItem, visualParent, regularChildren, parentOrgItem.childrenPlacementType, props.hasLeavesOnly);
regularChildren.forEach(item => navigationFamily.define(parentOrgItem, item));
}
break;
}
});
if(flagPartners) {
flagPartners = false;
if(parentProps.hasPartners) {
visualPartners[parentOrgItemId] = [visualParent.id];
visualParent = visualParent2 || visualParent;
visualParent2 = null;
visualParent.partners = visualPartners[parentOrgItemId];
}
if(parentProps.isPartner) {
visualPartners[parentOrgItem.parent].push(visualParent.id);
}
}
if (!parentProps.hasVisibleChildren) {
return orgTree.SKIP;
}
});
return {
visualTree: visualTree,
navigationFamily: navigationFamily,
branchAligner: branchAligner
}
}
function addPartners(visualTree, orgTreeProps, parent, partners, leftSiblingOffset, rightSiblingOffset) {
var leftItems = [];
var rightItems = [];
partners.map(partner => getNewTreeItem({}, partner)).forEach(item => {
var isLeft = true;
if (parent.connectorPlacement & SideFlag.Right) {
isLeft = true;
item.connectorPlacement = SideFlag.Right | SideFlag.Bottom;
} else if (parent.connectorPlacement & SideFlag.Left) {
isLeft = false;
item.connectorPlacement = SideFlag.Left | SideFlag.Bottom;
} else {
switch (item.adviserPlacementType) {
case AdviserPlacementType.Left:
isLeft = true;
item.connectorPlacement = SideFlag.Right | SideFlag.Bottom;
break;
default:
isLeft = false;
item.connectorPlacement = SideFlag.Left | SideFlag.Bottom;
break;
}
var itemProp = orgTreeProps[item.id];
switch (itemProp.actualItemType) {
case ItemType.GeneralPartner:
item.connectorPlacement = SideFlag.Top | SideFlag.Bottom;
break;
case ItemType.LimitedPartner:
item.connectorPlacement = SideFlag.Bottom;
break;
default:
break;
}
}
if(isLeft) {
leftItems.unshift(item);
} else {
rightItems.push(item);
}
});
var partners = [...leftItems, parent, ...rightItems];
var parentIndex = leftItems.length;
var centerIndex = Math.floor((partners.length) / 2);
var invisiblePartner = null;
if(partners.length % 2 == 0) {
invisiblePartner = getNewTreeItem({ visibility: Visibility.Invisible });
partners.splice(centerIndex, 0, invisiblePartner);
if(centerIndex <= parentIndex) {
parentIndex+=1;
}
}
var visualParent = visualTree.parent(parent.id);
for (var index = parentIndex - 1; index >= 0; index -= 1) {
var item = partners[index];
visualTree.add(visualParent.id, item.id, item, leftSiblingOffset);
item.gravity = HorizontalAlignmentType.Right;
}
for (var index = parentIndex + 1; index < partners.length; index += 1) {
var item = partners[index];
visualTree.add(visualParent.id, item.id, item, visualTree.countChildren(visualParent.id) - rightSiblingOffset);
item.gravity = HorizontalAlignmentType.Left;
}
if(invisiblePartner != null) {
var mimicPartner = null;
if(centerIndex <= parentIndex) {
mimicPartner = partners[centerIndex - 1];
} else {
mimicPartner = partners[centerIndex + 1];
}
invisiblePartner.connectorPlacement = mimicPartner.connectorPlacement & (SideFlag.Left | SideFlag.Right);
}
var centerPartner = partners[centerIndex];
return centerPartner;
}
function addAdvisers(visualTree, orgTreeProps, parent, advisers, leftSiblingOffset, rightSiblingOffset) {
advisers.map(adviser => getNewTreeItem({}, adviser)).forEach(item => {
var itemProps = orgTreeProps[item.id];
var alteredItem;
switch(itemProps.actualItemType) {
case ItemType.SubAdviser:
item.connectorPlacement = SideFlag.Top | SideFlag.Bottom;
alteredItem = getNewTreeItem({ visibility: Visibility.Invisible });
visualTree.add(alteredItem.id, item.id, item);
break;
default:
alteredItem = item;
break;
}
var visualParent = visualTree.parent(parent.id);
if (parent.connectorPlacement & SideFlag.Right) {
visualTree.add(visualParent.id, alteredItem.id, alteredItem, leftSiblingOffset);
alteredItem.connectorPlacement = SideFlag.Right | SideFlag.Bottom;
alteredItem.gravity = HorizontalAlignmentType.Right;
} else if (parent.connectorPlacement & SideFlag.Left) {
visualTree.add(visualParent.id, alteredItem.id, alteredItem, visualTree.countChildren(visualParent.id) - rightSiblingOffset);
alteredItem.connectorPlacement = SideFlag.Left | SideFlag.Bottom;
alteredItem.gravity = HorizontalAlignmentType.Left;
} else {
switch (item.adviserPlacementType) {
case AdviserPlacementType.Left:
visualTree.add(visualParent.id, alteredItem.id, alteredItem, leftSiblingOffset);
alteredItem.connectorPlacement = SideFlag.Right | SideFlag.Bottom;
alteredItem.gravity = HorizontalAlignmentType.Right;
break;
default:
visualTree.add(visualParent.id, alteredItem.id, alteredItem, visualTree.countChildren(visualParent.id) - rightSiblingOffset);
alteredItem.connectorPlacement = SideFlag.Left | SideFlag.Bottom;
alteredItem.gravity = HorizontalAlignmentType.Left;
break;
}
switch (item.actualItemType) {
case ItemType.GeneralPartner:
alteredItem.connectorPlacement = SideFlag.Top | SideFlag.Bottom;
break;
case ItemType.LimitedPartner:
alteredItem.connectorPlacement = SideFlag.Bottom;
break;
}
}
})
}
function addAssistants(visualTree, orgTreeProps, visualParent, assistants) {
var nextVisualParent = createNewVisualAggregator(visualTree, visualParent, false);
assistants.map(assistant => getNewTreeItem({}, assistant)).forEach(item => {
var itemProps = orgTreeProps[item.id];
var alteredItem;
switch(itemProps.actualItemType) {
case ItemType.SubAssistant:
item.connectorPlacement = SideFlag.Top | SideFlag.Bottom;
alteredItem = getNewTreeItem({ visibility: Visibility.Invisible });
visualTree.add(alteredItem.id, item.id, item);
break;
case ItemType.Assistant:
alteredItem = item;
break;
}
switch (item.adviserPlacementType) {
case AdviserPlacementType.Left:
visualTree.add(visualParent.id, alteredItem.id, alteredItem, 0);
alteredItem.connectorPlacement = SideFlag.Right | SideFlag.Bottom;
alteredItem.gravity = HorizontalAlignmentType.Right;
break;
default:
visualTree.add(visualParent.id, alteredItem.id, alteredItem);
alteredItem.connectorPlacement = SideFlag.Left | SideFlag.Bottom;
alteredItem.gravity = HorizontalAlignmentType.Left;
break;
}
});
return nextVisualParent;
}
function addRowChildren(visualTree, visualParent, rowOfChildren, fillEmptyLevels, hideChildConnector, horizontalAlignment) {
var nextVisualParent = null;
if(fillEmptyLevels) {
nextVisualParent = createNewVisualAggregator(visualTree, visualParent, hideChildConnector);
}
var medianIndex = 0;
switch (horizontalAlignment) {
case HorizontalAlignmentType.Center:
medianIndex = Math.ceil(rowOfChildren.length / 2) - 1;
break;
case HorizontalAlignmentType.Left:
medianIndex = -1;
break;
case HorizontalAlignmentType.Right:
medianIndex = rowOfChildren.length - 1;
break;
}
for (var index = medianIndex; index >= 0; index -= 1) {
var item = getNewTreeItem({}, rowOfChildren[index]);
visualTree.add(visualParent.id, item.id, item, 0);
item.connectorPlacement = SideFlag.Top | SideFlag.Bottom;
item.gravity = HorizontalAlignmentType.Right;
}
for (index = medianIndex + 1; index < rowOfChildren.length; index += 1) {
item = getNewTreeItem({}, rowOfChildren[index]);
visualTree.add(visualParent.id, item.id, item);
item.connectorPlacement = SideFlag.Top | SideFlag.Bottom;
item.gravity = HorizontalAlignmentType.Left;
}
return nextVisualParent;
}
function addChildren(orgTree, visualTree, depths, options, treeItem, visualParent, regularChildren, childrenPlacementType, hasLeavesOnly) {
var visualParent,
currentVisualParent,
leftChildItem,
rightChildItem,
newAggregatorItem,
childItem, orgChildItem,
width,
height,
twinColumns,
rowIndex,
index, len,
singleItemPlacement,
hideParentConnector = (treeItem.visibility == Visibility.Invisible) && (treeItem.connectorPlacement === 0);
switch (options.horizontalAlignment) {
case HorizontalAlignmentType.Center:
case HorizontalAlignmentType.Left:
singleItemPlacement = AdviserPlacementType.Right;
break;
case HorizontalAlignmentType.Right:
singleItemPlacement = AdviserPlacementType.Left;
break;
}
if (childrenPlacementType === ChildrenPlacementType.Auto) {
if (hasLeavesOnly) {
childrenPlacementType = (options.leavesPlacementType === ChildrenPlacementType.Auto) ?
ChildrenPlacementType.Matrix : options.leavesPlacementType;
}
else {
childrenPlacementType = (options.childrenPlacementType === ChildrenPlacementType.Auto) ?
ChildrenPlacementType.Horizontal : options.childrenPlacementType;
}
}
if (childrenPlacementType == ChildrenPlacementType.Matrix && regularChildren.length < 3) {
childrenPlacementType = ChildrenPlacementType.Horizontal;
}
switch (childrenPlacementType) {
case ChildrenPlacementType.Horizontal:
for (index = 0, len = regularChildren.length; index < len; index += 1) {
childItem = getNewTreeItem({}, regularChildren[index]);
orgChildItem = orgTree.node(childItem.id);
visualTree.add(visualParent.id, childItem.id, childItem);
childItem.connectorPlacement = (orgChildItem.hideParentConnection ? 0 : SideFlag.Top) | (orgChildItem.hideChildrenConnection ? 0 : SideFlag.Bottom);
if (index === 0) {
childItem.relationDegree = 1;
}
}
break;
case ChildrenPlacementType.Matrix:
width = Math.min(options.maximumColumnsInMatrix, Math.ceil(Math.sqrt(regularChildren.length)));
height = Math.ceil(regularChildren.length / width);
twinColumns = Math.ceil(width / 2.0);
for (var columnIndex = 0; columnIndex < twinColumns; columnIndex += 1) {
currentVisualParent = visualParent;
for (rowIndex = 0; rowIndex < height; rowIndex += 1) {
leftChildItem = getMatrixItem(regularChildren, columnIndex * 2, rowIndex, width);
rightChildItem = getMatrixItem(regularChildren, columnIndex * 2 + 1, rowIndex, width);
if (leftChildItem !== null || rightChildItem !== null) {
var depth = depths[rowIndex - 1] || 1;
while(depth > 1) {
newAggregatorItem = getNewTreeItem({
visibility: Visibility.Invisible,
connectorPlacement: hideParentConnector ? 0 : SideFlag.Top | SideFlag.Bottom
});
visualTree.add(currentVisualParent.id, newAggregatorItem.id, newAggregatorItem);
currentVisualParent = newAggregatorItem;
depth -= 1;
}
}
if (leftChildItem !== null) {
leftChildItem = getNewTreeItem({},leftChildItem);
if (columnIndex === 0) {
leftChildItem.relationDegree = 1;
}
visualTree.add(currentVisualParent.id, leftChildItem.id, leftChildItem);
leftChildItem.connectorPlacement = (hideParentConnector ? 0 : SideFlag.Right) | SideFlag.Bottom;
leftChildItem.gravity = HorizontalAlignmentType.Right;
}
if (leftChildItem !== null || rightChildItem !== null) {
newAggregatorItem = getNewTreeItem({
visibility: Visibility.Invisible,
connectorPlacement: hideParentConnector ? 0 : SideFlag.Top | SideFlag.Bottom
});
visualTree.add(currentVisualParent.id, newAggregatorItem.id, newAggregatorItem);
}
if (rightChildItem !== null) {
rightChildItem = getNewTreeItem({},rightChildItem);
visualTree.add(currentVisualParent.id, rightChildItem.id, rightChildItem);
rightChildItem.connectorPlacement = (hideParentConnector ? 0 : SideFlag.Left) | SideFlag.Bottom;
rightChildItem.gravity = HorizontalAlignmentType.Left;
}
currentVisualParent = newAggregatorItem;
}
}
if (width > 2) {
// No center alignment to aggregator required
visualParent.visualAggregatorId = null;
}
break;
case ChildrenPlacementType.Vertical:
for (index = 0, len = regularChildren.length; index < len; index += 1) {
childItem = getNewTreeItem({}, regularChildren[index]);
var depth = (index == len - 1) ? 1 : depths[index] || 1;
var aggregatorItem = visualParent;
while(depth > 0) {
newAggregatorItem = getNewTreeItem({
visibility: Visibility.Invisible,
connectorPlacement: hideParentConnector ? 0 : SideFlag.Top | SideFlag.Bottom
});
visualTree.add(aggregatorItem.id, newAggregatorItem.id, newAggregatorItem);
aggregatorItem = newAggregatorItem;
depth -= 1;
}
switch (singleItemPlacement) {
case AdviserPlacementType.Left:
visualTree.add(visualParent.id, childItem.id, childItem, 0);
childItem.connectorPlacement = (hideParentConnector ? 0 : SideFlag.Right) | SideFlag.Bottom;
childItem.gravity = HorizontalAlignmentType.Right;
break;
case AdviserPlacementType.Right:
visualTree.add(visualParent.id, childItem.id, childItem);
childItem.connectorPlacement = (hideParentConnector ? 0 : SideFlag.Left) | SideFlag.Bottom;
childItem.gravity = HorizontalAlignmentType.Left;
break;
}
visualParent = newAggregatorItem;
}
break;
default:
throw "Children placement is undefined!";
}
}
function getRegularChildrenRows(options, regularChildren, childrenPlacementType, hasLeavesOnly) {
var results = [];
if (childrenPlacementType === ChildrenPlacementType.Auto) {
if (hasLeavesOnly) {
childrenPlacementType = (options.leavesPlacementType === ChildrenPlacementType.Auto) ?
ChildrenPlacementType.Matrix : options.leavesPlacementType;
}
else {
childrenPlacementType = (options.childrenPlacementType === ChildrenPlacementType.Auto) ?
ChildrenPlacementType.Horizontal : options.childrenPlacementType;
}
}
if (childrenPlacementType == ChildrenPlacementType.Matrix && regularChildren.length < 3) {
childrenPlacementType = ChildrenPlacementType.Horizontal;
}
switch (childrenPlacementType) {
case ChildrenPlacementType.Horizontal:
results.push(regularChildren);
break;
case ChildrenPlacementType.Matrix:
var width = Math.min(options.maximumColumnsInMatrix, Math.ceil(Math.sqrt(regularChildren.length)));
for(var index = 0; index < regularChildren.length; index+= width) {
results.push(regularChildren.slice(index, index + width));
}
break;
case ChildrenPlacementType.Vertical:
regularChildren.forEach(childItem => {
results.push([childItem])
})
break;
}
return results;
}
function getMatrixItem(items, x, y, width) {
var result,
isOdd = (width % 2 > 0),
index;
if (isOdd) {
if (x === width - 1) {
x = items.length;
}
else if (x === width) {
x = width - 1;
}
}
index = y * width + x;
result = (index > items.length - 1) ? null : items[index];
return result;
}
function createNewVisualAggregator(visualTree, treeItem, hideChildrenConnector) {
var newAggregatorItem,
hideParentConnector = ((treeItem.visibility == Visibility.Invisible) && ((treeItem.connectorPlacement & SideFlag.Top) === 0)) || hideChildrenConnector;
newAggregatorItem = getNewTreeItem({
visibility: Visibility.Invisible,
visualAggregatorId: treeItem.visualAggregatorId,
connectorPlacement: hideParentConnector ? 0 : (SideFlag.Top | SideFlag.Bottom)
});
visualTree.insert(treeItem.id, newAggregatorItem.id, newAggregatorItem);
treeItem.visualAggregatorId = newAggregatorItem.id;
return newAggregatorItem;
}
function getNewTreeItem(properties, orgItem) {
var result = new TreeItem(),
optionKey;
if (orgItem != null) {
result.actualItemType = orgItem.itemType;
result.adviserPlacementType = orgItem.adviserPlacementType;
}
for (optionKey in properties) {
if (properties.hasOwnProperty(optionKey)) {
result[optionKey] = properties[optionKey];
}
}
if (orgItem != null) {
result.id = orgItem.id;
result.visibility = orgItem.isVisible ? Visibility.Auto : Visibility.Invisible;
} else {
_treeItemCounter += 1;
result.id = _treeItemCounter;
}
return result;
}
return {
build: build
};
};