UNPKG

topola

Version:

Topola – online genealogy visualization

337 lines (336 loc) 15.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EntryId = exports.HierarchyCreator = void 0; exports.getRootsCount = getRootsCount; var api_1 = require("./api"); var d3_hierarchy_1 = require("d3-hierarchy"); var hierarchy_filter_1 = require("./hierarchy-filter"); var id_generator_1 = require("../id-generator"); var utils_1 = require("../utils"); var HierarchyCreator = /** @class */ (function () { function HierarchyCreator(data, startEntryId) { var _a; this.data = data; this.queuedNodesById = new Map(); this.idGenerator = new id_generator_1.IdGenerator(); _a = this.expandStartId(startEntryId), this.startEntryId = _a[0], this.startFamIndi = _a[1]; } HierarchyCreator.createHierarchy = function (data, startEntryId) { return new HierarchyCreator(data, startEntryId).createHierarchy(); }; // Convert entry id to values of startEntryId and startFamIndi fields HierarchyCreator.prototype.expandStartId = function (startEntryId) { if (startEntryId.isFam) return [startEntryId, null]; var indi = this.data.getIndi(startEntryId.id); if (!indi) throw new Error('Invalid startId'); var famsIds = indi.getFamiliesAsSpouse(); if (famsIds.length) return [EntryId.fam(famsIds[0]), startEntryId.id]; return [startEntryId, null]; }; HierarchyCreator.prototype.createHierarchy = function () { var upRoot = this.idToNode(this.startEntryId, null, null, false); var downRoot = this.idToNode(this.startEntryId, null, null, false); if (!upRoot || !downRoot) throw new Error('Invalid root node'); if (this.startFamIndi) { upRoot.indi = { id: this.startFamIndi }; downRoot.indi = { id: this.startFamIndi }; } var queue = [upRoot, downRoot]; while (queue.length) { var node = queue.shift(); var filter = node === upRoot ? HierarchyCreator.UP_FILTER : node === downRoot ? HierarchyCreator.DOWN_FILTER : HierarchyCreator.ALL_ACCEPTING_FILTER; //TODO: Filter only on root node? this.fillNodeData(node, filter); for (var _i = 0, _a = node.childNodes.getAll(); _i < _a.length; _i++) { var childNode = _a[_i]; queue.push(childNode); } } var getChildNodes = function (node) { var childNodes = node.childNodes.getAll(); return childNodes.length ? childNodes : null; }; return { upRoot: (0, d3_hierarchy_1.hierarchy)(upRoot, getChildNodes), downRoot: (0, d3_hierarchy_1.hierarchy)(downRoot, getChildNodes), }; }; HierarchyCreator.prototype.fillNodeData = function (node, filter) { if (this.isFamNode(node)) { var fam = this.data.getFam(node.id); var _a = node.indi && node.indi.id === fam.getMother() ? [fam.getMother(), fam.getFather()] : [fam.getFather(), fam.getMother()], indiId = _a[0], spouseId = _a[1]; Object.assign(node, { id: this.idGenerator.getId(node.id), indi: indiId && { id: indiId }, spouse: spouseId && { id: spouseId }, }); if (!node.duplicateOf && !node.duplicated) { node.childNodes = this.childNodesForFam(fam, node, filter); } } else { var indi = this.data.getIndi(node.id); Object.assign(node, { id: this.idGenerator.getId(node.id), indi: { id: indi.getId() }, }); if (!node.duplicateOf && !node.duplicated) { node.childNodes = this.childNodesForIndi(indi, node, filter); } } node.linkStubs = this.createLinkStubs(node); }; HierarchyCreator.prototype.childNodesForFam = function (fam, parentNode, filter) { var indi = parentNode.indi ? this.data.getIndi(parentNode.indi.id) : null; var spouse = parentNode.spouse ? this.data.getIndi(parentNode.spouse.id) : null; var _a = this.getParentsAndSiblings(indi), indiParentsFamsIds = _a[0], indiSiblingsIds = _a[1]; var _b = this.getParentsAndSiblings(spouse), spouseParentsFamsIds = _b[0], spouseSiblingsIds = _b[1]; var childrenIds = fam.getChildren(); return new api_1.ChildNodes({ indiParents: filter.indiParents ? this.famAsSpouseIdsToNodes(indiParentsFamsIds, parentNode, api_1.LinkType.IndiParents) : [], indiSiblings: filter.indiSiblings ? this.indiIdsToFamAsSpouseNodes(indiSiblingsIds, parentNode, api_1.LinkType.IndiSiblings) : [], spouseParents: filter.spouseParents ? this.famAsSpouseIdsToNodes(spouseParentsFamsIds, parentNode, api_1.LinkType.SpouseParents) : [], spouseSiblings: filter.spouseSiblings ? this.indiIdsToFamAsSpouseNodes(spouseSiblingsIds, parentNode, api_1.LinkType.SpouseSiblings) : [], children: filter.children ? this.indiIdsToFamAsSpouseNodes(childrenIds, parentNode, api_1.LinkType.Children) : [], }); }; HierarchyCreator.prototype.childNodesForIndi = function (indi, parentNode, filter) { var _a = this.getParentsAndSiblings(indi), indiParentsFamsIds = _a[0], indiSiblingsIds = _a[1]; return new api_1.ChildNodes({ indiParents: filter.indiParents ? this.famAsSpouseIdsToNodes(indiParentsFamsIds, parentNode, api_1.LinkType.IndiParents) : [], indiSiblings: filter.indiSiblings ? this.indiIdsToFamAsSpouseNodes(indiSiblingsIds, parentNode, api_1.LinkType.IndiSiblings) : [], }); }; HierarchyCreator.prototype.areParentsAndSiblingsPresent = function (indiId) { var indi = indiId && this.data.getIndi(indiId); var famcId = indi && indi.getFamilyAsChild(); var famc = famcId && this.data.getFam(famcId); if (!famc) return [false, false]; return [ !!(famc.getFather() || famc.getMother()), famc.getChildren().length > 1, ]; }; HierarchyCreator.prototype.getParentsAndSiblings = function (indi) { var indiFamcId = indi && indi.getFamilyAsChild(); var indiFamc = this.data.getFam(indiFamcId); if (!indiFamc) return [[], []]; var father = this.data.getIndi(indiFamc.getFather()); var mother = this.data.getIndi(indiFamc.getMother()); var parentFamsIds = [] .concat(father ? father.getFamiliesAsSpouse() : [], mother ? mother.getFamiliesAsSpouse() : []) .filter(function (id) { return id !== indiFamcId; }); parentFamsIds.unshift(indiFamcId); var siblingsIds = Array.from(indiFamc.getChildren()); siblingsIds.splice(siblingsIds.indexOf(indi.getId()), 1); // Remove indi from indi's siblings return [parentFamsIds, siblingsIds]; }; HierarchyCreator.prototype.indiIdsToFamAsSpouseNodes = function (indiIds, parentNode, childNodeType) { var _this = this; return indiIds.flatMap(function (id) { return _this.indiIdToFamAsSpouseNodes(id, parentNode, childNodeType); }); }; HierarchyCreator.prototype.indiIdToFamAsSpouseNodes = function (indiId, parentNode, childNodeType) { var _this = this; if (this.isChildNodeTypeForbidden(childNodeType, parentNode)) return []; var famsIds = this.data.getIndi(indiId).getFamiliesAsSpouse(); if (!famsIds.length) { var node = this.idToNode(EntryId.indi(indiId), parentNode, childNodeType); return node ? [node] : []; } var famsNodes = famsIds.map(function (id) { return { id: id, indi: { id: indiId }, family: { id: id }, parentNode: parentNode, linkFromParentType: childNodeType, childNodes: api_1.ChildNodes.EMPTY, linkStubs: [], }; }); famsNodes.forEach(function (node, i) { if (i !== 0) node.primaryMarriage = famsNodes[0]; var duplicateOf = _this.queuedNodesById.get(node.id); if (duplicateOf) { node.duplicateOf = duplicateOf; duplicateOf.duplicated = true; } else _this.queuedNodesById.set(node.id, node); }); return famsNodes; }; HierarchyCreator.prototype.famAsSpouseIdsToNodes = function (famsIds, parentNode, childNodeType) { var nodes = this.idsToNodes(famsIds.map(EntryId.fam), parentNode, childNodeType); nodes.slice(1).forEach(function (node) { return (node.primaryMarriage = nodes[0]); }); return nodes; }; HierarchyCreator.prototype.idsToNodes = function (entryIds, parentNode, childNodeType, duplicateCheck) { var _this = this; if (duplicateCheck === void 0) { duplicateCheck = true; } return entryIds .map(function (entryId) { return _this.idToNode(entryId, parentNode, childNodeType, duplicateCheck); }) .filter(function (node) { return node != null; }); }; HierarchyCreator.prototype.idToNode = function (entryId, parentNode, childNodeType, duplicateCheck) { if (duplicateCheck === void 0) { duplicateCheck = true; } if (this.isChildNodeTypeForbidden(childNodeType, parentNode)) return null; var id = entryId.id, isFam = entryId.isFam; if (isFam) { var fam = this.data.getFam(id); if (!fam || (!fam.getFather() && !fam.getMother())) return null; // Don't create fam nodes that are missing both husband and wife } var duplicateOf = this.queuedNodesById.get(id); var node = { id: id, parentNode: parentNode, linkFromParentType: childNodeType, childNodes: api_1.ChildNodes.EMPTY, linkStubs: [], }; if (isFam) node.family = { id: id }; if (duplicateCheck && duplicateOf) { node.duplicateOf = duplicateOf; duplicateOf.duplicated = true; } if (!duplicateOf) this.queuedNodesById.set(id, node); return node; }; HierarchyCreator.prototype.createLinkStubs = function (node) { var _this = this; if (!this.isFamNode(node) || (!node.duplicateOf && !node.duplicated && !node.primaryMarriage)) { return []; } var fam = this.data.getFam(node.family.id); var _a = this.areParentsAndSiblingsPresent(node.indi ? node.indi.id : null), indiParentsPresent = _a[0], indiSiblingsPresent = _a[1]; var _b = this.areParentsAndSiblingsPresent(node.spouse ? node.spouse.id : null), spouseParentsPresent = _b[0], spouseSiblingsPresent = _b[1]; var childrenPresent = (0, utils_1.nonEmpty)(fam.getChildren()); return [ indiParentsPresent ? [api_1.LinkType.IndiParents] : [], indiSiblingsPresent ? [api_1.LinkType.IndiSiblings] : [], spouseParentsPresent ? [api_1.LinkType.SpouseParents] : [], spouseSiblingsPresent ? [api_1.LinkType.SpouseSiblings] : [], childrenPresent ? [api_1.LinkType.Children] : [], ] .flat() .filter(function (linkType) { return !_this.isChildNodeTypeForbidden(linkType, node) && !node.childNodes.get(linkType).length; }); }; HierarchyCreator.prototype.isChildNodeTypeForbidden = function (childNodeType, parentNode) { if (childNodeType === null || !parentNode) return false; switch ((0, api_1.otherSideLinkType)(parentNode.linkFromParentType)) { case api_1.LinkType.IndiParents: case api_1.LinkType.IndiSiblings: if (childNodeType === api_1.LinkType.IndiParents || childNodeType === api_1.LinkType.IndiSiblings) { return true; } break; case api_1.LinkType.Children: if (!parentNode.primaryMarriage && childNodeType === api_1.LinkType.Children) { return true; } break; } if (parentNode.primaryMarriage) { // Forbid indi/spouse from parentNode that is also indi/spouse in primaryMarriage from having parents and siblings, as they are already added to primaryMarriage node. This prevents drawing parents/siblings of a person for each marriage of this person. var indiId = parentNode.indi.id; var spouseId = parentNode.spouse.id; var pmIndiId = parentNode.primaryMarriage.indi.id; var pmSpouseId = parentNode.primaryMarriage.spouse.id; if (indiId === pmIndiId || indiId === pmSpouseId) { if (childNodeType === api_1.LinkType.IndiParents || childNodeType === api_1.LinkType.IndiSiblings) { return true; } } else if (spouseId === pmIndiId || spouseId === pmSpouseId) { if (childNodeType === api_1.LinkType.SpouseParents || childNodeType === api_1.LinkType.SpouseSiblings) { return true; } } } return false; }; HierarchyCreator.prototype.isFamNode = function (node) { return !!node.family; }; HierarchyCreator.UP_FILTER = hierarchy_filter_1.HierarchyFilter.allRejecting().modify({ indiParents: true, spouseParents: true, indiSiblings: true, spouseSiblings: true, }); HierarchyCreator.DOWN_FILTER = hierarchy_filter_1.HierarchyFilter.allRejecting().modify({ children: true, }); HierarchyCreator.ALL_ACCEPTING_FILTER = hierarchy_filter_1.HierarchyFilter.allAccepting(); return HierarchyCreator; }()); exports.HierarchyCreator = HierarchyCreator; /* Id of indi or fam */ var EntryId = /** @class */ (function () { function EntryId(indiId, famId) { if (!indiId && !famId) throw new Error('Invalid EntryId'); this.id = (indiId || famId); this.isFam = !!famId; } EntryId.indi = function (id) { return new EntryId(id, null); }; EntryId.fam = function (id) { return new EntryId(null, id); }; return EntryId; }()); exports.EntryId = EntryId; function getRootsCount(upRoot, data) { var upIndi = upRoot.data.indi && data.getIndi(upRoot.data.indi.id); var upSpouse = upRoot.data.spouse && data.getIndi(upRoot.data.spouse.id); return ((upIndi ? upIndi.getFamiliesAsSpouse().length : 0) + (upSpouse ? upSpouse.getFamiliesAsSpouse().length - 1 : 0)); }