gedcom-d3 2.0.7 → 2.0.8
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.
- package/README.md +129 -7
- package/d3ize.js +553 -556
- package/package.json +35 -35
package/d3ize.js
CHANGED
|
@@ -1,625 +1,622 @@
|
|
|
1
|
-
const d3ize = tree => {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
const d3ize = (tree) => {
|
|
2
|
+
const notes = tree.filter(hasTag('NOTE'))
|
|
3
|
+
let surnameList = []
|
|
4
|
+
const peopleNodes = tree
|
|
5
|
+
.filter(hasTag('INDI'))
|
|
6
|
+
.map((p) => toNode(p, notes, surnameList))
|
|
7
|
+
const families = tree.filter(hasTag('FAM'))
|
|
8
|
+
const links = families.reduce((memo, family) => {
|
|
9
|
+
return memo.concat(familyLinks(family, peopleNodes))
|
|
10
|
+
}, [])
|
|
11
|
+
assignFy(peopleNodes, links)
|
|
12
|
+
return {
|
|
13
|
+
nodes: peopleNodes,
|
|
14
|
+
links: links,
|
|
15
|
+
families: families,
|
|
16
|
+
surnameList: surnameList,
|
|
17
|
+
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// Tag search function
|
|
21
|
-
const hasTag = val => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
const hasTag = (val) => {
|
|
22
|
+
return (node) => {
|
|
23
|
+
return node.tag === val
|
|
24
|
+
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// Data search function
|
|
28
|
-
const hasData = val => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
const hasData = (val) => {
|
|
29
|
+
return (node) => {
|
|
30
|
+
return node.data === val
|
|
31
|
+
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// ID search function
|
|
35
|
-
const hasID = val => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
const hasID = (val) => {
|
|
36
|
+
return (node) => {
|
|
37
|
+
return node.id === val
|
|
38
|
+
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
const assignFy = (peopleNodes, links) => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
42
|
+
// YOB known
|
|
43
|
+
let yesyob = peopleNodes.filter((p) => {
|
|
44
|
+
return p.yob !== '?' && !isNaN(+p.yob)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
yesyob.forEach((p) => (p.fy = +p.yob))
|
|
48
|
+
|
|
49
|
+
// YOB unknown
|
|
50
|
+
let noyob = peopleNodes.filter((p) => {
|
|
51
|
+
return p.yob === '?'
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
let count = 10
|
|
55
|
+
|
|
56
|
+
// Cycle through list, adding fy until all complete
|
|
57
|
+
while (noyob.length > 0 && count > 0) {
|
|
58
|
+
let tempnoyob = noyob.slice()
|
|
59
|
+
|
|
60
|
+
tempnoyob.forEach((p, index) => {
|
|
61
|
+
// Build array of family
|
|
62
|
+
let tpFamily = []
|
|
63
|
+
|
|
64
|
+
links.forEach((link) => {
|
|
65
|
+
if (link.source == p.id) {
|
|
66
|
+
tpFamily.push({
|
|
67
|
+
pRole: 'source',
|
|
68
|
+
pType: link.sourceType,
|
|
69
|
+
other: link.target,
|
|
70
|
+
oType: link.targetType,
|
|
71
|
+
})
|
|
72
|
+
} else if (link.target == p.id) {
|
|
73
|
+
tpFamily.push({
|
|
74
|
+
pRole: 'target',
|
|
75
|
+
pType: link.targetType,
|
|
76
|
+
other: link.source,
|
|
77
|
+
oType: link.sourceType,
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// Check family for YOB
|
|
83
|
+
tpFamily.forEach((member) => {
|
|
84
|
+
// USE SOME() INSTEAD OF FOREACH!!!
|
|
85
|
+
peopleNodes.forEach((person) => {
|
|
86
|
+
// USE SOME() INSTEAD OF FOREACH!!!
|
|
87
|
+
if (person.id == member.other && person.fy !== undefined) {
|
|
88
|
+
// Person is source
|
|
89
|
+
if (member.pRole === 'source') {
|
|
90
|
+
// Person is husband
|
|
91
|
+
if (member.pType === 'HUSB' && member.oType === 'WIFE') {
|
|
92
|
+
p.fy = +person.fy - 3
|
|
93
|
+
|
|
94
|
+
// Person is father
|
|
95
|
+
} else if (member.pType === 'HUSB' && member.oType === 'CHIL') {
|
|
96
|
+
p.fy = +person.fy - 30
|
|
97
|
+
|
|
98
|
+
// Person is mother
|
|
99
|
+
} else if (member.pType === 'WIFE') {
|
|
100
|
+
p.fy = +person.fy - 27
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Person is target
|
|
104
|
+
} else if (member.pRole === 'target') {
|
|
105
|
+
// Person is wife
|
|
106
|
+
if (member.pType === 'WIFE' && member.oType === 'HUSB') {
|
|
107
|
+
p.fy = +person.fy + 3
|
|
108
|
+
|
|
109
|
+
// Person is child of father
|
|
110
|
+
} else if (member.pType === 'CHIL' && member.oType === 'HUSB') {
|
|
111
|
+
p.fy = +person.fy + 30
|
|
112
|
+
|
|
113
|
+
// Person is child of mother
|
|
114
|
+
} else if (member.pType === 'CHIL' && member.oType === 'WIFE') {
|
|
115
|
+
p.fy = +person.fy + 27
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
if (p.fy !== undefined) {
|
|
122
|
+
noyob.splice(index, index + 1)
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
count -= 1
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const convertFy = (peopleNodes) => {
|
|
129
|
+
const fyRatio = (peopleNodes) => {
|
|
130
|
+
if (peopleNodes.length <= 50) {
|
|
131
|
+
return 3
|
|
132
|
+
} else if (peopleNodes.length > 50 && peopleNodes.length <= 150) {
|
|
133
|
+
return 4
|
|
134
|
+
} else if (peopleNodes.length > 150 && peopleNodes.length <= 250) {
|
|
135
|
+
return 5
|
|
136
|
+
} else if (peopleNodes.length > 250) {
|
|
137
|
+
return 6
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
let allFy = []
|
|
141
|
+
peopleNodes.forEach((p) => {
|
|
142
|
+
if (p.fy !== undefined) {
|
|
143
|
+
p.fy = p.fy * fyRatio(peopleNodes)
|
|
144
|
+
allFy.push(p.fy)
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
let total = 0
|
|
149
|
+
allFy.forEach((fy) => (total += fy))
|
|
150
|
+
let average = total / allFy.length
|
|
151
|
+
|
|
152
|
+
peopleNodes.forEach((p) => {
|
|
153
|
+
if (p.fy !== undefined) {
|
|
154
|
+
p.fy = -(p.fy - average)
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
convertFy(peopleNodes)
|
|
153
159
|
}
|
|
154
160
|
|
|
155
161
|
// Get title
|
|
156
|
-
const getTitle = p => {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
162
|
+
const getTitle = (p) => {
|
|
163
|
+
const title = p.tree.filter(hasTag('TITL')) || []
|
|
164
|
+
if (title.length > 0) {
|
|
165
|
+
return title[title.length - 1].data
|
|
166
|
+
}
|
|
161
167
|
}
|
|
162
168
|
|
|
163
169
|
// Get full name
|
|
164
|
-
const getName = p => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
170
|
+
const getName = (p) => {
|
|
171
|
+
let nameNode = (p.tree.filter(hasTag('NAME')) || [])[0]
|
|
172
|
+
if (nameNode) {
|
|
173
|
+
return nameNode.data.replace(/\//g, '')
|
|
174
|
+
} else {
|
|
175
|
+
return '?'
|
|
176
|
+
}
|
|
171
177
|
}
|
|
172
178
|
|
|
173
179
|
// Get first name
|
|
174
|
-
const getFirstName = p => {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
} else {
|
|
194
|
-
return '?';
|
|
195
|
-
}
|
|
180
|
+
const getFirstName = (p) => {
|
|
181
|
+
// Find 'NAME' tag
|
|
182
|
+
const nameNode = (p.tree.filter(hasTag('NAME')) || [])[0]
|
|
183
|
+
if (nameNode) {
|
|
184
|
+
// Find 'GIVN' tag
|
|
185
|
+
let firstNameNode = (nameNode.tree.filter(hasTag('GIVN')) || [])[0]
|
|
186
|
+
if (firstNameNode) {
|
|
187
|
+
// Remove middle name
|
|
188
|
+
if (firstNameNode.data.search(' ') !== -1) {
|
|
189
|
+
return firstNameNode.data.slice(0, firstNameNode.data.search(' '))
|
|
190
|
+
} else {
|
|
191
|
+
return firstNameNode.data
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
return '?'
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
return '?'
|
|
198
|
+
}
|
|
196
199
|
}
|
|
197
200
|
|
|
198
201
|
// Get surname
|
|
199
|
-
const getSurname = p => {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
202
|
+
const getSurname = (p) => {
|
|
203
|
+
// Find 'NAME' tag
|
|
204
|
+
const nameNode = (p.tree.filter(hasTag('NAME')) || [])[0]
|
|
205
|
+
if (nameNode) {
|
|
206
|
+
// Find 'SURN' tag
|
|
207
|
+
const surnameNode = (nameNode.tree.filter(hasTag('SURN')) || [])[0]
|
|
208
|
+
|
|
209
|
+
// If surname listed
|
|
210
|
+
if (surnameNode) {
|
|
211
|
+
// Remove alternate surnames
|
|
212
|
+
if (surnameNode.data.search(',') !== -1) {
|
|
213
|
+
return surnameNode.data.slice(0, surnameNode.data.search(','))
|
|
214
|
+
} else {
|
|
215
|
+
return surnameNode.data
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Derive surname from name
|
|
219
|
+
} else {
|
|
220
|
+
nameArr = nameNode.data.split(' ')
|
|
221
|
+
|
|
222
|
+
// Look for forward slashes
|
|
223
|
+
let isSlashes = nameArr.some((str) => str[0] === '/')
|
|
224
|
+
if (isSlashes) {
|
|
225
|
+
return nameArr.find((str) => str[0] === '/').replace(/\//g, '')
|
|
226
|
+
|
|
227
|
+
// no slashes, use final item in array
|
|
228
|
+
} else {
|
|
229
|
+
nameArr[nameArr.length - 1] = nameArr[nameArr.length - 1].replace(
|
|
230
|
+
/\//g,
|
|
231
|
+
''
|
|
232
|
+
)
|
|
233
|
+
return nameArr.length > 1 ? nameArr[nameArr.length - 1] : 'Hrm'
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
return '?'
|
|
238
|
+
}
|
|
236
239
|
}
|
|
237
240
|
|
|
238
241
|
// Get gender
|
|
239
|
-
const getGender = p => {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
242
|
+
const getGender = (p) => {
|
|
243
|
+
// Find 'SEX' tag
|
|
244
|
+
let genderNode = (p.tree.filter(hasTag('SEX')) || [])[0]
|
|
245
|
+
if (genderNode) {
|
|
246
|
+
return genderNode.data
|
|
247
|
+
} else {
|
|
248
|
+
return 'Unknown'
|
|
249
|
+
}
|
|
248
250
|
}
|
|
249
251
|
|
|
250
252
|
// Get date of birth
|
|
251
|
-
const getDOB = p => {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
return '?';
|
|
266
|
-
}
|
|
253
|
+
const getDOB = (p) => {
|
|
254
|
+
// Find 'BIRT' tag
|
|
255
|
+
let dobNode = (p.tree.filter(hasTag('BIRT')) || [])[0]
|
|
256
|
+
if (dobNode) {
|
|
257
|
+
// Find 'DATE' tag
|
|
258
|
+
let dateNode = (dobNode.tree.filter(hasTag('DATE')) || [])[0]
|
|
259
|
+
if (dateNode) {
|
|
260
|
+
return dateNode.data
|
|
261
|
+
} else {
|
|
262
|
+
return '?'
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
return '?'
|
|
266
|
+
}
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
// Get year of birth
|
|
270
|
-
const getYOB = p => {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
return '?';
|
|
285
|
-
}
|
|
270
|
+
const getYOB = (p) => {
|
|
271
|
+
// Find 'BIRT' tag
|
|
272
|
+
let dobNode = (p.tree.filter(hasTag('BIRT')) || [])[0]
|
|
273
|
+
if (dobNode) {
|
|
274
|
+
// Find 'DATE' tag
|
|
275
|
+
let dateNode = (dobNode.tree.filter(hasTag('DATE')) || [])[0]
|
|
276
|
+
if (dateNode) {
|
|
277
|
+
return dateNode.data.slice(-4)
|
|
278
|
+
} else {
|
|
279
|
+
return '?'
|
|
280
|
+
}
|
|
281
|
+
} else {
|
|
282
|
+
return '?'
|
|
283
|
+
}
|
|
286
284
|
}
|
|
287
285
|
|
|
288
286
|
// Get place of birth
|
|
289
|
-
const getPOB = p => {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
return '';
|
|
304
|
-
}
|
|
287
|
+
const getPOB = (p) => {
|
|
288
|
+
// Find 'BIRT' tag
|
|
289
|
+
let pobNode = (p.tree.filter(hasTag('BIRT')) || [])[0]
|
|
290
|
+
if (pobNode) {
|
|
291
|
+
// Find 'DATE' tag
|
|
292
|
+
let placeNode = (pobNode.tree.filter(hasTag('PLAC')) || [])[0]
|
|
293
|
+
if (placeNode) {
|
|
294
|
+
return placeNode.data
|
|
295
|
+
} else {
|
|
296
|
+
return ''
|
|
297
|
+
}
|
|
298
|
+
} else {
|
|
299
|
+
return ''
|
|
300
|
+
}
|
|
305
301
|
}
|
|
306
302
|
|
|
307
303
|
// Get date of death
|
|
308
|
-
const getDOD = p => {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
return 'Present';
|
|
331
|
-
}
|
|
304
|
+
const getDOD = (p) => {
|
|
305
|
+
// Find 'DEAT' tag
|
|
306
|
+
let dobNode = (p.tree.filter(hasTag('BIRT')) || [])[0]
|
|
307
|
+
let dodNode = (p.tree.filter(hasTag('DEAT')) || [])[0]
|
|
308
|
+
if (dodNode) {
|
|
309
|
+
// Find 'DATE' tag
|
|
310
|
+
let dateNode = (dodNode.tree.filter(hasTag('DATE')) || [])[0]
|
|
311
|
+
if (dateNode) {
|
|
312
|
+
return dateNode.data
|
|
313
|
+
} else {
|
|
314
|
+
return '?'
|
|
315
|
+
}
|
|
316
|
+
} else if (dobNode) {
|
|
317
|
+
let dateNode = (dobNode.tree.filter(hasTag('DATE')) || [])[0]
|
|
318
|
+
if (dateNode) {
|
|
319
|
+
return dateNode.data.slice(-4) + 100
|
|
320
|
+
} else {
|
|
321
|
+
return '?'
|
|
322
|
+
}
|
|
323
|
+
} else {
|
|
324
|
+
return 'Present'
|
|
325
|
+
}
|
|
332
326
|
}
|
|
333
327
|
|
|
334
328
|
// Get year of death
|
|
335
|
-
const getYOD = p => {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
return '?';
|
|
375
|
-
}
|
|
329
|
+
const getYOD = (p) => {
|
|
330
|
+
let thisYear = new Date().getFullYear()
|
|
331
|
+
|
|
332
|
+
// Find 'DEAT' tag
|
|
333
|
+
let dobNode = (p.tree.filter(hasTag('BIRT')) || [])[0]
|
|
334
|
+
let dodNode = (p.tree.filter(hasTag('DEAT')) || [])[0]
|
|
335
|
+
|
|
336
|
+
// If DEATH tag
|
|
337
|
+
if (dodNode) {
|
|
338
|
+
// Find 'DATE' tag
|
|
339
|
+
let dateNode = (dodNode.tree.filter(hasTag('DATE')) || [])[0]
|
|
340
|
+
|
|
341
|
+
// If death date listed
|
|
342
|
+
if (dateNode) {
|
|
343
|
+
return dateNode.data.slice(-4)
|
|
344
|
+
} else {
|
|
345
|
+
return '?'
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// BIRT tag, but no DEAT tag
|
|
349
|
+
} else if (dobNode && !dodNode) {
|
|
350
|
+
let dateNode = (dobNode.tree.filter(hasTag('DATE')) || [])[0]
|
|
351
|
+
|
|
352
|
+
// If DOB listed
|
|
353
|
+
if (dateNode) {
|
|
354
|
+
// If born > 100 yrs ago, call dead
|
|
355
|
+
if (dateNode.data.slice(-4) < thisYear - 100) {
|
|
356
|
+
return '?'
|
|
357
|
+
} else {
|
|
358
|
+
return 'Present'
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
361
|
+
return '?'
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// no DEAT or BIRT tag
|
|
365
|
+
} else {
|
|
366
|
+
return '?'
|
|
367
|
+
}
|
|
376
368
|
}
|
|
377
369
|
|
|
378
370
|
// Get place of birth
|
|
379
|
-
const getPOD = p => {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
return '';
|
|
394
|
-
}
|
|
371
|
+
const getPOD = (p) => {
|
|
372
|
+
// Find 'BIRT' tag
|
|
373
|
+
let podNode = (p.tree.filter(hasTag('DEAT')) || [])[0]
|
|
374
|
+
if (podNode) {
|
|
375
|
+
// Find 'DATE' tag
|
|
376
|
+
let placeNode = (podNode.tree.filter(hasTag('PLAC')) || [])[0]
|
|
377
|
+
if (placeNode) {
|
|
378
|
+
return placeNode.data
|
|
379
|
+
} else {
|
|
380
|
+
return ''
|
|
381
|
+
}
|
|
382
|
+
} else {
|
|
383
|
+
return ''
|
|
384
|
+
}
|
|
395
385
|
}
|
|
396
386
|
|
|
397
387
|
// Get relatives
|
|
398
|
-
const getFamilies = p => {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
388
|
+
const getFamilies = (p) => {
|
|
389
|
+
let families = []
|
|
390
|
+
let pediInfo
|
|
391
|
+
// If child
|
|
392
|
+
let familyNode1 = p.tree.filter(hasTag('FAMC')) || []
|
|
393
|
+
if (familyNode1) {
|
|
394
|
+
for (let i = 0; i < familyNode1.length; i++) {
|
|
395
|
+
if (familyNode1[i].tree.length > 0) {
|
|
396
|
+
// Get pedigree info
|
|
397
|
+
if (familyNode1[i].tree[0].tag == 'PEDI') {
|
|
398
|
+
pediInfo = {
|
|
399
|
+
frel: familyNode1[i].tree[0].data,
|
|
400
|
+
mrel: familyNode1[i].tree[0].data,
|
|
401
|
+
}
|
|
402
|
+
} else if (familyNode1[i].tree[0].tag == '_FREL') {
|
|
403
|
+
pediInfo = {
|
|
404
|
+
frel: familyNode1[i].tree[0].data,
|
|
405
|
+
mrel: familyNode1[i].tree[1].data,
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
families.push({ id: familyNode1[i].data, pedi: pediInfo })
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
let familyNode2 = p.tree.filter(hasTag('FAMS')) || []
|
|
414
|
+
if (familyNode2) {
|
|
415
|
+
for (let i = 0; i < familyNode2.length; i++) {
|
|
416
|
+
families.push({ id: familyNode2[i].data })
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return families
|
|
424
420
|
}
|
|
425
421
|
|
|
426
422
|
// Get color
|
|
427
423
|
const getColor = (p, surnameList) => {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
}
|
|
424
|
+
const colorList = [
|
|
425
|
+
'#ff7f50', // coral
|
|
426
|
+
'#00b4ff', // sky blue
|
|
427
|
+
'#fac641', // mexican egg yolk
|
|
428
|
+
'#8a9b0f', // olive
|
|
429
|
+
'#70ccc7', // sea foam
|
|
430
|
+
'#a37e58', // light brown
|
|
431
|
+
'#ec4913', // burnt orange
|
|
432
|
+
'#a27dbd', // soft royal purple
|
|
433
|
+
'#11644d', // forest
|
|
434
|
+
'#b3347c', // magenta
|
|
435
|
+
'#359668', // grass & sage
|
|
436
|
+
'#e87c76', // soft pink
|
|
437
|
+
'#80d152', // neon green
|
|
438
|
+
'#ecd078', // tangerine
|
|
439
|
+
'#6e90e6', // ligt purple blue
|
|
440
|
+
'#e08e79', // blush
|
|
441
|
+
'#c44d58', // rouge
|
|
442
|
+
'#7ff0ca', // light sea foam
|
|
443
|
+
'#a6b890', // olive sage
|
|
444
|
+
'#a5c2cc', // light blue grey
|
|
445
|
+
'#e8b28e', // peach
|
|
446
|
+
'#826942', // chocolate
|
|
447
|
+
'#d4ee5e', // lime
|
|
448
|
+
'#cad96a', // light yellow
|
|
449
|
+
'#e887aa', // newborn pink
|
|
450
|
+
'#6541a3', // royal purple
|
|
451
|
+
'#75616b', // dry wine
|
|
452
|
+
'#71cfde', // baby foam
|
|
453
|
+
'#7a7a7a', // light grey
|
|
454
|
+
]
|
|
455
|
+
|
|
456
|
+
// If color description listed in GEDCOM
|
|
457
|
+
const dscr = (p.tree.filter(hasTag('DSCR')) || [])[0]
|
|
458
|
+
|
|
459
|
+
const foundName = surnameList.find((sName) => sName.surname === p.surname)
|
|
460
|
+
|
|
461
|
+
// If surname already in list
|
|
462
|
+
if (foundName) {
|
|
463
|
+
foundName.count = foundName.count + 1
|
|
464
|
+
} else {
|
|
465
|
+
surnameList.push({
|
|
466
|
+
surname: p.surname,
|
|
467
|
+
count: 1,
|
|
468
|
+
color: colorList[surnameList.length % colorList.length],
|
|
469
|
+
})
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// surnameList.color = surnameList.length % colorList.length});
|
|
473
|
+
|
|
474
|
+
// If color listed assign that
|
|
475
|
+
if (dscr) {
|
|
476
|
+
return dscr.data
|
|
477
|
+
|
|
478
|
+
// else assign color from colorList
|
|
479
|
+
} else {
|
|
480
|
+
return surnameList.find((sName) => sName.surname === p.surname).color
|
|
481
|
+
}
|
|
487
482
|
}
|
|
488
483
|
|
|
489
484
|
// Get person notes
|
|
490
|
-
const getNotes = p => {
|
|
491
|
-
|
|
485
|
+
const getNotes = (p) => {
|
|
486
|
+
return p.tree.filter(hasTag('NOTE'))
|
|
492
487
|
}
|
|
493
488
|
|
|
494
489
|
// Get Bio
|
|
495
490
|
const getBio = (p, notes) => {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
}
|
|
491
|
+
if (p.notes.length != 0) {
|
|
492
|
+
let bio = ''
|
|
493
|
+
|
|
494
|
+
// Notes for person
|
|
495
|
+
p.notes.forEach((personNote) => {
|
|
496
|
+
// personNote.data points to NOTE object
|
|
497
|
+
if (notes.length > 0) {
|
|
498
|
+
notes.forEach((note) => {
|
|
499
|
+
if (personNote.data === note.pointer) {
|
|
500
|
+
bio += note.data
|
|
501
|
+
|
|
502
|
+
// Concat broken up note
|
|
503
|
+
if (note.tree.length > 0) {
|
|
504
|
+
note.tree.forEach((fragment) => (bio += fragment.data))
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
})
|
|
508
|
+
|
|
509
|
+
// personNote.data is actual note
|
|
510
|
+
} else {
|
|
511
|
+
bio += personNote.data
|
|
512
|
+
}
|
|
513
|
+
})
|
|
514
|
+
return bio
|
|
515
|
+
}
|
|
522
516
|
}
|
|
523
517
|
|
|
524
|
-
const getFy = p => {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
518
|
+
const getFy = (p) => {
|
|
519
|
+
if (p.yob === '?') {
|
|
520
|
+
return 0
|
|
521
|
+
} else {
|
|
522
|
+
return +(-p.yob * 3 + 6000)
|
|
523
|
+
}
|
|
530
524
|
}
|
|
531
525
|
|
|
532
526
|
const toNode = (p, notes, surnameList) => {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
527
|
+
p.id = p.pointer
|
|
528
|
+
p.title = getTitle(p)
|
|
529
|
+
p.name = getName(p)
|
|
530
|
+
p.firstName = getFirstName(p)
|
|
531
|
+
p.surname = getSurname(p)
|
|
532
|
+
p.gender = getGender(p)
|
|
533
|
+
p.dob = getDOB(p)
|
|
534
|
+
p.yob = getYOB(p)
|
|
535
|
+
p.pob = getPOB(p)
|
|
536
|
+
p.dod = getDOD(p)
|
|
537
|
+
p.yod = getYOD(p)
|
|
538
|
+
p.pod = getPOD(p)
|
|
539
|
+
p.families = getFamilies(p)
|
|
540
|
+
p.color = getColor(p, surnameList)
|
|
541
|
+
p.notes = getNotes(p)
|
|
542
|
+
p.bio = getBio(p, notes)
|
|
543
|
+
return p
|
|
550
544
|
}
|
|
551
545
|
|
|
552
546
|
const familyLinks = (family, peopleNodes) => {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
547
|
+
let memberLinks = []
|
|
548
|
+
let maritalStatus = null
|
|
549
|
+
let pedigree
|
|
550
|
+
|
|
551
|
+
// Filter only individual objects from family tree
|
|
552
|
+
let memberSet = family.tree.filter(function (member) {
|
|
553
|
+
return (
|
|
554
|
+
member.tag &&
|
|
555
|
+
(member.tag === 'HUSB' || member.tag === 'WIFE' || member.tag === 'CHIL')
|
|
556
|
+
)
|
|
557
|
+
})
|
|
558
|
+
|
|
559
|
+
// Filter marital status events
|
|
560
|
+
family.tree.filter((event) => {
|
|
561
|
+
if (event.tag === 'DIV' || event.tag === 'MARR') {
|
|
562
|
+
if (maritalStatus !== 'DIV') {
|
|
563
|
+
maritalStatus = event.tag
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
// Iterate over each member of set to connect with other members
|
|
569
|
+
while (memberSet.length > 1) {
|
|
570
|
+
for (let i = 1; i < memberSet.length; i++) {
|
|
571
|
+
// Exclude sibling relationships
|
|
572
|
+
if (memberSet[0].tag != 'CHIL') {
|
|
573
|
+
// If marital status listed
|
|
574
|
+
if (memberSet[0].tag == 'HUSB' && memberSet[i].tag == 'WIFE') {
|
|
575
|
+
memberLinks.push({
|
|
576
|
+
source: memberSet[0].data,
|
|
577
|
+
target: memberSet[i].data,
|
|
578
|
+
sourceType: memberSet[0].tag,
|
|
579
|
+
targetType: memberSet[i].tag,
|
|
580
|
+
type: maritalStatus,
|
|
581
|
+
})
|
|
582
|
+
} else {
|
|
583
|
+
// Filter pedigree info
|
|
584
|
+
function getPedigree(personID, parentType, relInfo) {
|
|
585
|
+
// GRAMPS
|
|
586
|
+
let person = peopleNodes.filter(hasID(personID))
|
|
587
|
+
let personFamily = person[0].families.filter(hasID(family.pointer))
|
|
588
|
+
if (parentType == 'HUSB') {
|
|
589
|
+
if (personFamily[0].pedi) {
|
|
590
|
+
return personFamily[0].pedi.frel
|
|
591
|
+
} else if (relInfo.some((parent) => parent.tag === '_FREL')) {
|
|
592
|
+
return relInfo.find((parent) => parent.tag === '_FREL').data
|
|
593
|
+
}
|
|
594
|
+
} else {
|
|
595
|
+
if (personFamily[0].pedi) {
|
|
596
|
+
return personFamily[0].pedi.mrel
|
|
597
|
+
} else if (relInfo.some((parent) => parent.tag === '_MREL')) {
|
|
598
|
+
return relInfo.find((parent) => parent.tag === '_MREL').data
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
memberLinks.push({
|
|
604
|
+
source: memberSet[0].data,
|
|
605
|
+
target: memberSet[i].data,
|
|
606
|
+
sourceType: memberSet[0].tag,
|
|
607
|
+
targetType: memberSet[i].tag,
|
|
608
|
+
type: getPedigree(
|
|
609
|
+
memberSet[i].data,
|
|
610
|
+
memberSet[0].tag,
|
|
611
|
+
memberSet[i].tree
|
|
612
|
+
),
|
|
613
|
+
})
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
memberSet.splice(0, 1)
|
|
618
|
+
}
|
|
619
|
+
return memberLinks
|
|
623
620
|
}
|
|
624
621
|
|
|
625
|
-
module.exports = d3ize
|
|
622
|
+
module.exports = d3ize
|