diff --git a/asset-manifest.json b/asset-manifest.json
index 62dc032..5f9afd8 100644
--- a/asset-manifest.json
+++ b/asset-manifest.json
@@ -1,15 +1,15 @@
{
"files": {
"main.css": "./static/css/main.a3606e6e.chunk.css",
- "main.js": "./static/js/main.2b6d3b8f.chunk.js",
- "main.js.map": "./static/js/main.2b6d3b8f.chunk.js.map",
+ "main.js": "./static/js/main.26dcf0f4.chunk.js",
+ "main.js.map": "./static/js/main.26dcf0f4.chunk.js.map",
"runtime~main.js": "./static/js/runtime~main.d653cc00.js",
"runtime~main.js.map": "./static/js/runtime~main.d653cc00.js.map",
"static/css/2.9c7c3dac.chunk.css": "./static/css/2.9c7c3dac.chunk.css",
"static/js/2.4e337743.chunk.js": "./static/js/2.4e337743.chunk.js",
"static/js/2.4e337743.chunk.js.map": "./static/js/2.4e337743.chunk.js.map",
"index.html": "./index.html",
- "precache-manifest.5cadd60b21a33322a878b90f5de39df2.js": "./precache-manifest.5cadd60b21a33322a878b90f5de39df2.js",
+ "precache-manifest.f604fdb4d9bb57475bc879bc75a1de06.js": "./precache-manifest.f604fdb4d9bb57475bc879bc75a1de06.js",
"service-worker.js": "./service-worker.js",
"static/css/2.9c7c3dac.chunk.css.map": "./static/css/2.9c7c3dac.chunk.css.map",
"static/css/main.a3606e6e.chunk.css.map": "./static/css/main.a3606e6e.chunk.css.map",
diff --git a/index.html b/index.html
index e388633..5707b7d 100644
--- a/index.html
+++ b/index.html
@@ -1 +1 @@
-
Topola Genealogy Viewer You need to enable JavaScript to run this app.
\ No newline at end of file
+Topola Genealogy Viewer You need to enable JavaScript to run this app.
\ No newline at end of file
diff --git a/precache-manifest.5cadd60b21a33322a878b90f5de39df2.js b/precache-manifest.f604fdb4d9bb57475bc879bc75a1de06.js
similarity index 92%
rename from precache-manifest.5cadd60b21a33322a878b90f5de39df2.js
rename to precache-manifest.f604fdb4d9bb57475bc879bc75a1de06.js
index 3eb86b2..aa740e9 100644
--- a/precache-manifest.5cadd60b21a33322a878b90f5de39df2.js
+++ b/precache-manifest.f604fdb4d9bb57475bc879bc75a1de06.js
@@ -1,6 +1,6 @@
self.__precacheManifest = (self.__precacheManifest || []).concat([
{
- "revision": "f8eb7f452d5933644b50e5500038ce1e",
+ "revision": "2cd97932ffff1d1f713e794e402edda7",
"url": "./index.html"
},
{
@@ -8,7 +8,7 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "./static/css/2.9c7c3dac.chunk.css"
},
{
- "revision": "39c4fd64c08dc4ec801f",
+ "revision": "3ac335c5a99d7438585f",
"url": "./static/css/main.a3606e6e.chunk.css"
},
{
@@ -16,8 +16,8 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "./static/js/2.4e337743.chunk.js"
},
{
- "revision": "39c4fd64c08dc4ec801f",
- "url": "./static/js/main.2b6d3b8f.chunk.js"
+ "revision": "3ac335c5a99d7438585f",
+ "url": "./static/js/main.26dcf0f4.chunk.js"
},
{
"revision": "8c97409f0ee389fe75da",
diff --git a/service-worker.js b/service-worker.js
index ca85869..6c39fb7 100644
--- a/service-worker.js
+++ b/service-worker.js
@@ -14,7 +14,7 @@
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
importScripts(
- "./precache-manifest.5cadd60b21a33322a878b90f5de39df2.js"
+ "./precache-manifest.f604fdb4d9bb57475bc879bc75a1de06.js"
);
self.addEventListener('message', (event) => {
diff --git a/static/js/main.26dcf0f4.chunk.js b/static/js/main.26dcf0f4.chunk.js
new file mode 100644
index 0000000..9690157
--- /dev/null
+++ b/static/js/main.26dcf0f4.chunk.js
@@ -0,0 +1,2 @@
+(window.webpackJsonp=window.webpackJsonp||[]).push([[0],{169:function(e,t,n){e.exports=n.p+"static/media/topola.060eef13.jpg"},253:function(e){e.exports={"menu.open":"Otw\xf3rz","menu.open_file":"Otw\xf3rz plik","menu.load_from_url":"Otw\xf3rz URL","menu.select_wikitree_id":"Wybierz WikiTree ID","menu.print":"Drukuj","menu.download":"Pobierz","menu.pdf_file":"Plik PDF","menu.png_file":"Plik PNG","menu.svg_file":"Plik SVG","menu.download_pdf":"Pobierz PDF","menu.download_png":"Pobierz PNG","menu.download_svg":"Pobierz SVG","menu.view":"Widok","menu.hourglass":"Wykres klepsydrowy","menu.relatives":"Wszyscy krewni","menu.fancy":"Ozdobne drzewo (eksperymentalne)","menu.wikitree_login":"Zaloguj do WikiTree","menu.wikitree_logged_in":"Zalogowano","menu.wikitree_popup_username":"Zalogowano do WikiTree jako {username}","menu.wikitree_popup":"Zalogowano do WikiTree","menu.github":"Projekt na stronie GitHub","menu.search.placeholder":"Szukaj osoby","menu.search.no_results":"Brak wynik\xf3w","intro.title":"Topola Genealogy","intro.description":"Topola Genealogy pozwala przegl\u0105da\u0107 drzewo genealogiczne w interaktywny spos\xf3b.","intro.instructions":"Kliknij OTW\xd3RZ PLIK lub OTW\xd3RZ URL, aby za\u0142adowa\u0107 plik GEDCOM. Wi\u0119kszo\u015b\u0107 program\xf3w genealogicznych posiada funkcj\u0119 eksportu do pliku GEDCOM.","intro.examples":"Poni\u017cej jest kilka przyk\u0142ad\xf3w znalezionych w Internecie:","intro.from":"\u017ar\xf3d\u0142o:","intro.privacy":"Prywatno\u015b\u0107","intro.privacy_note":'U\u017cywaj\u0105c funkcji "Otw\xf3rz plik", Twoje dane nie s\u0105 nigdzie wysy\u0142ane i pozostaj\u0105 na Twoim komputerze. U\u017cywaj\u0105c funkcji "Otw\xf3rz URL", dane z podanego adresu przesy\u0142ane s\u0105 przez us\u0142ug\u0119 {link} w celu umo\u017cliwienia za\u0142adowania danych z innej domeny (CORS).',"load_from_url.title":"Otw\xf3rz z adresu URL","load_from_url.comment":"Dane z podanego adresu URL zostan\u0105 za\u0142adowane poprzez us\u0142ug\u0119 {link} w celu unikni\u0119cia problem\xf3w z CORS.","load_from_url.cancel":"Anuluj","load_from_url.load":"Otw\xf3rz","select_wikitree_id.title":"Podaj WikiTree ID","select_wikitree_id.comment":"Wpisz identyfikator profilu {wikiTreeLink}. Przyk\u0142ady: {example1}, {example2}","select_wikitree_id.cancel":"Anuluj","select_wikitree_id.load":"Otw\xf3rz","gedcom.BAPM":"Chrzest","gedcom.BIRT":"Narodziny","gedcom.BURI":"Pogrzeb","gedcom.CENS":"Spis ludno\u015bci","gedcom.CHR":"Chrzest","gedcom.DEAT":"\u015amier\u0107","gedcom.DSCR":"Opis","gedcom.EMAIL":"E-mail","gedcom.EVEN":"Wydarzenie","gedcom.OCCU":"Zaw\xf3d","gedcom.RIN":"ID","gedcom.TITL":"Tytu\u0142","gedcom.WWW":"Strona WWW","gedcom._UPD":"Ostatnia aktualizacja","date.abt":"oko\u0142o","date.cal":"wyliczone","date.est":"oszacowane","date.between":"mi\u0119dzy {from} a {to}","date.after":"po {from}","date.before":"przed {to}","error.error":"B\u0142\u0105d","error.failed_pdf":"Nie uda\u0142o si\u0119 utworzy\u0107 pliku PDF. Spr\xf3buj jeszcze raz z mniejszym diagramem lub pobierz plik SVG.","error.failed_png":"Nie uda\u0142o si\u0119 utworzy\u0107 pliku PNG. Spr\xf3buj jeszcze raz z mniejszym diagramem lub pobierz plik SVG.","error.failed_to_load_file":"B\u0142\u0105d wczytywania pliku"}},254:function(e,t){},297:function(e,t,n){e.exports=n(528)},309:function(e,t){},315:function(e,t){},525:function(e,t,n){},528:function(e,t,n){"use strict";n.r(t);var a=n(44),r=n(251),i=n(252),o=n(0),s=n(64),l=n(253),c=n(9),u=n(39),d=n(37),m=n(40),f=n(65),h=n(15),p=n.n(h),g=n(18),v=n(27),w=n(28),E=n(31);function k(e,t){window.gtag("event",e,t)}var y,b=n(17),O=n(256),D=n.n(O),T=n(168),M=n(33),S=n(553);function I(){var e=b.select("#svgContainer").node(),t=e.scrollLeft+e.clientWidth/2,n=e.scrollTop+e.clientHeight/2,a=b.zoomTransform(e).k;b.select(e).call(b.zoom().translateTo,t/a,n/a)}function x(e){var t=new FileReader;return t.readAsDataURL(e),new Promise(function(e,n){t.onload=function(t){return e(t.target.result)}})}function _(e){return j.apply(this,arguments)}function j(){return(j=Object(g.a)(p.a.mark(function e(t){var n,a,r,i;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(n=t.href.baseVal){e.next=3;break}return e.abrupt("return");case 3:return e.prev=3,e.next=6,fetch(n);case 6:return a=e.sent,e.next=9,a.blob();case 9:return r=e.sent,e.next=12,x(r);case 12:i=e.sent,t.href.baseVal=i,e.next=19;break;case 16:e.prev=16,e.t0=e.catch(3),console.warn("Failed to load image:",e.t0);case 19:case"end":return e.stop()}},e,null,[[3,16]])}))).apply(this,arguments)}function R(e){return L.apply(this,arguments)}function L(){return(L=Object(g.a)(p.a.mark(function e(t){var n;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return n=Array.from(t.getElementsByTagName("image")),e.next=3,Promise.all(n.map(_));case 3:case"end":return e.stop()}},e)}))).apply(this,arguments)}function C(e){var t=new Image;return t.src=URL.createObjectURL(e),new Promise(function(e,n){t.addEventListener("load",function(){return e(t)})})}function N(e){var t=document.createElement("canvas");t.width=2*e.width,t.height=2*e.height;var n=t.getContext("2d"),a=n.fillStyle;return n.fillStyle="white",n.fillRect(0,0,t.width,t.height),n.fillStyle=a,n.drawImage(e,0,0,t.width,t.height),t}function z(e,t){return new Promise(function(n,a){e.toBlob(function(e){e?n(e):a()},t)})}!function(e){e[e.Hourglass=0]="Hourglass",e[e.Relatives=1]="Relatives",e[e.Fancy=2]="Fancy"}(y||(y={}));var W=function(e){function t(){var e,n;Object(v.a)(this,t);for(var a=arguments.length,r=new Array(a),i=0;i0&&void 0!==arguments[0]?arguments[0]:{initialRender:!1};if(t.initialRender||!this.animating){t.initialRender?(b.select("#chart").node().innerHTML="",this.chart=Object(M.createChart)({json:this.props.data,chartType:this.getChartType(),renderer:this.getRendererType(),svgSelector:"#chart",indiCallback:function(t){return e.props.onSelection(t)},animate:!0,updateSvgSize:!1,locale:this.context.intl.locale})):this.chart.setData(this.props.data);var n=this.chart.render({startIndi:this.props.selection.id,baseGeneration:this.props.selection.generation}),a=b.select("#chartSvg"),r=b.select("#svgContainer").node(),i=b.zoomTransform(r).k,o=b.min([1,i,r.clientWidth/n.size[0],r.clientHeight/n.size[1]]),s=[b.max([.1,o]),2];this.zoomBehavior=b.zoom().scaleExtent(s).translateExtent([[0,0],n.size]).on("zoom",function(){return function(e){var t=b.select("#svgContainer").node(),n=b.event.transform.k,a=b.max([0,(t.clientWidth-e[0]*n)/2]),r=b.max([0,(t.clientHeight-e[1]*n)/2]);b.select("#chartSvg").attr("width",e[0]*n).attr("height",e[1]*n).attr("transform","translate(".concat(a,", ").concat(r,")")),b.select("#chart").attr("transform","scale(".concat(n,")")),t.scrollLeft=-b.event.transform.x,t.scrollTop=-b.event.transform.y}(n.size)}),b.select(r).on("scroll",I).call(this.zoomBehavior);var l,c,u=r.clientWidth/2-n.origin[0]*i,d=r.clientHeight/2-n.origin[1]*i,m=b.max([0,(r.clientWidth-n.size[0]*i)/2]),f=b.max([0,(r.clientHeight-n.size[1]*i)/2]),h=a.transition().delay(200).duration(500);(t.initialRender?a:h).attr("transform","translate(".concat(m,", ").concat(f,")")).attr("width",n.size[0]*i).attr("height",n.size[1]*i),t.initialRender?(r.scrollLeft=-u,r.scrollTop=-d):h.tween("scrollLeft",(c=-u,function(){var e=b.interpolateNumber(r.scrollLeft,c);return function(t){r.scrollLeft=e(t)}})).tween("scrollTop",(l=-d,function(){var e=b.interpolateNumber(r.scrollTop,l);return function(t){r.scrollTop=e(t)}})),this.animating=!0,n.animationPromise.then(function(){e.animating=!1,e.rerenderRequired&&(e.rerenderRequired=!1,e.renderChart({initialRender:!1}))})}else this.rerenderRequired=!0}},{key:"componentDidMount",value:function(){this.renderChart({initialRender:!0})}},{key:"componentDidUpdate",value:function(e){var t=this.props.chartType!==e.chartType;this.renderChart({initialRender:t})}},{key:"render",value:function(){var e=this;return o.createElement("div",{id:"svgContainer"},o.createElement(S.a,{minWidth:768,className:"zoom"},o.createElement("button",{className:"zoom-in",onClick:function(){return e.zoom(1.3)}},"+"),o.createElement("button",{className:"zoom-out",onClick:function(){return e.zoom(1/1.3)}},"\u2212")),o.createElement("svg",{id:"chartSvg"},o.createElement("g",{id:"chart"})))}},{key:"getStrippedSvg",value:function(){var e=document.getElementById("chartSvg").cloneNode(!0);e.removeAttribute("transform");var t=b.select("#svgContainer").node(),n=b.zoomTransform(t).k;return e.setAttribute("width",String(Number(e.getAttribute("width"))/n)),e.setAttribute("height",String(Number(e.getAttribute("height"))/n)),e.querySelector("#chart").removeAttribute("transform"),e}},{key:"getSvgContents",value:function(){return(new XMLSerializer).serializeToString(this.getStrippedSvg())}},{key:"getSvgContentsWithInlinedImages",value:function(){var e=Object(g.a)(p.a.mark(function e(){var t;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.getStrippedSvg(),e.next=3,R(t);case 3:return e.abrupt("return",(new XMLSerializer).serializeToString(t));case 4:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"print",value:function(){var e=this,t=document.createElement("iframe");t.style.position="absolute",t.style.top="-1000px",t.style.left="-1000px",t.onload=function(){t.contentDocument.open(),t.contentDocument.write(e.getSvgContents()),t.contentDocument.close(),setTimeout(function(){t.contentWindow.focus(),t.contentWindow.print(),t.parentNode.removeChild(t)},500)},document.body.appendChild(t)}},{key:"downloadSvg",value:function(){var e=Object(g.a)(p.a.mark(function e(){var t,n;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.getSvgContentsWithInlinedImages();case 2:t=e.sent,n=new Blob([t],{type:"image/svg+xml"}),Object(T.saveAs)(n,"topola.svg");case 5:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"drawOnCanvas",value:function(){var e=Object(g.a)(p.a.mark(function e(){var t,n;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.getSvgContentsWithInlinedImages();case 2:return t=e.sent,n=new Blob([t],{type:"image/svg+xml"}),e.t0=N,e.next=7,C(n);case 7:return e.t1=e.sent,e.next=10,(0,e.t0)(e.t1);case 10:return e.abrupt("return",e.sent);case 11:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"downloadPng",value:function(){var e=Object(g.a)(p.a.mark(function e(){var t,n;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.drawOnCanvas();case 2:return t=e.sent,e.next=5,z(t,"image/png");case 5:n=e.sent,Object(T.saveAs)(n,"topola.png");case 7:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"downloadPdf",value:function(){var e=Object(g.a)(p.a.mark(function e(){var t,n;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.drawOnCanvas();case 2:t=e.sent,(n=new D.a({orientation:t.width>t.height?"l":"p",unit:"pt",format:[t.width,t.height]})).addImage(t,"PNG",0,0,t.width,t.height,"NONE"),n.save("topola.pdf");case 6:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()}]),t}(o.PureComponent);W.contextTypes={intl:c.d};var P=n(259),A=n.n(P),F=n(260),U=n.n(F),G=n(164);function H(e){return e.substring(1,e.length-1)}function B(e){var t=new Map;return e.indis.forEach(function(e){t.set(e.id,e)}),t}function V(e){var t=new Map;return e.fams.forEach(function(e){t.set(e.id,e)}),t}function K(e){var t=e.find(function(e){return"HEAD"===e.tag}),n={},a={},r={};return e.forEach(function(e){"INDI"===e.tag?n[H(e.pointer)]=e:"FAM"===e.tag?a[H(e.pointer)]=e:e.pointer&&(r[H(e.pointer)]=e)}),{head:t,indis:n,fams:a,other:r}}function J(e,t){return et?1:0}function q(e,t){var n=e&&(e.date||e.dateRange&&e.dateRange.from),a=t&&(t.date||t.dateRange&&t.dateRange.from);return n&&n.year&&a&&a.year?n.year!==a.year?n.year-a.year:n.month&&a.month?n.month!==a.month?n.month-a.month:n.day&&a.day&&n.day!==a.day?n.month-a.month:0:0:0}function Z(e){var t=function(e){var t=B(e);return function(e,n){var a=t.get(e),r=t.get(n);return q(a&&a.birth,r&&r.birth)||J(e,n)}}(e),n=e.fams.map(function(e){return function(e,t){if(!e.children)return e;var n=e.children.sort(t);return Object.assign({},e,{children:n})}(e,t)});return Object.assign({},e,{fams:n})}function Y(e){var t=function(e){var t=V(e);return function(e,n){var a=t.get(e),r=t.get(n);return q(a&&a.marriage,r&&r.marriage)||J(e,n)}}(e),n=e.indis.map(function(e){return function(e,t){if(!e.fams)return e;var n=e.fams.sort(t);return Object.assign({},e,{fams:n})}(e,t)});return Object.assign({},e,{indis:n})}function X(e){return Y(Z(e))}var $=[".jpg",".png",".gif"];function Q(e,t){if(!e.images||0===e.images.length)return e;var n=[];return e.images.forEach(function(e){var a=e.url.match(/[^/\\]*$/)[0];t.has(a)?n.push({url:t.get(a),title:e.title}):e.url.startsWith("http")&&function(e){var t=e.toLowerCase();return $.some(function(e){return t.endsWith(e)})}(e.url)&&n.push(e)}),Object.assign({},e,{images:n})}function ee(e,t){var n=e.indis.map(function(e){return Q(e,t)});return Object.assign({},e,{indis:n})}function te(e){var t=e&&e.tree&&e.tree.find(function(e){return"SOUR"===e.tag}),n=t&&t.tree&&t.tree.find(function(e){return"NAME"===e.tag});return n&&n.data||null}var ne=new Map([["abt","about"],["cal","calculated"],["est","estimated"]]);function ae(e,t){var n=void 0!==e.day,a=void 0!==e.month,r=void 0!==e.year;if(!n&&!a&&!r)return e.text||"";var i=new Date(r?e.year:0,a?e.month-1:0,n?e.day:1),o=e.qualifier&&e.qualifier.toLowerCase(),s={day:n?"numeric":void 0,month:a?"long":void 0,year:r?"numeric":void 0};return[o&&t.formatMessage({id:"date.".concat(o),defaultMessage:ne.get(o)||o}),new Intl.DateTimeFormat(t.locale,s).format(i)].join(" ")}function re(e,t){return e?e.date?ae(e.date,t):e.dateRange?function(e,t){var n=e.from,a=e.to,r=n&&ae(n,t),i=a&&ae(a,t);return r&&i?t.formatMessage({id:"date.between",defaultMessage:"between {from} and {to}"},{from:r,to:i}):r?t.formatMessage({id:"date.after",defaultMessage:"after {from}"},{from:r}):i?t.formatMessage({id:"date.before",defaultMessage:"before {to}"},{to:i}):""}(e.dateRange,t):"":""}var ie=["BIRT","BAPM","CHR","DEAT","BURI","EVEN","CENS"],oe=["NAME","SEX","FAMC","FAMS","NOTE","SOUR"],se=new Map([["BAPM","Baptism"],["BIRT","Birth"],["BURI","Burial"],["CENS","Census"],["CHR","Christening"],["DEAT","Death"],["EMAIL","E-mail"],["EVEN","Event"],["OCCU","Occupation"],["TITL","Title"],["WWW","WWW"]]);function le(e){return o.createElement(c.a,{id:"gedcom.".concat(e),defaultMessage:se.get(e)||e})}function ce(e){return o.createElement(o.Fragment,null,e.map(function(e,t){return o.createElement("div",{key:t},o.createElement(U.a,{properties:{target:"_blank"}},e),o.createElement("br",null))}))}function ue(e){var t=[e.data];return e.tree.forEach(function(e){if("CONC"===e.tag&&e.data){var n=t.length-1;t[n]+=e.data}else"CONT"===e.tag&&e.data&&t.push(e.data)}),t}function de(e,t){var n=[];e.data&&e.data.length>1&&n.push(o.createElement("i",null,e.data));var r=e.tree.find(function(e){return"DATE"===e.tag});r&&r.data&&n.push(function(e,t){return re(Object(M.getDate)(e),t)}(r.data,t));var i=e.tree.find(function(e){return"PLAC"===e.tag});return i&&i.data&&n.push.apply(n,Object(a.a)(ue(i))),e.tree.filter(function(e){return"NOTE"===e.tag}).forEach(function(e){return ue(e).forEach(function(e){return n.push(o.createElement("i",null,e))})}),n.length?o.createElement(o.Fragment,null,o.createElement("div",{className:"ui sub header"},le(e.tag)),o.createElement("span",null,ce(n))):null}function me(e){return ce(ue(e).map(function(e,t){return o.createElement("i",{key:t},e)}))}function fe(e){return o.createElement("h2",{className:"ui header"},e.data.split("/").filter(function(e){return!!e}).map(function(e,t){return o.createElement("div",{key:t},e,o.createElement("br",null))}))}function he(e,t,n){return A()(t,function(t){return e.filter(function(e){return e.tag===t}).map(function(e){return n(e)})}).filter(function(e){return null!==e}).map(function(e,t){return o.createElement("div",{className:"ui segment",key:t},e)})}function pe(e){return e.tree.length>0||e.data&&!e.data.startsWith("@")}function ge(e){return e.filter(function(e){return!oe.includes(e.tag)&&!ie.includes(e.tag)}).filter(pe).map(function(e){return function(e){var t=[];return e.data&&t.push.apply(t,Object(a.a)(ue(e))),e.tree.filter(function(e){return"NOTE"===e.tag}).forEach(function(e){return ue(e).forEach(function(e){return t.push(o.createElement("i",null,e))})}),t.length?o.createElement(o.Fragment,null,o.createElement("div",{className:"ui sub header"},le(e.tag)),o.createElement("span",null,ce(t))):null}(e)}).filter(function(e){return null!==e}).map(function(e,t){return o.createElement("div",{className:"ui segment",key:t},e)})}var ve=function(e){function t(){return Object(v.a)(this,t),Object(u.a)(this,Object(d.a)(t).apply(this,arguments))}return Object(m.a)(t,e),Object(w.a)(t,[{key:"render",value:function(){var e=this,t=this.props.gedcom.indis[this.props.indi].tree,n=t.map(function(t){return function(e,t){if(e.data){var n=t.other[H(e.data)];if(n)return n}return e}(t,e.props.gedcom)}).filter(pe);return o.createElement("div",{className:"ui segments",id:"details"},he(t,["NAME"],fe),he(t,ie,function(t){return de(t,e.context.intl)}),ge(n),he(n,["NOTE"],me))}}]),t}(o.Component);function we(e,t,n){return{id:t&&e.indis.some(function(e){return e.id===t})?t:e.indis[0].id,generation:n||0}}function Ee(e,t,n){var a=function(e,t){var n=Object(G.parse)(e),a=Object(M.gedcomEntriesToJson)(n);if(!a||!a.indis||!a.indis.length||!a.fams||!a.fams.length)throw new Error("Failed to read GEDCOM file");return{chartData:ee(X(a),t),gedcom:K(n)}}(e,n||new Map),r=JSON.stringify(a);try{sessionStorage.setItem(t,r)}catch(i){console.warn("Failed to store data in session storage: "+i)}return a}function ke(e,t){return ye.apply(this,arguments)}function ye(){return(ye=Object(g.a)(p.a.mark(function e(t,n){var a,r,i,o;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(e.prev=0,!(a=sessionStorage.getItem(t))){e.next=4;break}return e.abrupt("return",JSON.parse(a));case 4:e.next=9;break;case 6:e.prev=6,e.t0=e.catch(0),console.warn("Failed to load data from session storage: "+e.t0);case 9:return r=n?"https://cors-anywhere.herokuapp.com/"+t:t,e.next=12,window.fetch(r);case 12:if(200===(i=e.sent).status){e.next=15;break}throw new Error(i.statusText);case 15:return e.next=17,i.text();case 17:return o=e.sent,e.abrupt("return",Ee(o,t));case 19:case"end":return e.stop()}},e,null,[[0,6]])}))).apply(this,arguments)}function be(e,t,n){return Oe.apply(this,arguments)}function Oe(){return(Oe=Object(g.a)(p.a.mark(function e(t,n,a){var r;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(e.prev=0,!(r=sessionStorage.getItem(t))){e.next=4;break}return e.abrupt("return",JSON.parse(r));case 4:e.next=9;break;case 6:e.prev=6,e.t0=e.catch(0),console.warn("Failed to load data from session storage: "+e.t0);case 9:if(n){e.next=11;break}throw new Error("Error loading data. Please upload your file again.");case 11:return e.abrupt("return",Ee(n,t,a));case 12:case"end":return e.stop()}},e,null,[[0,6]])}))).apply(this,arguments)}ve.contextTypes={intl:c.d};var De=n(169),Te=n.n(De),Me=n(543),Se=n(549),Ie=n(286),xe=n(536);function _e(e){return o.createElement(xe.a,{to:{pathname:"/view",search:E.stringify({url:e.url})}},e.text)}function je(){var e=o.createElement(o.Fragment,null,o.createElement("p",null,o.createElement(c.a,{id:"intro.description",defaultMessage:"Topola Genealogy is a genealogy tree viewer that lets you browse the structure of the family."})),o.createElement("p",null,o.createElement(c.a,{id:"intro.instructions",defaultMessage:"Use the OPEN FILE or LOAD FROM URL buttons above to load a GEDCOM file. You can export a GEDCOM file from most of the existing genealogy programs and web sites."})),o.createElement("p",null,o.createElement(c.a,{id:"intro.examples",defaultMessage:"Here are some examples from the web that you can view:"})),o.createElement("ul",null,o.createElement("li",null,o.createElement(_e,{url:"http://genpol.com/module-Downloads-prep_hand_out-lid-32.html",text:"Karol Wojty\u0142a"})," ","(",o.createElement(c.a,{id:"intro.from",defaultMessage:"from"})," ",o.createElement("a",{href:"http://genpol.com/module-Downloads-display-lid-32.html"},"GENPOL"),")"),o.createElement("li",null,o.createElement(_e,{url:"https://webtreeprint.com/tp_downloader.php?path=famous_gedcoms/shakespeare.ged",text:"Shakespeare"})," ","(",o.createElement(c.a,{id:"intro.from",defaultMessage:"from"})," ",o.createElement("a",{href:"https://webtreeprint.com/tp_famous_gedcoms.php"},"webtreeprint.com"),")"),o.createElement("li",null,o.createElement(_e,{url:"http://genealogyoflife.com/tng/gedcom/HarryPotter.ged",text:"Harry Potter"})," ","(",o.createElement(c.a,{id:"intro.from",defaultMessage:"from"})," ",o.createElement("a",{href:"http://famousfamilytrees.blogspot.com/"},"Famous Family Trees"),")")),o.createElement("p",null,o.createElement("b",null,o.createElement(c.a,{id:"intro.privacy",defaultMessage:"Privacy"})),": ",o.createElement(c.a,{id:"intro.privacy_note",defaultMessage:'When using the "load from file" option, this site does not send your data anywhere and files loaded from disk do not leave your computer. When using "load from URL", data is passed through the {link} service to deal with an issue with cross-site file loading in the browser (CORS).',values:{link:o.createElement("a",{href:"https://cors-anywhere.herokuapp.com/"},"cors-anywhere")}})),o.createElement("p",{className:"ui right aligned version"},"version: ","2020-04-11 15:02:22 +0200".slice(0,16)," (",o.createElement("a",{href:"https://github.com/PeWu/topola-viewer/commit/".concat("fa7b0a5")},"fa7b0a5"),")"));return o.createElement("div",{id:"content"},o.createElement("div",{className:"backgroundImage"}),o.createElement(Me.a,{className:"intro"},o.createElement(S.a,{as:Me.a.Content,minWidth:768},o.createElement(Me.a.Header,null,o.createElement(c.a,{id:"intro.title",defaultMessage:"Topola Genealogy Viewer"}))),o.createElement(Me.a.Content,null,o.createElement(S.a,{as:Se.a,minWidth:768},o.createElement(Se.a.Row,null,o.createElement(Se.a.Column,{width:5},o.createElement(Ie.a,{src:Te.a,alt:"Topola logo"})),o.createElement(Se.a.Column,{width:11},e))),o.createElement(S.a,{maxWidth:767},o.createElement(Ie.a,{src:Te.a,alt:"Topola logo",centered:!0,size:"tiny",className:"blockImage"}),e))))}var Re=n(544),Le=n(554),Ce=n(539),Ne=n(87),ze=n.n(Ne);function We(e){try{return sessionStorage.getItem(e)}catch(t){console.warn("Failed to load data from session storage: "+t)}return null}function Pe(e,t){try{sessionStorage.setItem(e,t)}catch(n){console.warn("Failed to store data in session storage: "+n)}}function Ae(e,t){return Fe.apply(this,arguments)}function Fe(){return(Fe=Object(g.a)(p.a.mark(function e(t,n){var a,r,i,o,s;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:for(r in(a=new FormData).append("format","json"),t)a.append(r,t[r]);return i=n?"https://cors-anywhere.herokuapp.com/https://api.wikitree.com/api.php":"https://api.wikitree.com/api.php",e.next=6,window.fetch(i,{method:"POST",body:a});case 6:return o=e.sent,e.next=9,o.text();case 9:return s=e.sent,e.abrupt("return",JSON.parse(s));case 11:case"end":return e.stop()}},e)}))).apply(this,arguments)}function Ue(e,t){return Ge.apply(this,arguments)}function Ge(){return(Ge=Object(g.a)(p.a.mark(function e(t,n){var a,r,i,o;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(a="wikitree:ancestors:".concat(t),!(r=We(a))){e.next=4;break}return e.abrupt("return",JSON.parse(r));case 4:return e.next=6,Ae({action:"getAncestors",key:t,fields:"*"},n);case 6:return i=e.sent,o=i[0].ancestors,Pe(a,JSON.stringify(o)),e.abrupt("return",o);case 10:case"end":return e.stop()}},e)}))).apply(this,arguments)}function He(e,t){return Be.apply(this,arguments)}function Be(){return(Be=Object(g.a)(p.a.mark(function e(t,n){var a,r,i,o;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(a=[],r=[],t.forEach(function(e){var t=We("wikitree:relatives:".concat(e));t?a.push(JSON.parse(t)):r.push(e)}),0!==r.length){e.next=5;break}return e.abrupt("return",a);case 5:return e.next=7,Ae({action:"getRelatives",keys:r.join(","),getChildren:!0,getSpouses:!0},n);case 7:if(null!==(i=e.sent)[0].items){e.next=10;break}throw new Error("WikiTree profile ".concat(r[0]," not found."));case 10:return(o=i[0].items.map(function(e){return e.person})).forEach(function(e){Pe("wikitree:relatives:".concat(e.Name),JSON.stringify(e))}),e.abrupt("return",a.concat(o));case 13:case"end":return e.stop()}},e)}))).apply(this,arguments)}function Ve(e){return Ke.apply(this,arguments)}function Ke(){return(Ke=Object(g.a)(p.a.mark(function e(t){var n;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,Ae({action:"clientLogin",authcode:t},!1);case 2:return n=e.sent,e.abrupt("return",n.clientLogin);case 4:case"end":return e.stop()}},e)}))).apply(this,arguments)}function Je(e,t){return qe.apply(this,arguments)}function qe(){return(qe=Object(g.a)(p.a.mark(function e(t,n){var r,i,o,s,l,c,u,d,m,h,g,v,w,E,k,y,b,O,D,T,M;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if((r="apps.wikitree.com"!==window.location.hostname)||ze.a.get("wikidb_wtb_UserID")||!n){e.next=6;break}return e.next=4,Ve(n);case 4:"Success"===e.sent.result&&sessionStorage.clear();case 6:return i=[],e.next=9,He([t],r);case 9:if((o=e.sent)[0].Name){e.next=12;break}throw new Error("WikiTree profile ".concat(t," is not accessible. Try logging in."));case 12:return s=Object.values(o[0].Spouses).map(function(e){return e.Name}),e.next=15,Promise.all([t].concat(s).map(function(e){return Ue(e,r)}));case 15:return l=e.sent,c=l.flat().map(function(e){return e.Name}).filter(function(e){return!!e}),e.next=19,He(c,r);case 19:u=e.sent,i.push.apply(i,Object(a.a)(u)),d=5,m=[t],h=0;case 24:if(!(m.length>0&&h<=d)){e.next=35;break}return e.next=27,He(m,r);case 27:g=e.sent,i.push.apply(i,Object(a.a)(g)),v=g.flatMap(function(e){return Object.values(e.Spouses)}),i.push.apply(i,Object(a.a)(v)),m=g.flatMap(function(e){return Object.values(e.Children).map(function(e){return e.Name})}),h++,e.next=24;break;case 35:return w=new Map,E=new Map,k=new Map,y=new Map,i.forEach(function(e){if(y.set(e.Id,e.Name),e.Mother||e.Father){var t=Ze(e.Mother,e.Father);Qe(w,e.Mother).add(t),Qe(w,e.Father).add(t),Qe(E,t).add(e.Id),k.set(t,{wife:e.Mother||void 0,husband:e.Father||void 0})}}),b=[],O=new Set,i.forEach(function(e){if(!O.has(e.Id)){O.add(e.Id);var t=Ye(e);e.Spouses&&Object.values(e.Spouses).forEach(function(t){var n=Ze(e.Id,t.Id);Qe(w,e.Id).add(n),Qe(w,t.Id).add(n);var a="Male"===e.Gender?{wife:t.Id,husband:e.Id,spouse:t}:{wife:e.Id,husband:t.Id,spouse:t};k.set(n,a)}),t.fams=Array.from(Qe(w,e.Id)),b.push(t)}}),D=Array.from(k.entries()).map(function(e){var t=Object(f.a)(e,2),n=t[0],a=t[1],r={id:n},i=a.wife&&y.get(a.wife);i&&(r.wife=i);var o=a.husband&&y.get(a.husband);if(o&&(r.husb=o),r.children=Array.from(Qe(E,n)).map(function(e){return y.get(e)}),a.spouse&&(a.spouse.marriage_date&&"0000-00-00"!==a.spouse.marriage_date||a.spouse.marriage_location)){var s=Xe(a.spouse.marriage_date);r.marriage=Object.assign({},s,{place:a.spouse.marriage_location})}return r}),T=X({indis:b,fams:D}),M=$e(b),e.abrupt("return",{chartData:T,gedcom:M});case 47:case"end":return e.stop()}},e)}))).apply(this,arguments)}function Ze(e,t){return t>e?"".concat(e,"_").concat(t):"".concat(t,"_").concat(e)}function Ye(e){var t={id:e.Name};if("Unknown"!==e.FirstName&&(t.firstName=e.FirstName),"Unknown"!==e.LastNameAtBirth&&(t.lastName=e.LastNameAtBirth),(e.Mother||e.Father)&&(t.famc=Ze(e.Mother,e.Father)),"Male"===e.Gender?t.sex="M":"Female"===e.Gender&&(t.sex="F"),e.BirthDate&&"0000-00-00"!==e.BirthDate||e.BirthLocation){var n=Xe(e.BirthDate,e.DataStatus&&e.DataStatus.BirthDate);t.birth=Object.assign({},n,{place:e.BirthLocation})}if(e.DeathDate&&"0000-00-00"!==e.DeathDate||e.DeathLocation){var a=Xe(e.DeathDate,e.DataStatus&&e.DataStatus.DeathDate);t.death=Object.assign({},a,{place:e.DeathLocation})}return e.PhotoData&&(t.images=[{url:"https://www.wikitree.com".concat(e.PhotoData.path)}]),t}function Xe(e,t){if(e){var n=e.match(/(\d\d\d\d)-(\d\d)-(\d\d)/);if(!n)return{date:{text:e}};var a={};return"0000"!==n[1]&&(a.year=~~n[1]),"00"!==n[2]&&(a.month=~~n[2]),"00"!==n[3]&&(a.day=~~n[3]),"after"===t?{dateRange:{from:a}}:"before"===t?{dateRange:{to:a}}:("guess"===t&&(a.qualifier="abt"),{date:a})}}function $e(e){var t={};return e.forEach(function(e){var n=e.id.replace(/ /g,"_");t[e.id]={level:0,pointer:"@".concat(e.id,"@"),tag:"INDI",data:"",tree:[{level:1,pointer:"",tag:"NAME",data:"".concat(e.firstName||""," /").concat(e.lastName||"","/"),tree:[]},{level:1,pointer:"",tag:"WWW",data:"https://www.wikitree.com/wiki/".concat(n),tree:[]}]}}),{head:{level:0,pointer:"",tag:"HEAD",data:"",tree:[]},indis:t,fams:{},other:{}}}function Qe(e,t){var n=e.get(t);if(n)return n;var a=new Set;return e.set(t,a),a}var et=n(551),tt=n(552),nt=n(547),at=n(264),rt=n.n(at),it=n(170),ot=n.n(it),st=n(265),lt=n.n(st),ct=n(266),ut=n.n(ct);function dt(e){return e.toLocaleLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/\u0142/g,"l")}function mt(e,t){return e.score!==t.score?t.score-e.score:lt()(e.ref,t.ref)}var ft=function(){function e(t){Object(v.a)(this,e),this.index=void 0,this.indiMap=void 0,this.famMap=void 0,this.indiMap=B(t),this.famMap=V(t)}return Object(w.a)(e,[{key:"initialize",value:function(){var e=this;this.index=ut()(function(){var t=this;this.ref("id"),this.field("id"),this.field("name",{boost:10}),this.field("normalizedName",{boost:8}),this.field("spouseLastName",{boost:2}),this.field("normalizedSpouseLastName",{boost:2}),e.indiMap.forEach(function(n){var a=[n.firstName,n.lastName].join(" "),r=function(e,t,n){return(e.fams||[]).map(function(e){return n.get(e)}).map(function(e){return e&&e.husb}).map(function(e){return e&&t.get(e)}).map(function(e){return e&&e.lastName}).join(" ")}(n,e.indiMap,e.famMap);t.add({id:n.id,name:a,normalizedName:dt(a),spouseLastName:r,normalizedSpouseLastName:dt(r)})})})}},{key:"search",value:function(e){var t=this,n=e.split(" ").filter(function(e){return!!e}).map(function(e){return"+".concat(e,"*")}).join(" ");return this.index.search(n).sort(mt).slice(0,8).map(function(e){return{id:e.ref,indi:t.indiMap.get(e.ref)}})}}]),e}();function ht(e){var t=[e.indi.firstName,e.indi.lastName].join(" ").trim();return e.id.length>8?t:o.createElement(o.Fragment,null,t," ",o.createElement("i",null,"(",e.id,")"))}function pt(e,t){var n=re(e.birth,t),a=re(e.death,t);return a?"".concat(n," \u2013 ").concat(a):n}var gt,vt,wt=n(541),Et=n(550),kt=n(66),yt=n(540),bt=n(538),Ot=n(530),Dt=n(545),Tt=n(542),Mt=n(546),St="https://www.wikitree.com/photo.php/a/a5/WikiTree_Images.png";function It(e){return new Promise(function(t,n){var a=new FileReader;a.onload=function(e){t(e.target.result)},a.readAsText(e)})}function xt(e){var t=e.toLowerCase();return t.endsWith(".jpg")||t.endsWith(".png")}!function(e){e[e.UNKNOWN=0]="UNKNOWN",e[e.NOT_LOGGED_IN=1]="NOT_LOGGED_IN",e[e.LOGGED_IN=2]="LOGGED_IN"}(gt||(gt={})),function(e){e[e.LARGE=0]="LARGE",e[e.SMALL=1]="SMALL"}(vt||(vt={}));var _t,jt=function(e){function t(){var e,n;Object(v.a)(this,t);for(var a=arguments.length,r=new Array(a),i=0;i {\n const reader = new FileReader();\n reader.readAsDataURL(blob);\n return new Promise((resolve, reject) => {\n reader.onload = (e) => resolve((e.target as FileReader).result as string);\n });\n}\n\nasync function inlineImage(image: SVGImageElement) {\n const href = image.href.baseVal;\n if (!href) {\n return;\n }\n try {\n const response = await fetch(href);\n const blob = await response.blob();\n const dataUrl = await loadAsDataUrl(blob);\n image.href.baseVal = dataUrl;\n } catch (e) {\n console.warn('Failed to load image:', e);\n }\n}\n\n/**\n * Fetches all images in the SVG and replaces them with inlined images as data\n * URLs. Images are replaced in place. The replacement is done, the returned\n * promise is resolved.\n */\nasync function inlineImages(svg: Element): Promise {\n const images = Array.from(svg.getElementsByTagName('image'));\n await Promise.all(images.map(inlineImage));\n}\n\n/** Loads a blob into an image object. */\nfunction loadImage(blob: Blob): Promise {\n const image = new Image();\n image.src = URL.createObjectURL(blob);\n return new Promise((resolve, reject) => {\n image.addEventListener('load', () => resolve(image));\n });\n}\n\n/** Draw image on a new canvas and return the canvas. */\nfunction drawOnCanvas(image: HTMLImageElement) {\n const canvas = document.createElement('canvas');\n // Scale image for better quality.\n canvas.width = image.width * 2;\n canvas.height = image.height * 2;\n\n const ctx = canvas.getContext('2d')!;\n const oldFill = ctx.fillStyle;\n ctx.fillStyle = 'white';\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.fillStyle = oldFill;\n\n ctx.drawImage(image, 0, 0, canvas.width, canvas.height);\n return canvas;\n}\n\nfunction canvasToBlob(canvas: HTMLCanvasElement, type: string) {\n return new Promise((resolve, reject) => {\n canvas.toBlob((blob) => {\n if (blob) {\n resolve(blob);\n } else {\n reject();\n }\n }, type);\n });\n}\n\n/** Supported chart types. */\nexport enum ChartType {\n Hourglass,\n Relatives,\n Fancy,\n}\n\nexport interface ChartProps {\n data: JsonGedcomData;\n selection: IndiInfo;\n chartType: ChartType;\n onSelection: (indiInfo: IndiInfo) => void;\n}\n\n/** Component showing the genealogy chart and handling transition animations. */\nexport class Chart extends React.PureComponent {\n private chart?: ChartHandle;\n /** Animation is in progress. */\n private animating = false;\n /** Rendering is required after the current animation finishes. */\n private rerenderRequired = false;\n /** The d3 zoom behavior object. */\n private zoomBehavior?: d3.ZoomBehavior;\n\n private getChartType() {\n switch (this.props.chartType) {\n case ChartType.Hourglass:\n return HourglassChart;\n case ChartType.Relatives:\n return RelativesChart;\n case ChartType.Fancy:\n return FancyChart;\n default:\n // Fall back to hourglass chart.\n return HourglassChart;\n }\n }\n\n private getRendererType() {\n switch (this.props.chartType) {\n case ChartType.Fancy:\n return CircleRenderer;\n default:\n // Use DetailedRenderer by default.\n return DetailedRenderer;\n }\n }\n\n private zoom(factor: number) {\n const parent = d3.select('#svgContainer') as d3.Selection<\n Element,\n any,\n any,\n any\n >;\n this.zoomBehavior!.scaleBy(parent, factor);\n }\n\n /**\n * Renders the chart or performs a transition animation to a new state.\n * If indiInfo is not given, it means that it is the initial render and no\n * animation is performed.\n */\n private renderChart(args: {initialRender: boolean} = {initialRender: false}) {\n // Wait for animation to finish if animation is in progress.\n if (!args.initialRender && this.animating) {\n this.rerenderRequired = true;\n return;\n }\n\n if (args.initialRender) {\n (d3.select('#chart').node() as HTMLElement).innerHTML = '';\n this.chart = createChart({\n json: this.props.data,\n chartType: this.getChartType(),\n renderer: this.getRendererType(),\n svgSelector: '#chart',\n indiCallback: (info) => this.props.onSelection(info),\n animate: true,\n updateSvgSize: false,\n locale: this.context.intl.locale,\n });\n } else {\n this.chart!.setData(this.props.data);\n }\n const chartInfo = this.chart!.render({\n startIndi: this.props.selection.id,\n baseGeneration: this.props.selection.generation,\n });\n const svg = d3.select('#chartSvg');\n const parent = d3.select('#svgContainer').node() as Element;\n\n const scale = d3.zoomTransform(parent).k;\n const zoomOutFactor = d3.min([\n 1,\n scale,\n parent.clientWidth / chartInfo.size[0],\n parent.clientHeight / chartInfo.size[1],\n ])!;\n const extent: [number, number] = [d3.max([0.1, zoomOutFactor])!, 2];\n\n this.zoomBehavior = d3\n .zoom()\n .scaleExtent(extent)\n .translateExtent([[0, 0], chartInfo.size])\n .on('zoom', () => zoomed(chartInfo.size));\n d3.select(parent)\n .on('scroll', scrolled)\n .call(this.zoomBehavior);\n\n const scrollTopTween = (scrollTop: number) => {\n return () => {\n const i = d3.interpolateNumber(parent.scrollTop, scrollTop);\n return (t: number) => {\n parent.scrollTop = i(t);\n };\n };\n };\n const scrollLeftTween = (scrollLeft: number) => {\n return () => {\n const i = d3.interpolateNumber(parent.scrollLeft, scrollLeft);\n return (t: number) => {\n parent.scrollLeft = i(t);\n };\n };\n };\n\n const dx = parent.clientWidth / 2 - chartInfo.origin[0] * scale;\n const dy = parent.clientHeight / 2 - chartInfo.origin[1] * scale;\n const offsetX = d3.max([\n 0,\n (parent.clientWidth - chartInfo.size[0] * scale) / 2,\n ]);\n const offsetY = d3.max([\n 0,\n (parent.clientHeight - chartInfo.size[1] * scale) / 2,\n ]);\n const svgTransition = svg\n .transition()\n .delay(200)\n .duration(500);\n const transition = args.initialRender ? svg : svgTransition;\n transition\n .attr('transform', `translate(${offsetX}, ${offsetY})`)\n .attr('width', chartInfo.size[0] * scale)\n .attr('height', chartInfo.size[1] * scale);\n if (args.initialRender) {\n parent.scrollLeft = -dx;\n parent.scrollTop = -dy;\n } else {\n svgTransition\n .tween('scrollLeft', scrollLeftTween(-dx))\n .tween('scrollTop', scrollTopTween(-dy));\n }\n\n // After the animation is finished, rerender the chart if required.\n this.animating = true;\n chartInfo.animationPromise.then(() => {\n this.animating = false;\n if (this.rerenderRequired) {\n this.rerenderRequired = false;\n this.renderChart({initialRender: false});\n }\n });\n }\n\n componentDidMount() {\n this.renderChart({initialRender: true});\n }\n\n componentDidUpdate(prevProps: ChartProps) {\n const initialRender = this.props.chartType !== prevProps.chartType;\n this.renderChart({initialRender});\n }\n\n /** Make intl appear in this.context. */\n static contextTypes = {\n intl: intlShape,\n };\n\n render() {\n return (\n \n \n this.zoom(ZOOM_FACTOR)}>\n +\n \n this.zoom(1 / ZOOM_FACTOR)}\n >\n −\n \n \n \n \n \n
\n );\n }\n\n /** Return a copy of the SVG chart but without scaling and positioning. */\n private getStrippedSvg() {\n const svg = document.getElementById('chartSvg')!.cloneNode(true) as Element;\n\n svg.removeAttribute('transform');\n const parent = d3.select('#svgContainer').node() as Element;\n const scale = d3.zoomTransform(parent).k;\n svg.setAttribute(\n 'width',\n String(Number(svg.getAttribute('width')) / scale),\n );\n svg.setAttribute(\n 'height',\n String(Number(svg.getAttribute('height')) / scale),\n );\n svg.querySelector('#chart')!.removeAttribute('transform');\n\n return svg;\n }\n\n private getSvgContents() {\n return new XMLSerializer().serializeToString(this.getStrippedSvg());\n }\n\n private async getSvgContentsWithInlinedImages() {\n const svg = this.getStrippedSvg();\n await inlineImages(svg);\n return new XMLSerializer().serializeToString(svg);\n }\n\n /** Shows the print dialog to print the currently displayed chart. */\n print() {\n const printWindow = document.createElement('iframe');\n printWindow.style.position = 'absolute';\n printWindow.style.top = '-1000px';\n printWindow.style.left = '-1000px';\n printWindow.onload = () => {\n printWindow.contentDocument!.open();\n printWindow.contentDocument!.write(this.getSvgContents());\n printWindow.contentDocument!.close();\n // Doesn't work on Firefox without the setTimeout.\n setTimeout(() => {\n printWindow.contentWindow!.focus();\n printWindow.contentWindow!.print();\n printWindow.parentNode!.removeChild(printWindow);\n }, 500);\n };\n document.body.appendChild(printWindow);\n }\n\n async downloadSvg() {\n const contents = await this.getSvgContentsWithInlinedImages();\n const blob = new Blob([contents], {type: 'image/svg+xml'});\n saveAs(blob, 'topola.svg');\n }\n\n private async drawOnCanvas(): Promise {\n const contents = await this.getSvgContentsWithInlinedImages();\n const blob = new Blob([contents], {type: 'image/svg+xml'});\n return await drawOnCanvas(await loadImage(blob));\n }\n\n async downloadPng() {\n const canvas = await this.drawOnCanvas();\n const blob = await canvasToBlob(canvas, 'image/png');\n saveAs(blob, 'topola.png');\n }\n\n async downloadPdf() {\n const canvas = await this.drawOnCanvas();\n const doc = new jsPDF({\n orientation: canvas.width > canvas.height ? 'l' : 'p',\n unit: 'pt',\n format: [canvas.width, canvas.height],\n });\n doc.addImage(canvas, 'PNG', 0, 0, canvas.width, canvas.height, 'NONE');\n doc.save('topola.pdf');\n }\n}\n","import {\n JsonFam,\n JsonGedcomData,\n JsonIndi,\n gedcomEntriesToJson,\n JsonImage,\n JsonEvent,\n} from 'topola';\nimport {GedcomEntry, parse as parseGedcom} from 'parse-gedcom';\n\nexport interface GedcomData {\n /** The HEAD entry. */\n head: GedcomEntry;\n /** INDI entries mapped by id. */\n indis: {[key: string]: GedcomEntry};\n /** FAM entries mapped by id. */\n fams: {[key: string]: GedcomEntry};\n /** Other entries mapped by id, e.g. NOTE, SOUR. */\n other: {[key: string]: GedcomEntry};\n}\n\nexport interface TopolaData {\n chartData: JsonGedcomData;\n gedcom: GedcomData;\n}\n\n/**\n * Returns the identifier extracted from a pointer string.\n * E.g. '@I123@' -> 'I123'\n */\nexport function pointerToId(pointer: string): string {\n return pointer.substring(1, pointer.length - 1);\n}\n\nexport function idToIndiMap(data: JsonGedcomData): Map {\n const map = new Map();\n data.indis.forEach((indi) => {\n map.set(indi.id, indi);\n });\n return map;\n}\n\nexport function idToFamMap(data: JsonGedcomData): Map {\n const map = new Map();\n data.fams.forEach((fam) => {\n map.set(fam.id, fam);\n });\n return map;\n}\n\nfunction prepareGedcom(entries: GedcomEntry[]): GedcomData {\n const head = entries.find((entry) => entry.tag === 'HEAD')!;\n const indis: {[key: string]: GedcomEntry} = {};\n const fams: {[key: string]: GedcomEntry} = {};\n const other: {[key: string]: GedcomEntry} = {};\n entries.forEach((entry) => {\n if (entry.tag === 'INDI') {\n indis[pointerToId(entry.pointer)] = entry;\n } else if (entry.tag === 'FAM') {\n fams[pointerToId(entry.pointer)] = entry;\n } else if (entry.pointer) {\n other[pointerToId(entry.pointer)] = entry;\n }\n });\n return {head, indis, fams, other};\n}\n\nfunction strcmp(a: string, b: string) {\n if (a < b) {\n return -1;\n }\n if (a > b) {\n return 1;\n }\n return 0;\n}\n\n/** Compares dates of the given events. */\nfunction compareDates(\n event1: JsonEvent | undefined,\n event2: JsonEvent | undefined,\n): number {\n const date1 =\n event1 && (event1.date || (event1.dateRange && event1.dateRange.from));\n const date2 =\n event2 && (event2.date || (event2.dateRange && event2.dateRange.from));\n if (!date1 || !date1.year || !date2 || !date2.year) {\n return 0;\n }\n if (date1.year !== date2.year) {\n return date1.year - date2.year;\n }\n if (!date1.month || !date2.month) {\n return 0;\n }\n if (date1.month !== date2.month) {\n return date1.month - date2.month;\n }\n if (date1.day && date2.day && date1.day !== date2.day) {\n return date1.month - date2.month;\n }\n return 0;\n}\n\n/** Birth date comparator for individuals. */\nfunction birthDatesComparator(gedcom: JsonGedcomData) {\n const indiMap = idToIndiMap(gedcom);\n\n return (indiId1: string, indiId2: string) => {\n const indi1: JsonIndi | undefined = indiMap.get(indiId1);\n const indi2: JsonIndi | undefined = indiMap.get(indiId2);\n return (\n compareDates(indi1 && indi1.birth, indi2 && indi2.birth) ||\n strcmp(indiId1, indiId2)\n );\n };\n}\n\n/** Marriage date comparator for families. */\nfunction marriageDatesComparator(gedcom: JsonGedcomData) {\n const famMap = idToFamMap(gedcom);\n\n return (famId1: string, famId2: string) => {\n const fam1: JsonFam | undefined = famMap.get(famId1);\n const fam2: JsonFam | undefined = famMap.get(famId2);\n return (\n compareDates(fam1 && fam1.marriage, fam2 && fam2.marriage) ||\n strcmp(famId1, famId2)\n );\n };\n}\n\n/**\n * Sorts children by birth date in the given family.\n * Does not modify the input objects.\n */\nfunction sortFamilyChildren(\n fam: JsonFam,\n comparator: (id1: string, id2: string) => number,\n): JsonFam {\n if (!fam.children) {\n return fam;\n }\n const newChildren = fam.children.sort(comparator);\n return Object.assign({}, fam, {children: newChildren});\n}\n\n/**\n * Sorts children by birth date.\n * Does not modify the input object.\n */\nfunction sortChildren(gedcom: JsonGedcomData): JsonGedcomData {\n const comparator = birthDatesComparator(gedcom);\n const newFams = gedcom.fams.map((fam) => sortFamilyChildren(fam, comparator));\n return Object.assign({}, gedcom, {fams: newFams});\n}\n\n/**\n * Sorts spouses by marriage date.\n * Does not modify the input objects.\n */\nfunction sortIndiSpouses(\n indi: JsonIndi,\n comparator: (id1: string, id2: string) => number,\n): JsonFam {\n if (!indi.fams) {\n return indi;\n }\n const newFams = indi.fams.sort(comparator);\n return Object.assign({}, indi, {fams: newFams});\n}\n\nfunction sortSpouses(gedcom: JsonGedcomData): JsonGedcomData {\n const comparator = marriageDatesComparator(gedcom);\n const newIndis = gedcom.indis.map((indi) =>\n sortIndiSpouses(indi, comparator),\n );\n return Object.assign({}, gedcom, {indis: newIndis});\n}\n\n/** Sorts children and spouses. */\nexport function normalizeGedcom(gedcom: JsonGedcomData): JsonGedcomData {\n return sortSpouses(sortChildren(gedcom));\n}\n\nconst IMAGE_EXTENSIONS = ['.jpg', '.png', '.gif'];\n\n/** Returns true if the given file name has a known image extension. */\nfunction isImageFile(fileName: string): boolean {\n const lowerName = fileName.toLowerCase();\n return IMAGE_EXTENSIONS.some((ext) => lowerName.endsWith(ext));\n}\n\n/**\n * Removes images that are not HTTP links or do not have known image extensions.\n * Does not modify the input object.\n */\nfunction filterImage(indi: JsonIndi, images: Map): JsonIndi {\n if (!indi.images || indi.images.length === 0) {\n return indi;\n }\n const newImages: JsonImage[] = [];\n indi.images.forEach((image) => {\n const fileName = image.url.match(/[^/\\\\]*$/)![0];\n // If the image file has been loaded into memory, use it.\n if (images.has(fileName)) {\n newImages.push({url: images.get(fileName)!, title: image.title});\n } else if (image.url.startsWith('http') && isImageFile(image.url)) {\n newImages.push(image);\n }\n });\n return Object.assign({}, indi, {images: newImages});\n}\n\n/**\n * Removes images that are not HTTP links.\n * Does not modify the input object.\n */\nfunction filterImages(\n gedcom: JsonGedcomData,\n images: Map,\n): JsonGedcomData {\n const newIndis = gedcom.indis.map((indi) => filterImage(indi, images));\n return Object.assign({}, gedcom, {indis: newIndis});\n}\n\n/**\n * Converts GEDCOM file into JSON data performing additional transformations:\n * - sort children by birth date\n * - remove images that are not HTTP links and aren't mapped in `images`.\n *\n * @param images Map from file name to image URL. This is used to pass in\n * uploaded images.\n */\nexport function convertGedcom(\n gedcom: string,\n images: Map,\n): TopolaData {\n const entries = parseGedcom(gedcom);\n const json = gedcomEntriesToJson(entries);\n if (\n !json ||\n !json.indis ||\n !json.indis.length ||\n !json.fams ||\n !json.fams.length\n ) {\n throw new Error('Failed to read GEDCOM file');\n }\n\n return {\n chartData: filterImages(normalizeGedcom(json), images),\n gedcom: prepareGedcom(entries),\n };\n}\n\nexport function getSoftware(head: GedcomEntry): string | null {\n const sour =\n head && head.tree && head.tree.find((entry) => entry.tag === 'SOUR');\n const name =\n sour && sour.tree && sour.tree.find((entry) => entry.tag === 'NAME');\n return (name && name.data) || null;\n}\n","import {Date as TopolaDate, DateRange, getDate, DateOrRange} from 'topola';\nimport {InjectedIntl} from 'react-intl';\n\nconst DATE_QUALIFIERS = new Map([\n ['abt', 'about'],\n ['cal', 'calculated'],\n ['est', 'estimated'],\n]);\n\nfunction formatDate(date: TopolaDate, intl: InjectedIntl) {\n const hasDay = date.day !== undefined;\n const hasMonth = date.month !== undefined;\n const hasYear = date.year !== undefined;\n if (!hasDay && !hasMonth && !hasYear) {\n return date.text || '';\n }\n const dateObject = new Date(\n hasYear ? date.year! : 0,\n hasMonth ? date.month! - 1 : 0,\n hasDay ? date.day! : 1,\n );\n\n const qualifier = date.qualifier && date.qualifier.toLowerCase();\n const translatedQualifier =\n qualifier &&\n intl.formatMessage({\n id: `date.${qualifier}`,\n defaultMessage: DATE_QUALIFIERS.get(qualifier) || qualifier,\n });\n\n const formatOptions = {\n day: hasDay ? 'numeric' : undefined,\n month: hasMonth ? 'long' : undefined,\n year: hasYear ? 'numeric' : undefined,\n };\n const translatedDate = new Intl.DateTimeFormat(\n intl.locale,\n formatOptions,\n ).format(dateObject);\n\n return [translatedQualifier, translatedDate].join(' ');\n}\n\nfunction formatDateRage(dateRange: DateRange, intl: InjectedIntl) {\n const fromDate = dateRange.from;\n const toDate = dateRange.to;\n const translatedFromDate = fromDate && formatDate(fromDate, intl);\n const translatedToDate = toDate && formatDate(toDate, intl);\n if (translatedFromDate && translatedToDate) {\n return intl.formatMessage(\n {\n id: 'date.between',\n defaultMessage: 'between {from} and {to}',\n },\n {from: translatedFromDate, to: translatedToDate},\n );\n }\n if (translatedFromDate) {\n return intl.formatMessage(\n {\n id: 'date.after',\n defaultMessage: 'after {from}',\n },\n {from: translatedFromDate},\n );\n }\n if (translatedToDate) {\n return intl.formatMessage(\n {\n id: 'date.before',\n defaultMessage: 'before {to}',\n },\n {to: translatedToDate},\n );\n }\n return '';\n}\n\n/** Formats a DateOrRange object. */\nexport function formatDateOrRange(\n dateOrRange: DateOrRange | undefined,\n intl: InjectedIntl,\n): string {\n if (!dateOrRange) {\n return '';\n }\n if (dateOrRange.date) {\n return formatDate(dateOrRange.date, intl);\n }\n if (dateOrRange.dateRange) {\n return formatDateRage(dateOrRange.dateRange, intl);\n }\n return '';\n}\n\n/** Formats a date given in GEDCOM format. */\nexport function translateDate(gedcomDate: string, intl: InjectedIntl): string {\n return formatDateOrRange(getDate(gedcomDate), intl);\n}\n","import * as React from 'react';\nimport flatMap from 'array.prototype.flatmap';\nimport Linkify from 'react-linkify';\nimport {FormattedMessage, InjectedIntl} from 'react-intl';\nimport {GedcomData, pointerToId} from './gedcom_util';\nimport {GedcomEntry} from 'parse-gedcom';\nimport {intlShape} from 'react-intl';\nimport {translateDate} from './date_util';\n\ninterface Props {\n gedcom: GedcomData;\n indi: string;\n}\n\nconst EVENT_TAGS = ['BIRT', 'BAPM', 'CHR', 'DEAT', 'BURI', 'EVEN', 'CENS'];\nconst EXCLUDED_TAGS = ['NAME', 'SEX', 'FAMC', 'FAMS', 'NOTE', 'SOUR'];\nconst TAG_DESCRIPTIONS = new Map([\n ['BAPM', 'Baptism'],\n ['BIRT', 'Birth'],\n ['BURI', 'Burial'],\n ['CENS', 'Census'],\n ['CHR', 'Christening'],\n ['DEAT', 'Death'],\n ['EMAIL', 'E-mail'],\n ['EVEN', 'Event'],\n ['OCCU', 'Occupation'],\n ['TITL', 'Title'],\n ['WWW', 'WWW'],\n]);\n\nfunction translateTag(tag: string) {\n return (\n \n );\n}\n\nfunction joinLines(lines: (JSX.Element | string)[]) {\n return (\n <>\n {lines.map((line, index) => (\n \n {line} \n \n
\n ))}\n >\n );\n}\n\n/**\n * Returns the data for the given GEDCOM entry as an array of lines. Supports\n * continuations with CONT and CONC.\n */\nfunction getData(entry: GedcomEntry) {\n const result = [entry.data];\n entry.tree.forEach((subentry) => {\n if (subentry.tag === 'CONC' && subentry.data) {\n const last = result.length - 1;\n result[last] += subentry.data;\n } else if (subentry.tag === 'CONT' && subentry.data) {\n result.push(subentry.data);\n }\n });\n return result;\n}\n\nfunction eventDetails(entry: GedcomEntry, intl: InjectedIntl) {\n const lines = [];\n if (entry.data && entry.data.length > 1) {\n lines.push({entry.data} );\n }\n const date = entry.tree.find((subentry) => subentry.tag === 'DATE');\n if (date && date.data) {\n lines.push(translateDate(date.data, intl));\n }\n const place = entry.tree.find((subentry) => subentry.tag === 'PLAC');\n if (place && place.data) {\n lines.push(...getData(place));\n }\n entry.tree\n .filter((subentry) => subentry.tag === 'NOTE')\n .forEach((note) =>\n getData(note).forEach((line) => lines.push({line} )),\n );\n if (!lines.length) {\n return null;\n }\n return (\n <>\n {translateTag(entry.tag)}
\n {joinLines(lines)} \n >\n );\n}\n\nfunction dataDetails(entry: GedcomEntry) {\n const lines = [];\n if (entry.data) {\n lines.push(...getData(entry));\n }\n entry.tree\n .filter((subentry) => subentry.tag === 'NOTE')\n .forEach((note) =>\n getData(note).forEach((line) => lines.push({line} )),\n );\n if (!lines.length) {\n return null;\n }\n return (\n <>\n {translateTag(entry.tag)}
\n {joinLines(lines)} \n >\n );\n}\n\nfunction noteDetails(entry: GedcomEntry) {\n return joinLines(\n getData(entry).map((line, index) => {line} ),\n );\n}\n\nfunction nameDetails(entry: GedcomEntry) {\n return (\n \n {entry.data\n .split('/')\n .filter((name) => !!name)\n .map((name, index) => (\n \n {name}\n \n
\n ))}\n \n );\n}\n\nfunction getDetails(\n entries: GedcomEntry[],\n tags: string[],\n detailsFunction: (entry: GedcomEntry) => JSX.Element | null,\n): JSX.Element[] {\n return flatMap(tags, (tag) =>\n entries\n .filter((entry) => entry.tag === tag)\n .map((entry) => detailsFunction(entry)),\n )\n .filter((element) => element !== null)\n .map((element, index) => (\n \n {element}\n
\n ));\n}\n\n/**\n * Returns true if there is displayable information in this entry.\n * Returns false if there is no data in this entry or this is only a reference\n * to another entry.\n */\nfunction hasData(entry: GedcomEntry) {\n return entry.tree.length > 0 || (entry.data && !entry.data.startsWith('@'));\n}\n\nfunction getOtherDetails(entries: GedcomEntry[]) {\n return entries\n .filter(\n (entry) =>\n !EXCLUDED_TAGS.includes(entry.tag) && !EVENT_TAGS.includes(entry.tag),\n )\n .filter(hasData)\n .map((entry) => dataDetails(entry))\n .filter((element) => element !== null)\n .map((element, index) => (\n \n {element}\n
\n ));\n}\n\n/**\n * If the entry is a reference to a top-level entry, the referenced entry is\n * returned. Otherwise, returns the given entry unmodified.\n */\nfunction dereference(entry: GedcomEntry, gedcom: GedcomData) {\n if (entry.data) {\n const dereferenced = gedcom.other[pointerToId(entry.data)];\n if (dereferenced) {\n return dereferenced;\n }\n }\n return entry;\n}\n\nexport class Details extends React.Component {\n /** Make intl appear in this.context. */\n static contextTypes = {\n intl: intlShape,\n };\n\n render() {\n const entries = this.props.gedcom.indis[this.props.indi].tree;\n const entriesWithData = entries\n .map((entry) => dereference(entry, this.props.gedcom))\n .filter(hasData);\n\n return (\n \n {getDetails(entries, ['NAME'], nameDetails)}\n {getDetails(entries, EVENT_TAGS, (entry) =>\n eventDetails(entry, this.context.intl as InjectedIntl),\n )}\n {getOtherDetails(entriesWithData)}\n {getDetails(entriesWithData, ['NOTE'], noteDetails)}\n
\n );\n }\n}\n","import {convertGedcom, TopolaData} from './gedcom_util';\nimport {IndiInfo, JsonGedcomData} from 'topola';\n\n/**\n * Returns a valid IndiInfo object, either with the given indi and generation\n * or with an individual taken from the data and generation 0.\n */\nexport function getSelection(\n data: JsonGedcomData,\n indi?: string,\n generation?: number,\n): IndiInfo {\n // If ID is not given or it doesn't exist in the data, use the first ID in\n // the data.\n const id =\n indi && data.indis.some((i) => i.id === indi) ? indi : data.indis[0].id;\n return {id, generation: generation || 0};\n}\n\nfunction prepareData(\n gedcom: string,\n cacheId: string,\n images?: Map,\n): TopolaData {\n const data = convertGedcom(gedcom, images || new Map());\n const serializedData = JSON.stringify(data);\n try {\n sessionStorage.setItem(cacheId, serializedData);\n } catch (e) {\n console.warn('Failed to store data in session storage: ' + e);\n }\n return data;\n}\n\n/** Fetches data from the given URL. Uses cors-anywhere if handleCors is true. */\nexport async function loadFromUrl(\n url: string,\n handleCors: boolean,\n): Promise {\n try {\n const cachedData = sessionStorage.getItem(url);\n if (cachedData) {\n return JSON.parse(cachedData);\n }\n } catch (e) {\n console.warn('Failed to load data from session storage: ' + e);\n }\n const urlToFetch = handleCors\n ? 'https://cors-anywhere.herokuapp.com/' + url\n : url;\n\n const response = await window.fetch(urlToFetch);\n if (response.status !== 200) {\n throw new Error(response.statusText);\n }\n const gedcom = await response.text();\n return prepareData(gedcom, url);\n}\n\n/** Loads data from the given GEDCOM file contents. */\nexport async function loadGedcom(\n hash: string,\n gedcom?: string,\n images?: Map,\n): Promise {\n try {\n const cachedData = sessionStorage.getItem(hash);\n if (cachedData) {\n return JSON.parse(cachedData);\n }\n } catch (e) {\n console.warn('Failed to load data from session storage: ' + e);\n }\n if (!gedcom) {\n throw new Error('Error loading data. Please upload your file again.');\n }\n return prepareData(gedcom, hash, images);\n}\n","import * as queryString from 'query-string';\nimport * as React from 'react';\nimport logo from './topola.jpg';\nimport {Card, Grid, Image, Responsive} from 'semantic-ui-react';\nimport {FormattedMessage} from 'react-intl';\nimport {Link} from 'react-router-dom';\n\n/** Link that loads a GEDCOM file from URL. */\nfunction GedcomLink(props: {url: string; text: string}) {\n return (\n \n {props.text}\n \n );\n}\n\nfunction formatBuildDate(dateString: string) {\n return dateString.slice(0, 16);\n}\n\n/** The intro page. */\nexport function Intro() {\n const contents = (\n <>\n \n \n
\n \n \n
\n \n \n
\n \n \n \n \n \n {': '}\n cors-anywhere
\n ),\n }}\n />\n \n \n version: {formatBuildDate(process.env.REACT_APP_GIT_TIME!)} (\n \n {process.env.REACT_APP_GIT_SHA}\n \n )\n
\n >\n );\n\n return (\n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n {contents} \n \n \n \n \n {contents}\n \n \n \n
\n );\n}\n","import Cookies from 'js-cookie';\nimport {Date, JsonFam, JsonIndi, DateOrRange} from 'topola';\nimport {GedcomData, TopolaData, normalizeGedcom} from './gedcom_util';\nimport {GedcomEntry} from 'parse-gedcom';\n\n/** WikiTree API getAncestors request. */\ninterface GetAncestorsRequest {\n action: 'getAncestors';\n key: string;\n fields: string;\n}\n\n/** WikiTree API getRelatives request. */\ninterface GetRelatives {\n action: 'getRelatives';\n keys: string;\n getChildren?: true;\n getSpouses?: true;\n}\n\ninterface ClientLogin {\n action: 'clientLogin';\n authcode: string;\n}\n\ntype WikiTreeRequest = GetAncestorsRequest | GetRelatives | ClientLogin;\n\n/** Person structure returned from WikiTree API. */\ninterface Person {\n Id: number;\n Name: string;\n FirstName: string;\n LastNameAtBirth: string;\n Spouses: {[key: number]: Person};\n Children: {[key: number]: Person};\n Mother: number;\n Father: number;\n Gender: string;\n BirthDate: string;\n DeathDate: string;\n BirthLocation: string;\n DeathLocation: string;\n marriage_location: string;\n marriage_date: string;\n DataStatus?: {\n BirthDate: string;\n DeathDate: string;\n };\n PhotoData?: {\n path: string;\n };\n}\n\n/** Gets item from session storage. Logs exception if one is thrown. */\nfunction getSessionStorageItem(key: string): string | null {\n try {\n return sessionStorage.getItem(key);\n } catch (e) {\n console.warn('Failed to load data from session storage: ' + e);\n }\n return null;\n}\n\n/** Sets item in session storage. Logs exception if one is thrown. */\nfunction setSessionStorageItem(key: string, value: string) {\n try {\n sessionStorage.setItem(key, value);\n } catch (e) {\n console.warn('Failed to store data in session storage: ' + e);\n }\n}\n\n/** Sends a request to the WikiTree API. Returns the parsed response JSON. */\nasync function wikiTreeGet(request: WikiTreeRequest, handleCors: boolean) {\n const requestData = new FormData();\n requestData.append('format', 'json');\n for (const key in request) {\n requestData.append(key, request[key]);\n }\n const apiUrl = handleCors\n ? 'https://cors-anywhere.herokuapp.com/https://apps.wikitree.com/api.php'\n : 'https://apps.wikitree.com/api.php';\n const response = await window.fetch(apiUrl, {\n method: 'POST',\n body: requestData,\n });\n const responseBody = await response.text();\n return JSON.parse(responseBody);\n}\n\n/**\n * Retrieves ancestors from WikiTree for the given person ID.\n * Uses sessionStorage for caching responses.\n */\nasync function getAncestors(key: string, handleCors: boolean) {\n const cacheKey = `wikitree:ancestors:${key}`;\n const cachedData = getSessionStorageItem(cacheKey);\n if (cachedData) {\n return JSON.parse(cachedData);\n }\n const response = await wikiTreeGet(\n {\n action: 'getAncestors',\n key: key,\n fields: '*',\n },\n handleCors,\n );\n const result = response[0].ancestors as Person[];\n setSessionStorageItem(cacheKey, JSON.stringify(result));\n return result;\n}\n\n/**\n * Retrieves relatives from WikiTree for the given array of person IDs.\n * Uses sessionStorage for caching responses.\n */\nasync function getRelatives(keys: string[], handleCors: boolean) {\n const result: Person[] = [];\n const keysToFetch: string[] = [];\n keys.forEach((key) => {\n const cachedData = getSessionStorageItem(`wikitree:relatives:${key}`);\n if (cachedData) {\n result.push(JSON.parse(cachedData));\n } else {\n keysToFetch.push(key);\n }\n });\n if (keysToFetch.length === 0) {\n return result;\n }\n const response = await wikiTreeGet(\n {\n action: 'getRelatives',\n keys: keysToFetch.join(','),\n getChildren: true,\n getSpouses: true,\n },\n handleCors,\n );\n if (response[0].items === null) {\n throw new Error(`WikiTree profile ${keysToFetch[0]} not found.`);\n }\n const fetchedResults = response[0].items.map(\n (x: {person: Person}) => x.person,\n ) as Person[];\n fetchedResults.forEach((person) => {\n setSessionStorageItem(\n `wikitree:relatives:${person.Name}`,\n JSON.stringify(person),\n );\n });\n return result.concat(fetchedResults);\n}\n\nexport async function clientLogin(authcode: string) {\n const response = await wikiTreeGet(\n {\n action: 'clientLogin',\n authcode,\n },\n false,\n );\n return response.clientLogin;\n}\n\n/**\n * Loads data from WikiTree to populate an hourglass chart starting from the\n * given person ID.\n */\nexport async function loadWikiTree(\n key: string,\n authcode?: string,\n): Promise {\n // Work around CORS if not in apps.wikitree.com domain.\n const handleCors = window.location.hostname !== 'apps.wikitree.com';\n\n if (!handleCors && !Cookies.get('wikidb_wtb_UserID') && authcode) {\n const loginResult = await clientLogin(authcode);\n if (loginResult.result === 'Success') {\n sessionStorage.clear();\n }\n }\n\n const everyone: Person[] = [];\n\n // Fetch the ancestors of the input person and ancestors of his/her spouses.\n const firstPerson = await getRelatives([key], handleCors);\n if (!firstPerson[0].Name) {\n throw new Error(\n `WikiTree profile ${key} is not accessible. Try logging in.`,\n );\n }\n\n const spouseKeys = Object.values(firstPerson[0].Spouses).map((s) => s.Name);\n const ancestors = await Promise.all(\n [key]\n .concat(spouseKeys)\n .map((personId) => getAncestors(personId, handleCors)),\n );\n const ancestorKeys = ancestors\n .flat()\n .map((person) => person.Name)\n .filter((key) => !!key);\n const ancestorDetails = await getRelatives(ancestorKeys, handleCors);\n everyone.push(...ancestorDetails);\n\n // Limit the number of generations of descendants because there may be tens of\n // generations for some profiles.\n const descendantGenerationLimit = 5;\n\n // Fetch descendants recursively.\n let toFetch = [key];\n let generation = 0;\n while (toFetch.length > 0 && generation <= descendantGenerationLimit) {\n const people = await getRelatives(toFetch, handleCors);\n everyone.push(...people);\n const allSpouses = people.flatMap((person) =>\n Object.values(person.Spouses),\n );\n everyone.push(...allSpouses);\n // Fetch all children.\n toFetch = people.flatMap((person) =>\n Object.values(person.Children).map((c) => c.Name),\n );\n generation++;\n }\n\n // Map from person id to the set of families where they are a spouse.\n const families = new Map>();\n // Map from family id to the set of children.\n const children = new Map>();\n // Map from famliy id to the spouses.\n const spouses = new Map<\n string,\n {wife?: number; husband?: number; spouse?: Person}\n >();\n // Map from numerical id to human-readable id.\n const idToName = new Map();\n\n everyone.forEach((person) => {\n idToName.set(person.Id, person.Name);\n if (person.Mother || person.Father) {\n const famId = getFamilyId(person.Mother, person.Father);\n getSet(families, person.Mother).add(famId);\n getSet(families, person.Father).add(famId);\n getSet(children, famId).add(person.Id);\n spouses.set(famId, {\n wife: person.Mother || undefined,\n husband: person.Father || undefined,\n });\n }\n });\n\n const indis: JsonIndi[] = [];\n const converted = new Set();\n everyone.forEach((person) => {\n if (converted.has(person.Id)) {\n return;\n }\n converted.add(person.Id);\n const indi = convertPerson(person);\n if (person.Spouses) {\n Object.values(person.Spouses).forEach((spouse) => {\n const famId = getFamilyId(person.Id, spouse.Id);\n getSet(families, person.Id).add(famId);\n getSet(families, spouse.Id).add(famId);\n const familySpouses =\n person.Gender === 'Male'\n ? {wife: spouse.Id, husband: person.Id, spouse}\n : {wife: person.Id, husband: spouse.Id, spouse};\n spouses.set(famId, familySpouses);\n });\n }\n indi.fams = Array.from(getSet(families, person.Id));\n indis.push(indi);\n });\n\n const fams = Array.from(spouses.entries()).map(([key, value]) => {\n const fam: JsonFam = {\n id: key,\n };\n const wife = value.wife && idToName.get(value.wife);\n if (wife) {\n fam.wife = wife;\n }\n const husband = value.husband && idToName.get(value.husband);\n if (husband) {\n fam.husb = husband;\n }\n fam.children = Array.from(getSet(children, key)).map(\n (child) => idToName.get(child)!,\n );\n if (\n value.spouse &&\n ((value.spouse.marriage_date &&\n value.spouse.marriage_date !== '0000-00-00') ||\n value.spouse.marriage_location)\n ) {\n const parsedDate = parseDate(value.spouse.marriage_date);\n fam.marriage = Object.assign({}, parsedDate, {\n place: value.spouse.marriage_location,\n });\n }\n return fam;\n });\n\n const chartData = normalizeGedcom({indis, fams});\n const gedcom = buildGedcom(indis);\n return {chartData, gedcom};\n}\n\n/** Creates a family identifier given 2 spouse identifiers. */\nfunction getFamilyId(spouse1: number, spouse2: number) {\n if (spouse2 > spouse1) {\n return `${spouse1}_${spouse2}`;\n }\n return `${spouse2}_${spouse1}`;\n}\n\nfunction convertPerson(person: Person): JsonIndi {\n const indi: JsonIndi = {\n id: person.Name,\n };\n if (person.FirstName !== 'Unknown') {\n indi.firstName = person.FirstName;\n }\n if (person.LastNameAtBirth !== 'Unknown') {\n indi.lastName = person.LastNameAtBirth;\n }\n if (person.Mother || person.Father) {\n indi.famc = getFamilyId(person.Mother, person.Father);\n }\n if (person.Gender === 'Male') {\n indi.sex = 'M';\n } else if (person.Gender === 'Female') {\n indi.sex = 'F';\n }\n if (\n (person.BirthDate && person.BirthDate !== '0000-00-00') ||\n person.BirthLocation\n ) {\n const parsedDate = parseDate(\n person.BirthDate,\n person.DataStatus && person.DataStatus.BirthDate,\n );\n indi.birth = Object.assign({}, parsedDate, {place: person.BirthLocation});\n }\n if (\n (person.DeathDate && person.DeathDate !== '0000-00-00') ||\n person.DeathLocation\n ) {\n const parsedDate = parseDate(\n person.DeathDate,\n person.DataStatus && person.DataStatus.DeathDate,\n );\n indi.death = Object.assign({}, parsedDate, {place: person.DeathLocation});\n }\n if (person.PhotoData) {\n indi.images = [{url: `https://www.wikitree.com${person.PhotoData.path}`}];\n }\n return indi;\n}\n\n/**\n * Parses a date in the format returned by WikiTree and converts in to\n * the format defined by Topola.\n */\nfunction parseDate(date: string, dataStatus?: string): DateOrRange | undefined {\n if (!date) {\n return undefined;\n }\n const matchedDate = date.match(/(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)/);\n if (!matchedDate) {\n return {date: {text: date}};\n }\n const parsedDate: Date = {};\n if (matchedDate[1] !== '0000') {\n parsedDate.year = ~~matchedDate[1];\n }\n if (matchedDate[2] !== '00') {\n parsedDate.month = ~~matchedDate[2];\n }\n if (matchedDate[3] !== '00') {\n parsedDate.day = ~~matchedDate[3];\n }\n if (dataStatus === 'after') {\n return {dateRange: {from: parsedDate}};\n }\n if (dataStatus === 'before') {\n return {dateRange: {to: parsedDate}};\n }\n if (dataStatus === 'guess') {\n parsedDate.qualifier = 'abt';\n }\n return {date: parsedDate};\n}\n\n/**\n * Creates a GEDCOM structure for the purpose of displaying the details\n * panel.\n */\nfunction buildGedcom(indis: JsonIndi[]): GedcomData {\n const gedcomIndis: {[key: string]: GedcomEntry} = {};\n indis.forEach((indi) => {\n // WikiTree URLs replace spaces with underscores.\n const escapedId = indi.id.replace(/ /g, '_');\n gedcomIndis[indi.id] = {\n level: 0,\n pointer: `@${indi.id}@`,\n tag: 'INDI',\n data: '',\n tree: [\n {\n level: 1,\n pointer: '',\n tag: 'NAME',\n data: `${indi.firstName || ''} /${indi.lastName || ''}/`,\n tree: [],\n },\n {\n level: 1,\n pointer: '',\n tag: 'WWW',\n data: `https://www.wikitree.com/wiki/${escapedId}`,\n tree: [],\n },\n ],\n };\n });\n\n return {\n head: {level: 0, pointer: '', tag: 'HEAD', data: '', tree: []},\n indis: gedcomIndis,\n fams: {},\n other: {},\n };\n}\n\n/**\n * Returns a set which is a value from a SetMultimap. If the key doesn't exist,\n * an empty set is added to the map.\n */\nfunction getSet(map: Map>, key: K): Set {\n const set = map.get(key);\n if (set) {\n return set;\n }\n const newSet = new Set();\n map.set(key, newSet);\n return newSet;\n}\n","import naturalSort from 'javascript-natural-sort';\nimport lunr from 'lunr';\nimport {idToIndiMap, idToFamMap} from './gedcom_util';\nimport {JsonIndi, JsonFam, JsonGedcomData} from 'topola';\n\nconst MAX_RESULTS = 8;\n\nexport interface SearchResult {\n id: string;\n indi: JsonIndi;\n}\n\nexport interface SearchIndex {\n search(input: string): SearchResult[];\n}\n\n/** Removes accents from letters, e.g. ó->o, ę->e. */\nfunction normalize(input: string) {\n return input\n .toLocaleLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/\\u0142/g, 'l'); // Special case: ł is not affected by NFD.\n}\n\n/** Comparator to sort by score first, then by id. */\nfunction compare(a: lunr.Index.Result, b: lunr.Index.Result) {\n if (a.score !== b.score) {\n return b.score - a.score;\n }\n return naturalSort(a.ref, b.ref);\n}\n\n/** Returns all last names of all husbands as a space-separated string. */\nfunction getHusbandLastName(\n indi: JsonIndi,\n indiMap: Map,\n famMap: Map,\n): string {\n return (indi.fams || [])\n .map((famId) => famMap.get(famId))\n .map((fam) => fam && fam.husb)\n .map((husbId) => husbId && indiMap.get(husbId))\n .map((husband) => husband && husband.lastName)\n .join(' ');\n}\n\nclass LunrSearchIndex implements SearchIndex {\n private index: lunr.Index | undefined;\n private indiMap: Map;\n private famMap: Map;\n\n constructor(data: JsonGedcomData) {\n this.indiMap = idToIndiMap(data);\n this.famMap = idToFamMap(data);\n }\n\n initialize() {\n const self = this;\n this.index = lunr(function() {\n this.ref('id');\n this.field('id');\n this.field('name', {boost: 10});\n this.field('normalizedName', {boost: 8});\n this.field('spouseLastName', {boost: 2});\n this.field('normalizedSpouseLastName', {boost: 2});\n\n self.indiMap.forEach((indi) => {\n const name = [indi.firstName, indi.lastName].join(' ');\n const spouseLastName = getHusbandLastName(\n indi,\n self.indiMap,\n self.famMap,\n );\n this.add({\n id: indi.id,\n name,\n normalizedName: normalize(name),\n spouseLastName,\n normalizedSpouseLastName: normalize(spouseLastName),\n });\n });\n });\n }\n\n public search(input: string): SearchResult[] {\n const query = input\n .split(' ')\n .filter((s) => !!s)\n .map((s) => `+${s}*`)\n .join(' ');\n const results = this.index!.search(query);\n return results\n .sort(compare)\n .slice(0, MAX_RESULTS)\n .map((result) => ({id: result.ref, indi: this.indiMap.get(result.ref)!}));\n }\n}\n\n/** Builds a search index from data. */\nexport function buildSearchIndex(data: JsonGedcomData): SearchIndex {\n const index = new LunrSearchIndex(data);\n index.initialize();\n return index;\n}\n","import * as React from 'react';\nimport {InjectedIntl} from 'react-intl';\nimport {SearchResult} from './search_index';\nimport {formatDateOrRange} from './date_util';\nimport {JsonIndi} from 'topola';\n\nfunction getNameLine(result: SearchResult) {\n const name = [result.indi.firstName, result.indi.lastName].join(' ').trim();\n if (result.id.length > 8) {\n return name;\n }\n return (\n <>\n {name} ({result.id}) \n >\n );\n}\n\nfunction getDescriptionLine(indi: JsonIndi, intl: InjectedIntl) {\n const birthDate = formatDateOrRange(indi.birth, intl);\n const deathDate = formatDateOrRange(indi.death, intl);\n if (!deathDate) {\n return birthDate;\n }\n return `${birthDate} – ${deathDate}`;\n}\n\n/** Produces an object that is displayed in the Semantic UI Search results. */\nexport function displaySearchResult(result: SearchResult, intl: InjectedIntl) {\n return {\n id: result.id,\n key: result.id,\n title: getNameLine(result),\n description: getDescriptionLine(result.indi, intl),\n };\n}\n","import * as queryString from 'query-string';\nimport * as React from 'react';\nimport Cookies from 'js-cookie';\nimport debounce from 'debounce';\nimport md5 from 'md5';\nimport {analyticsEvent} from './analytics';\nimport {buildSearchIndex, SearchIndex} from './search_index';\nimport {displaySearchResult} from './search_util';\nimport {FormattedMessage, intlShape} from 'react-intl';\nimport {IndiInfo, JsonGedcomData} from 'topola';\nimport {Link} from 'react-router-dom';\nimport {RouteComponentProps} from 'react-router-dom';\nimport {\n Header,\n Button,\n Icon,\n Menu,\n Modal,\n Input,\n Form,\n Dropdown,\n Search,\n SearchProps,\n SearchResultProps,\n Responsive,\n} from 'semantic-ui-react';\n\nconst WIKITREE_LOGO_URL =\n 'https://www.wikitree.com/photo.php/a/a5/WikiTree_Images.png';\n\nenum WikiTreeLoginState {\n UNKNOWN,\n NOT_LOGGED_IN,\n LOGGED_IN,\n}\n\nenum ScreenSize {\n LARGE,\n SMALL,\n}\n\n/** Menus and dialogs state. */\ninterface State {\n loadUrlDialogOpen: boolean;\n wikiTreeIdDialogOpen: boolean;\n url?: string;\n wikiTreeId?: string;\n wikiTreeLoginState: WikiTreeLoginState;\n wikiTreeLoginUsername?: string;\n searchResults: SearchResultProps[];\n}\n\ninterface EventHandlers {\n onSelection: (indiInfo: IndiInfo) => void;\n onPrint: () => void;\n onDownloadPdf: () => void;\n onDownloadPng: () => void;\n onDownloadSvg: () => void;\n}\n\ninterface Props {\n /** True if the application is currently showing a chart. */\n showingChart: boolean;\n /** Data used for the search index. */\n data?: JsonGedcomData;\n standalone: boolean;\n /** Whether to show the \"All relatives\" chart type in the menu. */\n allowAllRelativesChart: boolean;\n eventHandlers: EventHandlers;\n /** Whether to show additional WikiTree menus. */\n showWikiTreeMenus: boolean;\n}\n\nfunction loadFileAsText(file: File): Promise {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = (evt: ProgressEvent) => {\n resolve((evt.target as FileReader).result as string);\n };\n reader.readAsText(file);\n });\n}\n\nfunction isImageFileName(fileName: string) {\n const lower = fileName.toLowerCase();\n return lower.endsWith('.jpg') || lower.endsWith('.png');\n}\n\nexport class TopBar extends React.Component<\n RouteComponentProps & Props,\n State\n> {\n state: State = {\n loadUrlDialogOpen: false,\n wikiTreeIdDialogOpen: false,\n searchResults: [],\n wikiTreeLoginState: WikiTreeLoginState.UNKNOWN,\n };\n /** Make intl appear in this.context. */\n static contextTypes = {\n intl: intlShape,\n };\n\n urlInputRef: React.RefObject = React.createRef();\n wikiTreeIdInputRef: React.RefObject = React.createRef();\n wikiTreeLoginFormRef: React.RefObject = React.createRef();\n wikiTreeReturnUrlRef: React.RefObject = React.createRef();\n searchRef?: {setValue(value: string): void};\n searchIndex?: SearchIndex;\n\n /** Handles the \"Upload file\" button. */\n private async handleUpload(event: React.SyntheticEvent) {\n const files = (event.target as HTMLInputElement).files;\n if (!files || !files.length) {\n return;\n }\n const filesArray = Array.from(files);\n (event.target as HTMLInputElement).value = ''; // Reset the file input.\n analyticsEvent('upload_files_selected', {\n event_value: files.length,\n });\n\n const gedcomFile =\n filesArray.length === 1\n ? filesArray[0]\n : filesArray.find((file) => file.name.toLowerCase().endsWith('.ged')) ||\n filesArray[0];\n\n // Convert uploaded images to object URLs.\n const images = filesArray\n .filter(\n (file) => file.name !== gedcomFile.name && isImageFileName(file.name),\n )\n .map((file) => ({\n name: file.name,\n url: URL.createObjectURL(file),\n }));\n const imageMap = new Map(\n images.map((entry) => [entry.name, entry.url] as [string, string]),\n );\n\n const data = await loadFileAsText(gedcomFile);\n const imageFileNames = images\n .map((image) => image.name)\n .sort()\n .join('|');\n // Hash GEDCOM contents with uploaded image file names.\n const hash = md5(md5(data) + imageFileNames);\n\n // Use history.replace() when reuploading the same file and history.push() when loading\n // a new file.\n const search = queryString.parse(this.props.location.search);\n const historyPush =\n search.file === hash\n ? this.props.history.replace\n : this.props.history.push;\n\n historyPush({\n pathname: '/view',\n search: queryString.stringify({file: hash}),\n state: {data, images: imageMap},\n });\n }\n\n /** Opens the \"Load from URL\" dialog. */\n private openLoadUrlDialog() {\n this.setState(\n Object.assign({}, this.state, {loadUrlDialogOpen: true}),\n () => this.urlInputRef.current!.focus(),\n );\n }\n\n private openWikiTreeIdDialog() {\n this.setState(\n Object.assign({}, this.state, {wikiTreeIdDialogOpen: true}),\n () => this.wikiTreeIdInputRef.current!.focus(),\n );\n }\n\n /** Cancels any of the open dialogs. */\n private handleClose() {\n this.setState(\n Object.assign({}, this.state, {\n loadUrlDialogOpen: false,\n wikiTreeIdDialogOpen: false,\n }),\n );\n }\n\n /** Load button clicked in the \"Load from URL\" dialog. */\n private handleLoad() {\n this.setState(\n Object.assign({}, this.state, {\n loadUrlDialogOpen: false,\n }),\n );\n if (this.state.url) {\n analyticsEvent('url_selected');\n this.props.history.push({\n pathname: '/view',\n search: queryString.stringify({url: this.state.url}),\n });\n }\n }\n\n /** Select button clicked in the \"Select WikiTree ID\" dialog. */\n private handleSelectWikiTreeId() {\n this.setState(\n Object.assign({}, this.state, {\n wikiTreeIdDialogOpen: false,\n }),\n );\n if (this.state.wikiTreeId) {\n analyticsEvent('wikitree_id_selected');\n const search = queryString.parse(this.props.location.search);\n const standalone =\n search.standalone !== undefined ? search.standalone : true;\n this.props.history.push({\n pathname: '/view',\n search: queryString.stringify({\n indi: this.state.wikiTreeId,\n source: 'wikitree',\n standalone,\n }),\n });\n }\n }\n\n /** Called when the URL input is typed into. */\n private handleUrlChange(value: string) {\n this.setState(\n Object.assign({}, this.state, {\n url: value,\n }),\n );\n }\n\n /** Called when the URL input is typed into. */\n private handleWikiTreeIdChange(value: string) {\n this.setState(\n Object.assign({}, this.state, {\n wikiTreeId: value,\n }),\n );\n }\n\n /** On search input change. */\n private handleSearch(input: string | undefined) {\n if (!input) {\n return;\n }\n const results = this.searchIndex!.search(input).map((result) =>\n displaySearchResult(result, this.context.intl),\n );\n this.setState(Object.assign({}, this.state, {searchResults: results}));\n }\n\n /** On search result selected. */\n private handleResultSelect(id: string) {\n analyticsEvent('search_result_selected');\n this.props.eventHandlers.onSelection({id, generation: 0});\n this.searchRef!.setValue('');\n }\n\n private initializeSearchIndex() {\n if (this.props.data) {\n this.searchIndex = buildSearchIndex(this.props.data);\n }\n }\n\n changeView(view: string) {\n const location = this.props.location;\n const search = queryString.parse(location.search);\n if (search.view !== view) {\n search.view = view;\n location.search = queryString.stringify(search);\n this.props.history.push(location);\n }\n }\n\n /**\n * Redirect to the WikiTree Apps login page with a return URL pointing to\n * Topola Viewer hosted on apps.wikitree.com.\n */\n private wikiTreeLogin() {\n const wikiTreeTopolaUrl =\n 'https://apps.wikitree.com/apps/wiech13/topola-viewer';\n // Append '&' because the login page appends '?authcode=...' to this URL.\n // TODO: remove ?authcode if it is in the current URL.\n const returnUrl = `${wikiTreeTopolaUrl}${window.location.hash}&`;\n this.wikiTreeReturnUrlRef.current!.value = returnUrl;\n this.wikiTreeLoginFormRef.current!.submit();\n }\n\n private checkWikiTreeLoginState() {\n const wikiTreeLoginState =\n Cookies.get('wikidb_wtb_UserID') !== undefined\n ? WikiTreeLoginState.LOGGED_IN\n : WikiTreeLoginState.NOT_LOGGED_IN;\n if (this.state.wikiTreeLoginState !== wikiTreeLoginState) {\n const wikiTreeLoginUsername = Cookies.get('wikidb_wtb_UserName');\n this.setState(\n Object.assign({}, this.state, {\n wikiTreeLoginState,\n wikiTreeLoginUsername,\n }),\n );\n }\n }\n\n async componentDidMount() {\n this.checkWikiTreeLoginState();\n this.initializeSearchIndex();\n }\n\n componentDidUpdate(prevProps: Props) {\n this.checkWikiTreeLoginState();\n if (prevProps.data !== this.props.data) {\n this.initializeSearchIndex();\n }\n }\n\n private loadFromUrlModal() {\n return (\n this.handleClose()}\n centered={false}\n >\n \n \n \n ),\n }}\n />\n \n \n \n \n this.handleClose()}>\n \n \n this.handleLoad()}>\n \n \n \n \n );\n }\n\n private enterWikiTreeId(event: React.MouseEvent, id: string) {\n event.preventDefault(); // Do not follow link in href.\n ((this.wikiTreeIdInputRef.current as unknown) as {\n inputRef: HTMLInputElement;\n }).inputRef.value = id;\n this.handleWikiTreeIdChange(id);\n this.wikiTreeIdInputRef.current!.focus();\n }\n\n private wikiTreeIdModal() {\n return (\n this.handleClose()}\n centered={false}\n >\n \n \n txt}\n />\n \n \n \n \n \n this.handleClose()}>\n \n \n this.handleSelectWikiTreeId()}>\n \n \n \n \n );\n }\n\n private search() {\n return (\n , data: SearchProps) =>\n this.handleSearch(data.value),\n 200,\n )}\n onResultSelect={(_, data) => this.handleResultSelect(data.result.id)}\n results={this.state.searchResults}\n noResultsMessage={this.context.intl.formatMessage({\n id: 'menu.search.no_results',\n defaultMessage: 'No results found',\n })}\n placeholder={this.context.intl.formatMessage({\n id: 'menu.search.placeholder',\n defaultMessage: 'Search for people',\n })}\n selectFirstResult={true}\n ref={(ref) =>\n (this.searchRef = (ref as unknown) as {\n setValue(value: string): void;\n })\n }\n id=\"search\"\n />\n );\n }\n\n private chartMenus(screenSize: ScreenSize) {\n if (!this.props.showingChart) {\n return null;\n }\n const chartTypeItems = (\n <>\n this.changeView('hourglass')}>\n \n \n \n {this.props.allowAllRelativesChart ? (\n this.changeView('relatives')}>\n \n \n \n ) : null}\n this.changeView('fancy')}>\n \n \n \n >\n );\n switch (screenSize) {\n case ScreenSize.LARGE:\n return (\n <>\n this.props.eventHandlers.onPrint()}>\n \n \n \n\n \n \n \n \n }\n className=\"item\"\n >\n \n this.props.eventHandlers.onDownloadPdf()}\n >\n \n \n this.props.eventHandlers.onDownloadPng()}\n >\n \n \n this.props.eventHandlers.onDownloadSvg()}\n >\n \n \n \n \n\n \n \n \n \n }\n className=\"item\"\n >\n {chartTypeItems} \n \n {this.search()}\n >\n );\n\n case ScreenSize.SMALL:\n return (\n <>\n this.props.eventHandlers.onPrint()}>\n \n \n \n\n \n\n this.props.eventHandlers.onDownloadPdf()}\n >\n \n \n \n this.props.eventHandlers.onDownloadPng()}\n >\n \n \n \n this.props.eventHandlers.onDownloadSvg()}\n >\n \n \n \n\n \n {chartTypeItems}\n \n >\n );\n }\n }\n\n private title() {\n return (\n \n Topola Genealogy \n \n );\n }\n\n private fileMenus(screenSize: ScreenSize) {\n const loadWikiTreeItem = (\n <>\n \n \n >\n );\n\n // In standalone WikiTree mode, show only the \"Select WikiTree ID\" menu.\n if (!this.props.standalone && this.props.showWikiTreeMenus) {\n switch (screenSize) {\n case ScreenSize.LARGE:\n return (\n <>\n this.openWikiTreeIdDialog()}>\n {loadWikiTreeItem}\n \n {this.wikiTreeIdModal()}\n >\n );\n case ScreenSize.SMALL:\n return (\n <>\n this.openWikiTreeIdDialog()}>\n {loadWikiTreeItem}\n \n \n {this.wikiTreeIdModal()}\n >\n );\n }\n }\n\n // Don't show \"open\" menus in non-standalone mode.\n if (!this.props.standalone) {\n return null;\n }\n\n const openFileItem = (\n <>\n \n \n >\n );\n const loadUrlItem = (\n <>\n \n \n >\n );\n const commonElements = (\n <>\n {this.loadFromUrlModal()}\n {this.wikiTreeIdModal()}\n this.handleUpload(e)}\n />\n >\n );\n switch (screenSize) {\n case ScreenSize.LARGE:\n // Show dropdown if chart is shown, otherwise show individual menu\n // items.\n const menus = this.props.showingChart ? (\n \n \n \n \n }\n className=\"item\"\n >\n \n \n {openFileItem}\n \n this.openLoadUrlDialog()}>\n {loadUrlItem}\n \n this.openWikiTreeIdDialog()}>\n {loadWikiTreeItem}\n \n \n \n ) : (\n <>\n \n {openFileItem} \n \n this.openLoadUrlDialog()}>\n {loadUrlItem}\n \n this.openWikiTreeIdDialog()}>\n {loadWikiTreeItem}\n \n >\n );\n return (\n <>\n {menus}\n {commonElements}\n >\n );\n\n case ScreenSize.SMALL:\n return (\n <>\n \n {openFileItem}\n \n this.openLoadUrlDialog()}>\n {loadUrlItem}\n \n this.openWikiTreeIdDialog()}>\n {loadWikiTreeItem}\n \n \n {commonElements}\n >\n );\n }\n }\n\n private wikiTreeLoginMenu(screenSize: ScreenSize) {\n if (!this.props.showWikiTreeMenus) {\n return null;\n }\n switch (this.state.wikiTreeLoginState) {\n case WikiTreeLoginState.NOT_LOGGED_IN:\n const loginForm = (\n \n );\n switch (screenSize) {\n case ScreenSize.LARGE:\n return (\n this.wikiTreeLogin()}>\n \n \n {loginForm}\n \n );\n\n case ScreenSize.SMALL:\n return (\n <>\n this.wikiTreeLogin()}>\n \n \n {loginForm}\n \n \n >\n );\n }\n break;\n\n case WikiTreeLoginState.LOGGED_IN:\n const tooltip = this.state.wikiTreeLoginUsername\n ? this.context.intl.formatMessage(\n {\n id: 'menu.wikitree_popup_username',\n defaultMessage: 'Logged in to WikiTree as {username}',\n },\n {username: this.state.wikiTreeLoginUsername},\n )\n : this.context.intl.formatMessage({\n id: 'menu.wikitree_popup',\n defaultMessage: 'Logged in to WikiTree',\n });\n switch (screenSize) {\n case ScreenSize.LARGE:\n return (\n \n \n \n \n );\n\n case ScreenSize.SMALL:\n return (\n <>\n \n \n \n \n \n >\n );\n\n default:\n return null;\n }\n }\n }\n\n private mobileMenus() {\n return (\n <>\n \n \n \n }\n className=\"item\"\n icon={null}\n >\n \n {this.fileMenus(ScreenSize.SMALL)}\n {this.chartMenus(ScreenSize.SMALL)}\n {this.wikiTreeLoginMenu(ScreenSize.SMALL)}\n\n \n \n \n \n \n {this.props.standalone ? (\n {this.title()}\n ) : (\n this.title()\n )}\n >\n );\n }\n\n private desktopMenus() {\n return (\n <>\n {this.props.standalone ? {this.title()} : null}\n {this.fileMenus(ScreenSize.LARGE)}\n {this.chartMenus(ScreenSize.LARGE)}\n \n {this.wikiTreeLoginMenu(ScreenSize.LARGE)}\n \n \n \n \n >\n );\n }\n\n render() {\n return (\n <>\n \n {this.desktopMenus()}\n \n \n {this.mobileMenus()}\n \n >\n );\n }\n}\n","import * as H from 'history';\nimport * as queryString from 'query-string';\nimport * as React from 'react';\nimport {analyticsEvent} from './analytics';\nimport {Chart, ChartType} from './chart';\nimport {Details} from './details';\nimport {FormattedMessage} from 'react-intl';\nimport {getSelection, loadFromUrl, loadGedcom} from './load_data';\nimport {getSoftware, TopolaData} from './gedcom_util';\nimport {IndiInfo} from 'topola';\nimport {intlShape} from 'react-intl';\nimport {Intro} from './intro';\nimport {Loader, Message, Portal, Responsive} from 'semantic-ui-react';\nimport {loadWikiTree} from './wikitree';\nimport {Redirect, Route, RouteComponentProps, Switch} from 'react-router-dom';\nimport {TopBar} from './top_bar';\n\n/** Shows an error message in the middle of the screen. */\nfunction ErrorMessage(props: {message?: string}) {\n return (\n \n \n \n \n {props.message}
\n \n );\n}\n\ninterface ErrorPopupProps {\n message?: string;\n open: boolean;\n onDismiss: () => void;\n}\n\n/**\n * Shows a dismissable error message in the bottom left corner of the screen.\n */\nfunction ErrorPopup(props: ErrorPopupProps) {\n return (\n \n \n \n \n \n {props.message}
\n \n \n );\n}\n\n/**\n * Message types used in embedded mode.\n * When the parent is ready to receive messages, it sends PARENT_READY.\n * When the child (this app) is ready to receive messages, it sends READY.\n * When the child receives PARENT_READY, it sends READY.\n * When the parent receives READY, it sends data in a GEDCOM message.\n */\nenum EmbeddedMessageType {\n GEDCOM = 'gedcom',\n READY = 'ready',\n PARENT_READY = 'parent_ready',\n}\n\n/** Message sent to parent or received from parent in embedded mode. */\ninterface EmbeddedMessage {\n message: EmbeddedMessageType;\n}\n\ninterface GedcomMessage extends EmbeddedMessage {\n message: EmbeddedMessageType.GEDCOM;\n gedcom?: string;\n}\n\n/** Interface encapsulating functions specific for a data source. */\ninterface DataSource {\n /**\n * Returns true if the application is now loading a completely new data set\n * and the existing one should be wiped.\n */\n isNewData(args: Arguments, state: State): boolean;\n /** Loads data from the data source. */\n loadData(args: Arguments): Promise;\n}\n\n/** Files opened from the local computer. */\nclass UploadedDataSource implements DataSource {\n isNewData(args: Arguments, state: State): boolean {\n return (\n args.hash !== state.hash ||\n !!(args.gedcom && !state.loading && !state.data)\n );\n }\n\n async loadData(args: Arguments): Promise {\n try {\n const data = await loadGedcom(args.hash!, args.gedcom, args.images);\n const software = getSoftware(data.gedcom.head);\n analyticsEvent('upload_file_loaded', {\n event_label: software,\n event_value: (args.images && args.images.size) || 0,\n });\n return data;\n } catch (error) {\n analyticsEvent('upload_file_error');\n throw error;\n }\n }\n}\n\n/** GEDCOM file loaded by pointing to a URL. */\nclass GedcomUrlDataSource implements DataSource {\n isNewData(args: Arguments, state: State): boolean {\n return args.url !== state.url;\n }\n\n async loadData(args: Arguments): Promise {\n try {\n const data = await loadFromUrl(args.url!, args.handleCors);\n const software = getSoftware(data.gedcom.head);\n analyticsEvent('upload_file_loaded', {event_label: software});\n return data;\n } catch (error) {\n analyticsEvent('url_file_error');\n throw error;\n }\n }\n}\n\n/** Loading data from the WikiTree API. */\nclass WikiTreeDataSource implements DataSource {\n isNewData(args: Arguments, state: State): boolean {\n if (state.selection && state.selection.id === args.indi) {\n // Selection unchanged -> don't reload.\n return false;\n }\n if (\n state.data &&\n state.data.chartData.indis.some((indi) => indi.id === args.indi)\n ) {\n // New selection exists in current view -> animate instead of reloading.\n return false;\n }\n return true;\n }\n\n async loadData(args: Arguments): Promise {\n try {\n const data = await loadWikiTree(args.indi!, args.authcode);\n analyticsEvent('wikitree_loaded');\n return data;\n } catch (error) {\n analyticsEvent('wikitree_error');\n throw error;\n }\n }\n}\n\n/** Supported data sources. */\nenum DataSourceEnum {\n UPLOADED,\n GEDCOM_URL,\n WIKITREE,\n}\n\n/** Mapping from data source identifier to data source handler functions. */\nconst DATA_SOURCES = new Map([\n [DataSourceEnum.UPLOADED, new UploadedDataSource()],\n [DataSourceEnum.GEDCOM_URL, new GedcomUrlDataSource()],\n [DataSourceEnum.WIKITREE, new WikiTreeDataSource()],\n]);\n\n/** Arguments passed to the application, primarily through URL parameters. */\ninterface Arguments {\n showSidePanel: boolean;\n embedded: boolean;\n url?: string;\n indi?: string;\n generation?: number;\n hash?: string;\n handleCors: boolean;\n standalone: boolean;\n source?: DataSourceEnum;\n authcode?: string;\n chartType: ChartType;\n gedcom?: string;\n images?: Map;\n}\n\n/**\n * Retrieve arguments passed into the application through the URL and uploaded\n * data.\n */\nfunction getArguments(location: H.Location): Arguments {\n const search = queryString.parse(location.search);\n const getParam = (name: string) => {\n const value = search[name];\n return typeof value === 'string' ? value : undefined;\n };\n\n const parsedGen = Number(getParam('gen'));\n const view = getParam('view');\n const chartTypes = new Map([\n ['relatives', ChartType.Relatives],\n ['fancy', ChartType.Fancy],\n ]);\n const hash = getParam('file');\n const url = getParam('url');\n const source =\n getParam('source') === 'wikitree'\n ? DataSourceEnum.WIKITREE\n : hash\n ? DataSourceEnum.UPLOADED\n : url\n ? DataSourceEnum.GEDCOM_URL\n : undefined;\n return {\n showSidePanel: getParam('sidePanel') !== 'false', // True by default.\n embedded: getParam('embedded') === 'true', // False by default.\n url,\n indi: getParam('indi'),\n generation: !isNaN(parsedGen) ? parsedGen : undefined,\n hash,\n handleCors: getParam('handleCors') !== 'false', // True by default.\n standalone: getParam('standalone') !== 'false', // True by default.\n source,\n authcode: getParam('?authcode'),\n\n // Hourglass is the default view.\n chartType: chartTypes.get(view) || ChartType.Hourglass,\n\n gedcom: location.state && location.state.data,\n images: location.state && location.state.images,\n };\n}\n\n/** Returs true if the changes object has values that are different than those in state. */\nfunction hasUpdatedValues(state: T, changes: Partial | undefined) {\n if (!changes) {\n return false;\n }\n return Object.entries(changes).some(\n ([key, value]) => value !== undefined && state[key] !== value,\n );\n}\n\ninterface State {\n /** Loaded data. */\n data?: TopolaData;\n /** Selected individual. */\n selection?: IndiInfo;\n /** Hash of the GEDCOM contents. */\n hash?: string;\n /** Error to display. */\n error?: string;\n /** True if data is currently being loaded. */\n loading: boolean;\n /** URL of the data that is loaded or is being loaded. */\n url?: string;\n /** Whether the side panel is shown. */\n showSidePanel?: boolean;\n /** Whether the app is in embedded mode, i.e. embedded in an iframe. */\n embedded: boolean;\n /** Whether the app is in standalone mode, i.e. showing 'open file' menus. */\n standalone: boolean;\n /** Type of displayed chart. */\n chartType: ChartType;\n /** Whether to show the error popup. */\n showErrorPopup: boolean;\n /** Source of the data. */\n source?: DataSourceEnum;\n loadingMore?: boolean;\n}\n\nexport class App extends React.Component {\n state: State = {\n loading: false,\n embedded: false,\n standalone: true,\n chartType: ChartType.Hourglass,\n showErrorPopup: false,\n };\n chartRef: Chart | null = null;\n\n /** Make intl appear in this.context. */\n static contextTypes = {\n intl: intlShape,\n };\n\n /** Sets the state with a new individual selection and chart type. */\n private updateDisplay(\n selection: IndiInfo,\n otherStateChanges?: Partial,\n ) {\n if (\n !this.state.selection ||\n this.state.selection.id !== selection.id ||\n this.state.selection!.generation !== selection.generation ||\n hasUpdatedValues(this.state, otherStateChanges)\n ) {\n this.setState(\n Object.assign({}, this.state, {selection}, otherStateChanges),\n );\n }\n }\n\n /** Sets error message after data load failure. */\n private setError(error: string) {\n this.setState(\n Object.assign({}, this.state, {\n error: error,\n loading: false,\n }),\n );\n }\n\n private async onMessage(message: EmbeddedMessage) {\n if (message.message === EmbeddedMessageType.PARENT_READY) {\n // Parent didn't receive the first 'ready' message, so we need to send it again.\n window.parent.postMessage({message: EmbeddedMessageType.READY}, '*');\n } else if (message.message === EmbeddedMessageType.GEDCOM) {\n const gedcom = (message as GedcomMessage).gedcom;\n if (!gedcom) {\n return;\n }\n try {\n const data = await loadGedcom('', gedcom);\n const software = getSoftware(data.gedcom.head);\n analyticsEvent('embedded_file_loaded', {\n event_label: software,\n });\n // Set state with data.\n this.setState(\n Object.assign({}, this.state, {\n data,\n selection: getSelection(data.chartData),\n error: undefined,\n loading: false,\n }),\n );\n } catch (error) {\n analyticsEvent('embedded_file_error');\n this.setError(error.message);\n }\n }\n }\n\n componentDidMount() {\n this.componentDidUpdate();\n }\n\n async componentDidUpdate() {\n if (this.props.location.pathname !== '/view') {\n return;\n }\n\n const args = getArguments(this.props.location);\n\n if (args.embedded && !this.state.embedded) {\n this.setState(\n Object.assign({}, this.state, {\n embedded: true,\n standalone: false,\n showSidePanel: args.showSidePanel,\n }),\n );\n // Notify the parent window that we are ready.\n window.parent.postMessage('ready', '*');\n window.addEventListener('message', (data) => this.onMessage(data.data));\n }\n if (args.embedded) {\n // If the app is embedded, do not run the normal loading code.\n return;\n }\n\n const dataSource = DATA_SOURCES.get(args.source!);\n\n if (!dataSource) {\n this.props.history.replace({pathname: '/'});\n } else if (\n (!this.state.loading && !this.state.data && !this.state.error) ||\n args.source !== this.state.source ||\n dataSource.isNewData(args, this.state)\n ) {\n // Set loading state.\n this.setState(\n Object.assign({}, this.state, {\n data: undefined,\n selection: {id: args.indi},\n hash: args.hash,\n error: undefined,\n loading: true,\n url: args.url,\n standalone: args.standalone,\n chartType: args.chartType,\n source: args.source,\n }),\n );\n try {\n const data = await dataSource.loadData(args);\n\n // Set state with data.\n this.setState(\n Object.assign({}, this.state, {\n data,\n hash: args.hash,\n selection: getSelection(data.chartData, args.indi, args.generation),\n error: undefined,\n loading: false,\n url: args.url,\n showSidePanel: args.showSidePanel,\n standalone: args.standalone,\n chartType: args.chartType,\n source: args.source,\n }),\n );\n } catch (error) {\n this.setError(error.message);\n }\n } else if (this.state.data && this.state.selection) {\n // Update selection if it has changed in the URL.\n const selection = getSelection(\n this.state.data.chartData,\n args.indi,\n args.generation,\n );\n const loadMoreFromWikitree =\n args.source === DataSourceEnum.WIKITREE &&\n (!this.state.selection || this.state.selection.id !== selection.id);\n this.updateDisplay(selection, {\n chartType: args.chartType,\n loadingMore: loadMoreFromWikitree || undefined,\n });\n if (loadMoreFromWikitree) {\n const data = await loadWikiTree(args.indi!);\n this.setState(\n Object.assign({}, this.state, {\n data,\n hash: args.hash,\n selection: getSelection(data.chartData, args.indi, args.generation),\n error: undefined,\n loading: false,\n url: args.url,\n showSidePanel: args.showSidePanel,\n standalone: args.standalone,\n chartType: args.chartType,\n source: args.source,\n loadingMore: false,\n }),\n );\n }\n }\n }\n\n /**\n * Called when the user clicks an individual box in the chart.\n * Updates the browser URL.\n */\n private onSelection = (selection: IndiInfo) => {\n analyticsEvent('selection_changed');\n if (this.state.embedded) {\n // In embedded mode the URL doesn't change.\n this.updateDisplay(selection);\n return;\n }\n const location = this.props.location;\n const search = queryString.parse(location.search);\n search.indi = selection.id;\n search.gen = String(selection.generation);\n location.search = queryString.stringify(search);\n this.props.history.push(location);\n };\n\n private onPrint = () => {\n analyticsEvent('print');\n this.chartRef && this.chartRef.print();\n };\n\n private showErrorPopup(message: string) {\n this.setState(\n Object.assign({}, this.state, {\n showErrorPopup: true,\n error: message,\n }),\n );\n }\n\n private onDownloadPdf = async () => {\n analyticsEvent('download_pdf');\n try {\n this.chartRef && (await this.chartRef.downloadPdf());\n } catch (e) {\n this.showErrorPopup(\n this.context.intl.formatMessage({\n id: 'error.failed_pdf',\n defaultMessage:\n 'Failed to generate PDF file.' +\n ' Please try with a smaller diagram or download an SVG file.',\n }),\n );\n }\n };\n\n private onDownloadPng = async () => {\n analyticsEvent('download_png');\n try {\n this.chartRef && (await this.chartRef.downloadPng());\n } catch (e) {\n this.showErrorPopup(\n this.context.intl.formatMessage({\n id: 'error.failed_png',\n defaultMessage:\n 'Failed to generate PNG file.' +\n ' Please try with a smaller diagram or download an SVG file.',\n }),\n );\n }\n };\n\n private onDownloadSvg = () => {\n analyticsEvent('download_svg');\n this.chartRef && this.chartRef.downloadSvg();\n };\n\n onDismissErrorPopup = () => {\n this.setState(\n Object.assign({}, this.state, {\n showErrorPopup: false,\n }),\n );\n };\n\n private renderMainArea = () => {\n if (this.state.data && this.state.selection) {\n return (\n \n \n {this.state.loadingMore ? (\n \n ) : null}\n (this.chartRef = ref)}\n />\n {this.state.showSidePanel ? (\n \n \n \n ) : null}\n
\n );\n }\n if (this.state.error) {\n return ;\n }\n return ;\n };\n\n render() {\n return (\n <>\n (\n \n )}\n />\n \n \n \n \n \n >\n );\n }\n}\n","import * as locale_en from 'react-intl/locale-data/en';\nimport * as locale_pl from 'react-intl/locale-data/pl';\nimport * as React from 'react';\nimport * as ReactDOM from 'react-dom';\nimport messages_pl from './translations/pl.json';\nimport {addLocaleData} from 'react-intl';\nimport {App} from './app';\nimport {detect} from 'detect-browser';\nimport {HashRouter as Router, Route} from 'react-router-dom';\nimport {IntlProvider} from 'react-intl';\nimport './index.css';\nimport 'semantic-ui-css/semantic.min.css';\nimport 'canvas-toBlob';\n\naddLocaleData([...locale_en, ...locale_pl]);\n\nconst messages = {\n pl: messages_pl,\n};\nconst language = navigator.language && navigator.language.split(/[-_]/)[0];\n\nconst browser = detect();\n\nif (browser && browser.name === 'ie') {\n ReactDOM.render(\n \n Topola Genealogy Viewer does not support Internet Explorer. Please try a\n different browser.\n
,\n document.querySelector('#root'),\n );\n} else {\n ReactDOM.render(\n \n \n \n \n ,\n document.querySelector('#root'),\n );\n}\n"],"sourceRoot":""}
\ No newline at end of file
+{"version":3,"sources":["topola.jpg","analytics.ts","chart.tsx","gedcom_util.ts","date_util.ts","details.tsx","load_data.ts","intro.tsx","wikitree.ts","search_index.ts","search_util.tsx","top_bar.tsx","app.tsx","index.tsx"],"names":["module","exports","__webpack_require__","p","analyticsEvent","action","data","window","gtag","ChartType","scrolled","parent","d3","node","x","scrollLeft","clientWidth","y","scrollTop","clientHeight","scale","k","call","translateTo","loadAsDataUrl","blob","reader","FileReader","readAsDataURL","Promise","resolve","reject","onload","e","target","result","inlineImage","_callee6","image","href","response","dataUrl","regenerator_default","a","wrap","_context6","prev","next","baseVal","abrupt","fetch","sent","t0","console","warn","stop","inlineImages","_callee7","svg","images","_context7","Array","from","getElementsByTagName","all","map","loadImage","Image","src","URL","createObjectURL","addEventListener","drawOnCanvas","canvas","document","createElement","width","height","ctx","getContext","oldFill","fillStyle","fillRect","drawImage","canvasToBlob","type","toBlob","Chart","_React$PureComponent","_getPrototypeOf2","_this","Object","classCallCheck","this","_len","arguments","length","args","_key","possibleConstructorReturn","getPrototypeOf","apply","concat","chart","animating","rerenderRequired","zoomBehavior","inherits","createClass","key","value","props","chartType","Hourglass","HourglassChart","Relatives","RelativesChart","Fancy","FancyChart","CircleRenderer","DetailedRenderer","factor","scaleBy","_this2","undefined","initialRender","innerHTML","createChart","json","getChartType","renderer","getRendererType","svgSelector","indiCallback","info","onSelection","animate","updateSvgSize","locale","context","intl","setData","chartInfo","render","startIndi","selection","id","baseGeneration","generation","zoomOutFactor","size","extent","scaleExtent","translateExtent","on","transform","offsetX","offsetY","attr","zoomed","dx","origin","dy","svgTransition","transition","delay","duration","tween","i","t","animationPromise","then","renderChart","prevProps","_this3","react","Responsive","minWidth","className","onClick","zoom","getElementById","cloneNode","removeAttribute","setAttribute","String","Number","getAttribute","querySelector","XMLSerializer","serializeToString","getStrippedSvg","_getSvgContentsWithInlinedImages","asyncToGenerator","mark","_callee","_context","_this4","printWindow","style","position","top","left","contentDocument","open","write","getSvgContents","close","setTimeout","contentWindow","focus","print","parentNode","removeChild","body","appendChild","_downloadSvg","_callee2","contents","_context2","getSvgContentsWithInlinedImages","Blob","saveAs","_drawOnCanvas2","_callee3","_context3","t1","_downloadPng","_callee4","_context4","_downloadPdf","_callee5","doc","_context5","jsPDF","orientation","unit","format","addImage","save","React","contextTypes","intlShape","pointerToId","pointer","substring","idToIndiMap","Map","indis","forEach","indi","set","idToFamMap","fams","fam","prepareGedcom","entries","head","find","entry","tag","other","strcmp","b","compareDates","event1","event2","date1","date","dateRange","date2","year","month","day","sortChildren","gedcom","comparator","indiMap","indiId1","indiId2","indi1","get","indi2","birth","birthDatesComparator","newFams","children","newChildren","sort","assign","sortFamilyChildren","sortSpouses","famMap","famId1","famId2","fam1","fam2","marriage","marriageDatesComparator","newIndis","sortIndiSpouses","normalizeGedcom","IMAGE_EXTENSIONS","filterImage","newImages","fileName","url","match","has","push","title","startsWith","lowerName","toLowerCase","some","ext","endsWith","isImageFile","filterImages","getSoftware","sour","tree","name","DATE_QUALIFIERS","formatDate","hasDay","hasMonth","hasYear","text","dateObject","Date","qualifier","formatOptions","formatMessage","defaultMessage","Intl","DateTimeFormat","join","formatDateOrRange","dateOrRange","fromDate","toDate","to","translatedFromDate","translatedToDate","formatDateRage","EVENT_TAGS","EXCLUDED_TAGS","TAG_DESCRIPTIONS","translateTag","index_es","joinLines","lines","line","index","Linkify_default","properties","getData","subentry","last","eventDetails","gedcomDate","getDate","translateDate","place","toConsumableArray","filter","note","noteDetails","nameDetails","split","getDetails","tags","detailsFunction","flatMap","element","hasData","getOtherDetails","includes","dataDetails","Details","_React$Component","entriesWithData","dereferenced","dereference","getSelection","prepareData","cacheId","parseGedcom","gedcomEntriesToJson","Error","chartData","convertGedcom","serializedData","JSON","stringify","sessionStorage","setItem","loadFromUrl","_x","_x2","_loadFromUrl","handleCors","cachedData","urlToFetch","getItem","parse","status","statusText","loadGedcom","_x3","_x4","_x5","_loadGedcom","hash","GedcomLink","Link","pathname","search","queryString","Intro","values","link","process","slice","Card","as","Content","Header","Grid","Row","Column","Image_Image","logo","alt","maxWidth","centered","getSessionStorageItem","setSessionStorageItem","wikiTreeGet","request","requestData","apiUrl","responseBody","FormData","append","method","getAncestors","cacheKey","fields","ancestors","getRelatives","keys","keysToFetch","fetchedResults","getChildren","getSpouses","items","person","Name","clientLogin","_x7","_clientLogin","authcode","loadWikiTree","_x8","_x9","_loadWikiTree","everyone","firstPerson","spouseKeys","ancestorKeys","ancestorDetails","descendantGenerationLimit","toFetch","people","allSpouses","families","spouses","idToName","converted","location","hostname","Cookies","clear","Spouses","s","personId","flat","Children","c","Id","Mother","Father","famId","getFamilyId","getSet","add","wife","husband","Set","convertPerson","spouse","familySpouses","Gender","_ref","_ref2","slicedToArray","husb","child","marriage_date","marriage_location","parsedDate","parseDate","buildGedcom","spouse1","spouse2","FirstName","firstName","LastNameAtBirth","lastName","famc","sex","BirthDate","BirthLocation","DataStatus","DeathDate","DeathLocation","death","PhotoData","path","dataStatus","matchedDate","gedcomIndis","escapedId","replace","level","newSet","normalize","input","toLocaleLowerCase","compare","score","naturalSort","ref","LunrSearchIndex","self","lunr","field","boost","spouseLastName","husbId","getHusbandLastName","normalizedName","normalizedSpouseLastName","query","getNameLine","trim","getDescriptionLine","birthDate","deathDate","WikiTreeLoginState","ScreenSize","WIKITREE_LOGO_URL","loadFileAsText","file","evt","readAsText","isImageFileName","lower","EmbeddedMessageType","TopBar","state","loadUrlDialogOpen","wikiTreeIdDialogOpen","searchResults","wikiTreeLoginState","UNKNOWN","urlInputRef","wikiTreeIdInputRef","wikiTreeLoginFormRef","wikiTreeReturnUrlRef","searchRef","searchIndex","_handleUpload","event","files","filesArray","gedcomFile","imageMap","imageFileNames","event_value","md5","history","setState","current","wikiTreeId","standalone","source","results","description","displaySearchResult","eventHandlers","setValue","initialize","buildSearchIndex","view","returnUrl","submit","LOGGED_IN","NOT_LOGGED_IN","wikiTreeLoginUsername","_componentDidMount","checkWikiTreeLoginState","initializeSearchIndex","_this5","Modal","onClose","handleClose","Icon","txt","Form","onSubmit","handleLoad","Input","placeholder","fluid","onChange","handleUrlChange","Actions","Button","secondary","primary","preventDefault","inputRef","handleWikiTreeIdChange","_this6","handleSelectWikiTreeId","wikiTreeLink","rel","example1","enterWikiTreeId","example2","_this7","Search","onSearchChange","debounce","_","handleSearch","onResultSelect","handleResultSelect","noResultsMessage","selectFirstResult","screenSize","_this8","showingChart","chartTypeItems","Dropdown","Item","changeView","allowAllRelativesChart","LARGE","Menu","onPrint","trigger","onDownloadPdf","onDownloadPng","onDownloadSvg","SMALL","Divider","_this9","loadWikiTreeItem","showWikiTreeMenus","openWikiTreeIdDialog","wikiTreeIdModal","openFileItem","loadUrlItem","commonElements","loadFromUrlModal","accept","multiple","handleUpload","menus","htmlFor","openLoadUrlDialog","_this10","loginForm","display","wikiTreeLogin","tooltip","username","icon","fileMenus","chartMenus","wikiTreeLoginMenu","attached","inverted","color","desktopMenus","mobileMenus","ErrorMessage","Message","negative","message","ErrorPopup","Portal","onDismiss","DataSourceEnum","UploadedDataSource","loading","event_label","GedcomUrlDataSource","WikiTreeDataSource","DATA_SOURCES","UPLOADED","GEDCOM_URL","WIKITREE","getArguments","getParam","parsedGen","chartTypes","showSidePanel","embedded","isNaN","App","showErrorPopup","chartRef","updateDisplay","gen","downloadPdf","downloadPng","downloadSvg","onDismissErrorPopup","renderMainArea","error","loadingMore","Loader","active","chart_Chart","details_Details","otherStateChanges","changes","_onMessage","PARENT_READY","postMessage","READY","GEDCOM","setError","componentDidUpdate","_componentDidUpdate","dataSource","loadMoreFromWikitree","_data","onMessage","isNewData","loadData","Route","top_bar_TopBar","Switch","exact","component","Redirect","addLocaleData","locale_en","locale_pl","messages","pl","messages_pl","language","navigator","browser","detect","ReactDOM","HashRouter"],"mappings":"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","file":"static/js/main.26dcf0f4.chunk.js","sourcesContent":["module.exports = __webpack_public_path__ + \"static/media/topola.060eef13.jpg\";","/** Sends an event to Google Analytics. */\nexport function analyticsEvent(action: string, data?: any) {\n (window as any).gtag('event', action, data);\n}\n","import * as d3 from 'd3';\nimport * as React from 'react';\nimport jsPDF from 'jspdf';\nimport {intlShape} from 'react-intl';\nimport {saveAs} from 'file-saver';\nimport {\n JsonGedcomData,\n ChartHandle,\n IndiInfo,\n createChart,\n DetailedRenderer,\n HourglassChart,\n RelativesChart,\n FancyChart,\n CircleRenderer,\n} from 'topola';\nimport {Responsive} from 'semantic-ui-react';\n\n/** How much to zoom when using the +/- buttons. */\nconst ZOOM_FACTOR = 1.3;\n\n/**\n * Called when the view is dragged with the mouse.\n *\n * @param size the size of the chart\n */\nfunction zoomed(size: [number, number]) {\n const parent = d3.select('#svgContainer').node() as Element;\n\n const scale = d3.event.transform.k;\n const offsetX = d3.max([0, (parent.clientWidth - size[0] * scale) / 2]);\n const offsetY = d3.max([0, (parent.clientHeight - size[1] * scale) / 2]);\n d3.select('#chartSvg')\n .attr('width', size[0] * scale)\n .attr('height', size[1] * scale)\n .attr('transform', `translate(${offsetX}, ${offsetY})`);\n d3.select('#chart').attr('transform', `scale(${scale})`);\n\n parent.scrollLeft = -d3.event.transform.x;\n parent.scrollTop = -d3.event.transform.y;\n}\n\n/** Called when the scrollbars are used. */\nfunction scrolled() {\n const parent = d3.select('#svgContainer').node() as Element;\n const x = parent.scrollLeft + parent.clientWidth / 2;\n const y = parent.scrollTop + parent.clientHeight / 2;\n const scale = d3.zoomTransform(parent).k;\n d3.select(parent).call(d3.zoom().translateTo, x / scale, y / scale);\n}\n\n/** Loads blob as data URL. */\nfunction loadAsDataUrl(blob: Blob): Promise {\n const reader = new FileReader();\n reader.readAsDataURL(blob);\n return new Promise((resolve, reject) => {\n reader.onload = (e) => resolve((e.target as FileReader).result as string);\n });\n}\n\nasync function inlineImage(image: SVGImageElement) {\n const href = image.href.baseVal;\n if (!href) {\n return;\n }\n try {\n const response = await fetch(href);\n const blob = await response.blob();\n const dataUrl = await loadAsDataUrl(blob);\n image.href.baseVal = dataUrl;\n } catch (e) {\n console.warn('Failed to load image:', e);\n }\n}\n\n/**\n * Fetches all images in the SVG and replaces them with inlined images as data\n * URLs. Images are replaced in place. The replacement is done, the returned\n * promise is resolved.\n */\nasync function inlineImages(svg: Element): Promise {\n const images = Array.from(svg.getElementsByTagName('image'));\n await Promise.all(images.map(inlineImage));\n}\n\n/** Loads a blob into an image object. */\nfunction loadImage(blob: Blob): Promise {\n const image = new Image();\n image.src = URL.createObjectURL(blob);\n return new Promise((resolve, reject) => {\n image.addEventListener('load', () => resolve(image));\n });\n}\n\n/** Draw image on a new canvas and return the canvas. */\nfunction drawOnCanvas(image: HTMLImageElement) {\n const canvas = document.createElement('canvas');\n // Scale image for better quality.\n canvas.width = image.width * 2;\n canvas.height = image.height * 2;\n\n const ctx = canvas.getContext('2d')!;\n const oldFill = ctx.fillStyle;\n ctx.fillStyle = 'white';\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.fillStyle = oldFill;\n\n ctx.drawImage(image, 0, 0, canvas.width, canvas.height);\n return canvas;\n}\n\nfunction canvasToBlob(canvas: HTMLCanvasElement, type: string) {\n return new Promise((resolve, reject) => {\n canvas.toBlob((blob) => {\n if (blob) {\n resolve(blob);\n } else {\n reject();\n }\n }, type);\n });\n}\n\n/** Supported chart types. */\nexport enum ChartType {\n Hourglass,\n Relatives,\n Fancy,\n}\n\nexport interface ChartProps {\n data: JsonGedcomData;\n selection: IndiInfo;\n chartType: ChartType;\n onSelection: (indiInfo: IndiInfo) => void;\n}\n\n/** Component showing the genealogy chart and handling transition animations. */\nexport class Chart extends React.PureComponent {\n private chart?: ChartHandle;\n /** Animation is in progress. */\n private animating = false;\n /** Rendering is required after the current animation finishes. */\n private rerenderRequired = false;\n /** The d3 zoom behavior object. */\n private zoomBehavior?: d3.ZoomBehavior;\n\n private getChartType() {\n switch (this.props.chartType) {\n case ChartType.Hourglass:\n return HourglassChart;\n case ChartType.Relatives:\n return RelativesChart;\n case ChartType.Fancy:\n return FancyChart;\n default:\n // Fall back to hourglass chart.\n return HourglassChart;\n }\n }\n\n private getRendererType() {\n switch (this.props.chartType) {\n case ChartType.Fancy:\n return CircleRenderer;\n default:\n // Use DetailedRenderer by default.\n return DetailedRenderer;\n }\n }\n\n private zoom(factor: number) {\n const parent = d3.select('#svgContainer') as d3.Selection<\n Element,\n any,\n any,\n any\n >;\n this.zoomBehavior!.scaleBy(parent, factor);\n }\n\n /**\n * Renders the chart or performs a transition animation to a new state.\n * If indiInfo is not given, it means that it is the initial render and no\n * animation is performed.\n */\n private renderChart(args: {initialRender: boolean} = {initialRender: false}) {\n // Wait for animation to finish if animation is in progress.\n if (!args.initialRender && this.animating) {\n this.rerenderRequired = true;\n return;\n }\n\n if (args.initialRender) {\n (d3.select('#chart').node() as HTMLElement).innerHTML = '';\n this.chart = createChart({\n json: this.props.data,\n chartType: this.getChartType(),\n renderer: this.getRendererType(),\n svgSelector: '#chart',\n indiCallback: (info) => this.props.onSelection(info),\n animate: true,\n updateSvgSize: false,\n locale: this.context.intl.locale,\n });\n } else {\n this.chart!.setData(this.props.data);\n }\n const chartInfo = this.chart!.render({\n startIndi: this.props.selection.id,\n baseGeneration: this.props.selection.generation,\n });\n const svg = d3.select('#chartSvg');\n const parent = d3.select('#svgContainer').node() as Element;\n\n const scale = d3.zoomTransform(parent).k;\n const zoomOutFactor = d3.min([\n 1,\n scale,\n parent.clientWidth / chartInfo.size[0],\n parent.clientHeight / chartInfo.size[1],\n ])!;\n const extent: [number, number] = [d3.max([0.1, zoomOutFactor])!, 2];\n\n this.zoomBehavior = d3\n .zoom()\n .scaleExtent(extent)\n .translateExtent([[0, 0], chartInfo.size])\n .on('zoom', () => zoomed(chartInfo.size));\n d3.select(parent)\n .on('scroll', scrolled)\n .call(this.zoomBehavior);\n\n const scrollTopTween = (scrollTop: number) => {\n return () => {\n const i = d3.interpolateNumber(parent.scrollTop, scrollTop);\n return (t: number) => {\n parent.scrollTop = i(t);\n };\n };\n };\n const scrollLeftTween = (scrollLeft: number) => {\n return () => {\n const i = d3.interpolateNumber(parent.scrollLeft, scrollLeft);\n return (t: number) => {\n parent.scrollLeft = i(t);\n };\n };\n };\n\n const dx = parent.clientWidth / 2 - chartInfo.origin[0] * scale;\n const dy = parent.clientHeight / 2 - chartInfo.origin[1] * scale;\n const offsetX = d3.max([\n 0,\n (parent.clientWidth - chartInfo.size[0] * scale) / 2,\n ]);\n const offsetY = d3.max([\n 0,\n (parent.clientHeight - chartInfo.size[1] * scale) / 2,\n ]);\n const svgTransition = svg\n .transition()\n .delay(200)\n .duration(500);\n const transition = args.initialRender ? svg : svgTransition;\n transition\n .attr('transform', `translate(${offsetX}, ${offsetY})`)\n .attr('width', chartInfo.size[0] * scale)\n .attr('height', chartInfo.size[1] * scale);\n if (args.initialRender) {\n parent.scrollLeft = -dx;\n parent.scrollTop = -dy;\n } else {\n svgTransition\n .tween('scrollLeft', scrollLeftTween(-dx))\n .tween('scrollTop', scrollTopTween(-dy));\n }\n\n // After the animation is finished, rerender the chart if required.\n this.animating = true;\n chartInfo.animationPromise.then(() => {\n this.animating = false;\n if (this.rerenderRequired) {\n this.rerenderRequired = false;\n this.renderChart({initialRender: false});\n }\n });\n }\n\n componentDidMount() {\n this.renderChart({initialRender: true});\n }\n\n componentDidUpdate(prevProps: ChartProps) {\n const initialRender = this.props.chartType !== prevProps.chartType;\n this.renderChart({initialRender});\n }\n\n /** Make intl appear in this.context. */\n static contextTypes = {\n intl: intlShape,\n };\n\n render() {\n return (\n \n \n this.zoom(ZOOM_FACTOR)}>\n +\n \n this.zoom(1 / ZOOM_FACTOR)}\n >\n −\n \n \n \n \n \n
\n );\n }\n\n /** Return a copy of the SVG chart but without scaling and positioning. */\n private getStrippedSvg() {\n const svg = document.getElementById('chartSvg')!.cloneNode(true) as Element;\n\n svg.removeAttribute('transform');\n const parent = d3.select('#svgContainer').node() as Element;\n const scale = d3.zoomTransform(parent).k;\n svg.setAttribute(\n 'width',\n String(Number(svg.getAttribute('width')) / scale),\n );\n svg.setAttribute(\n 'height',\n String(Number(svg.getAttribute('height')) / scale),\n );\n svg.querySelector('#chart')!.removeAttribute('transform');\n\n return svg;\n }\n\n private getSvgContents() {\n return new XMLSerializer().serializeToString(this.getStrippedSvg());\n }\n\n private async getSvgContentsWithInlinedImages() {\n const svg = this.getStrippedSvg();\n await inlineImages(svg);\n return new XMLSerializer().serializeToString(svg);\n }\n\n /** Shows the print dialog to print the currently displayed chart. */\n print() {\n const printWindow = document.createElement('iframe');\n printWindow.style.position = 'absolute';\n printWindow.style.top = '-1000px';\n printWindow.style.left = '-1000px';\n printWindow.onload = () => {\n printWindow.contentDocument!.open();\n printWindow.contentDocument!.write(this.getSvgContents());\n printWindow.contentDocument!.close();\n // Doesn't work on Firefox without the setTimeout.\n setTimeout(() => {\n printWindow.contentWindow!.focus();\n printWindow.contentWindow!.print();\n printWindow.parentNode!.removeChild(printWindow);\n }, 500);\n };\n document.body.appendChild(printWindow);\n }\n\n async downloadSvg() {\n const contents = await this.getSvgContentsWithInlinedImages();\n const blob = new Blob([contents], {type: 'image/svg+xml'});\n saveAs(blob, 'topola.svg');\n }\n\n private async drawOnCanvas(): Promise {\n const contents = await this.getSvgContentsWithInlinedImages();\n const blob = new Blob([contents], {type: 'image/svg+xml'});\n return await drawOnCanvas(await loadImage(blob));\n }\n\n async downloadPng() {\n const canvas = await this.drawOnCanvas();\n const blob = await canvasToBlob(canvas, 'image/png');\n saveAs(blob, 'topola.png');\n }\n\n async downloadPdf() {\n const canvas = await this.drawOnCanvas();\n const doc = new jsPDF({\n orientation: canvas.width > canvas.height ? 'l' : 'p',\n unit: 'pt',\n format: [canvas.width, canvas.height],\n });\n doc.addImage(canvas, 'PNG', 0, 0, canvas.width, canvas.height, 'NONE');\n doc.save('topola.pdf');\n }\n}\n","import {\n JsonFam,\n JsonGedcomData,\n JsonIndi,\n gedcomEntriesToJson,\n JsonImage,\n JsonEvent,\n} from 'topola';\nimport {GedcomEntry, parse as parseGedcom} from 'parse-gedcom';\n\nexport interface GedcomData {\n /** The HEAD entry. */\n head: GedcomEntry;\n /** INDI entries mapped by id. */\n indis: {[key: string]: GedcomEntry};\n /** FAM entries mapped by id. */\n fams: {[key: string]: GedcomEntry};\n /** Other entries mapped by id, e.g. NOTE, SOUR. */\n other: {[key: string]: GedcomEntry};\n}\n\nexport interface TopolaData {\n chartData: JsonGedcomData;\n gedcom: GedcomData;\n}\n\n/**\n * Returns the identifier extracted from a pointer string.\n * E.g. '@I123@' -> 'I123'\n */\nexport function pointerToId(pointer: string): string {\n return pointer.substring(1, pointer.length - 1);\n}\n\nexport function idToIndiMap(data: JsonGedcomData): Map {\n const map = new Map();\n data.indis.forEach((indi) => {\n map.set(indi.id, indi);\n });\n return map;\n}\n\nexport function idToFamMap(data: JsonGedcomData): Map {\n const map = new Map();\n data.fams.forEach((fam) => {\n map.set(fam.id, fam);\n });\n return map;\n}\n\nfunction prepareGedcom(entries: GedcomEntry[]): GedcomData {\n const head = entries.find((entry) => entry.tag === 'HEAD')!;\n const indis: {[key: string]: GedcomEntry} = {};\n const fams: {[key: string]: GedcomEntry} = {};\n const other: {[key: string]: GedcomEntry} = {};\n entries.forEach((entry) => {\n if (entry.tag === 'INDI') {\n indis[pointerToId(entry.pointer)] = entry;\n } else if (entry.tag === 'FAM') {\n fams[pointerToId(entry.pointer)] = entry;\n } else if (entry.pointer) {\n other[pointerToId(entry.pointer)] = entry;\n }\n });\n return {head, indis, fams, other};\n}\n\nfunction strcmp(a: string, b: string) {\n if (a < b) {\n return -1;\n }\n if (a > b) {\n return 1;\n }\n return 0;\n}\n\n/** Compares dates of the given events. */\nfunction compareDates(\n event1: JsonEvent | undefined,\n event2: JsonEvent | undefined,\n): number {\n const date1 =\n event1 && (event1.date || (event1.dateRange && event1.dateRange.from));\n const date2 =\n event2 && (event2.date || (event2.dateRange && event2.dateRange.from));\n if (!date1 || !date1.year || !date2 || !date2.year) {\n return 0;\n }\n if (date1.year !== date2.year) {\n return date1.year - date2.year;\n }\n if (!date1.month || !date2.month) {\n return 0;\n }\n if (date1.month !== date2.month) {\n return date1.month - date2.month;\n }\n if (date1.day && date2.day && date1.day !== date2.day) {\n return date1.month - date2.month;\n }\n return 0;\n}\n\n/** Birth date comparator for individuals. */\nfunction birthDatesComparator(gedcom: JsonGedcomData) {\n const indiMap = idToIndiMap(gedcom);\n\n return (indiId1: string, indiId2: string) => {\n const indi1: JsonIndi | undefined = indiMap.get(indiId1);\n const indi2: JsonIndi | undefined = indiMap.get(indiId2);\n return (\n compareDates(indi1 && indi1.birth, indi2 && indi2.birth) ||\n strcmp(indiId1, indiId2)\n );\n };\n}\n\n/** Marriage date comparator for families. */\nfunction marriageDatesComparator(gedcom: JsonGedcomData) {\n const famMap = idToFamMap(gedcom);\n\n return (famId1: string, famId2: string) => {\n const fam1: JsonFam | undefined = famMap.get(famId1);\n const fam2: JsonFam | undefined = famMap.get(famId2);\n return (\n compareDates(fam1 && fam1.marriage, fam2 && fam2.marriage) ||\n strcmp(famId1, famId2)\n );\n };\n}\n\n/**\n * Sorts children by birth date in the given family.\n * Does not modify the input objects.\n */\nfunction sortFamilyChildren(\n fam: JsonFam,\n comparator: (id1: string, id2: string) => number,\n): JsonFam {\n if (!fam.children) {\n return fam;\n }\n const newChildren = fam.children.sort(comparator);\n return Object.assign({}, fam, {children: newChildren});\n}\n\n/**\n * Sorts children by birth date.\n * Does not modify the input object.\n */\nfunction sortChildren(gedcom: JsonGedcomData): JsonGedcomData {\n const comparator = birthDatesComparator(gedcom);\n const newFams = gedcom.fams.map((fam) => sortFamilyChildren(fam, comparator));\n return Object.assign({}, gedcom, {fams: newFams});\n}\n\n/**\n * Sorts spouses by marriage date.\n * Does not modify the input objects.\n */\nfunction sortIndiSpouses(\n indi: JsonIndi,\n comparator: (id1: string, id2: string) => number,\n): JsonFam {\n if (!indi.fams) {\n return indi;\n }\n const newFams = indi.fams.sort(comparator);\n return Object.assign({}, indi, {fams: newFams});\n}\n\nfunction sortSpouses(gedcom: JsonGedcomData): JsonGedcomData {\n const comparator = marriageDatesComparator(gedcom);\n const newIndis = gedcom.indis.map((indi) =>\n sortIndiSpouses(indi, comparator),\n );\n return Object.assign({}, gedcom, {indis: newIndis});\n}\n\n/** Sorts children and spouses. */\nexport function normalizeGedcom(gedcom: JsonGedcomData): JsonGedcomData {\n return sortSpouses(sortChildren(gedcom));\n}\n\nconst IMAGE_EXTENSIONS = ['.jpg', '.png', '.gif'];\n\n/** Returns true if the given file name has a known image extension. */\nfunction isImageFile(fileName: string): boolean {\n const lowerName = fileName.toLowerCase();\n return IMAGE_EXTENSIONS.some((ext) => lowerName.endsWith(ext));\n}\n\n/**\n * Removes images that are not HTTP links or do not have known image extensions.\n * Does not modify the input object.\n */\nfunction filterImage(indi: JsonIndi, images: Map): JsonIndi {\n if (!indi.images || indi.images.length === 0) {\n return indi;\n }\n const newImages: JsonImage[] = [];\n indi.images.forEach((image) => {\n const fileName = image.url.match(/[^/\\\\]*$/)![0];\n // If the image file has been loaded into memory, use it.\n if (images.has(fileName)) {\n newImages.push({url: images.get(fileName)!, title: image.title});\n } else if (image.url.startsWith('http') && isImageFile(image.url)) {\n newImages.push(image);\n }\n });\n return Object.assign({}, indi, {images: newImages});\n}\n\n/**\n * Removes images that are not HTTP links.\n * Does not modify the input object.\n */\nfunction filterImages(\n gedcom: JsonGedcomData,\n images: Map,\n): JsonGedcomData {\n const newIndis = gedcom.indis.map((indi) => filterImage(indi, images));\n return Object.assign({}, gedcom, {indis: newIndis});\n}\n\n/**\n * Converts GEDCOM file into JSON data performing additional transformations:\n * - sort children by birth date\n * - remove images that are not HTTP links and aren't mapped in `images`.\n *\n * @param images Map from file name to image URL. This is used to pass in\n * uploaded images.\n */\nexport function convertGedcom(\n gedcom: string,\n images: Map,\n): TopolaData {\n const entries = parseGedcom(gedcom);\n const json = gedcomEntriesToJson(entries);\n if (\n !json ||\n !json.indis ||\n !json.indis.length ||\n !json.fams ||\n !json.fams.length\n ) {\n throw new Error('Failed to read GEDCOM file');\n }\n\n return {\n chartData: filterImages(normalizeGedcom(json), images),\n gedcom: prepareGedcom(entries),\n };\n}\n\nexport function getSoftware(head: GedcomEntry): string | null {\n const sour =\n head && head.tree && head.tree.find((entry) => entry.tag === 'SOUR');\n const name =\n sour && sour.tree && sour.tree.find((entry) => entry.tag === 'NAME');\n return (name && name.data) || null;\n}\n","import {Date as TopolaDate, DateRange, getDate, DateOrRange} from 'topola';\nimport {InjectedIntl} from 'react-intl';\n\nconst DATE_QUALIFIERS = new Map([\n ['abt', 'about'],\n ['cal', 'calculated'],\n ['est', 'estimated'],\n]);\n\nfunction formatDate(date: TopolaDate, intl: InjectedIntl) {\n const hasDay = date.day !== undefined;\n const hasMonth = date.month !== undefined;\n const hasYear = date.year !== undefined;\n if (!hasDay && !hasMonth && !hasYear) {\n return date.text || '';\n }\n const dateObject = new Date(\n hasYear ? date.year! : 0,\n hasMonth ? date.month! - 1 : 0,\n hasDay ? date.day! : 1,\n );\n\n const qualifier = date.qualifier && date.qualifier.toLowerCase();\n const translatedQualifier =\n qualifier &&\n intl.formatMessage({\n id: `date.${qualifier}`,\n defaultMessage: DATE_QUALIFIERS.get(qualifier) || qualifier,\n });\n\n const formatOptions = {\n day: hasDay ? 'numeric' : undefined,\n month: hasMonth ? 'long' : undefined,\n year: hasYear ? 'numeric' : undefined,\n };\n const translatedDate = new Intl.DateTimeFormat(\n intl.locale,\n formatOptions,\n ).format(dateObject);\n\n return [translatedQualifier, translatedDate].join(' ');\n}\n\nfunction formatDateRage(dateRange: DateRange, intl: InjectedIntl) {\n const fromDate = dateRange.from;\n const toDate = dateRange.to;\n const translatedFromDate = fromDate && formatDate(fromDate, intl);\n const translatedToDate = toDate && formatDate(toDate, intl);\n if (translatedFromDate && translatedToDate) {\n return intl.formatMessage(\n {\n id: 'date.between',\n defaultMessage: 'between {from} and {to}',\n },\n {from: translatedFromDate, to: translatedToDate},\n );\n }\n if (translatedFromDate) {\n return intl.formatMessage(\n {\n id: 'date.after',\n defaultMessage: 'after {from}',\n },\n {from: translatedFromDate},\n );\n }\n if (translatedToDate) {\n return intl.formatMessage(\n {\n id: 'date.before',\n defaultMessage: 'before {to}',\n },\n {to: translatedToDate},\n );\n }\n return '';\n}\n\n/** Formats a DateOrRange object. */\nexport function formatDateOrRange(\n dateOrRange: DateOrRange | undefined,\n intl: InjectedIntl,\n): string {\n if (!dateOrRange) {\n return '';\n }\n if (dateOrRange.date) {\n return formatDate(dateOrRange.date, intl);\n }\n if (dateOrRange.dateRange) {\n return formatDateRage(dateOrRange.dateRange, intl);\n }\n return '';\n}\n\n/** Formats a date given in GEDCOM format. */\nexport function translateDate(gedcomDate: string, intl: InjectedIntl): string {\n return formatDateOrRange(getDate(gedcomDate), intl);\n}\n","import * as React from 'react';\nimport flatMap from 'array.prototype.flatmap';\nimport Linkify from 'react-linkify';\nimport {FormattedMessage, InjectedIntl} from 'react-intl';\nimport {GedcomData, pointerToId} from './gedcom_util';\nimport {GedcomEntry} from 'parse-gedcom';\nimport {intlShape} from 'react-intl';\nimport {translateDate} from './date_util';\n\ninterface Props {\n gedcom: GedcomData;\n indi: string;\n}\n\nconst EVENT_TAGS = ['BIRT', 'BAPM', 'CHR', 'DEAT', 'BURI', 'EVEN', 'CENS'];\nconst EXCLUDED_TAGS = ['NAME', 'SEX', 'FAMC', 'FAMS', 'NOTE', 'SOUR'];\nconst TAG_DESCRIPTIONS = new Map([\n ['BAPM', 'Baptism'],\n ['BIRT', 'Birth'],\n ['BURI', 'Burial'],\n ['CENS', 'Census'],\n ['CHR', 'Christening'],\n ['DEAT', 'Death'],\n ['EMAIL', 'E-mail'],\n ['EVEN', 'Event'],\n ['OCCU', 'Occupation'],\n ['TITL', 'Title'],\n ['WWW', 'WWW'],\n]);\n\nfunction translateTag(tag: string) {\n return (\n \n );\n}\n\nfunction joinLines(lines: (JSX.Element | string)[]) {\n return (\n <>\n {lines.map((line, index) => (\n \n {line} \n \n
\n ))}\n >\n );\n}\n\n/**\n * Returns the data for the given GEDCOM entry as an array of lines. Supports\n * continuations with CONT and CONC.\n */\nfunction getData(entry: GedcomEntry) {\n const result = [entry.data];\n entry.tree.forEach((subentry) => {\n if (subentry.tag === 'CONC' && subentry.data) {\n const last = result.length - 1;\n result[last] += subentry.data;\n } else if (subentry.tag === 'CONT' && subentry.data) {\n result.push(subentry.data);\n }\n });\n return result;\n}\n\nfunction eventDetails(entry: GedcomEntry, intl: InjectedIntl) {\n const lines = [];\n if (entry.data && entry.data.length > 1) {\n lines.push({entry.data} );\n }\n const date = entry.tree.find((subentry) => subentry.tag === 'DATE');\n if (date && date.data) {\n lines.push(translateDate(date.data, intl));\n }\n const place = entry.tree.find((subentry) => subentry.tag === 'PLAC');\n if (place && place.data) {\n lines.push(...getData(place));\n }\n entry.tree\n .filter((subentry) => subentry.tag === 'NOTE')\n .forEach((note) =>\n getData(note).forEach((line) => lines.push({line} )),\n );\n if (!lines.length) {\n return null;\n }\n return (\n <>\n {translateTag(entry.tag)}
\n {joinLines(lines)} \n >\n );\n}\n\nfunction dataDetails(entry: GedcomEntry) {\n const lines = [];\n if (entry.data) {\n lines.push(...getData(entry));\n }\n entry.tree\n .filter((subentry) => subentry.tag === 'NOTE')\n .forEach((note) =>\n getData(note).forEach((line) => lines.push({line} )),\n );\n if (!lines.length) {\n return null;\n }\n return (\n <>\n {translateTag(entry.tag)}
\n {joinLines(lines)} \n >\n );\n}\n\nfunction noteDetails(entry: GedcomEntry) {\n return joinLines(\n getData(entry).map((line, index) => {line} ),\n );\n}\n\nfunction nameDetails(entry: GedcomEntry) {\n return (\n \n {entry.data\n .split('/')\n .filter((name) => !!name)\n .map((name, index) => (\n \n {name}\n \n
\n ))}\n \n );\n}\n\nfunction getDetails(\n entries: GedcomEntry[],\n tags: string[],\n detailsFunction: (entry: GedcomEntry) => JSX.Element | null,\n): JSX.Element[] {\n return flatMap(tags, (tag) =>\n entries\n .filter((entry) => entry.tag === tag)\n .map((entry) => detailsFunction(entry)),\n )\n .filter((element) => element !== null)\n .map((element, index) => (\n \n {element}\n
\n ));\n}\n\n/**\n * Returns true if there is displayable information in this entry.\n * Returns false if there is no data in this entry or this is only a reference\n * to another entry.\n */\nfunction hasData(entry: GedcomEntry) {\n return entry.tree.length > 0 || (entry.data && !entry.data.startsWith('@'));\n}\n\nfunction getOtherDetails(entries: GedcomEntry[]) {\n return entries\n .filter(\n (entry) =>\n !EXCLUDED_TAGS.includes(entry.tag) && !EVENT_TAGS.includes(entry.tag),\n )\n .filter(hasData)\n .map((entry) => dataDetails(entry))\n .filter((element) => element !== null)\n .map((element, index) => (\n \n {element}\n
\n ));\n}\n\n/**\n * If the entry is a reference to a top-level entry, the referenced entry is\n * returned. Otherwise, returns the given entry unmodified.\n */\nfunction dereference(entry: GedcomEntry, gedcom: GedcomData) {\n if (entry.data) {\n const dereferenced = gedcom.other[pointerToId(entry.data)];\n if (dereferenced) {\n return dereferenced;\n }\n }\n return entry;\n}\n\nexport class Details extends React.Component {\n /** Make intl appear in this.context. */\n static contextTypes = {\n intl: intlShape,\n };\n\n render() {\n const entries = this.props.gedcom.indis[this.props.indi].tree;\n const entriesWithData = entries\n .map((entry) => dereference(entry, this.props.gedcom))\n .filter(hasData);\n\n return (\n \n {getDetails(entries, ['NAME'], nameDetails)}\n {getDetails(entries, EVENT_TAGS, (entry) =>\n eventDetails(entry, this.context.intl as InjectedIntl),\n )}\n {getOtherDetails(entriesWithData)}\n {getDetails(entriesWithData, ['NOTE'], noteDetails)}\n
\n );\n }\n}\n","import {convertGedcom, TopolaData} from './gedcom_util';\nimport {IndiInfo, JsonGedcomData} from 'topola';\n\n/**\n * Returns a valid IndiInfo object, either with the given indi and generation\n * or with an individual taken from the data and generation 0.\n */\nexport function getSelection(\n data: JsonGedcomData,\n indi?: string,\n generation?: number,\n): IndiInfo {\n // If ID is not given or it doesn't exist in the data, use the first ID in\n // the data.\n const id =\n indi && data.indis.some((i) => i.id === indi) ? indi : data.indis[0].id;\n return {id, generation: generation || 0};\n}\n\nfunction prepareData(\n gedcom: string,\n cacheId: string,\n images?: Map,\n): TopolaData {\n const data = convertGedcom(gedcom, images || new Map());\n const serializedData = JSON.stringify(data);\n try {\n sessionStorage.setItem(cacheId, serializedData);\n } catch (e) {\n console.warn('Failed to store data in session storage: ' + e);\n }\n return data;\n}\n\n/** Fetches data from the given URL. Uses cors-anywhere if handleCors is true. */\nexport async function loadFromUrl(\n url: string,\n handleCors: boolean,\n): Promise {\n try {\n const cachedData = sessionStorage.getItem(url);\n if (cachedData) {\n return JSON.parse(cachedData);\n }\n } catch (e) {\n console.warn('Failed to load data from session storage: ' + e);\n }\n const urlToFetch = handleCors\n ? 'https://cors-anywhere.herokuapp.com/' + url\n : url;\n\n const response = await window.fetch(urlToFetch);\n if (response.status !== 200) {\n throw new Error(response.statusText);\n }\n const gedcom = await response.text();\n return prepareData(gedcom, url);\n}\n\n/** Loads data from the given GEDCOM file contents. */\nexport async function loadGedcom(\n hash: string,\n gedcom?: string,\n images?: Map,\n): Promise {\n try {\n const cachedData = sessionStorage.getItem(hash);\n if (cachedData) {\n return JSON.parse(cachedData);\n }\n } catch (e) {\n console.warn('Failed to load data from session storage: ' + e);\n }\n if (!gedcom) {\n throw new Error('Error loading data. Please upload your file again.');\n }\n return prepareData(gedcom, hash, images);\n}\n","import * as queryString from 'query-string';\nimport * as React from 'react';\nimport logo from './topola.jpg';\nimport {Card, Grid, Image, Responsive} from 'semantic-ui-react';\nimport {FormattedMessage} from 'react-intl';\nimport {Link} from 'react-router-dom';\n\n/** Link that loads a GEDCOM file from URL. */\nfunction GedcomLink(props: {url: string; text: string}) {\n return (\n \n {props.text}\n \n );\n}\n\nfunction formatBuildDate(dateString: string) {\n return dateString.slice(0, 16);\n}\n\n/** The intro page. */\nexport function Intro() {\n const contents = (\n <>\n \n \n
\n \n \n
\n \n \n
\n \n \n \n \n \n {': '}\n cors-anywhere\n ),\n }}\n />\n
\n \n version: {formatBuildDate(process.env.REACT_APP_GIT_TIME!)} (\n \n {process.env.REACT_APP_GIT_SHA}\n \n )\n
\n >\n );\n\n return (\n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n {contents} \n \n \n \n \n {contents}\n \n \n \n
\n );\n}\n","import Cookies from 'js-cookie';\nimport {Date, JsonFam, JsonIndi, DateOrRange} from 'topola';\nimport {GedcomData, TopolaData, normalizeGedcom} from './gedcom_util';\nimport {GedcomEntry} from 'parse-gedcom';\n\n/** WikiTree API getAncestors request. */\ninterface GetAncestorsRequest {\n action: 'getAncestors';\n key: string;\n fields: string;\n}\n\n/** WikiTree API getRelatives request. */\ninterface GetRelatives {\n action: 'getRelatives';\n keys: string;\n getChildren?: true;\n getSpouses?: true;\n}\n\ninterface ClientLogin {\n action: 'clientLogin';\n authcode: string;\n}\n\ntype WikiTreeRequest = GetAncestorsRequest | GetRelatives | ClientLogin;\n\n/** Person structure returned from WikiTree API. */\ninterface Person {\n Id: number;\n Name: string;\n FirstName: string;\n LastNameAtBirth: string;\n Spouses: {[key: number]: Person};\n Children: {[key: number]: Person};\n Mother: number;\n Father: number;\n Gender: string;\n BirthDate: string;\n DeathDate: string;\n BirthLocation: string;\n DeathLocation: string;\n marriage_location: string;\n marriage_date: string;\n DataStatus?: {\n BirthDate: string;\n DeathDate: string;\n };\n PhotoData?: {\n path: string;\n };\n}\n\n/** Gets item from session storage. Logs exception if one is thrown. */\nfunction getSessionStorageItem(key: string): string | null {\n try {\n return sessionStorage.getItem(key);\n } catch (e) {\n console.warn('Failed to load data from session storage: ' + e);\n }\n return null;\n}\n\n/** Sets item in session storage. Logs exception if one is thrown. */\nfunction setSessionStorageItem(key: string, value: string) {\n try {\n sessionStorage.setItem(key, value);\n } catch (e) {\n console.warn('Failed to store data in session storage: ' + e);\n }\n}\n\n/** Sends a request to the WikiTree API. Returns the parsed response JSON. */\nasync function wikiTreeGet(request: WikiTreeRequest, handleCors: boolean) {\n const requestData = new FormData();\n requestData.append('format', 'json');\n for (const key in request) {\n requestData.append(key, request[key]);\n }\n const apiUrl = handleCors\n ? 'https://cors-anywhere.herokuapp.com/https://api.wikitree.com/api.php'\n : 'https://api.wikitree.com/api.php';\n const response = await window.fetch(apiUrl, {\n method: 'POST',\n body: requestData,\n });\n const responseBody = await response.text();\n return JSON.parse(responseBody);\n}\n\n/**\n * Retrieves ancestors from WikiTree for the given person ID.\n * Uses sessionStorage for caching responses.\n */\nasync function getAncestors(key: string, handleCors: boolean) {\n const cacheKey = `wikitree:ancestors:${key}`;\n const cachedData = getSessionStorageItem(cacheKey);\n if (cachedData) {\n return JSON.parse(cachedData);\n }\n const response = await wikiTreeGet(\n {\n action: 'getAncestors',\n key: key,\n fields: '*',\n },\n handleCors,\n );\n const result = response[0].ancestors as Person[];\n setSessionStorageItem(cacheKey, JSON.stringify(result));\n return result;\n}\n\n/**\n * Retrieves relatives from WikiTree for the given array of person IDs.\n * Uses sessionStorage for caching responses.\n */\nasync function getRelatives(keys: string[], handleCors: boolean) {\n const result: Person[] = [];\n const keysToFetch: string[] = [];\n keys.forEach((key) => {\n const cachedData = getSessionStorageItem(`wikitree:relatives:${key}`);\n if (cachedData) {\n result.push(JSON.parse(cachedData));\n } else {\n keysToFetch.push(key);\n }\n });\n if (keysToFetch.length === 0) {\n return result;\n }\n const response = await wikiTreeGet(\n {\n action: 'getRelatives',\n keys: keysToFetch.join(','),\n getChildren: true,\n getSpouses: true,\n },\n handleCors,\n );\n if (response[0].items === null) {\n throw new Error(`WikiTree profile ${keysToFetch[0]} not found.`);\n }\n const fetchedResults = response[0].items.map(\n (x: {person: Person}) => x.person,\n ) as Person[];\n fetchedResults.forEach((person) => {\n setSessionStorageItem(\n `wikitree:relatives:${person.Name}`,\n JSON.stringify(person),\n );\n });\n return result.concat(fetchedResults);\n}\n\nexport async function clientLogin(authcode: string) {\n const response = await wikiTreeGet(\n {\n action: 'clientLogin',\n authcode,\n },\n false,\n );\n return response.clientLogin;\n}\n\n/**\n * Loads data from WikiTree to populate an hourglass chart starting from the\n * given person ID.\n */\nexport async function loadWikiTree(\n key: string,\n authcode?: string,\n): Promise {\n // Work around CORS if not in apps.wikitree.com domain.\n const handleCors = window.location.hostname !== 'apps.wikitree.com';\n\n if (!handleCors && !Cookies.get('wikidb_wtb_UserID') && authcode) {\n const loginResult = await clientLogin(authcode);\n if (loginResult.result === 'Success') {\n sessionStorage.clear();\n }\n }\n\n const everyone: Person[] = [];\n\n // Fetch the ancestors of the input person and ancestors of his/her spouses.\n const firstPerson = await getRelatives([key], handleCors);\n if (!firstPerson[0].Name) {\n throw new Error(\n `WikiTree profile ${key} is not accessible. Try logging in.`,\n );\n }\n\n const spouseKeys = Object.values(firstPerson[0].Spouses).map((s) => s.Name);\n const ancestors = await Promise.all(\n [key]\n .concat(spouseKeys)\n .map((personId) => getAncestors(personId, handleCors)),\n );\n const ancestorKeys = ancestors\n .flat()\n .map((person) => person.Name)\n .filter((key) => !!key);\n const ancestorDetails = await getRelatives(ancestorKeys, handleCors);\n everyone.push(...ancestorDetails);\n\n // Limit the number of generations of descendants because there may be tens of\n // generations for some profiles.\n const descendantGenerationLimit = 5;\n\n // Fetch descendants recursively.\n let toFetch = [key];\n let generation = 0;\n while (toFetch.length > 0 && generation <= descendantGenerationLimit) {\n const people = await getRelatives(toFetch, handleCors);\n everyone.push(...people);\n const allSpouses = people.flatMap((person) =>\n Object.values(person.Spouses),\n );\n everyone.push(...allSpouses);\n // Fetch all children.\n toFetch = people.flatMap((person) =>\n Object.values(person.Children).map((c) => c.Name),\n );\n generation++;\n }\n\n // Map from person id to the set of families where they are a spouse.\n const families = new Map>();\n // Map from family id to the set of children.\n const children = new Map>();\n // Map from famliy id to the spouses.\n const spouses = new Map<\n string,\n {wife?: number; husband?: number; spouse?: Person}\n >();\n // Map from numerical id to human-readable id.\n const idToName = new Map();\n\n everyone.forEach((person) => {\n idToName.set(person.Id, person.Name);\n if (person.Mother || person.Father) {\n const famId = getFamilyId(person.Mother, person.Father);\n getSet(families, person.Mother).add(famId);\n getSet(families, person.Father).add(famId);\n getSet(children, famId).add(person.Id);\n spouses.set(famId, {\n wife: person.Mother || undefined,\n husband: person.Father || undefined,\n });\n }\n });\n\n const indis: JsonIndi[] = [];\n const converted = new Set();\n everyone.forEach((person) => {\n if (converted.has(person.Id)) {\n return;\n }\n converted.add(person.Id);\n const indi = convertPerson(person);\n if (person.Spouses) {\n Object.values(person.Spouses).forEach((spouse) => {\n const famId = getFamilyId(person.Id, spouse.Id);\n getSet(families, person.Id).add(famId);\n getSet(families, spouse.Id).add(famId);\n const familySpouses =\n person.Gender === 'Male'\n ? {wife: spouse.Id, husband: person.Id, spouse}\n : {wife: person.Id, husband: spouse.Id, spouse};\n spouses.set(famId, familySpouses);\n });\n }\n indi.fams = Array.from(getSet(families, person.Id));\n indis.push(indi);\n });\n\n const fams = Array.from(spouses.entries()).map(([key, value]) => {\n const fam: JsonFam = {\n id: key,\n };\n const wife = value.wife && idToName.get(value.wife);\n if (wife) {\n fam.wife = wife;\n }\n const husband = value.husband && idToName.get(value.husband);\n if (husband) {\n fam.husb = husband;\n }\n fam.children = Array.from(getSet(children, key)).map(\n (child) => idToName.get(child)!,\n );\n if (\n value.spouse &&\n ((value.spouse.marriage_date &&\n value.spouse.marriage_date !== '0000-00-00') ||\n value.spouse.marriage_location)\n ) {\n const parsedDate = parseDate(value.spouse.marriage_date);\n fam.marriage = Object.assign({}, parsedDate, {\n place: value.spouse.marriage_location,\n });\n }\n return fam;\n });\n\n const chartData = normalizeGedcom({indis, fams});\n const gedcom = buildGedcom(indis);\n return {chartData, gedcom};\n}\n\n/** Creates a family identifier given 2 spouse identifiers. */\nfunction getFamilyId(spouse1: number, spouse2: number) {\n if (spouse2 > spouse1) {\n return `${spouse1}_${spouse2}`;\n }\n return `${spouse2}_${spouse1}`;\n}\n\nfunction convertPerson(person: Person): JsonIndi {\n const indi: JsonIndi = {\n id: person.Name,\n };\n if (person.FirstName !== 'Unknown') {\n indi.firstName = person.FirstName;\n }\n if (person.LastNameAtBirth !== 'Unknown') {\n indi.lastName = person.LastNameAtBirth;\n }\n if (person.Mother || person.Father) {\n indi.famc = getFamilyId(person.Mother, person.Father);\n }\n if (person.Gender === 'Male') {\n indi.sex = 'M';\n } else if (person.Gender === 'Female') {\n indi.sex = 'F';\n }\n if (\n (person.BirthDate && person.BirthDate !== '0000-00-00') ||\n person.BirthLocation\n ) {\n const parsedDate = parseDate(\n person.BirthDate,\n person.DataStatus && person.DataStatus.BirthDate,\n );\n indi.birth = Object.assign({}, parsedDate, {place: person.BirthLocation});\n }\n if (\n (person.DeathDate && person.DeathDate !== '0000-00-00') ||\n person.DeathLocation\n ) {\n const parsedDate = parseDate(\n person.DeathDate,\n person.DataStatus && person.DataStatus.DeathDate,\n );\n indi.death = Object.assign({}, parsedDate, {place: person.DeathLocation});\n }\n if (person.PhotoData) {\n indi.images = [{url: `https://www.wikitree.com${person.PhotoData.path}`}];\n }\n return indi;\n}\n\n/**\n * Parses a date in the format returned by WikiTree and converts in to\n * the format defined by Topola.\n */\nfunction parseDate(date: string, dataStatus?: string): DateOrRange | undefined {\n if (!date) {\n return undefined;\n }\n const matchedDate = date.match(/(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)/);\n if (!matchedDate) {\n return {date: {text: date}};\n }\n const parsedDate: Date = {};\n if (matchedDate[1] !== '0000') {\n parsedDate.year = ~~matchedDate[1];\n }\n if (matchedDate[2] !== '00') {\n parsedDate.month = ~~matchedDate[2];\n }\n if (matchedDate[3] !== '00') {\n parsedDate.day = ~~matchedDate[3];\n }\n if (dataStatus === 'after') {\n return {dateRange: {from: parsedDate}};\n }\n if (dataStatus === 'before') {\n return {dateRange: {to: parsedDate}};\n }\n if (dataStatus === 'guess') {\n parsedDate.qualifier = 'abt';\n }\n return {date: parsedDate};\n}\n\n/**\n * Creates a GEDCOM structure for the purpose of displaying the details\n * panel.\n */\nfunction buildGedcom(indis: JsonIndi[]): GedcomData {\n const gedcomIndis: {[key: string]: GedcomEntry} = {};\n indis.forEach((indi) => {\n // WikiTree URLs replace spaces with underscores.\n const escapedId = indi.id.replace(/ /g, '_');\n gedcomIndis[indi.id] = {\n level: 0,\n pointer: `@${indi.id}@`,\n tag: 'INDI',\n data: '',\n tree: [\n {\n level: 1,\n pointer: '',\n tag: 'NAME',\n data: `${indi.firstName || ''} /${indi.lastName || ''}/`,\n tree: [],\n },\n {\n level: 1,\n pointer: '',\n tag: 'WWW',\n data: `https://www.wikitree.com/wiki/${escapedId}`,\n tree: [],\n },\n ],\n };\n });\n\n return {\n head: {level: 0, pointer: '', tag: 'HEAD', data: '', tree: []},\n indis: gedcomIndis,\n fams: {},\n other: {},\n };\n}\n\n/**\n * Returns a set which is a value from a SetMultimap. If the key doesn't exist,\n * an empty set is added to the map.\n */\nfunction getSet(map: Map>, key: K): Set {\n const set = map.get(key);\n if (set) {\n return set;\n }\n const newSet = new Set();\n map.set(key, newSet);\n return newSet;\n}\n","import naturalSort from 'javascript-natural-sort';\nimport lunr from 'lunr';\nimport {idToIndiMap, idToFamMap} from './gedcom_util';\nimport {JsonIndi, JsonFam, JsonGedcomData} from 'topola';\n\nconst MAX_RESULTS = 8;\n\nexport interface SearchResult {\n id: string;\n indi: JsonIndi;\n}\n\nexport interface SearchIndex {\n search(input: string): SearchResult[];\n}\n\n/** Removes accents from letters, e.g. ó->o, ę->e. */\nfunction normalize(input: string) {\n return input\n .toLocaleLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .replace(/\\u0142/g, 'l'); // Special case: ł is not affected by NFD.\n}\n\n/** Comparator to sort by score first, then by id. */\nfunction compare(a: lunr.Index.Result, b: lunr.Index.Result) {\n if (a.score !== b.score) {\n return b.score - a.score;\n }\n return naturalSort(a.ref, b.ref);\n}\n\n/** Returns all last names of all husbands as a space-separated string. */\nfunction getHusbandLastName(\n indi: JsonIndi,\n indiMap: Map,\n famMap: Map,\n): string {\n return (indi.fams || [])\n .map((famId) => famMap.get(famId))\n .map((fam) => fam && fam.husb)\n .map((husbId) => husbId && indiMap.get(husbId))\n .map((husband) => husband && husband.lastName)\n .join(' ');\n}\n\nclass LunrSearchIndex implements SearchIndex {\n private index: lunr.Index | undefined;\n private indiMap: Map;\n private famMap: Map;\n\n constructor(data: JsonGedcomData) {\n this.indiMap = idToIndiMap(data);\n this.famMap = idToFamMap(data);\n }\n\n initialize() {\n const self = this;\n this.index = lunr(function() {\n this.ref('id');\n this.field('id');\n this.field('name', {boost: 10});\n this.field('normalizedName', {boost: 8});\n this.field('spouseLastName', {boost: 2});\n this.field('normalizedSpouseLastName', {boost: 2});\n\n self.indiMap.forEach((indi) => {\n const name = [indi.firstName, indi.lastName].join(' ');\n const spouseLastName = getHusbandLastName(\n indi,\n self.indiMap,\n self.famMap,\n );\n this.add({\n id: indi.id,\n name,\n normalizedName: normalize(name),\n spouseLastName,\n normalizedSpouseLastName: normalize(spouseLastName),\n });\n });\n });\n }\n\n public search(input: string): SearchResult[] {\n const query = input\n .split(' ')\n .filter((s) => !!s)\n .map((s) => `+${s}*`)\n .join(' ');\n const results = this.index!.search(query);\n return results\n .sort(compare)\n .slice(0, MAX_RESULTS)\n .map((result) => ({id: result.ref, indi: this.indiMap.get(result.ref)!}));\n }\n}\n\n/** Builds a search index from data. */\nexport function buildSearchIndex(data: JsonGedcomData): SearchIndex {\n const index = new LunrSearchIndex(data);\n index.initialize();\n return index;\n}\n","import * as React from 'react';\nimport {InjectedIntl} from 'react-intl';\nimport {SearchResult} from './search_index';\nimport {formatDateOrRange} from './date_util';\nimport {JsonIndi} from 'topola';\n\nfunction getNameLine(result: SearchResult) {\n const name = [result.indi.firstName, result.indi.lastName].join(' ').trim();\n if (result.id.length > 8) {\n return name;\n }\n return (\n <>\n {name} ({result.id}) \n >\n );\n}\n\nfunction getDescriptionLine(indi: JsonIndi, intl: InjectedIntl) {\n const birthDate = formatDateOrRange(indi.birth, intl);\n const deathDate = formatDateOrRange(indi.death, intl);\n if (!deathDate) {\n return birthDate;\n }\n return `${birthDate} – ${deathDate}`;\n}\n\n/** Produces an object that is displayed in the Semantic UI Search results. */\nexport function displaySearchResult(result: SearchResult, intl: InjectedIntl) {\n return {\n id: result.id,\n key: result.id,\n title: getNameLine(result),\n description: getDescriptionLine(result.indi, intl),\n };\n}\n","import * as queryString from 'query-string';\nimport * as React from 'react';\nimport Cookies from 'js-cookie';\nimport debounce from 'debounce';\nimport md5 from 'md5';\nimport {analyticsEvent} from './analytics';\nimport {buildSearchIndex, SearchIndex} from './search_index';\nimport {displaySearchResult} from './search_util';\nimport {FormattedMessage, intlShape} from 'react-intl';\nimport {IndiInfo, JsonGedcomData} from 'topola';\nimport {Link} from 'react-router-dom';\nimport {RouteComponentProps} from 'react-router-dom';\nimport {\n Header,\n Button,\n Icon,\n Menu,\n Modal,\n Input,\n Form,\n Dropdown,\n Search,\n SearchProps,\n SearchResultProps,\n Responsive,\n} from 'semantic-ui-react';\n\nconst WIKITREE_LOGO_URL =\n 'https://www.wikitree.com/photo.php/a/a5/WikiTree_Images.png';\n\nenum WikiTreeLoginState {\n UNKNOWN,\n NOT_LOGGED_IN,\n LOGGED_IN,\n}\n\nenum ScreenSize {\n LARGE,\n SMALL,\n}\n\n/** Menus and dialogs state. */\ninterface State {\n loadUrlDialogOpen: boolean;\n wikiTreeIdDialogOpen: boolean;\n url?: string;\n wikiTreeId?: string;\n wikiTreeLoginState: WikiTreeLoginState;\n wikiTreeLoginUsername?: string;\n searchResults: SearchResultProps[];\n}\n\ninterface EventHandlers {\n onSelection: (indiInfo: IndiInfo) => void;\n onPrint: () => void;\n onDownloadPdf: () => void;\n onDownloadPng: () => void;\n onDownloadSvg: () => void;\n}\n\ninterface Props {\n /** True if the application is currently showing a chart. */\n showingChart: boolean;\n /** Data used for the search index. */\n data?: JsonGedcomData;\n standalone: boolean;\n /** Whether to show the \"All relatives\" chart type in the menu. */\n allowAllRelativesChart: boolean;\n eventHandlers: EventHandlers;\n /** Whether to show additional WikiTree menus. */\n showWikiTreeMenus: boolean;\n}\n\nfunction loadFileAsText(file: File): Promise {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = (evt: ProgressEvent) => {\n resolve((evt.target as FileReader).result as string);\n };\n reader.readAsText(file);\n });\n}\n\nfunction isImageFileName(fileName: string) {\n const lower = fileName.toLowerCase();\n return lower.endsWith('.jpg') || lower.endsWith('.png');\n}\n\nexport class TopBar extends React.Component<\n RouteComponentProps & Props,\n State\n> {\n state: State = {\n loadUrlDialogOpen: false,\n wikiTreeIdDialogOpen: false,\n searchResults: [],\n wikiTreeLoginState: WikiTreeLoginState.UNKNOWN,\n };\n /** Make intl appear in this.context. */\n static contextTypes = {\n intl: intlShape,\n };\n\n urlInputRef: React.RefObject = React.createRef();\n wikiTreeIdInputRef: React.RefObject = React.createRef();\n wikiTreeLoginFormRef: React.RefObject = React.createRef();\n wikiTreeReturnUrlRef: React.RefObject = React.createRef();\n searchRef?: {setValue(value: string): void};\n searchIndex?: SearchIndex;\n\n /** Handles the \"Upload file\" button. */\n private async handleUpload(event: React.SyntheticEvent) {\n const files = (event.target as HTMLInputElement).files;\n if (!files || !files.length) {\n return;\n }\n const filesArray = Array.from(files);\n (event.target as HTMLInputElement).value = ''; // Reset the file input.\n analyticsEvent('upload_files_selected', {\n event_value: files.length,\n });\n\n const gedcomFile =\n filesArray.length === 1\n ? filesArray[0]\n : filesArray.find((file) => file.name.toLowerCase().endsWith('.ged')) ||\n filesArray[0];\n\n // Convert uploaded images to object URLs.\n const images = filesArray\n .filter(\n (file) => file.name !== gedcomFile.name && isImageFileName(file.name),\n )\n .map((file) => ({\n name: file.name,\n url: URL.createObjectURL(file),\n }));\n const imageMap = new Map(\n images.map((entry) => [entry.name, entry.url] as [string, string]),\n );\n\n const data = await loadFileAsText(gedcomFile);\n const imageFileNames = images\n .map((image) => image.name)\n .sort()\n .join('|');\n // Hash GEDCOM contents with uploaded image file names.\n const hash = md5(md5(data) + imageFileNames);\n\n // Use history.replace() when reuploading the same file and history.push() when loading\n // a new file.\n const search = queryString.parse(this.props.location.search);\n const historyPush =\n search.file === hash\n ? this.props.history.replace\n : this.props.history.push;\n\n historyPush({\n pathname: '/view',\n search: queryString.stringify({file: hash}),\n state: {data, images: imageMap},\n });\n }\n\n /** Opens the \"Load from URL\" dialog. */\n private openLoadUrlDialog() {\n this.setState(\n Object.assign({}, this.state, {loadUrlDialogOpen: true}),\n () => this.urlInputRef.current!.focus(),\n );\n }\n\n private openWikiTreeIdDialog() {\n this.setState(\n Object.assign({}, this.state, {wikiTreeIdDialogOpen: true}),\n () => this.wikiTreeIdInputRef.current!.focus(),\n );\n }\n\n /** Cancels any of the open dialogs. */\n private handleClose() {\n this.setState(\n Object.assign({}, this.state, {\n loadUrlDialogOpen: false,\n wikiTreeIdDialogOpen: false,\n }),\n );\n }\n\n /** Load button clicked in the \"Load from URL\" dialog. */\n private handleLoad() {\n this.setState(\n Object.assign({}, this.state, {\n loadUrlDialogOpen: false,\n }),\n );\n if (this.state.url) {\n analyticsEvent('url_selected');\n this.props.history.push({\n pathname: '/view',\n search: queryString.stringify({url: this.state.url}),\n });\n }\n }\n\n /** Select button clicked in the \"Select WikiTree ID\" dialog. */\n private handleSelectWikiTreeId() {\n this.setState(\n Object.assign({}, this.state, {\n wikiTreeIdDialogOpen: false,\n }),\n );\n if (this.state.wikiTreeId) {\n analyticsEvent('wikitree_id_selected');\n const search = queryString.parse(this.props.location.search);\n const standalone =\n search.standalone !== undefined ? search.standalone : true;\n this.props.history.push({\n pathname: '/view',\n search: queryString.stringify({\n indi: this.state.wikiTreeId,\n source: 'wikitree',\n standalone,\n }),\n });\n }\n }\n\n /** Called when the URL input is typed into. */\n private handleUrlChange(value: string) {\n this.setState(\n Object.assign({}, this.state, {\n url: value,\n }),\n );\n }\n\n /** Called when the URL input is typed into. */\n private handleWikiTreeIdChange(value: string) {\n this.setState(\n Object.assign({}, this.state, {\n wikiTreeId: value,\n }),\n );\n }\n\n /** On search input change. */\n private handleSearch(input: string | undefined) {\n if (!input) {\n return;\n }\n const results = this.searchIndex!.search(input).map((result) =>\n displaySearchResult(result, this.context.intl),\n );\n this.setState(Object.assign({}, this.state, {searchResults: results}));\n }\n\n /** On search result selected. */\n private handleResultSelect(id: string) {\n analyticsEvent('search_result_selected');\n this.props.eventHandlers.onSelection({id, generation: 0});\n this.searchRef!.setValue('');\n }\n\n private initializeSearchIndex() {\n if (this.props.data) {\n this.searchIndex = buildSearchIndex(this.props.data);\n }\n }\n\n changeView(view: string) {\n const location = this.props.location;\n const search = queryString.parse(location.search);\n if (search.view !== view) {\n search.view = view;\n location.search = queryString.stringify(search);\n this.props.history.push(location);\n }\n }\n\n /**\n * Redirect to the WikiTree Apps login page with a return URL pointing to\n * Topola Viewer hosted on apps.wikitree.com.\n */\n private wikiTreeLogin() {\n const wikiTreeTopolaUrl =\n 'https://apps.wikitree.com/apps/wiech13/topola-viewer';\n // Append '&' because the login page appends '?authcode=...' to this URL.\n // TODO: remove ?authcode if it is in the current URL.\n const returnUrl = `${wikiTreeTopolaUrl}${window.location.hash}&`;\n this.wikiTreeReturnUrlRef.current!.value = returnUrl;\n this.wikiTreeLoginFormRef.current!.submit();\n }\n\n private checkWikiTreeLoginState() {\n const wikiTreeLoginState =\n Cookies.get('wikidb_wtb_UserID') !== undefined\n ? WikiTreeLoginState.LOGGED_IN\n : WikiTreeLoginState.NOT_LOGGED_IN;\n if (this.state.wikiTreeLoginState !== wikiTreeLoginState) {\n const wikiTreeLoginUsername = Cookies.get('wikidb_wtb_UserName');\n this.setState(\n Object.assign({}, this.state, {\n wikiTreeLoginState,\n wikiTreeLoginUsername,\n }),\n );\n }\n }\n\n async componentDidMount() {\n this.checkWikiTreeLoginState();\n this.initializeSearchIndex();\n }\n\n componentDidUpdate(prevProps: Props) {\n this.checkWikiTreeLoginState();\n if (prevProps.data !== this.props.data) {\n this.initializeSearchIndex();\n }\n }\n\n private loadFromUrlModal() {\n return (\n this.handleClose()}\n centered={false}\n >\n \n \n \n \n \n this.handleClose()}>\n \n \n this.handleLoad()}>\n \n \n \n \n );\n }\n\n private enterWikiTreeId(event: React.MouseEvent, id: string) {\n event.preventDefault(); // Do not follow link in href.\n ((this.wikiTreeIdInputRef.current as unknown) as {\n inputRef: HTMLInputElement;\n }).inputRef.value = id;\n this.handleWikiTreeIdChange(id);\n this.wikiTreeIdInputRef.current!.focus();\n }\n\n private wikiTreeIdModal() {\n return (\n this.handleClose()}\n centered={false}\n >\n \n \n txt}\n />\n \n \n \n \n \n this.handleClose()}>\n \n \n this.handleSelectWikiTreeId()}>\n \n \n \n \n );\n }\n\n private search() {\n return (\n , data: SearchProps) =>\n this.handleSearch(data.value),\n 200,\n )}\n onResultSelect={(_, data) => this.handleResultSelect(data.result.id)}\n results={this.state.searchResults}\n noResultsMessage={this.context.intl.formatMessage({\n id: 'menu.search.no_results',\n defaultMessage: 'No results found',\n })}\n placeholder={this.context.intl.formatMessage({\n id: 'menu.search.placeholder',\n defaultMessage: 'Search for people',\n })}\n selectFirstResult={true}\n ref={(ref) =>\n (this.searchRef = (ref as unknown) as {\n setValue(value: string): void;\n })\n }\n id=\"search\"\n />\n );\n }\n\n private chartMenus(screenSize: ScreenSize) {\n if (!this.props.showingChart) {\n return null;\n }\n const chartTypeItems = (\n <>\n this.changeView('hourglass')}>\n \n \n \n {this.props.allowAllRelativesChart ? (\n this.changeView('relatives')}>\n \n \n \n ) : null}\n this.changeView('fancy')}>\n \n \n \n >\n );\n switch (screenSize) {\n case ScreenSize.LARGE:\n return (\n <>\n this.props.eventHandlers.onPrint()}>\n \n \n \n\n \n \n \n \n }\n className=\"item\"\n >\n \n this.props.eventHandlers.onDownloadPdf()}\n >\n \n \n this.props.eventHandlers.onDownloadPng()}\n >\n \n \n this.props.eventHandlers.onDownloadSvg()}\n >\n \n \n \n \n\n \n \n \n \n }\n className=\"item\"\n >\n {chartTypeItems} \n \n {this.search()}\n >\n );\n\n case ScreenSize.SMALL:\n return (\n <>\n this.props.eventHandlers.onPrint()}>\n \n \n \n\n \n\n this.props.eventHandlers.onDownloadPdf()}\n >\n \n \n \n this.props.eventHandlers.onDownloadPng()}\n >\n \n \n \n this.props.eventHandlers.onDownloadSvg()}\n >\n \n \n \n\n \n {chartTypeItems}\n \n >\n );\n }\n }\n\n private title() {\n return (\n \n Topola Genealogy \n \n );\n }\n\n private fileMenus(screenSize: ScreenSize) {\n const loadWikiTreeItem = (\n <>\n \n \n >\n );\n\n // In standalone WikiTree mode, show only the \"Select WikiTree ID\" menu.\n if (!this.props.standalone && this.props.showWikiTreeMenus) {\n switch (screenSize) {\n case ScreenSize.LARGE:\n return (\n <>\n this.openWikiTreeIdDialog()}>\n {loadWikiTreeItem}\n \n {this.wikiTreeIdModal()}\n >\n );\n case ScreenSize.SMALL:\n return (\n <>\n this.openWikiTreeIdDialog()}>\n {loadWikiTreeItem}\n \n \n {this.wikiTreeIdModal()}\n >\n );\n }\n }\n\n // Don't show \"open\" menus in non-standalone mode.\n if (!this.props.standalone) {\n return null;\n }\n\n const openFileItem = (\n <>\n \n \n >\n );\n const loadUrlItem = (\n <>\n \n \n >\n );\n const commonElements = (\n <>\n {this.loadFromUrlModal()}\n {this.wikiTreeIdModal()}\n this.handleUpload(e)}\n />\n >\n );\n switch (screenSize) {\n case ScreenSize.LARGE:\n // Show dropdown if chart is shown, otherwise show individual menu\n // items.\n const menus = this.props.showingChart ? (\n \n \n \n \n }\n className=\"item\"\n >\n \n \n {openFileItem}\n \n this.openLoadUrlDialog()}>\n {loadUrlItem}\n \n this.openWikiTreeIdDialog()}>\n {loadWikiTreeItem}\n \n \n \n ) : (\n <>\n \n {openFileItem} \n \n this.openLoadUrlDialog()}>\n {loadUrlItem}\n \n this.openWikiTreeIdDialog()}>\n {loadWikiTreeItem}\n \n >\n );\n return (\n <>\n {menus}\n {commonElements}\n >\n );\n\n case ScreenSize.SMALL:\n return (\n <>\n \n {openFileItem}\n \n this.openLoadUrlDialog()}>\n {loadUrlItem}\n \n this.openWikiTreeIdDialog()}>\n {loadWikiTreeItem}\n \n \n {commonElements}\n >\n );\n }\n }\n\n private wikiTreeLoginMenu(screenSize: ScreenSize) {\n if (!this.props.showWikiTreeMenus) {\n return null;\n }\n switch (this.state.wikiTreeLoginState) {\n case WikiTreeLoginState.NOT_LOGGED_IN:\n const loginForm = (\n \n );\n switch (screenSize) {\n case ScreenSize.LARGE:\n return (\n this.wikiTreeLogin()}>\n \n \n {loginForm}\n \n );\n\n case ScreenSize.SMALL:\n return (\n <>\n this.wikiTreeLogin()}>\n \n \n {loginForm}\n \n \n >\n );\n }\n break;\n\n case WikiTreeLoginState.LOGGED_IN:\n const tooltip = this.state.wikiTreeLoginUsername\n ? this.context.intl.formatMessage(\n {\n id: 'menu.wikitree_popup_username',\n defaultMessage: 'Logged in to WikiTree as {username}',\n },\n {username: this.state.wikiTreeLoginUsername},\n )\n : this.context.intl.formatMessage({\n id: 'menu.wikitree_popup',\n defaultMessage: 'Logged in to WikiTree',\n });\n switch (screenSize) {\n case ScreenSize.LARGE:\n return (\n \n \n \n \n );\n\n case ScreenSize.SMALL:\n return (\n <>\n \n \n \n \n \n >\n );\n\n default:\n return null;\n }\n }\n }\n\n private mobileMenus() {\n return (\n <>\n \n \n \n }\n className=\"item\"\n icon={null}\n >\n \n {this.fileMenus(ScreenSize.SMALL)}\n {this.chartMenus(ScreenSize.SMALL)}\n {this.wikiTreeLoginMenu(ScreenSize.SMALL)}\n\n \n \n \n \n \n {this.props.standalone ? (\n {this.title()}\n ) : (\n this.title()\n )}\n >\n );\n }\n\n private desktopMenus() {\n return (\n <>\n {this.props.standalone ? {this.title()} : null}\n {this.fileMenus(ScreenSize.LARGE)}\n {this.chartMenus(ScreenSize.LARGE)}\n \n {this.wikiTreeLoginMenu(ScreenSize.LARGE)}\n \n \n \n \n >\n );\n }\n\n render() {\n return (\n <>\n \n {this.desktopMenus()}\n \n \n {this.mobileMenus()}\n \n >\n );\n }\n}\n","import * as H from 'history';\nimport * as queryString from 'query-string';\nimport * as React from 'react';\nimport {analyticsEvent} from './analytics';\nimport {Chart, ChartType} from './chart';\nimport {Details} from './details';\nimport {FormattedMessage} from 'react-intl';\nimport {getSelection, loadFromUrl, loadGedcom} from './load_data';\nimport {getSoftware, TopolaData} from './gedcom_util';\nimport {IndiInfo} from 'topola';\nimport {intlShape} from 'react-intl';\nimport {Intro} from './intro';\nimport {Loader, Message, Portal, Responsive} from 'semantic-ui-react';\nimport {loadWikiTree} from './wikitree';\nimport {Redirect, Route, RouteComponentProps, Switch} from 'react-router-dom';\nimport {TopBar} from './top_bar';\n\n/** Shows an error message in the middle of the screen. */\nfunction ErrorMessage(props: {message?: string}) {\n return (\n \n \n \n \n {props.message}
\n \n );\n}\n\ninterface ErrorPopupProps {\n message?: string;\n open: boolean;\n onDismiss: () => void;\n}\n\n/**\n * Shows a dismissable error message in the bottom left corner of the screen.\n */\nfunction ErrorPopup(props: ErrorPopupProps) {\n return (\n \n \n \n \n \n {props.message}
\n \n \n );\n}\n\n/**\n * Message types used in embedded mode.\n * When the parent is ready to receive messages, it sends PARENT_READY.\n * When the child (this app) is ready to receive messages, it sends READY.\n * When the child receives PARENT_READY, it sends READY.\n * When the parent receives READY, it sends data in a GEDCOM message.\n */\nenum EmbeddedMessageType {\n GEDCOM = 'gedcom',\n READY = 'ready',\n PARENT_READY = 'parent_ready',\n}\n\n/** Message sent to parent or received from parent in embedded mode. */\ninterface EmbeddedMessage {\n message: EmbeddedMessageType;\n}\n\ninterface GedcomMessage extends EmbeddedMessage {\n message: EmbeddedMessageType.GEDCOM;\n gedcom?: string;\n}\n\n/** Interface encapsulating functions specific for a data source. */\ninterface DataSource {\n /**\n * Returns true if the application is now loading a completely new data set\n * and the existing one should be wiped.\n */\n isNewData(args: Arguments, state: State): boolean;\n /** Loads data from the data source. */\n loadData(args: Arguments): Promise;\n}\n\n/** Files opened from the local computer. */\nclass UploadedDataSource implements DataSource {\n isNewData(args: Arguments, state: State): boolean {\n return (\n args.hash !== state.hash ||\n !!(args.gedcom && !state.loading && !state.data)\n );\n }\n\n async loadData(args: Arguments): Promise {\n try {\n const data = await loadGedcom(args.hash!, args.gedcom, args.images);\n const software = getSoftware(data.gedcom.head);\n analyticsEvent('upload_file_loaded', {\n event_label: software,\n event_value: (args.images && args.images.size) || 0,\n });\n return data;\n } catch (error) {\n analyticsEvent('upload_file_error');\n throw error;\n }\n }\n}\n\n/** GEDCOM file loaded by pointing to a URL. */\nclass GedcomUrlDataSource implements DataSource {\n isNewData(args: Arguments, state: State): boolean {\n return args.url !== state.url;\n }\n\n async loadData(args: Arguments): Promise {\n try {\n const data = await loadFromUrl(args.url!, args.handleCors);\n const software = getSoftware(data.gedcom.head);\n analyticsEvent('upload_file_loaded', {event_label: software});\n return data;\n } catch (error) {\n analyticsEvent('url_file_error');\n throw error;\n }\n }\n}\n\n/** Loading data from the WikiTree API. */\nclass WikiTreeDataSource implements DataSource {\n isNewData(args: Arguments, state: State): boolean {\n if (state.selection && state.selection.id === args.indi) {\n // Selection unchanged -> don't reload.\n return false;\n }\n if (\n state.data &&\n state.data.chartData.indis.some((indi) => indi.id === args.indi)\n ) {\n // New selection exists in current view -> animate instead of reloading.\n return false;\n }\n return true;\n }\n\n async loadData(args: Arguments): Promise {\n try {\n const data = await loadWikiTree(args.indi!, args.authcode);\n analyticsEvent('wikitree_loaded');\n return data;\n } catch (error) {\n analyticsEvent('wikitree_error');\n throw error;\n }\n }\n}\n\n/** Supported data sources. */\nenum DataSourceEnum {\n UPLOADED,\n GEDCOM_URL,\n WIKITREE,\n}\n\n/** Mapping from data source identifier to data source handler functions. */\nconst DATA_SOURCES = new Map([\n [DataSourceEnum.UPLOADED, new UploadedDataSource()],\n [DataSourceEnum.GEDCOM_URL, new GedcomUrlDataSource()],\n [DataSourceEnum.WIKITREE, new WikiTreeDataSource()],\n]);\n\n/** Arguments passed to the application, primarily through URL parameters. */\ninterface Arguments {\n showSidePanel: boolean;\n embedded: boolean;\n url?: string;\n indi?: string;\n generation?: number;\n hash?: string;\n handleCors: boolean;\n standalone: boolean;\n source?: DataSourceEnum;\n authcode?: string;\n chartType: ChartType;\n gedcom?: string;\n images?: Map;\n}\n\n/**\n * Retrieve arguments passed into the application through the URL and uploaded\n * data.\n */\nfunction getArguments(location: H.Location): Arguments {\n const search = queryString.parse(location.search);\n const getParam = (name: string) => {\n const value = search[name];\n return typeof value === 'string' ? value : undefined;\n };\n\n const parsedGen = Number(getParam('gen'));\n const view = getParam('view');\n const chartTypes = new Map([\n ['relatives', ChartType.Relatives],\n ['fancy', ChartType.Fancy],\n ]);\n const hash = getParam('file');\n const url = getParam('url');\n const source =\n getParam('source') === 'wikitree'\n ? DataSourceEnum.WIKITREE\n : hash\n ? DataSourceEnum.UPLOADED\n : url\n ? DataSourceEnum.GEDCOM_URL\n : undefined;\n return {\n showSidePanel: getParam('sidePanel') !== 'false', // True by default.\n embedded: getParam('embedded') === 'true', // False by default.\n url,\n indi: getParam('indi'),\n generation: !isNaN(parsedGen) ? parsedGen : undefined,\n hash,\n handleCors: getParam('handleCors') !== 'false', // True by default.\n standalone: getParam('standalone') !== 'false', // True by default.\n source,\n authcode: getParam('?authcode'),\n\n // Hourglass is the default view.\n chartType: chartTypes.get(view) || ChartType.Hourglass,\n\n gedcom: location.state && location.state.data,\n images: location.state && location.state.images,\n };\n}\n\n/** Returs true if the changes object has values that are different than those in state. */\nfunction hasUpdatedValues(state: T, changes: Partial | undefined) {\n if (!changes) {\n return false;\n }\n return Object.entries(changes).some(\n ([key, value]) => value !== undefined && state[key] !== value,\n );\n}\n\ninterface State {\n /** Loaded data. */\n data?: TopolaData;\n /** Selected individual. */\n selection?: IndiInfo;\n /** Hash of the GEDCOM contents. */\n hash?: string;\n /** Error to display. */\n error?: string;\n /** True if data is currently being loaded. */\n loading: boolean;\n /** URL of the data that is loaded or is being loaded. */\n url?: string;\n /** Whether the side panel is shown. */\n showSidePanel?: boolean;\n /** Whether the app is in embedded mode, i.e. embedded in an iframe. */\n embedded: boolean;\n /** Whether the app is in standalone mode, i.e. showing 'open file' menus. */\n standalone: boolean;\n /** Type of displayed chart. */\n chartType: ChartType;\n /** Whether to show the error popup. */\n showErrorPopup: boolean;\n /** Source of the data. */\n source?: DataSourceEnum;\n loadingMore?: boolean;\n}\n\nexport class App extends React.Component {\n state: State = {\n loading: false,\n embedded: false,\n standalone: true,\n chartType: ChartType.Hourglass,\n showErrorPopup: false,\n };\n chartRef: Chart | null = null;\n\n /** Make intl appear in this.context. */\n static contextTypes = {\n intl: intlShape,\n };\n\n /** Sets the state with a new individual selection and chart type. */\n private updateDisplay(\n selection: IndiInfo,\n otherStateChanges?: Partial,\n ) {\n if (\n !this.state.selection ||\n this.state.selection.id !== selection.id ||\n this.state.selection!.generation !== selection.generation ||\n hasUpdatedValues(this.state, otherStateChanges)\n ) {\n this.setState(\n Object.assign({}, this.state, {selection}, otherStateChanges),\n );\n }\n }\n\n /** Sets error message after data load failure. */\n private setError(error: string) {\n this.setState(\n Object.assign({}, this.state, {\n error: error,\n loading: false,\n }),\n );\n }\n\n private async onMessage(message: EmbeddedMessage) {\n if (message.message === EmbeddedMessageType.PARENT_READY) {\n // Parent didn't receive the first 'ready' message, so we need to send it again.\n window.parent.postMessage({message: EmbeddedMessageType.READY}, '*');\n } else if (message.message === EmbeddedMessageType.GEDCOM) {\n const gedcom = (message as GedcomMessage).gedcom;\n if (!gedcom) {\n return;\n }\n try {\n const data = await loadGedcom('', gedcom);\n const software = getSoftware(data.gedcom.head);\n analyticsEvent('embedded_file_loaded', {\n event_label: software,\n });\n // Set state with data.\n this.setState(\n Object.assign({}, this.state, {\n data,\n selection: getSelection(data.chartData),\n error: undefined,\n loading: false,\n }),\n );\n } catch (error) {\n analyticsEvent('embedded_file_error');\n this.setError(error.message);\n }\n }\n }\n\n componentDidMount() {\n this.componentDidUpdate();\n }\n\n async componentDidUpdate() {\n if (this.props.location.pathname !== '/view') {\n return;\n }\n\n const args = getArguments(this.props.location);\n\n if (args.embedded && !this.state.embedded) {\n this.setState(\n Object.assign({}, this.state, {\n embedded: true,\n standalone: false,\n showSidePanel: args.showSidePanel,\n }),\n );\n // Notify the parent window that we are ready.\n window.parent.postMessage('ready', '*');\n window.addEventListener('message', (data) => this.onMessage(data.data));\n }\n if (args.embedded) {\n // If the app is embedded, do not run the normal loading code.\n return;\n }\n\n const dataSource = DATA_SOURCES.get(args.source!);\n\n if (!dataSource) {\n this.props.history.replace({pathname: '/'});\n } else if (\n (!this.state.loading && !this.state.data && !this.state.error) ||\n args.source !== this.state.source ||\n dataSource.isNewData(args, this.state)\n ) {\n // Set loading state.\n this.setState(\n Object.assign({}, this.state, {\n data: undefined,\n selection: {id: args.indi},\n hash: args.hash,\n error: undefined,\n loading: true,\n url: args.url,\n standalone: args.standalone,\n chartType: args.chartType,\n source: args.source,\n }),\n );\n try {\n const data = await dataSource.loadData(args);\n\n // Set state with data.\n this.setState(\n Object.assign({}, this.state, {\n data,\n hash: args.hash,\n selection: getSelection(data.chartData, args.indi, args.generation),\n error: undefined,\n loading: false,\n url: args.url,\n showSidePanel: args.showSidePanel,\n standalone: args.standalone,\n chartType: args.chartType,\n source: args.source,\n }),\n );\n } catch (error) {\n this.setError(error.message);\n }\n } else if (this.state.data && this.state.selection) {\n // Update selection if it has changed in the URL.\n const selection = getSelection(\n this.state.data.chartData,\n args.indi,\n args.generation,\n );\n const loadMoreFromWikitree =\n args.source === DataSourceEnum.WIKITREE &&\n (!this.state.selection || this.state.selection.id !== selection.id);\n this.updateDisplay(selection, {\n chartType: args.chartType,\n loadingMore: loadMoreFromWikitree || undefined,\n });\n if (loadMoreFromWikitree) {\n const data = await loadWikiTree(args.indi!);\n this.setState(\n Object.assign({}, this.state, {\n data,\n hash: args.hash,\n selection: getSelection(data.chartData, args.indi, args.generation),\n error: undefined,\n loading: false,\n url: args.url,\n showSidePanel: args.showSidePanel,\n standalone: args.standalone,\n chartType: args.chartType,\n source: args.source,\n loadingMore: false,\n }),\n );\n }\n }\n }\n\n /**\n * Called when the user clicks an individual box in the chart.\n * Updates the browser URL.\n */\n private onSelection = (selection: IndiInfo) => {\n analyticsEvent('selection_changed');\n if (this.state.embedded) {\n // In embedded mode the URL doesn't change.\n this.updateDisplay(selection);\n return;\n }\n const location = this.props.location;\n const search = queryString.parse(location.search);\n search.indi = selection.id;\n search.gen = String(selection.generation);\n location.search = queryString.stringify(search);\n this.props.history.push(location);\n };\n\n private onPrint = () => {\n analyticsEvent('print');\n this.chartRef && this.chartRef.print();\n };\n\n private showErrorPopup(message: string) {\n this.setState(\n Object.assign({}, this.state, {\n showErrorPopup: true,\n error: message,\n }),\n );\n }\n\n private onDownloadPdf = async () => {\n analyticsEvent('download_pdf');\n try {\n this.chartRef && (await this.chartRef.downloadPdf());\n } catch (e) {\n this.showErrorPopup(\n this.context.intl.formatMessage({\n id: 'error.failed_pdf',\n defaultMessage:\n 'Failed to generate PDF file.' +\n ' Please try with a smaller diagram or download an SVG file.',\n }),\n );\n }\n };\n\n private onDownloadPng = async () => {\n analyticsEvent('download_png');\n try {\n this.chartRef && (await this.chartRef.downloadPng());\n } catch (e) {\n this.showErrorPopup(\n this.context.intl.formatMessage({\n id: 'error.failed_png',\n defaultMessage:\n 'Failed to generate PNG file.' +\n ' Please try with a smaller diagram or download an SVG file.',\n }),\n );\n }\n };\n\n private onDownloadSvg = () => {\n analyticsEvent('download_svg');\n this.chartRef && this.chartRef.downloadSvg();\n };\n\n onDismissErrorPopup = () => {\n this.setState(\n Object.assign({}, this.state, {\n showErrorPopup: false,\n }),\n );\n };\n\n private renderMainArea = () => {\n if (this.state.data && this.state.selection) {\n return (\n \n \n {this.state.loadingMore ? (\n \n ) : null}\n (this.chartRef = ref)}\n />\n {this.state.showSidePanel ? (\n \n \n \n ) : null}\n
\n );\n }\n if (this.state.error) {\n return ;\n }\n return ;\n };\n\n render() {\n return (\n <>\n (\n \n )}\n />\n \n \n \n \n \n >\n );\n }\n}\n","import * as locale_en from 'react-intl/locale-data/en';\nimport * as locale_pl from 'react-intl/locale-data/pl';\nimport * as React from 'react';\nimport * as ReactDOM from 'react-dom';\nimport messages_pl from './translations/pl.json';\nimport {addLocaleData} from 'react-intl';\nimport {App} from './app';\nimport {detect} from 'detect-browser';\nimport {HashRouter as Router, Route} from 'react-router-dom';\nimport {IntlProvider} from 'react-intl';\nimport './index.css';\nimport 'semantic-ui-css/semantic.min.css';\nimport 'canvas-toBlob';\n\naddLocaleData([...locale_en, ...locale_pl]);\n\nconst messages = {\n pl: messages_pl,\n};\nconst language = navigator.language && navigator.language.split(/[-_]/)[0];\n\nconst browser = detect();\n\nif (browser && browser.name === 'ie') {\n ReactDOM.render(\n \n Topola Genealogy Viewer does not support Internet Explorer. Please try a\n different browser.\n
,\n document.querySelector('#root'),\n );\n} else {\n ReactDOM.render(\n \n \n \n \n ,\n document.querySelector('#root'),\n );\n}\n"],"sourceRoot":""}
\ No newline at end of file
diff --git a/static/js/main.2b6d3b8f.chunk.js b/static/js/main.2b6d3b8f.chunk.js
deleted file mode 100644
index aa63cc4..0000000
--- a/static/js/main.2b6d3b8f.chunk.js
+++ /dev/null
@@ -1,2 +0,0 @@
-(window.webpackJsonp=window.webpackJsonp||[]).push([[0],{169:function(e,t,n){e.exports=n.p+"static/media/topola.060eef13.jpg"},253:function(e){e.exports={"menu.open":"Otw\xf3rz","menu.open_file":"Otw\xf3rz plik","menu.load_from_url":"Otw\xf3rz URL","menu.select_wikitree_id":"Wybierz WikiTree ID","menu.print":"Drukuj","menu.download":"Pobierz","menu.pdf_file":"Plik PDF","menu.png_file":"Plik PNG","menu.svg_file":"Plik SVG","menu.download_pdf":"Pobierz PDF","menu.download_png":"Pobierz PNG","menu.download_svg":"Pobierz SVG","menu.view":"Widok","menu.hourglass":"Wykres klepsydrowy","menu.relatives":"Wszyscy krewni","menu.fancy":"Ozdobne drzewo (eksperymentalne)","menu.wikitree_login":"Zaloguj do WikiTree","menu.wikitree_logged_in":"Zalogowano","menu.wikitree_popup_username":"Zalogowano do WikiTree jako {username}","menu.wikitree_popup":"Zalogowano do WikiTree","menu.github":"Projekt na stronie GitHub","menu.search.placeholder":"Szukaj osoby","menu.search.no_results":"Brak wynik\xf3w","intro.title":"Topola Genealogy","intro.description":"Topola Genealogy pozwala przegl\u0105da\u0107 drzewo genealogiczne w interaktywny spos\xf3b.","intro.instructions":"Kliknij OTW\xd3RZ PLIK lub OTW\xd3RZ URL, aby za\u0142adowa\u0107 plik GEDCOM. Wi\u0119kszo\u015b\u0107 program\xf3w genealogicznych posiada funkcj\u0119 eksportu do pliku GEDCOM.","intro.examples":"Poni\u017cej jest kilka przyk\u0142ad\xf3w znalezionych w Internecie:","intro.from":"\u017ar\xf3d\u0142o:","intro.privacy":"Prywatno\u015b\u0107","intro.privacy_note":'U\u017cywaj\u0105c funkcji "Otw\xf3rz plik", Twoje dane nie s\u0105 nigdzie wysy\u0142ane i pozostaj\u0105 na Twoim komputerze. U\u017cywaj\u0105c funkcji "Otw\xf3rz URL", dane z podanego adresu przesy\u0142ane s\u0105 przez us\u0142ug\u0119 {link} w celu umo\u017cliwienia za\u0142adowania danych z innej domeny (CORS).',"load_from_url.title":"Otw\xf3rz z adresu URL","load_from_url.comment":"Dane z podanego adresu URL zostan\u0105 za\u0142adowane poprzez us\u0142ug\u0119 {link} w celu unikni\u0119cia problem\xf3w z CORS.","load_from_url.cancel":"Anuluj","load_from_url.load":"Otw\xf3rz","select_wikitree_id.title":"Podaj WikiTree ID","select_wikitree_id.comment":"Wpisz identyfikator profilu {wikiTreeLink}. Przyk\u0142ady: {example1}, {example2}","select_wikitree_id.cancel":"Anuluj","select_wikitree_id.load":"Otw\xf3rz","gedcom.BAPM":"Chrzest","gedcom.BIRT":"Narodziny","gedcom.BURI":"Pogrzeb","gedcom.CENS":"Spis ludno\u015bci","gedcom.CHR":"Chrzest","gedcom.DEAT":"\u015amier\u0107","gedcom.DSCR":"Opis","gedcom.EMAIL":"E-mail","gedcom.EVEN":"Wydarzenie","gedcom.OCCU":"Zaw\xf3d","gedcom.RIN":"ID","gedcom.TITL":"Tytu\u0142","gedcom.WWW":"Strona WWW","gedcom._UPD":"Ostatnia aktualizacja","date.abt":"oko\u0142o","date.cal":"wyliczone","date.est":"oszacowane","date.between":"mi\u0119dzy {from} a {to}","date.after":"po {from}","date.before":"przed {to}","error.error":"B\u0142\u0105d","error.failed_pdf":"Nie uda\u0142o si\u0119 utworzy\u0107 pliku PDF. Spr\xf3buj jeszcze raz z mniejszym diagramem lub pobierz plik SVG.","error.failed_png":"Nie uda\u0142o si\u0119 utworzy\u0107 pliku PNG. Spr\xf3buj jeszcze raz z mniejszym diagramem lub pobierz plik SVG.","error.failed_to_load_file":"B\u0142\u0105d wczytywania pliku"}},254:function(e,t){},297:function(e,t,n){e.exports=n(528)},309:function(e,t){},315:function(e,t){},525:function(e,t,n){},528:function(e,t,n){"use strict";n.r(t);var a=n(44),r=n(251),i=n(252),o=n(0),s=n(64),l=n(253),c=n(9),u=n(39),d=n(37),m=n(40),f=n(65),h=n(15),p=n.n(h),g=n(18),v=n(27),w=n(28),E=n(31);function k(e,t){window.gtag("event",e,t)}var y,b=n(17),O=n(256),D=n.n(O),T=n(168),M=n(33),S=n(553);function I(){var e=b.select("#svgContainer").node(),t=e.scrollLeft+e.clientWidth/2,n=e.scrollTop+e.clientHeight/2,a=b.zoomTransform(e).k;b.select(e).call(b.zoom().translateTo,t/a,n/a)}function x(e){var t=new FileReader;return t.readAsDataURL(e),new Promise(function(e,n){t.onload=function(t){return e(t.target.result)}})}function _(e){return j.apply(this,arguments)}function j(){return(j=Object(g.a)(p.a.mark(function e(t){var n,a,r,i;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(n=t.href.baseVal){e.next=3;break}return e.abrupt("return");case 3:return e.prev=3,e.next=6,fetch(n);case 6:return a=e.sent,e.next=9,a.blob();case 9:return r=e.sent,e.next=12,x(r);case 12:i=e.sent,t.href.baseVal=i,e.next=19;break;case 16:e.prev=16,e.t0=e.catch(3),console.warn("Failed to load image:",e.t0);case 19:case"end":return e.stop()}},e,null,[[3,16]])}))).apply(this,arguments)}function R(e){return L.apply(this,arguments)}function L(){return(L=Object(g.a)(p.a.mark(function e(t){var n;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return n=Array.from(t.getElementsByTagName("image")),e.next=3,Promise.all(n.map(_));case 3:case"end":return e.stop()}},e)}))).apply(this,arguments)}function C(e){var t=new Image;return t.src=URL.createObjectURL(e),new Promise(function(e,n){t.addEventListener("load",function(){return e(t)})})}function N(e){var t=document.createElement("canvas");t.width=2*e.width,t.height=2*e.height;var n=t.getContext("2d"),a=n.fillStyle;return n.fillStyle="white",n.fillRect(0,0,t.width,t.height),n.fillStyle=a,n.drawImage(e,0,0,t.width,t.height),t}function z(e,t){return new Promise(function(n,a){e.toBlob(function(e){e?n(e):a()},t)})}!function(e){e[e.Hourglass=0]="Hourglass",e[e.Relatives=1]="Relatives",e[e.Fancy=2]="Fancy"}(y||(y={}));var W=function(e){function t(){var e,n;Object(v.a)(this,t);for(var a=arguments.length,r=new Array(a),i=0;i0&&void 0!==arguments[0]?arguments[0]:{initialRender:!1};if(t.initialRender||!this.animating){t.initialRender?(b.select("#chart").node().innerHTML="",this.chart=Object(M.createChart)({json:this.props.data,chartType:this.getChartType(),renderer:this.getRendererType(),svgSelector:"#chart",indiCallback:function(t){return e.props.onSelection(t)},animate:!0,updateSvgSize:!1,locale:this.context.intl.locale})):this.chart.setData(this.props.data);var n=this.chart.render({startIndi:this.props.selection.id,baseGeneration:this.props.selection.generation}),a=b.select("#chartSvg"),r=b.select("#svgContainer").node(),i=b.zoomTransform(r).k,o=b.min([1,i,r.clientWidth/n.size[0],r.clientHeight/n.size[1]]),s=[b.max([.1,o]),2];this.zoomBehavior=b.zoom().scaleExtent(s).translateExtent([[0,0],n.size]).on("zoom",function(){return function(e){var t=b.select("#svgContainer").node(),n=b.event.transform.k,a=b.max([0,(t.clientWidth-e[0]*n)/2]),r=b.max([0,(t.clientHeight-e[1]*n)/2]);b.select("#chartSvg").attr("width",e[0]*n).attr("height",e[1]*n).attr("transform","translate(".concat(a,", ").concat(r,")")),b.select("#chart").attr("transform","scale(".concat(n,")")),t.scrollLeft=-b.event.transform.x,t.scrollTop=-b.event.transform.y}(n.size)}),b.select(r).on("scroll",I).call(this.zoomBehavior);var l,c,u=r.clientWidth/2-n.origin[0]*i,d=r.clientHeight/2-n.origin[1]*i,m=b.max([0,(r.clientWidth-n.size[0]*i)/2]),f=b.max([0,(r.clientHeight-n.size[1]*i)/2]),h=a.transition().delay(200).duration(500);(t.initialRender?a:h).attr("transform","translate(".concat(m,", ").concat(f,")")).attr("width",n.size[0]*i).attr("height",n.size[1]*i),t.initialRender?(r.scrollLeft=-u,r.scrollTop=-d):h.tween("scrollLeft",(c=-u,function(){var e=b.interpolateNumber(r.scrollLeft,c);return function(t){r.scrollLeft=e(t)}})).tween("scrollTop",(l=-d,function(){var e=b.interpolateNumber(r.scrollTop,l);return function(t){r.scrollTop=e(t)}})),this.animating=!0,n.animationPromise.then(function(){e.animating=!1,e.rerenderRequired&&(e.rerenderRequired=!1,e.renderChart({initialRender:!1}))})}else this.rerenderRequired=!0}},{key:"componentDidMount",value:function(){this.renderChart({initialRender:!0})}},{key:"componentDidUpdate",value:function(e){var t=this.props.chartType!==e.chartType;this.renderChart({initialRender:t})}},{key:"render",value:function(){var e=this;return o.createElement("div",{id:"svgContainer"},o.createElement(S.a,{minWidth:768,className:"zoom"},o.createElement("button",{className:"zoom-in",onClick:function(){return e.zoom(1.3)}},"+"),o.createElement("button",{className:"zoom-out",onClick:function(){return e.zoom(1/1.3)}},"\u2212")),o.createElement("svg",{id:"chartSvg"},o.createElement("g",{id:"chart"})))}},{key:"getStrippedSvg",value:function(){var e=document.getElementById("chartSvg").cloneNode(!0);e.removeAttribute("transform");var t=b.select("#svgContainer").node(),n=b.zoomTransform(t).k;return e.setAttribute("width",String(Number(e.getAttribute("width"))/n)),e.setAttribute("height",String(Number(e.getAttribute("height"))/n)),e.querySelector("#chart").removeAttribute("transform"),e}},{key:"getSvgContents",value:function(){return(new XMLSerializer).serializeToString(this.getStrippedSvg())}},{key:"getSvgContentsWithInlinedImages",value:function(){var e=Object(g.a)(p.a.mark(function e(){var t;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.getStrippedSvg(),e.next=3,R(t);case 3:return e.abrupt("return",(new XMLSerializer).serializeToString(t));case 4:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"print",value:function(){var e=this,t=document.createElement("iframe");t.style.position="absolute",t.style.top="-1000px",t.style.left="-1000px",t.onload=function(){t.contentDocument.open(),t.contentDocument.write(e.getSvgContents()),t.contentDocument.close(),setTimeout(function(){t.contentWindow.focus(),t.contentWindow.print(),t.parentNode.removeChild(t)},500)},document.body.appendChild(t)}},{key:"downloadSvg",value:function(){var e=Object(g.a)(p.a.mark(function e(){var t,n;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.getSvgContentsWithInlinedImages();case 2:t=e.sent,n=new Blob([t],{type:"image/svg+xml"}),Object(T.saveAs)(n,"topola.svg");case 5:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"drawOnCanvas",value:function(){var e=Object(g.a)(p.a.mark(function e(){var t,n;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.getSvgContentsWithInlinedImages();case 2:return t=e.sent,n=new Blob([t],{type:"image/svg+xml"}),e.t0=N,e.next=7,C(n);case 7:return e.t1=e.sent,e.next=10,(0,e.t0)(e.t1);case 10:return e.abrupt("return",e.sent);case 11:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"downloadPng",value:function(){var e=Object(g.a)(p.a.mark(function e(){var t,n;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.drawOnCanvas();case 2:return t=e.sent,e.next=5,z(t,"image/png");case 5:n=e.sent,Object(T.saveAs)(n,"topola.png");case 7:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"downloadPdf",value:function(){var e=Object(g.a)(p.a.mark(function e(){var t,n;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.drawOnCanvas();case 2:t=e.sent,(n=new D.a({orientation:t.width>t.height?"l":"p",unit:"pt",format:[t.width,t.height]})).addImage(t,"PNG",0,0,t.width,t.height,"NONE"),n.save("topola.pdf");case 6:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()}]),t}(o.PureComponent);W.contextTypes={intl:c.d};var P=n(259),A=n.n(P),F=n(260),U=n.n(F),G=n(164);function H(e){return e.substring(1,e.length-1)}function B(e){var t=new Map;return e.indis.forEach(function(e){t.set(e.id,e)}),t}function V(e){var t=new Map;return e.fams.forEach(function(e){t.set(e.id,e)}),t}function K(e){var t=e.find(function(e){return"HEAD"===e.tag}),n={},a={},r={};return e.forEach(function(e){"INDI"===e.tag?n[H(e.pointer)]=e:"FAM"===e.tag?a[H(e.pointer)]=e:e.pointer&&(r[H(e.pointer)]=e)}),{head:t,indis:n,fams:a,other:r}}function J(e,t){return et?1:0}function q(e,t){var n=e&&(e.date||e.dateRange&&e.dateRange.from),a=t&&(t.date||t.dateRange&&t.dateRange.from);return n&&n.year&&a&&a.year?n.year!==a.year?n.year-a.year:n.month&&a.month?n.month!==a.month?n.month-a.month:n.day&&a.day&&n.day!==a.day?n.month-a.month:0:0:0}function Z(e){var t=function(e){var t=B(e);return function(e,n){var a=t.get(e),r=t.get(n);return q(a&&a.birth,r&&r.birth)||J(e,n)}}(e),n=e.fams.map(function(e){return function(e,t){if(!e.children)return e;var n=e.children.sort(t);return Object.assign({},e,{children:n})}(e,t)});return Object.assign({},e,{fams:n})}function Y(e){var t=function(e){var t=V(e);return function(e,n){var a=t.get(e),r=t.get(n);return q(a&&a.marriage,r&&r.marriage)||J(e,n)}}(e),n=e.indis.map(function(e){return function(e,t){if(!e.fams)return e;var n=e.fams.sort(t);return Object.assign({},e,{fams:n})}(e,t)});return Object.assign({},e,{indis:n})}function X(e){return Y(Z(e))}var $=[".jpg",".png",".gif"];function Q(e,t){if(!e.images||0===e.images.length)return e;var n=[];return e.images.forEach(function(e){var a=e.url.match(/[^/\\]*$/)[0];t.has(a)?n.push({url:t.get(a),title:e.title}):e.url.startsWith("http")&&function(e){var t=e.toLowerCase();return $.some(function(e){return t.endsWith(e)})}(e.url)&&n.push(e)}),Object.assign({},e,{images:n})}function ee(e,t){var n=e.indis.map(function(e){return Q(e,t)});return Object.assign({},e,{indis:n})}function te(e){var t=e&&e.tree&&e.tree.find(function(e){return"SOUR"===e.tag}),n=t&&t.tree&&t.tree.find(function(e){return"NAME"===e.tag});return n&&n.data||null}var ne=new Map([["abt","about"],["cal","calculated"],["est","estimated"]]);function ae(e,t){var n=void 0!==e.day,a=void 0!==e.month,r=void 0!==e.year;if(!n&&!a&&!r)return e.text||"";var i=new Date(r?e.year:0,a?e.month-1:0,n?e.day:1),o=e.qualifier&&e.qualifier.toLowerCase(),s={day:n?"numeric":void 0,month:a?"long":void 0,year:r?"numeric":void 0};return[o&&t.formatMessage({id:"date.".concat(o),defaultMessage:ne.get(o)||o}),new Intl.DateTimeFormat(t.locale,s).format(i)].join(" ")}function re(e,t){return e?e.date?ae(e.date,t):e.dateRange?function(e,t){var n=e.from,a=e.to,r=n&&ae(n,t),i=a&&ae(a,t);return r&&i?t.formatMessage({id:"date.between",defaultMessage:"between {from} and {to}"},{from:r,to:i}):r?t.formatMessage({id:"date.after",defaultMessage:"after {from}"},{from:r}):i?t.formatMessage({id:"date.before",defaultMessage:"before {to}"},{to:i}):""}(e.dateRange,t):"":""}var ie=["BIRT","BAPM","CHR","DEAT","BURI","EVEN","CENS"],oe=["NAME","SEX","FAMC","FAMS","NOTE","SOUR"],se=new Map([["BAPM","Baptism"],["BIRT","Birth"],["BURI","Burial"],["CENS","Census"],["CHR","Christening"],["DEAT","Death"],["EMAIL","E-mail"],["EVEN","Event"],["OCCU","Occupation"],["TITL","Title"],["WWW","WWW"]]);function le(e){return o.createElement(c.a,{id:"gedcom.".concat(e),defaultMessage:se.get(e)||e})}function ce(e){return o.createElement(o.Fragment,null,e.map(function(e,t){return o.createElement("div",{key:t},o.createElement(U.a,{properties:{target:"_blank"}},e),o.createElement("br",null))}))}function ue(e){var t=[e.data];return e.tree.forEach(function(e){if("CONC"===e.tag&&e.data){var n=t.length-1;t[n]+=e.data}else"CONT"===e.tag&&e.data&&t.push(e.data)}),t}function de(e,t){var n=[];e.data&&e.data.length>1&&n.push(o.createElement("i",null,e.data));var r=e.tree.find(function(e){return"DATE"===e.tag});r&&r.data&&n.push(function(e,t){return re(Object(M.getDate)(e),t)}(r.data,t));var i=e.tree.find(function(e){return"PLAC"===e.tag});return i&&i.data&&n.push.apply(n,Object(a.a)(ue(i))),e.tree.filter(function(e){return"NOTE"===e.tag}).forEach(function(e){return ue(e).forEach(function(e){return n.push(o.createElement("i",null,e))})}),n.length?o.createElement(o.Fragment,null,o.createElement("div",{className:"ui sub header"},le(e.tag)),o.createElement("span",null,ce(n))):null}function me(e){return ce(ue(e).map(function(e,t){return o.createElement("i",{key:t},e)}))}function fe(e){return o.createElement("h2",{className:"ui header"},e.data.split("/").filter(function(e){return!!e}).map(function(e,t){return o.createElement("div",{key:t},e,o.createElement("br",null))}))}function he(e,t,n){return A()(t,function(t){return e.filter(function(e){return e.tag===t}).map(function(e){return n(e)})}).filter(function(e){return null!==e}).map(function(e,t){return o.createElement("div",{className:"ui segment",key:t},e)})}function pe(e){return e.tree.length>0||e.data&&!e.data.startsWith("@")}function ge(e){return e.filter(function(e){return!oe.includes(e.tag)&&!ie.includes(e.tag)}).filter(pe).map(function(e){return function(e){var t=[];return e.data&&t.push.apply(t,Object(a.a)(ue(e))),e.tree.filter(function(e){return"NOTE"===e.tag}).forEach(function(e){return ue(e).forEach(function(e){return t.push(o.createElement("i",null,e))})}),t.length?o.createElement(o.Fragment,null,o.createElement("div",{className:"ui sub header"},le(e.tag)),o.createElement("span",null,ce(t))):null}(e)}).filter(function(e){return null!==e}).map(function(e,t){return o.createElement("div",{className:"ui segment",key:t},e)})}var ve=function(e){function t(){return Object(v.a)(this,t),Object(u.a)(this,Object(d.a)(t).apply(this,arguments))}return Object(m.a)(t,e),Object(w.a)(t,[{key:"render",value:function(){var e=this,t=this.props.gedcom.indis[this.props.indi].tree,n=t.map(function(t){return function(e,t){if(e.data){var n=t.other[H(e.data)];if(n)return n}return e}(t,e.props.gedcom)}).filter(pe);return o.createElement("div",{className:"ui segments",id:"details"},he(t,["NAME"],fe),he(t,ie,function(t){return de(t,e.context.intl)}),ge(n),he(n,["NOTE"],me))}}]),t}(o.Component);function we(e,t,n){return{id:t&&e.indis.some(function(e){return e.id===t})?t:e.indis[0].id,generation:n||0}}function Ee(e,t,n){var a=function(e,t){var n=Object(G.parse)(e),a=Object(M.gedcomEntriesToJson)(n);if(!a||!a.indis||!a.indis.length||!a.fams||!a.fams.length)throw new Error("Failed to read GEDCOM file");return{chartData:ee(X(a),t),gedcom:K(n)}}(e,n||new Map),r=JSON.stringify(a);try{sessionStorage.setItem(t,r)}catch(i){console.warn("Failed to store data in session storage: "+i)}return a}function ke(e,t){return ye.apply(this,arguments)}function ye(){return(ye=Object(g.a)(p.a.mark(function e(t,n){var a,r,i,o;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(e.prev=0,!(a=sessionStorage.getItem(t))){e.next=4;break}return e.abrupt("return",JSON.parse(a));case 4:e.next=9;break;case 6:e.prev=6,e.t0=e.catch(0),console.warn("Failed to load data from session storage: "+e.t0);case 9:return r=n?"https://cors-anywhere.herokuapp.com/"+t:t,e.next=12,window.fetch(r);case 12:if(200===(i=e.sent).status){e.next=15;break}throw new Error(i.statusText);case 15:return e.next=17,i.text();case 17:return o=e.sent,e.abrupt("return",Ee(o,t));case 19:case"end":return e.stop()}},e,null,[[0,6]])}))).apply(this,arguments)}function be(e,t,n){return Oe.apply(this,arguments)}function Oe(){return(Oe=Object(g.a)(p.a.mark(function e(t,n,a){var r;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(e.prev=0,!(r=sessionStorage.getItem(t))){e.next=4;break}return e.abrupt("return",JSON.parse(r));case 4:e.next=9;break;case 6:e.prev=6,e.t0=e.catch(0),console.warn("Failed to load data from session storage: "+e.t0);case 9:if(n){e.next=11;break}throw new Error("Error loading data. Please upload your file again.");case 11:return e.abrupt("return",Ee(n,t,a));case 12:case"end":return e.stop()}},e,null,[[0,6]])}))).apply(this,arguments)}ve.contextTypes={intl:c.d};var De=n(169),Te=n.n(De),Me=n(543),Se=n(549),Ie=n(286),xe=n(536);function _e(e){return o.createElement(xe.a,{to:{pathname:"/view",search:E.stringify({url:e.url})}},e.text)}function je(){var e=o.createElement(o.Fragment,null,o.createElement("p",null,o.createElement(c.a,{id:"intro.description",defaultMessage:"Topola Genealogy is a genealogy tree viewer that lets you browse the structure of the family."})),o.createElement("p",null,o.createElement(c.a,{id:"intro.instructions",defaultMessage:"Use the OPEN FILE or LOAD FROM URL buttons above to load a GEDCOM file. You can export a GEDCOM file from most of the existing genealogy programs and web sites."})),o.createElement("p",null,o.createElement(c.a,{id:"intro.examples",defaultMessage:"Here are some examples from the web that you can view:"})),o.createElement("ul",null,o.createElement("li",null,o.createElement(_e,{url:"http://genpol.com/module-Downloads-prep_hand_out-lid-32.html",text:"Karol Wojty\u0142a"})," ","(",o.createElement(c.a,{id:"intro.from",defaultMessage:"from"})," ",o.createElement("a",{href:"http://genpol.com/module-Downloads-display-lid-32.html"},"GENPOL"),")"),o.createElement("li",null,o.createElement(_e,{url:"https://webtreeprint.com/tp_downloader.php?path=famous_gedcoms/shakespeare.ged",text:"Shakespeare"})," ","(",o.createElement(c.a,{id:"intro.from",defaultMessage:"from"})," ",o.createElement("a",{href:"https://webtreeprint.com/tp_famous_gedcoms.php"},"webtreeprint.com"),")"),o.createElement("li",null,o.createElement(_e,{url:"http://genealogyoflife.com/tng/gedcom/HarryPotter.ged",text:"Harry Potter"})," ","(",o.createElement(c.a,{id:"intro.from",defaultMessage:"from"})," ",o.createElement("a",{href:"http://famousfamilytrees.blogspot.com/"},"Famous Family Trees"),")")),o.createElement("p",null,o.createElement("b",null,o.createElement(c.a,{id:"intro.privacy",defaultMessage:"Privacy"})),": ",o.createElement(c.a,{id:"intro.privacy_note",defaultMessage:'When using the "load from file" option, this site does not send your data anywhere and files loaded from disk do not leave your computer. When using "load from URL", data is passed through the {link} service to deal with an issue with cross-site file loading in the browser (CORS).',values:{link:o.createElement("a",{href:"https://cors-anywhere.herokuapp.com/"},"cors-anywhere")}})),o.createElement("p",{className:"ui right aligned version"},"version: ","2020-04-10 15:37:36 +0200".slice(0,16)," (",o.createElement("a",{href:"https://github.com/PeWu/topola-viewer/commit/".concat("35e6a91")},"35e6a91"),")"));return o.createElement("div",{id:"content"},o.createElement("div",{className:"backgroundImage"}),o.createElement(Me.a,{className:"intro"},o.createElement(S.a,{as:Me.a.Content,minWidth:768},o.createElement(Me.a.Header,null,o.createElement(c.a,{id:"intro.title",defaultMessage:"Topola Genealogy Viewer"}))),o.createElement(Me.a.Content,null,o.createElement(S.a,{as:Se.a,minWidth:768},o.createElement(Se.a.Row,null,o.createElement(Se.a.Column,{width:5},o.createElement(Ie.a,{src:Te.a,alt:"Topola logo"})),o.createElement(Se.a.Column,{width:11},e))),o.createElement(S.a,{maxWidth:767},o.createElement(Ie.a,{src:Te.a,alt:"Topola logo",centered:!0,size:"tiny",className:"blockImage"}),e))))}var Re=n(544),Le=n(554),Ce=n(539),Ne=n(87),ze=n.n(Ne);function We(e){try{return sessionStorage.getItem(e)}catch(t){console.warn("Failed to load data from session storage: "+t)}return null}function Pe(e,t){try{sessionStorage.setItem(e,t)}catch(n){console.warn("Failed to store data in session storage: "+n)}}function Ae(e,t){return Fe.apply(this,arguments)}function Fe(){return(Fe=Object(g.a)(p.a.mark(function e(t,n){var a,r,i,o,s;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:for(r in(a=new FormData).append("format","json"),t)a.append(r,t[r]);return i=n?"https://cors-anywhere.herokuapp.com/https://apps.wikitree.com/api.php":"https://apps.wikitree.com/api.php",e.next=6,window.fetch(i,{method:"POST",body:a});case 6:return o=e.sent,e.next=9,o.text();case 9:return s=e.sent,e.abrupt("return",JSON.parse(s));case 11:case"end":return e.stop()}},e)}))).apply(this,arguments)}function Ue(e,t){return Ge.apply(this,arguments)}function Ge(){return(Ge=Object(g.a)(p.a.mark(function e(t,n){var a,r,i,o;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(a="wikitree:ancestors:".concat(t),!(r=We(a))){e.next=4;break}return e.abrupt("return",JSON.parse(r));case 4:return e.next=6,Ae({action:"getAncestors",key:t,fields:"*"},n);case 6:return i=e.sent,o=i[0].ancestors,Pe(a,JSON.stringify(o)),e.abrupt("return",o);case 10:case"end":return e.stop()}},e)}))).apply(this,arguments)}function He(e,t){return Be.apply(this,arguments)}function Be(){return(Be=Object(g.a)(p.a.mark(function e(t,n){var a,r,i,o;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(a=[],r=[],t.forEach(function(e){var t=We("wikitree:relatives:".concat(e));t?a.push(JSON.parse(t)):r.push(e)}),0!==r.length){e.next=5;break}return e.abrupt("return",a);case 5:return e.next=7,Ae({action:"getRelatives",keys:r.join(","),getChildren:!0,getSpouses:!0},n);case 7:if(null!==(i=e.sent)[0].items){e.next=10;break}throw new Error("WikiTree profile ".concat(r[0]," not found."));case 10:return(o=i[0].items.map(function(e){return e.person})).forEach(function(e){Pe("wikitree:relatives:".concat(e.Name),JSON.stringify(e))}),e.abrupt("return",a.concat(o));case 13:case"end":return e.stop()}},e)}))).apply(this,arguments)}function Ve(e){return Ke.apply(this,arguments)}function Ke(){return(Ke=Object(g.a)(p.a.mark(function e(t){var n;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,Ae({action:"clientLogin",authcode:t},!1);case 2:return n=e.sent,e.abrupt("return",n.clientLogin);case 4:case"end":return e.stop()}},e)}))).apply(this,arguments)}function Je(e,t){return qe.apply(this,arguments)}function qe(){return(qe=Object(g.a)(p.a.mark(function e(t,n){var r,i,o,s,l,c,u,d,m,h,g,v,w,E,k,y,b,O,D,T,M;return p.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if((r="apps.wikitree.com"!==window.location.hostname)||ze.a.get("wikidb_wtb_UserID")||!n){e.next=6;break}return e.next=4,Ve(n);case 4:"Success"===e.sent.result&&sessionStorage.clear();case 6:return i=[],e.next=9,He([t],r);case 9:if((o=e.sent)[0].Name){e.next=12;break}throw new Error("WikiTree profile ".concat(t," is not accessible. Try logging in."));case 12:return s=Object.values(o[0].Spouses).map(function(e){return e.Name}),e.next=15,Promise.all([t].concat(s).map(function(e){return Ue(e,r)}));case 15:return l=e.sent,c=l.flat().map(function(e){return e.Name}).filter(function(e){return!!e}),e.next=19,He(c,r);case 19:u=e.sent,i.push.apply(i,Object(a.a)(u)),d=5,m=[t],h=0;case 24:if(!(m.length>0&&h<=d)){e.next=35;break}return e.next=27,He(m,r);case 27:g=e.sent,i.push.apply(i,Object(a.a)(g)),v=g.flatMap(function(e){return Object.values(e.Spouses)}),i.push.apply(i,Object(a.a)(v)),m=g.flatMap(function(e){return Object.values(e.Children).map(function(e){return e.Name})}),h++,e.next=24;break;case 35:return w=new Map,E=new Map,k=new Map,y=new Map,i.forEach(function(e){if(y.set(e.Id,e.Name),e.Mother||e.Father){var t=Ze(e.Mother,e.Father);Qe(w,e.Mother).add(t),Qe(w,e.Father).add(t),Qe(E,t).add(e.Id),k.set(t,{wife:e.Mother||void 0,husband:e.Father||void 0})}}),b=[],O=new Set,i.forEach(function(e){if(!O.has(e.Id)){O.add(e.Id);var t=Ye(e);e.Spouses&&Object.values(e.Spouses).forEach(function(t){var n=Ze(e.Id,t.Id);Qe(w,e.Id).add(n),Qe(w,t.Id).add(n);var a="Male"===e.Gender?{wife:t.Id,husband:e.Id,spouse:t}:{wife:e.Id,husband:t.Id,spouse:t};k.set(n,a)}),t.fams=Array.from(Qe(w,e.Id)),b.push(t)}}),D=Array.from(k.entries()).map(function(e){var t=Object(f.a)(e,2),n=t[0],a=t[1],r={id:n},i=a.wife&&y.get(a.wife);i&&(r.wife=i);var o=a.husband&&y.get(a.husband);if(o&&(r.husb=o),r.children=Array.from(Qe(E,n)).map(function(e){return y.get(e)}),a.spouse&&(a.spouse.marriage_date&&"0000-00-00"!==a.spouse.marriage_date||a.spouse.marriage_location)){var s=Xe(a.spouse.marriage_date);r.marriage=Object.assign({},s,{place:a.spouse.marriage_location})}return r}),T=X({indis:b,fams:D}),M=$e(b),e.abrupt("return",{chartData:T,gedcom:M});case 47:case"end":return e.stop()}},e)}))).apply(this,arguments)}function Ze(e,t){return t>e?"".concat(e,"_").concat(t):"".concat(t,"_").concat(e)}function Ye(e){var t={id:e.Name};if("Unknown"!==e.FirstName&&(t.firstName=e.FirstName),"Unknown"!==e.LastNameAtBirth&&(t.lastName=e.LastNameAtBirth),(e.Mother||e.Father)&&(t.famc=Ze(e.Mother,e.Father)),"Male"===e.Gender?t.sex="M":"Female"===e.Gender&&(t.sex="F"),e.BirthDate&&"0000-00-00"!==e.BirthDate||e.BirthLocation){var n=Xe(e.BirthDate,e.DataStatus&&e.DataStatus.BirthDate);t.birth=Object.assign({},n,{place:e.BirthLocation})}if(e.DeathDate&&"0000-00-00"!==e.DeathDate||e.DeathLocation){var a=Xe(e.DeathDate,e.DataStatus&&e.DataStatus.DeathDate);t.death=Object.assign({},a,{place:e.DeathLocation})}return e.PhotoData&&(t.images=[{url:"https://www.wikitree.com".concat(e.PhotoData.path)}]),t}function Xe(e,t){if(e){var n=e.match(/(\d\d\d\d)-(\d\d)-(\d\d)/);if(!n)return{date:{text:e}};var a={};return"0000"!==n[1]&&(a.year=~~n[1]),"00"!==n[2]&&(a.month=~~n[2]),"00"!==n[3]&&(a.day=~~n[3]),"after"===t?{dateRange:{from:a}}:"before"===t?{dateRange:{to:a}}:("guess"===t&&(a.qualifier="abt"),{date:a})}}function $e(e){var t={};return e.forEach(function(e){var n=e.id.replace(/ /g,"_");t[e.id]={level:0,pointer:"@".concat(e.id,"@"),tag:"INDI",data:"",tree:[{level:1,pointer:"",tag:"NAME",data:"".concat(e.firstName||""," /").concat(e.lastName||"","/"),tree:[]},{level:1,pointer:"",tag:"WWW",data:"https://www.wikitree.com/wiki/".concat(n),tree:[]}]}}),{head:{level:0,pointer:"",tag:"HEAD",data:"",tree:[]},indis:t,fams:{},other:{}}}function Qe(e,t){var n=e.get(t);if(n)return n;var a=new Set;return e.set(t,a),a}var et=n(551),tt=n(552),nt=n(547),at=n(264),rt=n.n(at),it=n(170),ot=n.n(it),st=n(265),lt=n.n(st),ct=n(266),ut=n.n(ct);function dt(e){return e.toLocaleLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/\u0142/g,"l")}function mt(e,t){return e.score!==t.score?t.score-e.score:lt()(e.ref,t.ref)}var ft=function(){function e(t){Object(v.a)(this,e),this.index=void 0,this.indiMap=void 0,this.famMap=void 0,this.indiMap=B(t),this.famMap=V(t)}return Object(w.a)(e,[{key:"initialize",value:function(){var e=this;this.index=ut()(function(){var t=this;this.ref("id"),this.field("id"),this.field("name",{boost:10}),this.field("normalizedName",{boost:8}),this.field("spouseLastName",{boost:2}),this.field("normalizedSpouseLastName",{boost:2}),e.indiMap.forEach(function(n){var a=[n.firstName,n.lastName].join(" "),r=function(e,t,n){return(e.fams||[]).map(function(e){return n.get(e)}).map(function(e){return e&&e.husb}).map(function(e){return e&&t.get(e)}).map(function(e){return e&&e.lastName}).join(" ")}(n,e.indiMap,e.famMap);t.add({id:n.id,name:a,normalizedName:dt(a),spouseLastName:r,normalizedSpouseLastName:dt(r)})})})}},{key:"search",value:function(e){var t=this,n=e.split(" ").filter(function(e){return!!e}).map(function(e){return"+".concat(e,"*")}).join(" ");return this.index.search(n).sort(mt).slice(0,8).map(function(e){return{id:e.ref,indi:t.indiMap.get(e.ref)}})}}]),e}();function ht(e){var t=[e.indi.firstName,e.indi.lastName].join(" ").trim();return e.id.length>8?t:o.createElement(o.Fragment,null,t," ",o.createElement("i",null,"(",e.id,")"))}function pt(e,t){var n=re(e.birth,t),a=re(e.death,t);return a?"".concat(n," \u2013 ").concat(a):n}var gt,vt,wt=n(541),Et=n(550),kt=n(66),yt=n(540),bt=n(538),Ot=n(530),Dt=n(545),Tt=n(542),Mt=n(546),St="https://www.wikitree.com/photo.php/a/a5/WikiTree_Images.png";function It(e){return new Promise(function(t,n){var a=new FileReader;a.onload=function(e){t(e.target.result)},a.readAsText(e)})}function xt(e){var t=e.toLowerCase();return t.endsWith(".jpg")||t.endsWith(".png")}!function(e){e[e.UNKNOWN=0]="UNKNOWN",e[e.NOT_LOGGED_IN=1]="NOT_LOGGED_IN",e[e.LOGGED_IN=2]="LOGGED_IN"}(gt||(gt={})),function(e){e[e.LARGE=0]="LARGE",e[e.SMALL=1]="SMALL"}(vt||(vt={}));var _t,jt=function(e){function t(){var e,n;Object(v.a)(this,t);for(var a=arguments.length,r=new Array(a),i=0;i