UNPKG

topola

Version:

Topola – online genealogy visualization

309 lines (308 loc) 10.3 kB
"use strict"; /// <reference path="parse-gedcom.d.ts" /> Object.defineProperty(exports, "__esModule", { value: true }); exports.getDate = getDate; exports.gedcomToJson = gedcomToJson; exports.gedcomEntriesToJson = gedcomEntriesToJson; var parse_gedcom_1 = require("parse-gedcom"); /** Returns the first entry with the given tag or undefined if not found. */ function findTag(tree, tag) { return tree.find(function (entry) { return entry.tag === tag; }); } /** Returns all entries with the given tag. */ function findTags(tree, tag) { return tree.filter(function (entry) { return entry.tag === tag; }); } /** * Returns the identifier extracted from a pointer string. * E.g. '@I123@' -> 'I123' */ function pointerToId(pointer) { return pointer.substring(1, pointer.length - 1); } /** Extracts the first and last name from a GEDCOM name field. */ function extractName(name) { var arr = name.split('/'); if (arr.length === 1) { return { firstName: arr[0].trim() }; } return { firstName: arr[0].trim(), lastName: arr[1].trim() }; } /** Maps month abbreviations used in GEDCOM to month numbers. */ var MONTHS = new Map([ ['jan', 1], ['feb', 2], ['mar', 3], ['apr', 4], ['may', 5], ['jun', 6], ['jul', 7], ['aug', 8], ['sep', 9], ['oct', 10], ['nov', 11], ['dec', 12], ]); /** Parses the GEDCOM date into the Date structure. */ function parseDate(parts) { if (!parts || !parts.length) { return undefined; } var result = {}; // Remove parentheses if they surround the text. if (parts[0].startsWith('(') && parts[parts.length - 1].endsWith(')')) { parts[0] = parts[0].substring(1); var lastPart = parts[parts.length - 1]; parts[parts.length - 1] = lastPart.substring(0, lastPart.length - 1); } var fullText = parts.join(' '); var firstPart = parts[0].toLowerCase(); if (firstPart === 'cal' || firstPart === 'abt' || firstPart === 'est') { result.qualifier = firstPart; parts = parts.slice(1); } if (parts.length && parts[parts.length - 1].match(/^\d{1,4}$/)) { result.year = Number(parts[parts.length - 1]); parts = parts.slice(0, parts.length - 1); } if (parts.length) { var lastPart = parts[parts.length - 1].toLowerCase(); if (MONTHS.has(lastPart)) { result.month = MONTHS.get(lastPart); parts = parts.slice(0, parts.length - 1); } } if (parts.length && parts[0].match(/^\d\d?$/)) { result.day = Number(parts[0]); parts = parts.slice(0, parts.length - 1); } if (parts.length) { // A part didn't get parsed. Return the whole text verbatim. return { text: fullText }; } return result; } /** Parses a GEDCOM date or date range. */ function getDate(gedcomDate) { var parts = gedcomDate.replace(/@.*@/, '').trim().split(' '); var firstPart = parts[0].toLowerCase(); if (firstPart.startsWith('bet')) { var i = parts.findIndex(function (x) { return x.toLowerCase() === 'and'; }); var from = parseDate(parts.slice(1, i)); var to = parseDate(parts.slice(i + 1)); var dateRange = {}; if (from) { dateRange.from = from; } if (to) { dateRange.to = to; } return { dateRange: dateRange }; } if (firstPart.startsWith('bef') || firstPart.startsWith('aft')) { var date_1 = parseDate(parts.slice(1)); if (!date_1) { return undefined; } if (firstPart.startsWith('bef')) { return { dateRange: { to: date_1 } }; } return { dateRange: { from: date_1 } }; } var date = parseDate(parts); if (date) { return { date: date }; } return undefined; } /** * tries to treat an input tag as NOTE and parsse all lines of notes */ function createNotes(notesTag) { if (!notesTag || notesTag.tag !== 'NOTE') return undefined; return findTags(notesTag.tree, 'CONT') .filter(function (x) { return x.data; }) .reduce(function (a, i) { return a.concat(i.data); }, [notesTag.data]); } /** * Creates a JsonEvent object from a GEDCOM entry. * Used for BIRT, DEAT and MARR tags. */ function createEvent(entry) { if (!entry) { return undefined; } var typeTag = findTag(entry.tree, 'TYPE'); var dateTag = findTag(entry.tree, 'DATE'); var placeTag = findTag(entry.tree, 'PLAC'); var date = dateTag && dateTag.data && getDate(dateTag.data); var place = placeTag && placeTag.data; if (date || place) { var result = date || {}; if (place) { result.place = place; } result.confirmed = true; if (typeTag) { result.type = typeTag.data; } var notes = createNotes(findTag(entry.tree, 'NOTE')); if (notes) { result.notes = notes; } return result; } if (entry.data && entry.data.toLowerCase() === 'y') { return { confirmed: true }; } return undefined; } /** Creates a JsonIndi object from an INDI entry in GEDCOM. */ function createIndi(entry, objects, existingIds) { var id = pointerToId(entry.pointer); var fams = findTags(entry.tree, 'FAMS') .map(function (entry) { return pointerToId(entry.data); }) .filter(function (id) { return existingIds.has(id); }); var indi = { id: id, fams: fams }; // Name. var nameTags = findTags(entry.tree, 'NAME'); var isMaiden = function (nameTag) { var type = findTag(nameTag.tree, 'TYPE'); return type !== undefined && type.data === 'maiden'; }; var main = nameTags.find(function (x) { return !isMaiden(x); }); var maiden = nameTags.find(isMaiden); if (main) { var _a = extractName(main.data), firstName = _a.firstName, lastName = _a.lastName; if (firstName) { indi.firstName = firstName; } if (lastName) { indi.lastName = lastName; } } if (maiden) { var _b = extractName(maiden.data), firstName = _b.firstName, lastName = _b.lastName; if (lastName) { indi.maidenName = lastName; } if (firstName && !indi.firstName) { indi.firstName = firstName; } } // Number of children. var nchiTag = findTag(entry.tree, 'NCHI'); if (nchiTag) { indi.numberOfChildren = +nchiTag.data; } // Number of marriages. var nmrTag = findTag(entry.tree, 'NMR'); if (nmrTag) { indi.numberOfMarriages = +nmrTag.data; } // Sex. var sexTag = findTag(entry.tree, 'SEX'); if (sexTag) { indi.sex = sexTag.data; } // Family with parents. var famcTag = findTag(entry.tree, 'FAMC'); if (famcTag) { var id_1 = pointerToId(famcTag.data); if (existingIds.has(id_1)) { indi.famc = id_1; } } // Image URL. var objeTags = findTags(entry.tree, 'OBJE'); if (objeTags.length > 0) { // Dereference OBJEct if needed. var getFileTag = function (tag) { var realObjeTag = tag.data ? objects.get(pointerToId(tag.data)) : tag; if (!realObjeTag) return undefined; var file = findTag(realObjeTag.tree, 'FILE'); var title = findTag(realObjeTag.tree, 'TITL'); if (!file) return undefined; return { url: file.data, title: title && title.data, }; }; indi.images = objeTags .map(getFileTag) .filter(function (x) { return x !== undefined; }); } // Birth date and place. var birth = createEvent(findTag(entry.tree, 'BIRT')); if (birth) { indi.birth = birth; } // Death date and place. var death = createEvent(findTag(entry.tree, 'DEAT')); if (death) { indi.death = death; } // Notes. var notes = createNotes(findTag(entry.tree, 'NOTE')); if (notes) { indi.notes = notes; } // Events indi.events = findTags(entry.tree, 'EVEN') .map(createEvent) .filter(function (x) { return x !== null; }); return indi; } /** Creates a JsonFam object from an FAM entry in GEDCOM. */ function createFam(entry, existingIds) { var id = pointerToId(entry.pointer); var children = findTags(entry.tree, 'CHIL') .map(function (entry) { return pointerToId(entry.data); }) .filter(function (id) { return existingIds.has(id); }); var fam = { id: id, children: children }; // Husband. var husbTag = findTag(entry.tree, 'HUSB'); if (husbTag) { var id_2 = pointerToId(husbTag.data); if (existingIds.has(id_2)) { fam.husb = pointerToId(husbTag.data); } } // Wife. var wifeTag = findTag(entry.tree, 'WIFE'); if (wifeTag) { var id_3 = pointerToId(wifeTag.data); if (existingIds.has(id_3)) { fam.wife = pointerToId(wifeTag.data); } } // Marriage var marriage = createEvent(findTag(entry.tree, 'MARR')); if (marriage) { fam.marriage = marriage; } return fam; } /** Creates a map from ID to entry from an array of entries. */ function createMap(entries) { return new Map(entries.map(function (entry) { return [pointerToId(entry.pointer), entry]; })); } /** Parses a GEDCOM file into a JsonGedcomData structure. */ function gedcomToJson(gedcomContents) { return gedcomEntriesToJson((0, parse_gedcom_1.parse)(gedcomContents)); } /** Converts parsed GEDCOM entries into a JsonGedcomData structure. */ function gedcomEntriesToJson(gedcom) { var objects = createMap(findTags(gedcom, 'OBJE')); var existingIds = new Set(gedcom.map(function (entry) { return pointerToId(entry.pointer); }).filter(function (id) { return !!id; })); var indis = findTags(gedcom, 'INDI').map(function (entry) { return createIndi(entry, objects, existingIds); }); var fams = findTags(gedcom, 'FAM').map(function (entry) { return createFam(entry, existingIds); }); return { indis: indis, fams: fams }; }