@treeviz/gedcom-parser 2.1.12 → 2.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -104,244 +104,6 @@ var SPOUSE = {
104
104
  PART: "spouse" /* SPOUSE */
105
105
  };
106
106
 
107
- // src/utils/get-all-prop.ts
108
- var getAllProp = (indi, prop) => {
109
- return prop && (indi?.get(prop)?.toList().values() ?? []).filter(Boolean) || [];
110
- };
111
-
112
- // src/utils/date-formatter.ts
113
- var ACCEPTED_DATE_FORMATS = [
114
- "yyyy.MM.dd.",
115
- "yyyy-MM-dd",
116
- "MM/dd/yyyy"
117
- ];
118
- var ACCEPTED_DATE_FORMATS_REGEX = new RegExp(
119
- `(${ACCEPTED_DATE_FORMATS.map(
120
- (acceptedFormat) => acceptedFormat.replace(/[^.\-/]/g, "\\d")
121
- ).join("|")})`,
122
- "gi"
123
- );
124
- var commonDateFormatter = (date, format2 = "yyyy", prefix = "") => {
125
- const formattedDate = date?.toValue(format2);
126
- if (!formattedDate) {
127
- return void 0;
128
- }
129
- return `${prefix}${formattedDate}`.trim();
130
- };
131
- var noteDateFormatter = (date, format2 = "yyyy", prefix = "", shortNote = true, showNote = true) => {
132
- const rawDate = commonDateFormatter(date, format2, "");
133
- if (!rawDate) {
134
- return void 0;
135
- }
136
- const note = date?.toNote(shortNote) ?? "";
137
- return `${prefix}${note && showNote ? i18n.t(`${note} {{date}}`, { date: rawDate }) : rawDate}`;
138
- };
139
- var marriageDateFormatter = (fam, showDays = false, showPlaces = false, shortNote = true, showNote = true, showDisplayTextIfNoDate = true, showAllEvents = false, dateFormatPattern) => {
140
- const fullDateFormat = i18n.t("dateFormat");
141
- const marriageDates = [];
142
- const marriagePlaces = [];
143
- const marriages = (fam?.toValueList().values() ?? []).flatMap((fam2) => {
144
- const family = fam2;
145
- const marrEvents = (family?.MARR?.toList().values() ?? []).filter(
146
- Boolean
147
- );
148
- if (marrEvents.length === 0) {
149
- const displayText = family?.get("DISPLAYTEXT")?.toValue() ?? "";
150
- if (showDisplayTextIfNoDate && displayText) {
151
- const displayDate = displayText.match(
152
- ACCEPTED_DATE_FORMATS_REGEX
153
- )?.[0];
154
- if (displayDate) {
155
- marriagePlaces.push(displayText.replace(displayDate, ""));
156
- const marriageDate = createCommonDate(
157
- fam2?.getGedcom(),
158
- void 0,
159
- family
160
- );
161
- marriageDate.value = displayDate;
162
- marriageDates.push(marriageDate);
163
- } else {
164
- marriagePlaces.push(displayText);
165
- marriageDates.push(void 0);
166
- return ["\u221E"];
167
- }
168
- }
169
- return [];
170
- }
171
- const eventsToProcess = showAllEvents ? marrEvents : marrEvents.slice(0, 1);
172
- return eventsToProcess.map((marrEvent) => {
173
- const marriageEventDetail = marrEvent;
174
- const marriageDate = marriageEventDetail?.DATE;
175
- const marriagePlace = showPlaces ? marriageEventDetail?.PLAC?.value : void 0;
176
- marriagePlaces.push(marriagePlace);
177
- marriageDates.push(marriageDate);
178
- const formattedDate = noteDateFormatter(
179
- marriageDate,
180
- showDays ? fullDateFormat : "yyyy",
181
- "\u221E",
182
- shortNote,
183
- showNote
184
- );
185
- return formattedDate ? formattedDate : void 0;
186
- });
187
- });
188
- const marriageArray = marriages.length ? marriages.filter(Boolean) : [];
189
- const datesArray = marriageArray.filter(Boolean);
190
- const placesArray = marriagePlaces.filter(Boolean);
191
- const dates = datesArray.join(" ").trim();
192
- return {
193
- rawArray: marriageDates,
194
- inArray: datesArray,
195
- inOrder: dates,
196
- places: placesArray,
197
- marriage: marriageArray,
198
- ...showPlaces ? {
199
- marriages,
200
- marriagePlaces
201
- } : {}
202
- };
203
- };
204
- var dateFormatter = (indi, showMarriages = false, showDays = false, showPlaces = false, shortNote = true, showNote = true, isOnStage, showAllEvents = false, dateFormatPattern) => {
205
- const fullDateFormat = i18n.t("dateFormat");
206
- const birthEvents = getAllProp(indi, "BIRT");
207
- const firstBirthValue = birthEvents[0];
208
- const deathEvents = getAllProp(indi, "DEAT");
209
- const firstDeathValue = deathEvents[0];
210
- const birth = commonDateFormatter(
211
- firstBirthValue?.DATE,
212
- showDays ? fullDateFormat : "yyyy"
213
- );
214
- const birthPlace = showPlaces ? firstBirthValue?.PLAC?.value : void 0;
215
- const death = commonDateFormatter(
216
- firstDeathValue?.DATE,
217
- showDays ? fullDateFormat : "yyyy"
218
- );
219
- const deathPlace = showPlaces ? firstDeathValue?.PLAC?.value : void 0;
220
- const marriagePlaces = [];
221
- const marriages = [];
222
- if (showMarriages || isOnStage) {
223
- (indi?.get("FAMS")?.toValueList().values() ?? []).forEach((fam) => {
224
- const famRef = fam?.ref;
225
- const marriageDates = marriageDateFormatter(
226
- famRef,
227
- showDays,
228
- showPlaces,
229
- shortNote,
230
- showNote,
231
- true,
232
- showAllEvents);
233
- marriages.push(...marriageDates?.marriage ?? []);
234
- marriagePlaces.push(...marriageDates?.marriagePlaces ?? []);
235
- });
236
- }
237
- const birthStrings = [];
238
- const birthPlaces = [];
239
- const eventsToProcess = showAllEvents ? birthEvents : birthEvents.slice(0, 1);
240
- eventsToProcess.forEach((birthEvent) => {
241
- const birthDate = commonDateFormatter(
242
- birthEvent?.DATE,
243
- showDays ? fullDateFormat : "yyyy"
244
- );
245
- const birthEventNote = birthEvent?.DATE?.toNote(shortNote) ?? "";
246
- const birthEventPlace = showPlaces ? birthEvent?.PLAC?.value : void 0;
247
- if (birthDate) {
248
- birthStrings.push(
249
- `*${birthEventNote && showNote ? i18n.t(`${birthEventNote} {{date}}`, {
250
- date: birthDate
251
- }) : birthDate}`
252
- );
253
- }
254
- birthPlaces.push(birthEventPlace);
255
- });
256
- const deathStrings = [];
257
- const deathPlaces = [];
258
- const deathEventsToProcess = showAllEvents ? deathEvents : deathEvents.slice(0, 1);
259
- deathEventsToProcess.forEach((deathEvent) => {
260
- const deathDate = commonDateFormatter(
261
- deathEvent?.DATE,
262
- showDays ? fullDateFormat : "yyyy"
263
- );
264
- const deathEventNote = deathEvent?.DATE?.toNote(shortNote) ?? "";
265
- const deathEventPlace = showPlaces ? deathEvent?.PLAC?.value : void 0;
266
- if (deathDate) {
267
- deathStrings.push(
268
- `\u2020${deathEventNote && showNote ? i18n.t(`${deathEventNote} {{date}}`, {
269
- date: deathDate
270
- }) : deathDate}`
271
- );
272
- }
273
- deathPlaces.push(deathEventPlace);
274
- });
275
- const birthString = birthStrings[0] ?? "";
276
- const deathString = deathStrings[0] ?? "";
277
- const marriageArray = marriages.length ? marriages.filter(Boolean) : [];
278
- const datesArray = !birth && !death ? marriageArray : [...birthStrings, ...marriageArray, ...deathStrings];
279
- const placesArray = !birth && !death ? marriagePlaces : [...birthPlaces, ...marriagePlaces, ...deathPlaces];
280
- const nonNullDatesArray = datesArray.filter(Boolean);
281
- const dates = nonNullDatesArray.join(" ").trim();
282
- return {
283
- inArray: nonNullDatesArray,
284
- inOrder: dates,
285
- places: placesArray,
286
- birth: birthString,
287
- marriage: marriageArray,
288
- death: deathString,
289
- ...showPlaces ? {
290
- birthPlace,
291
- deathPlace,
292
- ...showMarriages ? { marriages, marriagePlaces } : {}
293
- } : {},
294
- ...showAllEvents ? {
295
- births: birthStrings,
296
- birthPlaces,
297
- deaths: deathStrings,
298
- deathPlaces
299
- } : {}
300
- };
301
- };
302
-
303
- // src/utils/range.ts
304
- var toTuple = (r) => {
305
- if (Array.isArray(r)) {
306
- const [s2, e2] = r;
307
- return [
308
- s2 === void 0 ? void 0 : Number(s2),
309
- e2 === void 0 ? void 0 : Number(e2)
310
- ];
311
- }
312
- if (r === "-" || r === "") return [void 0, void 0];
313
- const [s, e] = r.split("-");
314
- return [s ? Number(s) : void 0, e ? Number(e) : void 0];
315
- };
316
- var isValidRange = (range) => {
317
- if (!Array.isArray(range) && range !== "" && range !== "-" && !/^\d*-\d*$/.test(range)) {
318
- return false;
319
- }
320
- if (Array.isArray(range) && range.some((r) => typeof r === "string" && isNaN(Number(r)))) {
321
- return false;
322
- }
323
- return true;
324
- };
325
- var inRange = (year, range, trueIfNoYear = false) => {
326
- if (!isValidRange(range)) {
327
- return false;
328
- }
329
- if (!year) {
330
- return trueIfNoYear;
331
- }
332
- const [start = -Infinity, end = Infinity] = toTuple(range);
333
- if (!start && !end) {
334
- return true;
335
- }
336
- if (start && !end) {
337
- return Number(year) >= Number(start);
338
- }
339
- if (!start && end) {
340
- return Number(year) <= Number(end);
341
- }
342
- return Number(start) <= Number(year) && Number(year) <= Number(end);
343
- };
344
-
345
107
  // src/constants/constants.ts
346
108
  var ID_REG = /^@[_a-zA-Z0-9]+@/;
347
109
  var ID_GETTER_REG = /^(?<at>@)?(?<letter>[A-Z])?/i;
@@ -426,1087 +188,1355 @@ var objeXrefKey = (obje, fallback = 0) => {
426
188
  return `@O${fallback}@`;
427
189
  };
428
190
  var objeNoteValue = (obje) => mediaIdValue(obje, "FILE.NOTE") || mediaIdValue(obje, "NOTE") || "";
429
- var List = class _List {
430
- // length = 0;
431
- constructor(items) {
432
- this.items = {};
191
+
192
+ // src/classes/common.ts
193
+ var Common = class _Common {
194
+ constructor(gedcom, id, main, parent) {
433
195
  this.isListable = true;
434
- this.marker = void 0;
435
- if (items) {
436
- this.concat(items);
196
+ this._gedcom = gedcom;
197
+ this._main = main;
198
+ this._parent = parent;
199
+ this.initUniqueId();
200
+ if (id) {
201
+ this._id = id;
202
+ } else {
203
+ delete this._id;
437
204
  }
438
205
  }
439
- get length() {
440
- return this.values().length;
441
- }
442
- has(indi) {
443
- return !!this.item(
444
- typeof indi === "string" ? indi : indi?.id ?? ""
445
- );
206
+ set type(type) {
207
+ if (type) {
208
+ this.uniqueId = this.uniqueId?.replace(/^[^:]+/, `~${type}`);
209
+ }
210
+ this._type = type;
446
211
  }
447
- get(name) {
448
- const propList = new _List();
449
- this.entries().forEach(([key, item]) => {
450
- propList.item(key, item.get(name));
451
- });
452
- return propList;
212
+ get type() {
213
+ return this._type;
453
214
  }
454
- getIf(name, condition, name2) {
455
- const propList = new _List();
456
- this.entries().forEach(([key, item]) => {
457
- const passedItem = item.getIf(name, condition, name2);
458
- if (passedItem) {
459
- propList.item(key, passedItem);
215
+ initUniqueId() {
216
+ if (this._gedcom) {
217
+ this.uniqueId = `~Common:${this._gedcom.refcount++}`;
218
+ }
219
+ }
220
+ set uniqueId(id) {
221
+ const currentId = this._uniqueId;
222
+ if (this._gedcom?.reflist && id) {
223
+ this._uniqueId = id;
224
+ if (currentId && this._gedcom.reflist[currentId]) {
225
+ delete this._gedcom.reflist[currentId];
460
226
  }
461
- });
462
- return propList;
227
+ this._gedcom.reflist[id] = this;
228
+ }
463
229
  }
464
- keys() {
465
- return Object.keys(this.items);
230
+ get uniqueId() {
231
+ return this._uniqueId;
466
232
  }
467
- values() {
468
- return Object.values(this.items);
233
+ set id(id) {
234
+ this._id = idGetter(id);
469
235
  }
470
- entries() {
471
- return Object.entries(this.items);
236
+ get id() {
237
+ return idGetter(this._id);
472
238
  }
473
- setLength(_type) {
239
+ set value(value) {
240
+ this._value = value;
474
241
  }
475
- first() {
476
- return this.index(0);
242
+ get value() {
243
+ return this._value;
477
244
  }
478
- last() {
479
- return this.index(this.keys().length - 1);
245
+ /**
246
+ * Set the gedcom reference for this object
247
+ * @param gedcom - The GedComType to set as the gedcom reference
248
+ */
249
+ setGedcom(gedcom) {
250
+ this._gedcom = gedcom;
480
251
  }
481
- index(index) {
482
- const keys = this.keys();
483
- const key = keys[index];
484
- return key && this.items[key];
252
+ // avoid to override
253
+ get originalValue() {
254
+ return this._value;
485
255
  }
486
- item(name, value) {
487
- if (arguments.length > 1) {
488
- set(this.items, name, value);
489
- this.setLength();
256
+ get ref() {
257
+ if (typeof this.value === "string" && isId(this.value) && this.refType) {
258
+ return this._gedcom?.[getListTag(this.refType)]?.item(
259
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
260
+ this.value
261
+ );
490
262
  }
491
- return get(this.items, name);
263
+ return this;
492
264
  }
493
- removeItem(name) {
494
- unset(this.items, name);
495
- this.setLength();
265
+ get main() {
266
+ return this._main;
496
267
  }
497
- exclude(excludedList) {
498
- excludedList.keys().forEach((itemId) => {
499
- delete this.items[itemId];
500
- });
501
- this.setLength();
502
- return this;
268
+ get parent() {
269
+ return this._parent;
503
270
  }
504
- concat(items) {
505
- if (Array.isArray(items)) {
506
- items.forEach((item) => {
507
- this.append(item);
508
- });
509
- } else {
510
- Object.assign(this.items, items ?? {});
271
+ addRef(refNode) {
272
+ if (typeof refNode.value === "string" && isId(refNode.value) && refNode.refType) {
273
+ if (!this._refs) {
274
+ this._refs = new List();
275
+ }
276
+ this._refs.item(refNode.value, refNode);
511
277
  }
512
- this.setLength();
513
- return this;
514
278
  }
515
- merge(mergedList) {
516
- this.concat(mergedList?.items);
517
- return this;
279
+ getRefs() {
280
+ return this._refs;
518
281
  }
519
- intersection(mergedList) {
520
- const inter = intersectionBy(
521
- this.values(),
522
- mergedList?.values() ?? [],
523
- "id"
524
- );
525
- return new _List(inter);
282
+ exportValue() {
283
+ return this.value;
526
284
  }
527
- difference(mergedList) {
528
- const inter = differenceBy(
529
- this.values(),
530
- mergedList?.values() ?? [],
531
- "id"
532
- );
533
- return new _List(inter);
285
+ removeValue() {
286
+ delete this._value;
534
287
  }
535
- prepend(item) {
536
- if (item.id) {
537
- let increase = true;
538
- if (this.items[item.id]) {
539
- delete this.items[item.id];
540
- increase = false;
541
- }
542
- this.items = Object.assign(
543
- {
544
- [item.id]: item
545
- },
546
- this.items
547
- );
548
- if (increase) {
549
- this.setLength("increase");
550
- }
288
+ set(name, value) {
289
+ if (isGedcomTrailerTag(name)) {
290
+ return void 0;
551
291
  }
552
- return this;
292
+ if (typeof value === "string") {
293
+ const usedValue = createCommon(this._gedcom, void 0, this.main);
294
+ usedValue.value = value;
295
+ set(this, name, usedValue);
296
+ } else {
297
+ set(this, name, value);
298
+ }
299
+ return get(this, name);
553
300
  }
554
- append(item) {
555
- if (item.id) {
556
- let increase = true;
557
- if (this.items[item.id]) {
558
- delete this.items[item.id];
559
- increase = false;
560
- }
561
- this.items[item.id] = item;
562
- if (increase) {
563
- this.setLength("increase");
301
+ assign(name, value, unique = false) {
302
+ if (isGedcomTrailerTag(name)) {
303
+ return void 0;
304
+ }
305
+ let curValue = this.get(name);
306
+ if (curValue?.isListable) {
307
+ if (curValue instanceof _Common) {
308
+ curValue = new List().concat({ ...[curValue] });
309
+ this.set(name, curValue);
564
310
  }
311
+ const values = [
312
+ ...curValue.values(),
313
+ ...value instanceof List ? value.values() : [value]
314
+ ];
315
+ curValue.concat({
316
+ ...!unique ? values : uniqBy(
317
+ values,
318
+ (value2) => value2 instanceof _Common ? value2?.value : void 0
319
+ )
320
+ });
321
+ } else {
322
+ this.set(name, value);
565
323
  }
566
- return this;
324
+ return get(this, name);
567
325
  }
568
- delete(item) {
569
- if (item.id && this.items[item.id]) {
570
- delete this.items[item.id];
571
- this.setLength("decrease");
326
+ get(name) {
327
+ if (!name.includes(".")) {
328
+ return get(this, name);
572
329
  }
573
- return this;
574
- }
575
- copy(ClassName = _List) {
576
- const newList = new ClassName();
577
- Object.assign(newList.items, this.items);
578
- newList.setLength();
579
- return newList;
580
- }
581
- except(item, ClassName = _List) {
582
- return this.copy(ClassName).delete(item);
330
+ const nameParts = name.split(".");
331
+ let last = this;
332
+ for (const part of nameParts) {
333
+ const indexPart = Number(part);
334
+ const itemIndex = part.match(/items\[(?<itemIndex>\d+)\]/)?.groups?.itemIndex;
335
+ const itemId = part.match(/items\[(?<itemId>@[A-Z]+\d+@)\]/)?.groups?.itemId;
336
+ const usedPart = itemId || itemIndex;
337
+ if (last instanceof List) {
338
+ if (!isNaN(indexPart)) {
339
+ last = last.index(indexPart);
340
+ } else {
341
+ if (usedPart) {
342
+ last = last.item(usedPart);
343
+ } else {
344
+ last = last?.index(0)?.get(part);
345
+ }
346
+ }
347
+ } else {
348
+ const usedIndex = Number(itemIndex ?? indexPart);
349
+ if (!isNaN(usedIndex)) {
350
+ last = last?.toList().index(usedIndex);
351
+ } else {
352
+ last = last?.get(itemId ?? part);
353
+ }
354
+ }
355
+ if (!last) {
356
+ break;
357
+ }
358
+ }
359
+ return last;
583
360
  }
584
- forEach(iterate) {
585
- this.entries().forEach(([key, item], index) => {
586
- iterate(item, key, index);
587
- });
361
+ remove(name) {
362
+ unset(this, name);
588
363
  }
589
- map(iterate) {
590
- return this.entries().map(([key, item], index) => {
591
- return iterate(item, key, index);
592
- });
364
+ getGedcom() {
365
+ return this._gedcom;
593
366
  }
594
- reduce(iterate, initialValue) {
595
- return this.entries().reduce((acc, [key, item], index) => {
596
- return iterate(acc, item, key, index);
597
- }, initialValue);
367
+ getIf(name, condition, name2) {
368
+ const nameParts = name.split(".");
369
+ const name2Parts = name2.split(".");
370
+ if (nameParts.length !== name2Parts.length) {
371
+ throw new Error("Tag pathes must be of the same depth");
372
+ }
373
+ if (nameParts.length > 1 && nameParts[0] !== name2Parts[0]) {
374
+ throw new Error("Final tags must be siblings");
375
+ }
376
+ const valueCommon = this.get(name);
377
+ if (valueCommon instanceof _Common) {
378
+ return valueCommon?.toValue() !== condition ? void 0 : this.get(name2);
379
+ }
380
+ const valueCommon2 = this.get(name2)?.toList();
381
+ const ifList = new List();
382
+ valueCommon?.forEach((item, key) => {
383
+ const pair = valueCommon2?.item(key);
384
+ ifList.item(key, item?.toValue() !== condition ? void 0 : pair);
385
+ });
386
+ return ifList;
598
387
  }
599
- filter(filters, ClassName = _List) {
600
- const newList = new ClassName();
601
- const isIterator = typeof filters === "function";
602
- this.entries().forEach(([itemId, item], index) => {
603
- if (isIterator) {
604
- filters(item, itemId, index) && newList.item(itemId, item);
388
+ toString() {
389
+ return this.value?.toString() || "";
390
+ }
391
+ toValue() {
392
+ return this.value;
393
+ }
394
+ toProp(tag) {
395
+ const prop = this.get(tag);
396
+ return prop;
397
+ }
398
+ toList() {
399
+ return new List().concat(
400
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
401
+ this.id ? { [this.id]: this } : { ...[this] }
402
+ );
403
+ }
404
+ index(i) {
405
+ return this.toList()?.index(i);
406
+ }
407
+ toValueList() {
408
+ return new List().concat(
409
+ this.value ? { [this.value]: this } : (
410
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
411
+ { ...[this] }
412
+ )
413
+ );
414
+ }
415
+ standardizeObject(tag, options) {
416
+ if (tag === "OBJE" && options?.obje?.standardize && "standardizeMedia" in this) {
417
+ const standardize = this.standardizeMedia;
418
+ return standardize.call(
419
+ this,
420
+ options.obje.namespace,
421
+ options.obje.override,
422
+ this.isAncestry() ? ancestryMediaFileUrl : void 0,
423
+ options.gedcomVersion
424
+ );
425
+ }
426
+ }
427
+ toJson(tag, options) {
428
+ const json = this.toObject(tag, options);
429
+ return JSON.stringify(json);
430
+ }
431
+ toObject(tag, options) {
432
+ this.standardizeObject(tag, options);
433
+ const validKeys = getValidKeys(this);
434
+ let json = {};
435
+ validKeys.forEach((key) => {
436
+ if (options?.persist && (key === "DAY" || key === "MONTH" || key === "YEAR")) {
605
437
  return;
606
438
  }
607
- if (!Object.keys(filters).length || Object.entries(filters).every(([key, value]) => {
608
- let itemValue;
609
- if (key === "id") {
610
- itemValue = item.toProp("id");
611
- } else {
612
- itemValue = item.toProp(key)?.toValue();
613
- }
614
- if (Array.isArray(value)) {
615
- return value.includes(itemValue);
439
+ if (key === "id" && this.id !== void 0) {
440
+ json.id = this.id;
441
+ } else if (key === "_id" && this._id !== void 0) {
442
+ json.id = this._id;
443
+ } else if (key === "value" && this.value !== void 0) {
444
+ json.value = this.value;
445
+ } else if (key === "_value" && this._value !== void 0) {
446
+ json.value = this._value;
447
+ } else {
448
+ const validKey = key;
449
+ const prop = this.get(validKey);
450
+ if (typeof prop?.toObject === "function") {
451
+ if (prop instanceof _Common) {
452
+ json[validKey] = prop.toObject(validKey, options);
453
+ } else if (prop instanceof List) {
454
+ json = {
455
+ ...json,
456
+ ...prop.toObject(validKey, options)
457
+ };
458
+ }
616
459
  }
617
- return itemValue === value;
618
- })) {
619
- newList.item(itemId, item);
620
460
  }
621
461
  });
622
- return newList;
462
+ return json;
623
463
  }
624
- find(filters, _ClassName = _List) {
625
- let foundItem;
626
- const isIterator = typeof filters === "function";
627
- this.entries().find(([itemId, item], index) => {
628
- if (isIterator) {
629
- if (filters(item, itemId, index)) {
630
- foundItem = item;
631
- return true;
632
- }
633
- return false;
464
+ merge(other, override = false, avoidKeys = []) {
465
+ if (!other) {
466
+ return this;
467
+ }
468
+ const validKeys = getValidKeys(other);
469
+ validKeys.forEach((validKey) => {
470
+ if (avoidKeys.includes(validKey)) {
471
+ return;
634
472
  }
635
- if (!Object.keys(filters).length || Object.entries(filters).every(([key, value]) => {
636
- let itemValue;
637
- if (key === "id") {
638
- itemValue = item.toProp("id");
639
- } else {
640
- itemValue = item.toProp(key)?.toValue();
641
- }
642
- if (Array.isArray(value)) {
643
- return value.includes(itemValue);
473
+ const current = this.get(validKey);
474
+ const mergeValue = other.get(validKey);
475
+ if (mergeValue) {
476
+ if (current instanceof _Common && !current.isListable) {
477
+ if (override) {
478
+ this.set(validKey, mergeValue);
479
+ }
480
+ } else if (current instanceof _Common || current instanceof List) {
481
+ this.assign(validKey, mergeValue, true);
482
+ } else if (!current || override && validKey !== "id" && validKey !== "_id") {
483
+ this.set(validKey, mergeValue);
644
484
  }
645
- return itemValue === value;
646
- })) {
647
- foundItem = item;
648
- return true;
649
485
  }
650
- return false;
651
486
  });
652
- return foundItem;
487
+ return this;
653
488
  }
654
- orderBy(orders, ClassName = _List) {
655
- const newList = new ClassName();
656
- const isIterator = typeof orders === "function";
657
- if (!isIterator && (!orders || !Object.keys(orders).length)) {
658
- return newList.merge(this);
489
+ clone(newId = false, avoidKeys = []) {
490
+ const ctor = this.constructor;
491
+ const instance = new ctor(
492
+ this._gedcom,
493
+ this._id,
494
+ this._main,
495
+ this._parent
496
+ );
497
+ if (newId && this._id) {
498
+ instance.id = `IFT${this._id.replace(/[^0-9]/g, "")}`;
659
499
  }
660
- const sortedItems = this.entries().toSorted(
661
- ([itemAId, itemA], [itemBId, itemB]) => {
662
- if (isIterator) {
663
- return orders(itemA, itemAId, itemB, itemBId);
664
- }
665
- const [key, { direction, getter }] = Object.entries(
666
- typeof orders === "string" ? { [orders]: { direction: "ASC" } } : orders
667
- )[0];
668
- const rawA = itemA.get(key);
669
- const rawB = itemB.get(key);
670
- let valueA = rawA?.toValue();
671
- let valueB = rawB?.toValue();
672
- if (getter && typeof getter === "function") {
673
- valueA = getter(valueA, rawA);
674
- valueB = getter(valueB, rawB);
675
- }
676
- let sortValue = 0;
677
- if (!valueA && valueA !== 0 && !valueB && valueB !== 0) {
678
- sortValue = 0;
679
- } else if (!valueB && valueB !== 0) {
680
- sortValue = 1;
681
- } else if (!valueA && valueA !== 0) {
682
- sortValue = -1;
683
- } else if (valueA < valueB) {
684
- sortValue = -1;
685
- } else if (valueA > valueB) {
686
- sortValue = 1;
500
+ const cloned = createProxy(instance);
501
+ cloned.merge(this, true, avoidKeys);
502
+ return cloned;
503
+ }
504
+ // fromGedcom<T = typeof this>(value: string) {
505
+ fromGedcom(_value) {
506
+ }
507
+ toGedcom(tag, level = 0, options) {
508
+ const gedcom = this.toGedcomLines(tag, level, options);
509
+ return gedcom.join("\n");
510
+ }
511
+ toGedcomLines(tag, level = 0, options) {
512
+ this.standardizeObject(tag, options);
513
+ const validKeys = getValidKeys(this);
514
+ const gedcom = [];
515
+ const isGedcom7Export = options?.gedcomVersion === "7.0";
516
+ validKeys.forEach((key) => {
517
+ const validKey = key;
518
+ if (isGedcom7Export && validKey === "CONC") {
519
+ return;
520
+ }
521
+ const prop = this.get(validKey);
522
+ if (typeof prop?.toGedcomLines === "function") {
523
+ if (prop instanceof _Common) {
524
+ const value = prop.exportValue();
525
+ gedcom.push(
526
+ `${level} ${validKey}${value ? ` ${value}` : ""}`
527
+ );
528
+ gedcom.push(
529
+ ...prop.toGedcomLines(validKey, level + 1, options)
530
+ );
531
+ } else if (prop instanceof List) {
532
+ gedcom.push(
533
+ ...prop.toGedcomLines(validKey, level, options)
534
+ );
687
535
  }
688
- return sortValue * (direction === "DESC" ? -1 : 1);
689
536
  }
690
- );
691
- sortedItems.forEach(([key, value]) => {
692
- newList.item(key, value);
693
537
  });
694
- return newList;
538
+ return gedcom;
695
539
  }
696
- groupBy(groups, sort, minOccurance = -1, lessOccuranceLabel = "Appearing less than 5 times", ClassName = _List) {
697
- const groupped = {};
698
- const isIterator = typeof groups === "function";
699
- if (!isIterator && (!groups || !Object.keys(groups).length)) {
700
- groupped.Unknown = new ClassName().merge(this);
701
- groupped.Unknown.marker = "Unknown";
702
- return groupped;
703
- }
704
- this.forEach((item, itemId) => {
705
- const group = [];
706
- if (isIterator) {
707
- const newGroups = groups(item, itemId);
708
- group.push(
709
- ...Array.isArray(newGroups) ? newGroups : [newGroups]
710
- );
711
- } else {
712
- const [key, { getter }] = Object.entries(
713
- typeof groups === "string" ? { [groups]: {} } : groups
714
- )[0];
715
- const raw = item.get(key)?.index(0);
716
- let value = raw?.toValue();
717
- if (getter && typeof getter === "function") {
718
- value = getter(value, raw);
719
- }
720
- if (value) {
721
- group.push(value);
722
- }
723
- }
724
- if (!group.length) {
725
- group.push("Unknown");
726
- }
727
- group.forEach((g) => {
728
- const newGroup = typeof g === "string" ? { group: g } : g;
729
- if (!groupped[newGroup.group]) {
730
- groupped[newGroup.group] = new ClassName();
731
- groupped[newGroup.group].marker = newGroup.marker ?? newGroup.group;
732
- }
733
- groupped[newGroup.group]?.append(item);
734
- });
735
- });
736
- if (sort) {
737
- return Object.entries(groupped).toSorted(([ak, a], [bk, b]) => {
738
- if (typeof sort === "function") {
739
- return sort(a, ak, b, bk);
740
- }
741
- if (ak === "Unknown") {
742
- return 1;
743
- }
744
- if (bk === "Unknown") {
745
- return -1;
746
- }
747
- if (sort === "group") {
748
- return ak.localeCompare(bk);
749
- }
750
- return (b?.length ?? 0) - (a?.length ?? 0);
751
- }).reduce((acc, [key, value]) => {
752
- if (minOccurance > -1 && (value?.length ?? 0) < minOccurance) {
753
- const otherKey = lessOccuranceLabel;
754
- acc[otherKey] = acc[otherKey] && value ? acc[otherKey].merge(value) : value;
755
- } else {
756
- acc[key] = value;
757
- }
758
- return acc;
759
- }, {});
760
- }
761
- return groupped;
762
- }
763
- findIndex(item) {
764
- if (!item?.id) {
765
- return -1;
766
- }
767
- return this.keys().findIndex((key) => key === item.id);
540
+ getHeadRecord() {
541
+ return get(this, "HEAD") || get(this.getGedcom(), "HEAD");
768
542
  }
769
- getItems() {
770
- return this.items;
543
+ getOriginalHeadRecord() {
544
+ return get(this, ORIG_HEAD_TAG) || get(this.getGedcom(), ORIG_HEAD_TAG) || get(this.getHeadRecord(), ORIG_HEAD_TAG);
771
545
  }
772
- toJson(tag, options) {
773
- const json = this.toObject(tag, options);
774
- return JSON.stringify(json);
546
+ getSourceHeads() {
547
+ const heads = [this.getOriginalHeadRecord(), this.getHeadRecord()];
548
+ return heads.filter((head, index) => {
549
+ return Boolean(head && heads.indexOf(head) === index);
550
+ });
775
551
  }
776
- toObject(tag, options) {
777
- const json = {};
778
- this.entries().forEach(([_, item]) => {
779
- const validTag = getValidTag(tag);
780
- if (json[validTag]) {
781
- if (!Array.isArray(json[validTag])) {
782
- json[validTag] = [json[validTag]];
783
- }
784
- json[validTag].push(
785
- item.toObject(validTag, options)
786
- );
787
- } else {
788
- json[validTag] = item.toObject(
789
- validTag,
790
- options
791
- );
552
+ getFromSourceHeads(path) {
553
+ for (const head of this.getSourceHeads()) {
554
+ const value = get(head, path);
555
+ if (value !== void 0 && value !== null && value !== "") {
556
+ return value;
792
557
  }
558
+ }
559
+ return void 0;
560
+ }
561
+ sourStartsWith(prefix) {
562
+ return this.getSourceHeads().some((head) => {
563
+ const sour = get(head, "SOUR.value");
564
+ return !!sour?.toLowerCase()?.startsWith(prefix);
793
565
  });
794
- return json;
795
566
  }
796
- toGedcom(tag, level = 0, options) {
797
- const gedcom = this.toGedcomLines(tag, level, options);
798
- return gedcom.join("\n");
567
+ isGenoPro() {
568
+ return this.sourStartsWith("genopro");
799
569
  }
800
- toGedcomLines(tag, level = 0, options) {
801
- const gedcom = [];
802
- this.entries().forEach(([_, item]) => {
803
- const validTag = getValidTag(tag);
804
- const xref = item.id;
805
- const payload = item.exportValue?.();
806
- if (xref) {
807
- gedcom.push(
808
- `${level} ${xref} ${validTag}${payload ? ` ${payload}` : ""}`
809
- );
810
- } else {
811
- gedcom.push(
812
- `${level} ${validTag}${payload ? ` ${payload}` : ""}`
813
- );
814
- }
815
- gedcom.push(
816
- ...item.toGedcomLines(
817
- validTag,
818
- level + 1,
819
- options
820
- )
821
- );
822
- });
823
- return gedcom;
570
+ isAhnenblatt() {
571
+ return this.sourStartsWith("ahnenblatt");
824
572
  }
825
- toValue() {
826
- const propList = new _List();
827
- this.entries().forEach(([key, item]) => {
828
- propList.item(key, item?.toValue());
829
- });
830
- return propList;
573
+ isGeni() {
574
+ return this.sourStartsWith("geni");
831
575
  }
832
- // @deprecated use get instead
833
- toProp(tag) {
834
- return this.get(tag);
576
+ isAncestry() {
577
+ return this.sourStartsWith("ancestry");
835
578
  }
836
- toList() {
837
- return new _List().concat(this.items);
579
+ isMyHeritage() {
580
+ return this.sourStartsWith("myheritage");
838
581
  }
839
- toValueList() {
840
- const newList = new _List();
841
- this.values().forEach((value) => {
842
- value.value && newList.item(value.value, value);
582
+ /**
583
+ * Get the source type as a string (for prefixing tree IDs and names)
584
+ * Returns the detected source type or undefined if unknown
585
+ */
586
+ getSourceType() {
587
+ if (this.isAncestry()) return "Ancestry";
588
+ if (this.isMyHeritage()) return "MyHeritage";
589
+ if (this.isFamilySearch()) return "FamilySearch";
590
+ if (this.isGNO2GED()) return "GNO2GED";
591
+ if (this.isGenoPro()) return "GenoPro";
592
+ if (this.isAhnenblatt()) return "Ahnenblatt";
593
+ if (this.isGeni()) return "Geni";
594
+ return void 0;
595
+ }
596
+ isFamilySearch() {
597
+ return this.getSourceHeads().some((head) => {
598
+ const sourName = get(head, "SOUR.NAME.value");
599
+ return sourName === "FamilySearch API";
843
600
  });
844
- return newList;
845
601
  }
846
- };
847
-
848
- // src/classes/common.ts
849
- var Common = class _Common {
850
- constructor(gedcom, id, main, parent) {
851
- this.isListable = true;
852
- this._gedcom = gedcom;
853
- this._main = main;
854
- this._parent = parent;
855
- this.initUniqueId();
856
- if (id) {
857
- this._id = id;
858
- } else {
859
- delete this._id;
860
- }
602
+ isGNO2GED() {
603
+ return this.getSourceHeads().some((head) => {
604
+ const sour = get(head, "SOUR.value");
605
+ return sour === "GNO2GED";
606
+ });
861
607
  }
862
- set type(type) {
863
- if (type) {
864
- this.uniqueId = this.uniqueId?.replace(/^[^:]+/, `~${type}`);
865
- }
866
- this._type = type;
608
+ getAncestryTreeId() {
609
+ return this.getFromSourceHeads("SOUR._TREE.RIN.value") || (get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value"));
867
610
  }
868
- get type() {
869
- return this._type;
611
+ getMyHeritageTreeId() {
612
+ return this.getFromSourceHeads("_EXPORTED_FROM_SITE_ID.value") || (get(this, "HEAD._EXPORTED_FROM_SITE_ID.value") || get(this.getGedcom(), "HEAD._EXPORTED_FROM_SITE_ID.value"));
870
613
  }
871
- initUniqueId() {
872
- if (this._gedcom) {
873
- this.uniqueId = `~Common:${this._gedcom.refcount++}`;
614
+ getTreeId() {
615
+ if (this?.isAncestry()) {
616
+ const id = this.getAncestryTreeId();
617
+ if (id !== void 0) return id;
874
618
  }
875
- }
876
- set uniqueId(id) {
877
- const currentId = this._uniqueId;
878
- if (this._gedcom?.reflist && id) {
879
- this._uniqueId = id;
880
- if (currentId && this._gedcom.reflist[currentId]) {
881
- delete this._gedcom.reflist[currentId];
882
- }
883
- this._gedcom.reflist[id] = this;
619
+ if (this?.isMyHeritage()) {
620
+ const id = this.getMyHeritageTreeId();
621
+ if (id !== void 0) return id;
622
+ }
623
+ if (this?.isFamilySearch()) {
624
+ const id = this.getFamilySearchTreeId();
625
+ if (id !== void 0) return id;
626
+ }
627
+ if (this?.isGNO2GED()) {
628
+ const id = this.getGNO2GEDTreeId();
629
+ if (id !== void 0) return id;
630
+ }
631
+ if (this?.isAhnenblatt()) {
632
+ const id = this.getAhnenblattTreeId();
633
+ if (id !== void 0) return id;
634
+ }
635
+ if (this?.isGeni()) {
636
+ const id = this.getGeniTreeId();
637
+ if (id !== void 0) return id;
638
+ }
639
+ if (this?.isGenoPro()) {
640
+ const id = this.getGenoProTreeId();
641
+ if (id !== void 0) return id;
884
642
  }
643
+ return this.getUniversalTreeId();
885
644
  }
886
- get uniqueId() {
887
- return this._uniqueId;
645
+ getAncestryTreeName() {
646
+ return this.getFromSourceHeads("SOUR._TREE.value") || (get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value"));
888
647
  }
889
- set id(id) {
890
- this._id = idGetter(id);
648
+ getMyHeritageTreeName() {
649
+ const path = "HEAD.FILE.value";
650
+ const treeDetails = get(this, path) || get(this.getGedcom(), path);
651
+ return treeDetails?.match(
652
+ /Exported by MyHeritage.com from (?<tree>.+) in.+$/
653
+ )?.groups?.tree;
891
654
  }
892
- get id() {
893
- return idGetter(this._id);
655
+ getFamilySearchTreeId() {
656
+ const rin = this.getFromSourceHeads("SOUR._TREE.RIN.value") || get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value");
657
+ if (rin) {
658
+ return rin;
659
+ }
660
+ return "familysearch";
894
661
  }
895
- set value(value) {
896
- this._value = value;
897
- }
898
- get value() {
899
- return this._value;
900
- }
901
- /**
902
- * Set the gedcom reference for this object
903
- * @param gedcom - The GedComType to set as the gedcom reference
904
- */
905
- setGedcom(gedcom) {
906
- this._gedcom = gedcom;
907
- }
908
- // avoid to override
909
- get originalValue() {
910
- return this._value;
911
- }
912
- get ref() {
913
- if (typeof this.value === "string" && isId(this.value) && this.refType) {
914
- return this._gedcom?.[getListTag(this.refType)]?.item(
915
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
916
- this.value
917
- );
662
+ getFamilySearchTreeName() {
663
+ const treeName = this.getFromSourceHeads("SOUR._TREE.value") || get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value");
664
+ if (treeName) {
665
+ return treeName;
918
666
  }
919
- return this;
667
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
668
+ return fileName || "FamilySearch Import";
920
669
  }
921
- get main() {
922
- return this._main;
670
+ getAhnenblattTreeId() {
671
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
672
+ if (fileName) {
673
+ const idMatch = fileName.match(/_(\d+)/);
674
+ if (idMatch) {
675
+ return idMatch[1];
676
+ }
677
+ }
678
+ return void 0;
923
679
  }
924
- get parent() {
925
- return this._parent;
680
+ getAhnenblattTreeName() {
681
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
682
+ return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
926
683
  }
927
- addRef(refNode) {
928
- if (typeof refNode.value === "string" && isId(refNode.value) && refNode.refType) {
929
- if (!this._refs) {
930
- this._refs = new List();
684
+ getGeniTreeId() {
685
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
686
+ if (fileName) {
687
+ const idMatch = fileName.match(/_(\d+)/);
688
+ if (idMatch) {
689
+ return idMatch[1];
931
690
  }
932
- this._refs.item(refNode.value, refNode);
933
691
  }
692
+ return void 0;
934
693
  }
935
- getRefs() {
936
- return this._refs;
694
+ getGeniTreeName() {
695
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
696
+ return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
937
697
  }
938
- exportValue() {
939
- return this.value;
698
+ getGenoProTreeId() {
699
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
700
+ if (fileName) {
701
+ const idMatch = fileName.match(/_(\d+)/);
702
+ if (idMatch) {
703
+ return idMatch[1];
704
+ }
705
+ }
706
+ return void 0;
940
707
  }
941
- removeValue() {
942
- delete this._value;
708
+ getGenoProTreeName() {
709
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
710
+ return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
943
711
  }
944
- set(name, value) {
945
- if (isGedcomTrailerTag(name)) {
946
- return void 0;
947
- }
948
- if (typeof value === "string") {
949
- const usedValue = createCommon(this._gedcom, void 0, this.main);
950
- usedValue.value = value;
951
- set(this, name, usedValue);
952
- } else {
953
- set(this, name, value);
712
+ getGNO2GEDTreeId() {
713
+ const rin = get(this, "HEAD._TREE.RIN.value") || get(this.getGedcom(), "HEAD._TREE.RIN.value");
714
+ if (rin) {
715
+ return rin;
954
716
  }
955
- return get(this, name);
717
+ return `gno_${this._gedcom?.refcount || "unknown"}`;
956
718
  }
957
- assign(name, value, unique = false) {
958
- if (isGedcomTrailerTag(name)) {
959
- return void 0;
719
+ getGNO2GEDTreeName() {
720
+ const treeName = get(this, "HEAD._TREE.value") || get(this.getGedcom(), "HEAD._TREE.value");
721
+ if (treeName) {
722
+ return treeName;
960
723
  }
961
- let curValue = this.get(name);
962
- if (curValue?.isListable) {
963
- if (curValue instanceof _Common) {
964
- curValue = new List().concat({ ...[curValue] });
965
- this.set(name, curValue);
724
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
725
+ return fileName || "GNO2GED Export";
726
+ }
727
+ /**
728
+ * Universal tree ID getter for unknown/unrecognized GEDCOM sources
729
+ * Tries to extract an ID from various common locations
730
+ */
731
+ getUniversalTreeId() {
732
+ const sourceType = this.getSourceType();
733
+ const prefix = sourceType ? sourceType.toLowerCase() : "tree";
734
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
735
+ if (fileName) {
736
+ const idMatch = fileName.match(/_(\d+)/);
737
+ if (idMatch) {
738
+ return `${prefix}_${idMatch[1]}`;
966
739
  }
967
- const values = [
968
- ...curValue.values(),
969
- ...value instanceof List ? value.values() : [value]
970
- ];
971
- curValue.concat({
972
- ...!unique ? values : uniqBy(
973
- values,
974
- (value2) => value2 instanceof _Common ? value2?.value : void 0
975
- )
976
- });
977
- } else {
978
- this.set(name, value);
979
740
  }
980
- return get(this, name);
741
+ return `${prefix}_${this._gedcom?.refcount || "unknown"}`;
981
742
  }
982
- get(name) {
983
- if (!name.includes(".")) {
984
- return get(this, name);
743
+ /**
744
+ * Universal tree name getter for unknown/unrecognized GEDCOM sources
745
+ * Tries to extract a name from various common locations
746
+ */
747
+ getUniversalTreeName() {
748
+ const sourceType = this.getSourceType();
749
+ const prefix = sourceType ? `${sourceType}-` : "";
750
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
751
+ if (fileName) {
752
+ const cleanName = fileName.replace(/\.ged$/i, "").replace(/_/g, " ");
753
+ return `${prefix}${cleanName}`;
985
754
  }
986
- const nameParts = name.split(".");
987
- let last = this;
988
- for (const part of nameParts) {
989
- const indexPart = Number(part);
990
- const itemIndex = part.match(/items\[(?<itemIndex>\d+)\]/)?.groups?.itemIndex;
991
- const itemId = part.match(/items\[(?<itemId>@[A-Z]+\d+@)\]/)?.groups?.itemId;
992
- const usedPart = itemId || itemIndex;
993
- if (last instanceof List) {
994
- if (!isNaN(indexPart)) {
995
- last = last.index(indexPart);
996
- } else {
997
- if (usedPart) {
998
- last = last.item(usedPart);
999
- } else {
1000
- last = last?.index(0)?.get(part);
1001
- }
1002
- }
1003
- } else {
1004
- const usedIndex = Number(itemIndex ?? indexPart);
1005
- if (!isNaN(usedIndex)) {
1006
- last = last?.toList().index(usedIndex);
1007
- } else {
1008
- last = last?.get(itemId ?? part);
1009
- }
1010
- }
1011
- if (!last) {
1012
- break;
1013
- }
755
+ const sourName = this.getFromSourceHeads("SOUR.NAME.value") || get(this, "HEAD.SOUR.NAME.value") || get(this.getGedcom(), "HEAD.SOUR.NAME.value");
756
+ if (sourName) {
757
+ return `${prefix}${sourName}`;
1014
758
  }
1015
- return last;
1016
- }
1017
- remove(name) {
1018
- unset(this, name);
1019
- }
1020
- getGedcom() {
1021
- return this._gedcom;
759
+ const sourValue = this.getFromSourceHeads("SOUR.value") || get(this, "HEAD.SOUR.value") || get(this.getGedcom(), "HEAD.SOUR.value");
760
+ if (sourValue) {
761
+ return `${prefix}${sourValue}`;
762
+ }
763
+ return `${prefix}Unknown Tree`;
1022
764
  }
1023
- getIf(name, condition, name2) {
1024
- const nameParts = name.split(".");
1025
- const name2Parts = name2.split(".");
1026
- if (nameParts.length !== name2Parts.length) {
1027
- throw new Error("Tag pathes must be of the same depth");
765
+ getTreeName() {
766
+ if (this?.isAncestry()) {
767
+ const name = this.getAncestryTreeName();
768
+ if (name) return name;
1028
769
  }
1029
- if (nameParts.length > 1 && nameParts[0] !== name2Parts[0]) {
1030
- throw new Error("Final tags must be siblings");
770
+ if (this?.isMyHeritage()) {
771
+ const name = this.getMyHeritageTreeName();
772
+ if (name) return name;
1031
773
  }
1032
- const valueCommon = this.get(name);
1033
- if (valueCommon instanceof _Common) {
1034
- return valueCommon?.toValue() !== condition ? void 0 : this.get(name2);
774
+ if (this?.isFamilySearch()) {
775
+ const name = this.getFamilySearchTreeName();
776
+ if (name) return name;
1035
777
  }
1036
- const valueCommon2 = this.get(name2)?.toList();
1037
- const ifList = new List();
1038
- valueCommon?.forEach((item, key) => {
1039
- const pair = valueCommon2?.item(key);
1040
- ifList.item(key, item?.toValue() !== condition ? void 0 : pair);
1041
- });
1042
- return ifList;
1043
- }
1044
- toString() {
1045
- return this.value?.toString() || "";
1046
- }
1047
- toValue() {
1048
- return this.value;
1049
- }
1050
- toProp(tag) {
1051
- const prop = this.get(tag);
1052
- return prop;
1053
- }
1054
- toList() {
1055
- return new List().concat(
1056
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1057
- this.id ? { [this.id]: this } : { ...[this] }
1058
- );
1059
- }
1060
- index(i) {
1061
- return this.toList()?.index(i);
778
+ if (this?.isGNO2GED()) {
779
+ const name = this.getGNO2GEDTreeName();
780
+ if (name) return name;
781
+ }
782
+ if (this?.isAhnenblatt()) {
783
+ const name = this.getAhnenblattTreeName();
784
+ if (name) return name;
785
+ }
786
+ if (this?.isGeni()) {
787
+ const name = this.getGeniTreeName();
788
+ if (name) return name;
789
+ }
790
+ if (this?.isGenoPro()) {
791
+ const name = this.getGenoProTreeName();
792
+ if (name) return name;
793
+ }
794
+ return this.getUniversalTreeName();
1062
795
  }
1063
- toValueList() {
1064
- return new List().concat(
1065
- this.value ? { [this.value]: this } : (
1066
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1067
- { ...[this] }
1068
- )
1069
- );
1070
- }
1071
- standardizeObject(tag, options) {
1072
- if (tag === "OBJE" && options?.obje?.standardize && "standardizeMedia" in this) {
1073
- const standardize = this.standardizeMedia;
1074
- return standardize.call(
1075
- this,
1076
- options.obje.namespace,
1077
- options.obje.override,
1078
- this.isAncestry() ? ancestryMediaFileUrl : void 0,
1079
- options.gedcomVersion
1080
- );
1081
- }
1082
- }
1083
- toJson(tag, options) {
1084
- const json = this.toObject(tag, options);
1085
- return JSON.stringify(json);
1086
- }
1087
- toObject(tag, options) {
1088
- this.standardizeObject(tag, options);
1089
- const validKeys = getValidKeys(this);
1090
- let json = {};
1091
- validKeys.forEach((key) => {
1092
- if (options?.persist && (key === "DAY" || key === "MONTH" || key === "YEAR")) {
1093
- return;
1094
- }
1095
- if (key === "id" && this.id !== void 0) {
1096
- json.id = this.id;
1097
- } else if (key === "_id" && this._id !== void 0) {
1098
- json.id = this._id;
1099
- } else if (key === "value" && this.value !== void 0) {
1100
- json.value = this.value;
1101
- } else if (key === "_value" && this._value !== void 0) {
1102
- json.value = this._value;
1103
- } else {
1104
- const validKey = key;
1105
- const prop = this.get(validKey);
1106
- if (typeof prop?.toObject === "function") {
1107
- if (prop instanceof _Common) {
1108
- json[validKey] = prop.toObject(validKey, options);
1109
- } else if (prop instanceof List) {
1110
- json = {
1111
- ...json,
1112
- ...prop.toObject(validKey, options)
1113
- };
1114
- }
1115
- }
796
+ };
797
+ var createProxy = (target) => {
798
+ return new Proxy(target, {
799
+ get(t, prop, receiver) {
800
+ if (prop === "unwrapped") {
801
+ return target;
1116
802
  }
1117
- });
1118
- return json;
1119
- }
1120
- merge(other, override = false, avoidKeys = []) {
1121
- if (!other) {
1122
- return this;
1123
- }
1124
- const validKeys = getValidKeys(other);
1125
- validKeys.forEach((validKey) => {
1126
- if (avoidKeys.includes(validKey)) {
1127
- return;
803
+ if (prop in t) {
804
+ return Reflect.get(t, prop, receiver);
1128
805
  }
1129
- const current = this.get(validKey);
1130
- const mergeValue = other.get(validKey);
1131
- if (mergeValue) {
1132
- if (current instanceof _Common || current instanceof List) {
1133
- this.assign(validKey, mergeValue, true);
1134
- } else if (!current || override && validKey !== "id" && validKey !== "_id") {
1135
- this.set(validKey, mergeValue);
806
+ if (!isOnlyMainProp(prop)) {
807
+ const ref = t.ref;
808
+ if (ref && prop in ref) {
809
+ return Reflect.get(ref, prop, receiver);
1136
810
  }
1137
811
  }
1138
- });
1139
- return this;
1140
- }
1141
- clone(newId = false, avoidKeys = []) {
1142
- const ctor = this.constructor;
1143
- const instance = new ctor(
1144
- this._gedcom,
1145
- this._id,
1146
- this._main,
1147
- this._parent
1148
- );
1149
- if (newId && this._id) {
1150
- instance.id = `IFT${this._id.replace(/[^0-9]/g, "")}`;
1151
812
  }
1152
- const cloned = createProxy(instance);
1153
- cloned.merge(this, true, avoidKeys);
1154
- return cloned;
813
+ });
814
+ };
815
+ var createCommon = (gedcom, id, main, parent) => {
816
+ return createProxy(
817
+ new Common(gedcom, id, main, parent)
818
+ );
819
+ };
820
+ var isOnlyMainProp = (key) => {
821
+ return key === "_gedcom" || key === "_main" || key === "_parent" || key === "_uniqueId" || key === "_type" || key === "_refs" || key === "id" || key === "_id";
822
+ };
823
+ var isValidKey = (common, key) => {
824
+ const prop = get(common, key);
825
+ return key !== "_gedcom" && key !== "_main" && key !== "_parent" && key !== "_uniqueId" && key !== "_refs" && key !== "_type" && !(typeof key === "string" && isGedcomTrailerTag(key)) && (key === "id" || key === "_id" || key === "value" || key === "_value" || prop instanceof Common || prop instanceof List);
826
+ };
827
+ var getValidKeys = (common) => {
828
+ return Object.keys(common).filter((key) => {
829
+ return isValidKey(common, key);
830
+ });
831
+ };
832
+ var getValidTag = (tag) => {
833
+ return tag.replace(/^@@/, "");
834
+ };
835
+ var getListTag = (tag) => {
836
+ return `@@${tag}`;
837
+ };
838
+ var isId = (string) => {
839
+ return ID_REG.test(string);
840
+ };
841
+ var idGetter = (id) => {
842
+ if (id === void 0) {
843
+ return void 0;
1155
844
  }
1156
- // fromGedcom<T = typeof this>(value: string) {
1157
- fromGedcom(_value) {
845
+ let newId = `${id}`;
846
+ const parts = newId?.match(ID_GETTER_REG)?.groups;
847
+ if (!parts?.at && !parts?.letter) {
848
+ newId = `@U${newId}`;
849
+ } else if (!parts.at) {
850
+ newId = `@${newId}`;
851
+ } else if (!parts.letter) {
852
+ newId = newId.replace(/^@/, "@U");
1158
853
  }
1159
- toGedcom(tag, level = 0, options) {
1160
- const gedcom = this.toGedcomLines(tag, level, options);
1161
- return gedcom.join("\n");
854
+ if (!newId.endsWith("@")) {
855
+ newId = `${newId}@`;
1162
856
  }
1163
- toGedcomLines(tag, level = 0, options) {
1164
- this.standardizeObject(tag, options);
1165
- const validKeys = getValidKeys(this);
1166
- const gedcom = [];
1167
- const isGedcom7Export = options?.gedcomVersion === "7.0";
1168
- validKeys.forEach((key) => {
1169
- const validKey = key;
1170
- if (isGedcom7Export && validKey === "CONC") {
1171
- return;
1172
- }
1173
- const prop = this.get(validKey);
1174
- if (typeof prop?.toGedcomLines === "function") {
1175
- if (prop instanceof _Common) {
1176
- const value = prop.exportValue();
1177
- gedcom.push(
1178
- `${level} ${validKey}${value ? ` ${value}` : ""}`
1179
- );
1180
- gedcom.push(
1181
- ...prop.toGedcomLines(validKey, level + 1, options)
1182
- );
1183
- } else if (prop instanceof List) {
1184
- gedcom.push(
1185
- ...prop.toGedcomLines(validKey, level, options)
1186
- );
1187
- }
1188
- }
1189
- });
1190
- return gedcom;
857
+ return newId;
858
+ };
859
+
860
+ // src/classes/list.ts
861
+ var List = class _List {
862
+ // length = 0;
863
+ constructor(items) {
864
+ this.items = {};
865
+ this.isListable = true;
866
+ this.marker = void 0;
867
+ if (items) {
868
+ this.concat(items);
869
+ }
1191
870
  }
1192
- getHeadRecord() {
1193
- return get(this, "HEAD") || get(this.getGedcom(), "HEAD");
871
+ get length() {
872
+ return this.values().length;
1194
873
  }
1195
- getOriginalHeadRecord() {
1196
- return get(this, ORIG_HEAD_TAG) || get(this.getGedcom(), ORIG_HEAD_TAG) || get(this.getHeadRecord(), ORIG_HEAD_TAG);
874
+ has(indi) {
875
+ return !!this.item(
876
+ typeof indi === "string" ? indi : indi?.id ?? ""
877
+ );
1197
878
  }
1198
- getSourceHeads() {
1199
- const heads = [this.getOriginalHeadRecord(), this.getHeadRecord()];
1200
- return heads.filter((head, index) => {
1201
- return Boolean(head && heads.indexOf(head) === index);
879
+ get(name) {
880
+ const propList = new _List();
881
+ this.entries().forEach(([key, item]) => {
882
+ propList.item(key, item.get(name));
1202
883
  });
884
+ return propList;
1203
885
  }
1204
- getFromSourceHeads(path) {
1205
- for (const head of this.getSourceHeads()) {
1206
- const value = get(head, path);
1207
- if (value !== void 0 && value !== null && value !== "") {
1208
- return value;
886
+ getIf(name, condition, name2) {
887
+ const propList = new _List();
888
+ this.entries().forEach(([key, item]) => {
889
+ const passedItem = item.getIf(name, condition, name2);
890
+ if (passedItem) {
891
+ propList.item(key, passedItem);
1209
892
  }
1210
- }
1211
- return void 0;
1212
- }
1213
- sourStartsWith(prefix) {
1214
- return this.getSourceHeads().some((head) => {
1215
- const sour = get(head, "SOUR.value");
1216
- return !!sour?.toLowerCase()?.startsWith(prefix);
1217
893
  });
894
+ return propList;
1218
895
  }
1219
- isGenoPro() {
1220
- return this.sourStartsWith("genopro");
896
+ keys() {
897
+ return Object.keys(this.items);
1221
898
  }
1222
- isAhnenblatt() {
1223
- return this.sourStartsWith("ahnenblatt");
899
+ values() {
900
+ return Object.values(this.items);
1224
901
  }
1225
- isGeni() {
1226
- return this.sourStartsWith("geni");
902
+ entries() {
903
+ return Object.entries(this.items);
1227
904
  }
1228
- isAncestry() {
1229
- return this.sourStartsWith("ancestry");
905
+ setLength(_type) {
1230
906
  }
1231
- isMyHeritage() {
1232
- return this.sourStartsWith("myheritage");
907
+ first() {
908
+ return this.index(0);
1233
909
  }
1234
- /**
1235
- * Get the source type as a string (for prefixing tree IDs and names)
1236
- * Returns the detected source type or undefined if unknown
1237
- */
1238
- getSourceType() {
1239
- if (this.isAncestry()) return "Ancestry";
1240
- if (this.isMyHeritage()) return "MyHeritage";
1241
- if (this.isFamilySearch()) return "FamilySearch";
1242
- if (this.isGNO2GED()) return "GNO2GED";
1243
- if (this.isGenoPro()) return "GenoPro";
1244
- if (this.isAhnenblatt()) return "Ahnenblatt";
1245
- if (this.isGeni()) return "Geni";
1246
- return void 0;
910
+ last() {
911
+ return this.index(this.keys().length - 1);
1247
912
  }
1248
- isFamilySearch() {
1249
- return this.getSourceHeads().some((head) => {
1250
- const sourName = get(head, "SOUR.NAME.value");
1251
- return sourName === "FamilySearch API";
1252
- });
913
+ index(index) {
914
+ const keys = this.keys();
915
+ const key = keys[index];
916
+ return key && this.items[key];
1253
917
  }
1254
- isGNO2GED() {
1255
- return this.getSourceHeads().some((head) => {
1256
- const sour = get(head, "SOUR.value");
1257
- return sour === "GNO2GED";
1258
- });
918
+ item(name, value) {
919
+ if (arguments.length > 1) {
920
+ set(this.items, name, value);
921
+ this.setLength();
922
+ }
923
+ return get(this.items, name);
1259
924
  }
1260
- getAncestryTreeId() {
1261
- return this.getFromSourceHeads("SOUR._TREE.RIN.value") || (get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value"));
925
+ removeItem(name) {
926
+ unset(this.items, name);
927
+ this.setLength();
1262
928
  }
1263
- getMyHeritageTreeId() {
1264
- return this.getFromSourceHeads("_EXPORTED_FROM_SITE_ID.value") || (get(this, "HEAD._EXPORTED_FROM_SITE_ID.value") || get(this.getGedcom(), "HEAD._EXPORTED_FROM_SITE_ID.value"));
929
+ exclude(excludedList) {
930
+ excludedList.keys().forEach((itemId) => {
931
+ delete this.items[itemId];
932
+ });
933
+ this.setLength();
934
+ return this;
1265
935
  }
1266
- getTreeId() {
1267
- if (this?.isAncestry()) {
1268
- const id = this.getAncestryTreeId();
1269
- if (id !== void 0) return id;
1270
- }
1271
- if (this?.isMyHeritage()) {
1272
- const id = this.getMyHeritageTreeId();
1273
- if (id !== void 0) return id;
1274
- }
1275
- if (this?.isFamilySearch()) {
1276
- const id = this.getFamilySearchTreeId();
1277
- if (id !== void 0) return id;
1278
- }
1279
- if (this?.isGNO2GED()) {
1280
- const id = this.getGNO2GEDTreeId();
1281
- if (id !== void 0) return id;
1282
- }
1283
- if (this?.isAhnenblatt()) {
1284
- const id = this.getAhnenblattTreeId();
1285
- if (id !== void 0) return id;
1286
- }
1287
- if (this?.isGeni()) {
1288
- const id = this.getGeniTreeId();
1289
- if (id !== void 0) return id;
1290
- }
1291
- if (this?.isGenoPro()) {
1292
- const id = this.getGenoProTreeId();
1293
- if (id !== void 0) return id;
936
+ concat(items) {
937
+ if (Array.isArray(items)) {
938
+ items.forEach((item) => {
939
+ this.append(item);
940
+ });
941
+ } else {
942
+ Object.assign(this.items, items ?? {});
1294
943
  }
1295
- return this.getUniversalTreeId();
944
+ this.setLength();
945
+ return this;
1296
946
  }
1297
- getAncestryTreeName() {
1298
- return this.getFromSourceHeads("SOUR._TREE.value") || (get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value"));
947
+ merge(mergedList) {
948
+ this.concat(mergedList?.items);
949
+ return this;
1299
950
  }
1300
- getMyHeritageTreeName() {
1301
- const path = "HEAD.FILE.value";
1302
- const treeDetails = get(this, path) || get(this.getGedcom(), path);
1303
- return treeDetails?.match(
1304
- /Exported by MyHeritage.com from (?<tree>.+) in.+$/
1305
- )?.groups?.tree;
951
+ intersection(mergedList) {
952
+ const inter = intersectionBy(
953
+ this.values(),
954
+ mergedList?.values() ?? [],
955
+ "id"
956
+ );
957
+ return new _List(inter);
1306
958
  }
1307
- getFamilySearchTreeId() {
1308
- const rin = this.getFromSourceHeads("SOUR._TREE.RIN.value") || get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value");
1309
- if (rin) {
1310
- return rin;
1311
- }
1312
- return "familysearch";
959
+ difference(mergedList) {
960
+ const inter = differenceBy(
961
+ this.values(),
962
+ mergedList?.values() ?? [],
963
+ "id"
964
+ );
965
+ return new _List(inter);
1313
966
  }
1314
- getFamilySearchTreeName() {
1315
- const treeName = this.getFromSourceHeads("SOUR._TREE.value") || get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value");
1316
- if (treeName) {
1317
- return treeName;
967
+ prepend(item) {
968
+ if (item.id) {
969
+ let increase = true;
970
+ if (this.items[item.id]) {
971
+ delete this.items[item.id];
972
+ increase = false;
973
+ }
974
+ this.items = Object.assign(
975
+ {
976
+ [item.id]: item
977
+ },
978
+ this.items
979
+ );
980
+ if (increase) {
981
+ this.setLength("increase");
982
+ }
1318
983
  }
1319
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1320
- return fileName || "FamilySearch Import";
984
+ return this;
1321
985
  }
1322
- getAhnenblattTreeId() {
1323
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1324
- if (fileName) {
1325
- const idMatch = fileName.match(/_(\d+)/);
1326
- if (idMatch) {
1327
- return idMatch[1];
986
+ append(item) {
987
+ if (item.id) {
988
+ let increase = true;
989
+ if (this.items[item.id]) {
990
+ delete this.items[item.id];
991
+ increase = false;
992
+ }
993
+ this.items[item.id] = item;
994
+ if (increase) {
995
+ this.setLength("increase");
1328
996
  }
1329
997
  }
1330
- return void 0;
1331
- }
1332
- getAhnenblattTreeName() {
1333
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1334
- return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
998
+ return this;
1335
999
  }
1336
- getGeniTreeId() {
1337
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1338
- if (fileName) {
1339
- const idMatch = fileName.match(/_(\d+)/);
1340
- if (idMatch) {
1341
- return idMatch[1];
1342
- }
1000
+ delete(item) {
1001
+ if (item.id && this.items[item.id]) {
1002
+ delete this.items[item.id];
1003
+ this.setLength("decrease");
1343
1004
  }
1344
- return void 0;
1005
+ return this;
1345
1006
  }
1346
- getGeniTreeName() {
1347
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1348
- return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
1007
+ copy(ClassName = _List) {
1008
+ const newList = new ClassName();
1009
+ Object.assign(newList.items, this.items);
1010
+ newList.setLength();
1011
+ return newList;
1349
1012
  }
1350
- getGenoProTreeId() {
1351
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1352
- if (fileName) {
1353
- const idMatch = fileName.match(/_(\d+)/);
1354
- if (idMatch) {
1355
- return idMatch[1];
1356
- }
1357
- }
1358
- return void 0;
1013
+ except(item, ClassName = _List) {
1014
+ return this.copy(ClassName).delete(item);
1359
1015
  }
1360
- getGenoProTreeName() {
1361
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1362
- return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
1016
+ forEach(iterate) {
1017
+ this.entries().forEach(([key, item], index) => {
1018
+ iterate(item, key, index);
1019
+ });
1363
1020
  }
1364
- getGNO2GEDTreeId() {
1365
- const rin = get(this, "HEAD._TREE.RIN.value") || get(this.getGedcom(), "HEAD._TREE.RIN.value");
1366
- if (rin) {
1367
- return rin;
1368
- }
1369
- return `gno_${this._gedcom?.refcount || "unknown"}`;
1021
+ map(iterate) {
1022
+ return this.entries().map(([key, item], index) => {
1023
+ return iterate(item, key, index);
1024
+ });
1370
1025
  }
1371
- getGNO2GEDTreeName() {
1372
- const treeName = get(this, "HEAD._TREE.value") || get(this.getGedcom(), "HEAD._TREE.value");
1373
- if (treeName) {
1374
- return treeName;
1375
- }
1376
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1377
- return fileName || "GNO2GED Export";
1026
+ reduce(iterate, initialValue) {
1027
+ return this.entries().reduce((acc, [key, item], index) => {
1028
+ return iterate(acc, item, key, index);
1029
+ }, initialValue);
1378
1030
  }
1379
- /**
1380
- * Universal tree ID getter for unknown/unrecognized GEDCOM sources
1381
- * Tries to extract an ID from various common locations
1382
- */
1383
- getUniversalTreeId() {
1384
- const sourceType = this.getSourceType();
1385
- const prefix = sourceType ? sourceType.toLowerCase() : "tree";
1386
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1387
- if (fileName) {
1388
- const idMatch = fileName.match(/_(\d+)/);
1389
- if (idMatch) {
1390
- return `${prefix}_${idMatch[1]}`;
1031
+ filter(filters, ClassName = _List) {
1032
+ const newList = new ClassName();
1033
+ const isIterator = typeof filters === "function";
1034
+ this.entries().forEach(([itemId, item], index) => {
1035
+ if (isIterator) {
1036
+ filters(item, itemId, index) && newList.item(itemId, item);
1037
+ return;
1391
1038
  }
1392
- }
1393
- return `${prefix}_${this._gedcom?.refcount || "unknown"}`;
1039
+ if (!Object.keys(filters).length || Object.entries(filters).every(([key, value]) => {
1040
+ let itemValue;
1041
+ if (key === "id") {
1042
+ itemValue = item.toProp("id");
1043
+ } else {
1044
+ itemValue = item.toProp(key)?.toValue();
1045
+ }
1046
+ if (Array.isArray(value)) {
1047
+ return value.includes(itemValue);
1048
+ }
1049
+ return itemValue === value;
1050
+ })) {
1051
+ newList.item(itemId, item);
1052
+ }
1053
+ });
1054
+ return newList;
1394
1055
  }
1395
- /**
1396
- * Universal tree name getter for unknown/unrecognized GEDCOM sources
1397
- * Tries to extract a name from various common locations
1398
- */
1399
- getUniversalTreeName() {
1400
- const sourceType = this.getSourceType();
1401
- const prefix = sourceType ? `${sourceType}-` : "";
1402
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1403
- if (fileName) {
1404
- const cleanName = fileName.replace(/\.ged$/i, "").replace(/_/g, " ");
1405
- return `${prefix}${cleanName}`;
1406
- }
1407
- const sourName = this.getFromSourceHeads("SOUR.NAME.value") || get(this, "HEAD.SOUR.NAME.value") || get(this.getGedcom(), "HEAD.SOUR.NAME.value");
1408
- if (sourName) {
1409
- return `${prefix}${sourName}`;
1410
- }
1411
- const sourValue = this.getFromSourceHeads("SOUR.value") || get(this, "HEAD.SOUR.value") || get(this.getGedcom(), "HEAD.SOUR.value");
1412
- if (sourValue) {
1413
- return `${prefix}${sourValue}`;
1414
- }
1415
- return `${prefix}Unknown Tree`;
1056
+ find(filters, _ClassName = _List) {
1057
+ let foundItem;
1058
+ const isIterator = typeof filters === "function";
1059
+ this.entries().find(([itemId, item], index) => {
1060
+ if (isIterator) {
1061
+ if (filters(item, itemId, index)) {
1062
+ foundItem = item;
1063
+ return true;
1064
+ }
1065
+ return false;
1066
+ }
1067
+ if (!Object.keys(filters).length || Object.entries(filters).every(([key, value]) => {
1068
+ let itemValue;
1069
+ if (key === "id") {
1070
+ itemValue = item.toProp("id");
1071
+ } else {
1072
+ itemValue = item.toProp(key)?.toValue();
1073
+ }
1074
+ if (Array.isArray(value)) {
1075
+ return value.includes(itemValue);
1076
+ }
1077
+ return itemValue === value;
1078
+ })) {
1079
+ foundItem = item;
1080
+ return true;
1081
+ }
1082
+ return false;
1083
+ });
1084
+ return foundItem;
1416
1085
  }
1417
- getTreeName() {
1418
- if (this?.isAncestry()) {
1419
- const name = this.getAncestryTreeName();
1420
- if (name) return name;
1421
- }
1422
- if (this?.isMyHeritage()) {
1423
- const name = this.getMyHeritageTreeName();
1424
- if (name) return name;
1425
- }
1426
- if (this?.isFamilySearch()) {
1427
- const name = this.getFamilySearchTreeName();
1428
- if (name) return name;
1429
- }
1430
- if (this?.isGNO2GED()) {
1431
- const name = this.getGNO2GEDTreeName();
1432
- if (name) return name;
1086
+ orderBy(orders, ClassName = _List) {
1087
+ const newList = new ClassName();
1088
+ const isIterator = typeof orders === "function";
1089
+ if (!isIterator && (!orders || !Object.keys(orders).length)) {
1090
+ return newList.merge(this);
1433
1091
  }
1434
- if (this?.isAhnenblatt()) {
1435
- const name = this.getAhnenblattTreeName();
1436
- if (name) return name;
1092
+ const sortedItems = this.entries().toSorted(
1093
+ ([itemAId, itemA], [itemBId, itemB]) => {
1094
+ if (isIterator) {
1095
+ return orders(itemA, itemAId, itemB, itemBId);
1096
+ }
1097
+ const [key, { direction, getter }] = Object.entries(
1098
+ typeof orders === "string" ? { [orders]: { direction: "ASC" } } : orders
1099
+ )[0];
1100
+ const rawA = itemA.get(key);
1101
+ const rawB = itemB.get(key);
1102
+ let valueA = rawA?.toValue();
1103
+ let valueB = rawB?.toValue();
1104
+ if (getter && typeof getter === "function") {
1105
+ valueA = getter(valueA, rawA);
1106
+ valueB = getter(valueB, rawB);
1107
+ }
1108
+ let sortValue = 0;
1109
+ if (!valueA && valueA !== 0 && !valueB && valueB !== 0) {
1110
+ sortValue = 0;
1111
+ } else if (!valueB && valueB !== 0) {
1112
+ sortValue = 1;
1113
+ } else if (!valueA && valueA !== 0) {
1114
+ sortValue = -1;
1115
+ } else if (valueA < valueB) {
1116
+ sortValue = -1;
1117
+ } else if (valueA > valueB) {
1118
+ sortValue = 1;
1119
+ }
1120
+ return sortValue * (direction === "DESC" ? -1 : 1);
1121
+ }
1122
+ );
1123
+ sortedItems.forEach(([key, value]) => {
1124
+ newList.item(key, value);
1125
+ });
1126
+ return newList;
1127
+ }
1128
+ groupBy(groups, sort, minOccurance = -1, lessOccuranceLabel = "Appearing less than 5 times", ClassName = _List) {
1129
+ const groupped = {};
1130
+ const isIterator = typeof groups === "function";
1131
+ if (!isIterator && (!groups || !Object.keys(groups).length)) {
1132
+ groupped.Unknown = new ClassName().merge(this);
1133
+ groupped.Unknown.marker = "Unknown";
1134
+ return groupped;
1437
1135
  }
1438
- if (this?.isGeni()) {
1439
- const name = this.getGeniTreeName();
1440
- if (name) return name;
1136
+ this.forEach((item, itemId) => {
1137
+ const group = [];
1138
+ if (isIterator) {
1139
+ const newGroups = groups(item, itemId);
1140
+ group.push(
1141
+ ...Array.isArray(newGroups) ? newGroups : [newGroups]
1142
+ );
1143
+ } else {
1144
+ const [key, { getter }] = Object.entries(
1145
+ typeof groups === "string" ? { [groups]: {} } : groups
1146
+ )[0];
1147
+ const raw = item.get(key)?.index(0);
1148
+ let value = raw?.toValue();
1149
+ if (getter && typeof getter === "function") {
1150
+ value = getter(value, raw);
1151
+ }
1152
+ if (value) {
1153
+ group.push(value);
1154
+ }
1155
+ }
1156
+ if (!group.length) {
1157
+ group.push("Unknown");
1158
+ }
1159
+ group.forEach((g) => {
1160
+ const newGroup = typeof g === "string" ? { group: g } : g;
1161
+ if (!groupped[newGroup.group]) {
1162
+ groupped[newGroup.group] = new ClassName();
1163
+ groupped[newGroup.group].marker = newGroup.marker ?? newGroup.group;
1164
+ }
1165
+ groupped[newGroup.group]?.append(item);
1166
+ });
1167
+ });
1168
+ if (sort) {
1169
+ return Object.entries(groupped).toSorted(([ak, a], [bk, b]) => {
1170
+ if (typeof sort === "function") {
1171
+ return sort(a, ak, b, bk);
1172
+ }
1173
+ if (ak === "Unknown") {
1174
+ return 1;
1175
+ }
1176
+ if (bk === "Unknown") {
1177
+ return -1;
1178
+ }
1179
+ if (sort === "group") {
1180
+ return ak.localeCompare(bk);
1181
+ }
1182
+ return (b?.length ?? 0) - (a?.length ?? 0);
1183
+ }).reduce((acc, [key, value]) => {
1184
+ if (minOccurance > -1 && (value?.length ?? 0) < minOccurance) {
1185
+ const otherKey = lessOccuranceLabel;
1186
+ acc[otherKey] = acc[otherKey] && value ? acc[otherKey].merge(value) : value;
1187
+ } else {
1188
+ acc[key] = value;
1189
+ }
1190
+ return acc;
1191
+ }, {});
1441
1192
  }
1442
- if (this?.isGenoPro()) {
1443
- const name = this.getGenoProTreeName();
1444
- if (name) return name;
1193
+ return groupped;
1194
+ }
1195
+ findIndex(item) {
1196
+ if (!item?.id) {
1197
+ return -1;
1445
1198
  }
1446
- return this.getUniversalTreeName();
1199
+ return this.keys().findIndex((key) => key === item.id);
1200
+ }
1201
+ getItems() {
1202
+ return this.items;
1203
+ }
1204
+ toJson(tag, options) {
1205
+ const json = this.toObject(tag, options);
1206
+ return JSON.stringify(json);
1207
+ }
1208
+ toObject(tag, options) {
1209
+ const json = {};
1210
+ this.entries().forEach(([_, item]) => {
1211
+ const validTag = getValidTag(tag);
1212
+ if (json[validTag]) {
1213
+ if (!Array.isArray(json[validTag])) {
1214
+ json[validTag] = [json[validTag]];
1215
+ }
1216
+ json[validTag].push(
1217
+ item.toObject(validTag, options)
1218
+ );
1219
+ } else {
1220
+ json[validTag] = item.toObject(
1221
+ validTag,
1222
+ options
1223
+ );
1224
+ }
1225
+ });
1226
+ return json;
1227
+ }
1228
+ toGedcom(tag, level = 0, options) {
1229
+ const gedcom = this.toGedcomLines(tag, level, options);
1230
+ return gedcom.join("\n");
1231
+ }
1232
+ toGedcomLines(tag, level = 0, options) {
1233
+ const gedcom = [];
1234
+ this.entries().forEach(([_, item]) => {
1235
+ const validTag = getValidTag(tag);
1236
+ const xref = item.id;
1237
+ const payload = item.exportValue?.();
1238
+ if (xref) {
1239
+ gedcom.push(
1240
+ `${level} ${xref} ${validTag}${payload ? ` ${payload}` : ""}`
1241
+ );
1242
+ } else {
1243
+ gedcom.push(
1244
+ `${level} ${validTag}${payload ? ` ${payload}` : ""}`
1245
+ );
1246
+ }
1247
+ gedcom.push(
1248
+ ...item.toGedcomLines(
1249
+ validTag,
1250
+ level + 1,
1251
+ options
1252
+ )
1253
+ );
1254
+ });
1255
+ return gedcom;
1256
+ }
1257
+ toValue() {
1258
+ const propList = new _List();
1259
+ this.entries().forEach(([key, item]) => {
1260
+ propList.item(key, item?.toValue());
1261
+ });
1262
+ return propList;
1263
+ }
1264
+ // @deprecated use get instead
1265
+ toProp(tag) {
1266
+ return this.get(tag);
1267
+ }
1268
+ toList() {
1269
+ return new _List().concat(this.items);
1270
+ }
1271
+ toValueList() {
1272
+ const newList = new _List();
1273
+ this.values().forEach((value) => {
1274
+ value.value && newList.item(value.value, value);
1275
+ });
1276
+ return newList;
1277
+ }
1278
+ };
1279
+
1280
+ // src/utils/get-all-prop.ts
1281
+ var getAllProp = (indi, prop) => {
1282
+ return prop && (indi?.get(prop)?.toList().values() ?? []).filter(Boolean) || [];
1283
+ };
1284
+
1285
+ // src/utils/date-formatter.ts
1286
+ var ACCEPTED_DATE_FORMATS = [
1287
+ "yyyy.MM.dd.",
1288
+ "yyyy-MM-dd",
1289
+ "MM/dd/yyyy"
1290
+ ];
1291
+ var ACCEPTED_DATE_FORMATS_REGEX = new RegExp(
1292
+ `(${ACCEPTED_DATE_FORMATS.map(
1293
+ (acceptedFormat) => acceptedFormat.replace(/[^.\-/]/g, "\\d")
1294
+ ).join("|")})`,
1295
+ "gi"
1296
+ );
1297
+ var firstDate = (date) => {
1298
+ if (!date) return void 0;
1299
+ if (isCommonDate(date)) return date;
1300
+ if (date instanceof List) {
1301
+ return firstDate(
1302
+ date.index(0)
1303
+ );
1304
+ }
1305
+ const node = date;
1306
+ if (typeof node.toValue === "function") {
1307
+ return node;
1447
1308
  }
1309
+ return void 0;
1448
1310
  };
1449
- var createProxy = (target) => {
1450
- return new Proxy(target, {
1451
- get(t, prop, receiver) {
1452
- if (prop === "unwrapped") {
1453
- return target;
1454
- }
1455
- if (prop in t) {
1456
- return Reflect.get(t, prop, receiver);
1457
- }
1458
- if (!isOnlyMainProp(prop)) {
1459
- const ref = t.ref;
1460
- if (ref && prop in ref) {
1461
- return Reflect.get(ref, prop, receiver);
1311
+ var dateNote = (date, shortNote = true) => {
1312
+ if (date && typeof date.toNote === "function") {
1313
+ return date.toNote(shortNote) ?? "";
1314
+ }
1315
+ return "";
1316
+ };
1317
+ var commonDateFormatter = (date, format2 = "yyyy", prefix = "") => {
1318
+ const used = firstDate(date);
1319
+ const formattedDate = used?.toValue(format2);
1320
+ if (!formattedDate) {
1321
+ return void 0;
1322
+ }
1323
+ return `${prefix}${formattedDate}`.trim();
1324
+ };
1325
+ var noteDateFormatter = (date, format2 = "yyyy", prefix = "", shortNote = true, showNote = true) => {
1326
+ const used = firstDate(date);
1327
+ const rawDate = commonDateFormatter(used, format2, "");
1328
+ if (!rawDate) {
1329
+ return void 0;
1330
+ }
1331
+ const note = dateNote(used, shortNote);
1332
+ return `${prefix}${note && showNote ? i18n.t(`${note} {{date}}`, { date: rawDate }) : rawDate}`;
1333
+ };
1334
+ var marriageDateFormatter = (fam, showDays = false, showPlaces = false, shortNote = true, showNote = true, showDisplayTextIfNoDate = true, showAllEvents = false, dateFormatPattern) => {
1335
+ const fullDateFormat = i18n.t("dateFormat");
1336
+ const marriageDates = [];
1337
+ const marriagePlaces = [];
1338
+ const marriages = (fam?.toValueList().values() ?? []).flatMap((fam2) => {
1339
+ const family = fam2;
1340
+ const marrEvents = (family?.MARR?.toList().values() ?? []).filter(
1341
+ Boolean
1342
+ );
1343
+ if (marrEvents.length === 0) {
1344
+ const displayText = family?.get("DISPLAYTEXT")?.toValue() ?? "";
1345
+ if (showDisplayTextIfNoDate && displayText) {
1346
+ const displayDate = displayText.match(
1347
+ ACCEPTED_DATE_FORMATS_REGEX
1348
+ )?.[0];
1349
+ if (displayDate) {
1350
+ marriagePlaces.push(displayText.replace(displayDate, ""));
1351
+ const marriageDate = createCommonDate(
1352
+ fam2?.getGedcom(),
1353
+ void 0,
1354
+ family
1355
+ );
1356
+ marriageDate.value = displayDate;
1357
+ marriageDates.push(marriageDate);
1358
+ } else {
1359
+ marriagePlaces.push(displayText);
1360
+ marriageDates.push(void 0);
1361
+ return ["\u221E"];
1462
1362
  }
1463
1363
  }
1364
+ return [];
1464
1365
  }
1366
+ const eventsToProcess = showAllEvents ? marrEvents : marrEvents.slice(0, 1);
1367
+ return eventsToProcess.map((marrEvent) => {
1368
+ const marriageEventDetail = marrEvent;
1369
+ const marriageDate = marriageEventDetail?.DATE;
1370
+ const marriagePlace = showPlaces ? marriageEventDetail?.PLAC?.value : void 0;
1371
+ marriagePlaces.push(marriagePlace);
1372
+ marriageDates.push(marriageDate);
1373
+ const formattedDate = noteDateFormatter(
1374
+ marriageDate,
1375
+ showDays ? fullDateFormat : "yyyy",
1376
+ "\u221E",
1377
+ shortNote,
1378
+ showNote
1379
+ );
1380
+ return formattedDate ? formattedDate : void 0;
1381
+ });
1465
1382
  });
1383
+ const marriageArray = marriages.length ? marriages.filter(Boolean) : [];
1384
+ const datesArray = marriageArray.filter(Boolean);
1385
+ const placesArray = marriagePlaces.filter(Boolean);
1386
+ const dates = datesArray.join(" ").trim();
1387
+ return {
1388
+ rawArray: marriageDates,
1389
+ inArray: datesArray,
1390
+ inOrder: dates,
1391
+ places: placesArray,
1392
+ marriage: marriageArray,
1393
+ ...showPlaces ? {
1394
+ marriages,
1395
+ marriagePlaces
1396
+ } : {}
1397
+ };
1466
1398
  };
1467
- var createCommon = (gedcom, id, main, parent) => {
1468
- return createProxy(
1469
- new Common(gedcom, id, main, parent)
1399
+ var dateFormatter = (indi, showMarriages = false, showDays = false, showPlaces = false, shortNote = true, showNote = true, isOnStage, showAllEvents = false, dateFormatPattern) => {
1400
+ const fullDateFormat = i18n.t("dateFormat");
1401
+ const birthEvents = getAllProp(indi, "BIRT");
1402
+ const firstBirthValue = birthEvents[0];
1403
+ const deathEvents = getAllProp(indi, "DEAT");
1404
+ const firstDeathValue = deathEvents[0];
1405
+ const birth = commonDateFormatter(
1406
+ firstBirthValue?.DATE,
1407
+ showDays ? fullDateFormat : "yyyy"
1470
1408
  );
1471
- };
1472
- var isOnlyMainProp = (key) => {
1473
- return key === "_gedcom" || key === "_main" || key === "_parent" || key === "_uniqueId" || key === "_type" || key === "_refs" || key === "id" || key === "_id";
1474
- };
1475
- var isValidKey = (common, key) => {
1476
- const prop = get(common, key);
1477
- return key !== "_gedcom" && key !== "_main" && key !== "_parent" && key !== "_uniqueId" && key !== "_refs" && key !== "_type" && !(typeof key === "string" && isGedcomTrailerTag(key)) && (key === "id" || key === "_id" || key === "value" || key === "_value" || prop instanceof Common || prop instanceof List);
1478
- };
1479
- var getValidKeys = (common) => {
1480
- return Object.keys(common).filter((key) => {
1481
- return isValidKey(common, key);
1409
+ const birthPlace = showPlaces ? firstBirthValue?.PLAC?.value : void 0;
1410
+ const death = commonDateFormatter(
1411
+ firstDeathValue?.DATE,
1412
+ showDays ? fullDateFormat : "yyyy"
1413
+ );
1414
+ const deathPlace = showPlaces ? firstDeathValue?.PLAC?.value : void 0;
1415
+ const marriagePlaces = [];
1416
+ const marriages = [];
1417
+ if (showMarriages || isOnStage) {
1418
+ (indi?.get("FAMS")?.toValueList().values() ?? []).forEach((fam) => {
1419
+ const famRef = fam?.ref;
1420
+ const marriageDates = marriageDateFormatter(
1421
+ famRef,
1422
+ showDays,
1423
+ showPlaces,
1424
+ shortNote,
1425
+ showNote,
1426
+ true,
1427
+ showAllEvents);
1428
+ marriages.push(...marriageDates?.marriage ?? []);
1429
+ marriagePlaces.push(...marriageDates?.marriagePlaces ?? []);
1430
+ });
1431
+ }
1432
+ const birthStrings = [];
1433
+ const birthPlaces = [];
1434
+ const eventsToProcess = showAllEvents ? birthEvents : birthEvents.slice(0, 1);
1435
+ eventsToProcess.forEach((birthEvent) => {
1436
+ const birthDateNode = firstDate(birthEvent?.DATE);
1437
+ const birthDate = commonDateFormatter(
1438
+ birthDateNode,
1439
+ showDays ? fullDateFormat : "yyyy"
1440
+ );
1441
+ const birthEventNote = dateNote(birthDateNode, shortNote);
1442
+ const birthEventPlace = showPlaces ? birthEvent?.PLAC?.value : void 0;
1443
+ if (birthDate) {
1444
+ birthStrings.push(
1445
+ `*${birthEventNote && showNote ? i18n.t(`${birthEventNote} {{date}}`, {
1446
+ date: birthDate
1447
+ }) : birthDate}`
1448
+ );
1449
+ }
1450
+ birthPlaces.push(birthEventPlace);
1482
1451
  });
1452
+ const deathStrings = [];
1453
+ const deathPlaces = [];
1454
+ const deathEventsToProcess = showAllEvents ? deathEvents : deathEvents.slice(0, 1);
1455
+ deathEventsToProcess.forEach((deathEvent) => {
1456
+ const deathDateNode = firstDate(deathEvent?.DATE);
1457
+ const deathDate = commonDateFormatter(
1458
+ deathDateNode,
1459
+ showDays ? fullDateFormat : "yyyy"
1460
+ );
1461
+ const deathEventNote = dateNote(deathDateNode, shortNote);
1462
+ const deathEventPlace = showPlaces ? deathEvent?.PLAC?.value : void 0;
1463
+ if (deathDate) {
1464
+ deathStrings.push(
1465
+ `\u2020${deathEventNote && showNote ? i18n.t(`${deathEventNote} {{date}}`, {
1466
+ date: deathDate
1467
+ }) : deathDate}`
1468
+ );
1469
+ }
1470
+ deathPlaces.push(deathEventPlace);
1471
+ });
1472
+ const birthString = birthStrings[0] ?? "";
1473
+ const deathString = deathStrings[0] ?? "";
1474
+ const marriageArray = marriages.length ? marriages.filter(Boolean) : [];
1475
+ const datesArray = !birth && !death ? marriageArray : [...birthStrings, ...marriageArray, ...deathStrings];
1476
+ const placesArray = !birth && !death ? marriagePlaces : [...birthPlaces, ...marriagePlaces, ...deathPlaces];
1477
+ const nonNullDatesArray = datesArray.filter(Boolean);
1478
+ const dates = nonNullDatesArray.join(" ").trim();
1479
+ return {
1480
+ inArray: nonNullDatesArray,
1481
+ inOrder: dates,
1482
+ places: placesArray,
1483
+ birth: birthString,
1484
+ marriage: marriageArray,
1485
+ death: deathString,
1486
+ ...showPlaces ? {
1487
+ birthPlace,
1488
+ deathPlace,
1489
+ ...showMarriages ? { marriages, marriagePlaces } : {}
1490
+ } : {},
1491
+ ...showAllEvents ? {
1492
+ births: birthStrings,
1493
+ birthPlaces,
1494
+ deaths: deathStrings,
1495
+ deathPlaces
1496
+ } : {}
1497
+ };
1483
1498
  };
1484
- var getValidTag = (tag) => {
1485
- return tag.replace(/^@@/, "");
1486
- };
1487
- var getListTag = (tag) => {
1488
- return `@@${tag}`;
1499
+
1500
+ // src/utils/range.ts
1501
+ var toTuple = (r) => {
1502
+ if (Array.isArray(r)) {
1503
+ const [s2, e2] = r;
1504
+ return [
1505
+ s2 === void 0 ? void 0 : Number(s2),
1506
+ e2 === void 0 ? void 0 : Number(e2)
1507
+ ];
1508
+ }
1509
+ if (r === "-" || r === "") return [void 0, void 0];
1510
+ const [s, e] = r.split("-");
1511
+ return [s ? Number(s) : void 0, e ? Number(e) : void 0];
1489
1512
  };
1490
- var isId = (string) => {
1491
- return ID_REG.test(string);
1513
+ var isValidRange = (range) => {
1514
+ if (!Array.isArray(range) && range !== "" && range !== "-" && !/^\d*-\d*$/.test(range)) {
1515
+ return false;
1516
+ }
1517
+ if (Array.isArray(range) && range.some((r) => typeof r === "string" && isNaN(Number(r)))) {
1518
+ return false;
1519
+ }
1520
+ return true;
1492
1521
  };
1493
- var idGetter = (id) => {
1494
- if (id === void 0) {
1495
- return void 0;
1522
+ var inRange = (year, range, trueIfNoYear = false) => {
1523
+ if (!isValidRange(range)) {
1524
+ return false;
1496
1525
  }
1497
- let newId = `${id}`;
1498
- const parts = newId?.match(ID_GETTER_REG)?.groups;
1499
- if (!parts?.at && !parts?.letter) {
1500
- newId = `@U${newId}`;
1501
- } else if (!parts.at) {
1502
- newId = `@${newId}`;
1503
- } else if (!parts.letter) {
1504
- newId = newId.replace(/^@/, "@U");
1526
+ if (!year) {
1527
+ return trueIfNoYear;
1505
1528
  }
1506
- if (!newId.endsWith("@")) {
1507
- newId = `${newId}@`;
1529
+ const [start = -Infinity, end = Infinity] = toTuple(range);
1530
+ if (!start && !end) {
1531
+ return true;
1508
1532
  }
1509
- return newId;
1533
+ if (start && !end) {
1534
+ return Number(year) >= Number(start);
1535
+ }
1536
+ if (!start && end) {
1537
+ return Number(year) <= Number(end);
1538
+ }
1539
+ return Number(start) <= Number(year) && Number(year) <= Number(end);
1510
1540
  };
1511
1541
 
1512
1542
  // src/classes/date.ts
@@ -1560,6 +1590,8 @@ var formatDateWithComponents = (date, hasDay, hasMonth, hasYear, noteValue, base
1560
1590
  var CommonDate = class extends Common {
1561
1591
  constructor(gedcom, id, main, parent) {
1562
1592
  super(gedcom, id, main, parent);
1593
+ /** One DATE per event — merge must not turn this into a List. */
1594
+ this.isListable = false;
1563
1595
  delete this.id;
1564
1596
  }
1565
1597
  get calendar() {
@@ -5108,14 +5140,15 @@ var Indi = class extends Common {
5108
5140
  }
5109
5141
  return foundPath;
5110
5142
  }
5111
- kinship(other, showMainPerson, lang = "en", entirePath, displayName = "givenname") {
5143
+ kinship(other, showMainPerson, lang = "en", entirePath, displayName = "givenname", nameOrder) {
5112
5144
  const KinshipTranslatorClass2 = getKinshipTranslatorClass();
5113
5145
  const translator = new KinshipTranslatorClass2(
5114
5146
  this,
5115
5147
  other,
5116
5148
  lang,
5117
5149
  entirePath,
5118
- showMainPerson ? displayName : void 0
5150
+ showMainPerson ? displayName : void 0,
5151
+ nameOrder
5119
5152
  );
5120
5153
  return translator.translate(!!showMainPerson);
5121
5154
  }
@@ -6039,11 +6072,12 @@ var hungarianOrdinalize = (n) => {
6039
6072
 
6040
6073
  // src/kinship-translator/kinship-translator.basic.ts
6041
6074
  var KinshipTranslatorBasic = class {
6042
- constructor(path, displayName = "givenname") {
6075
+ constructor(path, displayName = "givenname", nameOrder = "first-last") {
6043
6076
  this.isOfSpouse = false;
6044
6077
  this.isSpouseOf = false;
6045
6078
  this._path = path;
6046
6079
  this.displayName = displayName;
6080
+ this.nameOrder = nameOrder;
6047
6081
  }
6048
6082
  get path() {
6049
6083
  return this._path;
@@ -6466,7 +6500,7 @@ var KinshipTranslatorHU = class extends KinshipTranslatorBasic {
6466
6500
  }
6467
6501
  of(relation) {
6468
6502
  const formattedName = nameFormatter(this.path?.[0]?.indi, {
6469
- nameOrder: "last-first"
6503
+ nameOrder: this.nameOrder
6470
6504
  });
6471
6505
  let name = formattedName.inOrder.filter(Boolean).join(" ");
6472
6506
  if (this.displayName === "none") {
@@ -6680,7 +6714,7 @@ var KinshipTranslatorEn = class extends KinshipTranslatorBasic {
6680
6714
  }
6681
6715
  of(relation) {
6682
6716
  const formattedName = nameFormatter(this.path?.[0]?.indi, {
6683
- nameOrder: "first-last"
6717
+ nameOrder: this.nameOrder
6684
6718
  });
6685
6719
  let name = formattedName.inOrder.filter(Boolean).join(" ");
6686
6720
  if (this.displayName === "none") {
@@ -6896,7 +6930,7 @@ var KinshipTranslatorDe = class extends KinshipTranslatorBasic {
6896
6930
  }
6897
6931
  of(relation) {
6898
6932
  const formattedName = nameFormatter(this.path?.[0]?.indi, {
6899
- nameOrder: "first-last"
6933
+ nameOrder: this.nameOrder
6900
6934
  });
6901
6935
  let name = formattedName.inOrder.filter(Boolean).join(" ");
6902
6936
  if (this.displayName === "none") {
@@ -7113,7 +7147,7 @@ var KinshipTranslatorFr = class extends KinshipTranslatorBasic {
7113
7147
  }
7114
7148
  of(relation) {
7115
7149
  const formattedName = nameFormatter(this.path?.[0]?.indi, {
7116
- nameOrder: "first-last"
7150
+ nameOrder: this.nameOrder
7117
7151
  });
7118
7152
  let name = formattedName.inOrder.filter(Boolean).join(" ");
7119
7153
  if (this.displayName === "none") {
@@ -7451,7 +7485,7 @@ var KinshipTranslatorEs = class extends KinshipTranslatorBasic {
7451
7485
  }
7452
7486
  of(relation) {
7453
7487
  const formattedName = nameFormatter(this.path?.[0]?.indi, {
7454
- nameOrder: "first-last"
7488
+ nameOrder: this.nameOrder
7455
7489
  });
7456
7490
  let name = formattedName.inOrder.filter(Boolean).join(" ");
7457
7491
  if (this.displayName === "none") {
@@ -7758,7 +7792,7 @@ var KinshipTranslatorRu = class extends KinshipTranslatorBasic {
7758
7792
  }
7759
7793
  of(relation) {
7760
7794
  const formattedName = nameFormatter(this.path?.[0]?.indi, {
7761
- nameOrder: "first-last"
7795
+ nameOrder: this.nameOrder
7762
7796
  });
7763
7797
  let name = formattedName.inOrder.filter(Boolean).join(" ");
7764
7798
  if (this.displayName === "none") {
@@ -7994,7 +8028,7 @@ var KinshipTranslatorJa = class extends KinshipTranslatorBasic {
7994
8028
  }
7995
8029
  of(relation) {
7996
8030
  const formattedName = nameFormatter(this.path?.[0]?.indi, {
7997
- nameOrder: "last-first"
8031
+ nameOrder: this.nameOrder
7998
8032
  });
7999
8033
  let name = formattedName.inOrder.filter(Boolean).join(" ");
8000
8034
  if (this.displayName === "none") {
@@ -8243,7 +8277,7 @@ var KinshipTranslatorZh = class extends KinshipTranslatorBasic {
8243
8277
  }
8244
8278
  of(relation) {
8245
8279
  const formattedName = nameFormatter(this.path?.[0]?.indi, {
8246
- nameOrder: "last-first"
8280
+ nameOrder: this.nameOrder
8247
8281
  });
8248
8282
  let name = formattedName.inOrder.filter(Boolean).join(" ");
8249
8283
  if (this.displayName === "none") {
@@ -8312,22 +8346,30 @@ var KinshipTranslatorZh = class extends KinshipTranslatorBasic {
8312
8346
  };
8313
8347
 
8314
8348
  // src/kinship-translator/kinship-translator.ts
8349
+ var nameOrderForLang = (lang) => lang === "hu" || lang === "ja" || lang === "zh" ? "last-first" : "first-last";
8315
8350
  var KinshipTranslator = class {
8316
- constructor(person1, person2, lang = "en", entirePath, displayName = "givenname") {
8351
+ constructor(person1, person2, lang = "en", entirePath, displayName = "givenname", nameOrder) {
8317
8352
  this.displayName = displayName;
8318
8353
  this.lang = lang;
8319
8354
  this.entirePath = entirePath;
8320
8355
  this.person1 = person1;
8321
8356
  this.person2 = typeof person2 === "string" ? this.person1.getGedcom()?.indi(person2) : person2;
8322
8357
  this.path = this.person1.path(this.person2);
8358
+ const order = nameOrder ?? nameOrderForLang(lang);
8359
+ this.nameOrder = order;
8323
8360
  if (translators_exports[lang]) {
8324
8361
  this.translator = new translators_exports[lang](
8325
8362
  this.path ?? [],
8326
- displayName
8363
+ displayName,
8364
+ order
8327
8365
  );
8328
8366
  }
8329
8367
  if (!this.translator) {
8330
- this.translator = new KinshipTranslatorEn(this.path ?? [], displayName);
8368
+ this.translator = new KinshipTranslatorEn(
8369
+ this.path ?? [],
8370
+ displayName,
8371
+ order
8372
+ );
8331
8373
  }
8332
8374
  }
8333
8375
  get path0() {
@@ -8372,14 +8414,16 @@ var KinshipTranslator = class {
8372
8414
  showMainPerson,
8373
8415
  this.lang,
8374
8416
  false,
8375
- this.displayName
8417
+ this.displayName,
8418
+ this.nameOrder
8376
8419
  ),
8377
8420
  relative: this.path?.[index - 1]?.indi.kinship(
8378
8421
  path2.indi.id,
8379
8422
  showMainPerson,
8380
8423
  this.lang,
8381
8424
  false,
8382
- this.displayName
8425
+ this.displayName,
8426
+ this.nameOrder
8383
8427
  )
8384
8428
  }));
8385
8429
  return path;