diff --git a/asset-manifest.json b/asset-manifest.json index 0c5f46d..07a4c41 100644 --- a/asset-manifest.json +++ b/asset-manifest.json @@ -1,8 +1,8 @@ { "files": { "main.css": "./static/css/main.901d228d.chunk.css", - "main.js": "./static/js/main.72fe0747.chunk.js", - "main.js.map": "./static/js/main.72fe0747.chunk.js.map", + "main.js": "./static/js/main.d5fb1703.chunk.js", + "main.js.map": "./static/js/main.d5fb1703.chunk.js.map", "runtime-main.js": "./static/js/runtime-main.4ad99b93.js", "runtime-main.js.map": "./static/js/runtime-main.4ad99b93.js.map", "static/js/2.df9da761.chunk.js": "./static/js/2.df9da761.chunk.js", @@ -33,6 +33,6 @@ "static/css/3.b14ff839.chunk.css", "static/js/3.674a9ce7.chunk.js", "static/css/main.901d228d.chunk.css", - "static/js/main.72fe0747.chunk.js" + "static/js/main.d5fb1703.chunk.js" ] } \ No newline at end of file diff --git a/index.html b/index.html index 7a39c64..10f9e8f 100644 --- a/index.html +++ b/index.html @@ -1 +1 @@ -Topola Genealogy Viewer
\ No newline at end of file +Topola Genealogy Viewer
\ No newline at end of file diff --git a/static/js/main.72fe0747.chunk.js.map b/static/js/main.72fe0747.chunk.js.map deleted file mode 100644 index e46f61a..0000000 --- a/static/js/main.72fe0747.chunk.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["util/analytics.ts","chart.tsx","util/media.ts","datasource/data_source.ts","util/error.ts","util/gedcom_util.ts","util/date_util.ts","details.tsx","datasource/load_data.ts","datasource/embedded.ts","topola.jpg","intro.tsx","menu/menu_item.tsx","menu/search_index.ts","menu/search.tsx","menu/upload_menu.tsx","menu/url_menu.tsx","menu/wikitree.png","datasource/wikitree.ts","menu/wikitree_menu.tsx","menu/top_bar.tsx","app.tsx","util/error_i18n.ts","index.tsx"],"names":["analyticsEvent","action","data","window","gtag","ChartType","AppMedia","createMedia","breakpoints","small","large","mediaStyles","createMediaStyle","Media","MediaContextProvider","zoomed","size","event","parent","select","node","scale","transform","k","offsetX","max","clientWidth","offsetY","clientHeight","attr","scrollLeft","x","scrollTop","y","scrolled","zoomTransform","call","zoom","translateTo","loadAsDataUrl","blob","reader","FileReader","readAsDataURL","Promise","resolve","reject","onload","e","target","result","inlineImage","image","a","href","baseVal","fetch","response","dataUrl","console","warn","inlineImages","svg","images","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","DataSourceEnum","ChartComponent","chart","animating","rerenderRequired","zoomBehavior","this","props","chartType","Hourglass","HourglassChart","Relatives","RelativesChart","Fancy","FancyChart","CircleRenderer","DetailedRenderer","factor","scaleBy","args","initialRender","freezeAnimation","innerHTML","createChart","json","getChartType","renderer","getRendererType","svgSelector","indiCallback","info","onSelection","animate","updateSvgSize","locale","intl","setData","chartInfo","render","startIndi","selection","id","baseGeneration","generation","zoomOutFactor","min","extent","scaleExtent","translateExtent","on","scrollTopTween","i","interpolateNumber","t","scrollLeftTween","dx","origin","dy","svgTransition","transition","delay","duration","tween","animationPromise","then","renderChart","prevProps","at","className","onClick","getElementById","cloneNode","removeAttribute","setAttribute","String","Number","getAttribute","querySelector","XMLSerializer","serializeToString","getStrippedSvg","printWindow","style","position","top","left","contentDocument","open","write","getSvgContents","close","setTimeout","contentWindow","focus","print","parentNode","removeChild","body","appendChild","getSvgContentsWithInlinedImages","contents","Blob","saveAs","jspdf","default","doc","orientation","unit","format","addImage","save","React","Chart","injectIntl","forwardRef","TopolaError","code","message","Error","pointerToId","pointer","substring","length","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","Object","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","undefined","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","normalizedTag","replace","joinLines","lines","line","index","properties","getData","subentry","last","eventDetails","gedcomDate","getDate","translateDate","place","filter","note","noteDetails","nameDetails","split","getDetails","tags","detailsFunction","flatMap","element","hasData","getOtherDetails","includes","dataDetails","DetailsComponent","entriesWithData","dereferenced","dereference","Details","getSelection","prepareData","cacheId","parseGedcom","gedcomEntriesToJson","chartData","convertGedcom","serializedData","JSON","stringify","sessionStorage","setItem","loadFromUrl","handleCors","cachedData","getItem","parse","driveUrlMatch1","driveUrlMatch2","urlToFetch","status","statusText","loadGedcom","hash","EmbeddedMessageType","UploadedDataSource","newSource","oldSource","spec","source","event_label","event_value","GedcomUrlDataSource","EmbeddedDataSource","PARENT_READY","postMessage","READY","GEDCOM","onMessage","GedcomLink","pathname","search","queryString","Intro","dateString","values","link","process","slice","Card","Content","as","Header","Grid","Row","Column","logo","alt","centered","MenuType","MenuItem","newProps","menuType","Menu","Item","Dropdown","require","lunr","normalize","input","toLocaleLowerCase","compare","score","naturalSort","ref","LunrSearchIndex","self","use","multiLanguage","field","boost","firstName","lastName","spouseLastName","famId","husb","husbId","husband","getHusbandLastName","add","normalizedName","normalizedSpouseLastName","query","s","getNameLine","trim","SearchBarComponent","state","searchResults","searchRef","searchIndex","birthDate","deathDate","death","key","description","getDescriptionLine","results","displaySearchResult","setState","setValue","initialize","buildSearchIndex","initializeSearchIndex","Search","onSearchChange","debounce","_","handleSearch","value","onResultSelect","handleResultSelect","noResultsMessage","placeholder","selectFirstResult","SearchBar","loadFileAsText","file","evt","readAsText","isImageFileName","lower","UploadMenu","files","filesArray","gedcomFile","imageMap","imageFileNames","md5","location","history","content","Icon","htmlFor","accept","multiple","onChange","handleUpload","UrlMenu","dialogOpen","inputRef","current","Modal","onClose","handleClose","Form","onSubmit","handleLoad","Input","fluid","handleUrlChange","Actions","Button","secondary","primary","openDialog","loadFromUrlModal","PRIVATE_ID_PREFIX","USER_NAME_COOKIE","getSessionStorageItem","setSessionStorageItem","wikiTreeGet","request","requestData","FormData","append","apiUrl","method","credentials","responseBody","getAncestors","cacheKey","fields","ancestors","getRelatives","keys","keysToFetch","getChildren","getSpouses","items","fetchedResults","person","Name","concat","clientLogin","authcode","getLoggedInUserName","Cookies","loadWikiTree","hostname","loginResult","clear","username","everyone","firstPerson","spouseKeys","Spouses","personId","ancestorKeys","flat","ancestorDetails","privateFathers","privateMothers","ancestorList","offset","Id","Father","Mother","privateFather","privateMother","privateAncestors","descendantGenerationLimit","toFetch","people","allSpouses","Children","c","families","spouses","idToName","getFamilyId","getSet","wife","converted","Set","convertPerson","spouse","familySpouses","Gender","child","marriage_date","marriage_location","parsedDate","parseDate","buildGedcom","spouse1","spouse2","hideId","FirstName","RealName","LastNameAtBirth","famc","sex","BirthDate","BirthLocation","BirthDateDecade","DataStatus","parseDecade","DeathDate","DeathLocation","DeathDateDecade","PhotoData","dataStatus","matchedDate","decade","gedcomIndis","escapedId","level","newSet","WikiTreeLoginState","WikiTreeDataSource","ScreenSize","WikiTreeMenu","wikiTreeId","standalone","preventDefault","handleIdChange","wikitreeLogo","handleSelectId","wikiTreeLink","rel","example1","enterId","example2","wikiTreeIdModal","WikiTreeLoginMenuComponent","wikiTreeLoginState","UNKNOWN","wikiTreeLoginFormRef","wikiTreeReturnUrlRef","returnUrl","submit","wikiTreeLoginUsername","LOGGED_IN","NOT_LOGGED_IN","checkWikiTreeLoginState","wikiTreeLogin","display","tooltip","WikiTreeLoginMenu","AppState","TopBar","view","screenSize","showingChart","chartTypeItems","changeView","allowAllRelativesChart","LARGE","eventHandlers","onPrint","trigger","onDownloadPdf","onDownloadPng","onDownloadSvg","SMALL","Divider","showWikiTreeMenus","icon","fileMenus","chartMenus","wikiTreeLoginMenu","attached","inverted","color","desktopMenus","mobileMenus","ErrorMessage","Message","negative","ErrorPopup","Portal","onDismiss","getArguments","getParam","chartTypes","embedded","sourceSpec","WIKITREE","UPLOADED","GEDCOM_URL","EMBEDDED","parsedGen","isNaN","showSidePanel","App","INITIAL","showErrorPopup","chartRef","uploadedDataSource","gedcomUrlDataSource","wikiTreeDataSource","embeddedDataSource","gen","downloadPdf","downloadPng","downloadSvg","onDismissErrorPopup","renderMainArea","SHOWING_CHART","LOADING_MORE","error","Loader","active","ERROR","LOADING","otherStateChanges","changes","componentDidUpdate","oldSouce","isNewData","loadData","setError","loadMoreFromWikitree","updateDisplay","exact","path","component","messages","cs","messages_cs","de","messages_de","fr","messages_fr","it","messages_it","pl","messages_pl","ru","messages_ru","language","navigator","browser","detect","ReactDOM"],"mappings":"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","file":"static/js/main.72fe0747.chunk.js","sourcesContent":["/** 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 React from 'react';\nimport {injectIntl, WrappedComponentProps} from 'react-intl';\nimport {interpolateNumber} from 'd3-interpolate';\nimport {max, min} from 'd3-array';\nimport {Media} from './util/media';\nimport {saveAs} from 'file-saver';\nimport {select, Selection} from 'd3-selection';\nimport 'd3-transition';\nimport {\n D3ZoomEvent,\n zoom,\n ZoomBehavior,\n ZoomedElementBaseType,\n zoomTransform,\n} from 'd3-zoom';\nimport {\n JsonGedcomData,\n ChartHandle,\n IndiInfo,\n createChart,\n DetailedRenderer,\n HourglassChart,\n RelativesChart,\n FancyChart,\n CircleRenderer,\n} from 'topola';\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(\n size: [number, number],\n event: D3ZoomEvent,\n) {\n const parent = select('#svgContainer').node() as Element;\n\n const scale = event.transform.k;\n const offsetX = max([0, (parent.clientWidth - size[0] * scale) / 2]);\n const offsetY = max([0, (parent.clientHeight - size[1] * scale) / 2]);\n select('#chartSvg')\n .attr('width', size[0] * scale)\n .attr('height', size[1] * scale)\n .attr('transform', `translate(${offsetX}, ${offsetY})`);\n select('#chart').attr('transform', `scale(${scale})`);\n\n parent.scrollLeft = -event.transform.x;\n parent.scrollTop = -event.transform.y;\n}\n\n/** Called when the scrollbars are used. */\nfunction scrolled() {\n const parent = select('#svgContainer').node() as Element;\n const x = parent.scrollLeft + parent.clientWidth / 2;\n const y = parent.scrollTop + parent.clientHeight / 2;\n const scale = zoomTransform(parent).k;\n select(parent).call(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 freezeAnimation?: boolean;\n}\n\n/** Component showing the genealogy chart and handling transition animations. */\nexport class ChartComponent extends React.PureComponent<\n ChartProps & WrappedComponentProps,\n {}\n> {\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?: 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 = select('#svgContainer') as Selection;\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 // Freeze changing selection after initial rendering.\n if (!args.initialRender && this.props.freezeAnimation) {\n return;\n }\n\n if (args.initialRender) {\n (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.props.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 = select('#chartSvg');\n const parent = select('#svgContainer').node() as Element;\n\n const scale = zoomTransform(parent).k;\n const zoomOutFactor = min([\n 1,\n scale,\n parent.clientWidth / chartInfo.size[0],\n parent.clientHeight / chartInfo.size[1],\n ])!;\n const extent: [number, number] = [max([0.1, zoomOutFactor])!, 2];\n\n this.zoomBehavior = zoom()\n .scaleExtent(extent)\n .translateExtent([[0, 0], chartInfo.size])\n .on('zoom', (event) => zoomed(chartInfo.size, event));\n select(parent).on('scroll', scrolled).call(this.zoomBehavior);\n\n const scrollTopTween = (scrollTop: number) => {\n return () => {\n const i = 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 = 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 = max([\n 0,\n (parent.clientWidth - chartInfo.size[0] * scale) / 2,\n ]);\n const offsetY = max([\n 0,\n (parent.clientHeight - chartInfo.size[1] * scale) / 2,\n ]);\n const svgTransition = svg.transition().delay(200).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 render() {\n return (\n
\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 = select('#svgContainer').node() as Element;\n const scale = 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 // Lazy load jspdf.\n const {default: jspdf} = await import('jspdf');\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}\nexport const Chart = injectIntl(ChartComponent, {forwardRef: true});\n","import {createMedia} from '@artsy/fresnel';\n\n/** Defines the breakpoints at which to show different UI variants. */\nconst AppMedia = createMedia({\n breakpoints: {\n small: 320,\n large: 768,\n },\n});\nexport const mediaStyles = AppMedia.createMediaStyle();\nexport const {Media, MediaContextProvider} = AppMedia;\n","import {IndiInfo} from 'topola';\nimport {TopolaData} from '../util/gedcom_util';\n\n/** Supported data sources. */\nexport enum DataSourceEnum {\n UPLOADED,\n GEDCOM_URL,\n WIKITREE,\n EMBEDDED,\n}\n\n/** Source specification together with individual selection. */\nexport interface SourceSelection {\n spec: SourceSpecT;\n selection?: IndiInfo;\n}\n\n/** Interface encapsulating functions specific for a data source. */\nexport interface 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(\n newSource: SourceSelection,\n oldSource: SourceSelection,\n data?: TopolaData,\n ): boolean;\n /** Loads data from the data source. */\n loadData(spec: SourceSelection): Promise;\n}\n","/** Error class adding an error code used for i18n. */\nexport class TopolaError extends Error {\n constructor(\n public readonly code: string,\n message: string,\n public readonly args: {[key: string]: string} = {},\n ) {\n super(message);\n }\n}\n","import {GedcomEntry, parse as parseGedcom} from 'parse-gedcom';\nimport {TopolaError} from './error';\nimport {\n JsonFam,\n JsonGedcomData,\n JsonIndi,\n gedcomEntriesToJson,\n JsonImage,\n JsonEvent,\n} from 'topola';\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 TopolaError('GEDCOM_READ_FAILED', '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, DateOrRange, DateRange, getDate} from 'topola';\nimport {IntlShape} 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: IntlShape) {\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: Intl.DateTimeFormatOptions = {\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: IntlShape) {\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: IntlShape,\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: IntlShape): 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 {\n FormattedMessage,\n injectIntl,\n IntlShape,\n WrappedComponentProps,\n} from 'react-intl';\nimport {GedcomData, pointerToId} from './util/gedcom_util';\nimport {GedcomEntry} from 'parse-gedcom';\nimport {translateDate} from './util/date_util';\n\ninterface Props {\n gedcom: GedcomData;\n indi: string;\n}\n\nconst EVENT_TAGS = ['BIRT', 'BAPM', 'CHR', 'EVEN', 'CENS', 'DEAT', 'BURI'];\nconst EXCLUDED_TAGS = ['NAME', 'SEX', 'FAMC', 'FAMS', 'NOTE', 'SOUR'];\nconst TAG_DESCRIPTIONS = new Map([\n ['ADOP', 'Adoption'],\n ['BAPM', 'Baptism'],\n ['BIRT', 'Birth'],\n ['BURI', 'Burial'],\n ['CENS', 'Census'],\n ['CHR', 'Christening'],\n ['CREM', 'Cremation'],\n ['DEAT', 'Death'],\n ['EDUC', 'Education'],\n ['EMAIL', 'E-mail'],\n ['EMIG', 'Emigration'],\n ['EVEN', 'Event'],\n ['FACT', 'Fact'],\n ['IMMI', 'Immigration'],\n ['MARR', 'Marriage'],\n ['MILT', 'Military services'],\n ['NATU', 'Naturalization'],\n ['OCCU', 'Occupation'],\n ['TITL', 'Title'],\n ['WWW', 'WWW'],\n]);\n\nfunction translateTag(tag: string) {\n const normalizedTag = tag.replace(/_/g, '');\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: IntlShape) {\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\nclass DetailsComponent extends React.Component<\n Props & WrappedComponentProps,\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.props.intl),\n )}\n {getOtherDetails(entriesWithData)}\n {getDetails(entriesWithData, ['NOTE'], noteDetails)}\n
\n );\n }\n}\nexport const Details = injectIntl(DetailsComponent);\n","import {analyticsEvent} from '../util/analytics';\nimport {convertGedcom, getSoftware, TopolaData} from '../util/gedcom_util';\nimport {DataSource, DataSourceEnum, SourceSelection} from './data_source';\nimport {IndiInfo, JsonGedcomData} from 'topola';\nimport {TopolaError} from '../util/error';\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 selection?: IndiInfo,\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 selection && data.indis.some((i) => i.id === selection.id)\n ? selection.id\n : data.indis[0].id;\n return {id, generation: selection?.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\n const driveUrlMatch1 = url.match(\n /https:\\/\\/drive\\.google\\.com\\/file\\/d\\/(.*)\\/.*/,\n );\n if (driveUrlMatch1) {\n url = `https://drive.google.com/uc?id=${driveUrlMatch1[1]}&export=download`;\n }\n const driveUrlMatch2 = url.match(\n /https:\\/\\/drive\\.google\\.com\\/open\\?id=([^&]*)&?.*/,\n );\n if (driveUrlMatch2) {\n url = `https://drive.google.com/uc?id=${driveUrlMatch2[1]}&export=download`;\n }\n\n const urlToFetch = handleCors\n ? 'https://topola-cors.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 TopolaError(\n 'ERROR_LOADING_UPLOADED_FILE',\n 'Error loading data. Please upload your file again.',\n );\n }\n return prepareData(gedcom, hash, images);\n}\n\nexport interface UploadSourceSpec {\n source: DataSourceEnum.UPLOADED;\n gedcom?: string;\n /** Hash of the GEDCOM contents. */\n hash: string;\n images?: Map;\n}\n\n/** Files opened from the local computer. */\nexport class UploadedDataSource implements DataSource {\n // isNewData(args: Arguments, state: State): boolean {\n isNewData(\n newSource: SourceSelection,\n oldSource: SourceSelection,\n data?: TopolaData,\n ): boolean {\n return newSource.spec.hash !== oldSource.spec.hash;\n }\n\n async loadData(\n source: SourceSelection,\n ): Promise {\n try {\n const data = await loadGedcom(\n source.spec.hash,\n source.spec.gedcom,\n source.spec.images,\n );\n const software = getSoftware(data.gedcom.head);\n analyticsEvent('upload_file_loaded', {\n event_label: software,\n event_value: (source.spec.images && source.spec.images.size) || 0,\n });\n return data;\n } catch (error) {\n analyticsEvent('upload_file_error');\n throw error;\n }\n }\n}\n\nexport interface UrlSourceSpec {\n source: DataSourceEnum.GEDCOM_URL;\n /** URL of the data that is loaded or is being loaded. */\n url: string;\n handleCors: boolean;\n}\n\n/** GEDCOM file loaded by pointing to a URL. */\nexport class GedcomUrlDataSource implements DataSource {\n isNewData(\n newSource: SourceSelection,\n oldSource: SourceSelection,\n data?: TopolaData,\n ): boolean {\n return newSource.spec.url !== oldSource.spec.url;\n }\n\n async loadData(source: SourceSelection): Promise {\n try {\n const data = await loadFromUrl(source.spec.url, source.spec.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","import {analyticsEvent} from '../util/analytics';\nimport {DataSource, DataSourceEnum, SourceSelection} from './data_source';\nimport {getSoftware, TopolaData} from '../util/gedcom_util';\nimport {loadGedcom} from './load_data';\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\nexport interface EmbeddedSourceSpec {\n source: DataSourceEnum.EMBEDDED;\n}\n\n/** GEDCOM file received from outside of the iframe. */\nexport class EmbeddedDataSource implements DataSource {\n isNewData(\n newSource: SourceSelection,\n oldSource: SourceSelection,\n data?: TopolaData,\n ): boolean {\n // Never reload data.\n return false;\n }\n\n private async onMessage(\n message: EmbeddedMessage,\n resolve: (value: TopolaData) => void,\n reject: (reason: any) => void,\n ) {\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 resolve(data);\n } catch (error) {\n analyticsEvent('embedded_file_error');\n reject(error);\n }\n }\n }\n\n async loadData(\n source: SourceSelection,\n ): Promise {\n // Notify the parent window that we are ready.\n return new Promise((resolve, reject) => {\n window.parent.postMessage({message: EmbeddedMessageType.READY}, '*');\n window.addEventListener('message', (data) =>\n this.onMessage(data.data, resolve, reject),\n );\n });\n }\n}\n","export default __webpack_public_path__ + \"static/media/topola.a3ffa9a5.jpg\";","import * as queryString from 'query-string';\nimport * as React from 'react';\nimport logo from './topola.jpg';\nimport {Card, Grid, Image} from 'semantic-ui-react';\nimport {FormattedMessage} from 'react-intl';\nimport {Link} from 'react-router-dom';\nimport {Media} from './util/media';\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 \"Topola\n \n {contents}\n \n \n \n \n {contents}\n \n \n \n
\n );\n}\n","import * as React from 'react';\nimport {\n Menu,\n Dropdown,\n MenuItemProps,\n DropdownItemProps,\n} from 'semantic-ui-react';\n\nexport enum MenuType {\n Menu,\n Dropdown,\n}\n\ninterface Props {\n menuType?: MenuType;\n}\n\nexport class MenuItem extends React.Component<\n Props & MenuItemProps & DropdownItemProps\n> {\n render() {\n const newProps = {...this.props};\n // Remove menuType from props to avoid error message in the console.\n delete newProps.menuType;\n return (\n <>\n {this.props.menuType === MenuType.Menu ? (\n {this.props.children}\n ) : (\n {this.props.children}\n )}\n \n );\n }\n}\n","import lunr from 'lunr';\nimport naturalSort from 'javascript-natural-sort';\nimport {idToFamMap, idToIndiMap} from '../util/gedcom_util';\nimport {JsonFam, JsonGedcomData, JsonIndi} from 'topola';\n\n// TODO: Add type declarations and use import instead of require.\nrequire('lunr-languages/lunr.stemmer.support')(lunr);\nrequire('lunr-languages/lunr.multi')(lunr);\nrequire('lunr-languages/lunr.de')(lunr);\nrequire('lunr-languages/lunr.fr')(lunr);\nrequire('lunr-languages/lunr.it')(lunr);\nrequire('lunr-languages/lunr.ru')(lunr);\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.use((lunr as any).multiLanguage('de', 'en', 'fr', 'it', 'ru'));\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 debounce from 'debounce';\nimport {analyticsEvent} from '../util/analytics';\nimport {buildSearchIndex, SearchIndex, SearchResult} from './search_index';\nimport {formatDateOrRange} from '../util/date_util';\nimport {IndiInfo, JsonGedcomData} from 'topola';\nimport {injectIntl, WrappedComponentProps} from 'react-intl';\nimport {JsonIndi} from 'topola';\nimport {RouteComponentProps} from 'react-router-dom';\nimport {Search, SearchProps, SearchResultProps} from 'semantic-ui-react';\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\ninterface Props {\n /** Data used for the search index. */\n data: JsonGedcomData;\n onSelection: (indiInfo: IndiInfo) => void;\n}\n\ninterface State {\n searchResults: SearchResultProps[];\n}\n\n/** Displays and handles the search box in the top bar. */\nclass SearchBarComponent extends React.Component<\n RouteComponentProps & WrappedComponentProps & Props,\n State\n> {\n state: State = {\n searchResults: [],\n };\n\n searchRef?: {setValue(value: string): void};\n searchIndex?: SearchIndex;\n\n private getDescriptionLine(indi: JsonIndi) {\n const birthDate = formatDateOrRange(indi.birth, this.props.intl);\n const deathDate = formatDateOrRange(indi.death, this.props.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. */\n private displaySearchResult(result: SearchResult) {\n return {\n id: result.id,\n key: result.id,\n title: getNameLine(result),\n description: this.getDescriptionLine(result.indi),\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 this.displaySearchResult(result),\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.onSelection({id, generation: 0});\n this.searchRef!.setValue('');\n }\n\n private initializeSearchIndex() {\n this.searchIndex = buildSearchIndex(this.props.data);\n }\n\n componentDidMount() {\n this.initializeSearchIndex();\n }\n\n componentDidUpdate(prevProps: Props) {\n if (prevProps.data !== this.props.data) {\n this.initializeSearchIndex();\n }\n }\n\n render() {\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.props.intl.formatMessage({\n id: 'menu.search.no_results',\n defaultMessage: 'No results found',\n })}\n placeholder={this.props.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}\nexport const SearchBar = injectIntl(SearchBarComponent);\n","import * as queryString from 'query-string';\nimport * as React from 'react';\nimport md5 from 'md5';\nimport {analyticsEvent} from '../util/analytics';\nimport {Dropdown, Icon, Menu} from 'semantic-ui-react';\nimport {FormattedMessage} from 'react-intl';\nimport {MenuType} from './menu_item';\nimport {RouteComponentProps} from 'react-router-dom';\n\nfunction loadFileAsText(file: File): Promise {\n return new Promise((resolve) => {\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\ninterface Props {\n menuType: MenuType;\n}\n\n/** Displays and handles the \"Open file\" menu. */\nexport class UploadMenu extends React.Component {\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 render() {\n const content = (\n <>\n \n \n \n );\n return (\n <>\n {this.props.menuType === MenuType.Menu ? (\n \n ) : (\n \n {content}\n \n )}\n this.handleUpload(e)}\n />\n \n );\n }\n}\n","import * as queryString from 'query-string';\nimport * as React from 'react';\nimport {analyticsEvent} from '../util/analytics';\nimport {Button, Form, Header, Icon, Input, Modal} from 'semantic-ui-react';\nimport {FormattedMessage} from 'react-intl';\nimport {MenuItem, MenuType} from './menu_item';\nimport {RouteComponentProps} from 'react-router-dom';\n\ninterface Props {\n menuType: MenuType;\n}\n\ninterface State {\n dialogOpen: boolean;\n url?: string;\n}\n\n/** Displays and handles the \"Open URL\" menu. */\nexport class UrlMenu extends React.Component<\n RouteComponentProps & Props,\n State\n> {\n state: State = {dialogOpen: false};\n\n inputRef: React.RefObject = React.createRef();\n\n /** Opens the \"Load from URL\" dialog. */\n private openDialog() {\n this.setState(Object.assign({}, this.state, {dialogOpen: true}), () =>\n this.inputRef.current!.focus(),\n );\n }\n\n /** Cancels any of the open dialogs. */\n private handleClose() {\n this.setState(\n Object.assign({}, this.state, {\n dialogOpen: 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 dialogOpen: 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 /** 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 private loadFromUrlModal() {\n return (\n this.handleClose()}\n centered={false}\n >\n
\n \n \n
\n \n
this.handleLoad()}>\n this.handleUrlChange(data.value)}\n ref={this.inputRef}\n />\n

\n \n topola-cors.herokuapp.com\n \n ),\n }}\n />\n

\n \n
\n \n \n \n \n \n );\n }\n\n render() {\n return (\n <>\n this.openDialog()}\n menuType={this.props.menuType}\n >\n \n \n \n {this.loadFromUrlModal()}\n \n );\n }\n}\n","export default __webpack_public_path__ + \"static/media/wikitree.7bffea31.png\";","import Cookies from 'js-cookie';\nimport {analyticsEvent} from '../util/analytics';\nimport {DataSource, DataSourceEnum, SourceSelection} from './data_source';\nimport {Date, DateOrRange, JsonFam, JsonIndi} from 'topola';\nimport {GedcomData, normalizeGedcom, TopolaData} from '../util/gedcom_util';\nimport {GedcomEntry} from 'parse-gedcom';\nimport {IntlShape} from 'react-intl';\nimport {TopolaError} from '../util/error';\n\n/** Prefix for IDs of private individuals. */\nexport const PRIVATE_ID_PREFIX = '~Private';\n\n/**\n * Cookie where the logged in user name is stored. This cookie is shared\n * between apps hosted on apps.wikitree.com.\n */\nconst USER_NAME_COOKIE = 'wikidb_wtb_UserName';\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 GetRelativesRequest {\n action: 'getRelatives';\n keys: string;\n getChildren?: true;\n getSpouses?: true;\n}\n\n/** WikiTree API clientLogin request. */\ninterface ClientLoginRequest {\n action: 'clientLogin';\n authcode: string;\n}\n\n/** WikiTree API clientLogin response. */\ninterface ClientLoginResponse {\n result: string;\n username: string;\n}\n\ntype WikiTreeRequest =\n | GetAncestorsRequest\n | GetRelativesRequest\n | ClientLoginRequest;\n\n/** Person structure returned from WikiTree API. */\ninterface Person {\n Id: number;\n Name: string;\n FirstName: string;\n LastNameAtBirth: string;\n RealName: 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 BirthDateDecade: string;\n DeathDateDecade: string;\n marriage_location: string;\n marriage_date: string;\n DataStatus?: {\n BirthDate: string;\n DeathDate: string;\n };\n PhotoData?: {\n path: string;\n url: 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://topola-cors.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 credentials: handleCors ? undefined : 'include',\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(\n key: string,\n handleCors: boolean,\n): Promise {\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(\n keys: string[],\n handleCors: boolean,\n): Promise {\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 const id = keysToFetch[0];\n throw new TopolaError(\n 'WIKITREE_PROFILE_NOT_FOUND',\n `WikiTree profile ${id} not found`,\n {id},\n );\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(\n authcode: string,\n): Promise {\n const response = await wikiTreeGet(\n {\n action: 'clientLogin',\n authcode,\n },\n false,\n );\n return response.clientLogin;\n}\n\n/**\n * Returnes the logged in user name or undefined if not logged in.\n *\n * This is not an authoritative answer. The result of this function relies on\n * the cookies set on the apps.wikitree.com domain under which this application\n * is hosted. The authoritative source of login information is in cookies set on\n * the api.wikitree.com domain.\n */\nexport function getLoggedInUserName(): string | undefined {\n return Cookies.get(USER_NAME_COOKIE);\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 intl: IntlShape,\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 && !getLoggedInUserName() && authcode) {\n const loginResult = await clientLogin(authcode);\n if (loginResult.result === 'Success') {\n sessionStorage.clear();\n Cookies.set(USER_NAME_COOKIE, loginResult.username);\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 const id = key;\n throw new TopolaError(\n 'WIKITREE_PROFILE_NOT_ACCESSIBLE',\n `WikiTree profile ${id} is not accessible. Try logging in.`,\n {id},\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\n // Map from person id to father id if the father profile is private.\n const privateFathers: Map = new Map();\n // Map from person id to mother id if the mother profile is private.\n const privateMothers: Map = new Map();\n\n // Andujst private individual ids so that there are no collisions in the case\n // that ancestors were collected for more than one person.\n ancestors.forEach((ancestorList, index) => {\n const offset = 1000 * index;\n // Adjust ids by offset.\n ancestorList.forEach((person) => {\n if (person.Id < 0) {\n person.Id -= offset;\n person.Name = `${PRIVATE_ID_PREFIX}${person.Id}`;\n }\n if (person.Father < 0) {\n person.Father -= offset;\n privateFathers.set(person.Id, person.Father);\n }\n if (person.Mother < 0) {\n person.Mother -= offset;\n privateMothers.set(person.Id, person.Mother);\n }\n });\n });\n\n // Set the Father and Mother fields again because getRelatives doesn't return\n // private parents.\n ancestorDetails.forEach((person) => {\n const privateFather = privateFathers.get(person.Id);\n if (privateFather) {\n person.Father = privateFather;\n }\n const privateMother = privateMothers.get(person.Id);\n if (privateMother) {\n person.Mother = privateMother;\n }\n });\n everyone.push(...ancestorDetails);\n\n // Collect private individuals.\n const privateAncestors = ancestors.flat().filter((person) => person.Id < 0);\n everyone.push(...privateAncestors);\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, intl);\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, intl: IntlShape): JsonIndi {\n const indi: JsonIndi = {\n id: person.Name,\n };\n if (person.Name.startsWith(PRIVATE_ID_PREFIX)) {\n indi.hideId = true;\n indi.firstName = intl.formatMessage({\n id: 'wikitree.private',\n defaultMessage: 'Private',\n });\n }\n if (person.FirstName && person.FirstName !== 'Unknown') {\n indi.firstName = person.FirstName;\n } else if (person.RealName && person.RealName !== 'Unknown') {\n indi.firstName = person.RealName;\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 person.BirthDateDecade !== 'unknown'\n ) {\n const parsedDate = parseDate(\n person.BirthDate,\n person.DataStatus && person.DataStatus.BirthDate,\n );\n const date = parsedDate || parseDecade(person.BirthDateDecade);\n indi.birth = Object.assign({}, date, {place: person.BirthLocation});\n }\n if (\n (person.DeathDate && person.DeathDate !== '0000-00-00') ||\n person.DeathLocation ||\n person.DeathDateDecade !== 'unknown'\n ) {\n const parsedDate = parseDate(\n person.DeathDate,\n person.DataStatus && person.DataStatus.DeathDate,\n );\n const date = parsedDate || parseDecade(person.DeathDateDecade);\n indi.death = Object.assign({}, date, {place: person.DeathLocation});\n }\n if (person.PhotoData) {\n indi.images = [{url: `https://www.wikitree.com${person.PhotoData.url}`}];\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\nfunction parseDecade(decade: string): DateOrRange | undefined {\n return decade !== 'unknown' ? {date: {text: decade}} : undefined;\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 };\n if (!indi.id.startsWith('~')) {\n gedcomIndis[indi.id].tree.push({\n level: 1,\n pointer: '',\n tag: 'WWW',\n data: `https://www.wikitree.com/wiki/${escapedId}`,\n tree: [],\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\nexport interface WikiTreeSourceSpec {\n source: DataSourceEnum.WIKITREE;\n authcode?: string;\n}\n\n/** Loading data from the WikiTree API. */\nexport class WikiTreeDataSource implements DataSource {\n constructor(private intl: IntlShape) {}\n\n isNewData(\n newSource: SourceSelection,\n oldSource: SourceSelection,\n data?: TopolaData,\n ): boolean {\n if (!newSource.selection) {\n return false;\n }\n if (oldSource.selection?.id === newSource.selection.id) {\n // Selection unchanged -> don't reload.\n return false;\n }\n if (\n data &&\n data.chartData.indis.some((indi) => indi.id === newSource.selection?.id)\n ) {\n // New selection exists in current view -> animate instead of reloading.\n return false;\n }\n return true;\n }\n\n async loadData(\n source: SourceSelection,\n ): Promise {\n if (!source.selection) {\n throw new TopolaError(\n 'WIKITREE_ID_NOT_PROVIDED',\n 'WikiTree id needs to be provided',\n );\n }\n try {\n const data = await loadWikiTree(\n source.selection.id,\n this.intl,\n source.spec.authcode,\n );\n analyticsEvent('wikitree_loaded');\n return data;\n } catch (error) {\n analyticsEvent('wikitree_error');\n throw error;\n }\n }\n}\n","import * as queryString from 'query-string';\nimport * as React from 'react';\nimport wikitreeLogo from './wikitree.png';\nimport {analyticsEvent} from '../util/analytics';\nimport {FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl';\nimport {getLoggedInUserName} from '../datasource/wikitree';\nimport {MenuItem, MenuType} from './menu_item';\nimport {RouteComponentProps} from 'react-router-dom';\nimport {Header, Button, Modal, Input, Form} from 'semantic-ui-react';\n\nenum WikiTreeLoginState {\n UNKNOWN,\n NOT_LOGGED_IN,\n LOGGED_IN,\n}\n\ninterface Props {\n menuType: MenuType;\n}\n\ninterface State {\n dialogOpen: boolean;\n wikiTreeId?: string;\n}\n\n/** Displays and handles the \"Select WikiTree ID\" menu. */\nexport class WikiTreeMenu extends React.Component<\n RouteComponentProps & Props,\n State\n> {\n state: State = {\n dialogOpen: false,\n };\n\n inputRef: React.RefObject = React.createRef();\n\n private openDialog() {\n this.setState(Object.assign({}, this.state, {dialogOpen: true}), () =>\n this.inputRef.current!.focus(),\n );\n }\n\n /** Cancels any of the open dialogs. */\n private handleClose() {\n this.setState(\n Object.assign({}, this.state, {\n dialogOpen: false,\n }),\n );\n }\n\n /** Select button clicked in the \"Select WikiTree ID\" dialog. */\n private handleSelectId() {\n this.setState(\n Object.assign({}, this.state, {\n dialogOpen: 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 WikiTree ID input is typed into. */\n private handleIdChange(value: string) {\n this.setState(\n Object.assign({}, this.state, {\n wikiTreeId: value,\n }),\n );\n }\n\n private enterId(event: React.MouseEvent, id: string) {\n event.preventDefault(); // Do not follow link in href.\n ((this.inputRef.current as unknown) as {\n inputRef: HTMLInputElement;\n }).inputRef.value = id;\n this.handleIdChange(id);\n this.inputRef.current!.focus();\n }\n\n private wikiTreeIdModal() {\n return (\n this.handleClose()}\n centered={false}\n >\n
\n \n \n
\n \n
this.handleSelectId()}>\n

\n \n WikiTree\n \n ),\n example1: (\n this.enterId(e, 'Wojtyla-13')}\n className=\"link-span\"\n >\n Wojtyla-13\n \n ),\n example2: (\n this.enterId(e, 'Skłodowska-2')}\n className=\"link-span\"\n >\n Skłodowska-2\n \n ),\n }}\n />\n

\n this.handleIdChange(data.value)}\n ref={this.inputRef}\n />\n \n
\n \n \n \n \n \n );\n }\n\n render() {\n return (\n <>\n this.openDialog()}\n >\n \"WikiTree\n \n \n {this.wikiTreeIdModal()}\n \n );\n }\n}\n\ninterface LoginState {\n wikiTreeLoginState: WikiTreeLoginState;\n wikiTreeLoginUsername?: string;\n}\n\n/** Displays and handles the \"Log in to WikiTree\" menu. */\nclass WikiTreeLoginMenuComponent extends React.Component<\n RouteComponentProps & WrappedComponentProps & Props,\n LoginState\n> {\n state: LoginState = {\n wikiTreeLoginState: WikiTreeLoginState.UNKNOWN,\n };\n\n wikiTreeLoginFormRef: React.RefObject = React.createRef();\n wikiTreeReturnUrlRef: React.RefObject = React.createRef();\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 wikiTreeLoginUsername = getLoggedInUserName();\n const wikiTreeLoginState = wikiTreeLoginUsername\n ? WikiTreeLoginState.LOGGED_IN\n : WikiTreeLoginState.NOT_LOGGED_IN;\n if (this.state.wikiTreeLoginState !== wikiTreeLoginState) {\n this.setState(\n Object.assign({}, this.state, {\n wikiTreeLoginState,\n wikiTreeLoginUsername,\n }),\n );\n }\n }\n\n componentDidMount() {\n this.checkWikiTreeLoginState();\n }\n\n componentDidUpdate() {\n this.checkWikiTreeLoginState();\n }\n\n render() {\n switch (this.state.wikiTreeLoginState) {\n case WikiTreeLoginState.NOT_LOGGED_IN:\n return (\n <>\n this.wikiTreeLogin()}\n >\n \n \n \n \n \n \n \n \n );\n\n case WikiTreeLoginState.LOGGED_IN:\n const tooltip = this.state.wikiTreeLoginUsername\n ? this.props.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.props.intl.formatMessage({\n id: 'menu.wikitree_popup',\n defaultMessage: 'Logged in to WikiTree',\n });\n return (\n \n \"WikiTree\n \n \n );\n }\n return null;\n }\n}\nexport const WikiTreeLoginMenu = injectIntl(WikiTreeLoginMenuComponent);\n","import * as queryString from 'query-string';\nimport * as React from 'react';\nimport {Dropdown, Icon, Menu} from 'semantic-ui-react';\nimport {FormattedMessage} from 'react-intl';\nimport {IndiInfo, JsonGedcomData} from 'topola';\nimport {Link} from 'react-router-dom';\nimport {Media} from '../util/media';\nimport {MenuType} from './menu_item';\nimport {RouteComponentProps} from 'react-router-dom';\nimport {SearchBar} from './search';\nimport {UploadMenu} from './upload_menu';\nimport {UrlMenu} from './url_menu';\nimport {WikiTreeLoginMenu, WikiTreeMenu} from './wikitree_menu';\n\nenum ScreenSize {\n LARGE,\n SMALL,\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\nexport class TopBar extends React.Component {\n private 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 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 \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 // 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 case ScreenSize.SMALL:\n return (\n <>\n \n \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 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 \n \n \n \n ) : (\n <>\n \n \n \n \n );\n return menus;\n\n case ScreenSize.SMALL:\n return (\n <>\n \n \n \n \n \n );\n }\n }\n\n private wikiTreeLoginMenu(screenSize: ScreenSize) {\n if (!this.props.showWikiTreeMenus) {\n return null;\n }\n return (\n <>\n \n {screenSize === ScreenSize.SMALL ? : 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 './util/analytics';\nimport {Chart, ChartComponent, ChartType} from './chart';\nimport {DataSourceEnum, SourceSelection} from './datasource/data_source';\nimport {Details} from './details';\nimport {EmbeddedDataSource, EmbeddedSourceSpec} from './datasource/embedded';\nimport {FormattedMessage, WrappedComponentProps} from 'react-intl';\nimport {getI18nMessage} from './util/error_i18n';\nimport {IndiInfo} from 'topola';\nimport {Intro} from './intro';\nimport {Loader, Message, Portal} from 'semantic-ui-react';\nimport {Media} from './util/media';\nimport {Redirect, Route, RouteComponentProps, Switch} from 'react-router-dom';\nimport {TopBar} from './menu/top_bar';\nimport {TopolaData} from './util/gedcom_util';\nimport {\n getSelection,\n UploadSourceSpec,\n UrlSourceSpec,\n GedcomUrlDataSource,\n UploadedDataSource,\n} from './datasource/load_data';\nimport {\n loadWikiTree,\n PRIVATE_ID_PREFIX,\n WikiTreeDataSource,\n WikiTreeSourceSpec,\n} from './datasource/wikitree';\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\nenum AppState {\n INITIAL,\n LOADING,\n ERROR,\n SHOWING_CHART,\n LOADING_MORE,\n}\n\ntype DataSourceSpec =\n | UrlSourceSpec\n | UploadSourceSpec\n | WikiTreeSourceSpec\n | EmbeddedSourceSpec;\n\n/** Arguments passed to the application, primarily through URL parameters. */\ninterface Arguments {\n sourceSpec?: DataSourceSpec;\n selection?: IndiInfo;\n chartType: ChartType;\n standalone: boolean;\n freezeAnimation?: boolean;\n showSidePanel: boolean;\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 view = getParam('view');\n const chartTypes = new Map([\n ['relatives', ChartType.Relatives],\n ['fancy', ChartType.Fancy],\n ]);\n\n const hash = getParam('file');\n const url = getParam('url');\n const embedded = getParam('embedded') === 'true'; // False by default.\n var sourceSpec: DataSourceSpec | undefined = undefined;\n if (getParam('source') === 'wikitree') {\n sourceSpec = {\n source: DataSourceEnum.WIKITREE,\n authcode: getParam('?authcode'),\n };\n } else if (hash) {\n sourceSpec = {\n source: DataSourceEnum.UPLOADED,\n hash,\n gedcom: location.state && location.state.data,\n images: location.state && location.state.images,\n };\n } else if (url) {\n sourceSpec = {\n source: DataSourceEnum.GEDCOM_URL,\n url,\n handleCors: getParam('handleCors') !== 'false', // True by default.\n };\n } else if (embedded) {\n sourceSpec = {source: DataSourceEnum.EMBEDDED};\n }\n\n const indi = getParam('indi');\n const parsedGen = Number(getParam('gen'));\n const selection = indi\n ? {id: indi, generation: !isNaN(parsedGen) ? parsedGen : 0}\n : undefined;\n\n return {\n sourceSpec,\n selection,\n // Hourglass is the default view.\n chartType: chartTypes.get(view) || ChartType.Hourglass,\n\n showSidePanel: getParam('sidePanel') !== 'false', // True by default.\n standalone: getParam('standalone') !== 'false' && !embedded,\n freezeAnimation: getParam('freeze') === 'true', // False by default\n };\n}\n\n/**\n * Returs true if the changes object has values that are different than those\n * in state.\n */\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 /** State of the application. */\n state: AppState;\n /** Loaded data. */\n data?: TopolaData;\n /** Selected individual. */\n selection?: IndiInfo;\n /** Error to display. */\n error?: string;\n /** Whether the side panel is shown. */\n showSidePanel?: 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 /** Specification of the source of the data. */\n sourceSpec?: DataSourceSpec;\n /** Freeze animations after initial chart render. */\n freezeAnimation?: boolean;\n}\n\nexport class App extends React.Component<\n RouteComponentProps & WrappedComponentProps,\n {}\n> {\n state: State = {\n state: AppState.INITIAL,\n standalone: true,\n chartType: ChartType.Hourglass,\n showErrorPopup: false,\n };\n chartRef: ChartComponent | null = null;\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 state: AppState.ERROR,\n error,\n }),\n );\n }\n\n componentDidMount() {\n this.componentDidUpdate();\n }\n\n private readonly uploadedDataSource = new UploadedDataSource();\n private readonly gedcomUrlDataSource = new GedcomUrlDataSource();\n private readonly wikiTreeDataSource = new WikiTreeDataSource(this.props.intl);\n private readonly embeddedDataSource = new EmbeddedDataSource();\n\n private isNewData(sourceSpec: DataSourceSpec, selection?: IndiInfo) {\n if (\n !this.state.sourceSpec ||\n this.state.sourceSpec.source !== sourceSpec.source\n ) {\n // New data source means new data.\n return true;\n }\n const newSource = {spec: sourceSpec, selection};\n const oldSouce = {\n spec: this.state.sourceSpec,\n selection: this.state.selection,\n };\n switch (newSource.spec.source) {\n case DataSourceEnum.UPLOADED:\n return this.uploadedDataSource.isNewData(\n newSource as SourceSelection,\n oldSouce as SourceSelection,\n this.state.data,\n );\n case DataSourceEnum.GEDCOM_URL:\n return this.gedcomUrlDataSource.isNewData(\n newSource as SourceSelection,\n oldSouce as SourceSelection,\n this.state.data,\n );\n case DataSourceEnum.WIKITREE:\n return this.wikiTreeDataSource.isNewData(\n newSource as SourceSelection,\n oldSouce as SourceSelection,\n this.state.data,\n );\n case DataSourceEnum.EMBEDDED:\n return this.embeddedDataSource.isNewData(\n newSource as SourceSelection,\n oldSouce as SourceSelection,\n this.state.data,\n );\n }\n }\n\n private loadData(sourceSpec: DataSourceSpec, selection?: IndiInfo) {\n switch (sourceSpec.source) {\n case DataSourceEnum.UPLOADED:\n return this.uploadedDataSource.loadData({spec: sourceSpec, selection});\n case DataSourceEnum.GEDCOM_URL:\n return this.gedcomUrlDataSource.loadData({spec: sourceSpec, selection});\n case DataSourceEnum.WIKITREE:\n return this.wikiTreeDataSource.loadData({spec: sourceSpec, selection});\n case DataSourceEnum.EMBEDDED:\n return this.embeddedDataSource.loadData({spec: sourceSpec, selection});\n }\n }\n\n async componentDidUpdate() {\n if (this.props.location.pathname !== '/view') {\n if (this.state.state !== AppState.INITIAL) {\n this.setState(Object.assign({}, this.state, {state: AppState.INITIAL}));\n }\n return;\n }\n\n const args = getArguments(this.props.location);\n\n if (!args.sourceSpec) {\n this.props.history.replace({pathname: '/'});\n } else if (\n this.state.state === AppState.INITIAL ||\n this.isNewData(args.sourceSpec, args.selection)\n ) {\n // Set loading state.\n this.setState(\n Object.assign({}, this.state, {\n state: AppState.LOADING,\n sourceSpec: args.sourceSpec,\n selection: args.selection,\n standalone: args.standalone,\n chartType: args.chartType,\n }),\n );\n try {\n const data = await this.loadData(args.sourceSpec, args.selection);\n // Set state with data.\n this.setState(\n Object.assign({}, this.state, {\n state: AppState.SHOWING_CHART,\n data,\n selection: getSelection(data.chartData, args.selection),\n showSidePanel: args.showSidePanel,\n }),\n );\n } catch (error) {\n this.setError(getI18nMessage(error, this.props.intl));\n }\n } else if (\n this.state.state === AppState.SHOWING_CHART ||\n this.state.state === AppState.LOADING_MORE\n ) {\n // Update selection if it has changed in the URL.\n const selection = getSelection(\n this.state.data!.chartData,\n args.selection,\n );\n const loadMoreFromWikitree =\n args.sourceSpec.source === DataSourceEnum.WIKITREE &&\n (!this.state.selection || this.state.selection.id !== selection.id);\n this.updateDisplay(selection, {\n chartType: args.chartType,\n state: loadMoreFromWikitree\n ? AppState.LOADING_MORE\n : AppState.SHOWING_CHART,\n });\n if (loadMoreFromWikitree) {\n try {\n const data = await loadWikiTree(args.selection!.id, this.props.intl);\n const selection = getSelection(data.chartData, args.selection);\n this.setState(\n Object.assign({}, this.state, {\n state: AppState.SHOWING_CHART,\n data,\n selection,\n }),\n );\n } catch (error) {\n this.showErrorPopup(\n this.props.intl.formatMessage(\n {\n id: 'error.failed_wikitree_load_more',\n defaultMessage: 'Failed to load data from WikiTree. {error}',\n },\n {error},\n ),\n {state: AppState.SHOWING_CHART},\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 // Don't allow selecting WikiTree private profiles.\n if (selection.id.startsWith(PRIVATE_ID_PREFIX)) {\n return;\n }\n analyticsEvent('selection_changed');\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, otherStateChanges?: Partial) {\n this.setState(\n Object.assign(\n {},\n this.state,\n {\n showErrorPopup: true,\n error: message,\n },\n otherStateChanges,\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.props.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.props.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 private onDismissErrorPopup = () => {\n this.setState(\n Object.assign({}, this.state, {\n showErrorPopup: false,\n }),\n );\n };\n\n private renderMainArea = () => {\n switch (this.state.state) {\n case AppState.SHOWING_CHART:\n case AppState.LOADING_MORE:\n return (\n
\n \n {this.state.state === AppState.LOADING_MORE ? (\n \n ) : null}\n (this.chartRef = ref)}\n />\n {this.state.showSidePanel ? (\n \n \n \n ) : null}\n
\n );\n\n case AppState.ERROR:\n return ;\n\n case AppState.INITIAL:\n case AppState.LOADING:\n return ;\n }\n };\n\n render() {\n return (\n <>\n (\n \n )}\n />\n \n \n \n \n \n \n );\n }\n}\n","import {IntlShape} from 'react-intl';\nimport {TopolaError} from './error';\n\n/**\n * Returns a translated message for the given error. If the message can't be\n * translated, the original error.message is returned.\n */\nexport function getI18nMessage(error: Error, intl: IntlShape): string {\n if (!(error instanceof TopolaError)) {\n return error.message;\n }\n return intl.formatMessage(\n {\n id: `error.${error.code}`,\n defaultMessage: error.message,\n },\n error.args,\n );\n}\n","import * as React from 'react';\nimport * as ReactDOM from 'react-dom';\nimport messages_cs from './translations/cs.json';\nimport messages_de from './translations/de.json';\nimport messages_fr from './translations/fr.json';\nimport messages_it from './translations/it.json';\nimport messages_pl from './translations/pl.json';\nimport messages_ru from './translations/ru.json';\nimport {App} from './app';\nimport {detect} from 'detect-browser';\nimport {HashRouter as Router, Route} from 'react-router-dom';\nimport {IntlProvider} from 'react-intl';\nimport {MediaContextProvider, mediaStyles} from './util/media';\nimport './index.css';\nimport 'semantic-ui-css/semantic.min.css';\nimport 'canvas-toBlob';\n\nconst messages = {\n cs: messages_cs,\n de: messages_de,\n fr: messages_fr,\n it: messages_it,\n pl: messages_pl,\n ru: messages_ru,\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 (modern) browser.\n

,\n document.querySelector('#root'),\n );\n} else {\n ReactDOM.render(\n \n \n \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.72fe0747.chunk.js b/static/js/main.d5fb1703.chunk.js similarity index 75% rename from static/js/main.72fe0747.chunk.js rename to static/js/main.d5fb1703.chunk.js index 0270fbb..6250026 100644 --- a/static/js/main.72fe0747.chunk.js +++ b/static/js/main.d5fb1703.chunk.js @@ -1,2 +1,2 @@ -(this["webpackJsonptopola-viewer"]=this["webpackJsonptopola-viewer"]||[]).push([[0],{239:function(e){e.exports=JSON.parse('{"menu.open":"Otev\u0159\xedt","menu.open_file":"Otev\u0159\xedt soubor","menu.load_from_url":"Otev\u0159\xedt z URL","menu.select_wikitree_id":"Vybrat WikiTree ID","menu.print":"Tiskni","menu.download":"St\xe1hnout","menu.pdf_file":"Soubor PDF","menu.png_file":"Soubor PNG","menu.svg_file":"Soubor SVG","menu.download_pdf":"St\xe1hnout PDF","menu.download_png":"St\xe1hnout PNG","menu.download_svg":"St\xe1hnout SVG","menu.view":"Zobrazen\xed","menu.hourglass":"P\u0159es\xfdpac\xed hodiny","menu.relatives":"V\u0161ichni p\u0159\xedbuzn\xed","menu.fancy":"Rodinn\xfd strom (experiment\xe1ln\xed)","menu.wikitree_login":"P\u0159ihl\xe1\u0161en\xed na WikiTree","menu.wikitree_logged_in":"P\u0159ihl\xe1\u0161eno","menu.wikitree_popup_username":"P\u0159ihl\xe1\u0161eno do WikiTree jako {username}","menu.wikitree_popup":"P\u0159ihl\xe1\u0161eno do WikiTree","menu.github":"GitHub projekt","menu.search.placeholder":"Hledej osobu","menu.search.no_results":"\u017d\xe1dn\xe9 v\xfdsledky","intro.title":"Topola Genealogy","intro.description":"Topola Genealogy v\xe1m umo\u017e\u0148uje interaktivn\xed prohl\xed\u017een\xed rodokmenu.","intro.instructions":"Kliknut\xedm na OTEV\u0158\xcdT SOUBOR nebo OTEV\u0158\xcdT Z URL na\u010dt\u011bte soubor GEDCOM. V\u011bt\u0161ina genealogick\xfdch program\u016f m\xe1 funkci exportu do form\xe1tu GEDCOM.","intro.examples":"N\xed\u017ee je uvedeno n\u011bkolik p\u0159\xedklad\u016f pou\u017eit\xed z internetu:","intro.from":"zdroj:","intro.privacy":"Soukrom\xed","intro.privacy_note":"Pomoc\xed funkce \\"Otev\u0159\xedt soubor\\" nejsou va\u0161e data nikam odes\xedl\xe1na a z\u016fst\xe1vaj\xed ve va\u0161em po\u010d\xedta\u010di. P\u0159i pou\u017eit\xed funkce \\"Otev\u0159\xedt z URL\\" jsou data z dan\xe9 adresy odesl\xe1na slu\u017ebou {link}, aby bylo mo\u017en\xe9 data na\u010d\xedst (CORS).","load_from_url.title":"Otev\u0159\xedt z adresy URL","load_from_url.comment":"Data z dan\xe9 adresy URL budou na\u010dteny prost\u0159ednictv\xedm slu\u017eby {link}, aby nedoch\xe1zelo k probl\xe9m\u016fm s CORS.","load_from_url.cancel":"Zru\u0161it","load_from_url.load":"Otev\u0159\xedt","select_wikitree_id.title":"Zadejte WikiTree ID","select_wikitree_id.comment":"Zadejte ID profilu {wikiTreeLink}. P\u0159\xedklad: {example1}, {example2}","select_wikitree_id.cancel":"Zru\u0161it","select_wikitree_id.load":"Otev\u0159\xedt","gedcom.BAPM":"K\u0159est","gedcom.BIRT":"Narozen\xed","gedcom.BURI":"Poh\u0159eb","gedcom.CENS":"S\u010d\xedt\xe1n\xed lidu","gedcom.CHR":"K\u0159est","gedcom.DEAT":"\xdamrt\xed","gedcom.DSCR":"Opis","gedcom.EDUC":"Vzd\u011bl\xe1n\xed","gedcom.EMAIL":"E-mail","gedcom.EVEN":"Ud\xe1lost","gedcom.FACT":"Skute\u010dnost","gedcom.MILT":"Vojensk\xe1 slu\u017eba","gedcom.OCCU":"Povol\xe1n\xed","gedcom.RIN":"ID","gedcom.TITL":"Titul","gedcom.WWW":"Str\xe1nka WWW","gedcom.RELI":"Vyzn\xe1n\xed","gedcom._UPD":"Posledn\xed aktualizace","date.abt":"kolem","date.cal":"spo\u010dteno","date.est":"asi","date.between":"mezi {from} a {to}","date.after":"po {from}","date.before":"p\u0159ed {to}","error.error":"Chyba","error.failed_pdf":"Soubor PDF nelze vytvo\u0159it. Zkuste to znovu s men\u0161\xedm diagramem nebo st\xe1hn\u011bte soubor SVG.","error.failed_png":"Soubor PNG nelze vytvo\u0159it. Zkuste to znovu s men\u0161\xedm diagramem nebo st\xe1hn\u011bte soubor SVG.","error.failed_to_load_file":"Chyba p\u0159i na\u010d\xedt\xe1n\xed souboru","error.failed_wikitree_load_more":"Chyba p\u0159i na\u010d\xedt\xe1n\xed dat z WikiTree. {chyba}","error.GEDCOM_READ_FAILED":"Chyba p\u0159i na\u010d\xedt\xe1n\xed souboru GEDCOM","error.ERROR_LOADING_UPLOADED_FILE":"Chyba p\u0159i na\u010d\xedt\xe1n\xed dat. Znovu otev\u0159ete soubor.","error.WIKITREE_ID_NOT_PROVIDED":"WikiTree ID nebylo z\xedsk\xe1no","error.WIKITREE_PROFILE_NOT_ACCESSIBLE":"Profil WikiTree {id} nen\xed k dispozici","error.WIKITREE_PROFILE_NOT_FOUND":"Profil WikiTree {id} neexistuje","wikitree.private":"Soukrom\xe9"}')},240:function(e){e.exports=JSON.parse('{"menu.open":"\xd6ffnen","menu.open_file":"Datei \xf6ffnen","menu.load_from_url":"URL \xf6ffnen","menu.select_wikitree_id":"WikiTree ID ausw\xe4hlen","menu.print":"Print","menu.download":"Download","menu.pdf_file":"PDF-Datei","menu.png_file":"PNG-Datei","menu.svg_file":"SVG-Datei","menu.download_pdf":"PDF herunterladen","menu.download_png":"PNG herunterladen","menu.download_svg":"SVG herunterladen","menu.view":"View","menu.hourglass":"Sanduhrkarte","menu.relatives":"Alle Verwandten","menu.fancy":"Zierbaum (experimentell)","menu.wikitree_login":"Bei WikiTree anmelden","menu.wikitree_logged_in":"Eingeloggt","menu.wikitree_popup_username":"Bei WikiTree als {username} angemeldet","menu.wikitree_popup":"Bei WikiTree angemeldet","menu.github":"Projekt auf der GitHub-Website","menu.search.placeholder":"Person suchen","menu.search.no_results":"Keine Ergebnisse","intro.title":"Topola Genealogie","intro.description":"Mit der Topola Genealogie k\xf6nnen Sie den Stammbaum auf interaktive Weise durchsuchen.","intro.instructions":"Klicken Sie auf Datei \xf6ffnen oder URL \xf6ffnen, um eine GEDCOM-Datei zu laden. Die meisten Genealogie-Programme verf\xfcgen \xfcber eine GEDCOM-Exportfunktion.","intro.examples":"Nachfolgend einige Beispiele aus dem Internet:","intro.from":"source:","intro.privacy":"Datenschutz","intro.privacy_note":"Bei Verwendung der Funktion \\"Datei \xf6ffnen\\" werden Ihre Daten nirgendwo gesendet und verbleiben auf Ihrem Computer. Bei Verwendung der Funktion \\"URL \xf6ffnen\\" werden Daten von der angegebenen Adresse vom Dienst {link} an gesendet Erm\xf6glichen des Ladens von Daten aus einer anderen Dom\xe4ne (CORS).","load_from_url.title":"Von URL \xf6ffnen","load_from_url.comment":"Die Daten der angegebenen URL werden \xfcber den {link} -Dienst geladen, um CORS-Probleme zu vermeiden.","load_from_url.cancel":"Cancel","load_from_url.load":"\xd6ffnen","select_wikitree_id.title":"WikiTree ID eingeben","select_wikitree_id.comment":"Geben Sie die Profil-ID {wikiTreeLink} ein. Beispiele: {example1}, {example2}","select_wikitree_id.cancel":"Cancel","select_wikitree_id.load":"Open","gedcom.BAPM":"Taufe","gedcom.BIRT":"Geburt","gedcom.BURI":"Beerdigung","gedcom.CENS":"Volksz\xe4hlung","gedcom.CHR":"Taufe","gedcom.DEAT":"Tod","gedcom.DSCR":"Beschreibung","gedcom.EMAIL":"E-Mail","gedcom.EVEN":"Ereignis","gedcom.OCCU":"Beruf","gedcom.RIN":"ID","gedcom.TITL":"Titel","gedcom.WWW":"Website","gedcom._UPD":"Zuletzt aktualisiert","date.abt":"about","date.cal":"berechnet","date.est":"gesch\xe4tzt","date.between":"zwischen {from} und {to}","date.after":"after {from}","date.before":"before {to}","error.error":"Error","error.failed_pdf":"PDF konnte nicht erstellt werden. Versuchen Sie es erneut mit einem kleineren Diagramm oder laden Sie die SVG-Datei herunter.","error.failed_png":"Fehler beim Erstellen der PNG-Datei. Versuchen Sie es erneut mit einem kleineren Diagramm oder laden Sie die SVG-Datei herunter.","error.failed_to_load_file":"Fehler beim Laden der Datei","error.failed_wikitree_load_more":"Fehler beim Abrufen der Daten aus WikiTree. {error}","wikitree.private":"Privat"}')},241:function(e){e.exports=JSON.parse('{"menu.open":"Ouvrir","menu.open_file":"Ouvrir le fichier","menu.load_from_url":"Ouvrir l\'URL","menu.select_wikitree_id":"S\xe9lectionnez l\'ID WikiTree","menu.print":"Imprimer","menu.download":"T\xe9l\xe9charger","menu.pdf_file":"Fichier PDF","menu.png_file":"Fichier PNG","menu.svg_file":"Fichier SVG","menu.download_pdf":"T\xe9l\xe9charger le PDF","menu.download_png":"T\xe9l\xe9charger PNG","menu.download_svg":"T\xe9l\xe9charger SVG","menu.view":"Afficher","menu.hourglass":"Graphique en sablier","menu.relatives":"Tous les parents","menu.fancy":"Arbre ornemental (exp\xe9rimental)","menu.wikitree_login":"Connectez-vous \xe0 WikiTree","menu.wikitree_logged_in":"Connect\xe9","menu.wikitree_popup_username":"Connect\xe9 \xe0 WikiTree en tant que {username}","menu.wikitree_popup":"Connect\xe9 \xe0 WikiTree","menu.github":"Projet sur le site Web GitHub","menu.search.placeholder":"Rechercher une personne","menu.search.no_results":"Aucun r\xe9sultat","intro.title":"Topola G\xe9n\xe9alogie","intro.description":"La Topola G\xe9n\xe9alogie vous permet de parcourir l\'arbre g\xe9n\xe9alogique de mani\xe8re interactive.","intro.instructions":"Cliquez sur OPEN FILE ou OPEN URL pour charger un fichier GEDCOM. La plupart des logiciels de g\xe9n\xe9alogie ont une fonction d\'exportation GEDCOM.","intro.examples":"Voici quelques exemples trouv\xe9s sur Internet:","intro.from":"source:","intro.privacy":"Confidentialit\xe9","intro.privacy_note":"Lorsque vous utilisez la fonction \\"Ouvrir un fichier\\", vos donn\xe9es ne sont envoy\xe9es nulle part et restent sur votre ordinateur. Lorsque vous utilisez la fonction \\"Ouvrir l\'URL\\", les donn\xe9es de l\'adresse indiqu\xe9e sont envoy\xe9es par le service {link} \xe0 permettant le chargement des donn\xe9es depuis un autre domaine (CORS).","load_from_url.title":"Ouvrir depuis l\'URL","load_from_url.comment":"Les donn\xe9es de l\'URL donn\xe9e seront charg\xe9es via le service {link} pour \xe9viter les probl\xe8mes CORS.","load_from_url.cancel":"Annuler","load_from_url.load":"Ouvrir","select_wikitree_id.title":"Entrez l\'ID de WikiTree","select_wikitree_id.comment":"Entrez l\'ID de profil {wikiTreeLink}. Exemples: {example1}, {example2}","select_wikitree_id.cancel":"Annuler","select_wikitree_id.load":"Ouvrir","gedcom.BAPM":"Bapt\xeame","gedcom.BIRT":"Naissance","gedcom.BURI":"Fun\xe9railles","gedcom.CENS":"Recensement","gedcom.CHR":"Bapt\xeame","gedcom.DEAT":"Mort","gedcom.DSCR":"Description","gedcom.EMAIL":"E-mail","gedcom.EVEN":"\xc9v\xe9nement","gedcom.OCCU":"Profession","gedcom.RIN":"ID","gedcom.TITL":"Titre","gedcom.WWW":"Site Web","gedcom._UPD":"Derni\xe8re mise \xe0 jour","date.abt":"environ","date.cal":"calcul\xe9","date.est":"estim\xe9","date.between":"entre {from} et {to}","date.after":"apr\xe8s {from}","date.before":"avant {to}","error.error":"Erreur","error.failed_pdf":"\xc9chec de la cr\xe9ation du PDF. R\xe9essayez avec un diagramme plus petit ou t\xe9l\xe9chargez le fichier SVG.","error.failed_png":"Impossible de cr\xe9er le fichier PNG. R\xe9essayez avec un diagramme plus petit ou t\xe9l\xe9chargez le fichier SVG.","error.failed_to_load_file":"Erreur lors du chargement du fichier","error.failed_wikitree_load_more":"Erreur lors de l\'obtention des donn\xe9es de WikiTree. {error}","wikitree.private":"Priv\xe9"}')},242:function(e){e.exports=JSON.parse('{"menu.open":"Apri","menu.open_file":"Apri file","menu.load_from_url":"Apri URL","menu.select_wikitree_id":"Seleziona ID WikiTree","menu.print":"Stampa","menu.download":"Download","menu.pdf_file":"File PDF","menu.png_file":"File PNG","menu.svg_file":"File SVG","menu.download_pdf":"Scarica PDF","menu.download_png":"Scarica PNG","menu.download_svg":"Scarica SVG","menu.view":"Visualizza","menu.hourglass":"Grafico a clessidra","menu.relatives":"Tutti i parenti","menu.fancy":"Albero ornamentale (sperimentale)","menu.wikitree_login":"Accedi a WikiTree","menu.wikitree_logged_in":"Accesso effettuato","menu.wikitree_popup_username":"Accesso a WikiTree come {username}","menu.wikitree_popup":"Accesso a WikiTree","menu.github":"Progetto sul sito web GitHub","menu.search.placeholder":"Cerca persona","menu.search.no_results":"Nessun risultato","intro.title":"Topola Genealogy","intro.description":"Topola Genealogy ti consente di esplorare l\'albero genealogico in modo interattivo.","intro.instructions":"Fai clic su OPEN FILE o OPEN URL per caricare un file GEDCOM. La maggior parte dei software di genealogia ha una funzione di esportazione GEDCOM.","intro.examples":"Di seguito sono riportati alcuni esempi trovati su Internet:","intro.from":"source:","intro.privacy":"Privacy","intro.privacy_note":"Quando si utilizza la funzione \\"Apri file\\", i dati non vengono inviati da nessuna parte e rimangono sul computer. Quando si utilizza la funzione \\"Apri URL\\", i dati dall\'indirizzo specificato vengono inviati dal servizio {link} a consentire il caricamento dei dati da un altro dominio (CORS).","load_from_url.title":"Apri da URL","load_from_url.comment":"I dati dall\'URL specificato verranno caricati tramite il servizio {link} per evitare problemi CORS.","load_from_url.cancel":"Annulla","load_from_url.load":"Apri","select_wikitree_id.title":"Inserisci ID WikiTree","select_wikitree_id.comment":"Inserisci l\'ID profilo {wikiTreeLink}. Esempi: {example1}, {example2}","select_wikitree_id.cancel":"Annulla","select_wikitree_id.load":"Apri","gedcom.BAPM":"Battesimo","gedcom.BIRT":"Nascita","gedcom.BURI":"Funerale","gedcom.CENS":"Census","gedcom.CHR":"Battesimo","gedcom.DEAT":"Death","gedcom.DSCR":"Descrizione","gedcom.EMAIL":"E-mail","gedcom.EVEN":"Evento","gedcom.OCCU":"Professione","gedcom.RIN":"ID","gedcom.TITL":"Titolo","gedcom.WWW":"Sito web","gedcom._UPD":"Ultimo aggiornamento","date.abt":"about","date.cal":"calcolato","date.est":"stimato","date.between":"tra {from} e {to}","date.after":"after {from}","date.before":"before {to}","error.error":"Error","error.failed_pdf":"Impossibile creare PDF. Riprova con un diagramma pi\xf9 piccolo o scarica il file SVG.","error.failed_png":"Impossibile creare il file PNG. Riprova con un diagramma pi\xf9 piccolo o scarica il file SVG.","error.failed_to_load_file":"Errore durante il caricamento del file","error.failed_wikitree_load_more":"Errore durante il recupero dei dati da WikiTree. {error}","wikitree.private":"Privato"}')},243:function(e){e.exports=JSON.parse('{"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.EDUC":"Wykszta\u0142cenie","gedcom.EMAIL":"E-mail","gedcom.EVEN":"Wydarzenie","gedcom.FACT":"Fakt","gedcom.MILT":"S\u0142u\u017cba wojskowa","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","error.failed_wikitree_load_more":"B\u0142\u0105d podczas pobierania danych z WikiTree. {error}","error.GEDCOM_READ_FAILED":"B\u0142\u0105d wczytywania pliku GEDCOM","error.ERROR_LOADING_UPLOADED_FILE":"B\u0142\u0105d wczytywania danych. Otw\xf3rz ponownie plik.","error.WIKITREE_ID_NOT_PROVIDED":"Identyfikator WikiTree nie zosta\u0142 podany","error.WIKITREE_PROFILE_NOT_ACCESSIBLE":"Profil WikiTree {id} nie jest dost\u0119pny","error.WIKITREE_PROFILE_NOT_FOUND":"Profil WikiTree {id} nie istnieje","wikitree.private":"Prywatne"}')},244:function(e){e.exports=JSON.parse('{"menu.open":"\u041e\u0442\u043a\u0440\u044b\u0442\u044c","menu.open_file":"\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u0444\u0430\u0439\u043b","menu.load_from_url":"\u041e\u0442\u043a\u0440\u044b\u0442\u044c URL","menu.select_wikitree_id":"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 WikiTree ID","menu.print":"\u041f\u0435\u0447\u0430\u0442\u044c","menu.download":"\u0421\u043a\u0430\u0447\u0430\u0442\u044c","menu.pdf_file":"\u0424\u0430\u0439\u043b PDF","menu.png_file":"\u0424\u0430\u0439\u043b PNG","menu.svg_file":"\u0424\u0430\u0439\u043b SVG","menu.download_pdf":"\u0421\u043a\u0430\u0447\u0430\u0442\u044c PDF","menu.download_png":"\u0421\u043a\u0430\u0447\u0430\u0442\u044c PNG","menu.download_svg":"\u0421\u043a\u0430\u0447\u0430\u0442\u044c SVG","menu.view":"\u0412\u0438\u0434","menu.hourglass":"\u0413\u0440\u0430\u0444\u0438\u043a \u041f\u0435\u0441\u043e\u0447\u043d\u044b\u0435 \u0447\u0430\u0441\u044b","menu.relatives":"\u0412\u0441\u0435 \u0440\u043e\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u0438\u043a\u0438","menu.fancy":"\u0414\u0435\u043a\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0435 \u0434\u0435\u0440\u0435\u0432\u043e (\u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u043e\u0435))","menu.wikitree_login":"\u0412\u043e\u0439\u0442\u0438 \u0432 WikiTree","menu.wikitree_logged_in":"\u0412\u0445\u043e\u0434 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d","menu.wikitree_popup_username":"\u0412\u044b \u0432\u043e\u0448\u043b\u0438 \u0432 WikiTree \u043a\u0430\u043a {username}","menu.wikitree_popup":"\u0412\u044b \u0432\u043e\u0448\u043b\u0438 \u0432 WikiTree","menu.github":"\u041f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 GitHub","menu.search.placeholder":"\u0418\u0441\u043a\u0430\u0442\u044c \u0447\u0435\u043b\u043e\u0432\u0435\u043a\u0430","menu.search.no_results":"\u041d\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432","intro.title":"Topola Genealogy","intro.description":"Topola Genealogy \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0440\u043e\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c \u0441\u0435\u043c\u0435\u0439\u043d\u043e\u0435 \u0434\u0440\u0435\u0432\u043e \u0432 \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435.","intro.instructions":"\u0429\u0435\u043b\u043a\u043d\u0438\u0442\u0435 \\"\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u0444\u0430\u0439\u043b\\" \u0438\u043b\u0438 \\"\u041e\u0442\u043a\u0440\u044b\u0442\u044c URL\\", \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b GEDCOM. \u0411\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0430\u043b\u043e\u0433\u0438\u0438 \u0438\u043c\u0435\u044e\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0430 GEDCOM.","intro.examples":"\u041d\u0438\u0436\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u044b \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b, \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0435 \u0432 \u0418\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435:","intro.from":"\u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a:","intro.privacy":"\u041a\u043e\u043d\u0444\u0438\u0434\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c","intro.privacy_note":"\u041f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \\"\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u0444\u0430\u0439\u043b\\" \u0412\u0430\u0448\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0438\u043a\u0443\u0434\u0430 \u043d\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0438 \u043e\u0441\u0442\u0430\u044e\u0442\u0441\u044f \u043d\u0430 \u0412\u0430\u0448\u0435\u043c \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0435. \u041f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \\"\u041e\u0442\u043a\u0440\u044b\u0442\u044c URL\\" \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u0430\u0434\u0440\u0435\u0441\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0441\u043b\u0443\u0436\u0431\u043e\u0439 {link} \u043d\u0430 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u0434\u043e\u043c\u0435\u043d\u0430 (CORS).","load_from_url.title":"\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u0441 \u0430\u0434\u0440\u0435\u0441\u0430 URL","load_from_url.comment":"\u0414\u0430\u043d\u043d\u044b\u0435 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u0430\u0434\u0440\u0435\u0441\u0430 URL \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u044b \u0447\u0435\u0440\u0435\u0437 \u0441\u043b\u0443\u0436\u0431\u0443 {link}, \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c CORS.","load_from_url.cancel":"\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c","load_from_url.load":"\u041e\u0442\u043a\u0440\u044b\u0442\u044c","select_wikitree_id.title":"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 WikiTree","select_wikitree_id.comment":"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043f\u0440\u043e\u0444\u0438\u043b\u044f {wikiTreeLink}. \u041f\u0440\u0438\u043c\u0435\u0440\u044b: {example1}, {example2}","select_wikitree_id.cancel":"\u041e\u0442\u043c\u0435\u043d\u0430","select_wikitree_id.load":"\u041e\u0442\u043a\u0440\u044b\u0442\u044c","gedcom.ADOP":"\u0423\u0441\u044b\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435","gedcom.BAPM":"\u041a\u0440\u0435\u0449\u0435\u043d\u0438\u0435","gedcom.BIRT":"\u0420\u043e\u0436\u0434\u0435\u043d\u0438\u0435","gedcom.BURI":"\u041f\u043e\u0445\u043e\u0440\u043e\u043d\u044b","gedcom.CENS":"\u041f\u0435\u0440\u0435\u043f\u0438\u0441\u044c \u043d\u0430\u0441\u0435\u043b\u0435\u043d\u0438\u044f","gedcom.CHR":"\u041a\u0440\u0435\u0449\u0435\u043d\u0438\u0435","gedcom.CREM":"\u041a\u0440\u0435\u043c\u0430\u0446\u0438\u044f","gedcom.DEAT":"\u0421\u043c\u0435\u0440\u0442\u044c","gedcom.DSCR":"\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435","gedcom.EDUC":"\u041e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435","gedcom.EMAIL":"E-mail","gedcom.EMIG":"\u042d\u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f","gedcom.EVEN":"\u0421\u043e\u0431\u044b\u0442\u0438\u0435","gedcom.FACT":"\u0424\u0430\u043a\u0442","gedcom.IMMI":"\u0418\u043c\u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f","gedcom.MILT":"\u0412\u043e\u0435\u043d\u043d\u0430\u044f \u0441\u043b\u0443\u0436\u0431\u0430","gedcom.NATU":"\u041d\u0430\u0442\u0443\u0440\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f","gedcom.OCCU":"\u0412\u0438\u0434 \u0434\u0435\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438","gedcom.RIN":"ID","gedcom.TITL":"\u0422\u0438\u0442\u0443\u043b","gedcom.WWW":"\u0412\u0435\u0431-\u0441\u0430\u0439\u0442 WWW","gedcom._UPD":"\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435","date.abt":"\u043e\u043a\u043e\u043b\u043e","date.cal":"\u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u043e","date.est":"\u043f\u0440\u0438\u0431\u043b\u0438\u0437\u0438\u0442\u0435\u043b\u044c\u043d\u043e","date.between":"\u043c\u0435\u0436\u0434\u0443 {from} \u0438 {to}","date.after":"\u043f\u043e\u0441\u043b\u0435 {from}","date.before":"\u0434\u043e {to}","error.error":"\u041e\u0448\u0438\u0431\u043a\u0430","error.failed_pdf":"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c PDF-\u0444\u0430\u0439\u043b. \u041f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u0441 \u043c\u0435\u043d\u044c\u0448\u0435\u0439 \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u043e\u0439 \u0438\u043b\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u0444\u0430\u0439\u043b SVG.","error.failed_png":"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0444\u0430\u0439\u043b PNG. \u041f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u0441 \u043c\u0435\u043d\u044c\u0448\u0435\u0439 \u0441\u0445\u0435\u043c\u043e\u0439 \u0438\u043b\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u0444\u0430\u0439\u043b SVG.","error.failed_to_load_file":"\u041e\u0448\u0438\u0431\u043a\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u0430","error.failed_wikitree_load_more":"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 WikiTree. {error}}","wikitree.private":"\u0427\u0430\u0441\u0442\u043d\u044b\u0439"}')},323:function(e,t){},383:function(e,t,n){},386:function(e,t,n){"use strict";n.r(t);var r=n(0),a=n(82),i=n(239),o=n(240),s=n(241),c=n(242),u=n(243),l=n(244),d=n(24),p=n(10),h=n.n(p),m=n(17),f=n(13),g=n(15),j=n(19),b=n(20),O=n(26),v=n(34);function w(e,t){window.gtag("event",e,t)}var k,_=n(156),x=n(67),y=n(133),D=n(92),E=n(245),T=Object(E.createMedia)({breakpoints:{small:320,large:768}}),I=T.createMediaStyle(),S=T.Media,R=T.MediaContextProvider,M=n(200),N=n(150),C=(n(93),n(132)),L=n(48),P=n(1);function z(e,t){var n=Object(N.a)("#svgContainer").node(),r=t.transform.k,a=Object(y.a)([0,(n.clientWidth-e[0]*r)/2]),i=Object(y.a)([0,(n.clientHeight-e[1]*r)/2]);Object(N.a)("#chartSvg").attr("width",e[0]*r).attr("height",e[1]*r).attr("transform","translate(".concat(a,", ").concat(i,")")),Object(N.a)("#chart").attr("transform","scale(".concat(r,")")),n.scrollLeft=-t.transform.x,n.scrollTop=-t.transform.y}function A(){var e=Object(N.a)("#svgContainer").node(),t=e.scrollLeft+e.clientWidth/2,n=e.scrollTop+e.clientHeight/2,r=Object(C.b)(e).k;Object(N.a)(e).call(Object(C.a)().translateTo,t/r,n/r)}function W(e){var t=new FileReader;return t.readAsDataURL(e),new Promise((function(e,n){t.onload=function(t){return e(t.target.result)}}))}function G(e){return F.apply(this,arguments)}function F(){return(F=Object(m.a)(h.a.mark((function e(t){var n,r,a,i;return h.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 r=e.sent,e.next=9,r.blob();case 9:return a=e.sent,e.next=12,W(a);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 U(e){return B.apply(this,arguments)}function B(){return(B=Object(m.a)(h.a.mark((function e(t){var n;return h.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(G));case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function V(e){var t=new Image;return t.src=URL.createObjectURL(e),new Promise((function(e,n){t.addEventListener("load",(function(){return e(t)}))}))}function H(e){var t=document.createElement("canvas");t.width=2*e.width,t.height=2*e.height;var n=t.getContext("2d"),r=n.fillStyle;return n.fillStyle="white",n.fillRect(0,0,t.width,t.height),n.fillStyle=r,n.drawImage(e,0,0,t.width,t.height),t}function K(e,t){return new Promise((function(n,r){e.toBlob((function(e){e?n(e):r()}),t)}))}!function(e){e[e.Hourglass=0]="Hourglass",e[e.Relatives=1]="Relatives",e[e.Fancy=2]="Fancy"}(k||(k={}));var q,Z=function(e){Object(j.a)(r,e);var t=Object(b.a)(r);function r(){var e;Object(f.a)(this,r);for(var n=arguments.length,a=new Array(n),i=0;i0&&void 0!==arguments[0]?arguments[0]:{initialRender:!1};if(t.initialRender||!this.animating){if(t.initialRender||!this.props.freezeAnimation){t.initialRender?(Object(N.a)("#chart").node().innerHTML="",this.chart=Object(L.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.props.intl.locale})):this.chart.setData(this.props.data);var n=this.chart.render({startIndi:this.props.selection.id,baseGeneration:this.props.selection.generation}),r=Object(N.a)("#chartSvg"),a=Object(N.a)("#svgContainer").node(),i=Object(C.b)(a).k,o=Object(D.a)([1,i,a.clientWidth/n.size[0],a.clientHeight/n.size[1]]),s=[Object(y.a)([.1,o]),2];this.zoomBehavior=Object(C.a)().scaleExtent(s).translateExtent([[0,0],n.size]).on("zoom",(function(e){return z(n.size,e)})),Object(N.a)(a).on("scroll",A).call(this.zoomBehavior);var c=function(e){return function(){var t=Object(x.a)(a.scrollTop,e);return function(e){a.scrollTop=t(e)}}},u=function(e){return function(){var t=Object(x.a)(a.scrollLeft,e);return function(e){a.scrollLeft=t(e)}}},l=a.clientWidth/2-n.origin[0]*i,d=a.clientHeight/2-n.origin[1]*i,p=Object(y.a)([0,(a.clientWidth-n.size[0]*i)/2]),h=Object(y.a)([0,(a.clientHeight-n.size[1]*i)/2]),m=r.transition().delay(200).duration(500),f=t.initialRender?r:m;f.attr("transform","translate(".concat(p,", ").concat(h,")")).attr("width",n.size[0]*i).attr("height",n.size[1]*i),t.initialRender?(a.scrollLeft=-l,a.scrollTop=-d):m.tween("scrollLeft",u(-l)).tween("scrollTop",c(-d)),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 Object(P.jsxs)("div",{id:"svgContainer",children:[Object(P.jsxs)(S,{at:"large",className:"zoom",children:[Object(P.jsx)("button",{className:"zoom-in",onClick:function(){return e.zoom(1.3)},children:"+"}),Object(P.jsx)("button",{className:"zoom-out",onClick:function(){return e.zoom(1/1.3)},children:"\u2212"})]}),Object(P.jsx)("svg",{id:"chartSvg",children:Object(P.jsx)("g",{id:"chart"})})]})}},{key:"getStrippedSvg",value:function(){var e=document.getElementById("chartSvg").cloneNode(!0);e.removeAttribute("transform");var t=Object(N.a)("#svgContainer").node(),n=Object(C.b)(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(m.a)(h.a.mark((function e(){var t;return h.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.getStrippedSvg(),e.next=3,U(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(m.a)(h.a.mark((function e(){var t,n;return h.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(M.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(m.a)(h.a.mark((function e(){var t,n;return h.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=H,e.next=7,V(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(m.a)(h.a.mark((function e(){var t,n;return h.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,K(t,"image/png");case 5:n=e.sent,Object(M.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(m.a)(h.a.mark((function e(){var t,r,a,i;return h.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,n.e(4).then(n.bind(null,794));case 2:return t=e.sent,r=t.default,e.next=6,this.drawOnCanvas();case 6:a=e.sent,(i=new r({orientation:a.width>a.height?"l":"p",unit:"pt",format:[a.width,a.height]})).addImage(a,"PNG",0,0,a.width,a.height,"NONE"),i.save("topola.pdf");case 10:case"end":return e.stop()}}),e,this)})));return function(){return e.apply(this,arguments)}}()}]),r}(r.PureComponent),J=Object(_.c)(Z,{forwardRef:!0});!function(e){e[e.UPLOADED=0]="UPLOADED",e[e.GEDCOM_URL=1]="GEDCOM_URL",e[e.WIKITREE=2]="WIKITREE",e[e.EMBEDDED=3]="EMBEDDED"}(q||(q={}));var Y=n(62),X=n(247),Q=n.n(X),$=n(248),ee=n.n($),te=n(417),ne=n(199),re=n(138),ae=function(e){Object(j.a)(n,e);var t=Object(b.a)(n);function n(e,r){var a,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return Object(f.a)(this,n),(a=t.call(this,r)).code=e,a.args=i,a}return n}(Object(re.a)(Error));function ie(e){return e.substring(1,e.length-1)}function oe(e){var t=new Map;return e.indis.forEach((function(e){t.set(e.id,e)})),t}function se(e){var t=new Map;return e.fams.forEach((function(e){t.set(e.id,e)})),t}function ce(e){var t=e.find((function(e){return"HEAD"===e.tag})),n={},r={},a={};return e.forEach((function(e){"INDI"===e.tag?n[ie(e.pointer)]=e:"FAM"===e.tag?r[ie(e.pointer)]=e:e.pointer&&(a[ie(e.pointer)]=e)})),{head:t,indis:n,fams:r,other:a}}function ue(e,t){return et?1:0}function le(e,t){var n=e&&(e.date||e.dateRange&&e.dateRange.from),r=t&&(t.date||t.dateRange&&t.dateRange.from);return n&&n.year&&r&&r.year?n.year!==r.year?n.year-r.year:n.month&&r.month&&(n.month!==r.month||n.day&&r.day&&n.day!==r.day)?n.month-r.month:0:0}function de(e){var t=function(e){var t=oe(e);return function(e,n){var r=t.get(e),a=t.get(n);return le(r&&r.birth,a&&a.birth)||ue(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 pe(e){var t=function(e){var t=se(e);return function(e,n){var r=t.get(e),a=t.get(n);return le(r&&r.marriage,a&&a.marriage)||ue(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 he(e){return pe(de(e))}var me=[".jpg",".png",".gif"];function fe(e,t){if(!e.images||0===e.images.length)return e;var n=[];return e.images.forEach((function(e){var r=e.url.match(/[^/\\]*$/)[0];t.has(r)?n.push({url:t.get(r),title:e.title}):e.url.startsWith("http")&&function(e){var t=e.toLowerCase();return me.some((function(e){return t.endsWith(e)}))}(e.url)&&n.push(e)})),Object.assign({},e,{images:n})}function ge(e,t){var n=e.indis.map((function(e){return fe(e,t)}));return Object.assign({},e,{indis:n})}function je(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 be=new Map([["abt","about"],["cal","calculated"],["est","estimated"]]);function Oe(e,t){var n=void 0!==e.day,r=void 0!==e.month,a=void 0!==e.year;if(!n&&!r&&!a)return e.text||"";var i=new Date(a?e.year:0,r?e.month-1:0,n?e.day:1),o=e.qualifier&&e.qualifier.toLowerCase(),s={day:n?"numeric":void 0,month:r?"long":void 0,year:a?"numeric":void 0};return[o&&t.formatMessage({id:"date.".concat(o),defaultMessage:be.get(o)||o}),new Intl.DateTimeFormat(t.locale,s).format(i)].join(" ")}function ve(e,t){return e?e.date?Oe(e.date,t):e.dateRange?function(e,t){var n=e.from,r=e.to,a=n&&Oe(n,t),i=r&&Oe(r,t);return a&&i?t.formatMessage({id:"date.between",defaultMessage:"between {from} and {to}"},{from:a,to:i}):a?t.formatMessage({id:"date.after",defaultMessage:"after {from}"},{from:a}):i?t.formatMessage({id:"date.before",defaultMessage:"before {to}"},{to:i}):""}(e.dateRange,t):"":""}var we=["BIRT","BAPM","CHR","EVEN","CENS","DEAT","BURI"],ke=["NAME","SEX","FAMC","FAMS","NOTE","SOUR"],_e=new Map([["ADOP","Adoption"],["BAPM","Baptism"],["BIRT","Birth"],["BURI","Burial"],["CENS","Census"],["CHR","Christening"],["CREM","Cremation"],["DEAT","Death"],["EDUC","Education"],["EMAIL","E-mail"],["EMIG","Emigration"],["EVEN","Event"],["FACT","Fact"],["IMMI","Immigration"],["MARR","Marriage"],["MILT","Military services"],["NATU","Naturalization"],["OCCU","Occupation"],["TITL","Title"],["WWW","WWW"]]);function xe(e){var t=e.replace(/_/g,"");return Object(P.jsx)(te.a,{id:"gedcom.".concat(t),defaultMessage:_e.get(t)||t})}function ye(e){return Object(P.jsx)(P.Fragment,{children:e.map((function(e,t){return Object(P.jsxs)("div",{children:[Object(P.jsx)(ee.a,{properties:{target:"_blank"},children:e}),Object(P.jsx)("br",{})]},t)}))})}function De(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 Ee(e,t){var n=[];e.data&&e.data.length>1&&n.push(Object(P.jsx)("i",{children:e.data}));var r=e.tree.find((function(e){return"DATE"===e.tag}));r&&r.data&&n.push(function(e,t){return ve(Object(L.getDate)(e),t)}(r.data,t));var a=e.tree.find((function(e){return"PLAC"===e.tag}));return a&&a.data&&n.push.apply(n,Object(Y.a)(De(a))),e.tree.filter((function(e){return"NOTE"===e.tag})).forEach((function(e){return De(e).forEach((function(e){return n.push(Object(P.jsx)("i",{children:e}))}))})),n.length?Object(P.jsxs)(P.Fragment,{children:[Object(P.jsx)("div",{className:"ui sub header",children:xe(e.tag)}),Object(P.jsx)("span",{children:ye(n)})]}):null}function Te(e){return ye(De(e).map((function(e,t){return Object(P.jsx)("i",{children:e},t)})))}function Ie(e){return Object(P.jsx)("h2",{className:"ui header",children:e.data.split("/").filter((function(e){return!!e})).map((function(e,t){return Object(P.jsxs)("div",{children:[e,Object(P.jsx)("br",{})]},t)}))})}function Se(e,t,n){return Q()(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 Object(P.jsx)("div",{className:"ui segment",children:e},t)}))}function Re(e){return e.tree.length>0||e.data&&!e.data.startsWith("@")}function Me(e){return e.filter((function(e){return!ke.includes(e.tag)&&!we.includes(e.tag)})).filter(Re).map((function(e){return function(e){var t=[];return e.data&&t.push.apply(t,Object(Y.a)(De(e))),e.tree.filter((function(e){return"NOTE"===e.tag})).forEach((function(e){return De(e).forEach((function(e){return t.push(Object(P.jsx)("i",{children:e}))}))})),t.length?Object(P.jsxs)(P.Fragment,{children:[Object(P.jsx)("div",{className:"ui sub header",children:xe(e.tag)}),Object(P.jsx)("span",{children:ye(t)})]}):null}(e)})).filter((function(e){return null!==e})).map((function(e,t){return Object(P.jsx)("div",{className:"ui segment",children:e},t)}))}var Ne=function(e){Object(j.a)(n,e);var t=Object(b.a)(n);function n(){return Object(f.a)(this,n),t.apply(this,arguments)}return Object(g.a)(n,[{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[ie(e.data)];if(n)return n}return e}(t,e.props.gedcom)})).filter(Re);return Object(P.jsxs)("div",{className:"ui segments",id:"details",children:[Se(t,["NAME"],Ie),Se(t,we,(function(t){return Ee(t,e.props.intl)})),Me(n),Se(n,["NOTE"],Te)]})}}]),n}(r.Component),Ce=Object(_.c)(Ne);function Le(e,t){return{id:t&&e.indis.some((function(e){return e.id===t.id}))?t.id:e.indis[0].id,generation:(null===t||void 0===t?void 0:t.generation)||0}}function Pe(e,t,n){var r=function(e,t){var n=Object(ne.parse)(e),r=Object(L.gedcomEntriesToJson)(n);if(!r||!r.indis||!r.indis.length||!r.fams||!r.fams.length)throw new ae("GEDCOM_READ_FAILED","Failed to read GEDCOM file");return{chartData:ge(he(r),t),gedcom:ce(n)}}(e,n||new Map),a=JSON.stringify(r);try{sessionStorage.setItem(t,a)}catch(i){console.warn("Failed to store data in session storage: "+i)}return r}function ze(e,t){return Ae.apply(this,arguments)}function Ae(){return(Ae=Object(m.a)(h.a.mark((function e(t,n){var r,a,i,o,s,c;return h.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:return(a=t.match(/https:\/\/drive\.google\.com\/file\/d\/(.*)\/.*/))&&(t="https://drive.google.com/uc?id=".concat(a[1],"&export=download")),(i=t.match(/https:\/\/drive\.google\.com\/open\?id=([^&]*)&?.*/))&&(t="https://drive.google.com/uc?id=".concat(i[1],"&export=download")),o=n?"https://topola-cors.herokuapp.com/"+t:t,e.next=16,window.fetch(o);case 16:if(200===(s=e.sent).status){e.next=19;break}throw new Error(s.statusText);case 19:return e.next=21,s.text();case 21:return c=e.sent,e.abrupt("return",Pe(c,t));case 23:case"end":return e.stop()}}),e,null,[[0,6]])})))).apply(this,arguments)}function We(e,t,n){return Ge.apply(this,arguments)}function Ge(){return(Ge=Object(m.a)(h.a.mark((function e(t,n,r){var a;return h.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:if(n){e.next=11;break}throw new ae("ERROR_LOADING_UPLOADED_FILE","Error loading data. Please upload your file again.");case 11:return e.abrupt("return",Pe(n,t,r));case 12:case"end":return e.stop()}}),e,null,[[0,6]])})))).apply(this,arguments)}var Fe,Ue=function(){function e(){Object(f.a)(this,e)}return Object(g.a)(e,[{key:"isNewData",value:function(e,t,n){return e.spec.hash!==t.spec.hash}},{key:"loadData",value:function(){var e=Object(m.a)(h.a.mark((function e(t){var n;return h.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,We(t.spec.hash,t.spec.gedcom,t.spec.images);case 3:return n=e.sent,w("upload_file_loaded",{event_label:je(n.gedcom.head),event_value:t.spec.images&&t.spec.images.size||0}),e.abrupt("return",n);case 9:throw e.prev=9,e.t0=e.catch(0),w("upload_file_error"),e.t0;case 13:case"end":return e.stop()}}),e,null,[[0,9]])})));return function(t){return e.apply(this,arguments)}}()}]),e}(),Be=function(){function e(){Object(f.a)(this,e)}return Object(g.a)(e,[{key:"isNewData",value:function(e,t,n){return e.spec.url!==t.spec.url}},{key:"loadData",value:function(){var e=Object(m.a)(h.a.mark((function e(t){var n;return h.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,ze(t.spec.url,t.spec.handleCors);case 3:return n=e.sent,w("upload_file_loaded",{event_label:je(n.gedcom.head)}),e.abrupt("return",n);case 9:throw e.prev=9,e.t0=e.catch(0),w("url_file_error"),e.t0;case 13:case"end":return e.stop()}}),e,null,[[0,9]])})));return function(t){return e.apply(this,arguments)}}()}]),e}();!function(e){e.GEDCOM="gedcom",e.READY="ready",e.PARENT_READY="parent_ready"}(Fe||(Fe={}));var Ve=function(){function e(){Object(f.a)(this,e)}return Object(g.a)(e,[{key:"isNewData",value:function(e,t,n){return!1}},{key:"onMessage",value:function(){var e=Object(m.a)(h.a.mark((function e(t,n,r){var a,i;return h.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t.message!==Fe.PARENT_READY){e.next=4;break}window.parent.postMessage({message:Fe.READY},"*"),e.next=21;break;case 4:if(t.message!==Fe.GEDCOM){e.next=21;break}if(a=t.gedcom){e.next=8;break}return e.abrupt("return");case 8:return e.prev=8,e.next=11,We("",a);case 11:i=e.sent,w("embedded_file_loaded",{event_label:je(i.gedcom.head)}),n(i),e.next=21;break;case 17:e.prev=17,e.t0=e.catch(8),w("embedded_file_error"),r(e.t0);case 21:case"end":return e.stop()}}),e,null,[[8,17]])})));return function(t,n,r){return e.apply(this,arguments)}}()},{key:"loadData",value:function(){var e=Object(m.a)(h.a.mark((function e(t){var n=this;return h.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",new Promise((function(e,t){window.parent.postMessage({message:Fe.READY},"*"),window.addEventListener("message",(function(r){return n.onMessage(r.data,e,t)}))})));case 1:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}()}]),e}();var He=n.p+"static/media/topola.a3ffa9a5.jpg",Ke=n(412),qe=n(415),Ze=n(253),Je=n(73);function Ye(e){return Object(P.jsx)(Je.b,{to:{pathname:"/view",search:v.stringify({url:e.url})},children:e.text})}function Xe(){var e,t=Object(P.jsxs)(P.Fragment,{children:[Object(P.jsx)("p",{children:Object(P.jsx)(te.a,{id:"intro.description",defaultMessage:"Topola Genealogy is a genealogy tree viewer that lets you browse the structure of the family."})}),Object(P.jsx)("p",{children:Object(P.jsx)(te.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."})}),Object(P.jsx)("p",{children:Object(P.jsx)(te.a,{id:"intro.examples",defaultMessage:"Here are some examples from the web that you can view:"})}),Object(P.jsxs)("ul",{children:[Object(P.jsxs)("li",{children:[Object(P.jsx)(Ye,{url:"http://genpol.com/module-Downloads-prep_hand_out-lid-32.html",text:"Karol Wojty\u0142a"})," ","(",Object(P.jsx)(te.a,{id:"intro.from",defaultMessage:"from"})," ",Object(P.jsx)("a",{href:"http://genpol.com/module-Downloads-display-lid-32.html",children:"GENPOL"}),")"]}),Object(P.jsxs)("li",{children:[Object(P.jsx)(Ye,{url:"https://webtreeprint.com/tp_downloader.php?path=famous_gedcoms/shakespeare.ged",text:"Shakespeare"})," ","(",Object(P.jsx)(te.a,{id:"intro.from",defaultMessage:"from"})," ",Object(P.jsx)("a",{href:"https://webtreeprint.com/tp_famous_gedcoms.php",children:"webtreeprint.com"}),")"]})]}),Object(P.jsxs)("p",{children:[Object(P.jsx)("b",{children:Object(P.jsx)(te.a,{id:"intro.privacy",defaultMessage:"Privacy"})}),": ",Object(P.jsx)(te.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:Object(P.jsx)("a",{href:"https://topola-cors.herokuapp.com/",children:"cors-anywhere"})}})]}),Object(P.jsxs)("p",{className:"ui right aligned version",children:["version: ",(e="2021-04-15 00:25:15 +0200",e.slice(0,16))," (",Object(P.jsx)("a",{href:"https://github.com/PeWu/topola-viewer/commit/".concat("e615308"),children:"e615308"}),")"]})]});return Object(P.jsxs)("div",{id:"content",children:[Object(P.jsx)("div",{className:"backgroundImage"}),Object(P.jsxs)(Ke.a,{className:"intro",children:[Object(P.jsx)(Ke.a.Content,{as:S,at:"large",children:Object(P.jsx)(Ke.a.Header,{children:Object(P.jsx)(te.a,{id:"intro.title",defaultMessage:"Topola Genealogy Viewer"})})}),Object(P.jsxs)(Ke.a.Content,{children:[Object(P.jsx)(qe.a,{as:S,at:"large",children:Object(P.jsxs)(qe.a.Row,{children:[Object(P.jsx)(qe.a.Column,{width:5,children:Object(P.jsx)(Ze.a,{src:He,alt:"Topola logo"})}),Object(P.jsx)(qe.a.Column,{width:11,children:t})]})}),Object(P.jsxs)(S,{at:"small",children:[Object(P.jsx)(Ze.a,{src:He,alt:"Topola logo",centered:!0,size:"tiny",className:"blockImage"}),t]})]})]})]})}var Qe,$e=n(413),et=n(418),tt=n(405),nt=n(25),rt=n(407),at=n(99),it=n(408);!function(e){e[e.Menu=0]="Menu",e[e.Dropdown=1]="Dropdown"}(Qe||(Qe={}));var ot=function(e){Object(j.a)(n,e);var t=Object(b.a)(n);function n(){return Object(f.a)(this,n),t.apply(this,arguments)}return Object(g.a)(n,[{key:"render",value:function(){var e=Object(d.a)({},this.props);return delete e.menuType,Object(P.jsx)(P.Fragment,{children:this.props.menuType===Qe.Menu?Object(P.jsx)(it.a.Item,Object(d.a)(Object(d.a)({},e),{},{children:this.props.children})):Object(P.jsx)(rt.a.Item,Object(d.a)(Object(d.a)({},e),{},{children:this.props.children}))})}}]),n}(r.Component),st=n(250),ct=n.n(st),ut=n(69),lt=n.n(ut),dt=n(251),pt=n.n(dt);n(375)(lt.a),n(376)(lt.a),n(377)(lt.a),n(378)(lt.a),n(379)(lt.a),n(380)(lt.a);function ht(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:pt()(e.ref,t.ref)}var ft=function(){function e(t){Object(f.a)(this,e),this.index=void 0,this.indiMap=void 0,this.famMap=void 0,this.indiMap=oe(t),this.famMap=se(t)}return Object(g.a)(e,[{key:"initialize",value:function(){var e=this;this.index=lt()((function(){var t=this;this.use(lt.a.multiLanguage("de","en","fr","it","ru")),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 r=[n.firstName,n.lastName].join(" "),a=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:r,normalizedName:ht(r),spouseLastName:a,normalizedSpouseLastName:ht(a)})}))}))}},{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}();var gt=n(406);function jt(e){var t=[e.indi.firstName,e.indi.lastName].join(" ").trim();return e.id.length>8?t:Object(P.jsxs)(P.Fragment,{children:[t," ",Object(P.jsxs)("i",{children:["(",e.id,")"]})]})}var bt=function(e){Object(j.a)(n,e);var t=Object(b.a)(n);function n(){var e;Object(f.a)(this,n);for(var r=arguments.length,a=new Array(r),i=0;i0&&v<=j)){e.next=42;break}return e.next=34,Ut(b,a);case 34:w=e.sent,o.push.apply(o,Object(Y.a)(w)),k=w.flatMap((function(e){return Object.values(e.Spouses)})),o.push.apply(o,Object(Y.a)(k)),b=w.flatMap((function(e){return Object.values(e.Children).map((function(e){return e.Name}))})),v++,e.next=31;break;case 42:return _=new Map,x=new Map,y=new Map,D=new Map,o.forEach((function(e){if(D.set(e.Id,e.Name),e.Mother||e.Father){var t=Jt(e.Mother,e.Father);en(_,e.Mother).add(t),en(_,e.Father).add(t),en(x,t).add(e.Id),y.set(t,{wife:e.Mother||void 0,husband:e.Father||void 0})}})),E=[],T=new Set,o.forEach((function(e){if(!T.has(e.Id)){T.add(e.Id);var t=Yt(e,n);e.Spouses&&Object.values(e.Spouses).forEach((function(t){var n=Jt(e.Id,t.Id);en(_,e.Id).add(n),en(_,t.Id).add(n);var r="Male"===e.Gender?{wife:t.Id,husband:e.Id,spouse:t}:{wife:e.Id,husband:t.Id,spouse:t};y.set(n,r)})),t.fams=Array.from(en(_,e.Id)),E.push(t)}})),I=Array.from(y.entries()).map((function(e){var t=Object(O.a)(e,2),n=t[0],r=t[1],a={id:n},i=r.wife&&D.get(r.wife);i&&(a.wife=i);var o=r.husband&&D.get(r.husband);if(o&&(a.husb=o),a.children=Array.from(en(x,n)).map((function(e){return D.get(e)})),r.spouse&&(r.spouse.marriage_date&&"0000-00-00"!==r.spouse.marriage_date||r.spouse.marriage_location)){var s=Xt(r.spouse.marriage_date);a.marriage=Object.assign({},s,{place:r.spouse.marriage_location})}return a})),S=he({indis:E,fams:I}),R=$t(E),e.abrupt("return",{chartData:S,gedcom:R});case 54:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Jt(e,t){return t>e?"".concat(e,"_").concat(t):"".concat(t,"_").concat(e)}function Yt(e,t){var n={id:e.Name};if(e.Name.startsWith(Ct)&&(n.hideId=!0,n.firstName=t.formatMessage({id:"wikitree.private",defaultMessage:"Private"})),e.FirstName&&"Unknown"!==e.FirstName?n.firstName=e.FirstName:e.RealName&&"Unknown"!==e.RealName&&(n.firstName=e.RealName),"Unknown"!==e.LastNameAtBirth&&(n.lastName=e.LastNameAtBirth),(e.Mother||e.Father)&&(n.famc=Jt(e.Mother,e.Father)),"Male"===e.Gender?n.sex="M":"Female"===e.Gender&&(n.sex="F"),e.BirthDate&&"0000-00-00"!==e.BirthDate||e.BirthLocation||"unknown"!==e.BirthDateDecade){var r=Xt(e.BirthDate,e.DataStatus&&e.DataStatus.BirthDate)||Qt(e.BirthDateDecade);n.birth=Object.assign({},r,{place:e.BirthLocation})}if(e.DeathDate&&"0000-00-00"!==e.DeathDate||e.DeathLocation||"unknown"!==e.DeathDateDecade){var a=Xt(e.DeathDate,e.DataStatus&&e.DataStatus.DeathDate)||Qt(e.DeathDateDecade);n.death=Object.assign({},a,{place:e.DeathLocation})}return e.PhotoData&&(n.images=[{url:"https://www.wikitree.com".concat(e.PhotoData.url)}]),n}function Xt(e,t){if(e){var n=e.match(/(\d\d\d\d)-(\d\d)-(\d\d)/);if(!n)return{date:{text:e}};var r={};return"0000"!==n[1]&&(r.year=~~n[1]),"00"!==n[2]&&(r.month=~~n[2]),"00"!==n[3]&&(r.day=~~n[3]),"after"===t?{dateRange:{from:r}}:"before"===t?{dateRange:{to:r}}:("guess"===t&&(r.qualifier="abt"),{date:r})}}function Qt(e){return"unknown"!==e?{date:{text:e}}:void 0}function $t(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:[]}]},e.id.startsWith("~")||t[e.id].tree.push({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 en(e,t){var n=e.get(t);if(n)return n;var r=new Set;return e.set(t,r),r}var tn,nn=function(){function e(t){Object(f.a)(this,e),this.intl=t}return Object(g.a)(e,[{key:"isNewData",value:function(e,t,n){var r;return!!e.selection&&((null===(r=t.selection)||void 0===r?void 0:r.id)!==e.selection.id&&(!n||!n.chartData.indis.some((function(t){var n;return t.id===(null===(n=e.selection)||void 0===n?void 0:n.id)}))))}},{key:"loadData",value:function(){var e=Object(m.a)(h.a.mark((function e(t){var n;return h.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t.selection){e.next=2;break}throw new ae("WIKITREE_ID_NOT_PROVIDED","WikiTree id needs to be provided");case 2:return e.prev=2,e.next=5,qt(t.selection.id,this.intl,t.spec.authcode);case 5:return n=e.sent,w("wikitree_loaded"),e.abrupt("return",n);case 10:throw e.prev=10,e.t0=e.catch(2),w("wikitree_error"),e.t0;case 14:case"end":return e.stop()}}),e,this,[[2,10]])})));return function(t){return e.apply(this,arguments)}}()}]),e}();!function(e){e[e.UNKNOWN=0]="UNKNOWN",e[e.NOT_LOGGED_IN=1]="NOT_LOGGED_IN",e[e.LOGGED_IN=2]="LOGGED_IN"}(tn||(tn={}));var rn,an=function(e){Object(j.a)(n,e);var t=Object(b.a)(n);function n(){var e;Object(f.a)(this,n);for(var a=arguments.length,i=new Array(a),o=0;o0&&void 0!==arguments[0]?arguments[0]:{initialRender:!1};if(t.initialRender||!this.animating){if(t.initialRender||!this.props.freezeAnimation){t.initialRender?(Object(N.a)("#chart").node().innerHTML="",this.chart=Object(L.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.props.intl.locale})):this.chart.setData(this.props.data);var n=this.chart.render({startIndi:this.props.selection.id,baseGeneration:this.props.selection.generation}),r=Object(N.a)("#chartSvg"),a=Object(N.a)("#svgContainer").node(),i=Object(C.b)(a).k,o=Object(D.a)([1,i,a.clientWidth/n.size[0],a.clientHeight/n.size[1]]),s=[Object(y.a)([.1,o]),2];this.zoomBehavior=Object(C.a)().scaleExtent(s).translateExtent([[0,0],n.size]).on("zoom",(function(e){return z(n.size,e)})),Object(N.a)(a).on("scroll",A).call(this.zoomBehavior);var c=function(e){return function(){var t=Object(x.a)(a.scrollTop,e);return function(e){a.scrollTop=t(e)}}},u=function(e){return function(){var t=Object(x.a)(a.scrollLeft,e);return function(e){a.scrollLeft=t(e)}}},l=a.clientWidth/2-n.origin[0]*i,d=a.clientHeight/2-n.origin[1]*i,p=Object(y.a)([0,(a.clientWidth-n.size[0]*i)/2]),h=Object(y.a)([0,(a.clientHeight-n.size[1]*i)/2]),m=r.transition().delay(200).duration(500),f=t.initialRender?r:m;f.attr("transform","translate(".concat(p,", ").concat(h,")")).attr("width",n.size[0]*i).attr("height",n.size[1]*i),t.initialRender?(a.scrollLeft=-l,a.scrollTop=-d):m.tween("scrollLeft",u(-l)).tween("scrollTop",c(-d)),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 Object(P.jsxs)("div",{id:"svgContainer",children:[Object(P.jsxs)(S,{at:"large",className:"zoom",children:[Object(P.jsx)("button",{className:"zoom-in",onClick:function(){return e.zoom(1.3)},children:"+"}),Object(P.jsx)("button",{className:"zoom-out",onClick:function(){return e.zoom(1/1.3)},children:"\u2212"})]}),Object(P.jsx)("svg",{id:"chartSvg",children:Object(P.jsx)("g",{id:"chart"})})]})}},{key:"getStrippedSvg",value:function(){var e=document.getElementById("chartSvg").cloneNode(!0);e.removeAttribute("transform");var t=Object(N.a)("#svgContainer").node(),n=Object(C.b)(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(m.a)(h.a.mark((function e(){var t;return h.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.getStrippedSvg(),e.next=3,U(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(m.a)(h.a.mark((function e(){var t,n;return h.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(M.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(m.a)(h.a.mark((function e(){var t,n;return h.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=H,e.next=7,V(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(m.a)(h.a.mark((function e(){var t,n;return h.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,K(t,"image/png");case 5:n=e.sent,Object(M.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(m.a)(h.a.mark((function e(){var t,r,a,i;return h.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,n.e(4).then(n.bind(null,794));case 2:return t=e.sent,r=t.default,e.next=6,this.drawOnCanvas();case 6:a=e.sent,(i=new r({orientation:a.width>a.height?"l":"p",unit:"pt",format:[a.width,a.height]})).addImage(a,"PNG",0,0,a.width,a.height,"NONE"),i.save("topola.pdf");case 10:case"end":return e.stop()}}),e,this)})));return function(){return e.apply(this,arguments)}}()}]),r}(r.PureComponent),J=Object(_.c)(Z,{forwardRef:!0});!function(e){e[e.UPLOADED=0]="UPLOADED",e[e.GEDCOM_URL=1]="GEDCOM_URL",e[e.WIKITREE=2]="WIKITREE",e[e.EMBEDDED=3]="EMBEDDED"}(q||(q={}));var Y=n(62),X=n(247),Q=n.n(X),$=n(248),ee=n.n($),te=n(417),ne=n(199),re=n(138),ae=function(e){Object(j.a)(n,e);var t=Object(b.a)(n);function n(e,r){var a,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return Object(f.a)(this,n),(a=t.call(this,r)).code=e,a.args=i,a}return n}(Object(re.a)(Error));function ie(e){return e.substring(1,e.length-1)}function oe(e){var t=new Map;return e.indis.forEach((function(e){t.set(e.id,e)})),t}function se(e){var t=new Map;return e.fams.forEach((function(e){t.set(e.id,e)})),t}function ce(e){var t=e.find((function(e){return"HEAD"===e.tag})),n={},r={},a={};return e.forEach((function(e){"INDI"===e.tag?n[ie(e.pointer)]=e:"FAM"===e.tag?r[ie(e.pointer)]=e:e.pointer&&(a[ie(e.pointer)]=e)})),{head:t,indis:n,fams:r,other:a}}function ue(e,t){return et?1:0}function le(e,t){var n=e&&(e.date||e.dateRange&&e.dateRange.from),r=t&&(t.date||t.dateRange&&t.dateRange.from);return n&&n.year&&r&&r.year?n.year!==r.year?n.year-r.year:n.month&&r.month&&(n.month!==r.month||n.day&&r.day&&n.day!==r.day)?n.month-r.month:0:0}function de(e){var t=function(e){var t=oe(e);return function(e,n){var r=t.get(e),a=t.get(n);return le(r&&r.birth,a&&a.birth)||ue(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 pe(e){var t=function(e){var t=se(e);return function(e,n){var r=t.get(e),a=t.get(n);return le(r&&r.marriage,a&&a.marriage)||ue(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 he(e){return pe(de(e))}var me=[".jpg",".png",".gif"];function fe(e,t){if(!e.images||0===e.images.length)return e;var n=[];return e.images.forEach((function(e){var r=e.url.match(/[^/\\]*$/)[0];t.has(r)?n.push({url:t.get(r),title:e.title}):e.url.startsWith("http")&&function(e){var t=e.toLowerCase();return me.some((function(e){return t.endsWith(e)}))}(e.url)&&n.push(e)})),Object.assign({},e,{images:n})}function ge(e,t){var n=e.indis.map((function(e){return fe(e,t)}));return Object.assign({},e,{indis:n})}function je(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 be=new Map([["abt","about"],["cal","calculated"],["est","estimated"]]);function Oe(e,t){var n=void 0!==e.day,r=void 0!==e.month,a=void 0!==e.year;if(!n&&!r&&!a)return e.text||"";var i=new Date(a?e.year:0,r?e.month-1:0,n?e.day:1),o=e.qualifier&&e.qualifier.toLowerCase(),s={day:n?"numeric":void 0,month:r?"long":void 0,year:a?"numeric":void 0};return[o&&t.formatMessage({id:"date.".concat(o),defaultMessage:be.get(o)||o}),new Intl.DateTimeFormat(t.locale,s).format(i)].join(" ")}function ve(e,t){return e?e.date?Oe(e.date,t):e.dateRange?function(e,t){var n=e.from,r=e.to,a=n&&Oe(n,t),i=r&&Oe(r,t);return a&&i?t.formatMessage({id:"date.between",defaultMessage:"between {from} and {to}"},{from:a,to:i}):a?t.formatMessage({id:"date.after",defaultMessage:"after {from}"},{from:a}):i?t.formatMessage({id:"date.before",defaultMessage:"before {to}"},{to:i}):""}(e.dateRange,t):"":""}var we=["BIRT","BAPM","CHR","EVEN","CENS","DEAT","BURI"],ke=["NAME","SEX","FAMC","FAMS","NOTE","SOUR"],_e=new Map([["ADOP","Adoption"],["BAPM","Baptism"],["BIRT","Birth"],["BURI","Burial"],["CENS","Census"],["CHR","Christening"],["CREM","Cremation"],["DEAT","Death"],["EDUC","Education"],["EMAIL","E-mail"],["EMIG","Emigration"],["EVEN","Event"],["FACT","Fact"],["IMMI","Immigration"],["MARR","Marriage"],["MILT","Military services"],["NATU","Naturalization"],["OCCU","Occupation"],["TITL","Title"],["WWW","WWW"]]);function xe(e){var t=e.replace(/_/g,"");return Object(P.jsx)(te.a,{id:"gedcom.".concat(t),defaultMessage:_e.get(t)||t})}function ye(e){return Object(P.jsx)(P.Fragment,{children:e.map((function(e,t){return Object(P.jsxs)("div",{children:[Object(P.jsx)(ee.a,{properties:{target:"_blank"},children:e}),Object(P.jsx)("br",{})]},t)}))})}function De(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 Ee(e,t){var n=[];e.data&&e.data.length>1&&n.push(Object(P.jsx)("i",{children:e.data}));var r=e.tree.find((function(e){return"DATE"===e.tag}));r&&r.data&&n.push(function(e,t){return ve(Object(L.getDate)(e),t)}(r.data,t));var a=e.tree.find((function(e){return"PLAC"===e.tag}));return a&&a.data&&n.push.apply(n,Object(Y.a)(De(a))),e.tree.filter((function(e){return"NOTE"===e.tag})).forEach((function(e){return De(e).forEach((function(e){return n.push(Object(P.jsx)("i",{children:e}))}))})),n.length?Object(P.jsxs)(P.Fragment,{children:[Object(P.jsx)("div",{className:"ui sub header",children:xe(e.tag)}),Object(P.jsx)("span",{children:ye(n)})]}):null}function Te(e){return ye(De(e).map((function(e,t){return Object(P.jsx)("i",{children:e},t)})))}function Ie(e){return Object(P.jsx)("h2",{className:"ui header",children:e.data.split("/").filter((function(e){return!!e})).map((function(e,t){return Object(P.jsxs)("div",{children:[e,Object(P.jsx)("br",{})]},t)}))})}function Se(e,t,n){return Q()(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 Object(P.jsx)("div",{className:"ui segment",children:e},t)}))}function Re(e){return e.tree.length>0||e.data&&!e.data.startsWith("@")}function Me(e){return e.filter((function(e){return!ke.includes(e.tag)&&!we.includes(e.tag)})).filter(Re).map((function(e){return function(e){var t=[];return e.data&&t.push.apply(t,Object(Y.a)(De(e))),e.tree.filter((function(e){return"NOTE"===e.tag})).forEach((function(e){return De(e).forEach((function(e){return t.push(Object(P.jsx)("i",{children:e}))}))})),t.length?Object(P.jsxs)(P.Fragment,{children:[Object(P.jsx)("div",{className:"ui sub header",children:xe(e.tag)}),Object(P.jsx)("span",{children:ye(t)})]}):null}(e)})).filter((function(e){return null!==e})).map((function(e,t){return Object(P.jsx)("div",{className:"ui segment",children:e},t)}))}var Ne=function(e){Object(j.a)(n,e);var t=Object(b.a)(n);function n(){return Object(f.a)(this,n),t.apply(this,arguments)}return Object(g.a)(n,[{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[ie(e.data)];if(n)return n}return e}(t,e.props.gedcom)})).filter(Re);return Object(P.jsxs)("div",{className:"ui segments",id:"details",children:[Se(t,["NAME"],Ie),Se(t,we,(function(t){return Ee(t,e.props.intl)})),Me(n),Se(n,["NOTE"],Te)]})}}]),n}(r.Component),Ce=Object(_.c)(Ne);function Le(e,t){return{id:t&&e.indis.some((function(e){return e.id===t.id}))?t.id:e.indis[0].id,generation:(null===t||void 0===t?void 0:t.generation)||0}}function Pe(e,t,n){var r=function(e,t){var n=Object(ne.parse)(e),r=Object(L.gedcomEntriesToJson)(n);if(!r||!r.indis||!r.indis.length||!r.fams||!r.fams.length)throw new ae("GEDCOM_READ_FAILED","Failed to read GEDCOM file");return{chartData:ge(he(r),t),gedcom:ce(n)}}(e,n||new Map),a=JSON.stringify(r);try{sessionStorage.setItem(t,a)}catch(i){console.warn("Failed to store data in session storage: "+i)}return r}function ze(e,t){return Ae.apply(this,arguments)}function Ae(){return(Ae=Object(m.a)(h.a.mark((function e(t,n){var r,a,i,o,s,c;return h.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:return(a=t.match(/https:\/\/drive\.google\.com\/file\/d\/(.*)\/.*/))&&(t="https://drive.google.com/uc?id=".concat(a[1],"&export=download")),(i=t.match(/https:\/\/drive\.google\.com\/open\?id=([^&]*)&?.*/))&&(t="https://drive.google.com/uc?id=".concat(i[1],"&export=download")),o=n?"https://topola-cors.herokuapp.com/"+t:t,e.next=16,window.fetch(o);case 16:if(200===(s=e.sent).status){e.next=19;break}throw new Error(s.statusText);case 19:return e.next=21,s.text();case 21:return c=e.sent,e.abrupt("return",Pe(c,t));case 23:case"end":return e.stop()}}),e,null,[[0,6]])})))).apply(this,arguments)}function We(e,t,n){return Ge.apply(this,arguments)}function Ge(){return(Ge=Object(m.a)(h.a.mark((function e(t,n,r){var a;return h.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:if(n){e.next=11;break}throw new ae("ERROR_LOADING_UPLOADED_FILE","Error loading data. Please upload your file again.");case 11:return e.abrupt("return",Pe(n,t,r));case 12:case"end":return e.stop()}}),e,null,[[0,6]])})))).apply(this,arguments)}var Fe,Ue=function(){function e(){Object(f.a)(this,e)}return Object(g.a)(e,[{key:"isNewData",value:function(e,t,n){return e.spec.hash!==t.spec.hash}},{key:"loadData",value:function(){var e=Object(m.a)(h.a.mark((function e(t){var n;return h.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,We(t.spec.hash,t.spec.gedcom,t.spec.images);case 3:return n=e.sent,w("upload_file_loaded",{event_label:je(n.gedcom.head),event_value:t.spec.images&&t.spec.images.size||0}),e.abrupt("return",n);case 9:throw e.prev=9,e.t0=e.catch(0),w("upload_file_error"),e.t0;case 13:case"end":return e.stop()}}),e,null,[[0,9]])})));return function(t){return e.apply(this,arguments)}}()}]),e}(),Be=function(){function e(){Object(f.a)(this,e)}return Object(g.a)(e,[{key:"isNewData",value:function(e,t,n){return e.spec.url!==t.spec.url}},{key:"loadData",value:function(){var e=Object(m.a)(h.a.mark((function e(t){var n;return h.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,ze(t.spec.url,t.spec.handleCors);case 3:return n=e.sent,w("upload_file_loaded",{event_label:je(n.gedcom.head)}),e.abrupt("return",n);case 9:throw e.prev=9,e.t0=e.catch(0),w("url_file_error"),e.t0;case 13:case"end":return e.stop()}}),e,null,[[0,9]])})));return function(t){return e.apply(this,arguments)}}()}]),e}();!function(e){e.GEDCOM="gedcom",e.READY="ready",e.PARENT_READY="parent_ready"}(Fe||(Fe={}));var Ve=function(){function e(){Object(f.a)(this,e)}return Object(g.a)(e,[{key:"isNewData",value:function(e,t,n){return!1}},{key:"onMessage",value:function(){var e=Object(m.a)(h.a.mark((function e(t,n,r){var a,i;return h.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t.message!==Fe.PARENT_READY){e.next=4;break}window.parent.postMessage({message:Fe.READY},"*"),e.next=21;break;case 4:if(t.message!==Fe.GEDCOM){e.next=21;break}if(a=t.gedcom){e.next=8;break}return e.abrupt("return");case 8:return e.prev=8,e.next=11,We("",a);case 11:i=e.sent,w("embedded_file_loaded",{event_label:je(i.gedcom.head)}),n(i),e.next=21;break;case 17:e.prev=17,e.t0=e.catch(8),w("embedded_file_error"),r(e.t0);case 21:case"end":return e.stop()}}),e,null,[[8,17]])})));return function(t,n,r){return e.apply(this,arguments)}}()},{key:"loadData",value:function(){var e=Object(m.a)(h.a.mark((function e(t){var n=this;return h.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",new Promise((function(e,t){window.parent.postMessage({message:Fe.READY},"*"),window.addEventListener("message",(function(r){return n.onMessage(r.data,e,t)}))})));case 1:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}()}]),e}();var He=n.p+"static/media/topola.a3ffa9a5.jpg",Ke=n(412),qe=n(415),Ze=n(253),Je=n(73);function Ye(e){return Object(P.jsx)(Je.b,{to:{pathname:"/view",search:v.stringify({url:e.url})},children:e.text})}function Xe(){var e,t=Object(P.jsxs)(P.Fragment,{children:[Object(P.jsx)("p",{children:Object(P.jsx)(te.a,{id:"intro.description",defaultMessage:"Topola Genealogy is a genealogy tree viewer that lets you browse the structure of the family."})}),Object(P.jsx)("p",{children:Object(P.jsx)(te.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."})}),Object(P.jsx)("p",{children:Object(P.jsx)(te.a,{id:"intro.examples",defaultMessage:"Here are some examples from the web that you can view:"})}),Object(P.jsxs)("ul",{children:[Object(P.jsxs)("li",{children:[Object(P.jsx)(Ye,{url:"http://genpol.com/module-Downloads-prep_hand_out-lid-32.html",text:"Karol Wojty\u0142a"})," ","(",Object(P.jsx)(te.a,{id:"intro.from",defaultMessage:"from"})," ",Object(P.jsx)("a",{href:"http://genpol.com/module-Downloads-display-lid-32.html",children:"GENPOL"}),")"]}),Object(P.jsxs)("li",{children:[Object(P.jsx)(Ye,{url:"https://webtreeprint.com/tp_downloader.php?path=famous_gedcoms/shakespeare.ged",text:"Shakespeare"})," ","(",Object(P.jsx)(te.a,{id:"intro.from",defaultMessage:"from"})," ",Object(P.jsx)("a",{href:"https://webtreeprint.com/tp_famous_gedcoms.php",children:"webtreeprint.com"}),")"]})]}),Object(P.jsxs)("p",{children:[Object(P.jsx)("b",{children:Object(P.jsx)(te.a,{id:"intro.privacy",defaultMessage:"Privacy"})}),": ",Object(P.jsx)(te.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:Object(P.jsx)("a",{href:"https://topola-cors.herokuapp.com/",children:"cors-anywhere"})}})]}),Object(P.jsxs)("p",{className:"ui right aligned version",children:["version: ",(e="2021-05-01 21:11:34 +0200",e.slice(0,16))," (",Object(P.jsx)("a",{href:"https://github.com/PeWu/topola-viewer/commit/".concat("2a60896"),children:"2a60896"}),")"]})]});return Object(P.jsxs)("div",{id:"content",children:[Object(P.jsx)("div",{className:"backgroundImage"}),Object(P.jsxs)(Ke.a,{className:"intro",children:[Object(P.jsx)(Ke.a.Content,{as:S,at:"large",children:Object(P.jsx)(Ke.a.Header,{children:Object(P.jsx)(te.a,{id:"intro.title",defaultMessage:"Topola Genealogy Viewer"})})}),Object(P.jsxs)(Ke.a.Content,{children:[Object(P.jsx)(qe.a,{as:S,at:"large",children:Object(P.jsxs)(qe.a.Row,{children:[Object(P.jsx)(qe.a.Column,{width:5,children:Object(P.jsx)(Ze.a,{src:He,alt:"Topola logo"})}),Object(P.jsx)(qe.a.Column,{width:11,children:t})]})}),Object(P.jsxs)(S,{at:"small",children:[Object(P.jsx)(Ze.a,{src:He,alt:"Topola logo",centered:!0,size:"tiny",className:"blockImage"}),t]})]})]})]})}var Qe,$e=n(413),et=n(418),tt=n(405),nt=n(25),rt=n(407),at=n(99),it=n(408);!function(e){e[e.Menu=0]="Menu",e[e.Dropdown=1]="Dropdown"}(Qe||(Qe={}));var ot=function(e){Object(j.a)(n,e);var t=Object(b.a)(n);function n(){return Object(f.a)(this,n),t.apply(this,arguments)}return Object(g.a)(n,[{key:"render",value:function(){var e=Object(d.a)({},this.props);return delete e.menuType,Object(P.jsx)(P.Fragment,{children:this.props.menuType===Qe.Menu?Object(P.jsx)(it.a.Item,Object(d.a)(Object(d.a)({},e),{},{children:this.props.children})):Object(P.jsx)(rt.a.Item,Object(d.a)(Object(d.a)({},e),{},{children:this.props.children}))})}}]),n}(r.Component),st=n(250),ct=n.n(st),ut=n(69),lt=n.n(ut),dt=n(251),pt=n.n(dt);n(375)(lt.a),n(376)(lt.a),n(377)(lt.a),n(378)(lt.a),n(379)(lt.a),n(380)(lt.a);function ht(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:pt()(e.ref,t.ref)}var ft=function(){function e(t){Object(f.a)(this,e),this.index=void 0,this.indiMap=void 0,this.famMap=void 0,this.indiMap=oe(t),this.famMap=se(t)}return Object(g.a)(e,[{key:"initialize",value:function(){var e=this;this.index=lt()((function(){var t=this;this.use(lt.a.multiLanguage("de","en","fr","it","ru")),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 r=[n.firstName,n.lastName].join(" "),a=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:r,normalizedName:ht(r),spouseLastName:a,normalizedSpouseLastName:ht(a)})}))}))}},{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}();var gt=n(406);function jt(e){var t=[e.indi.firstName,e.indi.lastName].join(" ").trim();return e.id.length>8?t:Object(P.jsxs)(P.Fragment,{children:[t," ",Object(P.jsxs)("i",{children:["(",e.id,")"]})]})}var bt=function(e){Object(j.a)(n,e);var t=Object(b.a)(n);function n(){var e;Object(f.a)(this,n);for(var r=arguments.length,a=new Array(r),i=0;i0&&v<=j)){e.next=42;break}return e.next=34,Ut(b,a);case 34:w=e.sent,o.push.apply(o,Object(Y.a)(w)),k=w.flatMap((function(e){return Object.values(e.Spouses)})),o.push.apply(o,Object(Y.a)(k)),b=w.flatMap((function(e){return Object.values(e.Children).map((function(e){return e.Name}))})),v++,e.next=31;break;case 42:return _=new Map,x=new Map,y=new Map,D=new Map,o.forEach((function(e){if(D.set(e.Id,e.Name),e.Mother||e.Father){var t=Jt(e.Mother,e.Father);en(_,e.Mother).add(t),en(_,e.Father).add(t),en(x,t).add(e.Id),y.set(t,{wife:e.Mother||void 0,husband:e.Father||void 0})}})),E=[],T=new Set,o.forEach((function(e){if(!T.has(e.Id)){T.add(e.Id);var t=Yt(e,n);e.Spouses&&Object.values(e.Spouses).forEach((function(t){var n=Jt(e.Id,t.Id);en(_,e.Id).add(n),en(_,t.Id).add(n);var r="Male"===e.Gender?{wife:t.Id,husband:e.Id,spouse:t}:{wife:e.Id,husband:t.Id,spouse:t};y.set(n,r)})),t.fams=Array.from(en(_,e.Id)),E.push(t)}})),I=Array.from(y.entries()).map((function(e){var t=Object(O.a)(e,2),n=t[0],r=t[1],a={id:n},i=r.wife&&D.get(r.wife);i&&(a.wife=i);var o=r.husband&&D.get(r.husband);if(o&&(a.husb=o),a.children=Array.from(en(x,n)).map((function(e){return D.get(e)})),r.spouse&&(r.spouse.marriage_date&&"0000-00-00"!==r.spouse.marriage_date||r.spouse.marriage_location)){var s=Xt(r.spouse.marriage_date);a.marriage=Object.assign({},s,{place:r.spouse.marriage_location})}return a})),S=he({indis:E,fams:I}),R=$t(E),e.abrupt("return",{chartData:S,gedcom:R});case 54:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Jt(e,t){return t>e?"".concat(e,"_").concat(t):"".concat(t,"_").concat(e)}function Yt(e,t){var n={id:e.Name};if(e.Name.startsWith(Ct)&&(n.hideId=!0,n.firstName=t.formatMessage({id:"wikitree.private",defaultMessage:"Private"})),e.FirstName&&"Unknown"!==e.FirstName?n.firstName=e.FirstName:e.RealName&&"Unknown"!==e.RealName&&(n.firstName=e.RealName),"Unknown"!==e.LastNameAtBirth&&(n.lastName=e.LastNameAtBirth),(e.Mother||e.Father)&&(n.famc=Jt(e.Mother,e.Father)),"Male"===e.Gender?n.sex="M":"Female"===e.Gender&&(n.sex="F"),e.BirthDate&&"0000-00-00"!==e.BirthDate||e.BirthLocation||"unknown"!==e.BirthDateDecade){var r=Xt(e.BirthDate,e.DataStatus&&e.DataStatus.BirthDate)||Qt(e.BirthDateDecade);n.birth=Object.assign({},r,{place:e.BirthLocation})}if(e.DeathDate&&"0000-00-00"!==e.DeathDate||e.DeathLocation||"unknown"!==e.DeathDateDecade){var a=Xt(e.DeathDate,e.DataStatus&&e.DataStatus.DeathDate)||Qt(e.DeathDateDecade);n.death=Object.assign({},a,{place:e.DeathLocation})}return e.PhotoData&&(n.images=[{url:"https://www.wikitree.com".concat(e.PhotoData.url)}]),n}function Xt(e,t){if(e){var n=e.match(/(\d\d\d\d)-(\d\d)-(\d\d)/);if(!n)return{date:{text:e}};var r={};return"0000"!==n[1]&&(r.year=~~n[1]),"00"!==n[2]&&(r.month=~~n[2]),"00"!==n[3]&&(r.day=~~n[3]),"after"===t?{dateRange:{from:r}}:"before"===t?{dateRange:{to:r}}:("guess"===t&&(r.qualifier="abt"),{date:r})}}function Qt(e){return"unknown"!==e?{date:{text:e}}:void 0}function $t(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:[]}]},e.id.startsWith("~")||t[e.id].tree.push({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 en(e,t){var n=e.get(t);if(n)return n;var r=new Set;return e.set(t,r),r}var tn,nn=function(){function e(t){Object(f.a)(this,e),this.intl=t}return Object(g.a)(e,[{key:"isNewData",value:function(e,t,n){var r;return!!e.selection&&((null===(r=t.selection)||void 0===r?void 0:r.id)!==e.selection.id&&(!n||!n.chartData.indis.some((function(t){var n;return t.id===(null===(n=e.selection)||void 0===n?void 0:n.id)}))))}},{key:"loadData",value:function(){var e=Object(m.a)(h.a.mark((function e(t){var n;return h.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t.selection){e.next=2;break}throw new ae("WIKITREE_ID_NOT_PROVIDED","WikiTree id needs to be provided");case 2:return e.prev=2,e.next=5,qt(t.selection.id,this.intl,t.spec.authcode);case 5:return n=e.sent,w("wikitree_loaded"),e.abrupt("return",n);case 10:throw e.prev=10,e.t0=e.catch(2),w("wikitree_error"),e.t0;case 14:case"end":return e.stop()}}),e,this,[[2,10]])})));return function(t){return e.apply(this,arguments)}}()}]),e}(),rn=n(414);!function(e){e[e.UNKNOWN=0]="UNKNOWN",e[e.NOT_LOGGED_IN=1]="NOT_LOGGED_IN",e[e.LOGGED_IN=2]="LOGGED_IN"}(tn||(tn={}));var an,on=function(e){Object(j.a)(n,e);var t=Object(b.a)(n);function n(){var e;Object(f.a)(this,n);for(var a=arguments.length,i=new Array(a),o=0;o,\n) {\n const parent = select('#svgContainer').node() as Element;\n\n const scale = event.transform.k;\n const offsetX = max([0, (parent.clientWidth - size[0] * scale) / 2]);\n const offsetY = max([0, (parent.clientHeight - size[1] * scale) / 2]);\n select('#chartSvg')\n .attr('width', size[0] * scale)\n .attr('height', size[1] * scale)\n .attr('transform', `translate(${offsetX}, ${offsetY})`);\n select('#chart').attr('transform', `scale(${scale})`);\n\n parent.scrollLeft = -event.transform.x;\n parent.scrollTop = -event.transform.y;\n}\n\n/** Called when the scrollbars are used. */\nfunction scrolled() {\n const parent = select('#svgContainer').node() as Element;\n const x = parent.scrollLeft + parent.clientWidth / 2;\n const y = parent.scrollTop + parent.clientHeight / 2;\n const scale = zoomTransform(parent).k;\n select(parent).call(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 freezeAnimation?: boolean;\n}\n\n/** Component showing the genealogy chart and handling transition animations. */\nexport class ChartComponent extends React.PureComponent<\n ChartProps & WrappedComponentProps,\n {}\n> {\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?: 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 = select('#svgContainer') as Selection;\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 // Freeze changing selection after initial rendering.\n if (!args.initialRender && this.props.freezeAnimation) {\n return;\n }\n\n if (args.initialRender) {\n (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.props.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 = select('#chartSvg');\n const parent = select('#svgContainer').node() as Element;\n\n const scale = zoomTransform(parent).k;\n const zoomOutFactor = min([\n 1,\n scale,\n parent.clientWidth / chartInfo.size[0],\n parent.clientHeight / chartInfo.size[1],\n ])!;\n const extent: [number, number] = [max([0.1, zoomOutFactor])!, 2];\n\n this.zoomBehavior = zoom()\n .scaleExtent(extent)\n .translateExtent([[0, 0], chartInfo.size])\n .on('zoom', (event) => zoomed(chartInfo.size, event));\n select(parent).on('scroll', scrolled).call(this.zoomBehavior);\n\n const scrollTopTween = (scrollTop: number) => {\n return () => {\n const i = 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 = 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 = max([\n 0,\n (parent.clientWidth - chartInfo.size[0] * scale) / 2,\n ]);\n const offsetY = max([\n 0,\n (parent.clientHeight - chartInfo.size[1] * scale) / 2,\n ]);\n const svgTransition = svg.transition().delay(200).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 render() {\n return (\n
\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 = select('#svgContainer').node() as Element;\n const scale = 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 // Lazy load jspdf.\n const {default: jspdf} = await import('jspdf');\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}\nexport const Chart = injectIntl(ChartComponent, {forwardRef: true});\n","import {createMedia} from '@artsy/fresnel';\n\n/** Defines the breakpoints at which to show different UI variants. */\nconst AppMedia = createMedia({\n breakpoints: {\n small: 320,\n large: 768,\n },\n});\nexport const mediaStyles = AppMedia.createMediaStyle();\nexport const {Media, MediaContextProvider} = AppMedia;\n","import {IndiInfo} from 'topola';\nimport {TopolaData} from '../util/gedcom_util';\n\n/** Supported data sources. */\nexport enum DataSourceEnum {\n UPLOADED,\n GEDCOM_URL,\n WIKITREE,\n EMBEDDED,\n}\n\n/** Source specification together with individual selection. */\nexport interface SourceSelection {\n spec: SourceSpecT;\n selection?: IndiInfo;\n}\n\n/** Interface encapsulating functions specific for a data source. */\nexport interface 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(\n newSource: SourceSelection,\n oldSource: SourceSelection,\n data?: TopolaData,\n ): boolean;\n /** Loads data from the data source. */\n loadData(spec: SourceSelection): Promise;\n}\n","/** Error class adding an error code used for i18n. */\nexport class TopolaError extends Error {\n constructor(\n public readonly code: string,\n message: string,\n public readonly args: {[key: string]: string} = {},\n ) {\n super(message);\n }\n}\n","import {GedcomEntry, parse as parseGedcom} from 'parse-gedcom';\nimport {TopolaError} from './error';\nimport {\n JsonFam,\n JsonGedcomData,\n JsonIndi,\n gedcomEntriesToJson,\n JsonImage,\n JsonEvent,\n} from 'topola';\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 TopolaError('GEDCOM_READ_FAILED', '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, DateOrRange, DateRange, getDate} from 'topola';\nimport {IntlShape} 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: IntlShape) {\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: Intl.DateTimeFormatOptions = {\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: IntlShape) {\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: IntlShape,\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: IntlShape): 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 {\n FormattedMessage,\n injectIntl,\n IntlShape,\n WrappedComponentProps,\n} from 'react-intl';\nimport {GedcomData, pointerToId} from './util/gedcom_util';\nimport {GedcomEntry} from 'parse-gedcom';\nimport {translateDate} from './util/date_util';\n\ninterface Props {\n gedcom: GedcomData;\n indi: string;\n}\n\nconst EVENT_TAGS = ['BIRT', 'BAPM', 'CHR', 'EVEN', 'CENS', 'DEAT', 'BURI'];\nconst EXCLUDED_TAGS = ['NAME', 'SEX', 'FAMC', 'FAMS', 'NOTE', 'SOUR'];\nconst TAG_DESCRIPTIONS = new Map([\n ['ADOP', 'Adoption'],\n ['BAPM', 'Baptism'],\n ['BIRT', 'Birth'],\n ['BURI', 'Burial'],\n ['CENS', 'Census'],\n ['CHR', 'Christening'],\n ['CREM', 'Cremation'],\n ['DEAT', 'Death'],\n ['EDUC', 'Education'],\n ['EMAIL', 'E-mail'],\n ['EMIG', 'Emigration'],\n ['EVEN', 'Event'],\n ['FACT', 'Fact'],\n ['IMMI', 'Immigration'],\n ['MARR', 'Marriage'],\n ['MILT', 'Military services'],\n ['NATU', 'Naturalization'],\n ['OCCU', 'Occupation'],\n ['TITL', 'Title'],\n ['WWW', 'WWW'],\n]);\n\nfunction translateTag(tag: string) {\n const normalizedTag = tag.replace(/_/g, '');\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: IntlShape) {\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\nclass DetailsComponent extends React.Component<\n Props & WrappedComponentProps,\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.props.intl),\n )}\n {getOtherDetails(entriesWithData)}\n {getDetails(entriesWithData, ['NOTE'], noteDetails)}\n
\n );\n }\n}\nexport const Details = injectIntl(DetailsComponent);\n","import {analyticsEvent} from '../util/analytics';\nimport {convertGedcom, getSoftware, TopolaData} from '../util/gedcom_util';\nimport {DataSource, DataSourceEnum, SourceSelection} from './data_source';\nimport {IndiInfo, JsonGedcomData} from 'topola';\nimport {TopolaError} from '../util/error';\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 selection?: IndiInfo,\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 selection && data.indis.some((i) => i.id === selection.id)\n ? selection.id\n : data.indis[0].id;\n return {id, generation: selection?.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\n const driveUrlMatch1 = url.match(\n /https:\\/\\/drive\\.google\\.com\\/file\\/d\\/(.*)\\/.*/,\n );\n if (driveUrlMatch1) {\n url = `https://drive.google.com/uc?id=${driveUrlMatch1[1]}&export=download`;\n }\n const driveUrlMatch2 = url.match(\n /https:\\/\\/drive\\.google\\.com\\/open\\?id=([^&]*)&?.*/,\n );\n if (driveUrlMatch2) {\n url = `https://drive.google.com/uc?id=${driveUrlMatch2[1]}&export=download`;\n }\n\n const urlToFetch = handleCors\n ? 'https://topola-cors.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 TopolaError(\n 'ERROR_LOADING_UPLOADED_FILE',\n 'Error loading data. Please upload your file again.',\n );\n }\n return prepareData(gedcom, hash, images);\n}\n\nexport interface UploadSourceSpec {\n source: DataSourceEnum.UPLOADED;\n gedcom?: string;\n /** Hash of the GEDCOM contents. */\n hash: string;\n images?: Map;\n}\n\n/** Files opened from the local computer. */\nexport class UploadedDataSource implements DataSource {\n // isNewData(args: Arguments, state: State): boolean {\n isNewData(\n newSource: SourceSelection,\n oldSource: SourceSelection,\n data?: TopolaData,\n ): boolean {\n return newSource.spec.hash !== oldSource.spec.hash;\n }\n\n async loadData(\n source: SourceSelection,\n ): Promise {\n try {\n const data = await loadGedcom(\n source.spec.hash,\n source.spec.gedcom,\n source.spec.images,\n );\n const software = getSoftware(data.gedcom.head);\n analyticsEvent('upload_file_loaded', {\n event_label: software,\n event_value: (source.spec.images && source.spec.images.size) || 0,\n });\n return data;\n } catch (error) {\n analyticsEvent('upload_file_error');\n throw error;\n }\n }\n}\n\nexport interface UrlSourceSpec {\n source: DataSourceEnum.GEDCOM_URL;\n /** URL of the data that is loaded or is being loaded. */\n url: string;\n handleCors: boolean;\n}\n\n/** GEDCOM file loaded by pointing to a URL. */\nexport class GedcomUrlDataSource implements DataSource {\n isNewData(\n newSource: SourceSelection,\n oldSource: SourceSelection,\n data?: TopolaData,\n ): boolean {\n return newSource.spec.url !== oldSource.spec.url;\n }\n\n async loadData(source: SourceSelection): Promise {\n try {\n const data = await loadFromUrl(source.spec.url, source.spec.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","import {analyticsEvent} from '../util/analytics';\nimport {DataSource, DataSourceEnum, SourceSelection} from './data_source';\nimport {getSoftware, TopolaData} from '../util/gedcom_util';\nimport {loadGedcom} from './load_data';\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\nexport interface EmbeddedSourceSpec {\n source: DataSourceEnum.EMBEDDED;\n}\n\n/** GEDCOM file received from outside of the iframe. */\nexport class EmbeddedDataSource implements DataSource {\n isNewData(\n newSource: SourceSelection,\n oldSource: SourceSelection,\n data?: TopolaData,\n ): boolean {\n // Never reload data.\n return false;\n }\n\n private async onMessage(\n message: EmbeddedMessage,\n resolve: (value: TopolaData) => void,\n reject: (reason: any) => void,\n ) {\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 resolve(data);\n } catch (error) {\n analyticsEvent('embedded_file_error');\n reject(error);\n }\n }\n }\n\n async loadData(\n source: SourceSelection,\n ): Promise {\n // Notify the parent window that we are ready.\n return new Promise((resolve, reject) => {\n window.parent.postMessage({message: EmbeddedMessageType.READY}, '*');\n window.addEventListener('message', (data) =>\n this.onMessage(data.data, resolve, reject),\n );\n });\n }\n}\n","export default __webpack_public_path__ + \"static/media/topola.a3ffa9a5.jpg\";","import * as queryString from 'query-string';\nimport * as React from 'react';\nimport logo from './topola.jpg';\nimport {Card, Grid, Image} from 'semantic-ui-react';\nimport {FormattedMessage} from 'react-intl';\nimport {Link} from 'react-router-dom';\nimport {Media} from './util/media';\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 \"Topola\n \n {contents}\n \n \n \n \n {contents}\n \n \n \n
\n );\n}\n","import * as React from 'react';\nimport {\n Menu,\n Dropdown,\n MenuItemProps,\n DropdownItemProps,\n} from 'semantic-ui-react';\n\nexport enum MenuType {\n Menu,\n Dropdown,\n}\n\ninterface Props {\n menuType?: MenuType;\n}\n\nexport class MenuItem extends React.Component<\n Props & MenuItemProps & DropdownItemProps\n> {\n render() {\n const newProps = {...this.props};\n // Remove menuType from props to avoid error message in the console.\n delete newProps.menuType;\n return (\n <>\n {this.props.menuType === MenuType.Menu ? (\n {this.props.children}\n ) : (\n {this.props.children}\n )}\n \n );\n }\n}\n","import lunr from 'lunr';\nimport naturalSort from 'javascript-natural-sort';\nimport {idToFamMap, idToIndiMap} from '../util/gedcom_util';\nimport {JsonFam, JsonGedcomData, JsonIndi} from 'topola';\n\n// TODO: Add type declarations and use import instead of require.\nrequire('lunr-languages/lunr.stemmer.support')(lunr);\nrequire('lunr-languages/lunr.multi')(lunr);\nrequire('lunr-languages/lunr.de')(lunr);\nrequire('lunr-languages/lunr.fr')(lunr);\nrequire('lunr-languages/lunr.it')(lunr);\nrequire('lunr-languages/lunr.ru')(lunr);\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.use((lunr as any).multiLanguage('de', 'en', 'fr', 'it', 'ru'));\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 debounce from 'debounce';\nimport {analyticsEvent} from '../util/analytics';\nimport {buildSearchIndex, SearchIndex, SearchResult} from './search_index';\nimport {formatDateOrRange} from '../util/date_util';\nimport {IndiInfo, JsonGedcomData} from 'topola';\nimport {injectIntl, WrappedComponentProps} from 'react-intl';\nimport {JsonIndi} from 'topola';\nimport {RouteComponentProps} from 'react-router-dom';\nimport {Search, SearchProps, SearchResultProps} from 'semantic-ui-react';\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\ninterface Props {\n /** Data used for the search index. */\n data: JsonGedcomData;\n onSelection: (indiInfo: IndiInfo) => void;\n}\n\ninterface State {\n searchResults: SearchResultProps[];\n}\n\n/** Displays and handles the search box in the top bar. */\nclass SearchBarComponent extends React.Component<\n RouteComponentProps & WrappedComponentProps & Props,\n State\n> {\n state: State = {\n searchResults: [],\n };\n\n searchRef?: {setValue(value: string): void};\n searchIndex?: SearchIndex;\n\n private getDescriptionLine(indi: JsonIndi) {\n const birthDate = formatDateOrRange(indi.birth, this.props.intl);\n const deathDate = formatDateOrRange(indi.death, this.props.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. */\n private displaySearchResult(result: SearchResult) {\n return {\n id: result.id,\n key: result.id,\n title: getNameLine(result),\n description: this.getDescriptionLine(result.indi),\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 this.displaySearchResult(result),\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.onSelection({id, generation: 0});\n this.searchRef!.setValue('');\n }\n\n private initializeSearchIndex() {\n this.searchIndex = buildSearchIndex(this.props.data);\n }\n\n componentDidMount() {\n this.initializeSearchIndex();\n }\n\n componentDidUpdate(prevProps: Props) {\n if (prevProps.data !== this.props.data) {\n this.initializeSearchIndex();\n }\n }\n\n render() {\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.props.intl.formatMessage({\n id: 'menu.search.no_results',\n defaultMessage: 'No results found',\n })}\n placeholder={this.props.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}\nexport const SearchBar = injectIntl(SearchBarComponent);\n","import * as queryString from 'query-string';\nimport * as React from 'react';\nimport md5 from 'md5';\nimport {analyticsEvent} from '../util/analytics';\nimport {Dropdown, Icon, Menu} from 'semantic-ui-react';\nimport {FormattedMessage} from 'react-intl';\nimport {MenuType} from './menu_item';\nimport {RouteComponentProps} from 'react-router-dom';\n\nfunction loadFileAsText(file: File): Promise {\n return new Promise((resolve) => {\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\ninterface Props {\n menuType: MenuType;\n}\n\n/** Displays and handles the \"Open file\" menu. */\nexport class UploadMenu extends React.Component {\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 render() {\n const content = (\n <>\n \n \n \n );\n return (\n <>\n {this.props.menuType === MenuType.Menu ? (\n \n ) : (\n \n {content}\n \n )}\n this.handleUpload(e)}\n />\n \n );\n }\n}\n","import * as queryString from 'query-string';\nimport * as React from 'react';\nimport {analyticsEvent} from '../util/analytics';\nimport {Button, Form, Header, Icon, Input, Modal} from 'semantic-ui-react';\nimport {FormattedMessage} from 'react-intl';\nimport {MenuItem, MenuType} from './menu_item';\nimport {RouteComponentProps} from 'react-router-dom';\n\ninterface Props {\n menuType: MenuType;\n}\n\ninterface State {\n dialogOpen: boolean;\n url?: string;\n}\n\n/** Displays and handles the \"Open URL\" menu. */\nexport class UrlMenu extends React.Component<\n RouteComponentProps & Props,\n State\n> {\n state: State = {dialogOpen: false};\n\n inputRef: React.RefObject = React.createRef();\n\n /** Opens the \"Load from URL\" dialog. */\n private openDialog() {\n this.setState(Object.assign({}, this.state, {dialogOpen: true}), () =>\n this.inputRef.current!.focus(),\n );\n }\n\n /** Cancels any of the open dialogs. */\n private handleClose() {\n this.setState(\n Object.assign({}, this.state, {\n dialogOpen: 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 dialogOpen: 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 /** 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 private loadFromUrlModal() {\n return (\n this.handleClose()}\n centered={false}\n >\n
\n \n \n
\n \n
this.handleLoad()}>\n this.handleUrlChange(data.value)}\n ref={this.inputRef}\n />\n

\n \n topola-cors.herokuapp.com\n \n ),\n }}\n />\n

\n \n
\n \n \n \n \n \n );\n }\n\n render() {\n return (\n <>\n this.openDialog()}\n menuType={this.props.menuType}\n >\n \n \n \n {this.loadFromUrlModal()}\n \n );\n }\n}\n","export default __webpack_public_path__ + \"static/media/wikitree.7bffea31.png\";","import Cookies from 'js-cookie';\nimport {analyticsEvent} from '../util/analytics';\nimport {DataSource, DataSourceEnum, SourceSelection} from './data_source';\nimport {Date, DateOrRange, JsonFam, JsonIndi} from 'topola';\nimport {GedcomData, normalizeGedcom, TopolaData} from '../util/gedcom_util';\nimport {GedcomEntry} from 'parse-gedcom';\nimport {IntlShape} from 'react-intl';\nimport {TopolaError} from '../util/error';\n\n/** Prefix for IDs of private individuals. */\nexport const PRIVATE_ID_PREFIX = '~Private';\n\n/**\n * Cookie where the logged in user name is stored. This cookie is shared\n * between apps hosted on apps.wikitree.com.\n */\nconst USER_NAME_COOKIE = 'wikidb_wtb_UserName';\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 GetRelativesRequest {\n action: 'getRelatives';\n keys: string;\n getChildren?: true;\n getSpouses?: true;\n}\n\n/** WikiTree API clientLogin request. */\ninterface ClientLoginRequest {\n action: 'clientLogin';\n authcode: string;\n}\n\n/** WikiTree API clientLogin response. */\ninterface ClientLoginResponse {\n result: string;\n username: string;\n}\n\ntype WikiTreeRequest =\n | GetAncestorsRequest\n | GetRelativesRequest\n | ClientLoginRequest;\n\n/** Person structure returned from WikiTree API. */\ninterface Person {\n Id: number;\n Name: string;\n FirstName: string;\n LastNameAtBirth: string;\n RealName: 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 BirthDateDecade: string;\n DeathDateDecade: string;\n marriage_location: string;\n marriage_date: string;\n DataStatus?: {\n BirthDate: string;\n DeathDate: string;\n };\n PhotoData?: {\n path: string;\n url: 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://topola-cors.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 credentials: handleCors ? undefined : 'include',\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(\n key: string,\n handleCors: boolean,\n): Promise {\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(\n keys: string[],\n handleCors: boolean,\n): Promise {\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 const id = keysToFetch[0];\n throw new TopolaError(\n 'WIKITREE_PROFILE_NOT_FOUND',\n `WikiTree profile ${id} not found`,\n {id},\n );\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(\n authcode: string,\n): Promise {\n const response = await wikiTreeGet(\n {\n action: 'clientLogin',\n authcode,\n },\n false,\n );\n return response.clientLogin;\n}\n\n/**\n * Returnes the logged in user name or undefined if not logged in.\n *\n * This is not an authoritative answer. The result of this function relies on\n * the cookies set on the apps.wikitree.com domain under which this application\n * is hosted. The authoritative source of login information is in cookies set on\n * the api.wikitree.com domain.\n */\nexport function getLoggedInUserName(): string | undefined {\n return Cookies.get(USER_NAME_COOKIE);\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 intl: IntlShape,\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 && !getLoggedInUserName() && authcode) {\n const loginResult = await clientLogin(authcode);\n if (loginResult.result === 'Success') {\n sessionStorage.clear();\n Cookies.set(USER_NAME_COOKIE, loginResult.username);\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 const id = key;\n throw new TopolaError(\n 'WIKITREE_PROFILE_NOT_ACCESSIBLE',\n `WikiTree profile ${id} is not accessible. Try logging in.`,\n {id},\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\n // Map from person id to father id if the father profile is private.\n const privateFathers: Map = new Map();\n // Map from person id to mother id if the mother profile is private.\n const privateMothers: Map = new Map();\n\n // Andujst private individual ids so that there are no collisions in the case\n // that ancestors were collected for more than one person.\n ancestors.forEach((ancestorList, index) => {\n const offset = 1000 * index;\n // Adjust ids by offset.\n ancestorList.forEach((person) => {\n if (person.Id < 0) {\n person.Id -= offset;\n person.Name = `${PRIVATE_ID_PREFIX}${person.Id}`;\n }\n if (person.Father < 0) {\n person.Father -= offset;\n privateFathers.set(person.Id, person.Father);\n }\n if (person.Mother < 0) {\n person.Mother -= offset;\n privateMothers.set(person.Id, person.Mother);\n }\n });\n });\n\n // Set the Father and Mother fields again because getRelatives doesn't return\n // private parents.\n ancestorDetails.forEach((person) => {\n const privateFather = privateFathers.get(person.Id);\n if (privateFather) {\n person.Father = privateFather;\n }\n const privateMother = privateMothers.get(person.Id);\n if (privateMother) {\n person.Mother = privateMother;\n }\n });\n everyone.push(...ancestorDetails);\n\n // Collect private individuals.\n const privateAncestors = ancestors.flat().filter((person) => person.Id < 0);\n everyone.push(...privateAncestors);\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, intl);\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, intl: IntlShape): JsonIndi {\n const indi: JsonIndi = {\n id: person.Name,\n };\n if (person.Name.startsWith(PRIVATE_ID_PREFIX)) {\n indi.hideId = true;\n indi.firstName = intl.formatMessage({\n id: 'wikitree.private',\n defaultMessage: 'Private',\n });\n }\n if (person.FirstName && person.FirstName !== 'Unknown') {\n indi.firstName = person.FirstName;\n } else if (person.RealName && person.RealName !== 'Unknown') {\n indi.firstName = person.RealName;\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 person.BirthDateDecade !== 'unknown'\n ) {\n const parsedDate = parseDate(\n person.BirthDate,\n person.DataStatus && person.DataStatus.BirthDate,\n );\n const date = parsedDate || parseDecade(person.BirthDateDecade);\n indi.birth = Object.assign({}, date, {place: person.BirthLocation});\n }\n if (\n (person.DeathDate && person.DeathDate !== '0000-00-00') ||\n person.DeathLocation ||\n person.DeathDateDecade !== 'unknown'\n ) {\n const parsedDate = parseDate(\n person.DeathDate,\n person.DataStatus && person.DataStatus.DeathDate,\n );\n const date = parsedDate || parseDecade(person.DeathDateDecade);\n indi.death = Object.assign({}, date, {place: person.DeathLocation});\n }\n if (person.PhotoData) {\n indi.images = [{url: `https://www.wikitree.com${person.PhotoData.url}`}];\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\nfunction parseDecade(decade: string): DateOrRange | undefined {\n return decade !== 'unknown' ? {date: {text: decade}} : undefined;\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 };\n if (!indi.id.startsWith('~')) {\n gedcomIndis[indi.id].tree.push({\n level: 1,\n pointer: '',\n tag: 'WWW',\n data: `https://www.wikitree.com/wiki/${escapedId}`,\n tree: [],\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\nexport interface WikiTreeSourceSpec {\n source: DataSourceEnum.WIKITREE;\n authcode?: string;\n}\n\n/** Loading data from the WikiTree API. */\nexport class WikiTreeDataSource implements DataSource {\n constructor(private intl: IntlShape) {}\n\n isNewData(\n newSource: SourceSelection,\n oldSource: SourceSelection,\n data?: TopolaData,\n ): boolean {\n if (!newSource.selection) {\n return false;\n }\n if (oldSource.selection?.id === newSource.selection.id) {\n // Selection unchanged -> don't reload.\n return false;\n }\n if (\n data &&\n data.chartData.indis.some((indi) => indi.id === newSource.selection?.id)\n ) {\n // New selection exists in current view -> animate instead of reloading.\n return false;\n }\n return true;\n }\n\n async loadData(\n source: SourceSelection,\n ): Promise {\n if (!source.selection) {\n throw new TopolaError(\n 'WIKITREE_ID_NOT_PROVIDED',\n 'WikiTree id needs to be provided',\n );\n }\n try {\n const data = await loadWikiTree(\n source.selection.id,\n this.intl,\n source.spec.authcode,\n );\n analyticsEvent('wikitree_loaded');\n return data;\n } catch (error) {\n analyticsEvent('wikitree_error');\n throw error;\n }\n }\n}\n","import * as queryString from 'query-string';\nimport * as React from 'react';\nimport wikitreeLogo from './wikitree.png';\nimport {analyticsEvent} from '../util/analytics';\nimport {FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl';\nimport {getLoggedInUserName} from '../datasource/wikitree';\nimport {MenuItem, MenuType} from './menu_item';\nimport {RouteComponentProps} from 'react-router-dom';\nimport {Header, Button, Modal, Input, Form, Ref} from 'semantic-ui-react';\n\nenum WikiTreeLoginState {\n UNKNOWN,\n NOT_LOGGED_IN,\n LOGGED_IN,\n}\n\ninterface Props {\n menuType: MenuType;\n}\n\ninterface State {\n dialogOpen: boolean;\n wikiTreeId?: string;\n}\n\n/** Displays and handles the \"Select WikiTree ID\" menu. */\nexport class WikiTreeMenu extends React.Component<\n RouteComponentProps & Props,\n State\n> {\n state: State = {\n dialogOpen: false,\n };\n\n inputRef: React.RefObject = React.createRef();\n\n private openDialog() {\n this.setState(Object.assign({}, this.state, {dialogOpen: true}), () =>\n (this.inputRef.current!.firstChild as HTMLInputElement).focus(),\n );\n }\n\n /** Cancels any of the open dialogs. */\n private handleClose() {\n this.setState(\n Object.assign({}, this.state, {\n dialogOpen: false,\n }),\n );\n }\n\n /** Select button clicked in the \"Select WikiTree ID\" dialog. */\n private handleSelectId() {\n this.setState(\n Object.assign({}, this.state, {\n dialogOpen: 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 WikiTree ID input is typed into. */\n private handleIdChange(value: string) {\n this.setState(\n Object.assign({}, this.state, {\n wikiTreeId: value,\n }),\n );\n }\n\n private enterId(event: React.MouseEvent, id: string) {\n event.preventDefault(); // Do not follow link in href.\n (this.inputRef.current!.firstChild as HTMLInputElement).value = id;\n this.handleIdChange(id);\n (this.inputRef.current!.firstChild as HTMLInputElement).focus();\n }\n\n private wikiTreeIdModal() {\n return (\n this.handleClose()}\n centered={false}\n >\n
\n \n \n
\n \n
this.handleSelectId()}>\n

\n \n WikiTree\n \n ),\n example1: (\n this.enterId(e, 'Wojtyla-13')}\n className=\"link-span\"\n >\n Wojtyla-13\n \n ),\n example2: (\n this.enterId(e, 'Skłodowska-2')}\n className=\"link-span\"\n >\n Skłodowska-2\n \n ),\n }}\n />\n

\n \n this.handleIdChange(data.value)}\n />\n \n
\n
\n \n \n \n \n \n );\n }\n\n render() {\n return (\n <>\n this.openDialog()}\n >\n \"WikiTree\n \n \n {this.wikiTreeIdModal()}\n \n );\n }\n}\n\ninterface LoginState {\n wikiTreeLoginState: WikiTreeLoginState;\n wikiTreeLoginUsername?: string;\n}\n\n/** Displays and handles the \"Log in to WikiTree\" menu. */\nclass WikiTreeLoginMenuComponent extends React.Component<\n RouteComponentProps & WrappedComponentProps & Props,\n LoginState\n> {\n state: LoginState = {\n wikiTreeLoginState: WikiTreeLoginState.UNKNOWN,\n };\n\n wikiTreeLoginFormRef: React.RefObject = React.createRef();\n wikiTreeReturnUrlRef: React.RefObject = React.createRef();\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 wikiTreeLoginUsername = getLoggedInUserName();\n const wikiTreeLoginState = wikiTreeLoginUsername\n ? WikiTreeLoginState.LOGGED_IN\n : WikiTreeLoginState.NOT_LOGGED_IN;\n if (this.state.wikiTreeLoginState !== wikiTreeLoginState) {\n this.setState(\n Object.assign({}, this.state, {\n wikiTreeLoginState,\n wikiTreeLoginUsername,\n }),\n );\n }\n }\n\n componentDidMount() {\n this.checkWikiTreeLoginState();\n }\n\n componentDidUpdate() {\n this.checkWikiTreeLoginState();\n }\n\n render() {\n switch (this.state.wikiTreeLoginState) {\n case WikiTreeLoginState.NOT_LOGGED_IN:\n return (\n <>\n this.wikiTreeLogin()}\n >\n \n \n \n \n \n \n \n \n );\n\n case WikiTreeLoginState.LOGGED_IN:\n const tooltip = this.state.wikiTreeLoginUsername\n ? this.props.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.props.intl.formatMessage({\n id: 'menu.wikitree_popup',\n defaultMessage: 'Logged in to WikiTree',\n });\n return (\n \n \"WikiTree\n \n \n );\n }\n return null;\n }\n}\nexport const WikiTreeLoginMenu = injectIntl(WikiTreeLoginMenuComponent);\n","import * as queryString from 'query-string';\nimport * as React from 'react';\nimport {Dropdown, Icon, Menu} from 'semantic-ui-react';\nimport {FormattedMessage} from 'react-intl';\nimport {IndiInfo, JsonGedcomData} from 'topola';\nimport {Link} from 'react-router-dom';\nimport {Media} from '../util/media';\nimport {MenuType} from './menu_item';\nimport {RouteComponentProps} from 'react-router-dom';\nimport {SearchBar} from './search';\nimport {UploadMenu} from './upload_menu';\nimport {UrlMenu} from './url_menu';\nimport {WikiTreeLoginMenu, WikiTreeMenu} from './wikitree_menu';\n\nenum ScreenSize {\n LARGE,\n SMALL,\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\nexport class TopBar extends React.Component {\n private 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 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 \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 // 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 case ScreenSize.SMALL:\n return (\n <>\n \n \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 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 \n \n \n \n ) : (\n <>\n \n \n \n \n );\n return menus;\n\n case ScreenSize.SMALL:\n return (\n <>\n \n \n \n \n \n );\n }\n }\n\n private wikiTreeLoginMenu(screenSize: ScreenSize) {\n if (!this.props.showWikiTreeMenus) {\n return null;\n }\n return (\n <>\n \n {screenSize === ScreenSize.SMALL ? : 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 './util/analytics';\nimport {Chart, ChartComponent, ChartType} from './chart';\nimport {DataSourceEnum, SourceSelection} from './datasource/data_source';\nimport {Details} from './details';\nimport {EmbeddedDataSource, EmbeddedSourceSpec} from './datasource/embedded';\nimport {FormattedMessage, WrappedComponentProps} from 'react-intl';\nimport {getI18nMessage} from './util/error_i18n';\nimport {IndiInfo} from 'topola';\nimport {Intro} from './intro';\nimport {Loader, Message, Portal} from 'semantic-ui-react';\nimport {Media} from './util/media';\nimport {Redirect, Route, RouteComponentProps, Switch} from 'react-router-dom';\nimport {TopBar} from './menu/top_bar';\nimport {TopolaData} from './util/gedcom_util';\nimport {\n getSelection,\n UploadSourceSpec,\n UrlSourceSpec,\n GedcomUrlDataSource,\n UploadedDataSource,\n} from './datasource/load_data';\nimport {\n loadWikiTree,\n PRIVATE_ID_PREFIX,\n WikiTreeDataSource,\n WikiTreeSourceSpec,\n} from './datasource/wikitree';\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\nenum AppState {\n INITIAL,\n LOADING,\n ERROR,\n SHOWING_CHART,\n LOADING_MORE,\n}\n\ntype DataSourceSpec =\n | UrlSourceSpec\n | UploadSourceSpec\n | WikiTreeSourceSpec\n | EmbeddedSourceSpec;\n\n/** Arguments passed to the application, primarily through URL parameters. */\ninterface Arguments {\n sourceSpec?: DataSourceSpec;\n selection?: IndiInfo;\n chartType: ChartType;\n standalone: boolean;\n freezeAnimation?: boolean;\n showSidePanel: boolean;\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 view = getParam('view');\n const chartTypes = new Map([\n ['relatives', ChartType.Relatives],\n ['fancy', ChartType.Fancy],\n ]);\n\n const hash = getParam('file');\n const url = getParam('url');\n const embedded = getParam('embedded') === 'true'; // False by default.\n var sourceSpec: DataSourceSpec | undefined = undefined;\n if (getParam('source') === 'wikitree') {\n sourceSpec = {\n source: DataSourceEnum.WIKITREE,\n authcode: getParam('?authcode'),\n };\n } else if (hash) {\n sourceSpec = {\n source: DataSourceEnum.UPLOADED,\n hash,\n gedcom: location.state && location.state.data,\n images: location.state && location.state.images,\n };\n } else if (url) {\n sourceSpec = {\n source: DataSourceEnum.GEDCOM_URL,\n url,\n handleCors: getParam('handleCors') !== 'false', // True by default.\n };\n } else if (embedded) {\n sourceSpec = {source: DataSourceEnum.EMBEDDED};\n }\n\n const indi = getParam('indi');\n const parsedGen = Number(getParam('gen'));\n const selection = indi\n ? {id: indi, generation: !isNaN(parsedGen) ? parsedGen : 0}\n : undefined;\n\n return {\n sourceSpec,\n selection,\n // Hourglass is the default view.\n chartType: chartTypes.get(view) || ChartType.Hourglass,\n\n showSidePanel: getParam('sidePanel') !== 'false', // True by default.\n standalone: getParam('standalone') !== 'false' && !embedded,\n freezeAnimation: getParam('freeze') === 'true', // False by default\n };\n}\n\n/**\n * Returs true if the changes object has values that are different than those\n * in state.\n */\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 /** State of the application. */\n state: AppState;\n /** Loaded data. */\n data?: TopolaData;\n /** Selected individual. */\n selection?: IndiInfo;\n /** Error to display. */\n error?: string;\n /** Whether the side panel is shown. */\n showSidePanel?: 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 /** Specification of the source of the data. */\n sourceSpec?: DataSourceSpec;\n /** Freeze animations after initial chart render. */\n freezeAnimation?: boolean;\n}\n\nexport class App extends React.Component<\n RouteComponentProps & WrappedComponentProps,\n {}\n> {\n state: State = {\n state: AppState.INITIAL,\n standalone: true,\n chartType: ChartType.Hourglass,\n showErrorPopup: false,\n };\n chartRef: ChartComponent | null = null;\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 state: AppState.ERROR,\n error,\n }),\n );\n }\n\n componentDidMount() {\n this.componentDidUpdate();\n }\n\n private readonly uploadedDataSource = new UploadedDataSource();\n private readonly gedcomUrlDataSource = new GedcomUrlDataSource();\n private readonly wikiTreeDataSource = new WikiTreeDataSource(this.props.intl);\n private readonly embeddedDataSource = new EmbeddedDataSource();\n\n private isNewData(sourceSpec: DataSourceSpec, selection?: IndiInfo) {\n if (\n !this.state.sourceSpec ||\n this.state.sourceSpec.source !== sourceSpec.source\n ) {\n // New data source means new data.\n return true;\n }\n const newSource = {spec: sourceSpec, selection};\n const oldSouce = {\n spec: this.state.sourceSpec,\n selection: this.state.selection,\n };\n switch (newSource.spec.source) {\n case DataSourceEnum.UPLOADED:\n return this.uploadedDataSource.isNewData(\n newSource as SourceSelection,\n oldSouce as SourceSelection,\n this.state.data,\n );\n case DataSourceEnum.GEDCOM_URL:\n return this.gedcomUrlDataSource.isNewData(\n newSource as SourceSelection,\n oldSouce as SourceSelection,\n this.state.data,\n );\n case DataSourceEnum.WIKITREE:\n return this.wikiTreeDataSource.isNewData(\n newSource as SourceSelection,\n oldSouce as SourceSelection,\n this.state.data,\n );\n case DataSourceEnum.EMBEDDED:\n return this.embeddedDataSource.isNewData(\n newSource as SourceSelection,\n oldSouce as SourceSelection,\n this.state.data,\n );\n }\n }\n\n private loadData(sourceSpec: DataSourceSpec, selection?: IndiInfo) {\n switch (sourceSpec.source) {\n case DataSourceEnum.UPLOADED:\n return this.uploadedDataSource.loadData({spec: sourceSpec, selection});\n case DataSourceEnum.GEDCOM_URL:\n return this.gedcomUrlDataSource.loadData({spec: sourceSpec, selection});\n case DataSourceEnum.WIKITREE:\n return this.wikiTreeDataSource.loadData({spec: sourceSpec, selection});\n case DataSourceEnum.EMBEDDED:\n return this.embeddedDataSource.loadData({spec: sourceSpec, selection});\n }\n }\n\n async componentDidUpdate() {\n if (this.props.location.pathname !== '/view') {\n if (this.state.state !== AppState.INITIAL) {\n this.setState(Object.assign({}, this.state, {state: AppState.INITIAL}));\n }\n return;\n }\n\n const args = getArguments(this.props.location);\n\n if (!args.sourceSpec) {\n this.props.history.replace({pathname: '/'});\n } else if (\n this.state.state === AppState.INITIAL ||\n this.isNewData(args.sourceSpec, args.selection)\n ) {\n // Set loading state.\n this.setState(\n Object.assign({}, this.state, {\n state: AppState.LOADING,\n sourceSpec: args.sourceSpec,\n selection: args.selection,\n standalone: args.standalone,\n chartType: args.chartType,\n }),\n );\n try {\n const data = await this.loadData(args.sourceSpec, args.selection);\n // Set state with data.\n this.setState(\n Object.assign({}, this.state, {\n state: AppState.SHOWING_CHART,\n data,\n selection: getSelection(data.chartData, args.selection),\n showSidePanel: args.showSidePanel,\n }),\n );\n } catch (error) {\n this.setError(getI18nMessage(error, this.props.intl));\n }\n } else if (\n this.state.state === AppState.SHOWING_CHART ||\n this.state.state === AppState.LOADING_MORE\n ) {\n // Update selection if it has changed in the URL.\n const selection = getSelection(\n this.state.data!.chartData,\n args.selection,\n );\n const loadMoreFromWikitree =\n args.sourceSpec.source === DataSourceEnum.WIKITREE &&\n (!this.state.selection || this.state.selection.id !== selection.id);\n this.updateDisplay(selection, {\n chartType: args.chartType,\n state: loadMoreFromWikitree\n ? AppState.LOADING_MORE\n : AppState.SHOWING_CHART,\n });\n if (loadMoreFromWikitree) {\n try {\n const data = await loadWikiTree(args.selection!.id, this.props.intl);\n const selection = getSelection(data.chartData, args.selection);\n this.setState(\n Object.assign({}, this.state, {\n state: AppState.SHOWING_CHART,\n data,\n selection,\n }),\n );\n } catch (error) {\n this.showErrorPopup(\n this.props.intl.formatMessage(\n {\n id: 'error.failed_wikitree_load_more',\n defaultMessage: 'Failed to load data from WikiTree. {error}',\n },\n {error},\n ),\n {state: AppState.SHOWING_CHART},\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 // Don't allow selecting WikiTree private profiles.\n if (selection.id.startsWith(PRIVATE_ID_PREFIX)) {\n return;\n }\n analyticsEvent('selection_changed');\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, otherStateChanges?: Partial) {\n this.setState(\n Object.assign(\n {},\n this.state,\n {\n showErrorPopup: true,\n error: message,\n },\n otherStateChanges,\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.props.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.props.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 private onDismissErrorPopup = () => {\n this.setState(\n Object.assign({}, this.state, {\n showErrorPopup: false,\n }),\n );\n };\n\n private renderMainArea = () => {\n switch (this.state.state) {\n case AppState.SHOWING_CHART:\n case AppState.LOADING_MORE:\n return (\n
\n \n {this.state.state === AppState.LOADING_MORE ? (\n \n ) : null}\n (this.chartRef = ref)}\n />\n {this.state.showSidePanel ? (\n \n \n \n ) : null}\n
\n );\n\n case AppState.ERROR:\n return ;\n\n case AppState.INITIAL:\n case AppState.LOADING:\n return ;\n }\n };\n\n render() {\n return (\n <>\n (\n \n )}\n />\n \n \n \n \n \n \n );\n }\n}\n","import {IntlShape} from 'react-intl';\nimport {TopolaError} from './error';\n\n/**\n * Returns a translated message for the given error. If the message can't be\n * translated, the original error.message is returned.\n */\nexport function getI18nMessage(error: Error, intl: IntlShape): string {\n if (!(error instanceof TopolaError)) {\n return error.message;\n }\n return intl.formatMessage(\n {\n id: `error.${error.code}`,\n defaultMessage: error.message,\n },\n error.args,\n );\n}\n","import * as React from 'react';\nimport * as ReactDOM from 'react-dom';\nimport messages_cs from './translations/cs.json';\nimport messages_de from './translations/de.json';\nimport messages_fr from './translations/fr.json';\nimport messages_it from './translations/it.json';\nimport messages_pl from './translations/pl.json';\nimport messages_ru from './translations/ru.json';\nimport {App} from './app';\nimport {detect} from 'detect-browser';\nimport {HashRouter as Router, Route} from 'react-router-dom';\nimport {IntlProvider} from 'react-intl';\nimport {MediaContextProvider, mediaStyles} from './util/media';\nimport './index.css';\nimport 'semantic-ui-css/semantic.min.css';\nimport 'canvas-toBlob';\n\nconst messages = {\n cs: messages_cs,\n de: messages_de,\n fr: messages_fr,\n it: messages_it,\n pl: messages_pl,\n ru: messages_ru,\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 (modern) browser.\n

,\n document.querySelector('#root'),\n );\n} else {\n ReactDOM.render(\n \n \n \n \n \n \n \n ,\n document.querySelector('#root'),\n );\n}\n"],"sourceRoot":""} \ No newline at end of file