/* eslint-disable no-bitwise */
import {html} from 'lit'
import '@material/mwc-icon'
import dayjs from 'dayjs/esm'
import relativeTime from 'dayjs/esm/plugin/relativeTime'
import {asteriskIcon, crossIcon} from './icons.js'
import {additionalStrings} from './strings.js'
dayjs.extend(relativeTime)
const BASE_DIR = ''
export function translate(strings, s) {
if (s === undefined) {
return ''
}
if (s in strings) {
return strings[s].replace('_', '')
}
return s.replace('_', '')
}
export function personTitleFromProfile(personProfile) {
return `${personProfile.name_given || '…'} ${
personProfile.name_surname || '…'
}`
}
export function familyTitleFromProfile(familyProfile) {
return `${personTitleFromProfile(
familyProfile.father || {}
)} & ${personTitleFromProfile(familyProfile.mother || {})}`
}
export function citationTitleFromProfile(citationProfile) {
return `${citationProfile.source?.title || ''}
${citationProfile.page ? ` (${citationProfile.page})` : ''}`
}
export function eventTitleFromProfile(eventProfile, strings, date = true) {
if (eventProfile.summary) {
return html`${eventProfile.summary}${date && eventProfile.date
? ` (${eventProfile.date})`
: ''}`
}
// the code below is only kept for backward compatibility
const primary = translate(strings, 'Primary')
const family = translate(strings, 'Family')
const people =
eventProfile?.participants?.people.filter(
obj => obj.role === primary || obj.role === 'Primary'
) || []
const families =
eventProfile?.participants?.families.filter(
obj => obj.role === family || obj.role === 'Family'
) || []
const primaryPeople = `${people
.map(obj => personTitleFromProfile(obj.person))
.join(', ')}
${families.map(obj => familyTitleFromProfile(obj.family)).join(', ')}`
return html`${eventProfile.type}${primaryPeople.trim()
? `: ${primaryPeople}`
: ''}${eventProfile.date ? ` (${eventProfile.date})` : ''}`
}
export function renderPerson(personProfile) {
return html`
person
${personProfile.name_given || '…'}
${personProfile.name_surname || '…'}
${personProfile?.birth?.date
? html` ${asteriskIcon} ${personProfile.birth.date}`
: ''}
${personProfile?.death?.date
? html` ${crossIcon} ${personProfile.death.date}`
: ''}
`
}
export function getName(obj, type) {
switch (type) {
case 'person':
return obj?.primary_name?.first_name
case 'event':
return obj.type
case 'family':
return ''
case 'place':
return obj?.name?.value || obj.title
case 'source':
return obj.title
case 'repository':
return obj.name
case 'citation':
return obj.page
case 'media':
return obj.desc
default:
return ''
}
}
export function showObject(type, obj, strings) {
switch (type) {
case 'person':
return html`
person
${obj?.profile?.name_given || html`…`}
${obj?.profile?.name_surname || html`…`}
`
case 'family':
return html`
people
${familyTitleFromProfile(obj.profile || {}) || type}
`
case 'event':
return html`
event
${eventTitleFromProfile(obj.profile || {}, strings) || obj.type}
`
case 'place':
return html`
place
${obj?.profile?.name || obj?.name?.value || obj.title || type}
`
case 'source':
return html`
bookmarks
${getName(obj, type) || type}
`
case 'citation':
return html`
bookmark
${citationTitleFromProfile(obj.profile || {}) || type}
`
case 'repository':
return html`
account_balance
${getName(obj, type) || type}
`
case 'note':
return html`
sticky_note_2
${translate(strings, obj.type) || type}
`
case 'media':
return html`
photo
${getName(obj, type) || type}
`
case 'tag':
return html``
default:
return `unknown type: ${type}`
}
}
export const objectIcon = {
person: 'person',
family: 'people',
event: 'event',
place: 'place',
source: 'bookmarks',
citation: 'bookmark',
repository: 'account_balance',
note: 'sticky_note_2',
media: 'photo',
tag: 'label',
}
export const objectTypeToEndpoint = {
person: 'people',
family: 'families',
event: 'events',
place: 'places',
source: 'sources',
citation: 'citations',
repository: 'repositories',
note: 'notes',
media: 'media',
tag: 'tags',
}
export function objectDescription(type, obj, strings) {
switch (type) {
case 'person':
return html`${obj?.profile?.name_given || html`…`}
${obj?.profile?.name_surname || html`…`}`
case 'family':
return html`${familyTitleFromProfile(obj.profile || {}) || type}`
case 'event':
return html`${eventTitleFromProfile(obj.profile || {}, strings, false) ||
obj.type}`
case 'place':
return html`${obj?.profile?.name ||
obj?.name?.value ||
obj.title ||
type}`
case 'source':
return html`${getName(obj, type) || type}`
case 'citation':
return html`${citationTitleFromProfile(obj.profile || {}) || type}`
case 'repository':
return html`${getName(obj, type) || type}`
case 'note':
return html`${translate(strings, obj.type) || type}`
case 'media':
return html`${getName(obj, type) || type}`
case 'tag':
return html`${obj.name}`
default:
return `unknown type: ${type}`
}
}
export function objectDetail(type, obj, strings) {
switch (type) {
case 'person':
return `
${obj?.profile?.birth?.date ? `* ${obj.profile.birth.date}` : ''}${
obj?.profile?.birth?.place && obj?.profile?.birth?.date ? ', ' : ''
}${obj?.profile?.birth?.place || ''}
`
// case 'family':
// return ''
case 'event':
return `
${obj?.profile?.date || ''}${
obj?.profile?.place && obj?.profile?.date ? ', ' : ''
}${obj?.profile?.place || ''}
`
case 'place':
return `
${obj?.profile?.type ? obj.profile.type : ''}
`
// case 'source':
// return ''
// case 'citation':
// return ''
case 'repository':
return `
${obj.type ? translate(strings, obj.type) : ''}
`
case 'note':
return obj?.text?.string || ''
case 'media':
if (obj.mime.startsWith('audio')) {
return translate(strings, 'Audio')
}
if (obj.mime.startsWith('video')) {
return translate(strings, 'Video')
}
if (obj.mime.startsWith('image')) {
return translate(strings, 'Image')
}
if (obj.mime === 'application/pdf') {
return translate(strings, 'PDF')
}
return obj.mime || ''
// case 'tag':
// return ''
default:
return ''
}
}
export function prettyTimeDiffTimestamp(timestamp, locale) {
// pt_PT is the only locale we have to rename
const dayjsLocale = locale === 'pt_PT' ? 'pt' : locale
dayjs.locale(dayjsLocale.toLowerCase().replace('_', '-'))
return dayjs.unix(timestamp).fromNow()
}
export function debounce(func, wait) {
let timeout
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout)
func(...args)
}
clearTimeout(timeout)
timeout = setTimeout(later, wait)
}
}
export function getNameFromProfile(obj, type, strings) {
switch (type) {
case 'person':
return personTitleFromProfile(obj)
case 'event':
return eventTitleFromProfile(obj, strings)
case 'family':
return familyTitleFromProfile(obj)
case 'place':
return obj.name
case 'source':
return obj.title
case 'repository':
return obj.name
case 'citation':
return citationTitleFromProfile(obj)
case 'media':
return obj.desc
default:
return ''
}
}
export function fireEvent(target, name, detail) {
target.dispatchEvent(
new CustomEvent(name, {bubbles: true, composed: true, detail})
)
}
function uuidv4() {
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
(
c ^
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
).toString(16)
)
}
export function makeHandle() {
return uuidv4()
}
// Gregorian date to Julian day needed for Date.sortval
export function getSortval(year, month, day) {
return Math.ceil(2440587.5 + Date.UTC(year, month - 1, day) / 86400000)
}
export function getBrowserLanguage() {
// get browser language and replace all '-' with '_'
// since the strings from backend comes with underscore
const browserLang = navigator.language.replaceAll('-', '_')
if (browserLang in additionalStrings) {
return browserLang
}
if (browserLang.split('_')[0] in additionalStrings) {
return browserLang.split('_')[0]
}
return null
}
// return translated date span
export function dateSpanLocal(date1, date2, lang) {
const localStrings = {
ar: 'بين 1111 و 2222',
bg: 'между 1111 и 2222',
ca: 'entre 1111 i 2222',
cs: 'mezi 1111 a 2222',
da: 'mellem 1111 og 2222',
de: 'zwischen 1111 und 2222',
el: 'μεταξύ 1111 και 2222',
en: 'between 1111 and 2222',
en_GB: 'between 1111 and 2222',
es: 'entre 1111 y 2222',
fi: '1111 ja 2222 välillä',
fr: 'entre 1111 et 2222',
hr: 'između 1111 i 2222',
hu: '1111 és 2222 között',
is: 'milli 1111 og 2222',
it: 'tra 1111 e 2222',
ja: '1111と2222の間',
lt: 'tarp 1111 ir 2222',
nb: 'mellom 1111 og 2222',
nl: 'tussen 1111 en 2222',
nn: 'mellom 1111 og 2222',
pl: 'między 1111 a 2222',
pt_BR: 'entre 1111 e 2222',
pt_PT: 'entre 1111 e 2222',
ru: 'между 1111 и 2222',
sk: 'medzi 1111 a 2222',
sl: 'med 1111 in 2222',
sr: 'између 1111 и 2222',
sv: 'mellan 1111 och 2222',
uk: 'між 1111 та 2222',
zh_CN: ' 介于 1111 与 2222 之间',
zh_HK: ' 介于 1111 与 2222 之间',
zh_TW: ' 介于 1111 与 2222 之间',
}
let str = localStrings[lang]
if (!str) {
const candidates = Object.keys(localStrings).filter(key =>
key.startsWith(lang)
)
if (candidates.length === 0) {
return null
}
str = localStrings[candidates[0]]
}
return str.replace('1111', date1).replace('2222', date2)
}
export const personFilter = {
IsFemale: 'Females',
HasUnknownGender: 'People with unknown gender',
IsMale: 'Males',
HasAlternateName: 'People with an alternate name',
HasNickname: 'People with a nickname',
HaveAltFamilies: 'Adopted people',
HaveChildren: 'People with children',
IncompleteNames: 'People with incomplete names',
NeverMarried: 'People with no marriage records',
MultipleMarriages: 'People with multiple marriage records',
NoBirthdate: 'People without a known birth date',
NoDeathdate: 'People without a known death date',
PersonWithIncompleteEvent: 'People with incomplete events',
FamilyWithIncompleteEvent: 'Families with incomplete events',
PeoplePrivate: 'People marked private',
PeoplePublic: 'People not marked private',
MissingParent: 'People missing parents',
Disconnected: 'Disconnected people',
}
export const filterCounts = {
citations: {
HasGallery: 'Citations with media',
HasNote: 'Citations having notes',
},
events: {
HasGallery: 'Events with media',
HasNote: 'Events having notes',
HasSourceCount: 'Events with sources',
},
families: {
HasGallery: 'Families with media',
HasNote: 'Families having notes',
HasSourceCount: 'Families with sources',
},
media: {HasSourceCount: 'Media with sources'},
notes: {},
people: {
HavePhotos: 'People with media',
HasNote: 'People having notes',
HasSourceCount: 'People with sources',
HasAddress: 'People with addresses',
HasAssociation: 'People with associations',
},
places: {
HasGallery: 'Places with media',
HasNote: 'Places having notes',
HasSourceCount: 'Place with sources',
},
repositories: {},
sources: {
HasGallery: 'Sources with media',
HasNote: 'Sources having notes',
HasRepository: 'Sources with Repository references',
},
}
export const filterMime = {
'image/': 'Image',
'audio/': 'Audio',
'video/': 'Video',
'text/': 'Text',
'application/pdf': 'PDF',
}
export const reportCategoryIcon = {
0: 'description', // text
1: 'pie_chart', // draw
// 2: '', // code
// 3: '', // web
// 4: '', // book
5: 'account_tree', // graphviz
// 6: '', // tree
}
export function arrayEqual(A, B) {
return A.length > 0 && B.length > 0 && A.every(e => B.includes(e))
}
function _getMediaHandle(obj) {
if (obj.object_type === 'media') {
return obj.object.handle
}
if (obj.object?.media_list?.length) {
return obj.object.media_list[0].ref
}
return ''
}
function _getMediaRect(obj) {
if (obj.object?.media_list?.length) {
return obj.object.media_list[0].rect
}
return []
}
export function renderIcon(obj) {
const handle = _getMediaHandle(obj)
const rect = _getMediaRect(obj)
if (handle) {
return html`${objectIcon[obj.object_type]}`
}
return html`${objectIcon[obj.object_type]}`
}