UNPKG

@digitalculture/ochre-sdk

Version:

Node.js library for working with OCHRE (Online Cultural and Historical Research Environment) data

1,299 lines (1,291 loc) 100 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { fetchBibliography: () => fetchBibliography, fetchByUuid: () => fetchByUuid, fetchConcept: () => fetchConcept, fetchGallery: () => fetchGallery, fetchPeriod: () => fetchPeriod, fetchPropertyValue: () => fetchPropertyValue, fetchResource: () => fetchResource, fetchSet: () => fetchSet, fetchSpatialUnit: () => fetchSpatialUnit, fetchTree: () => fetchTree, fetchWebsite: () => fetchWebsite, filterProperties: () => filterProperties, getAllPropertyLabels: () => getAllPropertyLabels, getPropertyByLabel: () => getPropertyByLabel, getPropertyValueByLabel: () => getPropertyValueByLabel, getPropertyValuesByLabel: () => getPropertyValuesByLabel, parseBibliographies: () => parseBibliographies, parseBibliography: () => parseBibliography, parseConcept: () => parseConcept, parseConcepts: () => parseConcepts, parseContext: () => parseContext, parseCoordinates: () => parseCoordinates, parseDocument: () => parseDocument, parseEmail: () => parseEmail, parseEvents: () => parseEvents, parseFakeString: () => parseFakeString, parseIdentification: () => parseIdentification, parseImage: () => parseImage, parseImageMap: () => parseImageMap, parseInterpretations: () => parseInterpretations, parseLanguages: () => parseLanguages, parseLicense: () => parseLicense, parseLink: () => parseLink, parseLinks: () => parseLinks, parseMetadata: () => parseMetadata, parseNotes: () => parseNotes, parseObservation: () => parseObservation, parseObservations: () => parseObservations, parsePeriod: () => parsePeriod, parsePeriods: () => parsePeriods, parsePerson: () => parsePerson, parsePersons: () => parsePersons, parseProperties: () => parseProperties, parsePropertyValue: () => parsePropertyValue, parsePropertyValues: () => parsePropertyValues, parseResource: () => parseResource, parseResources: () => parseResources, parseSet: () => parseSet, parseSpatialUnit: () => parseSpatialUnit, parseSpatialUnits: () => parseSpatialUnits, parseStringContent: () => parseStringContent, parseStringDocumentItem: () => parseStringDocumentItem, parseStringItem: () => parseStringItem, parseTree: () => parseTree, parseWebsite: () => parseWebsite }); module.exports = __toCommonJS(index_exports); // src/utils/parse.ts var import_zod3 = require("zod"); // src/utils/fetchers/generic.ts var import_zod = require("zod"); var uuidSchema = import_zod.z.string().uuid({ message: "Invalid UUID provided" }); async function fetchByUuid(uuid) { try { const result = uuidSchema.safeParse(uuid); if (!result.success) { throw new Error(result.error.issues[0]?.message); } const response = await fetch( `https://ochre.lib.uchicago.edu/ochre?uuid=${uuid}&format=json&lang="*"` ); if (!response.ok) { throw new Error("Failed to fetch OCHRE data"); } const dataRaw = await response.json(); if (!("ochre" in dataRaw)) { throw new Error("Invalid OCHRE data: API response missing 'ochre' key"); } return [null, dataRaw]; } catch (error) { return [error instanceof Error ? error.message : "Unknown error", null]; } } // src/utils/string.ts var import_zod2 = require("zod"); var renderOptionsSchema = import_zod2.z.string().transform((str) => str.split(" ")).pipe( import_zod2.z.array( import_zod2.z.enum([ "bold", "italic", "underline" ]) ) ); var whitespaceSchema = import_zod2.z.string().transform((str) => str.split(" ")).pipe( import_zod2.z.array( import_zod2.z.enum([ "newline", "trailing", "leading" ]) ) ); var emailSchema = import_zod2.z.string().email({ message: "Invalid email" }); function getStringItemByLanguage(content, language) { const stringItemToFind = content.find((item) => item.lang === language); return stringItemToFind ?? null; } function parseEmail(string) { const splitString = string.split(" "); const returnSplitString = []; for (const string2 of splitString) { const cleanString = string2.replaceAll(/(?<=\s|^)[([{]+|[)\]}]+(?=\s|$)/g, "").replace(/[!),.:;?\]]$/, ""); const index = string2.indexOf(cleanString); const before = string2.slice(0, index); const after = string2.slice(index + cleanString.length); const isEmail = emailSchema.safeParse(cleanString).success; if (isEmail) { returnSplitString.push( before, `${before}<ExternalLink href="mailto:${cleanString}">${cleanString}</ExternalLink>${after}` ); continue; } returnSplitString.push(string2); } return returnSplitString.join(" "); } function parseRenderOptions(contentString, renderString) { let returnString = contentString; const result = renderOptionsSchema.safeParse(renderString); if (!result.success) { console.warn(`Invalid render options string provided: \u201C${renderString}\u201D`); return contentString; } for (const option of result.data) { switch (option) { case "bold": { returnString = `**${returnString}**`; break; } case "italic": { returnString = `*${returnString}*`; break; } case "underline": { returnString = `_${returnString}_`; break; } } } return returnString.replaceAll("&#39;", "'"); } function parseWhitespace(contentString, whitespace) { let returnString = contentString; const result = whitespaceSchema.safeParse(whitespace); if (!result.success) { console.warn(`Invalid whitespace string provided: \u201C${whitespace}\u201D`); return contentString; } for (const option of result.data) { switch (option) { case "newline": { returnString = `<br /> ${returnString}`; break; } case "trailing": { returnString = `${returnString} `; break; } case "leading": { returnString = ` ${returnString}`; break; } } } return returnString.replaceAll("&#39;", "'"); } function parseFakeString(string) { let returnString = ""; if (typeof string === "string") { returnString = string; } else if (typeof string === "number") { returnString = string.toString(); } else if (typeof string === "boolean") { returnString = string ? "Yes" : "No"; } return returnString.replaceAll("&#39;", "'").replaceAll(/^(\d+)\./gm, String.raw`$1\.`); } function parseStringItem(item) { let returnString = ""; switch (typeof item.string) { case "string": { returnString = item.string; break; } case "number": case "boolean": { returnString = parseFakeString(item.string); break; } case "object": { const stringItems = Array.isArray(item.string) ? item.string : [item.string]; for (const stringItem of stringItems) { if (typeof stringItem === "string" || typeof stringItem === "number" || typeof stringItem === "boolean") { returnString += parseFakeString(stringItem); } else { const renderedText = stringItem.rend != null ? parseRenderOptions( parseFakeString(stringItem.content), stringItem.rend ) : parseFakeString(stringItem.content); const whitespacedText = stringItem.whitespace != null ? parseWhitespace(renderedText, stringItem.whitespace) : renderedText; returnString += whitespacedText; } } break; } default: { returnString = ""; break; } } return returnString.replaceAll("&#39;", "'").replaceAll(/^(\d+)\./gm, String.raw`$1\.`); } function parseStringDocumentItem(item, footnotes) { if (typeof item === "string" || typeof item === "number" || typeof item === "boolean") { return parseEmail(parseFakeString(item)); } if ("whitespace" in item && !("content" in item) && !("string" in item)) { if (item.whitespace === "newline") { return " \n"; } else { return ""; } } if ("links" in item) { let itemString = ""; if (typeof item.string === "object") { itemString = parseStringContent(item.string); } else { itemString = parseFakeString(item.string).replaceAll("<", String.raw`\<`).replaceAll("{", String.raw`\{`); } const itemLinks = Array.isArray(item.links) ? item.links : [item.links]; for (const link of itemLinks) { if ("resource" in link) { const linkResource = Array.isArray(link.resource) ? link.resource[0] : link.resource; let linkContent = null; if (linkResource.content != null) { linkContent = parseFakeString(linkResource.content).replaceAll("<", String.raw`\<`).replaceAll("{", String.raw`\{`); } switch (linkResource.type) { case "image": { if (linkResource.rend === "inline") { return `<InlineImage uuid="${linkResource.uuid}" ${linkContent !== null ? `content="${linkContent}"` : ""} height={${linkResource.height?.toString() ?? "null"}} width={${linkResource.width?.toString() ?? "null"}} />`; } else if (linkResource.publicationDateTime != null) { return `<ExternalLink href="https:\\/\\/ochre.lib.uchicago.edu/ochre?uuid=${linkResource.uuid}" type="image"${linkContent !== null ? ` content="${linkContent}"` : ""}>${itemString}</ExternalLink>`; } else { return `<TooltipSpan type="image" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</TooltipSpan>`; } } case "internalDocument": { const isFootnote = linkContent?.toLocaleLowerCase("en-US").includes("footnote"); if (isFootnote) { if (footnotes) { footnotes.push({ uuid: linkResource.uuid, label: itemString, content: "" }); } return ` <Footnote uuid="${linkResource.uuid}"${itemString ? ` label="${itemString}"` : ""}${linkContent !== null ? ` content="${linkContent}"` : ""} />`; } else { return `<ExternalLink href="https:\\/\\/ochre.lib.uchicago.edu/ochre?uuid=${linkResource.uuid}" type="internalDocument" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</ExternalLink>`; } } case "externalDocument": { if (linkResource.publicationDateTime != null) { return `<ExternalLink href="https:\\/\\/ochre.lib.uchicago.edu/ochre?uuid=${linkResource.uuid}" type="externalDocument" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</ExternalLink>`; } else { return `<TooltipSpan type="externalDocument" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</TooltipSpan>`; } } case "webpage": { return `<ExternalLink href="${linkResource.href}" type="webpage" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</ExternalLink>`; } default: { return ""; } } } else if ("concept" in link) { const linkConcept = Array.isArray(link.concept) ? link.concept[0] : link.concept; if (linkConcept.publicationDateTime != null) { return `<ExternalLink href="https:\\/\\/ochre.lib.uchicago.edu/ochre?uuid=${linkConcept.uuid}" type="concept">${itemString}</ExternalLink>`; } else { return `<TooltipSpan type="concept">${itemString}</TooltipSpan>`; } } else if ("set" in link) { const linkSet = Array.isArray(link.set) ? link.set[0] : link.set; if (linkSet.publicationDateTime != null) { return `<ExternalLink href="https:\\/\\/ochre.lib.uchicago.edu/ochre?uuid=${linkSet.uuid}" type="set">${itemString}</ExternalLink>`; } else { return `<TooltipSpan type="set">${itemString}</TooltipSpan>`; } } else if ("person" in link) { const linkPerson = Array.isArray(link.person) ? link.person[0] : link.person; const linkContent = linkPerson.identification ? ["string", "number", "boolean"].includes( typeof linkPerson.identification.label ) ? parseFakeString(linkPerson.identification.label) : parseStringContent( linkPerson.identification.label ) : null; if (linkPerson.publicationDateTime != null) { return `<ExternalLink href="https:\\/\\/ochre.lib.uchicago.edu/ochre?uuid=${linkPerson.uuid}" type="${linkPerson.type ?? "person"}" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</ExternalLink>`; } else { return `<TooltipSpan type="${linkPerson.type ?? "person"}" ${linkContent !== null ? `content="${linkContent}"` : ""}>${itemString}</TooltipSpan>`; } } else if ("bibliography" in link) { const linkBibliography = Array.isArray(link.bibliography) ? link.bibliography[0] : link.bibliography; if (linkBibliography.publicationDateTime != null) { return `<ExternalLink href="https:\\/\\/ochre.lib.uchicago.edu/ochre?uuid=${linkBibliography.uuid}" type="${linkBibliography.type ?? "bibliography"}">${itemString}</ExternalLink>`; } else { return `<TooltipSpan type="bibliography">${itemString}</TooltipSpan>`; } } } } let returnString = ""; if ("string" in item) { const stringItems = Array.isArray(item.string) ? item.string : [item.string]; for (const stringItem of stringItems) { returnString += parseStringDocumentItem(stringItem, footnotes); } if ("whitespace" in item && item.whitespace != null) { returnString = parseWhitespace(parseEmail(returnString), item.whitespace); } return returnString.replaceAll("&#39;", "'").replaceAll(/^(\d+)\./gm, String.raw`$1\.`); } else { returnString = parseFakeString(item.content); if (item.rend != null) { returnString = parseRenderOptions(parseEmail(returnString), item.rend); } if (item.whitespace != null) { returnString = parseWhitespace(parseEmail(returnString), item.whitespace); } } return returnString.replaceAll(/^(\d+)\./gm, String.raw`$1\.`); } function parseStringContent(content, language = "eng") { switch (typeof content.content) { case "string": case "number": case "boolean": { return parseFakeString(content.content); } case "object": { if (Array.isArray(content.content)) { const stringItem = getStringItemByLanguage(content.content, language); if (stringItem) { return parseStringItem(stringItem); } else { const returnStringItem = content.content[0]; if (!returnStringItem) { throw new Error( `No string item found for language \u201C${language}\u201D in the following content: ${JSON.stringify( content.content )}.` ); } return parseStringItem(returnStringItem); } } else { return parseStringItem(content.content); } } default: { return String(content.content).replaceAll(/^(\d+)\./gm, String.raw`$1\.`); } } } // src/utils/fetchers/resource.ts async function fetchResource(uuid) { try { const [error, dataRaw] = await fetchByUuid(uuid); if (error !== null) { throw new Error(error); } if (!("resource" in dataRaw.ochre)) { throw new Error( "Invalid OCHRE data: API response missing 'resource' key" ); } const resourceItem = parseResource(dataRaw.ochre.resource); const data = { uuid: parseFakeString(dataRaw.ochre.uuid), publicationDateTime: new Date(dataRaw.ochre.publicationDateTime), belongsTo: { uuid: dataRaw.ochre.uuidBelongsTo, abbreviation: parseFakeString(dataRaw.ochre.belongsTo) }, metadata: parseMetadata(dataRaw.ochre.metadata), item: resourceItem }; return { metadata: data.metadata, resource: data.item }; } catch (error) { console.error(error); return null; } } // src/utils/getters.ts var DEFAULT_OPTIONS = { searchNestedProperties: false }; function getPropertyByLabel(properties, label, options = DEFAULT_OPTIONS) { const { searchNestedProperties } = options; const property = properties.find((property2) => property2.label === label); if (property) { return property; } if (searchNestedProperties) { for (const property2 of properties) { if (property2.properties.length > 0) { const nestedResult = getPropertyByLabel(property2.properties, label, { searchNestedProperties }); if (nestedResult) { return nestedResult; } } } } return null; } function getPropertyValuesByLabel(properties, label, options = DEFAULT_OPTIONS) { const { searchNestedProperties } = options; const property = properties.find((property2) => property2.label === label); if (property) { return property.values.map((value) => value.content); } if (searchNestedProperties) { for (const property2 of properties) { if (property2.properties.length > 0) { const nestedResult = getPropertyValuesByLabel( property2.properties, label, { searchNestedProperties } ); if (nestedResult) { return nestedResult; } } } } return null; } function getPropertyValueByLabel(properties, label, options = DEFAULT_OPTIONS) { const { searchNestedProperties } = options; const values = getPropertyValuesByLabel(properties, label, { searchNestedProperties }); if (values !== null && values.length > 0) { return values[0]; } if (searchNestedProperties) { for (const property of properties) { if (property.properties.length > 0) { const nestedResult = getPropertyValueByLabel( property.properties, label, { searchNestedProperties } ); if (nestedResult !== null) { return nestedResult; } } } } return null; } function getAllPropertyLabels(properties, options = DEFAULT_OPTIONS) { const { searchNestedProperties } = options; const labels = /* @__PURE__ */ new Set(); for (const property of properties) { labels.add(property.label); if (property.properties.length > 0 && searchNestedProperties) { const nestedLabels = getAllPropertyLabels(property.properties, { searchNestedProperties: true }); for (const label of nestedLabels) { labels.add(label); } } } return [...labels]; } function filterProperties(property, filter, options = DEFAULT_OPTIONS) { const { searchNestedProperties } = options; const isAllFields = filter.label.toLocaleLowerCase("en-US") === "all fields"; if (isAllFields || property.label.toLocaleLowerCase("en-US") === filter.label.toLocaleLowerCase("en-US")) { let isFound = property.values.some( (value) => value.content.toLocaleLowerCase("en-US").includes(filter.value.toLocaleLowerCase("en-US")) ); if (!isFound && searchNestedProperties) { isFound = property.properties.some( (property2) => filterProperties(property2, filter, { searchNestedProperties: true }) ); } return isFound; } return false; } // src/utils/parse.ts var websiteSchema = import_zod3.z.object({ type: import_zod3.z.enum( [ "traditional", "digital-collection", "plum", "cedar", "elm", "maple", "oak", "palm" ], { message: "Invalid website type" } ), status: import_zod3.z.enum( ["development", "preview", "production"], { message: "Invalid website status" } ), privacy: import_zod3.z.enum( ["public", "password", "private"], { message: "Invalid website privacy" } ) }); var componentSchema = import_zod3.z.enum( [ "annotated-document", "annotated-image", "bibliography", "blog", "button", "collection", "empty-space", "filter-categories", "iframe", "iiif-viewer", "image", "image-gallery", "n-columns", "n-rows", "network-graph", "search-bar", "table", "text", "timeline", "video" ], { message: "Invalid component" } ); function parseIdentification(identification) { try { const returnIdentification = { label: ["string", "number", "boolean"].includes(typeof identification.label) ? parseFakeString(identification.label) : parseStringContent(identification.label), abbreviation: "" }; for (const key of Object.keys(identification).filter( (key2) => key2 !== "label" )) { returnIdentification[key] = parseStringContent( identification[key] ); } return returnIdentification; } catch (error) { console.error(error); return { label: "", abbreviation: "" }; } } function parseLanguages(language) { if (language == null) { return ["eng"]; } if (Array.isArray(language)) { return language.map((lang) => parseStringContent(lang)); } else { return [parseStringContent(language)]; } } function parseMetadata(metadata) { let identification = { label: "", abbreviation: "" }; if (metadata.item) { if (metadata.item.label || metadata.item.abbreviation) { let label = ""; let abbreviation = ""; if (metadata.item.label) { label = parseStringContent(metadata.item.label); } if (metadata.item.abbreviation) { abbreviation = parseStringContent(metadata.item.abbreviation); } identification = { label, abbreviation }; } else { identification = parseIdentification(metadata.item.identification); } } let projectIdentification = null; const baseProjectIdentification = metadata.project?.identification ? parseIdentification(metadata.project.identification) : null; if (baseProjectIdentification) { projectIdentification = { ...baseProjectIdentification, website: metadata.project?.identification.website ?? null }; } return { project: projectIdentification ? { identification: projectIdentification } : null, item: metadata.item ? { identification, category: metadata.item.category, type: metadata.item.type, maxLength: metadata.item.maxLength ?? null } : null, dataset: parseStringContent(metadata.dataset), publisher: parseStringContent(metadata.publisher), languages: parseLanguages(metadata.language), identifier: parseStringContent(metadata.identifier), description: parseStringContent(metadata.description) }; } function parseContextItem(contextItem) { return { uuid: contextItem.uuid, publicationDateTime: contextItem.publicationDateTime != null ? new Date(contextItem.publicationDateTime) : null, number: contextItem.n, content: parseFakeString(contextItem.content) }; } function parseContext(context) { const contexts = Array.isArray(context.context) ? context.context : [context.context]; const returnContexts = { nodes: contexts.map((context2) => { const spatialUnit = []; if ("spatialUnit" in context2 && context2.spatialUnit) { const contextsToParse = Array.isArray(context2.spatialUnit) ? context2.spatialUnit : [context2.spatialUnit]; for (const contextItem of contextsToParse) { spatialUnit.push(parseContextItem(contextItem)); } } return { tree: parseContextItem(context2.tree), project: parseContextItem(context2.project), spatialUnit }; }), displayPath: context.displayPath }; return returnContexts; } function parseLicense(license) { if (typeof license.license === "string") { return null; } return { content: license.license.content, url: license.license.target }; } function parsePerson(person) { return { uuid: person.uuid, publicationDateTime: person.publicationDateTime != null ? new Date(person.publicationDateTime) : null, type: person.type ?? null, date: person.date != null ? new Date(person.date) : null, identification: person.identification ? parseIdentification(person.identification) : null, content: person.content != null ? parseFakeString(person.content) : null }; } function parsePersons(persons) { const returnPersons = []; for (const person of persons) { returnPersons.push(parsePerson(person)); } return returnPersons; } function parseLink(linkRaw) { const links = "resource" in linkRaw ? linkRaw.resource : "spatialUnit" in linkRaw ? linkRaw.spatialUnit : "concept" in linkRaw ? linkRaw.concept : "set" in linkRaw ? linkRaw.set : "tree" in linkRaw ? linkRaw.tree : "person" in linkRaw ? linkRaw.person : "bibliography" in linkRaw ? linkRaw.bibliography : "epigraphicUnit" in linkRaw ? linkRaw.epigraphicUnit : "propertyValue" in linkRaw ? linkRaw.propertyValue : null; if (!links) { throw new Error( `Invalid link provided: ${JSON.stringify(linkRaw, null, 2)}` ); } const linksToParse = Array.isArray(links) ? links : [links]; const returnLinks = []; for (const link of linksToParse) { const returnLink = { category: "resource" in linkRaw ? "resource" : "spatialUnit" in linkRaw ? "spatialUnit" : "concept" in linkRaw ? "concept" : "set" in linkRaw ? "set" : "person" in linkRaw ? "person" : "tree" in linkRaw ? "tree" : "bibliography" in linkRaw ? "bibliography" : "epigraphicUnit" in linkRaw ? "epigraphicUnit" : "propertyValue" in linkRaw ? "propertyValue" : null, content: "content" in link ? link.content != null ? parseFakeString(link.content) : null : null, href: "href" in link && link.href != null ? link.href : null, uuid: link.uuid, type: link.type ?? null, identification: link.identification ? parseIdentification(link.identification) : null, image: null, bibliographies: "bibliography" in linkRaw ? parseBibliographies( Array.isArray(linkRaw.bibliography) ? linkRaw.bibliography : [linkRaw.bibliography] ) : null, publicationDateTime: link.publicationDateTime != null ? new Date(link.publicationDateTime) : null }; if ("height" in link && link.height != null && link.width != null && link.heightPreview != null && link.widthPreview != null) { returnLink.image = { isInline: link.rend === "inline", isPrimary: link.isPrimary ?? false, heightPreview: link.heightPreview, widthPreview: link.widthPreview, height: link.height, width: link.width }; } returnLinks.push(returnLink); } return returnLinks; } function parseLinks(links) { const returnLinks = []; for (const link of links) { returnLinks.push(...parseLink(link)); } return returnLinks; } function parseDocument(document, language = "eng") { let returnString = ""; const footnotes = []; const documentWithLanguage = Array.isArray(document) ? document.find((doc) => doc.lang === language) : document; if (typeof documentWithLanguage.string === "string" || typeof documentWithLanguage.string === "number" || typeof documentWithLanguage.string === "boolean") { returnString += parseEmail(parseFakeString(documentWithLanguage.string)); } else { const documentItems = Array.isArray(documentWithLanguage.string) ? documentWithLanguage.string : [documentWithLanguage.string]; for (const item of documentItems) { returnString += parseStringDocumentItem(item, footnotes); } } return { content: returnString, footnotes }; } function parseImage(image) { return { publicationDateTime: image.publicationDateTime != null ? new Date(image.publicationDateTime) : null, identification: image.identification ? parseIdentification(image.identification) : null, url: image.href ?? (image.htmlImgSrcPrefix == null && image.content != null ? parseFakeString(image.content) : null), htmlPrefix: image.htmlImgSrcPrefix ?? null, content: image.htmlImgSrcPrefix != null && image.content != null ? parseFakeString(image.content) : null, widthPreview: image.widthPreview ?? null, heightPreview: image.heightPreview ?? null, width: image.width ?? null, height: image.height ?? null }; } function parseNotes(notes, language = "eng") { const returnNotes = []; for (const note of notes) { if (typeof note === "string") { if (note === "") { continue; } returnNotes.push({ number: -1, title: null, content: note }); continue; } let content = ""; const notesToParse = Array.isArray(note.content) ? note.content : [note.content]; let noteWithLanguage = notesToParse.find((item) => item.lang === language); if (!noteWithLanguage) { noteWithLanguage = notesToParse[0]; if (!noteWithLanguage) { throw new Error( `Note does not have a valid content item: ${JSON.stringify( note, null, 2 )}` ); } } if (typeof noteWithLanguage.string === "string" || typeof noteWithLanguage.string === "number" || typeof noteWithLanguage.string === "boolean") { content = parseEmail(parseFakeString(noteWithLanguage.string)); } else { content = parseEmail(parseDocument(noteWithLanguage).content); } returnNotes.push({ number: note.noteNo, title: noteWithLanguage.title != null ? parseFakeString(noteWithLanguage.title) : null, content }); } return returnNotes; } function parseCoordinates(coordinates) { return { latitude: coordinates.latitude, longitude: coordinates.longitude, type: coordinates.coord?.coordType ?? null, label: coordinates.coord?.coordLabel != null ? parseFakeString(coordinates.coord.coordLabel) : null }; } function parseObservation(observation) { return { number: observation.observationNo, date: observation.date != null ? new Date(observation.date) : null, observers: observation.observers != null ? parseFakeString(observation.observers).split(";").map((observer) => observer.trim()) : [], notes: observation.notes ? parseNotes( Array.isArray(observation.notes.note) ? observation.notes.note : [observation.notes.note] ) : [], links: observation.links ? parseLinks( Array.isArray(observation.links) ? observation.links : [observation.links] ) : [], properties: observation.properties ? parseProperties( Array.isArray(observation.properties.property) ? observation.properties.property : [observation.properties.property] ) : [] }; } function parseObservations(observations) { const returnObservations = []; for (const observation of observations) { returnObservations.push(parseObservation(observation)); } return returnObservations; } function parseEvents(events) { const returnEvents = []; for (const event of events) { returnEvents.push({ date: event.dateTime != null ? new Date(event.dateTime) : null, label: parseStringContent(event.label), agent: event.agent ? { uuid: event.agent.uuid, content: parseFakeString(event.agent.content) } : null }); } return returnEvents; } function parseProperties(properties, language = "eng") { const returnProperties = []; for (const property of properties) { const valuesToParse = "value" in property && property.value ? Array.isArray(property.value) ? property.value : [property.value] : []; const values = valuesToParse.map( (value) => !["string", "number", "boolean"].includes(typeof value) && typeof value === "object" ? { content: value.slug ? parseFakeString(value.slug) : parseStringContent(value), type: value.type, category: value.category !== "value" ? value.category ?? null : null, uuid: value.uuid ?? null, publicationDateTime: value.publicationDateTime != null ? new Date(value.publicationDateTime) : null } : { content: parseFakeString(value), type: "string", category: "value", uuid: null, publicationDateTime: null } ); returnProperties.push({ label: parseStringContent(property.label, language).replace(/\s*\.{3}$/, "").trim(), values, comment: property.comment != null ? parseFakeString(property.comment) : null, properties: property.property ? parseProperties( Array.isArray(property.property) ? property.property : [property.property] ) : [] }); } return returnProperties; } function parseInterpretations(interpretations) { const returnInterpretations = []; for (const interpretation of interpretations) { returnInterpretations.push({ date: new Date(interpretation.date), number: interpretation.interpretationNo, properties: interpretation.properties ? parseProperties( Array.isArray(interpretation.properties.property) ? interpretation.properties.property : [interpretation.properties.property] ) : [] }); } return returnInterpretations; } function parseImageMap(imageMap) { const returnImageMap = { area: [], width: imageMap.width, height: imageMap.height }; const imageMapAreasToParse = Array.isArray(imageMap.area) ? imageMap.area : [imageMap.area]; for (const area of imageMapAreasToParse) { returnImageMap.area.push({ uuid: area.uuid, publicationDateTime: area.publicationDateTime != null ? new Date(area.publicationDateTime) : null, type: area.type, title: parseFakeString(area.title), shape: area.shape === "rect" ? "rectangle" : "polygon", coords: area.coords.split(",").map((coord) => Number.parseInt(coord)) }); } return returnImageMap; } function parsePeriod(period) { return { uuid: period.uuid, category: "period", publicationDateTime: period.publicationDateTime != null ? new Date(period.publicationDateTime) : null, type: period.type ?? null, number: period.n ?? null, identification: parseIdentification(period.identification), description: period.description ? parseStringContent(period.description) : null }; } function parsePeriods(periods) { const returnPeriods = []; for (const period of periods) { returnPeriods.push(parsePeriod(period)); } return returnPeriods; } function parseBibliography(bibliography) { let resource = null; if (bibliography.source?.resource) { resource = { uuid: bibliography.source.resource.uuid, publicationDateTime: bibliography.source.resource.publicationDateTime ? new Date(bibliography.source.resource.publicationDateTime) : null, type: bibliography.source.resource.type, identification: parseIdentification( bibliography.source.resource.identification ) }; } return { uuid: bibliography.uuid, category: "bibliography", publicationDateTime: bibliography.publicationDateTime != null ? new Date(bibliography.publicationDateTime) : null, type: bibliography.type ?? null, number: bibliography.n ?? null, identification: bibliography.identification ? parseIdentification(bibliography.identification) : null, projectIdentification: bibliography.project?.identification ? parseIdentification(bibliography.project.identification) : null, context: bibliography.context ? parseContext(bibliography.context) : null, citation: { format: bibliography.citationFormat ?? null, short: bibliography.citationFormatSpan ? parseFakeString( "default:span" in bibliography.citationFormatSpan ? bibliography.citationFormatSpan["default:span"].content : bibliography.citationFormatSpan.span.content ) : null, long: bibliography.referenceFormatDiv ? parseFakeString( "default:div" in bibliography.referenceFormatDiv ? bibliography.referenceFormatDiv["default:div"]["default:div"].content : bibliography.referenceFormatDiv.div.div.content ) : null }, publicationInfo: { publishers: bibliography.publicationInfo?.publishers ? parsePersons( Array.isArray( bibliography.publicationInfo.publishers.publishers.person ) ? bibliography.publicationInfo.publishers.publishers.person : [bibliography.publicationInfo.publishers.publishers.person] ) : [], startDate: bibliography.publicationInfo?.startDate ? new Date( bibliography.publicationInfo.startDate.year, bibliography.publicationInfo.startDate.month, bibliography.publicationInfo.startDate.day ) : null }, entryInfo: bibliography.entryInfo ? { startIssue: parseFakeString(bibliography.entryInfo.startIssue), startVolume: parseFakeString(bibliography.entryInfo.startVolume) } : null, source: { resource, documentUrl: bibliography.sourceDocument ? `https://ochre.lib.uchicago.edu/ochre?uuid=${bibliography.sourceDocument.uuid}&load` : null }, periods: bibliography.periods ? parsePeriods( Array.isArray(bibliography.periods.period) ? bibliography.periods.period : [bibliography.periods.period] ) : [], authors: bibliography.authors ? parsePersons( Array.isArray(bibliography.authors.person) ? bibliography.authors.person : [bibliography.authors.person] ) : [], properties: bibliography.properties ? parseProperties( Array.isArray(bibliography.properties.property) ? bibliography.properties.property : [bibliography.properties.property] ) : [] }; } function parseBibliographies(bibliographies) { const returnBibliographies = []; for (const bibliography of bibliographies) { returnBibliographies.push(parseBibliography(bibliography)); } return returnBibliographies; } function parsePropertyValue(propertyValue) { return { uuid: propertyValue.uuid, category: "propertyValue", n: propertyValue.n, publicationDateTime: propertyValue.publicationDateTime ? new Date(propertyValue.publicationDateTime) : null, context: propertyValue.context ? parseContext(propertyValue.context) : null, availability: propertyValue.availability ? parseLicense(propertyValue.availability) : null, identification: parseIdentification(propertyValue.identification), date: propertyValue.date ? new Date(propertyValue.date) : null, creators: propertyValue.creators ? parsePersons( Array.isArray(propertyValue.creators.creator) ? propertyValue.creators.creator : [propertyValue.creators.creator] ) : [], description: propertyValue.description ? ["string", "number", "boolean"].includes( typeof propertyValue.description ) ? parseFakeString(propertyValue.description) : parseStringContent(propertyValue.description) : "", notes: propertyValue.notes ? parseNotes( Array.isArray(propertyValue.notes.note) ? propertyValue.notes.note : [propertyValue.notes.note] ) : [], links: propertyValue.links ? parseLinks( Array.isArray(propertyValue.links) ? propertyValue.links : [propertyValue.links] ) : [] }; } function parsePropertyValues(propertyValues) { const returnPropertyValues = []; for (const propertyValue of propertyValues) { returnPropertyValues.push(parsePropertyValue(propertyValue)); } return returnPropertyValues; } function parseTree(tree) { let creators = []; if (tree.creators) { creators = parsePersons( Array.isArray(tree.creators.creator) ? tree.creators.creator : [tree.creators.creator] ); } let date = null; if (tree.date != null) { date = new Date(tree.date); } let resources = []; let spatialUnits = []; let concepts = []; let periods = []; let bibliographies = []; let persons = []; let propertyValues = []; if (typeof tree.items !== "string" && "resource" in tree.items) { resources = parseResources( Array.isArray(tree.items.resource) ? tree.items.resource : [tree.items.resource] ); } if (typeof tree.items !== "string" && "spatialUnit" in tree.items) { spatialUnits = parseSpatialUnits( Array.isArray(tree.items.spatialUnit) ? tree.items.spatialUnit : [tree.items.spatialUnit] ); } if (typeof tree.items !== "string" && "concept" in tree.items) { concepts = parseConcepts( Array.isArray(tree.items.concept) ? tree.items.concept : [tree.items.concept] ); } if (typeof tree.items !== "string" && "period" in tree.items) { periods = parsePeriods( Array.isArray(tree.items.period) ? tree.items.period : [tree.items.period] ); } if (typeof tree.items !== "string" && "bibliography" in tree.items) { bibliographies = parseBibliographies( Array.isArray(tree.items.bibliography) ? tree.items.bibliography : [tree.items.bibliography] ); } if (typeof tree.items !== "string" && "person" in tree.items) { persons = parsePersons( Array.isArray(tree.items.person) ? tree.items.person : [tree.items.person] ); } if (typeof tree.items !== "string" && "propertyValue" in tree.items) { propertyValues = parsePropertyValues( Array.isArray(tree.items.propertyValue) ? tree.items.propertyValue : [tree.items.propertyValue] ); } const returnTree = { uuid: tree.uuid, category: "tree", publicationDateTime: new Date(tree.publicationDateTime), identification: parseIdentification(tree.identification), creators, license: parseLicense(tree.availability), date, type: tree.type, number: tree.n, items: { resources, spatialUnits, concepts, periods, bibliographies, persons, propertyValues }, properties: tree.properties ? parseProperties( Array.isArray(tree.properties.property) ? tree.properties.property : [tree.properties.property] ) : [] }; return returnTree; } function parseSet(set) { let resources = []; let spatialUnits = []; let concepts = []; let periods = []; let bibliographies = []; let persons = []; let propertyValues = []; if (typeof set.items !== "string" && "resource" in set.items) { resources = parseResources( Array.isArray(set.items.resource) ? set.items.resource : [set.items.resource], true ); } if (typeof set.items !== "string" && "spatialUnit" in set.items) { spatialUnits = parseSpatialUnits( Array.isArray(set.items.spatialUnit) ? set.items.spatialUnit : [set.items.spatialUnit], true ); } if (typeof set.items !== "string" && "concept" in set.items) { concepts = parseConcepts( Array.isArray(set.items.concept) ? set.items.concept : [set.items.concept], true ); } if (typeof set.items !== "string" && "period" in set.items) { periods = parsePeriods( Array.isArray(set.items.period) ? set.items.period : [set.items.period] ); } if (typeof set.items !== "string" && "bibliography" in set.items) { bibliographies = parseBibliographies( Array.isArray(set.items.bibliography) ? set.items.bibliography : [set.items.bibliography] ); } if (typeof set.items !== "string" && "person" in set.items) { persons = parsePersons( Array.isArray(set.items.person) ? set.items.person : [set.items.person] ); } if (typeof set.items !== "string" && "propertyValue" in set.items) { propertyValues = parsePropertyValues( Array.isArray(set.items.propertyValue) ? set.items.propertyValue : [set.items.propertyValue] ); } return { uuid: set.uuid, category: "set", publicationDateTime: set.publicationDateTime ? new Date(set.publicationDateTime) : null, date: set.date != null ? new Date(set.date) : null, license: parseLicense(set.availability), identification: parseIdentification(set.identification), isSuppressingBlanks: set.suppressBlanks ?? false, description: set.description ? ["string", "number", "boolean"].includes(typeof set.description) ? parseFakeString(set.description) : parseStringContent(set.description) : "", creators: set.creators ? parsePersons( Array.isArray(set.creators.creator) ? set.creators.creator : [set.creators.creator] ) : [], type: set.type, number: set.n, items: { resources, spatialUnits, concepts, periods, bibliographies, persons, propertyValues } }; } function parseResource(resource, isNested = false) { const returnResource = { uuid: resource.uuid, category: "resource", publicationDateTime: resource.publicationDateTime ? new Date(resource.publicationDateTime) : null, type: resource.type, number: resource.n, format: resource.format ?? null, context: "context" in resource && resource.context ? parseContext(resource.context) : null, license: "availability" in resource && resource.availability ? parseLicense(resource.availability) : null, copyright: "copyright" in resource && resource.copyright != null ? parseFakeString(resource.copyright) : null, identification: parseIdentification(resource.identification), date: resource.date != null ? new Date(resource.date) : null, image: resource.image ? parseImage(resource.image) : null, creators: resource.creators ? parsePersons( Array.isArray(resource.creators.creator) ? resource.creators.creator : [resource.creators.creator] ) : [], notes: ( // TODO: Remove this check once the { rend: "splitNotes" } issue is fixed resource.notes && "note" in resource.notes ? parseNotes( Array.isArray(resource.notes.note) ? resource.notes.note : [resource.notes.note] ) : [] ), description: resource.description ? ["string", "number", "boolean"].includes(typeof resource.description) ? parseFakeString(resource.description) : parseStringContent(resource.description) : "", document: resource.document ? parseDocument(resource.document.content) : null, href: resource.href ?? null, imageMap: resource.imagemap ? parseImageMap(resource.imagemap) : null, periods: resource.periods ? parsePeriods( Array.isArray(resource.periods.period) ? resource.periods.period : [resource.periods.period] ) : [], links: resource.links ? parseLinks( Array.isArray(resource.links) ? resource.links : [resource.links] ) : [], reverseLinks: resource.reverseLinks ? parseLinks( Array.isArray(resource.reverseLinks) ? resource.reverseLinks : [resource.reverseLinks] ) : [], properties: resource.properties ? parseProperties( Array.isArray(resource.properties.property) ? resource.properties.property : [resource.properties.property] ) : [], citedBibliographies: resource.citedBibliography ? parseBibliographies( Array.isArray(resource.citedBibliography.reference) ? resource.citedBibliography.reference : [resource.citedBibliography.reference] ) : [], resources: resource.resource ? parseResources( Array.isArray(resource.resource) ? resource.resource : [resource.resource], true ) : [] }; if (isNested) { const returnNestedResource = { ...returnResource, publicationDateTime: null, context: null, license: null, copyright: null }; delete returnNestedResource.publicationDateTime; delete returnNestedResource.license; delete returnNestedResource.copyright; return returnNestedResource; } return returnResource; } function parseResources(resources, isNested = false) { const returnResources = []; const resourcesToParse = Array.isArray(resources) ? resources : [resources]; for (const resource of resourcesToParse) { returnResources.push(parseResource(resource, isNested)); } return returnResources; } function parseSpatialUnit(spatialUnit, isNested = false) { const returnSpatialUnit = { uuid: spatialUnit.uuid, category: "spatialUnit", publicationDateTime: spatialUnit.publicationDateTime != null ? new Date(spatialUnit.publicationDateTime) : null, type: spatialUnit.type, number: spatialUnit.n, context: "context" in spatialUnit && spatialUnit.context ? parseContext(spatialUnit.context) : null, license: "availability" in spatialUnit && spatialUnit.availability ? parseLicense(spatialUnit.availability) : null, identification: parseIdentification(spatialUnit.identification), image: spatialUnit.image ? parseImage(spatialUnit.image) : null, description: spatialUnit.description ? ["string", "number", "boolean"].includes( typeof spatialUnit.description ) ? parseFakeString(spatialUnit.description) : parseStringContent(spatialUnit.description) : "", coordinates: spatialUnit.coordinates ? parseCoordinates(spatialUnit.coordinates) : null, observations: "observations" in spatialUnit && spatialUnit.observations ? parseObservations( Array.isArray(spatialUnit.observations.observation) ? spatialUnit.observations.observation : [spatialUnit.observations.observation] ) : spatialUnit.observation ? [parseObservation(spatialUnit.observation)] : [], events: "events" in spatialUnit && spatialUnit.events ? parseEvents( Array.isArray(spatialUnit.events.event) ? spatialUnit.events.event : [spatialUnit.events.event] ) : [] }; if (isNested) { const returnNestedSpatialUnit = {