ochre-sdk 0.19.3 → 0.19.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +7 -19
- package/dist/index.mjs +1888 -1879
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -274,8 +274,8 @@ function parseStringDocumentItem(item) {
|
|
|
274
274
|
for (const link of itemLinks) if ("resource" in link) {
|
|
275
275
|
const linkResource = Array.isArray(link.resource) ? link.resource[0] : link.resource;
|
|
276
276
|
let linkContent = null;
|
|
277
|
-
if (linkResource
|
|
278
|
-
switch (linkResource
|
|
277
|
+
if (linkResource?.content != null) linkContent = parseFakeString(linkResource.content).replaceAll("<", String.raw`\<`).replaceAll("{", String.raw`\{`);
|
|
278
|
+
switch (linkResource?.type) {
|
|
279
279
|
case "IIIF":
|
|
280
280
|
case "image": if (linkResource.rend === "inline") return applyWhitespaceToResult(`<InlineImage uuid="${linkResource.uuid}" ${linkContent !== null ? `content="${linkContent}"` : ""} height={${linkResource.height?.toString() ?? "null"}} width={${linkResource.width?.toString() ?? "null"}} />`, itemWhitespace);
|
|
281
281
|
else if (linkResource.publicationDateTime != null) {
|
|
@@ -328,28 +328,28 @@ function parseStringDocumentItem(item) {
|
|
|
328
328
|
}
|
|
329
329
|
} else if ("spatialUnit" in link) {
|
|
330
330
|
const linkSpatialUnit = Array.isArray(link.spatialUnit) ? link.spatialUnit[0] : link.spatialUnit;
|
|
331
|
-
if (linkSpatialUnit
|
|
331
|
+
if (linkSpatialUnit?.publicationDateTime != null) return applyWhitespaceToResult(`<InternalLink uuid="${linkSpatialUnit.uuid}">${itemString}</InternalLink>`, itemWhitespace);
|
|
332
332
|
else return applyWhitespaceToResult(`<TooltipSpan>${itemString}</TooltipSpan>`, itemWhitespace);
|
|
333
333
|
} else if ("concept" in link) {
|
|
334
334
|
const linkConcept = Array.isArray(link.concept) ? link.concept[0] : link.concept;
|
|
335
|
-
if (linkConcept
|
|
335
|
+
if (linkConcept?.publicationDateTime != null) return applyWhitespaceToResult(`<InternalLink uuid="${linkConcept.uuid}">${itemString}</InternalLink>`, itemWhitespace);
|
|
336
336
|
else return applyWhitespaceToResult(`<TooltipSpan>${itemString}</TooltipSpan>`, itemWhitespace);
|
|
337
337
|
} else if ("set" in link) {
|
|
338
338
|
const linkSet = Array.isArray(link.set) ? link.set[0] : link.set;
|
|
339
|
-
if (linkSet
|
|
339
|
+
if (linkSet?.publicationDateTime != null) return applyWhitespaceToResult(`<InternalLink uuid="${linkSet.uuid}">${itemString}</InternalLink>`, itemWhitespace);
|
|
340
340
|
else return applyWhitespaceToResult(`<TooltipSpan>${itemString}</TooltipSpan>`, itemWhitespace);
|
|
341
341
|
} else if ("person" in link) {
|
|
342
342
|
const linkPerson = Array.isArray(link.person) ? link.person[0] : link.person;
|
|
343
|
-
const linkContent = linkPerson
|
|
343
|
+
const linkContent = linkPerson?.identification != null ? [
|
|
344
344
|
"string",
|
|
345
345
|
"number",
|
|
346
346
|
"boolean"
|
|
347
347
|
].includes(typeof linkPerson.identification.label) ? parseFakeString(linkPerson.identification.label) : parseStringContent(linkPerson.identification.label) : null;
|
|
348
|
-
if (linkPerson
|
|
348
|
+
if (linkPerson?.publicationDateTime != null) return applyWhitespaceToResult(`<InternalLink uuid="${linkPerson.uuid}">${itemString}</InternalLink>`, itemWhitespace);
|
|
349
349
|
else return applyWhitespaceToResult(`<TooltipSpan${linkContent !== null ? ` content="${linkContent}"` : ""}>${itemString}</TooltipSpan>`, itemWhitespace);
|
|
350
350
|
} else if ("bibliography" in link) {
|
|
351
351
|
const linkBibliography = Array.isArray(link.bibliography) ? link.bibliography[0] : link.bibliography;
|
|
352
|
-
if (linkBibliography
|
|
352
|
+
if (linkBibliography?.publicationDateTime != null) return applyWhitespaceToResult(`<InternalLink uuid="${linkBibliography.uuid}">${itemString}</InternalLink>`, itemWhitespace);
|
|
353
353
|
else return applyWhitespaceToResult(`<TooltipSpan>${itemString}</TooltipSpan>`, itemWhitespace);
|
|
354
354
|
}
|
|
355
355
|
}
|
|
@@ -722,755 +722,406 @@ const boundsSchema = z.string().transform((str, ctx) => {
|
|
|
722
722
|
}).pipe(z.tuple([z.tuple([z.number(), z.number()]), z.tuple([z.number(), z.number()])], { message: "Must contain exactly 2 coordinate pairs" }));
|
|
723
723
|
|
|
724
724
|
//#endregion
|
|
725
|
-
//#region src/utils/
|
|
726
|
-
const DEFAULT_OPTIONS = { includeNestedProperties: false };
|
|
725
|
+
//#region src/utils/parse/index.ts
|
|
727
726
|
/**
|
|
728
|
-
*
|
|
727
|
+
* Parses raw identification data into the standardized Identification type
|
|
729
728
|
*
|
|
730
|
-
* @param
|
|
731
|
-
* @
|
|
732
|
-
* @param options - Search options, including whether to include nested properties
|
|
733
|
-
* @returns The matching Property object, or null if not found
|
|
729
|
+
* @param identification - Raw identification data from OCHRE format
|
|
730
|
+
* @returns Parsed Identification object with label and abbreviation
|
|
734
731
|
*/
|
|
735
|
-
function
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
732
|
+
function parseIdentification(identification) {
|
|
733
|
+
try {
|
|
734
|
+
const result = {
|
|
735
|
+
label: parseFakeStringOrContent(identification.label),
|
|
736
|
+
abbreviation: "",
|
|
737
|
+
code: identification.code ?? null
|
|
738
|
+
};
|
|
739
|
+
for (const key of Object.keys(identification)) {
|
|
740
|
+
if (key === "label" || key === "code") continue;
|
|
741
|
+
const raw = identification[key];
|
|
742
|
+
result[key] = parseFakeStringOrContent(raw);
|
|
743
743
|
}
|
|
744
|
+
return result;
|
|
745
|
+
} catch (error) {
|
|
746
|
+
console.error(error);
|
|
747
|
+
return {
|
|
748
|
+
label: "",
|
|
749
|
+
abbreviation: "",
|
|
750
|
+
code: null
|
|
751
|
+
};
|
|
744
752
|
}
|
|
745
|
-
return null;
|
|
746
753
|
}
|
|
747
754
|
/**
|
|
748
|
-
*
|
|
755
|
+
* Parses raw language data into an array of language codes
|
|
749
756
|
*
|
|
750
|
-
* @param
|
|
751
|
-
* @
|
|
752
|
-
* @param options - Search options, including whether to include nested properties
|
|
753
|
-
* @returns Array of property values as strings, or null if property not found
|
|
757
|
+
* @param language - Raw language data, either single or array
|
|
758
|
+
* @returns Array of language codes as strings
|
|
754
759
|
*/
|
|
755
|
-
function
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
if (
|
|
759
|
-
|
|
760
|
-
for (const property of properties) if (property.properties.length > 0) {
|
|
761
|
-
const nestedResult = getPropertyValuesByUuid(property.properties, uuid, { includeNestedProperties });
|
|
762
|
-
if (nestedResult) return nestedResult;
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
return null;
|
|
760
|
+
function parseLanguages(language) {
|
|
761
|
+
if (language == null) return ["eng"];
|
|
762
|
+
if (typeof language === "string") return [language];
|
|
763
|
+
if (Array.isArray(language)) return language.map((lang) => typeof lang === "object" ? parseStringContent(lang) : lang);
|
|
764
|
+
else return [parseStringContent(language)];
|
|
766
765
|
}
|
|
767
766
|
/**
|
|
768
|
-
*
|
|
767
|
+
* Parses raw metadata into the standardized Metadata type
|
|
769
768
|
*
|
|
770
|
-
* @param
|
|
771
|
-
* @
|
|
772
|
-
* @param options - Search options, including whether to include nested properties
|
|
773
|
-
* @returns The first property value as string, or null if property not found
|
|
769
|
+
* @param metadata - Raw metadata from OCHRE format
|
|
770
|
+
* @returns Parsed Metadata object
|
|
774
771
|
*/
|
|
775
|
-
function
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
772
|
+
function parseMetadata(metadata) {
|
|
773
|
+
let identification = {
|
|
774
|
+
label: "",
|
|
775
|
+
abbreviation: "",
|
|
776
|
+
code: null
|
|
777
|
+
};
|
|
778
|
+
if (metadata.item) if (metadata.item.label || metadata.item.abbreviation) identification = {
|
|
779
|
+
label: metadata.item.label ? parseStringContent(metadata.item.label) : "",
|
|
780
|
+
abbreviation: metadata.item.abbreviation ? parseStringContent(metadata.item.abbreviation) : "",
|
|
781
|
+
code: metadata.item.identification.code ?? null
|
|
782
|
+
};
|
|
783
|
+
else identification = parseIdentification(metadata.item.identification);
|
|
784
|
+
const projectId = metadata.project?.identification ? parseIdentification(metadata.project.identification) : null;
|
|
785
|
+
return {
|
|
786
|
+
project: projectId ? {
|
|
787
|
+
identification: {
|
|
788
|
+
...projectId,
|
|
789
|
+
website: metadata.project?.identification.website ?? null
|
|
790
|
+
},
|
|
791
|
+
dateFormat: metadata.project?.dateFormat ?? null,
|
|
792
|
+
page: metadata.project?.page ?? null
|
|
793
|
+
} : null,
|
|
794
|
+
collection: metadata.collection ? {
|
|
795
|
+
identification: parseIdentification(metadata.collection.identification),
|
|
796
|
+
page: metadata.collection.page
|
|
797
|
+
} : null,
|
|
798
|
+
publication: metadata.publication ? {
|
|
799
|
+
identification: parseIdentification(metadata.publication.identification),
|
|
800
|
+
page: metadata.publication.page
|
|
801
|
+
} : null,
|
|
802
|
+
item: metadata.item ? {
|
|
803
|
+
identification,
|
|
804
|
+
category: metadata.item.category,
|
|
805
|
+
type: metadata.item.type,
|
|
806
|
+
maxLength: metadata.item.maxLength ?? null
|
|
807
|
+
} : null,
|
|
808
|
+
dataset: parseFakeStringOrContent(metadata.dataset),
|
|
809
|
+
publisher: parseFakeStringOrContent(metadata.publisher),
|
|
810
|
+
languages: parseLanguages(metadata.language),
|
|
811
|
+
identifier: parseFakeStringOrContent(metadata.identifier),
|
|
812
|
+
description: parseFakeStringOrContent(metadata.description)
|
|
813
|
+
};
|
|
786
814
|
}
|
|
787
815
|
/**
|
|
788
|
-
*
|
|
816
|
+
* Parses raw context item data into the standardized ContextItem type
|
|
789
817
|
*
|
|
790
|
-
* @param
|
|
791
|
-
* @
|
|
792
|
-
* @param options - Search options, including whether to include nested properties
|
|
793
|
-
* @returns The matching Property object, or null if not found
|
|
818
|
+
* @param contextItem - Raw context item data from OCHRE format
|
|
819
|
+
* @returns Parsed ContextItem object
|
|
794
820
|
*/
|
|
795
|
-
function
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
if (nestedResult) return nestedResult;
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
return null;
|
|
821
|
+
function parseContextItem(contextItem) {
|
|
822
|
+
return {
|
|
823
|
+
uuid: contextItem.uuid,
|
|
824
|
+
publicationDateTime: parseOptionalDate(contextItem.publicationDateTime),
|
|
825
|
+
number: contextItem.n,
|
|
826
|
+
content: parseFakeString(contextItem.content)
|
|
827
|
+
};
|
|
806
828
|
}
|
|
807
829
|
/**
|
|
808
|
-
*
|
|
830
|
+
* Parses raw context data into the standardized Context type
|
|
809
831
|
*
|
|
810
|
-
* @param
|
|
811
|
-
* @
|
|
812
|
-
* @param values - The values to search for
|
|
813
|
-
* @param options - Search options, including whether to include nested properties
|
|
814
|
-
* @returns The matching Property object, or null if not found or all values do not match
|
|
832
|
+
* @param context - Raw context data from OCHRE format
|
|
833
|
+
* @returns Parsed Context object
|
|
815
834
|
*/
|
|
816
|
-
function
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
}
|
|
826
|
-
return null;
|
|
835
|
+
function parseContext(context) {
|
|
836
|
+
return {
|
|
837
|
+
nodes: ensureArray(context.context).map((ctx) => ({
|
|
838
|
+
tree: parseContextItem(ctx.tree),
|
|
839
|
+
project: parseContextItem(ctx.project),
|
|
840
|
+
spatialUnit: "spatialUnit" in ctx && ctx.spatialUnit ? ensureArray(ctx.spatialUnit).map((element) => parseContextItem(element)) : []
|
|
841
|
+
})),
|
|
842
|
+
displayPath: context.displayPath
|
|
843
|
+
};
|
|
827
844
|
}
|
|
828
845
|
/**
|
|
829
|
-
*
|
|
846
|
+
* Parses raw license data into the standardized License type
|
|
830
847
|
*
|
|
831
|
-
* @param
|
|
832
|
-
* @
|
|
833
|
-
* @param value - The value to search for
|
|
834
|
-
* @param options - Search options, including whether to include nested properties
|
|
835
|
-
* @returns The matching Property object, or null if not found or value does not match
|
|
848
|
+
* @param license - Raw license data from OCHRE format
|
|
849
|
+
* @returns Parsed License object or null if invalid
|
|
836
850
|
*/
|
|
837
|
-
function
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
const nestedResult = getPropertyByLabelAndValue(property.properties, label, value, { includeNestedProperties });
|
|
844
|
-
if (nestedResult) return nestedResult;
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
return null;
|
|
851
|
+
function parseLicense(license) {
|
|
852
|
+
if (typeof license.license === "string") return null;
|
|
853
|
+
return {
|
|
854
|
+
content: license.license.content,
|
|
855
|
+
url: license.license.target
|
|
856
|
+
};
|
|
848
857
|
}
|
|
849
858
|
/**
|
|
850
|
-
*
|
|
859
|
+
* Parses raw person data into the standardized Person type
|
|
851
860
|
*
|
|
852
|
-
* @param
|
|
853
|
-
* @
|
|
854
|
-
* @param options - Search options, including whether to include nested properties
|
|
855
|
-
* @returns Array of property values as strings, or null if property not found
|
|
861
|
+
* @param person - Raw person data from OCHRE format
|
|
862
|
+
* @returns Parsed Person object
|
|
856
863
|
*/
|
|
857
|
-
function
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
864
|
+
function parsePerson(person, metadata, persistentUrl, belongsTo) {
|
|
865
|
+
return {
|
|
866
|
+
uuid: person.uuid,
|
|
867
|
+
category: "person",
|
|
868
|
+
belongsTo: belongsTo ?? null,
|
|
869
|
+
metadata: metadata ?? null,
|
|
870
|
+
publicationDateTime: parseOptionalDate(person.publicationDateTime),
|
|
871
|
+
persistentUrl: persistentUrl ?? null,
|
|
872
|
+
type: person.type ?? null,
|
|
873
|
+
number: person.n ?? null,
|
|
874
|
+
context: person.context ? parseContext(person.context) : null,
|
|
875
|
+
date: person.date ?? null,
|
|
876
|
+
identification: person.identification ? parseIdentification(person.identification) : null,
|
|
877
|
+
availability: person.availability ? parseLicense(person.availability) : null,
|
|
878
|
+
image: person.image ? parseImage(person.image) : null,
|
|
879
|
+
address: person.address ? {
|
|
880
|
+
country: person.address.country ?? null,
|
|
881
|
+
city: person.address.city ?? null,
|
|
882
|
+
state: person.address.state ?? null
|
|
883
|
+
} : null,
|
|
884
|
+
description: person.description ? parseFakeStringOrContent(person.description) : null,
|
|
885
|
+
coordinates: parseCoordinates(person.coordinates),
|
|
886
|
+
content: person.content != null ? parseFakeString(person.content) : null,
|
|
887
|
+
notes: person.notes ? parseNotes(ensureArray(person.notes.note)) : [],
|
|
888
|
+
links: person.links ? parseLinks(ensureArray(person.links)) : [],
|
|
889
|
+
events: person.events ? parseEvents(ensureArray(person.events.event)) : [],
|
|
890
|
+
properties: person.properties ? parseProperties(ensureArray(person.properties.property)) : [],
|
|
891
|
+
bibliographies: person.bibliographies ? parseBibliographies(ensureArray(person.bibliographies.bibliography)) : []
|
|
892
|
+
};
|
|
868
893
|
}
|
|
869
894
|
/**
|
|
870
|
-
*
|
|
895
|
+
* Parses raw person data into the standardized Person type
|
|
871
896
|
*
|
|
872
|
-
* @param
|
|
873
|
-
* @
|
|
874
|
-
* @param options - Search options, including whether to include nested properties
|
|
875
|
-
* @returns The first property value as string, or null if property not found
|
|
897
|
+
* @param persons - Array of raw person data from OCHRE format
|
|
898
|
+
* @returns Array of parsed Person objects
|
|
876
899
|
*/
|
|
877
|
-
function
|
|
878
|
-
|
|
879
|
-
const values = getPropertyValuesByLabel(properties, label, { includeNestedProperties });
|
|
880
|
-
if (values !== null && values.length > 0) return values[0];
|
|
881
|
-
if (includeNestedProperties) {
|
|
882
|
-
for (const property of properties) if (property.properties.length > 0) {
|
|
883
|
-
const nestedResult = getPropertyValueByLabel(property.properties, label, { includeNestedProperties });
|
|
884
|
-
if (nestedResult !== null) return nestedResult;
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
return null;
|
|
900
|
+
function parsePersons(persons) {
|
|
901
|
+
return persons.map((person) => parsePerson(person));
|
|
888
902
|
}
|
|
889
903
|
/**
|
|
890
|
-
*
|
|
904
|
+
* Parses an array of raw links into standardized Link objects
|
|
891
905
|
*
|
|
892
|
-
* @param
|
|
893
|
-
* @
|
|
894
|
-
* @returns Array of unique properties
|
|
906
|
+
* @param linkRaw - Raw OCHRE link
|
|
907
|
+
* @returns Parsed Link object
|
|
895
908
|
*/
|
|
896
|
-
function
|
|
897
|
-
const
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
return
|
|
909
|
+
function parseLink(linkRaw) {
|
|
910
|
+
const categoryKey = [
|
|
911
|
+
"resource",
|
|
912
|
+
"spatialUnit",
|
|
913
|
+
"concept",
|
|
914
|
+
"set",
|
|
915
|
+
"tree",
|
|
916
|
+
"person",
|
|
917
|
+
"bibliography",
|
|
918
|
+
"propertyVariable",
|
|
919
|
+
"propertyValue"
|
|
920
|
+
].find((key) => key in linkRaw);
|
|
921
|
+
if (!categoryKey) throw new Error(`Invalid link provided: ${JSON.stringify(linkRaw, null, 2)}`);
|
|
922
|
+
const links = linkRaw[categoryKey];
|
|
923
|
+
if (links == null) return [];
|
|
924
|
+
return ensureArray(links).map((link) => {
|
|
925
|
+
const returnLink = {
|
|
926
|
+
category: categoryKey,
|
|
927
|
+
content: "content" in link && link.content != null ? parseFakeString(link.content) : null,
|
|
928
|
+
href: "href" in link && link.href != null ? link.href : null,
|
|
929
|
+
fileFormat: "fileFormat" in link && link.fileFormat != null ? link.fileFormat : null,
|
|
930
|
+
fileSize: "fileSize" in link && link.fileSize != null ? link.fileSize : null,
|
|
931
|
+
uuid: link.uuid ?? null,
|
|
932
|
+
type: link.type ?? null,
|
|
933
|
+
identification: link.identification ? parseIdentification(link.identification) : null,
|
|
934
|
+
description: "description" in link && link.description != null ? parseStringContent(link.description) : null,
|
|
935
|
+
image: null,
|
|
936
|
+
bibliographies: "bibliography" in linkRaw && linkRaw.bibliography != null ? parseBibliographies(ensureArray(linkRaw.bibliography)) : null,
|
|
937
|
+
publicationDateTime: parseOptionalDate(link.publicationDateTime)
|
|
938
|
+
};
|
|
939
|
+
if ("height" in link && link.height != null && link.width != null && link.heightPreview != null && link.widthPreview != null) returnLink.image = {
|
|
940
|
+
isInline: link.rend === "inline",
|
|
941
|
+
isPrimary: link.isPrimary ?? false,
|
|
942
|
+
heightPreview: link.heightPreview,
|
|
943
|
+
widthPreview: link.widthPreview,
|
|
944
|
+
height: link.height,
|
|
945
|
+
width: link.width
|
|
946
|
+
};
|
|
947
|
+
return returnLink;
|
|
948
|
+
});
|
|
911
949
|
}
|
|
912
950
|
/**
|
|
913
|
-
*
|
|
951
|
+
* Parses an array of raw links into standardized Link objects
|
|
914
952
|
*
|
|
915
|
-
* @param
|
|
916
|
-
* @
|
|
917
|
-
* @returns Array of unique property labels
|
|
918
|
-
*/
|
|
919
|
-
function getUniquePropertyLabels(properties, options = DEFAULT_OPTIONS) {
|
|
920
|
-
const { includeNestedProperties } = options;
|
|
921
|
-
const uniquePropertyLabels = new Array();
|
|
922
|
-
for (const property of properties) {
|
|
923
|
-
if (uniquePropertyLabels.includes(property.label)) continue;
|
|
924
|
-
uniquePropertyLabels.push(property.label);
|
|
925
|
-
if (property.properties.length > 0 && includeNestedProperties) {
|
|
926
|
-
const nestedProperties = getUniquePropertyLabels(property.properties, { includeNestedProperties: true });
|
|
927
|
-
for (const property of nestedProperties) {
|
|
928
|
-
if (uniquePropertyLabels.includes(property)) continue;
|
|
929
|
-
uniquePropertyLabels.push(property);
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
return uniquePropertyLabels;
|
|
934
|
-
}
|
|
935
|
-
/**
|
|
936
|
-
* Get the leaf property values from an array of property values
|
|
937
|
-
* @param propertyValues - The array of property values to get the leaf property values from
|
|
938
|
-
* @returns The array of leaf property values
|
|
953
|
+
* @param links - Array of raw OCHRE links
|
|
954
|
+
* @returns Array of parsed Link objects
|
|
939
955
|
*/
|
|
940
|
-
function
|
|
941
|
-
|
|
956
|
+
function parseLinks(links) {
|
|
957
|
+
const result = [];
|
|
958
|
+
for (const link of links) result.push(...parseLink(link));
|
|
959
|
+
return result;
|
|
942
960
|
}
|
|
943
961
|
/**
|
|
944
|
-
*
|
|
962
|
+
* Parses raw document content into a standardized Document structure
|
|
945
963
|
*
|
|
946
|
-
* @param
|
|
947
|
-
* @param
|
|
948
|
-
* @
|
|
949
|
-
* @param filter.value - The value to filter by
|
|
950
|
-
* @param options - Search options, including whether to include nested properties
|
|
951
|
-
* @returns True if the property matches the filter criteria, false otherwise
|
|
964
|
+
* @param document - Raw document content in OCHRE format
|
|
965
|
+
* @param language - Language code to use for content selection (defaults to "eng")
|
|
966
|
+
* @returns Parsed Document object with content and footnotes
|
|
952
967
|
*/
|
|
953
|
-
function
|
|
954
|
-
const
|
|
955
|
-
if (
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
if (typeof filter.value !== "string") return false;
|
|
960
|
-
return value.content.toLocaleLowerCase("en-US").includes(filter.value.toLocaleLowerCase("en-US"));
|
|
961
|
-
}
|
|
962
|
-
if (typeof value.content === "number") {
|
|
963
|
-
if (typeof filter.value !== "number") return false;
|
|
964
|
-
return value.content === filter.value;
|
|
965
|
-
}
|
|
966
|
-
if (typeof value.content === "boolean") {
|
|
967
|
-
if (typeof filter.value !== "boolean") return false;
|
|
968
|
-
return value.content === filter.value;
|
|
969
|
-
}
|
|
970
|
-
return false;
|
|
971
|
-
});
|
|
972
|
-
if (!isFound && includeNestedProperties) isFound = property.properties.some((property) => filterProperties(property, filter, { includeNestedProperties: true }));
|
|
973
|
-
return isFound;
|
|
974
|
-
}
|
|
975
|
-
return false;
|
|
968
|
+
function parseDocument(document, language = "eng") {
|
|
969
|
+
const doc = Array.isArray(document) ? document.find((d) => d.lang === language) : document;
|
|
970
|
+
if (isFakeString(doc.string)) return parseEmail(parseFakeString(doc.string));
|
|
971
|
+
let result = "";
|
|
972
|
+
for (const item of ensureArray(doc.string)) result += parseStringDocumentItem(item);
|
|
973
|
+
return result;
|
|
976
974
|
}
|
|
977
|
-
|
|
978
|
-
//#endregion
|
|
979
|
-
//#region src/utils/helpers.ts
|
|
980
975
|
/**
|
|
981
|
-
*
|
|
982
|
-
*
|
|
983
|
-
* @remarks
|
|
984
|
-
* Version 1 of the OCHRE API is deprecated and will be removed in the future.
|
|
985
|
-
* It points to the old Tamino server.
|
|
976
|
+
* Parses raw image data into a standardized Image structure
|
|
986
977
|
*
|
|
987
|
-
*
|
|
988
|
-
*
|
|
989
|
-
*/
|
|
990
|
-
const DEFAULT_API_VERSION = 2;
|
|
991
|
-
/**
|
|
992
|
-
* The default page size to use for fetching paginated items
|
|
993
|
-
*/
|
|
994
|
-
const DEFAULT_PAGE_SIZE = 48;
|
|
995
|
-
/**
|
|
996
|
-
* Flatten the properties of an item
|
|
997
|
-
* @param item - The item whose properties to flatten
|
|
998
|
-
* @returns The item with the properties flattened
|
|
978
|
+
* @param image - Raw image data in OCHRE format
|
|
979
|
+
* @returns Parsed Image object or null if invalid
|
|
999
980
|
*/
|
|
1000
|
-
function
|
|
1001
|
-
const allProperties = [];
|
|
1002
|
-
if ("properties" in item) allProperties.push(...item.properties);
|
|
1003
|
-
if ("observations" in item) for (const observation of item.observations) allProperties.push(...observation.properties);
|
|
1004
|
-
if ("interpretations" in item) for (const interpretation of item.interpretations) allProperties.push(...interpretation.properties);
|
|
1005
|
-
if ("bibliographies" in item) for (const bibliography of item.bibliographies) allProperties.push(...bibliography.properties);
|
|
981
|
+
function parseImage(image) {
|
|
1006
982
|
return {
|
|
1007
|
-
|
|
1008
|
-
|
|
983
|
+
publicationDateTime: parseOptionalDate(image.publicationDateTime),
|
|
984
|
+
identification: image.identification ? parseIdentification(image.identification) : null,
|
|
985
|
+
url: image.href ?? (image.htmlImgSrcPrefix == null && image.content != null ? parseFakeString(image.content) : null),
|
|
986
|
+
htmlPrefix: image.htmlImgSrcPrefix ?? null,
|
|
987
|
+
content: image.htmlImgSrcPrefix != null && image.content != null ? parseFakeString(image.content) : null,
|
|
988
|
+
widthPreview: image.widthPreview ?? null,
|
|
989
|
+
heightPreview: image.heightPreview ?? null,
|
|
990
|
+
width: image.width ?? null,
|
|
991
|
+
height: image.height ?? null
|
|
1009
992
|
};
|
|
1010
993
|
}
|
|
1011
|
-
|
|
1012
|
-
//#endregion
|
|
1013
|
-
//#region src/utils/parse.ts
|
|
1014
994
|
/**
|
|
1015
|
-
*
|
|
995
|
+
* Parses raw notes into standardized Note objects
|
|
1016
996
|
*
|
|
1017
|
-
* @param
|
|
1018
|
-
* @param
|
|
1019
|
-
* @returns Array of
|
|
997
|
+
* @param notes - Array of raw notes in OCHRE format
|
|
998
|
+
* @param language - Language code for content selection (defaults to "eng")
|
|
999
|
+
* @returns Array of parsed Note objects
|
|
1020
1000
|
*/
|
|
1021
|
-
function
|
|
1022
|
-
const
|
|
1023
|
-
const
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1001
|
+
function parseNotes(notes, language = "eng") {
|
|
1002
|
+
const result = [];
|
|
1003
|
+
for (const note of notes) {
|
|
1004
|
+
if (typeof note === "string") {
|
|
1005
|
+
if (note !== "") result.push({
|
|
1006
|
+
number: -1,
|
|
1007
|
+
title: null,
|
|
1008
|
+
date: null,
|
|
1009
|
+
authors: [],
|
|
1010
|
+
content: note
|
|
1011
|
+
});
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
1014
|
+
const notesToParse = note.content != null ? ensureArray(note.content) : [];
|
|
1015
|
+
if (notesToParse.length === 0) continue;
|
|
1016
|
+
const noteWithLanguage = notesToParse.find((item) => item.lang === language) ?? notesToParse[0];
|
|
1017
|
+
if (!noteWithLanguage) throw new Error(`Note does not have a valid content item: ${JSON.stringify(note, null, 2)}`);
|
|
1018
|
+
const content = isFakeString(noteWithLanguage.string) ? parseEmail(parseFakeString(noteWithLanguage.string)) : parseEmail(parseDocument(noteWithLanguage));
|
|
1019
|
+
result.push({
|
|
1020
|
+
number: note.noteNo,
|
|
1021
|
+
title: noteWithLanguage.title != null ? parseFakeString(noteWithLanguage.title) : null,
|
|
1022
|
+
date: note.date ?? null,
|
|
1023
|
+
authors: note.authors ? parsePersons(ensureArray(note.authors.author)) : [],
|
|
1024
|
+
content
|
|
1029
1025
|
});
|
|
1030
1026
|
}
|
|
1031
|
-
return
|
|
1027
|
+
return result;
|
|
1032
1028
|
}
|
|
1033
1029
|
/**
|
|
1034
|
-
* Parses
|
|
1030
|
+
* Parses raw coordinates data into a standardized array of Coordinate structures
|
|
1035
1031
|
*
|
|
1036
|
-
* @param
|
|
1037
|
-
* @returns
|
|
1032
|
+
* @param coordinates - Raw coordinates data in OCHRE format
|
|
1033
|
+
* @returns Parsed array of Coordinate objects
|
|
1038
1034
|
*/
|
|
1039
|
-
function
|
|
1040
|
-
return
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
abbreviation: "",
|
|
1086
|
-
code: identification.code ?? null
|
|
1087
|
-
};
|
|
1088
|
-
for (const key of Object.keys(identification)) {
|
|
1089
|
-
if (key === "label" || key === "code") continue;
|
|
1090
|
-
const raw = identification[key];
|
|
1091
|
-
result[key] = parseFakeStringOrContent(raw);
|
|
1035
|
+
function parseCoordinates(coordinates) {
|
|
1036
|
+
if (coordinates == null) return [];
|
|
1037
|
+
const result = [];
|
|
1038
|
+
for (const coord of ensureArray(coordinates.coord)) {
|
|
1039
|
+
const source = "source" in coord && coord.source ? coord.source.context === "self" ? {
|
|
1040
|
+
context: "self",
|
|
1041
|
+
uuid: coord.source.label.uuid,
|
|
1042
|
+
label: parseStringContent(coord.source.label)
|
|
1043
|
+
} : coord.source.context === "related" ? {
|
|
1044
|
+
context: "related",
|
|
1045
|
+
uuid: coord.source.label.uuid,
|
|
1046
|
+
label: parseStringContent(coord.source.label),
|
|
1047
|
+
value: parseStringContent(coord.source.value)
|
|
1048
|
+
} : {
|
|
1049
|
+
context: "inherited",
|
|
1050
|
+
uuid: coord.source.label.uuid,
|
|
1051
|
+
item: {
|
|
1052
|
+
uuid: coord.source.item.label.uuid,
|
|
1053
|
+
label: parseStringContent(coord.source.item.label)
|
|
1054
|
+
},
|
|
1055
|
+
label: parseStringContent(coord.source.label)
|
|
1056
|
+
} : null;
|
|
1057
|
+
switch (coord.type) {
|
|
1058
|
+
case "point":
|
|
1059
|
+
result.push({
|
|
1060
|
+
type: coord.type,
|
|
1061
|
+
latitude: coord.latitude,
|
|
1062
|
+
longitude: coord.longitude,
|
|
1063
|
+
altitude: coord.altitude ?? null,
|
|
1064
|
+
source
|
|
1065
|
+
});
|
|
1066
|
+
break;
|
|
1067
|
+
case "plane":
|
|
1068
|
+
result.push({
|
|
1069
|
+
type: coord.type,
|
|
1070
|
+
minimum: {
|
|
1071
|
+
latitude: coord.minimum.latitude,
|
|
1072
|
+
longitude: coord.minimum.longitude
|
|
1073
|
+
},
|
|
1074
|
+
maximum: {
|
|
1075
|
+
latitude: coord.maximum.latitude,
|
|
1076
|
+
longitude: coord.maximum.longitude
|
|
1077
|
+
},
|
|
1078
|
+
source
|
|
1079
|
+
});
|
|
1080
|
+
break;
|
|
1092
1081
|
}
|
|
1093
|
-
return result;
|
|
1094
|
-
} catch (error) {
|
|
1095
|
-
console.error(error);
|
|
1096
|
-
return {
|
|
1097
|
-
label: "",
|
|
1098
|
-
abbreviation: "",
|
|
1099
|
-
code: null
|
|
1100
|
-
};
|
|
1101
1082
|
}
|
|
1083
|
+
return result;
|
|
1102
1084
|
}
|
|
1103
1085
|
/**
|
|
1104
|
-
* Parses raw
|
|
1105
|
-
*
|
|
1106
|
-
* @param language - Raw language data, either single or array
|
|
1107
|
-
* @returns Array of language codes as strings
|
|
1108
|
-
*/
|
|
1109
|
-
function parseLanguages(language) {
|
|
1110
|
-
if (language == null) return ["eng"];
|
|
1111
|
-
if (typeof language === "string") return [language];
|
|
1112
|
-
if (Array.isArray(language)) return language.map((lang) => typeof lang === "object" ? parseStringContent(lang) : lang);
|
|
1113
|
-
else return [parseStringContent(language)];
|
|
1114
|
-
}
|
|
1115
|
-
/**
|
|
1116
|
-
* Parses raw metadata into the standardized Metadata type
|
|
1117
|
-
*
|
|
1118
|
-
* @param metadata - Raw metadata from OCHRE format
|
|
1119
|
-
* @returns Parsed Metadata object
|
|
1120
|
-
*/
|
|
1121
|
-
function parseMetadata(metadata) {
|
|
1122
|
-
let identification = {
|
|
1123
|
-
label: "",
|
|
1124
|
-
abbreviation: "",
|
|
1125
|
-
code: null
|
|
1126
|
-
};
|
|
1127
|
-
if (metadata.item) if (metadata.item.label || metadata.item.abbreviation) identification = {
|
|
1128
|
-
label: metadata.item.label ? parseStringContent(metadata.item.label) : "",
|
|
1129
|
-
abbreviation: metadata.item.abbreviation ? parseStringContent(metadata.item.abbreviation) : "",
|
|
1130
|
-
code: metadata.item.identification.code ?? null
|
|
1131
|
-
};
|
|
1132
|
-
else identification = parseIdentification(metadata.item.identification);
|
|
1133
|
-
const projectId = metadata.project?.identification ? parseIdentification(metadata.project.identification) : null;
|
|
1134
|
-
return {
|
|
1135
|
-
project: projectId ? {
|
|
1136
|
-
identification: {
|
|
1137
|
-
...projectId,
|
|
1138
|
-
website: metadata.project?.identification.website ?? null
|
|
1139
|
-
},
|
|
1140
|
-
dateFormat: metadata.project?.dateFormat ?? null,
|
|
1141
|
-
page: metadata.project?.page ?? null
|
|
1142
|
-
} : null,
|
|
1143
|
-
collection: metadata.collection ? {
|
|
1144
|
-
identification: parseIdentification(metadata.collection.identification),
|
|
1145
|
-
page: metadata.collection.page
|
|
1146
|
-
} : null,
|
|
1147
|
-
publication: metadata.publication ? {
|
|
1148
|
-
identification: parseIdentification(metadata.publication.identification),
|
|
1149
|
-
page: metadata.publication.page
|
|
1150
|
-
} : null,
|
|
1151
|
-
item: metadata.item ? {
|
|
1152
|
-
identification,
|
|
1153
|
-
category: metadata.item.category,
|
|
1154
|
-
type: metadata.item.type,
|
|
1155
|
-
maxLength: metadata.item.maxLength ?? null
|
|
1156
|
-
} : null,
|
|
1157
|
-
dataset: parseFakeStringOrContent(metadata.dataset),
|
|
1158
|
-
publisher: parseFakeStringOrContent(metadata.publisher),
|
|
1159
|
-
languages: parseLanguages(metadata.language),
|
|
1160
|
-
identifier: parseFakeStringOrContent(metadata.identifier),
|
|
1161
|
-
description: parseFakeStringOrContent(metadata.description)
|
|
1162
|
-
};
|
|
1163
|
-
}
|
|
1164
|
-
/**
|
|
1165
|
-
* Parses raw context item data into the standardized ContextItem type
|
|
1166
|
-
*
|
|
1167
|
-
* @param contextItem - Raw context item data from OCHRE format
|
|
1168
|
-
* @returns Parsed ContextItem object
|
|
1169
|
-
*/
|
|
1170
|
-
function parseContextItem(contextItem) {
|
|
1171
|
-
return {
|
|
1172
|
-
uuid: contextItem.uuid,
|
|
1173
|
-
publicationDateTime: parseOptionalDate(contextItem.publicationDateTime),
|
|
1174
|
-
number: contextItem.n,
|
|
1175
|
-
content: parseFakeString(contextItem.content)
|
|
1176
|
-
};
|
|
1177
|
-
}
|
|
1178
|
-
/**
|
|
1179
|
-
* Parses raw context data into the standardized Context type
|
|
1086
|
+
* Parses a raw observation into a standardized Observation structure
|
|
1180
1087
|
*
|
|
1181
|
-
* @param
|
|
1182
|
-
* @returns Parsed
|
|
1088
|
+
* @param observation - Raw observation data in OCHRE format
|
|
1089
|
+
* @returns Parsed Observation object
|
|
1183
1090
|
*/
|
|
1184
|
-
function
|
|
1091
|
+
function parseObservation(observation) {
|
|
1185
1092
|
return {
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1093
|
+
number: observation.observationNo,
|
|
1094
|
+
date: observation.date ?? null,
|
|
1095
|
+
observers: observation.observers != null ? isFakeString(observation.observers) ? parseFakeString(observation.observers).split(";").map((observer) => observer.trim()) : parsePersons(ensureArray(observation.observers)) : [],
|
|
1096
|
+
notes: observation.notes ? parseNotes(ensureArray(observation.notes.note)) : [],
|
|
1097
|
+
links: observation.links ? parseLinks(ensureArray(observation.links)) : [],
|
|
1098
|
+
properties: observation.properties ? parseProperties(ensureArray(observation.properties.property)) : [],
|
|
1099
|
+
bibliographies: observation.bibliographies ? parseBibliographies(ensureArray(observation.bibliographies.bibliography)) : []
|
|
1192
1100
|
};
|
|
1193
1101
|
}
|
|
1194
1102
|
/**
|
|
1195
|
-
* Parses raw
|
|
1103
|
+
* Parses an array of raw observations into standardized Observation objects
|
|
1196
1104
|
*
|
|
1197
|
-
* @param
|
|
1198
|
-
* @returns
|
|
1105
|
+
* @param observations - Array of raw observations in OCHRE format
|
|
1106
|
+
* @returns Array of parsed Observation objects
|
|
1199
1107
|
*/
|
|
1200
|
-
function
|
|
1201
|
-
|
|
1202
|
-
return {
|
|
1203
|
-
content: license.license.content,
|
|
1204
|
-
url: license.license.target
|
|
1205
|
-
};
|
|
1108
|
+
function parseObservations(observations) {
|
|
1109
|
+
return observations.map((obs) => parseObservation(obs));
|
|
1206
1110
|
}
|
|
1207
1111
|
/**
|
|
1208
|
-
* Parses raw
|
|
1112
|
+
* Parses an array of raw events into standardized Event objects
|
|
1209
1113
|
*
|
|
1210
|
-
* @param
|
|
1211
|
-
* @returns
|
|
1114
|
+
* @param events - Array of raw events in OCHRE format
|
|
1115
|
+
* @returns Array of parsed Event objects
|
|
1212
1116
|
*/
|
|
1213
|
-
function
|
|
1214
|
-
return {
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
type: person.type ?? null,
|
|
1222
|
-
number: person.n ?? null,
|
|
1223
|
-
context: person.context ? parseContext(person.context) : null,
|
|
1224
|
-
date: person.date ?? null,
|
|
1225
|
-
identification: person.identification ? parseIdentification(person.identification) : null,
|
|
1226
|
-
availability: person.availability ? parseLicense(person.availability) : null,
|
|
1227
|
-
image: person.image ? parseImage(person.image) : null,
|
|
1228
|
-
address: person.address ? {
|
|
1229
|
-
country: person.address.country ?? null,
|
|
1230
|
-
city: person.address.city ?? null,
|
|
1231
|
-
state: person.address.state ?? null
|
|
1232
|
-
} : null,
|
|
1233
|
-
description: person.description ? parseFakeStringOrContent(person.description) : null,
|
|
1234
|
-
coordinates: parseCoordinates(person.coordinates),
|
|
1235
|
-
content: person.content != null ? parseFakeString(person.content) : null,
|
|
1236
|
-
notes: person.notes ? parseNotes(ensureArray(person.notes.note)) : [],
|
|
1237
|
-
links: person.links ? parseLinks(ensureArray(person.links)) : [],
|
|
1238
|
-
events: person.events ? parseEvents(ensureArray(person.events.event)) : [],
|
|
1239
|
-
properties: person.properties ? parseProperties(ensureArray(person.properties.property)) : [],
|
|
1240
|
-
bibliographies: person.bibliographies ? parseBibliographies(ensureArray(person.bibliographies.bibliography)) : []
|
|
1241
|
-
};
|
|
1242
|
-
}
|
|
1243
|
-
/**
|
|
1244
|
-
* Parses raw person data into the standardized Person type
|
|
1245
|
-
*
|
|
1246
|
-
* @param persons - Array of raw person data from OCHRE format
|
|
1247
|
-
* @returns Array of parsed Person objects
|
|
1248
|
-
*/
|
|
1249
|
-
function parsePersons(persons) {
|
|
1250
|
-
return persons.map((person) => parsePerson(person));
|
|
1251
|
-
}
|
|
1252
|
-
/**
|
|
1253
|
-
* Parses an array of raw links into standardized Link objects
|
|
1254
|
-
*
|
|
1255
|
-
* @param linkRaw - Raw OCHRE link
|
|
1256
|
-
* @returns Parsed Link object
|
|
1257
|
-
*/
|
|
1258
|
-
function parseLink(linkRaw) {
|
|
1259
|
-
const categoryKey = [
|
|
1260
|
-
"resource",
|
|
1261
|
-
"spatialUnit",
|
|
1262
|
-
"concept",
|
|
1263
|
-
"set",
|
|
1264
|
-
"tree",
|
|
1265
|
-
"person",
|
|
1266
|
-
"bibliography",
|
|
1267
|
-
"propertyVariable",
|
|
1268
|
-
"propertyValue"
|
|
1269
|
-
].find((key) => key in linkRaw);
|
|
1270
|
-
if (!categoryKey) throw new Error(`Invalid link provided: ${JSON.stringify(linkRaw, null, 2)}`);
|
|
1271
|
-
const links = linkRaw[categoryKey];
|
|
1272
|
-
if (links == null) return [];
|
|
1273
|
-
return ensureArray(links).map((link) => {
|
|
1274
|
-
const returnLink = {
|
|
1275
|
-
category: categoryKey,
|
|
1276
|
-
content: "content" in link && link.content != null ? parseFakeString(link.content) : null,
|
|
1277
|
-
href: "href" in link && link.href != null ? link.href : null,
|
|
1278
|
-
fileFormat: "fileFormat" in link && link.fileFormat != null ? link.fileFormat : null,
|
|
1279
|
-
fileSize: "fileSize" in link && link.fileSize != null ? link.fileSize : null,
|
|
1280
|
-
uuid: link.uuid ?? null,
|
|
1281
|
-
type: link.type ?? null,
|
|
1282
|
-
identification: link.identification ? parseIdentification(link.identification) : null,
|
|
1283
|
-
description: "description" in link && link.description != null ? parseStringContent(link.description) : null,
|
|
1284
|
-
image: null,
|
|
1285
|
-
bibliographies: "bibliography" in linkRaw && linkRaw.bibliography != null ? parseBibliographies(ensureArray(linkRaw.bibliography)) : null,
|
|
1286
|
-
publicationDateTime: parseOptionalDate(link.publicationDateTime)
|
|
1287
|
-
};
|
|
1288
|
-
if ("height" in link && link.height != null && link.width != null && link.heightPreview != null && link.widthPreview != null) returnLink.image = {
|
|
1289
|
-
isInline: link.rend === "inline",
|
|
1290
|
-
isPrimary: link.isPrimary ?? false,
|
|
1291
|
-
heightPreview: link.heightPreview,
|
|
1292
|
-
widthPreview: link.widthPreview,
|
|
1293
|
-
height: link.height,
|
|
1294
|
-
width: link.width
|
|
1295
|
-
};
|
|
1296
|
-
return returnLink;
|
|
1297
|
-
});
|
|
1298
|
-
}
|
|
1299
|
-
/**
|
|
1300
|
-
* Parses an array of raw links into standardized Link objects
|
|
1301
|
-
*
|
|
1302
|
-
* @param links - Array of raw OCHRE links
|
|
1303
|
-
* @returns Array of parsed Link objects
|
|
1304
|
-
*/
|
|
1305
|
-
function parseLinks(links) {
|
|
1306
|
-
const result = [];
|
|
1307
|
-
for (const link of links) result.push(...parseLink(link));
|
|
1308
|
-
return result;
|
|
1309
|
-
}
|
|
1310
|
-
/**
|
|
1311
|
-
* Parses raw document content into a standardized Document structure
|
|
1312
|
-
*
|
|
1313
|
-
* @param document - Raw document content in OCHRE format
|
|
1314
|
-
* @param language - Language code to use for content selection (defaults to "eng")
|
|
1315
|
-
* @returns Parsed Document object with content and footnotes
|
|
1316
|
-
*/
|
|
1317
|
-
function parseDocument(document, language = "eng") {
|
|
1318
|
-
const doc = Array.isArray(document) ? document.find((d) => d.lang === language) : document;
|
|
1319
|
-
if (isFakeString(doc.string)) return parseEmail(parseFakeString(doc.string));
|
|
1320
|
-
let result = "";
|
|
1321
|
-
for (const item of ensureArray(doc.string)) result += parseStringDocumentItem(item);
|
|
1322
|
-
return result;
|
|
1323
|
-
}
|
|
1324
|
-
/**
|
|
1325
|
-
* Parses raw image data into a standardized Image structure
|
|
1326
|
-
*
|
|
1327
|
-
* @param image - Raw image data in OCHRE format
|
|
1328
|
-
* @returns Parsed Image object or null if invalid
|
|
1329
|
-
*/
|
|
1330
|
-
function parseImage(image) {
|
|
1331
|
-
return {
|
|
1332
|
-
publicationDateTime: parseOptionalDate(image.publicationDateTime),
|
|
1333
|
-
identification: image.identification ? parseIdentification(image.identification) : null,
|
|
1334
|
-
url: image.href ?? (image.htmlImgSrcPrefix == null && image.content != null ? parseFakeString(image.content) : null),
|
|
1335
|
-
htmlPrefix: image.htmlImgSrcPrefix ?? null,
|
|
1336
|
-
content: image.htmlImgSrcPrefix != null && image.content != null ? parseFakeString(image.content) : null,
|
|
1337
|
-
widthPreview: image.widthPreview ?? null,
|
|
1338
|
-
heightPreview: image.heightPreview ?? null,
|
|
1339
|
-
width: image.width ?? null,
|
|
1340
|
-
height: image.height ?? null
|
|
1341
|
-
};
|
|
1342
|
-
}
|
|
1343
|
-
/**
|
|
1344
|
-
* Parses raw notes into standardized Note objects
|
|
1345
|
-
*
|
|
1346
|
-
* @param notes - Array of raw notes in OCHRE format
|
|
1347
|
-
* @param language - Language code for content selection (defaults to "eng")
|
|
1348
|
-
* @returns Array of parsed Note objects
|
|
1349
|
-
*/
|
|
1350
|
-
function parseNotes(notes, language = "eng") {
|
|
1351
|
-
const result = [];
|
|
1352
|
-
for (const note of notes) {
|
|
1353
|
-
if (typeof note === "string") {
|
|
1354
|
-
if (note !== "") result.push({
|
|
1355
|
-
number: -1,
|
|
1356
|
-
title: null,
|
|
1357
|
-
date: null,
|
|
1358
|
-
authors: [],
|
|
1359
|
-
content: note
|
|
1360
|
-
});
|
|
1361
|
-
continue;
|
|
1362
|
-
}
|
|
1363
|
-
const notesToParse = note.content != null ? ensureArray(note.content) : [];
|
|
1364
|
-
if (notesToParse.length === 0) continue;
|
|
1365
|
-
const noteWithLanguage = notesToParse.find((item) => item.lang === language) ?? notesToParse[0];
|
|
1366
|
-
if (!noteWithLanguage) throw new Error(`Note does not have a valid content item: ${JSON.stringify(note, null, 2)}`);
|
|
1367
|
-
const content = isFakeString(noteWithLanguage.string) ? parseEmail(parseFakeString(noteWithLanguage.string)) : parseEmail(parseDocument(noteWithLanguage));
|
|
1368
|
-
result.push({
|
|
1369
|
-
number: note.noteNo,
|
|
1370
|
-
title: noteWithLanguage.title != null ? parseFakeString(noteWithLanguage.title) : null,
|
|
1371
|
-
date: note.date ?? null,
|
|
1372
|
-
authors: note.authors ? parsePersons(ensureArray(note.authors.author)) : [],
|
|
1373
|
-
content
|
|
1374
|
-
});
|
|
1375
|
-
}
|
|
1376
|
-
return result;
|
|
1377
|
-
}
|
|
1378
|
-
/**
|
|
1379
|
-
* Parses raw coordinates data into a standardized array of Coordinate structures
|
|
1380
|
-
*
|
|
1381
|
-
* @param coordinates - Raw coordinates data in OCHRE format
|
|
1382
|
-
* @returns Parsed array of Coordinate objects
|
|
1383
|
-
*/
|
|
1384
|
-
function parseCoordinates(coordinates) {
|
|
1385
|
-
if (coordinates == null) return [];
|
|
1386
|
-
const result = [];
|
|
1387
|
-
for (const coord of ensureArray(coordinates.coord)) {
|
|
1388
|
-
const source = "source" in coord && coord.source ? coord.source.context === "self" ? {
|
|
1389
|
-
context: "self",
|
|
1390
|
-
uuid: coord.source.label.uuid,
|
|
1391
|
-
label: parseStringContent(coord.source.label)
|
|
1392
|
-
} : coord.source.context === "related" ? {
|
|
1393
|
-
context: "related",
|
|
1394
|
-
uuid: coord.source.label.uuid,
|
|
1395
|
-
label: parseStringContent(coord.source.label),
|
|
1396
|
-
value: parseStringContent(coord.source.value)
|
|
1397
|
-
} : {
|
|
1398
|
-
context: "inherited",
|
|
1399
|
-
uuid: coord.source.label.uuid,
|
|
1400
|
-
item: {
|
|
1401
|
-
uuid: coord.source.item.label.uuid,
|
|
1402
|
-
label: parseStringContent(coord.source.item.label)
|
|
1403
|
-
},
|
|
1404
|
-
label: parseStringContent(coord.source.label)
|
|
1405
|
-
} : null;
|
|
1406
|
-
switch (coord.type) {
|
|
1407
|
-
case "point":
|
|
1408
|
-
result.push({
|
|
1409
|
-
type: coord.type,
|
|
1410
|
-
latitude: coord.latitude,
|
|
1411
|
-
longitude: coord.longitude,
|
|
1412
|
-
altitude: coord.altitude ?? null,
|
|
1413
|
-
source
|
|
1414
|
-
});
|
|
1415
|
-
break;
|
|
1416
|
-
case "plane":
|
|
1417
|
-
result.push({
|
|
1418
|
-
type: coord.type,
|
|
1419
|
-
minimum: {
|
|
1420
|
-
latitude: coord.minimum.latitude,
|
|
1421
|
-
longitude: coord.minimum.longitude
|
|
1422
|
-
},
|
|
1423
|
-
maximum: {
|
|
1424
|
-
latitude: coord.maximum.latitude,
|
|
1425
|
-
longitude: coord.maximum.longitude
|
|
1426
|
-
},
|
|
1427
|
-
source
|
|
1428
|
-
});
|
|
1429
|
-
break;
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
return result;
|
|
1433
|
-
}
|
|
1434
|
-
/**
|
|
1435
|
-
* Parses a raw observation into a standardized Observation structure
|
|
1436
|
-
*
|
|
1437
|
-
* @param observation - Raw observation data in OCHRE format
|
|
1438
|
-
* @returns Parsed Observation object
|
|
1439
|
-
*/
|
|
1440
|
-
function parseObservation(observation) {
|
|
1441
|
-
return {
|
|
1442
|
-
number: observation.observationNo,
|
|
1443
|
-
date: observation.date ?? null,
|
|
1444
|
-
observers: observation.observers != null ? isFakeString(observation.observers) ? parseFakeString(observation.observers).split(";").map((observer) => observer.trim()) : parsePersons(ensureArray(observation.observers)) : [],
|
|
1445
|
-
notes: observation.notes ? parseNotes(ensureArray(observation.notes.note)) : [],
|
|
1446
|
-
links: observation.links ? parseLinks(ensureArray(observation.links)) : [],
|
|
1447
|
-
properties: observation.properties ? parseProperties(ensureArray(observation.properties.property)) : [],
|
|
1448
|
-
bibliographies: observation.bibliographies ? parseBibliographies(ensureArray(observation.bibliographies.bibliography)) : []
|
|
1449
|
-
};
|
|
1450
|
-
}
|
|
1451
|
-
/**
|
|
1452
|
-
* Parses an array of raw observations into standardized Observation objects
|
|
1453
|
-
*
|
|
1454
|
-
* @param observations - Array of raw observations in OCHRE format
|
|
1455
|
-
* @returns Array of parsed Observation objects
|
|
1456
|
-
*/
|
|
1457
|
-
function parseObservations(observations) {
|
|
1458
|
-
return observations.map((obs) => parseObservation(obs));
|
|
1459
|
-
}
|
|
1460
|
-
/**
|
|
1461
|
-
* Parses an array of raw events into standardized Event objects
|
|
1462
|
-
*
|
|
1463
|
-
* @param events - Array of raw events in OCHRE format
|
|
1464
|
-
* @returns Array of parsed Event objects
|
|
1465
|
-
*/
|
|
1466
|
-
function parseEvents(events) {
|
|
1467
|
-
return events.map((event) => ({
|
|
1468
|
-
dateTime: event.endDateTime != null ? `${event.dateTime}/${event.endDateTime}` : event.dateTime ?? null,
|
|
1469
|
-
label: parseStringContent(event.label),
|
|
1470
|
-
location: event.location ? {
|
|
1471
|
-
uuid: event.location.uuid,
|
|
1472
|
-
publicationDateTime: parseOptionalDate(event.location.publicationDateTime),
|
|
1473
|
-
content: parseStringContent(event.location)
|
|
1117
|
+
function parseEvents(events) {
|
|
1118
|
+
return events.map((event) => ({
|
|
1119
|
+
dateTime: event.endDateTime != null ? `${event.dateTime}/${event.endDateTime}` : event.dateTime ?? null,
|
|
1120
|
+
label: parseStringContent(event.label),
|
|
1121
|
+
location: event.location ? {
|
|
1122
|
+
uuid: event.location.uuid,
|
|
1123
|
+
publicationDateTime: parseOptionalDate(event.location.publicationDateTime),
|
|
1124
|
+
content: parseStringContent(event.location)
|
|
1474
1125
|
} : null,
|
|
1475
1126
|
agent: event.agent ? {
|
|
1476
1127
|
uuid: event.agent.uuid,
|
|
@@ -2132,37 +1783,6 @@ function parseConcept(concept, metadata, persistentUrl, belongsTo) {
|
|
|
2132
1783
|
};
|
|
2133
1784
|
}
|
|
2134
1785
|
/**
|
|
2135
|
-
* Parses raw webpage resources into standardized WebElement or Webpage objects
|
|
2136
|
-
*
|
|
2137
|
-
* @param webpageResources - Array of raw webpage resources in OCHRE format
|
|
2138
|
-
* @param type - Type of resource to parse ("element" or "page")
|
|
2139
|
-
* @returns Array of parsed WebElement or Webpage objects
|
|
2140
|
-
*/
|
|
2141
|
-
const parseWebpageResources = (webpageResources, type) => {
|
|
2142
|
-
const returnElements = [];
|
|
2143
|
-
for (const resource of webpageResources) {
|
|
2144
|
-
if (getPropertyByLabelAndValue(resource.properties ? parseProperties(ensureArray(resource.properties.property)) : [], "presentation", type) === null) continue;
|
|
2145
|
-
switch (type) {
|
|
2146
|
-
case "element": {
|
|
2147
|
-
const element = parseWebElement(resource);
|
|
2148
|
-
returnElements.push(element);
|
|
2149
|
-
break;
|
|
2150
|
-
}
|
|
2151
|
-
case "page": {
|
|
2152
|
-
const webpage = parseWebpage(resource);
|
|
2153
|
-
if (webpage) returnElements.push(webpage);
|
|
2154
|
-
break;
|
|
2155
|
-
}
|
|
2156
|
-
case "block": {
|
|
2157
|
-
const block = parseWebBlock(resource);
|
|
2158
|
-
if (block) returnElements.push(block);
|
|
2159
|
-
break;
|
|
2160
|
-
}
|
|
2161
|
-
}
|
|
2162
|
-
}
|
|
2163
|
-
return returnElements;
|
|
2164
|
-
};
|
|
2165
|
-
/**
|
|
2166
1786
|
* Parses raw concept data into standardized Concept objects
|
|
2167
1787
|
*
|
|
2168
1788
|
* @param concepts - Array of raw concept data in OCHRE format
|
|
@@ -2171,88 +1791,908 @@ const parseWebpageResources = (webpageResources, type) => {
|
|
|
2171
1791
|
function parseConcepts(concepts) {
|
|
2172
1792
|
return concepts.map((concept) => parseConcept(concept));
|
|
2173
1793
|
}
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
1794
|
+
|
|
1795
|
+
//#endregion
|
|
1796
|
+
//#region src/utils/fetchers/gallery.ts
|
|
1797
|
+
/**
|
|
1798
|
+
* Fetches and parses a gallery from the OCHRE API
|
|
1799
|
+
*
|
|
1800
|
+
* @param uuid - The UUID of the gallery
|
|
1801
|
+
* @param filter - The filter to apply to the gallery
|
|
1802
|
+
* @param page - The page number to fetch
|
|
1803
|
+
* @param pageSize - The number of items per page
|
|
1804
|
+
* @param options - The options for the fetch
|
|
1805
|
+
* @param options.fetch - The fetch function to use
|
|
1806
|
+
* @param options.version - The version of the OCHRE API to use
|
|
1807
|
+
* @returns The parsed gallery or an error message if the fetch/parse fails
|
|
1808
|
+
*/
|
|
1809
|
+
async function fetchGallery(uuid, filter, page, pageSize, options) {
|
|
1810
|
+
try {
|
|
1811
|
+
const { uuid: parsedUuid, filter: parsedFilter, page: parsedPage, pageSize: parsedPageSize } = gallerySchema.parse({
|
|
1812
|
+
uuid,
|
|
1813
|
+
filter,
|
|
1814
|
+
page,
|
|
1815
|
+
pageSize
|
|
1816
|
+
});
|
|
1817
|
+
const response = await (options?.fetch ?? fetch)(`https://ochre.lib.uchicago.edu/ochre?xquery=${encodeURIComponent(`
|
|
1818
|
+
for $q in input()/ochre[@uuid='${parsedUuid}']
|
|
1819
|
+
let $filtered := $q//items/resource[contains(lower-case(identification/label), lower-case('${parsedFilter}'))]
|
|
1820
|
+
let $maxLength := count($filtered)
|
|
1821
|
+
return <gallery maxLength='{$maxLength}'>
|
|
1822
|
+
{$q/metadata/project}
|
|
1823
|
+
{$q/metadata/item}
|
|
1824
|
+
{$filtered[position() >= ${((parsedPage - 1) * parsedPageSize + 1).toString()} and position() < ${(parsedPage * parsedPageSize + 1).toString()}]}
|
|
1825
|
+
</gallery>
|
|
1826
|
+
`)}&format=json`);
|
|
1827
|
+
if (!response.ok) throw new Error("Error fetching gallery items, please try again later.");
|
|
1828
|
+
const data = await response.json();
|
|
1829
|
+
if (!("gallery" in data.result)) throw new Error("Failed to fetch gallery");
|
|
1830
|
+
return {
|
|
1831
|
+
item: {
|
|
1832
|
+
identification: parseIdentification(data.result.gallery.item.identification),
|
|
1833
|
+
projectIdentification: parseIdentification(data.result.gallery.project.identification),
|
|
1834
|
+
resources: parseResources(data.result.gallery.resource ? Array.isArray(data.result.gallery.resource) ? data.result.gallery.resource : [data.result.gallery.resource] : []),
|
|
1835
|
+
maxLength: data.result.gallery.maxLength
|
|
1836
|
+
},
|
|
1837
|
+
error: null
|
|
1838
|
+
};
|
|
1839
|
+
} catch (error) {
|
|
1840
|
+
console.error(error);
|
|
1841
|
+
return {
|
|
1842
|
+
item: null,
|
|
1843
|
+
error: error instanceof Error ? error.message : "Failed to fetch gallery"
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
2178
1846
|
}
|
|
1847
|
+
|
|
1848
|
+
//#endregion
|
|
1849
|
+
//#region src/utils/helpers.ts
|
|
2179
1850
|
/**
|
|
2180
|
-
*
|
|
1851
|
+
* The default API version to use
|
|
2181
1852
|
*
|
|
2182
|
-
* @
|
|
2183
|
-
*
|
|
2184
|
-
*
|
|
1853
|
+
* @remarks
|
|
1854
|
+
* Version 1 of the OCHRE API is deprecated and will be removed in the future.
|
|
1855
|
+
* It points to the old Tamino server.
|
|
1856
|
+
*
|
|
1857
|
+
* Version 2 of the OCHRE API is the current version and is the default.
|
|
1858
|
+
* It points to the new MarkLogic server.
|
|
2185
1859
|
*/
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
1860
|
+
const DEFAULT_API_VERSION = 2;
|
|
1861
|
+
/**
|
|
1862
|
+
* The default page size to use for fetching paginated items
|
|
1863
|
+
*/
|
|
1864
|
+
const DEFAULT_PAGE_SIZE = 48;
|
|
1865
|
+
/**
|
|
1866
|
+
* Flatten the properties of an item
|
|
1867
|
+
* @param item - The item whose properties to flatten
|
|
1868
|
+
* @returns The item with the properties flattened
|
|
1869
|
+
*/
|
|
1870
|
+
function flattenItemProperties(item) {
|
|
1871
|
+
const allProperties = [];
|
|
1872
|
+
if ("properties" in item) allProperties.push(...item.properties);
|
|
1873
|
+
if ("observations" in item) for (const observation of item.observations) allProperties.push(...observation.properties);
|
|
1874
|
+
if ("interpretations" in item) for (const interpretation of item.interpretations) allProperties.push(...interpretation.properties);
|
|
1875
|
+
if ("bibliographies" in item) for (const bibliography of item.bibliographies) allProperties.push(...bibliography.properties);
|
|
1876
|
+
return {
|
|
1877
|
+
...item,
|
|
1878
|
+
properties: flattenProperties(allProperties)
|
|
1879
|
+
};
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
//#endregion
|
|
1883
|
+
//#region src/utils/fetchers/uuid.ts
|
|
1884
|
+
/**
|
|
1885
|
+
* Fetches raw OCHRE data by UUID from the OCHRE API
|
|
1886
|
+
*
|
|
1887
|
+
* @param uuid - The UUID of the OCHRE item to fetch
|
|
1888
|
+
* @returns A tuple containing either [null, OchreData] on success or [error message, null] on failure
|
|
1889
|
+
* @internal
|
|
1890
|
+
*/
|
|
1891
|
+
async function fetchByUuid(uuid, options) {
|
|
1892
|
+
try {
|
|
1893
|
+
const version = options?.version ?? DEFAULT_API_VERSION;
|
|
1894
|
+
const parsedUuid = uuidSchema.parse(uuid);
|
|
1895
|
+
const response = await (options?.fetch ?? fetch)(version === 2 ? `https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?uuid=${parsedUuid}&format=json&lang="*"` : `https://ochre.lib.uchicago.edu/ochre?uuid=${parsedUuid}&format=json&lang="*"`);
|
|
1896
|
+
if (!response.ok) throw new Error("Failed to fetch OCHRE data");
|
|
1897
|
+
const dataRaw = await response.json();
|
|
1898
|
+
if (version === 2 && (!("result" in dataRaw) || !("ochre" in dataRaw.result)) || version !== 2 && !("ochre" in dataRaw)) throw new Error("Invalid OCHRE data: API response missing 'ochre' key");
|
|
1899
|
+
return [null, "result" in dataRaw && !Array.isArray(dataRaw.result) && "ochre" in dataRaw.result ? dataRaw.result : "ochre" in dataRaw ? dataRaw : null];
|
|
1900
|
+
} catch (error) {
|
|
1901
|
+
return [error instanceof Error ? error.message : "Unknown error", null];
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
//#endregion
|
|
1906
|
+
//#region src/utils/fetchers/item.ts
|
|
1907
|
+
/**
|
|
1908
|
+
* Fetches and parses an OCHRE item from the OCHRE API
|
|
1909
|
+
*
|
|
1910
|
+
* @param uuid - The UUID of the OCHRE item to fetch
|
|
1911
|
+
* @returns Object containing the parsed OCHRE item, or an error message if the fetch/parse fails
|
|
1912
|
+
*/
|
|
1913
|
+
async function fetchItem(uuid, category, itemCategories, options) {
|
|
1914
|
+
try {
|
|
1915
|
+
const version = options?.version ?? DEFAULT_API_VERSION;
|
|
1916
|
+
const [error, data] = await fetchByUuid(uuid, {
|
|
1917
|
+
fetch,
|
|
1918
|
+
version
|
|
1919
|
+
});
|
|
1920
|
+
if (error !== null) throw new Error(error);
|
|
1921
|
+
const categoryKey = category ?? getItemCategory(Object.keys(data.ochre));
|
|
1922
|
+
const belongsTo = {
|
|
1923
|
+
uuid: data.ochre.uuidBelongsTo,
|
|
1924
|
+
abbreviation: parseFakeString(data.ochre.belongsTo)
|
|
1925
|
+
};
|
|
1926
|
+
const metadata = parseMetadata(data.ochre.metadata);
|
|
1927
|
+
let item;
|
|
1928
|
+
switch (categoryKey) {
|
|
1929
|
+
case "resource":
|
|
1930
|
+
if (!("resource" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'resource' key");
|
|
1931
|
+
item = parseResource(data.ochre.resource, metadata, data.ochre.persistentUrl, belongsTo);
|
|
1932
|
+
break;
|
|
1933
|
+
case "spatialUnit":
|
|
1934
|
+
if (!("spatialUnit" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'spatialUnit' key");
|
|
1935
|
+
item = parseSpatialUnit(data.ochre.spatialUnit, metadata, data.ochre.persistentUrl, belongsTo);
|
|
1936
|
+
break;
|
|
1937
|
+
case "concept":
|
|
1938
|
+
if (!("concept" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'concept' key");
|
|
1939
|
+
item = parseConcept(data.ochre.concept, metadata, data.ochre.persistentUrl, belongsTo);
|
|
1940
|
+
break;
|
|
1941
|
+
case "period":
|
|
1942
|
+
if (!("period" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'period' key");
|
|
1943
|
+
item = parsePeriod(data.ochre.period, metadata, data.ochre.persistentUrl, {
|
|
1944
|
+
uuid: data.ochre.uuidBelongsTo,
|
|
1945
|
+
abbreviation: parseFakeString(data.ochre.belongsTo)
|
|
1946
|
+
});
|
|
1947
|
+
break;
|
|
1948
|
+
case "bibliography":
|
|
1949
|
+
if (!("bibliography" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'bibliography' key");
|
|
1950
|
+
item = parseBibliography(data.ochre.bibliography, metadata, data.ochre.persistentUrl, belongsTo);
|
|
1951
|
+
break;
|
|
1952
|
+
case "person":
|
|
1953
|
+
if (!("person" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'person' key");
|
|
1954
|
+
item = parsePerson(data.ochre.person, metadata, data.ochre.persistentUrl, belongsTo);
|
|
1955
|
+
break;
|
|
1956
|
+
case "propertyVariable":
|
|
1957
|
+
if (!("propertyVariable" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'propertyVariable' key");
|
|
1958
|
+
item = parsePropertyVariable(data.ochre.propertyVariable, metadata, data.ochre.persistentUrl, belongsTo);
|
|
1959
|
+
break;
|
|
1960
|
+
case "propertyValue":
|
|
1961
|
+
if (!("propertyValue" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'propertyValue' key");
|
|
1962
|
+
item = parsePropertyValue(data.ochre.propertyValue, metadata, data.ochre.persistentUrl, belongsTo);
|
|
1963
|
+
break;
|
|
1964
|
+
case "text":
|
|
1965
|
+
if (!("text" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'text' key");
|
|
1966
|
+
item = parseText(data.ochre.text, metadata, data.ochre.persistentUrl, belongsTo);
|
|
1967
|
+
break;
|
|
1968
|
+
case "set":
|
|
1969
|
+
if (!("set" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'set' key");
|
|
1970
|
+
item = parseSet(data.ochre.set, itemCategories, metadata, data.ochre.persistentUrl, belongsTo);
|
|
1971
|
+
break;
|
|
1972
|
+
case "tree":
|
|
1973
|
+
if (!("tree" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'tree' key");
|
|
1974
|
+
item = parseTree(data.ochre.tree, itemCategories, metadata, data.ochre.persistentUrl, belongsTo);
|
|
1975
|
+
break;
|
|
1976
|
+
default: throw new Error("Invalid category");
|
|
2249
1977
|
}
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
1978
|
+
return {
|
|
1979
|
+
error: null,
|
|
1980
|
+
item
|
|
1981
|
+
};
|
|
1982
|
+
} catch (error) {
|
|
1983
|
+
return {
|
|
1984
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
1985
|
+
item: void 0
|
|
1986
|
+
};
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
//#endregion
|
|
1991
|
+
//#region src/utils/fetchers/items-by-property-values.ts
|
|
1992
|
+
/**
|
|
1993
|
+
* Build an XQuery string to fetch items by property values from the OCHRE API
|
|
1994
|
+
* @param params - The parameters for the fetch
|
|
1995
|
+
* @param params.projectScopeUuid - The UUID of the project scope
|
|
1996
|
+
* @param params.belongsToCollectionScopeUuids - An array of collection scope UUIDs to filter by
|
|
1997
|
+
* @param params.propertyVariableUuids - An array of property variable UUIDs to fetch
|
|
1998
|
+
* @param params.propertyValues - An array of property values to fetch
|
|
1999
|
+
* @param params.itemCategory - The category of the items to fetch
|
|
2000
|
+
* @param params.page - The page number (1-indexed)
|
|
2001
|
+
* @param params.pageSize - The number of items per page
|
|
2002
|
+
* @param params.includeChildItems - Whether to include child items of the same category
|
|
2003
|
+
* @param options - Options for the fetch
|
|
2004
|
+
* @param options.version - The version of the OCHRE API to use
|
|
2005
|
+
* @returns An XQuery string
|
|
2006
|
+
*/
|
|
2007
|
+
function buildXQuery$1(params, options) {
|
|
2008
|
+
const version = options?.version ?? DEFAULT_API_VERSION;
|
|
2009
|
+
const { propertyVariableUuids, propertyValues, projectScopeUuid, belongsToCollectionScopeUuids, itemCategory, page, pageSize, includeChildItems = false } = params;
|
|
2010
|
+
const startPos = (page - 1) * pageSize + 1;
|
|
2011
|
+
const endPos = page * pageSize;
|
|
2012
|
+
let belongsToCollectionScopeFilter = "";
|
|
2013
|
+
if (belongsToCollectionScopeUuids.length > 0) belongsToCollectionScopeFilter = `[.//properties[property[label/@uuid="${BELONGS_TO_COLLECTION_UUID}" and value/(${belongsToCollectionScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ")})]]`;
|
|
2014
|
+
const propertyVariables = propertyVariableUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
|
|
2015
|
+
const propertyValuesFilters = propertyValues.map(({ dataType, value }) => {
|
|
2016
|
+
if (dataType === "IDREF") return `value[@uuid="${value}"]`;
|
|
2017
|
+
if (dataType === "date" || dataType === "dateTime" || dataType === "time" || dataType === "integer" || dataType === "decimal" || dataType === "boolean") return `value[@rawValue="${value}"]`;
|
|
2018
|
+
return `value[.="${value}"]`;
|
|
2019
|
+
}).join(" or ");
|
|
2020
|
+
return `<ochre>{${`let $match-uuids := distinct-values(
|
|
2021
|
+
${version === 2 ? "doc()" : "input()"}/ochre[@uuidBelongsTo="${projectScopeUuid}"]
|
|
2022
|
+
${belongsToCollectionScopeFilter}
|
|
2023
|
+
//property[label[${propertyVariables}]][${propertyValuesFilters}]]
|
|
2024
|
+
/@uuid
|
|
2025
|
+
)
|
|
2026
|
+
|
|
2027
|
+
let $items := ${version === 2 ? "doc()" : "input()"}/ochre[@uuidBelongsTo="${projectScopeUuid}"]
|
|
2028
|
+
/${itemCategory ?? "*"}[@uuid = $match-uuids
|
|
2029
|
+
or bibliographies/bibliography/@uuid = $match-uuids]
|
|
2030
|
+
|
|
2031
|
+
let $unique-uuids := distinct-values($items/@uuid)
|
|
2032
|
+
let $totalCount := count($unique-uuids)
|
|
2033
|
+
|
|
2034
|
+
return <items totalCount="{$totalCount}" page="${page}" pageSize="${pageSize}">{
|
|
2035
|
+
for $uuid in $unique-uuids[position() ge ${startPos} and position() le ${endPos}]
|
|
2036
|
+
let $item := ($items[@uuid = $uuid])[1]
|
|
2037
|
+
let $category := local-name($item)
|
|
2038
|
+
return element { node-name($item) } {
|
|
2039
|
+
$item/@*, ${includeChildItems ? "$item/node()" : "$item/node()[not(local-name(.) = $category)]"}
|
|
2040
|
+
}
|
|
2041
|
+
}</items>`}}</ochre>`;
|
|
2042
|
+
}
|
|
2043
|
+
/**
|
|
2044
|
+
* Fetches and parses items by property values from the OCHRE API
|
|
2045
|
+
*
|
|
2046
|
+
* @param params - The parameters for the fetch
|
|
2047
|
+
* @param params.projectScopeUuid - The UUID of the project scope
|
|
2048
|
+
* @param params.belongsToCollectionScopeUuids - The collection scope UUIDs to filter by
|
|
2049
|
+
* @param params.propertyVariableUuids - The property variable UUIDs to query by
|
|
2050
|
+
* @param params.propertyValues - The property values to query by
|
|
2051
|
+
* @param params.page - The page number (1-indexed)
|
|
2052
|
+
* @param params.pageSize - The number of items per page
|
|
2053
|
+
* @param params.itemCategory - The category of the items to fetch
|
|
2054
|
+
* @param params.includeChildItems - Whether to include child items of the same category
|
|
2055
|
+
* @param categoryParams - The category parameters for the fetch
|
|
2056
|
+
* @param categoryParams.category - The category of the items to fetch
|
|
2057
|
+
* @param categoryParams.itemCategories - The categories of the items to fetch
|
|
2058
|
+
* @param options - Options for the fetch
|
|
2059
|
+
* @param options.fetch - The fetch function to use
|
|
2060
|
+
* @param options.version - The version of the OCHRE API to use
|
|
2061
|
+
* @returns The parsed items by property values or null if the fetch/parse fails
|
|
2062
|
+
*/
|
|
2063
|
+
async function fetchItemsByPropertyValues(params, categoryParams, options) {
|
|
2064
|
+
try {
|
|
2065
|
+
const version = options?.version ?? DEFAULT_API_VERSION;
|
|
2066
|
+
const { propertyVariableUuids, propertyValues, projectScopeUuid, belongsToCollectionScopeUuids, page, pageSize = DEFAULT_PAGE_SIZE, itemCategory, includeChildItems } = params;
|
|
2067
|
+
const { category, itemCategories } = categoryParams ?? {};
|
|
2068
|
+
const xquery = buildXQuery$1({
|
|
2069
|
+
projectScopeUuid,
|
|
2070
|
+
belongsToCollectionScopeUuids,
|
|
2071
|
+
propertyVariableUuids,
|
|
2072
|
+
propertyValues,
|
|
2073
|
+
itemCategory,
|
|
2074
|
+
includeChildItems,
|
|
2075
|
+
page,
|
|
2076
|
+
pageSize
|
|
2077
|
+
}, { version });
|
|
2078
|
+
const response = await (options?.fetch ?? fetch)(version === 2 ? `https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?xquery=${encodeURIComponent(xquery)}&format=json&lang="*"` : `https://ochre.lib.uchicago.edu/ochre?xquery=${encodeURIComponent(xquery)}&format=json&lang="*"`);
|
|
2079
|
+
if (!response.ok) throw new Error(`OCHRE API responded with status: ${response.status}`);
|
|
2080
|
+
const data = await response.json();
|
|
2081
|
+
if (Array.isArray(data.result) || Object.keys(data.result.ochre).length === 0) throw new Error("No items found");
|
|
2082
|
+
if (category != null && data.result.ochre.items[category] == null) throw new Error(`No items found for category: ${category}`);
|
|
2083
|
+
if ((category === "set" || category === "tree") && itemCategories != null && "items" in data.result.ochre.items[category] && Array.isArray(data.result.ochre.items[category].items) && data.result.ochre.items[category].items.every((item) => item.category !== itemCategories)) throw new Error(`No items found for category: ${category} and item categories: ${itemCategories}`);
|
|
2084
|
+
const items = [];
|
|
2085
|
+
if ("resource" in data.result.ochre.items && data.result.ochre.items.resource != null) {
|
|
2086
|
+
const resources = parseResources(Array.isArray(data.result.ochre.items.resource) ? data.result.ochre.items.resource : [data.result.ochre.items.resource]);
|
|
2087
|
+
items.push(...resources);
|
|
2088
|
+
}
|
|
2089
|
+
if ("spatialUnit" in data.result.ochre.items && data.result.ochre.items.spatialUnit != null) {
|
|
2090
|
+
const spatialUnits = parseSpatialUnits(Array.isArray(data.result.ochre.items.spatialUnit) ? data.result.ochre.items.spatialUnit : [data.result.ochre.items.spatialUnit]);
|
|
2091
|
+
items.push(...spatialUnits);
|
|
2092
|
+
}
|
|
2093
|
+
if ("concept" in data.result.ochre.items && data.result.ochre.items.concept != null) {
|
|
2094
|
+
const concepts = parseConcepts(Array.isArray(data.result.ochre.items.concept) ? data.result.ochre.items.concept : [data.result.ochre.items.concept]);
|
|
2095
|
+
items.push(...concepts);
|
|
2096
|
+
}
|
|
2097
|
+
if ("period" in data.result.ochre.items && data.result.ochre.items.period != null) {
|
|
2098
|
+
const periods = parsePeriods(Array.isArray(data.result.ochre.items.period) ? data.result.ochre.items.period : [data.result.ochre.items.period]);
|
|
2099
|
+
items.push(...periods);
|
|
2100
|
+
}
|
|
2101
|
+
if ("bibliography" in data.result.ochre.items && data.result.ochre.items.bibliography != null) {
|
|
2102
|
+
const bibliographies = parseBibliographies(Array.isArray(data.result.ochre.items.bibliography) ? data.result.ochre.items.bibliography : [data.result.ochre.items.bibliography]);
|
|
2103
|
+
items.push(...bibliographies);
|
|
2104
|
+
}
|
|
2105
|
+
if ("person" in data.result.ochre.items && data.result.ochre.items.person != null) {
|
|
2106
|
+
const persons = parsePersons(Array.isArray(data.result.ochre.items.person) ? data.result.ochre.items.person : [data.result.ochre.items.person]);
|
|
2107
|
+
items.push(...persons);
|
|
2108
|
+
}
|
|
2109
|
+
if ("propertyVariable" in data.result.ochre.items && data.result.ochre.items.propertyVariable != null) {
|
|
2110
|
+
const propertyVariables = parsePropertyVariables(Array.isArray(data.result.ochre.items.propertyVariable) ? data.result.ochre.items.propertyVariable : [data.result.ochre.items.propertyVariable]);
|
|
2111
|
+
items.push(...propertyVariables);
|
|
2112
|
+
}
|
|
2113
|
+
if ("propertyValue" in data.result.ochre.items && data.result.ochre.items.propertyValue != null) {
|
|
2114
|
+
const propertyValues = parsePropertyValues(Array.isArray(data.result.ochre.items.propertyValue) ? data.result.ochre.items.propertyValue : [data.result.ochre.items.propertyValue]);
|
|
2115
|
+
items.push(...propertyValues);
|
|
2116
|
+
}
|
|
2117
|
+
if ("text" in data.result.ochre.items && data.result.ochre.items.text != null) {
|
|
2118
|
+
const texts = parseTexts(Array.isArray(data.result.ochre.items.text) ? data.result.ochre.items.text : [data.result.ochre.items.text]);
|
|
2119
|
+
items.push(...texts);
|
|
2120
|
+
}
|
|
2121
|
+
if ("set" in data.result.ochre.items && data.result.ochre.items.set != null) {
|
|
2122
|
+
const sets = parseSets(Array.isArray(data.result.ochre.items.set) ? data.result.ochre.items.set : [data.result.ochre.items.set]);
|
|
2123
|
+
items.push(...sets);
|
|
2124
|
+
}
|
|
2125
|
+
if ("tree" in data.result.ochre.items && data.result.ochre.items.tree != null) {
|
|
2126
|
+
const trees = parseTrees(Array.isArray(data.result.ochre.items.tree) ? data.result.ochre.items.tree : [data.result.ochre.items.tree]);
|
|
2127
|
+
items.push(...trees);
|
|
2128
|
+
}
|
|
2129
|
+
return {
|
|
2130
|
+
totalCount: data.result.ochre.items.totalCount,
|
|
2131
|
+
page: data.result.ochre.items.page,
|
|
2132
|
+
pageSize: data.result.ochre.items.pageSize,
|
|
2133
|
+
items,
|
|
2134
|
+
error: null
|
|
2135
|
+
};
|
|
2136
|
+
} catch (error) {
|
|
2137
|
+
console.error(error);
|
|
2138
|
+
return {
|
|
2139
|
+
totalCount: null,
|
|
2140
|
+
page: null,
|
|
2141
|
+
pageSize: null,
|
|
2142
|
+
items: null,
|
|
2143
|
+
error: error instanceof Error ? error.message : "Failed to fetch items by property values"
|
|
2144
|
+
};
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
//#endregion
|
|
2149
|
+
//#region src/utils/fetchers/property-values-by-property-variables.ts
|
|
2150
|
+
/**
|
|
2151
|
+
* Schema for a single property value query item in the OCHRE API response
|
|
2152
|
+
*/
|
|
2153
|
+
const propertyValueQueryItemSchema = z.object({
|
|
2154
|
+
uuid: z.string(),
|
|
2155
|
+
itemUuid: z.string().optional(),
|
|
2156
|
+
dataType: z.string(),
|
|
2157
|
+
rawValue: z.union([
|
|
2158
|
+
z.string(),
|
|
2159
|
+
z.number(),
|
|
2160
|
+
z.boolean()
|
|
2161
|
+
]).optional(),
|
|
2162
|
+
content: z.union([
|
|
2163
|
+
z.string(),
|
|
2164
|
+
z.number(),
|
|
2165
|
+
z.boolean(),
|
|
2166
|
+
richTextStringSchema,
|
|
2167
|
+
z.array(richTextStringSchema)
|
|
2168
|
+
]).optional()
|
|
2169
|
+
}).transform((val) => {
|
|
2170
|
+
const returnValue = {
|
|
2171
|
+
itemUuid: val.itemUuid != null && val.itemUuid !== "" ? val.itemUuid : null,
|
|
2172
|
+
dataType: val.dataType,
|
|
2173
|
+
content: null,
|
|
2174
|
+
label: null
|
|
2175
|
+
};
|
|
2176
|
+
switch (val.dataType) {
|
|
2177
|
+
case "IDREF":
|
|
2178
|
+
returnValue.content = val.uuid !== "" ? val.uuid : null;
|
|
2179
|
+
returnValue.label = val.content != null && val.content !== "" ? typeof val.content === "object" ? parseStringContent({ content: val.content }) : parseFakeString(val.content) : null;
|
|
2180
|
+
break;
|
|
2181
|
+
case "integer":
|
|
2182
|
+
case "decimal":
|
|
2183
|
+
case "time":
|
|
2184
|
+
returnValue.content = val.rawValue != null && val.rawValue !== "" ? Number(val.rawValue) : null;
|
|
2185
|
+
returnValue.label = val.content != null && val.content !== "" ? val.content.toString() : null;
|
|
2186
|
+
break;
|
|
2187
|
+
case "boolean":
|
|
2188
|
+
returnValue.content = val.rawValue != null && val.rawValue !== "" ? Boolean(val.rawValue) : null;
|
|
2189
|
+
returnValue.label = val.content != null && val.content !== "" ? val.content.toString() : null;
|
|
2190
|
+
break;
|
|
2191
|
+
default:
|
|
2192
|
+
returnValue.content = val.rawValue != null && val.rawValue !== "" ? val.rawValue.toString() : null;
|
|
2193
|
+
returnValue.label = val.content != null && val.content !== "" ? val.content.toString() : null;
|
|
2194
|
+
break;
|
|
2195
|
+
}
|
|
2196
|
+
return returnValue;
|
|
2197
|
+
});
|
|
2198
|
+
/**
|
|
2199
|
+
* Schema for the property values by property variables OCHRE API response
|
|
2200
|
+
*/
|
|
2201
|
+
const responseSchema = z.object({ result: z.union([z.object({ ochre: z.object({ propertyValue: z.union([propertyValueQueryItemSchema, z.array(propertyValueQueryItemSchema)]) }) }), z.array(z.unknown()).length(0)]) });
|
|
2202
|
+
/**
|
|
2203
|
+
* Build an XQuery string to fetch property values by property variables from the OCHRE API
|
|
2204
|
+
* @param params - The parameters for the fetch
|
|
2205
|
+
* @param params.projectScopeUuid - The UUID of the project scope
|
|
2206
|
+
* @param params.belongsToCollectionScopeUuids - An array of collection scope UUIDs to filter by
|
|
2207
|
+
* @param params.propertyVariableUuids - An array of property variable UUIDs to fetch
|
|
2208
|
+
* @param options - Options for the fetch
|
|
2209
|
+
* @param options.version - The version of the OCHRE API to use
|
|
2210
|
+
* @returns An XQuery string
|
|
2211
|
+
*/
|
|
2212
|
+
function buildXQuery(params, options) {
|
|
2213
|
+
const version = options?.version ?? DEFAULT_API_VERSION;
|
|
2214
|
+
const { projectScopeUuid, belongsToCollectionScopeUuids, propertyVariableUuids } = params;
|
|
2215
|
+
let collectionScopeFilter = "";
|
|
2216
|
+
if (belongsToCollectionScopeUuids.length > 0) collectionScopeFilter = `//properties[property[label/@uuid="${BELONGS_TO_COLLECTION_UUID}" and value/(${belongsToCollectionScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ")})]]`;
|
|
2217
|
+
const propertyVariableFilters = propertyVariableUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
|
|
2218
|
+
return `<ochre>{${`let $matching-props := ${version === 2 ? "doc()" : "input()"}/ochre[@uuidBelongsTo="${projectScopeUuid}"]
|
|
2219
|
+
${collectionScopeFilter}
|
|
2220
|
+
/*[not(self::set)]
|
|
2221
|
+
//property[label/(${propertyVariableFilters})]
|
|
2222
|
+
|
|
2223
|
+
for $v in $matching-props/value
|
|
2224
|
+
let $item-uuid := $v/ancestor::*[parent::ochre]/@uuid
|
|
2225
|
+
return <propertyValue uuid="{$v/@uuid}" rawValue="{$v/@rawValue}" dataType="{$v/@dataType}" itemUuid="{$item-uuid}">{
|
|
2226
|
+
if ($v/content) then $v/content else $v/text()
|
|
2227
|
+
}</propertyValue>`}}</ochre>`;
|
|
2228
|
+
}
|
|
2229
|
+
/**
|
|
2230
|
+
* Fetches and parses property values by property variables from the OCHRE API
|
|
2231
|
+
*
|
|
2232
|
+
* @param params - The parameters for the fetch
|
|
2233
|
+
* @param params.projectScopeUuid - The UUID of the project scope
|
|
2234
|
+
* @param params.belongsToCollectionScopeUuids - The collection scope UUIDs to filter by
|
|
2235
|
+
* @param params.propertyVariableUuids - The property variable UUIDs to query by
|
|
2236
|
+
* @param options - Options for the fetch
|
|
2237
|
+
* @param options.fetch - The fetch function to use
|
|
2238
|
+
* @param options.version - The version of the OCHRE API to use
|
|
2239
|
+
* @returns The parsed property values by property variables or null if the fetch/parse fails
|
|
2240
|
+
*/
|
|
2241
|
+
async function fetchPropertyValuesByPropertyVariables(params, options) {
|
|
2242
|
+
try {
|
|
2243
|
+
const version = options?.version ?? DEFAULT_API_VERSION;
|
|
2244
|
+
const { belongsToCollectionScopeUuids, propertyVariableUuids, projectScopeUuid } = params;
|
|
2245
|
+
const xquery = buildXQuery({
|
|
2246
|
+
projectScopeUuid,
|
|
2247
|
+
belongsToCollectionScopeUuids,
|
|
2248
|
+
propertyVariableUuids
|
|
2249
|
+
}, { version });
|
|
2250
|
+
const response = await (options?.fetch ?? fetch)(version === 2 ? `https://ochre.lib.uchicago.edu/ochre/v2/ochre.php?xquery=${encodeURIComponent(xquery)}&format=json&lang="*"` : `https://ochre.lib.uchicago.edu/ochre?xquery=${encodeURIComponent(xquery)}&format=json&lang="*"`);
|
|
2251
|
+
if (!response.ok) throw new Error(`OCHRE API responded with status: ${response.status}`);
|
|
2252
|
+
const data = await response.json();
|
|
2253
|
+
const parsedResultRaw = responseSchema.parse(data);
|
|
2254
|
+
if (Array.isArray(parsedResultRaw.result)) throw new TypeError("No items found");
|
|
2255
|
+
const parsedItems = Array.isArray(parsedResultRaw.result.ochre.propertyValue) ? parsedResultRaw.result.ochre.propertyValue : [parsedResultRaw.result.ochre.propertyValue];
|
|
2256
|
+
const groupedItemsMap = /* @__PURE__ */ new Map();
|
|
2257
|
+
for (const item of parsedItems) {
|
|
2258
|
+
const existing = groupedItemsMap.get(item.content);
|
|
2259
|
+
if (existing == null) groupedItemsMap.set(item.content, {
|
|
2260
|
+
dataType: item.dataType,
|
|
2261
|
+
content: item.content,
|
|
2262
|
+
label: item.label,
|
|
2263
|
+
itemUuids: new Set([item.itemUuid])
|
|
2264
|
+
});
|
|
2265
|
+
else existing.itemUuids.add(item.itemUuid);
|
|
2266
|
+
}
|
|
2267
|
+
const groupedItems = [];
|
|
2268
|
+
for (const group of groupedItemsMap.values()) groupedItems.push({
|
|
2269
|
+
count: group.itemUuids.size,
|
|
2270
|
+
dataType: group.dataType,
|
|
2271
|
+
content: group.content,
|
|
2272
|
+
label: group.label
|
|
2273
|
+
});
|
|
2274
|
+
return {
|
|
2275
|
+
items: groupedItems.toSorted((a, b) => {
|
|
2276
|
+
if (a.count !== b.count) return b.count - a.count;
|
|
2277
|
+
if (a.label !== b.label) return a.label?.localeCompare(b.label ?? "") ?? 0;
|
|
2278
|
+
return a.content?.toString().localeCompare(b.content?.toString() ?? "") ?? 0;
|
|
2279
|
+
}),
|
|
2280
|
+
error: null
|
|
2281
|
+
};
|
|
2282
|
+
} catch (error) {
|
|
2283
|
+
console.error(error);
|
|
2284
|
+
return {
|
|
2285
|
+
items: null,
|
|
2286
|
+
error: error instanceof Error ? error.message : "Failed to fetch property values by property variables"
|
|
2287
|
+
};
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
//#endregion
|
|
2292
|
+
//#region src/utils/getters.ts
|
|
2293
|
+
const DEFAULT_OPTIONS = { includeNestedProperties: false };
|
|
2294
|
+
/**
|
|
2295
|
+
* Finds a property by its UUID in an array of properties
|
|
2296
|
+
*
|
|
2297
|
+
* @param properties - Array of properties to search through
|
|
2298
|
+
* @param uuid - The UUID to search for
|
|
2299
|
+
* @param options - Search options, including whether to include nested properties
|
|
2300
|
+
* @returns The matching Property object, or null if not found
|
|
2301
|
+
*/
|
|
2302
|
+
function getPropertyByUuid(properties, uuid, options = DEFAULT_OPTIONS) {
|
|
2303
|
+
const { includeNestedProperties } = options;
|
|
2304
|
+
const property = properties.find((property) => property.uuid === uuid);
|
|
2305
|
+
if (property) return property;
|
|
2306
|
+
if (includeNestedProperties) {
|
|
2307
|
+
for (const property of properties) if (property.properties.length > 0) {
|
|
2308
|
+
const nestedResult = getPropertyByUuid(property.properties, uuid, { includeNestedProperties });
|
|
2309
|
+
if (nestedResult) return nestedResult;
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
return null;
|
|
2313
|
+
}
|
|
2314
|
+
/**
|
|
2315
|
+
* Retrieves all values for a property with the given UUID
|
|
2316
|
+
*
|
|
2317
|
+
* @param properties - Array of properties to search through
|
|
2318
|
+
* @param uuid - The UUID to search for
|
|
2319
|
+
* @param options - Search options, including whether to include nested properties
|
|
2320
|
+
* @returns Array of property values as strings, or null if property not found
|
|
2321
|
+
*/
|
|
2322
|
+
function getPropertyValuesByUuid(properties, uuid, options = DEFAULT_OPTIONS) {
|
|
2323
|
+
const { includeNestedProperties } = options;
|
|
2324
|
+
const property = properties.find((property) => property.uuid === uuid);
|
|
2325
|
+
if (property) return property.values.map((value) => value.content);
|
|
2326
|
+
if (includeNestedProperties) {
|
|
2327
|
+
for (const property of properties) if (property.properties.length > 0) {
|
|
2328
|
+
const nestedResult = getPropertyValuesByUuid(property.properties, uuid, { includeNestedProperties });
|
|
2329
|
+
if (nestedResult) return nestedResult;
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
return null;
|
|
2333
|
+
}
|
|
2334
|
+
/**
|
|
2335
|
+
* Gets the first value of a property with the given UUID
|
|
2336
|
+
*
|
|
2337
|
+
* @param properties - Array of properties to search through
|
|
2338
|
+
* @param uuid - The UUID to search for
|
|
2339
|
+
* @param options - Search options, including whether to include nested properties
|
|
2340
|
+
* @returns The first property value as string, or null if property not found
|
|
2341
|
+
*/
|
|
2342
|
+
function getPropertyValueByUuid(properties, uuid, options = DEFAULT_OPTIONS) {
|
|
2343
|
+
const { includeNestedProperties } = options;
|
|
2344
|
+
const values = getPropertyValuesByUuid(properties, uuid, { includeNestedProperties });
|
|
2345
|
+
if (values !== null && values.length > 0) return values[0];
|
|
2346
|
+
if (includeNestedProperties) {
|
|
2347
|
+
for (const property of properties) if (property.properties.length > 0) {
|
|
2348
|
+
const nestedResult = getPropertyValueByUuid(property.properties, uuid, { includeNestedProperties });
|
|
2349
|
+
if (nestedResult !== null) return nestedResult;
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
return null;
|
|
2353
|
+
}
|
|
2354
|
+
/**
|
|
2355
|
+
* Finds a property by its label in an array of properties
|
|
2356
|
+
*
|
|
2357
|
+
* @param properties - Array of properties to search through
|
|
2358
|
+
* @param label - The label to search for
|
|
2359
|
+
* @param options - Search options, including whether to include nested properties
|
|
2360
|
+
* @returns The matching Property object, or null if not found
|
|
2361
|
+
*/
|
|
2362
|
+
function getPropertyByLabel(properties, label, options = DEFAULT_OPTIONS) {
|
|
2363
|
+
const { includeNestedProperties } = options;
|
|
2364
|
+
const property = properties.find((property) => property.label === label);
|
|
2365
|
+
if (property) return property;
|
|
2366
|
+
if (includeNestedProperties) {
|
|
2367
|
+
for (const property of properties) if (property.properties.length > 0) {
|
|
2368
|
+
const nestedResult = getPropertyByLabel(property.properties, label, { includeNestedProperties });
|
|
2369
|
+
if (nestedResult) return nestedResult;
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
return null;
|
|
2373
|
+
}
|
|
2374
|
+
/**
|
|
2375
|
+
* Finds a property by its label and all values in an array of properties
|
|
2376
|
+
*
|
|
2377
|
+
* @param properties - Array of properties to search through
|
|
2378
|
+
* @param label - The label to search for
|
|
2379
|
+
* @param values - The values to search for
|
|
2380
|
+
* @param options - Search options, including whether to include nested properties
|
|
2381
|
+
* @returns The matching Property object, or null if not found or all values do not match
|
|
2382
|
+
*/
|
|
2383
|
+
function getPropertyByLabelAndValues(properties, label, values, options = DEFAULT_OPTIONS) {
|
|
2384
|
+
const { includeNestedProperties } = options;
|
|
2385
|
+
const property = properties.find((property) => property.label === label && deepEqual(property.values, values));
|
|
2386
|
+
if (property) return property;
|
|
2387
|
+
if (includeNestedProperties) {
|
|
2388
|
+
for (const property of properties) if (property.properties.length > 0) {
|
|
2389
|
+
const nestedResult = getPropertyByLabelAndValues(property.properties, label, values, { includeNestedProperties });
|
|
2390
|
+
if (nestedResult) return nestedResult;
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
return null;
|
|
2394
|
+
}
|
|
2395
|
+
/**
|
|
2396
|
+
* Finds a property by its label and value in an array of properties
|
|
2397
|
+
*
|
|
2398
|
+
* @param properties - Array of properties to search through
|
|
2399
|
+
* @param label - The label to search for
|
|
2400
|
+
* @param value - The value to search for
|
|
2401
|
+
* @param options - Search options, including whether to include nested properties
|
|
2402
|
+
* @returns The matching Property object, or null if not found or value does not match
|
|
2403
|
+
*/
|
|
2404
|
+
function getPropertyByLabelAndValue(properties, label, value, options = DEFAULT_OPTIONS) {
|
|
2405
|
+
const { includeNestedProperties } = options;
|
|
2406
|
+
const property = properties.find((property) => property.label === label && property.values.some((v) => v.content === value));
|
|
2407
|
+
if (property) return property;
|
|
2408
|
+
if (includeNestedProperties) {
|
|
2409
|
+
for (const property of properties) if (property.properties.length > 0) {
|
|
2410
|
+
const nestedResult = getPropertyByLabelAndValue(property.properties, label, value, { includeNestedProperties });
|
|
2411
|
+
if (nestedResult) return nestedResult;
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
return null;
|
|
2415
|
+
}
|
|
2416
|
+
/**
|
|
2417
|
+
* Retrieves all values for a property with the given label
|
|
2418
|
+
*
|
|
2419
|
+
* @param properties - Array of properties to search through
|
|
2420
|
+
* @param label - The label to search for
|
|
2421
|
+
* @param options - Search options, including whether to include nested properties
|
|
2422
|
+
* @returns Array of property values as strings, or null if property not found
|
|
2423
|
+
*/
|
|
2424
|
+
function getPropertyValuesByLabel(properties, label, options = DEFAULT_OPTIONS) {
|
|
2425
|
+
const { includeNestedProperties } = options;
|
|
2426
|
+
const property = properties.find((property) => property.label === label);
|
|
2427
|
+
if (property) return property.values.map((value) => value.content);
|
|
2428
|
+
if (includeNestedProperties) {
|
|
2429
|
+
for (const property of properties) if (property.properties.length > 0) {
|
|
2430
|
+
const nestedResult = getPropertyValuesByLabel(property.properties, label, { includeNestedProperties });
|
|
2431
|
+
if (nestedResult) return nestedResult;
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
return null;
|
|
2435
|
+
}
|
|
2436
|
+
/**
|
|
2437
|
+
* Gets the first value of a property with the given label
|
|
2438
|
+
*
|
|
2439
|
+
* @param properties - Array of properties to search through
|
|
2440
|
+
* @param label - The label to search for
|
|
2441
|
+
* @param options - Search options, including whether to include nested properties
|
|
2442
|
+
* @returns The first property value as string, or null if property not found
|
|
2443
|
+
*/
|
|
2444
|
+
function getPropertyValueByLabel(properties, label, options = DEFAULT_OPTIONS) {
|
|
2445
|
+
const { includeNestedProperties } = options;
|
|
2446
|
+
const values = getPropertyValuesByLabel(properties, label, { includeNestedProperties });
|
|
2447
|
+
if (values !== null && values.length > 0) return values[0];
|
|
2448
|
+
if (includeNestedProperties) {
|
|
2449
|
+
for (const property of properties) if (property.properties.length > 0) {
|
|
2450
|
+
const nestedResult = getPropertyValueByLabel(property.properties, label, { includeNestedProperties });
|
|
2451
|
+
if (nestedResult !== null) return nestedResult;
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
return null;
|
|
2455
|
+
}
|
|
2456
|
+
/**
|
|
2457
|
+
* Gets all unique properties from an array of properties
|
|
2458
|
+
*
|
|
2459
|
+
* @param properties - Array of properties to get unique properties from
|
|
2460
|
+
* @param options - Search options, including whether to include nested properties
|
|
2461
|
+
* @returns Array of unique properties
|
|
2462
|
+
*/
|
|
2463
|
+
function getUniqueProperties(properties, options = DEFAULT_OPTIONS) {
|
|
2464
|
+
const { includeNestedProperties } = options;
|
|
2465
|
+
const uniqueProperties = new Array();
|
|
2466
|
+
for (const property of properties) {
|
|
2467
|
+
if (uniqueProperties.some((p) => p.uuid === property.uuid)) continue;
|
|
2468
|
+
uniqueProperties.push(property);
|
|
2469
|
+
if (property.properties.length > 0 && includeNestedProperties) {
|
|
2470
|
+
const nestedProperties = getUniqueProperties(property.properties, { includeNestedProperties: true });
|
|
2471
|
+
for (const property of nestedProperties) {
|
|
2472
|
+
if (uniqueProperties.some((p) => p.uuid === property.uuid)) continue;
|
|
2473
|
+
uniqueProperties.push(property);
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
return uniqueProperties;
|
|
2478
|
+
}
|
|
2479
|
+
/**
|
|
2480
|
+
* Gets all unique property labels from an array of properties
|
|
2481
|
+
*
|
|
2482
|
+
* @param properties - Array of properties to get unique property labels from
|
|
2483
|
+
* @param options - Search options, including whether to include nested properties
|
|
2484
|
+
* @returns Array of unique property labels
|
|
2485
|
+
*/
|
|
2486
|
+
function getUniquePropertyLabels(properties, options = DEFAULT_OPTIONS) {
|
|
2487
|
+
const { includeNestedProperties } = options;
|
|
2488
|
+
const uniquePropertyLabels = new Array();
|
|
2489
|
+
for (const property of properties) {
|
|
2490
|
+
if (uniquePropertyLabels.includes(property.label)) continue;
|
|
2491
|
+
uniquePropertyLabels.push(property.label);
|
|
2492
|
+
if (property.properties.length > 0 && includeNestedProperties) {
|
|
2493
|
+
const nestedProperties = getUniquePropertyLabels(property.properties, { includeNestedProperties: true });
|
|
2494
|
+
for (const property of nestedProperties) {
|
|
2495
|
+
if (uniquePropertyLabels.includes(property)) continue;
|
|
2496
|
+
uniquePropertyLabels.push(property);
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
return uniquePropertyLabels;
|
|
2501
|
+
}
|
|
2502
|
+
/**
|
|
2503
|
+
* Get the leaf property values from an array of property values
|
|
2504
|
+
* @param propertyValues - The array of property values to get the leaf property values from
|
|
2505
|
+
* @returns The array of leaf property values
|
|
2506
|
+
*/
|
|
2507
|
+
function getLeafPropertyValues(propertyValues) {
|
|
2508
|
+
return propertyValues.filter((value) => value.hierarchy.isLeaf);
|
|
2509
|
+
}
|
|
2510
|
+
/**
|
|
2511
|
+
* Filters a property based on a label and value criteria
|
|
2512
|
+
*
|
|
2513
|
+
* @param property - The property to filter
|
|
2514
|
+
* @param filter - Filter criteria containing label and value to match
|
|
2515
|
+
* @param filter.label - The label to filter by
|
|
2516
|
+
* @param filter.value - The value to filter by
|
|
2517
|
+
* @param options - Search options, including whether to include nested properties
|
|
2518
|
+
* @returns True if the property matches the filter criteria, false otherwise
|
|
2519
|
+
*/
|
|
2520
|
+
function filterProperties(property, filter, options = DEFAULT_OPTIONS) {
|
|
2521
|
+
const { includeNestedProperties } = options;
|
|
2522
|
+
if (filter.label.toLocaleLowerCase("en-US") === "all fields" || property.label.toLocaleLowerCase("en-US") === filter.label.toLocaleLowerCase("en-US")) {
|
|
2523
|
+
let isFound = property.values.some((value) => {
|
|
2524
|
+
if (value.content === null) return false;
|
|
2525
|
+
if (typeof value.content === "string") {
|
|
2526
|
+
if (typeof filter.value !== "string") return false;
|
|
2527
|
+
return value.content.toLocaleLowerCase("en-US").includes(filter.value.toLocaleLowerCase("en-US"));
|
|
2528
|
+
}
|
|
2529
|
+
if (typeof value.content === "number") {
|
|
2530
|
+
if (typeof filter.value !== "number") return false;
|
|
2531
|
+
return value.content === filter.value;
|
|
2532
|
+
}
|
|
2533
|
+
if (typeof value.content === "boolean") {
|
|
2534
|
+
if (typeof filter.value !== "boolean") return false;
|
|
2535
|
+
return value.content === filter.value;
|
|
2536
|
+
}
|
|
2537
|
+
return false;
|
|
2538
|
+
});
|
|
2539
|
+
if (!isFound && includeNestedProperties) isFound = property.properties.some((property) => filterProperties(property, filter, { includeNestedProperties: true }));
|
|
2540
|
+
return isFound;
|
|
2541
|
+
}
|
|
2542
|
+
return false;
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
//#endregion
|
|
2546
|
+
//#region src/utils/parse/website.ts
|
|
2547
|
+
/**
|
|
2548
|
+
* Extracts CSS style properties for a given presentation variant.
|
|
2549
|
+
*
|
|
2550
|
+
* @param properties - Array of properties to parse
|
|
2551
|
+
* @param cssVariant - CSS variant to parse
|
|
2552
|
+
* @returns Array of CSS styles
|
|
2553
|
+
*/
|
|
2554
|
+
function parseCssStylesFromProperties(properties, cssVariant) {
|
|
2555
|
+
const cssProperties = getPropertyByLabelAndValue(properties, "presentation", cssVariant != null ? `css-${cssVariant}` : "css")?.properties ?? [];
|
|
2556
|
+
const styles = [];
|
|
2557
|
+
for (const property of cssProperties) {
|
|
2558
|
+
const value = property.values[0]?.content?.toString();
|
|
2559
|
+
if (value != null) styles.push({
|
|
2560
|
+
label: property.label,
|
|
2561
|
+
value
|
|
2562
|
+
});
|
|
2563
|
+
}
|
|
2564
|
+
return styles;
|
|
2565
|
+
}
|
|
2566
|
+
/**
|
|
2567
|
+
* Parses responsive CSS styles (default, tablet, mobile) from properties.
|
|
2568
|
+
*
|
|
2569
|
+
* @param properties - Array of properties to parse
|
|
2570
|
+
* @returns Object containing responsive CSS styles
|
|
2571
|
+
*/
|
|
2572
|
+
function parseResponsiveCssStyles(properties) {
|
|
2573
|
+
return {
|
|
2574
|
+
default: parseCssStylesFromProperties(properties),
|
|
2575
|
+
tablet: parseCssStylesFromProperties(properties, "tablet"),
|
|
2576
|
+
mobile: parseCssStylesFromProperties(properties, "mobile")
|
|
2577
|
+
};
|
|
2578
|
+
}
|
|
2579
|
+
/**
|
|
2580
|
+
* Parses raw bounds data into a standardized bounds structure
|
|
2581
|
+
*
|
|
2582
|
+
* @param bounds - Raw bounds data in OCHRE format
|
|
2583
|
+
* @returns Parsed bounds object
|
|
2584
|
+
*/
|
|
2585
|
+
function parseBounds(bounds) {
|
|
2586
|
+
const result = boundsSchema.safeParse(bounds);
|
|
2587
|
+
if (!result.success) throw new Error(`Invalid bounds: ${result.error.message}`);
|
|
2588
|
+
return result.data;
|
|
2589
|
+
}
|
|
2590
|
+
/**
|
|
2591
|
+
* Parses all context option arrays from an options object.
|
|
2592
|
+
*
|
|
2593
|
+
* @param options - Options object containing context options
|
|
2594
|
+
* @param options.flattenContexts - Flatten contexts
|
|
2595
|
+
* @param options.suppressContexts - Suppress contexts
|
|
2596
|
+
* @param options.filterContexts - Filter contexts
|
|
2597
|
+
* @param options.sortContexts - Sort contexts
|
|
2598
|
+
* @param options.detailContexts - Detail contexts
|
|
2599
|
+
* @param options.downloadContexts - Download contexts
|
|
2600
|
+
* @param options.labelContexts - Label contexts
|
|
2601
|
+
* @param options.prominentContexts - Prominent contexts
|
|
2602
|
+
* @returns Parsed context options
|
|
2603
|
+
*/
|
|
2604
|
+
function parseAllOptionContexts(options) {
|
|
2605
|
+
function handleContexts(v) {
|
|
2606
|
+
return parseContexts(v != null ? ensureArray(v) : []);
|
|
2607
|
+
}
|
|
2608
|
+
return {
|
|
2609
|
+
flatten: handleContexts(options.flattenContexts),
|
|
2610
|
+
suppress: handleContexts(options.suppressContexts),
|
|
2611
|
+
filter: handleContexts(options.filterContexts),
|
|
2612
|
+
sort: handleContexts(options.sortContexts),
|
|
2613
|
+
detail: handleContexts(options.detailContexts),
|
|
2614
|
+
download: handleContexts(options.downloadContexts),
|
|
2615
|
+
label: handleContexts(options.labelContexts),
|
|
2616
|
+
prominent: handleContexts(options.prominentContexts)
|
|
2617
|
+
};
|
|
2618
|
+
}
|
|
2619
|
+
/**
|
|
2620
|
+
* Parses raw web element properties into a standardized WebElementComponent structure
|
|
2621
|
+
*
|
|
2622
|
+
* @param componentProperty - Raw component property data in OCHRE format
|
|
2623
|
+
* @param elementResource - Raw element resource data in OCHRE format
|
|
2624
|
+
* @returns Parsed WebElementComponent object
|
|
2625
|
+
*/
|
|
2626
|
+
function parseWebElementProperties(componentProperty, elementResource) {
|
|
2627
|
+
const unparsedComponentName = componentProperty.values[0].content;
|
|
2628
|
+
const { data: componentName } = componentSchema.safeParse(unparsedComponentName);
|
|
2629
|
+
let properties = null;
|
|
2630
|
+
const links = elementResource.links ? parseLinks(ensureArray(elementResource.links)) : [];
|
|
2631
|
+
switch (componentName) {
|
|
2632
|
+
case "3d-viewer": {
|
|
2633
|
+
const resourceLink = links.find((link) => link.category === "resource" && link.fileFormat === "model/obj");
|
|
2634
|
+
if (resourceLink?.uuid == null) throw new Error(`Resource link not found for the following component: “${componentName}”`);
|
|
2635
|
+
let isInteractive = getPropertyValueByLabel(componentProperty.properties, "is-interactive");
|
|
2636
|
+
isInteractive ??= true;
|
|
2637
|
+
let isControlsDisplayed = getPropertyValueByLabel(componentProperty.properties, "controls-displayed");
|
|
2638
|
+
isControlsDisplayed ??= true;
|
|
2639
|
+
properties = {
|
|
2640
|
+
component: "3d-viewer",
|
|
2641
|
+
linkUuid: resourceLink.uuid,
|
|
2642
|
+
fileSize: resourceLink.fileSize,
|
|
2643
|
+
isInteractive,
|
|
2644
|
+
isControlsDisplayed
|
|
2645
|
+
};
|
|
2646
|
+
break;
|
|
2647
|
+
}
|
|
2648
|
+
case "advanced-search": {
|
|
2649
|
+
const boundElementPropertyUuid = getPropertyByLabel(componentProperty.properties, "bound-element")?.values[0]?.uuid ?? null;
|
|
2650
|
+
const linkToProperty = getPropertyByLabel(componentProperty.properties, "link-to");
|
|
2651
|
+
const href = linkToProperty?.values[0]?.href ?? linkToProperty?.values[0]?.slug ?? null;
|
|
2652
|
+
if (boundElementPropertyUuid == null && href == null) throw new Error(`Bound element or href not found for the following component: “${componentName}”`);
|
|
2653
|
+
properties = {
|
|
2654
|
+
component: "advanced-search",
|
|
2655
|
+
boundElementUuid: boundElementPropertyUuid,
|
|
2656
|
+
href
|
|
2657
|
+
};
|
|
2658
|
+
break;
|
|
2659
|
+
}
|
|
2660
|
+
case "annotated-document": {
|
|
2661
|
+
const documentLink = links.find((link) => link.type === "internalDocument");
|
|
2662
|
+
if (documentLink?.uuid == null) throw new Error(`Document link not found for the following component: “${componentName}”`);
|
|
2663
|
+
properties = {
|
|
2664
|
+
component: "annotated-document",
|
|
2665
|
+
linkUuid: documentLink.uuid
|
|
2666
|
+
};
|
|
2667
|
+
break;
|
|
2668
|
+
}
|
|
2669
|
+
case "annotated-image": {
|
|
2670
|
+
const imageLinks = links.filter((link) => link.type === "image" || link.type === "IIIF");
|
|
2671
|
+
if (imageLinks.length === 0 || imageLinks[0].uuid == null) throw new Error(`Image link not found for the following component: “${componentName}”`);
|
|
2672
|
+
let isFilterInputDisplayed = getPropertyValueByLabel(componentProperty.properties, "filter-input-displayed");
|
|
2673
|
+
isFilterInputDisplayed ??= true;
|
|
2674
|
+
let isOptionsDisplayed = getPropertyValueByLabel(componentProperty.properties, "options-displayed");
|
|
2675
|
+
isOptionsDisplayed ??= true;
|
|
2676
|
+
let isAnnotationHighlightsDisplayed = getPropertyValueByLabel(componentProperty.properties, "annotation-highlights-displayed");
|
|
2677
|
+
isAnnotationHighlightsDisplayed ??= true;
|
|
2678
|
+
let isAnnotationTooltipsDisplayed = getPropertyValueByLabel(componentProperty.properties, "annotation-tooltips-displayed");
|
|
2679
|
+
isAnnotationTooltipsDisplayed ??= true;
|
|
2680
|
+
properties = {
|
|
2681
|
+
component: "annotated-image",
|
|
2682
|
+
linkUuid: imageLinks[0].uuid,
|
|
2683
|
+
isFilterInputDisplayed,
|
|
2684
|
+
isOptionsDisplayed,
|
|
2685
|
+
isAnnotationHighlightsDisplayed,
|
|
2686
|
+
isAnnotationTooltipsDisplayed
|
|
2687
|
+
};
|
|
2688
|
+
break;
|
|
2689
|
+
}
|
|
2690
|
+
case "audio-player": {
|
|
2691
|
+
const audioLink = links.find((link) => link.type === "audio");
|
|
2692
|
+
if (audioLink?.uuid == null) throw new Error(`Audio link not found for the following component: “${componentName}”`);
|
|
2693
|
+
let isSpeedControlsDisplayed = getPropertyValueByLabel(componentProperty.properties, "speed-controls-displayed");
|
|
2694
|
+
isSpeedControlsDisplayed ??= true;
|
|
2695
|
+
let isVolumeControlsDisplayed = getPropertyValueByLabel(componentProperty.properties, "volume-controls-displayed");
|
|
2256
2696
|
isVolumeControlsDisplayed ??= true;
|
|
2257
2697
|
let isSeekBarDisplayed = getPropertyValueByLabel(componentProperty.properties, "seek-bar-displayed");
|
|
2258
2698
|
isSeekBarDisplayed ??= true;
|
|
@@ -2604,1172 +3044,741 @@ function parseWebElementProperties(componentProperty, elementResource) {
|
|
|
2604
3044
|
endIcon
|
|
2605
3045
|
});
|
|
2606
3046
|
}
|
|
2607
|
-
if (queries.length === 0) throw new Error(`No queries found for the following component: “${componentName}”`);
|
|
2608
|
-
const displayedProperties = getPropertyByLabel(componentProperty.properties, "use-property");
|
|
2609
|
-
let itemVariant = getPropertyValueByLabel(componentProperty.properties, "item-variant");
|
|
2610
|
-
itemVariant ??= "detailed";
|
|
2611
|
-
let paginationVariant = getPropertyValueByLabel(componentProperty.properties, "pagination-variant");
|
|
2612
|
-
paginationVariant ??= "default";
|
|
2613
|
-
let layout = getPropertyValueByLabel(componentProperty.properties, "layout");
|
|
2614
|
-
layout ??= "image-start";
|
|
2615
|
-
properties = {
|
|
2616
|
-
component: "query",
|
|
2617
|
-
itemCategory,
|
|
2618
|
-
queries,
|
|
2619
|
-
displayedProperties: displayedProperties?.values.filter((value) => value.uuid !== null).map((value) => ({
|
|
2620
|
-
uuid: value.uuid,
|
|
2621
|
-
label: value.content?.toString() ?? ""
|
|
2622
|
-
})) ?? null,
|
|
2623
|
-
itemVariant,
|
|
2624
|
-
paginationVariant,
|
|
2625
|
-
layout
|
|
2626
|
-
};
|
|
2627
|
-
break;
|
|
2628
|
-
}
|
|
2629
|
-
case "table": {
|
|
2630
|
-
const tableLink = links.find((link) => link.category === "set");
|
|
2631
|
-
if (tableLink?.uuid == null) throw new Error(`Table link not found for the following component: “${componentName}”`);
|
|
2632
|
-
properties = {
|
|
2633
|
-
component: "table",
|
|
2634
|
-
linkUuid: tableLink.uuid
|
|
2635
|
-
};
|
|
2636
|
-
break;
|
|
2637
|
-
}
|
|
2638
|
-
case "search-bar": {
|
|
2639
|
-
let queryVariant = null;
|
|
2640
|
-
queryVariant = getPropertyValueByLabel(componentProperty.properties, "query-variant");
|
|
2641
|
-
queryVariant ??= "submit";
|
|
2642
|
-
const boundElementUuid = getPropertyByLabel(componentProperty.properties, "bound-element")?.values[0]?.uuid ?? null;
|
|
2643
|
-
const linkToProperty = getPropertyByLabel(componentProperty.properties, "link-to");
|
|
2644
|
-
const href = linkToProperty?.values[0]?.href ?? linkToProperty?.values[0]?.slug ?? null;
|
|
2645
|
-
if (!boundElementUuid && !href) throw new Error(`Bound element or href not found for the following component: “${componentName}”`);
|
|
2646
|
-
let placeholder = getPropertyValueByLabel(componentProperty.properties, "placeholder-text");
|
|
2647
|
-
placeholder ??= null;
|
|
2648
|
-
let baseFilterQueries = getPropertyValueByLabel(componentProperty.properties, "base-filter-queries");
|
|
2649
|
-
baseFilterQueries ??= null;
|
|
2650
|
-
properties = {
|
|
2651
|
-
component: "search-bar",
|
|
2652
|
-
queryVariant,
|
|
2653
|
-
placeholder,
|
|
2654
|
-
baseFilterQueries: baseFilterQueries?.replaceAll(String.raw`\{`, "{").replaceAll(String.raw`\}`, "}") ?? null,
|
|
2655
|
-
boundElementUuid,
|
|
2656
|
-
href
|
|
2657
|
-
};
|
|
2658
|
-
break;
|
|
2659
|
-
}
|
|
2660
|
-
case "text": {
|
|
2661
|
-
const content = elementResource.document && "content" in elementResource.document ? parseDocument(elementResource.document.content) : null;
|
|
2662
|
-
if (!content) throw new Error(`Content not found for the following component: “${componentName}”`);
|
|
2663
|
-
let variantName = "block";
|
|
2664
|
-
let variant;
|
|
2665
|
-
const variantProperty = getPropertyByLabel(componentProperty.properties, "variant");
|
|
2666
|
-
if (variantProperty !== null) {
|
|
2667
|
-
variantName = variantProperty.values[0].content;
|
|
2668
|
-
if (variantName === "paragraph" || variantName === "label" || variantName === "heading" || variantName === "display") {
|
|
2669
|
-
let size = getPropertyValueByLabel(variantProperty.properties, "size");
|
|
2670
|
-
size ??= "md";
|
|
2671
|
-
variant = {
|
|
2672
|
-
name: variantName,
|
|
2673
|
-
size
|
|
2674
|
-
};
|
|
2675
|
-
} else variant = { name: variantName };
|
|
2676
|
-
} else variant = { name: variantName };
|
|
2677
|
-
let headingLevel = getPropertyValueByLabel(componentProperty.properties, "heading-level");
|
|
2678
|
-
headingLevel ??= null;
|
|
2679
|
-
properties = {
|
|
2680
|
-
component: "text",
|
|
2681
|
-
variant,
|
|
2682
|
-
headingLevel,
|
|
2683
|
-
content
|
|
2684
|
-
};
|
|
2685
|
-
break;
|
|
2686
|
-
}
|
|
2687
|
-
case "timeline": {
|
|
2688
|
-
const timelineLink = links.find((link) => link.category === "tree");
|
|
2689
|
-
if (timelineLink?.uuid == null) throw new Error(`Timeline link not found for the following component: “${componentName}”`);
|
|
2690
|
-
properties = {
|
|
2691
|
-
component: "timeline",
|
|
2692
|
-
linkUuid: timelineLink.uuid
|
|
2693
|
-
};
|
|
2694
|
-
break;
|
|
2695
|
-
}
|
|
2696
|
-
case "video": {
|
|
2697
|
-
const videoLink = links.find((link) => link.type === "video");
|
|
2698
|
-
if (videoLink?.uuid == null) throw new Error(`Video link not found for the following component: “${componentName}”`);
|
|
2699
|
-
let isChaptersDisplayed = getPropertyValueByLabel(componentProperty.properties, "chapters-displayed");
|
|
2700
|
-
isChaptersDisplayed ??= true;
|
|
2701
|
-
properties = {
|
|
2702
|
-
component: "video",
|
|
2703
|
-
linkUuid: videoLink.uuid,
|
|
2704
|
-
isChaptersDisplayed
|
|
2705
|
-
};
|
|
2706
|
-
break;
|
|
2707
|
-
}
|
|
2708
|
-
default:
|
|
2709
|
-
console.warn(`Invalid or non-implemented component name “${unparsedComponentName?.toString() ?? "(unknown)"}” for the following element: “${parseStringContent(elementResource.identification.label)}”`);
|
|
2710
|
-
break;
|
|
2711
|
-
}
|
|
2712
|
-
if (properties === null) throw new Error(`Properties not found for the following component: “${componentName}”`);
|
|
2713
|
-
return properties;
|
|
2714
|
-
}
|
|
2715
|
-
function parseWebTitle(properties, identification, overrides) {
|
|
2716
|
-
const title = {
|
|
2717
|
-
label: identification.label,
|
|
2718
|
-
variant: "default",
|
|
2719
|
-
properties: {
|
|
2720
|
-
isNameDisplayed: overrides?.isNameDisplayed ?? false,
|
|
2721
|
-
isDescriptionDisplayed: overrides?.isDescriptionDisplayed ?? false,
|
|
2722
|
-
isDateDisplayed: overrides?.isDateDisplayed ?? false,
|
|
2723
|
-
isCreatorsDisplayed: overrides?.isCreatorsDisplayed ?? false,
|
|
2724
|
-
isCountDisplayed: overrides?.isCountDisplayed ?? false
|
|
2725
|
-
}
|
|
2726
|
-
};
|
|
2727
|
-
const titleProperties = getPropertyByLabelAndValue(properties, "presentation", "title")?.properties ?? [];
|
|
2728
|
-
if (titleProperties.length > 0) {
|
|
2729
|
-
title.variant = getPropertyValueByLabel(titleProperties, "variant") ?? "default";
|
|
2730
|
-
title.properties.isNameDisplayed = getPropertyValueByLabel(titleProperties, "name-displayed") ?? false;
|
|
2731
|
-
title.properties.isDescriptionDisplayed = getPropertyValueByLabel(titleProperties, "description-displayed") ?? false;
|
|
2732
|
-
title.properties.isDateDisplayed = getPropertyValueByLabel(titleProperties, "date-displayed") ?? false;
|
|
2733
|
-
title.properties.isCreatorsDisplayed = getPropertyValueByLabel(titleProperties, "creators-displayed") ?? false;
|
|
2734
|
-
title.properties.isCountDisplayed = getPropertyValueByLabel(titleProperties, "count-displayed") ?? false;
|
|
2735
|
-
}
|
|
2736
|
-
return title;
|
|
2737
|
-
}
|
|
2738
|
-
/**
|
|
2739
|
-
* Parses raw web element data into a standardized WebElement structure
|
|
2740
|
-
*
|
|
2741
|
-
* @param elementResource - Raw element resource data in OCHRE format
|
|
2742
|
-
* @returns Parsed WebElement object
|
|
2743
|
-
*/
|
|
2744
|
-
function parseWebElement(elementResource) {
|
|
2745
|
-
const identification = parseIdentification(elementResource.identification);
|
|
2746
|
-
const elementProperties = elementResource.properties?.property ? parseProperties(Array.isArray(elementResource.properties.property) ? elementResource.properties.property : [elementResource.properties.property]) : [];
|
|
2747
|
-
const presentationProperty = getPropertyByLabel(elementProperties, "presentation");
|
|
2748
|
-
if (presentationProperty === null) throw new Error(`Presentation property not found for element “${identification.label}”`);
|
|
2749
|
-
const componentProperty = getPropertyByLabel(presentationProperty.properties, "component");
|
|
2750
|
-
if (componentProperty === null) throw new Error(`Component for element “${identification.label}” not found`);
|
|
2751
|
-
const properties = parseWebElementProperties(componentProperty, elementResource);
|
|
2752
|
-
const cssStyles = parseResponsiveCssStyles(elementProperties);
|
|
2753
|
-
const title = parseWebTitle(elementProperties, identification, {
|
|
2754
|
-
isNameDisplayed: properties.component === "annotated-image" || properties.component === "annotated-document" || properties.component === "collection",
|
|
2755
|
-
isCountDisplayed: properties.component === "collection" && properties.variant === "full"
|
|
2756
|
-
});
|
|
2757
|
-
return {
|
|
2758
|
-
uuid: elementResource.uuid,
|
|
2759
|
-
type: "element",
|
|
2760
|
-
title,
|
|
2761
|
-
cssStyles,
|
|
2762
|
-
...properties
|
|
2763
|
-
};
|
|
2764
|
-
}
|
|
2765
|
-
/**
|
|
2766
|
-
* Parses raw webpage data into a standardized Webpage structure
|
|
2767
|
-
*
|
|
2768
|
-
* @param webpageResource - Raw webpage resource data in OCHRE format
|
|
2769
|
-
* @returns Parsed Webpage object
|
|
2770
|
-
*/
|
|
2771
|
-
function parseWebpage(webpageResource, slugPrefix) {
|
|
2772
|
-
const webpageProperties = webpageResource.properties ? parseProperties(ensureArray(webpageResource.properties.property)) : [];
|
|
2773
|
-
if (webpageProperties.length === 0 || getPropertyValueByLabel(webpageProperties, "presentation") !== "page") return null;
|
|
2774
|
-
const identification = parseIdentification(webpageResource.identification);
|
|
2775
|
-
const slug = webpageResource.slug?.replace(/^\$[^-]*-/, "") ?? null;
|
|
2776
|
-
if (slug == null) throw new Error(`Slug not found for page “${identification.label}”`);
|
|
2777
|
-
const returnWebpage = {
|
|
2778
|
-
uuid: webpageResource.uuid,
|
|
2779
|
-
type: "page",
|
|
2780
|
-
title: identification.label,
|
|
2781
|
-
slug: slugPrefix != null ? `${slugPrefix}/${slug}`.replace(/\/$/, "") : slug,
|
|
2782
|
-
publicationDateTime: parseOptionalDate(webpageResource.publicationDateTime),
|
|
2783
|
-
items: [],
|
|
2784
|
-
properties: {
|
|
2785
|
-
width: "default",
|
|
2786
|
-
variant: "default",
|
|
2787
|
-
isBreadcrumbsDisplayed: false,
|
|
2788
|
-
isSidebarDisplayed: true,
|
|
2789
|
-
isDisplayedInNavbar: true,
|
|
2790
|
-
isNavbarSearchBarDisplayed: true,
|
|
2791
|
-
backgroundImage: null,
|
|
2792
|
-
cssStyles: {
|
|
2793
|
-
default: [],
|
|
2794
|
-
tablet: [],
|
|
2795
|
-
mobile: []
|
|
2796
|
-
}
|
|
2797
|
-
},
|
|
2798
|
-
webpages: []
|
|
2799
|
-
};
|
|
2800
|
-
const imageLink = (webpageResource.links != null ? parseLinks(ensureArray(webpageResource.links)) : []).find((link) => link.type === "image" || link.type === "IIIF");
|
|
2801
|
-
const webpageResources = webpageResource.resource != null ? ensureArray(webpageResource.resource) : [];
|
|
2802
|
-
const items = [];
|
|
2803
|
-
for (const resource of webpageResources) {
|
|
2804
|
-
const resourceType = getPropertyValueByLabel(resource.properties != null ? parseProperties(ensureArray(resource.properties.property)) : [], "presentation");
|
|
2805
|
-
if (resourceType === null) continue;
|
|
2806
|
-
switch (resourceType) {
|
|
2807
|
-
case "segment": {
|
|
2808
|
-
const segment = parseWebSegment(resource);
|
|
2809
|
-
if (segment) items.push(segment);
|
|
2810
|
-
break;
|
|
2811
|
-
}
|
|
2812
|
-
case "element": {
|
|
2813
|
-
const element = parseWebElement(resource);
|
|
2814
|
-
items.push(element);
|
|
2815
|
-
break;
|
|
2816
|
-
}
|
|
2817
|
-
case "block": {
|
|
2818
|
-
const block = parseWebBlock(resource);
|
|
2819
|
-
if (block) items.push(block);
|
|
2820
|
-
break;
|
|
2821
|
-
}
|
|
3047
|
+
if (queries.length === 0) throw new Error(`No queries found for the following component: “${componentName}”`);
|
|
3048
|
+
const displayedProperties = getPropertyByLabel(componentProperty.properties, "use-property");
|
|
3049
|
+
let itemVariant = getPropertyValueByLabel(componentProperty.properties, "item-variant");
|
|
3050
|
+
itemVariant ??= "detailed";
|
|
3051
|
+
let paginationVariant = getPropertyValueByLabel(componentProperty.properties, "pagination-variant");
|
|
3052
|
+
paginationVariant ??= "default";
|
|
3053
|
+
let layout = getPropertyValueByLabel(componentProperty.properties, "layout");
|
|
3054
|
+
layout ??= "image-start";
|
|
3055
|
+
properties = {
|
|
3056
|
+
component: "query",
|
|
3057
|
+
itemCategory,
|
|
3058
|
+
queries,
|
|
3059
|
+
displayedProperties: displayedProperties?.values.filter((value) => value.uuid !== null).map((value) => ({
|
|
3060
|
+
uuid: value.uuid,
|
|
3061
|
+
label: value.content?.toString() ?? ""
|
|
3062
|
+
})) ?? null,
|
|
3063
|
+
itemVariant,
|
|
3064
|
+
paginationVariant,
|
|
3065
|
+
layout
|
|
3066
|
+
};
|
|
3067
|
+
break;
|
|
2822
3068
|
}
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
returnWebpage.properties.isBreadcrumbsDisplayed = getPropertyValueByLabel(webpageSubProperties, "breadcrumbs-displayed") ?? false;
|
|
2832
|
-
returnWebpage.properties.isNavbarSearchBarDisplayed = getPropertyValueByLabel(webpageSubProperties, "navbar-search-bar-displayed") ?? true;
|
|
2833
|
-
}
|
|
2834
|
-
if (imageLink?.uuid != null) returnWebpage.properties.backgroundImage = {
|
|
2835
|
-
uuid: imageLink.uuid,
|
|
2836
|
-
label: imageLink.identification?.label ?? null,
|
|
2837
|
-
description: imageLink.description ?? null,
|
|
2838
|
-
width: imageLink.image?.width ?? 0,
|
|
2839
|
-
height: imageLink.image?.height ?? 0,
|
|
2840
|
-
quality: "high"
|
|
2841
|
-
};
|
|
2842
|
-
returnWebpage.properties.cssStyles = parseResponsiveCssStyles(webpageProperties);
|
|
2843
|
-
return returnWebpage;
|
|
2844
|
-
}
|
|
2845
|
-
/**
|
|
2846
|
-
* Parses raw webpage resources into an array of Webpage objects
|
|
2847
|
-
*
|
|
2848
|
-
* @param webpageResources - Array of raw webpage resources in OCHRE format
|
|
2849
|
-
* @returns Array of parsed Webpage objects
|
|
2850
|
-
*/
|
|
2851
|
-
function parseWebpages(webpageResources, slugPrefix) {
|
|
2852
|
-
const returnPages = [];
|
|
2853
|
-
for (const webpageResource of webpageResources) {
|
|
2854
|
-
const webpage = parseWebpage(webpageResource, slugPrefix);
|
|
2855
|
-
if (webpage !== null) returnPages.push(webpage);
|
|
2856
|
-
}
|
|
2857
|
-
return returnPages;
|
|
2858
|
-
}
|
|
2859
|
-
/**
|
|
2860
|
-
* Parses raw segment resource into a standardized WebSegment object
|
|
2861
|
-
*
|
|
2862
|
-
* @param segmentResource - Raw segment resource in OCHRE format
|
|
2863
|
-
* @returns Parsed WebSegment object
|
|
2864
|
-
*/
|
|
2865
|
-
function parseWebSegment(segmentResource, slugPrefix) {
|
|
2866
|
-
const webpageProperties = segmentResource.properties ? parseProperties(ensureArray(segmentResource.properties.property)) : [];
|
|
2867
|
-
if (webpageProperties.length === 0 || getPropertyValueByLabel(webpageProperties, "presentation") !== "segment") return null;
|
|
2868
|
-
const identification = parseIdentification(segmentResource.identification);
|
|
2869
|
-
const slug = segmentResource.identification.abbreviation != null ? parseFakeStringOrContent(segmentResource.identification.abbreviation) : null;
|
|
2870
|
-
if (slug == null) throw new Error(`Slug not found for segment “${identification.label}”`);
|
|
2871
|
-
const returnSegment = {
|
|
2872
|
-
uuid: segmentResource.uuid,
|
|
2873
|
-
type: "segment",
|
|
2874
|
-
title: identification.label,
|
|
2875
|
-
slug,
|
|
2876
|
-
publicationDateTime: parseOptionalDate(segmentResource.publicationDateTime),
|
|
2877
|
-
items: []
|
|
2878
|
-
};
|
|
2879
|
-
returnSegment.items = parseWebSegmentItems(segmentResource.resource ? ensureArray(segmentResource.resource) : [], slugPrefix != null ? `${slugPrefix}/${slug}`.replace(/\/$/, "") : slug);
|
|
2880
|
-
return returnSegment;
|
|
2881
|
-
}
|
|
2882
|
-
/**
|
|
2883
|
-
* Parses raw segment resources into an array of WebSegment objects
|
|
2884
|
-
*
|
|
2885
|
-
* @param segmentResources - Array of raw segment resources in OCHRE format
|
|
2886
|
-
* @returns Array of parsed WebSegment objects
|
|
2887
|
-
*/
|
|
2888
|
-
function parseSegments(segmentResources, slugPrefix) {
|
|
2889
|
-
const returnSegments = [];
|
|
2890
|
-
for (const segmentResource of segmentResources) {
|
|
2891
|
-
const segment = parseWebSegment(segmentResource, slugPrefix);
|
|
2892
|
-
if (segment !== null) returnSegments.push(segment);
|
|
2893
|
-
}
|
|
2894
|
-
return returnSegments;
|
|
2895
|
-
}
|
|
2896
|
-
/**
|
|
2897
|
-
* Parses raw segment item into a standardized WebSegmentItem object
|
|
2898
|
-
*
|
|
2899
|
-
* @param segmentItemResource - Raw segment item resource in OCHRE format
|
|
2900
|
-
* @returns Parsed WebSegmentItem object
|
|
2901
|
-
*/
|
|
2902
|
-
function parseWebSegmentItem(segmentItemResource, slugPrefix) {
|
|
2903
|
-
const webpageProperties = segmentItemResource.properties ? parseProperties(ensureArray(segmentItemResource.properties.property)) : [];
|
|
2904
|
-
if (webpageProperties.length === 0 || getPropertyValueByLabel(webpageProperties, "presentation") !== "segment-item") return null;
|
|
2905
|
-
const identification = parseIdentification(segmentItemResource.identification);
|
|
2906
|
-
const slug = segmentItemResource.identification.abbreviation != null ? parseFakeStringOrContent(segmentItemResource.identification.abbreviation) : null;
|
|
2907
|
-
if (slug == null) throw new Error(`Slug not found for segment item “${identification.label}”`);
|
|
2908
|
-
const returnSegmentItem = {
|
|
2909
|
-
uuid: segmentItemResource.uuid,
|
|
2910
|
-
type: "segment-item",
|
|
2911
|
-
title: identification.label,
|
|
2912
|
-
slug,
|
|
2913
|
-
publicationDateTime: parseOptionalDate(segmentItemResource.publicationDateTime),
|
|
2914
|
-
items: []
|
|
2915
|
-
};
|
|
2916
|
-
const resources = segmentItemResource.resource ? ensureArray(segmentItemResource.resource) : [];
|
|
2917
|
-
returnSegmentItem.items.push(...parseWebpages(resources, slugPrefix != null ? `${slugPrefix}/${slug}`.replace(/\/$/, "") : slug), ...parseSegments(resources, slugPrefix != null ? `${slugPrefix}/${slug}`.replace(/\/$/, "") : slug));
|
|
2918
|
-
return returnSegmentItem;
|
|
2919
|
-
}
|
|
2920
|
-
/**
|
|
2921
|
-
* Parses raw segment items into an array of WebSegmentItem objects
|
|
2922
|
-
*
|
|
2923
|
-
* @param segmentItems - Array of raw segment items in OCHRE format
|
|
2924
|
-
* @returns Array of parsed WebSegmentItem objects
|
|
2925
|
-
*/
|
|
2926
|
-
function parseWebSegmentItems(segmentItems, slugPrefix) {
|
|
2927
|
-
const returnItems = [];
|
|
2928
|
-
for (const segmentItem of segmentItems) {
|
|
2929
|
-
const segmentItemParsed = parseWebSegmentItem(segmentItem, slugPrefix);
|
|
2930
|
-
if (segmentItemParsed !== null) returnItems.push(segmentItemParsed);
|
|
2931
|
-
}
|
|
2932
|
-
return returnItems;
|
|
2933
|
-
}
|
|
2934
|
-
/**
|
|
2935
|
-
* Parses raw sidebar data into a standardized Sidebar structure
|
|
2936
|
-
*
|
|
2937
|
-
* @param resources - Array of raw sidebar resources in OCHRE format
|
|
2938
|
-
* @returns Parsed Sidebar object
|
|
2939
|
-
*/
|
|
2940
|
-
function parseSidebar(resources) {
|
|
2941
|
-
let returnSidebar = null;
|
|
2942
|
-
const items = [];
|
|
2943
|
-
const title = {
|
|
2944
|
-
label: "",
|
|
2945
|
-
variant: "default",
|
|
2946
|
-
properties: {
|
|
2947
|
-
isNameDisplayed: false,
|
|
2948
|
-
isDescriptionDisplayed: false,
|
|
2949
|
-
isDateDisplayed: false,
|
|
2950
|
-
isCreatorsDisplayed: false,
|
|
2951
|
-
isCountDisplayed: false
|
|
3069
|
+
case "table": {
|
|
3070
|
+
const tableLink = links.find((link) => link.category === "set");
|
|
3071
|
+
if (tableLink?.uuid == null) throw new Error(`Table link not found for the following component: “${componentName}”`);
|
|
3072
|
+
properties = {
|
|
3073
|
+
component: "table",
|
|
3074
|
+
linkUuid: tableLink.uuid
|
|
3075
|
+
};
|
|
3076
|
+
break;
|
|
2952
3077
|
}
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
3078
|
+
case "search-bar": {
|
|
3079
|
+
let queryVariant = null;
|
|
3080
|
+
queryVariant = getPropertyValueByLabel(componentProperty.properties, "query-variant");
|
|
3081
|
+
queryVariant ??= "submit";
|
|
3082
|
+
const boundElementUuid = getPropertyByLabel(componentProperty.properties, "bound-element")?.values[0]?.uuid ?? null;
|
|
3083
|
+
const linkToProperty = getPropertyByLabel(componentProperty.properties, "link-to");
|
|
3084
|
+
const href = linkToProperty?.values[0]?.href ?? linkToProperty?.values[0]?.slug ?? null;
|
|
3085
|
+
if (!boundElementUuid && !href) throw new Error(`Bound element or href not found for the following component: “${componentName}”`);
|
|
3086
|
+
let placeholder = getPropertyValueByLabel(componentProperty.properties, "placeholder-text");
|
|
3087
|
+
placeholder ??= null;
|
|
3088
|
+
let baseFilterQueries = getPropertyValueByLabel(componentProperty.properties, "base-filter-queries");
|
|
3089
|
+
baseFilterQueries ??= null;
|
|
3090
|
+
properties = {
|
|
3091
|
+
component: "search-bar",
|
|
3092
|
+
queryVariant,
|
|
3093
|
+
placeholder,
|
|
3094
|
+
baseFilterQueries: baseFilterQueries?.replaceAll(String.raw`\{`, "{").replaceAll(String.raw`\}`, "}") ?? null,
|
|
3095
|
+
boundElementUuid,
|
|
3096
|
+
href
|
|
3097
|
+
};
|
|
3098
|
+
break;
|
|
3099
|
+
}
|
|
3100
|
+
case "text": {
|
|
3101
|
+
const content = elementResource.document && "content" in elementResource.document ? parseDocument(elementResource.document.content) : null;
|
|
3102
|
+
if (!content) throw new Error(`Content not found for the following component: “${componentName}”`);
|
|
3103
|
+
let variantName = "block";
|
|
3104
|
+
let variant;
|
|
3105
|
+
const variantProperty = getPropertyByLabel(componentProperty.properties, "variant");
|
|
3106
|
+
if (variantProperty !== null) {
|
|
3107
|
+
variantName = variantProperty.values[0].content;
|
|
3108
|
+
if (variantName === "paragraph" || variantName === "label" || variantName === "heading" || variantName === "display") {
|
|
3109
|
+
let size = getPropertyValueByLabel(variantProperty.properties, "size");
|
|
3110
|
+
size ??= "md";
|
|
3111
|
+
variant = {
|
|
3112
|
+
name: variantName,
|
|
3113
|
+
size
|
|
3114
|
+
};
|
|
3115
|
+
} else variant = { name: variantName };
|
|
3116
|
+
} else variant = { name: variantName };
|
|
3117
|
+
let headingLevel = getPropertyValueByLabel(componentProperty.properties, "heading-level");
|
|
3118
|
+
headingLevel ??= null;
|
|
3119
|
+
properties = {
|
|
3120
|
+
component: "text",
|
|
3121
|
+
variant,
|
|
3122
|
+
headingLevel,
|
|
3123
|
+
content
|
|
3124
|
+
};
|
|
3125
|
+
break;
|
|
3126
|
+
}
|
|
3127
|
+
case "timeline": {
|
|
3128
|
+
const timelineLink = links.find((link) => link.category === "tree");
|
|
3129
|
+
if (timelineLink?.uuid == null) throw new Error(`Timeline link not found for the following component: “${componentName}”`);
|
|
3130
|
+
properties = {
|
|
3131
|
+
component: "timeline",
|
|
3132
|
+
linkUuid: timelineLink.uuid
|
|
3133
|
+
};
|
|
3134
|
+
break;
|
|
3135
|
+
}
|
|
3136
|
+
case "video": {
|
|
3137
|
+
const videoLink = links.find((link) => link.type === "video");
|
|
3138
|
+
if (videoLink?.uuid == null) throw new Error(`Video link not found for the following component: “${componentName}”`);
|
|
3139
|
+
let isChaptersDisplayed = getPropertyValueByLabel(componentProperty.properties, "chapters-displayed");
|
|
3140
|
+
isChaptersDisplayed ??= true;
|
|
3141
|
+
properties = {
|
|
3142
|
+
component: "video",
|
|
3143
|
+
linkUuid: videoLink.uuid,
|
|
3144
|
+
isChaptersDisplayed
|
|
3145
|
+
};
|
|
3146
|
+
break;
|
|
2986
3147
|
}
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3148
|
+
default:
|
|
3149
|
+
console.warn(`Invalid or non-implemented component name “${unparsedComponentName?.toString() ?? "(unknown)"}” for the following element: “${parseStringContent(elementResource.identification.label)}”`);
|
|
3150
|
+
break;
|
|
3151
|
+
}
|
|
3152
|
+
if (properties === null) throw new Error(`Properties not found for the following component: “${componentName}”`);
|
|
3153
|
+
return properties;
|
|
3154
|
+
}
|
|
3155
|
+
function parseWebTitle(properties, identification, overrides) {
|
|
3156
|
+
const title = {
|
|
3157
|
+
label: identification.label,
|
|
3158
|
+
variant: "default",
|
|
3159
|
+
properties: {
|
|
3160
|
+
isNameDisplayed: overrides?.isNameDisplayed ?? false,
|
|
3161
|
+
isDescriptionDisplayed: overrides?.isDescriptionDisplayed ?? false,
|
|
3162
|
+
isDateDisplayed: overrides?.isDateDisplayed ?? false,
|
|
3163
|
+
isCreatorsDisplayed: overrides?.isCreatorsDisplayed ?? false,
|
|
3164
|
+
isCountDisplayed: overrides?.isCountDisplayed ?? false
|
|
3003
3165
|
}
|
|
3166
|
+
};
|
|
3167
|
+
const titleProperties = getPropertyByLabelAndValue(properties, "presentation", "title")?.properties ?? [];
|
|
3168
|
+
if (titleProperties.length > 0) {
|
|
3169
|
+
title.variant = getPropertyValueByLabel(titleProperties, "variant") ?? "default";
|
|
3170
|
+
title.properties.isNameDisplayed = getPropertyValueByLabel(titleProperties, "name-displayed") ?? false;
|
|
3171
|
+
title.properties.isDescriptionDisplayed = getPropertyValueByLabel(titleProperties, "description-displayed") ?? false;
|
|
3172
|
+
title.properties.isDateDisplayed = getPropertyValueByLabel(titleProperties, "date-displayed") ?? false;
|
|
3173
|
+
title.properties.isCreatorsDisplayed = getPropertyValueByLabel(titleProperties, "creators-displayed") ?? false;
|
|
3174
|
+
title.properties.isCountDisplayed = getPropertyValueByLabel(titleProperties, "count-displayed") ?? false;
|
|
3004
3175
|
}
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3176
|
+
return title;
|
|
3177
|
+
}
|
|
3178
|
+
/**
|
|
3179
|
+
* Parses raw web element data into a standardized WebElement structure
|
|
3180
|
+
*
|
|
3181
|
+
* @param elementResource - Raw element resource data in OCHRE format
|
|
3182
|
+
* @returns Parsed WebElement object
|
|
3183
|
+
*/
|
|
3184
|
+
function parseWebElement(elementResource) {
|
|
3185
|
+
const identification = parseIdentification(elementResource.identification);
|
|
3186
|
+
const elementProperties = elementResource.properties?.property ? parseProperties(Array.isArray(elementResource.properties.property) ? elementResource.properties.property : [elementResource.properties.property]) : [];
|
|
3187
|
+
const presentationProperty = getPropertyByLabel(elementProperties, "presentation");
|
|
3188
|
+
if (presentationProperty === null) throw new Error(`Presentation property not found for element “${identification.label}”`);
|
|
3189
|
+
const componentProperty = getPropertyByLabel(presentationProperty.properties, "component");
|
|
3190
|
+
if (componentProperty === null) throw new Error(`Component for element “${identification.label}” not found`);
|
|
3191
|
+
const properties = parseWebElementProperties(componentProperty, elementResource);
|
|
3192
|
+
const cssStyles = parseResponsiveCssStyles(elementProperties);
|
|
3193
|
+
const title = parseWebTitle(elementProperties, identification, {
|
|
3194
|
+
isNameDisplayed: properties.component === "annotated-image" || properties.component === "annotated-document" || properties.component === "collection",
|
|
3195
|
+
isCountDisplayed: properties.component === "collection" && properties.variant === "full"
|
|
3196
|
+
});
|
|
3197
|
+
return {
|
|
3198
|
+
uuid: elementResource.uuid,
|
|
3199
|
+
type: "element",
|
|
3008
3200
|
title,
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
cssStyles
|
|
3201
|
+
cssStyles,
|
|
3202
|
+
...properties
|
|
3012
3203
|
};
|
|
3013
|
-
return returnSidebar;
|
|
3014
3204
|
}
|
|
3015
3205
|
/**
|
|
3016
|
-
* Parses raw
|
|
3206
|
+
* Parses raw webpage resources into standardized WebElement or Webpage objects
|
|
3017
3207
|
*
|
|
3018
|
-
* @param
|
|
3019
|
-
* @
|
|
3208
|
+
* @param webpageResources - Array of raw webpage resources in OCHRE format
|
|
3209
|
+
* @param type - Type of resource to parse ("element" or "page")
|
|
3210
|
+
* @returns Array of parsed WebElement or Webpage objects
|
|
3020
3211
|
*/
|
|
3021
|
-
|
|
3022
|
-
const
|
|
3023
|
-
const
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
const resourceType = getPropertyValueByLabel(resource.properties ? parseProperties(ensureArray(resource.properties.property)) : [], "presentation");
|
|
3027
|
-
if (resourceType === null) continue;
|
|
3028
|
-
switch (resourceType) {
|
|
3212
|
+
const parseWebpageResources = (webpageResources, type) => {
|
|
3213
|
+
const returnElements = [];
|
|
3214
|
+
for (const resource of webpageResources) {
|
|
3215
|
+
if (getPropertyByLabelAndValue(resource.properties ? parseProperties(ensureArray(resource.properties.property)) : [], "presentation", type) === null) continue;
|
|
3216
|
+
switch (type) {
|
|
3029
3217
|
case "element": {
|
|
3030
3218
|
const element = parseWebElement(resource);
|
|
3031
|
-
|
|
3219
|
+
returnElements.push(element);
|
|
3220
|
+
break;
|
|
3221
|
+
}
|
|
3222
|
+
case "page": {
|
|
3223
|
+
const webpage = parseWebpage(resource);
|
|
3224
|
+
if (webpage) returnElements.push(webpage);
|
|
3032
3225
|
break;
|
|
3033
3226
|
}
|
|
3034
3227
|
case "block": {
|
|
3035
3228
|
const block = parseWebBlock(resource);
|
|
3036
|
-
if (block)
|
|
3229
|
+
if (block) returnElements.push(block);
|
|
3037
3230
|
break;
|
|
3038
3231
|
}
|
|
3039
3232
|
}
|
|
3040
3233
|
}
|
|
3041
|
-
return
|
|
3042
|
-
|
|
3043
|
-
items
|
|
3044
|
-
};
|
|
3045
|
-
}
|
|
3234
|
+
return returnElements;
|
|
3235
|
+
};
|
|
3046
3236
|
/**
|
|
3047
|
-
* Parses raw
|
|
3237
|
+
* Parses raw webpage data into a standardized Webpage structure
|
|
3048
3238
|
*
|
|
3049
|
-
* @param
|
|
3050
|
-
* @returns Parsed
|
|
3239
|
+
* @param webpageResource - Raw webpage resource data in OCHRE format
|
|
3240
|
+
* @returns Parsed Webpage object
|
|
3051
3241
|
*/
|
|
3052
|
-
function
|
|
3053
|
-
const
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3242
|
+
function parseWebpage(webpageResource, slugPrefix) {
|
|
3243
|
+
const webpageProperties = webpageResource.properties ? parseProperties(ensureArray(webpageResource.properties.property)) : [];
|
|
3244
|
+
if (webpageProperties.length === 0 || getPropertyValueByLabel(webpageProperties, "presentation") !== "page") return null;
|
|
3245
|
+
const identification = parseIdentification(webpageResource.identification);
|
|
3246
|
+
const slug = webpageResource.slug?.replace(/^\$[^-]*-/, "") ?? null;
|
|
3247
|
+
if (slug == null) throw new Error(`Slug not found for page “${identification.label}”`);
|
|
3248
|
+
const returnWebpage = {
|
|
3249
|
+
uuid: webpageResource.uuid,
|
|
3250
|
+
type: "page",
|
|
3251
|
+
title: identification.label,
|
|
3252
|
+
slug: slugPrefix != null ? `${slugPrefix}/${slug}`.replace(/\/$/, "") : slug,
|
|
3253
|
+
publicationDateTime: parseOptionalDate(webpageResource.publicationDateTime),
|
|
3058
3254
|
items: [],
|
|
3059
3255
|
properties: {
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3256
|
+
width: "default",
|
|
3257
|
+
variant: "default",
|
|
3258
|
+
isBreadcrumbsDisplayed: false,
|
|
3259
|
+
isSidebarDisplayed: true,
|
|
3260
|
+
isDisplayedInNavbar: true,
|
|
3261
|
+
isNavbarSearchBarDisplayed: true,
|
|
3262
|
+
backgroundImage: null,
|
|
3263
|
+
cssStyles: {
|
|
3264
|
+
default: [],
|
|
3265
|
+
tablet: [],
|
|
3266
|
+
mobile: []
|
|
3267
|
+
}
|
|
3067
3268
|
},
|
|
3068
|
-
|
|
3069
|
-
default: [],
|
|
3070
|
-
tablet: [],
|
|
3071
|
-
mobile: []
|
|
3072
|
-
}
|
|
3269
|
+
webpages: []
|
|
3073
3270
|
};
|
|
3074
|
-
const
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
if (tabletOverwriteProperty !== null) {
|
|
3086
|
-
const tabletOverwriteProperties = tabletOverwriteProperty.properties;
|
|
3087
|
-
const propertiesTablet = {
|
|
3088
|
-
layout: getPropertyValueByLabel(tabletOverwriteProperties, "layout") ?? void 0,
|
|
3089
|
-
spacing: getPropertyValueByLabel(tabletOverwriteProperties, "spacing") ?? void 0,
|
|
3090
|
-
gap: getPropertyValueByLabel(tabletOverwriteProperties, "gap") ?? void 0,
|
|
3091
|
-
isAccordionEnabled: void 0,
|
|
3092
|
-
isAccordionExpandedByDefault: void 0,
|
|
3093
|
-
isAccordionSidebarDisplayed: void 0
|
|
3094
|
-
};
|
|
3095
|
-
if (propertiesTablet.layout === "accordion" || returnBlock.properties.default.layout === "accordion") {
|
|
3096
|
-
propertiesTablet.isAccordionEnabled = getPropertyValueByLabel(tabletOverwriteProperties, "accordion-enabled") ?? void 0;
|
|
3097
|
-
propertiesTablet.isAccordionExpandedByDefault = getPropertyValueByLabel(tabletOverwriteProperties, "accordion-expanded") ?? void 0;
|
|
3098
|
-
propertiesTablet.isAccordionSidebarDisplayed = getPropertyValueByLabel(tabletOverwriteProperties, "accordion-sidebar-displayed") ?? void 0;
|
|
3271
|
+
const imageLink = (webpageResource.links != null ? parseLinks(ensureArray(webpageResource.links)) : []).find((link) => link.type === "image" || link.type === "IIIF");
|
|
3272
|
+
const webpageResources = webpageResource.resource != null ? ensureArray(webpageResource.resource) : [];
|
|
3273
|
+
const items = [];
|
|
3274
|
+
for (const resource of webpageResources) {
|
|
3275
|
+
const resourceType = getPropertyValueByLabel(resource.properties != null ? parseProperties(ensureArray(resource.properties.property)) : [], "presentation");
|
|
3276
|
+
if (resourceType === null) continue;
|
|
3277
|
+
switch (resourceType) {
|
|
3278
|
+
case "segment": {
|
|
3279
|
+
const segment = parseWebSegment(resource);
|
|
3280
|
+
if (segment) items.push(segment);
|
|
3281
|
+
break;
|
|
3099
3282
|
}
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
const mobileOverwriteProperties = mobileOverwriteProperty.properties;
|
|
3105
|
-
const propertiesMobile = {
|
|
3106
|
-
layout: getPropertyValueByLabel(mobileOverwriteProperties, "layout") ?? void 0,
|
|
3107
|
-
spacing: getPropertyValueByLabel(mobileOverwriteProperties, "spacing") ?? void 0,
|
|
3108
|
-
gap: getPropertyValueByLabel(mobileOverwriteProperties, "gap") ?? void 0,
|
|
3109
|
-
isAccordionEnabled: void 0,
|
|
3110
|
-
isAccordionExpandedByDefault: void 0,
|
|
3111
|
-
isAccordionSidebarDisplayed: void 0
|
|
3112
|
-
};
|
|
3113
|
-
if (propertiesMobile.layout === "accordion" || returnBlock.properties.default.layout === "accordion") {
|
|
3114
|
-
propertiesMobile.isAccordionEnabled = getPropertyValueByLabel(mobileOverwriteProperties, "accordion-enabled") ?? void 0;
|
|
3115
|
-
propertiesMobile.isAccordionExpandedByDefault = getPropertyValueByLabel(mobileOverwriteProperties, "accordion-expanded") ?? void 0;
|
|
3116
|
-
propertiesMobile.isAccordionSidebarDisplayed = getPropertyValueByLabel(mobileOverwriteProperties, "accordion-sidebar-displayed") ?? void 0;
|
|
3283
|
+
case "element": {
|
|
3284
|
+
const element = parseWebElement(resource);
|
|
3285
|
+
items.push(element);
|
|
3286
|
+
break;
|
|
3117
3287
|
}
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
if (returnBlock.properties.default.layout === "accordion") {
|
|
3123
|
-
const accordionItems = [];
|
|
3124
|
-
for (const resource of blockResources) {
|
|
3125
|
-
const resourceProperties = resource.properties ? parseProperties(ensureArray(resource.properties.property)) : [];
|
|
3126
|
-
const resourceType = getPropertyValueByLabel(resourceProperties, "presentation");
|
|
3127
|
-
if (resourceType !== "element") throw new Error(`Accordion only accepts elements, but got “${resourceType}” for the following resource: “${parseStringContent(resource.identification.label)}”`);
|
|
3128
|
-
const componentType = getPropertyValueByLabel(getPropertyByLabel(resourceProperties, "presentation")?.properties ?? [], "component");
|
|
3129
|
-
if (componentType !== "text") throw new Error(`Accordion only accepts text components, but got “${componentType}” for the following resource: “${parseStringContent(resource.identification.label)}”`);
|
|
3130
|
-
const element = parseWebElementForAccordion(resource);
|
|
3131
|
-
accordionItems.push(element);
|
|
3132
|
-
}
|
|
3133
|
-
returnBlock.items = accordionItems;
|
|
3134
|
-
} else {
|
|
3135
|
-
const blockItems = [];
|
|
3136
|
-
for (const resource of blockResources) {
|
|
3137
|
-
const resourceType = getPropertyValueByLabel(resource.properties ? parseProperties(ensureArray(resource.properties.property)) : [], "presentation");
|
|
3138
|
-
if (resourceType === null) continue;
|
|
3139
|
-
switch (resourceType) {
|
|
3140
|
-
case "element": {
|
|
3141
|
-
const element = parseWebElement(resource);
|
|
3142
|
-
blockItems.push(element);
|
|
3143
|
-
break;
|
|
3144
|
-
}
|
|
3145
|
-
case "block": {
|
|
3146
|
-
const block = parseWebBlock(resource);
|
|
3147
|
-
if (block) blockItems.push(block);
|
|
3148
|
-
break;
|
|
3149
|
-
}
|
|
3288
|
+
case "block": {
|
|
3289
|
+
const block = parseWebBlock(resource);
|
|
3290
|
+
if (block) items.push(block);
|
|
3291
|
+
break;
|
|
3150
3292
|
}
|
|
3151
3293
|
}
|
|
3152
|
-
returnBlock.items = blockItems;
|
|
3153
3294
|
}
|
|
3154
|
-
|
|
3155
|
-
|
|
3295
|
+
returnWebpage.webpages = webpageResource.resource != null ? parseWebpageResources(ensureArray(webpageResource.resource), "page") : [];
|
|
3296
|
+
const webpageSubProperties = getPropertyByLabelAndValue(webpageProperties, "presentation", "page")?.properties ?? [];
|
|
3297
|
+
if (webpageSubProperties.length > 0) {
|
|
3298
|
+
returnWebpage.properties.isDisplayedInNavbar = getPropertyValueByLabel(webpageSubProperties, "displayed-in-navbar") ?? true;
|
|
3299
|
+
returnWebpage.properties.width = getPropertyValueByLabel(webpageSubProperties, "width") ?? "default";
|
|
3300
|
+
returnWebpage.properties.variant = getPropertyValueByLabel(webpageSubProperties, "variant") ?? "default";
|
|
3301
|
+
returnWebpage.properties.isSidebarDisplayed = getPropertyValueByLabel(webpageSubProperties, "sidebar-displayed") ?? true;
|
|
3302
|
+
returnWebpage.properties.isBreadcrumbsDisplayed = getPropertyValueByLabel(webpageSubProperties, "breadcrumbs-displayed") ?? false;
|
|
3303
|
+
returnWebpage.properties.isNavbarSearchBarDisplayed = getPropertyValueByLabel(webpageSubProperties, "navbar-search-bar-displayed") ?? true;
|
|
3304
|
+
}
|
|
3305
|
+
if (imageLink?.uuid != null) returnWebpage.properties.backgroundImage = {
|
|
3306
|
+
uuid: imageLink.uuid,
|
|
3307
|
+
label: imageLink.identification?.label ?? null,
|
|
3308
|
+
description: imageLink.description ?? null,
|
|
3309
|
+
width: imageLink.image?.width ?? 0,
|
|
3310
|
+
height: imageLink.image?.height ?? 0,
|
|
3311
|
+
quality: "high"
|
|
3312
|
+
};
|
|
3313
|
+
returnWebpage.properties.cssStyles = parseResponsiveCssStyles(webpageProperties);
|
|
3314
|
+
return returnWebpage;
|
|
3156
3315
|
}
|
|
3157
3316
|
/**
|
|
3158
|
-
* Parses raw
|
|
3317
|
+
* Parses raw webpage resources into an array of Webpage objects
|
|
3159
3318
|
*
|
|
3160
|
-
* @param
|
|
3161
|
-
* @returns
|
|
3319
|
+
* @param webpageResources - Array of raw webpage resources in OCHRE format
|
|
3320
|
+
* @returns Array of parsed Webpage objects
|
|
3162
3321
|
*/
|
|
3163
|
-
function
|
|
3164
|
-
const
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
status ??= "development";
|
|
3169
|
-
let privacy = getPropertyValueByLabel(websiteProperties, "privacy");
|
|
3170
|
-
privacy ??= "public";
|
|
3171
|
-
const returnProperties = {
|
|
3172
|
-
type,
|
|
3173
|
-
status,
|
|
3174
|
-
privacy,
|
|
3175
|
-
contact: null,
|
|
3176
|
-
theme: {
|
|
3177
|
-
isThemeToggleDisplayed: true,
|
|
3178
|
-
defaultTheme: "system"
|
|
3179
|
-
},
|
|
3180
|
-
icon: {
|
|
3181
|
-
logoUuid: null,
|
|
3182
|
-
faviconUuid: null,
|
|
3183
|
-
appleTouchIconUuid: null
|
|
3184
|
-
},
|
|
3185
|
-
navbar: {
|
|
3186
|
-
isDisplayed: true,
|
|
3187
|
-
variant: "default",
|
|
3188
|
-
alignment: "start",
|
|
3189
|
-
isProjectDisplayed: true,
|
|
3190
|
-
searchBarBoundElementUuid: null,
|
|
3191
|
-
items: null
|
|
3192
|
-
},
|
|
3193
|
-
footer: {
|
|
3194
|
-
isDisplayed: true,
|
|
3195
|
-
items: null
|
|
3196
|
-
},
|
|
3197
|
-
sidebar,
|
|
3198
|
-
itemPage: {
|
|
3199
|
-
isMainContentDisplayed: true,
|
|
3200
|
-
isDescriptionDisplayed: true,
|
|
3201
|
-
isDocumentDisplayed: true,
|
|
3202
|
-
isNotesDisplayed: true,
|
|
3203
|
-
isEventsDisplayed: true,
|
|
3204
|
-
isPeriodsDisplayed: true,
|
|
3205
|
-
isPropertiesDisplayed: true,
|
|
3206
|
-
isBibliographyDisplayed: true,
|
|
3207
|
-
isPropertyValuesGrouped: true,
|
|
3208
|
-
iiifViewer: "universal-viewer"
|
|
3209
|
-
},
|
|
3210
|
-
options: {
|
|
3211
|
-
contexts: null,
|
|
3212
|
-
scopes: null,
|
|
3213
|
-
labels: { title: null }
|
|
3214
|
-
}
|
|
3215
|
-
};
|
|
3216
|
-
const contactProperty = getPropertyByLabel(websiteProperties, "contact");
|
|
3217
|
-
if (contactProperty !== null) {
|
|
3218
|
-
const contactContent = contactProperty.values[0]?.content?.toString().split(";");
|
|
3219
|
-
if (contactContent?.length === 2) returnProperties.contact = {
|
|
3220
|
-
name: contactContent[0],
|
|
3221
|
-
email: contactContent[1] ?? null
|
|
3222
|
-
};
|
|
3223
|
-
else throw new Error(`Contact property must be in the format “name;email”, but got “${contactProperty.values[0]?.content}”`);
|
|
3224
|
-
}
|
|
3225
|
-
returnProperties.theme.isThemeToggleDisplayed = getPropertyValueByLabel(websiteProperties, "supports-theme-toggle") ?? true;
|
|
3226
|
-
returnProperties.theme.defaultTheme = getPropertyValueByLabel(websiteProperties, "default-theme") ?? "system";
|
|
3227
|
-
returnProperties.icon.logoUuid = getPropertyByLabel(websiteProperties, "logo")?.values[0]?.uuid ?? null;
|
|
3228
|
-
returnProperties.navbar.isDisplayed = getPropertyValueByLabel(websiteProperties, "navbar-displayed") ?? true;
|
|
3229
|
-
returnProperties.navbar.variant = getPropertyValueByLabel(websiteProperties, "navbar-variant") ?? "default";
|
|
3230
|
-
returnProperties.navbar.alignment = getPropertyValueByLabel(websiteProperties, "navbar-alignment") ?? "start";
|
|
3231
|
-
returnProperties.navbar.isProjectDisplayed = getPropertyValueByLabel(websiteProperties, "navbar-project-displayed") ?? true;
|
|
3232
|
-
returnProperties.navbar.searchBarBoundElementUuid = getPropertyByLabel(websiteProperties, "bound-element-navbar-search-bar")?.values[0]?.uuid ?? null;
|
|
3233
|
-
returnProperties.footer.isDisplayed = getPropertyValueByLabel(websiteProperties, "footer-displayed") ?? true;
|
|
3234
|
-
const itemPageTypeProperty = getPropertyByLabelAndValue(websiteProperties, "page-type", "item-page");
|
|
3235
|
-
if (itemPageTypeProperty !== null) {
|
|
3236
|
-
returnProperties.itemPage.isMainContentDisplayed = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-main-content-displayed") ?? true;
|
|
3237
|
-
returnProperties.itemPage.isDescriptionDisplayed = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-description-displayed") ?? true;
|
|
3238
|
-
returnProperties.itemPage.isDocumentDisplayed = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-document-displayed") ?? true;
|
|
3239
|
-
returnProperties.itemPage.isNotesDisplayed = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-notes-displayed") ?? true;
|
|
3240
|
-
returnProperties.itemPage.isEventsDisplayed = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-events-displayed") ?? true;
|
|
3241
|
-
returnProperties.itemPage.isPeriodsDisplayed = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-periods-displayed") ?? true;
|
|
3242
|
-
returnProperties.itemPage.isPropertiesDisplayed = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-properties-displayed") ?? true;
|
|
3243
|
-
returnProperties.itemPage.isBibliographyDisplayed = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-bibliography-displayed") ?? true;
|
|
3244
|
-
returnProperties.itemPage.isPropertyValuesGrouped = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-property-values-grouped") ?? true;
|
|
3245
|
-
returnProperties.itemPage.iiifViewer = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-iiif-viewer") ?? "universal-viewer";
|
|
3246
|
-
}
|
|
3247
|
-
if ("options" in websiteTree && websiteTree.options) {
|
|
3248
|
-
returnProperties.options.scopes = websiteTree.options.scopes != null ? ensureArray(websiteTree.options.scopes.scope).map((scope) => ({
|
|
3249
|
-
uuid: scope.uuid.content,
|
|
3250
|
-
type: scope.uuid.type,
|
|
3251
|
-
identification: parseIdentification(scope.identification)
|
|
3252
|
-
})) : null;
|
|
3253
|
-
returnProperties.options.contexts = parseAllOptionContexts(websiteTree.options);
|
|
3254
|
-
if ("notes" in websiteTree.options && websiteTree.options.notes != null) {
|
|
3255
|
-
const labelNotes = parseNotes(ensureArray(websiteTree.options.notes.note));
|
|
3256
|
-
returnProperties.options.labels.title = labelNotes.find((note) => note.title === "Title label")?.content ?? null;
|
|
3257
|
-
}
|
|
3258
|
-
}
|
|
3259
|
-
return returnProperties;
|
|
3260
|
-
}
|
|
3261
|
-
function parseContexts(contexts) {
|
|
3262
|
-
const contextsParsed = [];
|
|
3263
|
-
for (const mainContext of contexts) for (const contextItemToParse of ensureArray(mainContext.context)) {
|
|
3264
|
-
const levelsToParse = ensureArray(contextItemToParse.levels.level);
|
|
3265
|
-
let type = "";
|
|
3266
|
-
const levels = levelsToParse.map((level) => {
|
|
3267
|
-
let variableUuid = "";
|
|
3268
|
-
let valueUuid = null;
|
|
3269
|
-
if (typeof level === "string") {
|
|
3270
|
-
const splitLevel = level.split(", ");
|
|
3271
|
-
variableUuid = splitLevel[0];
|
|
3272
|
-
valueUuid = splitLevel[1] === "null" ? null : splitLevel[1];
|
|
3273
|
-
} else {
|
|
3274
|
-
const splitLevel = level.content.split(", ");
|
|
3275
|
-
type = level.dataType;
|
|
3276
|
-
variableUuid = splitLevel[0];
|
|
3277
|
-
valueUuid = splitLevel[1] === "null" ? null : splitLevel[1];
|
|
3278
|
-
}
|
|
3279
|
-
return {
|
|
3280
|
-
variableUuid,
|
|
3281
|
-
valueUuid
|
|
3282
|
-
};
|
|
3283
|
-
});
|
|
3284
|
-
contextsParsed.push({
|
|
3285
|
-
context: levels,
|
|
3286
|
-
type,
|
|
3287
|
-
identification: parseIdentification(contextItemToParse.identification)
|
|
3288
|
-
});
|
|
3322
|
+
function parseWebpages(webpageResources, slugPrefix) {
|
|
3323
|
+
const returnPages = [];
|
|
3324
|
+
for (const webpageResource of webpageResources) {
|
|
3325
|
+
const webpage = parseWebpage(webpageResource, slugPrefix);
|
|
3326
|
+
if (webpage !== null) returnPages.push(webpage);
|
|
3289
3327
|
}
|
|
3290
|
-
return
|
|
3328
|
+
return returnPages;
|
|
3291
3329
|
}
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3330
|
+
/**
|
|
3331
|
+
* Parses raw segment resource into a standardized WebSegment object
|
|
3332
|
+
*
|
|
3333
|
+
* @param segmentResource - Raw segment resource in OCHRE format
|
|
3334
|
+
* @returns Parsed WebSegment object
|
|
3335
|
+
*/
|
|
3336
|
+
function parseWebSegment(segmentResource, slugPrefix) {
|
|
3337
|
+
const webpageProperties = segmentResource.properties ? parseProperties(ensureArray(segmentResource.properties.property)) : [];
|
|
3338
|
+
if (webpageProperties.length === 0 || getPropertyValueByLabel(webpageProperties, "presentation") !== "segment") return null;
|
|
3339
|
+
const identification = parseIdentification(segmentResource.identification);
|
|
3340
|
+
const slug = segmentResource.identification.abbreviation != null ? parseFakeStringOrContent(segmentResource.identification.abbreviation) : null;
|
|
3341
|
+
if (slug == null) throw new Error(`Slug not found for segment “${identification.label}”`);
|
|
3342
|
+
const returnSegment = {
|
|
3343
|
+
uuid: segmentResource.uuid,
|
|
3344
|
+
type: "segment",
|
|
3345
|
+
title: identification.label,
|
|
3346
|
+
slug,
|
|
3347
|
+
publicationDateTime: parseOptionalDate(segmentResource.publicationDateTime),
|
|
3348
|
+
items: []
|
|
3310
3349
|
};
|
|
3350
|
+
returnSegment.items = parseWebSegmentItems(segmentResource.resource ? ensureArray(segmentResource.resource) : [], slugPrefix != null ? `${slugPrefix}/${slug}`.replace(/\/$/, "") : slug);
|
|
3351
|
+
return returnSegment;
|
|
3311
3352
|
}
|
|
3312
|
-
|
|
3313
|
-
//#endregion
|
|
3314
|
-
//#region src/utils/fetchers/gallery.ts
|
|
3315
3353
|
/**
|
|
3316
|
-
*
|
|
3354
|
+
* Parses raw segment resources into an array of WebSegment objects
|
|
3317
3355
|
*
|
|
3318
|
-
* @param
|
|
3319
|
-
* @
|
|
3320
|
-
* @param page - The page number to fetch
|
|
3321
|
-
* @param pageSize - The number of items per page
|
|
3322
|
-
* @param options - The options for the fetch
|
|
3323
|
-
* @param options.fetch - The fetch function to use
|
|
3324
|
-
* @param options.version - The version of the OCHRE API to use
|
|
3325
|
-
* @returns The parsed gallery or an error message if the fetch/parse fails
|
|
3356
|
+
* @param segmentResources - Array of raw segment resources in OCHRE format
|
|
3357
|
+
* @returns Array of parsed WebSegment objects
|
|
3326
3358
|
*/
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
page,
|
|
3333
|
-
pageSize
|
|
3334
|
-
});
|
|
3335
|
-
const response = await (options?.fetch ?? fetch)(`https://ochre.lib.uchicago.edu/ochre?xquery=${encodeURIComponent(`
|
|
3336
|
-
for $q in input()/ochre[@uuid='${parsedUuid}']
|
|
3337
|
-
let $filtered := $q//items/resource[contains(lower-case(identification/label), lower-case('${parsedFilter}'))]
|
|
3338
|
-
let $maxLength := count($filtered)
|
|
3339
|
-
return <gallery maxLength='{$maxLength}'>
|
|
3340
|
-
{$q/metadata/project}
|
|
3341
|
-
{$q/metadata/item}
|
|
3342
|
-
{$filtered[position() >= ${((parsedPage - 1) * parsedPageSize + 1).toString()} and position() < ${(parsedPage * parsedPageSize + 1).toString()}]}
|
|
3343
|
-
</gallery>
|
|
3344
|
-
`)}&format=json`);
|
|
3345
|
-
if (!response.ok) throw new Error("Error fetching gallery items, please try again later.");
|
|
3346
|
-
const data = await response.json();
|
|
3347
|
-
if (!("gallery" in data.result)) throw new Error("Failed to fetch gallery");
|
|
3348
|
-
return {
|
|
3349
|
-
item: {
|
|
3350
|
-
identification: parseIdentification(data.result.gallery.item.identification),
|
|
3351
|
-
projectIdentification: parseIdentification(data.result.gallery.project.identification),
|
|
3352
|
-
resources: parseResources(data.result.gallery.resource ? Array.isArray(data.result.gallery.resource) ? data.result.gallery.resource : [data.result.gallery.resource] : []),
|
|
3353
|
-
maxLength: data.result.gallery.maxLength
|
|
3354
|
-
},
|
|
3355
|
-
error: null
|
|
3356
|
-
};
|
|
3357
|
-
} catch (error) {
|
|
3358
|
-
console.error(error);
|
|
3359
|
-
return {
|
|
3360
|
-
item: null,
|
|
3361
|
-
error: error instanceof Error ? error.message : "Failed to fetch gallery"
|
|
3362
|
-
};
|
|
3359
|
+
function parseSegments(segmentResources, slugPrefix) {
|
|
3360
|
+
const returnSegments = [];
|
|
3361
|
+
for (const segmentResource of segmentResources) {
|
|
3362
|
+
const segment = parseWebSegment(segmentResource, slugPrefix);
|
|
3363
|
+
if (segment !== null) returnSegments.push(segment);
|
|
3363
3364
|
}
|
|
3365
|
+
return returnSegments;
|
|
3364
3366
|
}
|
|
3365
|
-
|
|
3366
|
-
//#endregion
|
|
3367
|
-
//#region src/utils/fetchers/uuid.ts
|
|
3368
3367
|
/**
|
|
3369
|
-
*
|
|
3368
|
+
* Parses raw segment item into a standardized WebSegmentItem object
|
|
3370
3369
|
*
|
|
3371
|
-
* @param
|
|
3372
|
-
* @returns
|
|
3373
|
-
* @internal
|
|
3370
|
+
* @param segmentItemResource - Raw segment item resource in OCHRE format
|
|
3371
|
+
* @returns Parsed WebSegmentItem object
|
|
3374
3372
|
*/
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3373
|
+
function parseWebSegmentItem(segmentItemResource, slugPrefix) {
|
|
3374
|
+
const webpageProperties = segmentItemResource.properties ? parseProperties(ensureArray(segmentItemResource.properties.property)) : [];
|
|
3375
|
+
if (webpageProperties.length === 0 || getPropertyValueByLabel(webpageProperties, "presentation") !== "segment-item") return null;
|
|
3376
|
+
const identification = parseIdentification(segmentItemResource.identification);
|
|
3377
|
+
const slug = segmentItemResource.identification.abbreviation != null ? parseFakeStringOrContent(segmentItemResource.identification.abbreviation) : null;
|
|
3378
|
+
if (slug == null) throw new Error(`Slug not found for segment item “${identification.label}”`);
|
|
3379
|
+
const returnSegmentItem = {
|
|
3380
|
+
uuid: segmentItemResource.uuid,
|
|
3381
|
+
type: "segment-item",
|
|
3382
|
+
title: identification.label,
|
|
3383
|
+
slug,
|
|
3384
|
+
publicationDateTime: parseOptionalDate(segmentItemResource.publicationDateTime),
|
|
3385
|
+
items: []
|
|
3386
|
+
};
|
|
3387
|
+
const resources = segmentItemResource.resource ? ensureArray(segmentItemResource.resource) : [];
|
|
3388
|
+
returnSegmentItem.items.push(...parseWebpages(resources, slugPrefix != null ? `${slugPrefix}/${slug}`.replace(/\/$/, "") : slug), ...parseSegments(resources, slugPrefix != null ? `${slugPrefix}/${slug}`.replace(/\/$/, "") : slug));
|
|
3389
|
+
return returnSegmentItem;
|
|
3387
3390
|
}
|
|
3388
|
-
|
|
3389
|
-
//#endregion
|
|
3390
|
-
//#region src/utils/fetchers/item.ts
|
|
3391
3391
|
/**
|
|
3392
|
-
*
|
|
3392
|
+
* Parses raw segment items into an array of WebSegmentItem objects
|
|
3393
3393
|
*
|
|
3394
|
-
* @param
|
|
3395
|
-
* @returns
|
|
3394
|
+
* @param segmentItems - Array of raw segment items in OCHRE format
|
|
3395
|
+
* @returns Array of parsed WebSegmentItem objects
|
|
3396
3396
|
*/
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
const
|
|
3401
|
-
|
|
3402
|
-
version
|
|
3403
|
-
});
|
|
3404
|
-
if (error !== null) throw new Error(error);
|
|
3405
|
-
const categoryKey = category ?? getItemCategory(Object.keys(data.ochre));
|
|
3406
|
-
const belongsTo = {
|
|
3407
|
-
uuid: data.ochre.uuidBelongsTo,
|
|
3408
|
-
abbreviation: parseFakeString(data.ochre.belongsTo)
|
|
3409
|
-
};
|
|
3410
|
-
const metadata = parseMetadata(data.ochre.metadata);
|
|
3411
|
-
let item;
|
|
3412
|
-
switch (categoryKey) {
|
|
3413
|
-
case "resource":
|
|
3414
|
-
if (!("resource" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'resource' key");
|
|
3415
|
-
item = parseResource(data.ochre.resource, metadata, data.ochre.persistentUrl, belongsTo);
|
|
3416
|
-
break;
|
|
3417
|
-
case "spatialUnit":
|
|
3418
|
-
if (!("spatialUnit" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'spatialUnit' key");
|
|
3419
|
-
item = parseSpatialUnit(data.ochre.spatialUnit, metadata, data.ochre.persistentUrl, belongsTo);
|
|
3420
|
-
break;
|
|
3421
|
-
case "concept":
|
|
3422
|
-
if (!("concept" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'concept' key");
|
|
3423
|
-
item = parseConcept(data.ochre.concept, metadata, data.ochre.persistentUrl, belongsTo);
|
|
3424
|
-
break;
|
|
3425
|
-
case "period":
|
|
3426
|
-
if (!("period" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'period' key");
|
|
3427
|
-
item = parsePeriod(data.ochre.period, metadata, data.ochre.persistentUrl, {
|
|
3428
|
-
uuid: data.ochre.uuidBelongsTo,
|
|
3429
|
-
abbreviation: parseFakeString(data.ochre.belongsTo)
|
|
3430
|
-
});
|
|
3431
|
-
break;
|
|
3432
|
-
case "bibliography":
|
|
3433
|
-
if (!("bibliography" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'bibliography' key");
|
|
3434
|
-
item = parseBibliography(data.ochre.bibliography, metadata, data.ochre.persistentUrl, belongsTo);
|
|
3435
|
-
break;
|
|
3436
|
-
case "person":
|
|
3437
|
-
if (!("person" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'person' key");
|
|
3438
|
-
item = parsePerson(data.ochre.person, metadata, data.ochre.persistentUrl, belongsTo);
|
|
3439
|
-
break;
|
|
3440
|
-
case "propertyVariable":
|
|
3441
|
-
if (!("propertyVariable" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'propertyVariable' key");
|
|
3442
|
-
item = parsePropertyVariable(data.ochre.propertyVariable, metadata, data.ochre.persistentUrl, belongsTo);
|
|
3443
|
-
break;
|
|
3444
|
-
case "propertyValue":
|
|
3445
|
-
if (!("propertyValue" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'propertyValue' key");
|
|
3446
|
-
item = parsePropertyValue(data.ochre.propertyValue, metadata, data.ochre.persistentUrl, belongsTo);
|
|
3447
|
-
break;
|
|
3448
|
-
case "text":
|
|
3449
|
-
if (!("text" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'text' key");
|
|
3450
|
-
item = parseText(data.ochre.text, metadata, data.ochre.persistentUrl, belongsTo);
|
|
3451
|
-
break;
|
|
3452
|
-
case "set":
|
|
3453
|
-
if (!("set" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'set' key");
|
|
3454
|
-
item = parseSet(data.ochre.set, itemCategories, metadata, data.ochre.persistentUrl, belongsTo);
|
|
3455
|
-
break;
|
|
3456
|
-
case "tree":
|
|
3457
|
-
if (!("tree" in data.ochre)) throw new Error("Invalid OCHRE data: API response missing 'tree' key");
|
|
3458
|
-
item = parseTree(data.ochre.tree, itemCategories, metadata, data.ochre.persistentUrl, belongsTo);
|
|
3459
|
-
break;
|
|
3460
|
-
default: throw new Error("Invalid category");
|
|
3461
|
-
}
|
|
3462
|
-
return {
|
|
3463
|
-
error: null,
|
|
3464
|
-
item
|
|
3465
|
-
};
|
|
3466
|
-
} catch (error) {
|
|
3467
|
-
return {
|
|
3468
|
-
error: error instanceof Error ? error.message : "Unknown error",
|
|
3469
|
-
item: void 0
|
|
3470
|
-
};
|
|
3397
|
+
function parseWebSegmentItems(segmentItems, slugPrefix) {
|
|
3398
|
+
const returnItems = [];
|
|
3399
|
+
for (const segmentItem of segmentItems) {
|
|
3400
|
+
const segmentItemParsed = parseWebSegmentItem(segmentItem, slugPrefix);
|
|
3401
|
+
if (segmentItemParsed !== null) returnItems.push(segmentItemParsed);
|
|
3471
3402
|
}
|
|
3403
|
+
return returnItems;
|
|
3472
3404
|
}
|
|
3473
|
-
|
|
3474
|
-
//#endregion
|
|
3475
|
-
//#region src/utils/fetchers/items-by-property-values.ts
|
|
3476
3405
|
/**
|
|
3477
|
-
*
|
|
3478
|
-
*
|
|
3479
|
-
* @param
|
|
3480
|
-
* @
|
|
3481
|
-
* @param params.propertyVariableUuids - An array of property variable UUIDs to fetch
|
|
3482
|
-
* @param params.propertyValues - An array of property values to fetch
|
|
3483
|
-
* @param params.itemCategory - The category of the items to fetch
|
|
3484
|
-
* @param params.page - The page number (1-indexed)
|
|
3485
|
-
* @param params.pageSize - The number of items per page
|
|
3486
|
-
* @param params.includeChildItems - Whether to include child items of the same category
|
|
3487
|
-
* @param options - Options for the fetch
|
|
3488
|
-
* @param options.version - The version of the OCHRE API to use
|
|
3489
|
-
* @returns An XQuery string
|
|
3406
|
+
* Parses raw sidebar data into a standardized Sidebar structure
|
|
3407
|
+
*
|
|
3408
|
+
* @param resources - Array of raw sidebar resources in OCHRE format
|
|
3409
|
+
* @returns Parsed Sidebar object
|
|
3490
3410
|
*/
|
|
3491
|
-
function
|
|
3492
|
-
|
|
3493
|
-
const
|
|
3494
|
-
const
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3411
|
+
function parseSidebar(resources) {
|
|
3412
|
+
let returnSidebar = null;
|
|
3413
|
+
const items = [];
|
|
3414
|
+
const title = {
|
|
3415
|
+
label: "",
|
|
3416
|
+
variant: "default",
|
|
3417
|
+
properties: {
|
|
3418
|
+
isNameDisplayed: false,
|
|
3419
|
+
isDescriptionDisplayed: false,
|
|
3420
|
+
isDateDisplayed: false,
|
|
3421
|
+
isCreatorsDisplayed: false,
|
|
3422
|
+
isCountDisplayed: false
|
|
3423
|
+
}
|
|
3424
|
+
};
|
|
3425
|
+
let layout = "start";
|
|
3426
|
+
let mobileLayout = "default";
|
|
3427
|
+
const cssStyles = {
|
|
3428
|
+
default: [],
|
|
3429
|
+
tablet: [],
|
|
3430
|
+
mobile: []
|
|
3431
|
+
};
|
|
3432
|
+
const sidebarResource = resources.find((resource) => {
|
|
3433
|
+
const resourceProperties = resource.properties ? parseProperties(ensureArray(resource.properties.property)) : [];
|
|
3434
|
+
return getPropertyValueByLabel(resourceProperties, "presentation") === "element" && getPropertyValueByLabel(resourceProperties[0]?.properties ?? [], "component") === "sidebar";
|
|
3435
|
+
});
|
|
3436
|
+
if (sidebarResource != null) {
|
|
3437
|
+
title.label = parseFakeStringOrContent(sidebarResource.identification.label);
|
|
3438
|
+
const sidebarBaseProperties = sidebarResource.properties ? parseProperties(ensureArray(sidebarResource.properties.property)) : [];
|
|
3439
|
+
const sidebarProperties = sidebarBaseProperties.find((property) => property.label === "presentation" && property.values[0]?.content === "element")?.properties.find((property) => property.label === "component" && property.values[0]?.content === "sidebar")?.properties ?? [];
|
|
3440
|
+
const sidebarLayoutProperty = sidebarProperties.find((property) => property.label === "layout");
|
|
3441
|
+
if (sidebarLayoutProperty) layout = sidebarLayoutProperty.values[0].content;
|
|
3442
|
+
const sidebarMobileLayoutProperty = sidebarProperties.find((property) => property.label === "layout-mobile");
|
|
3443
|
+
if (sidebarMobileLayoutProperty) mobileLayout = sidebarMobileLayoutProperty.values[0].content;
|
|
3444
|
+
const parsedCssStyles = parseResponsiveCssStyles(sidebarBaseProperties);
|
|
3445
|
+
cssStyles.default = parsedCssStyles.default;
|
|
3446
|
+
cssStyles.tablet = parsedCssStyles.tablet;
|
|
3447
|
+
cssStyles.mobile = parsedCssStyles.mobile;
|
|
3448
|
+
const titleProperties = sidebarBaseProperties.find((property) => property.label === "presentation" && property.values[0].content === "title")?.properties;
|
|
3449
|
+
if (titleProperties) {
|
|
3450
|
+
const titleVariant = getPropertyValueByLabel(titleProperties, "variant");
|
|
3451
|
+
if (titleVariant) title.variant = titleVariant;
|
|
3452
|
+
title.properties.isNameDisplayed = getPropertyValueByLabel(titleProperties, "name-displayed") === true;
|
|
3453
|
+
title.properties.isDescriptionDisplayed = getPropertyValueByLabel(titleProperties, "description-displayed") === true;
|
|
3454
|
+
title.properties.isDateDisplayed = getPropertyValueByLabel(titleProperties, "date-displayed") === true;
|
|
3455
|
+
title.properties.isCreatorsDisplayed = getPropertyValueByLabel(titleProperties, "creators-displayed") === true;
|
|
3456
|
+
title.properties.isCountDisplayed = getPropertyValueByLabel(titleProperties, "count-displayed") === true;
|
|
3457
|
+
}
|
|
3458
|
+
const sidebarResources = sidebarResource.resource ? ensureArray(sidebarResource.resource) : [];
|
|
3459
|
+
for (const resource of sidebarResources) {
|
|
3460
|
+
const resourceType = getPropertyValueByLabel(resource.properties ? parseProperties(ensureArray(resource.properties.property)) : [], "presentation");
|
|
3461
|
+
if (resourceType === null) continue;
|
|
3462
|
+
switch (resourceType) {
|
|
3463
|
+
case "element": {
|
|
3464
|
+
const element = parseWebElement(resource);
|
|
3465
|
+
items.push(element);
|
|
3466
|
+
break;
|
|
3467
|
+
}
|
|
3468
|
+
case "block": {
|
|
3469
|
+
const block = parseWebBlock(resource);
|
|
3470
|
+
if (block) items.push(block);
|
|
3471
|
+
break;
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
if (items.length > 0) returnSidebar = {
|
|
3477
|
+
isDisplayed: true,
|
|
3478
|
+
items,
|
|
3479
|
+
title,
|
|
3480
|
+
layout,
|
|
3481
|
+
mobileLayout,
|
|
3482
|
+
cssStyles
|
|
3483
|
+
};
|
|
3484
|
+
return returnSidebar;
|
|
3526
3485
|
}
|
|
3527
3486
|
/**
|
|
3528
|
-
*
|
|
3487
|
+
* Parses raw text element data for accordion layout with items support
|
|
3529
3488
|
*
|
|
3530
|
-
* @param
|
|
3531
|
-
* @
|
|
3532
|
-
* @param params.belongsToCollectionScopeUuids - The collection scope UUIDs to filter by
|
|
3533
|
-
* @param params.propertyVariableUuids - The property variable UUIDs to query by
|
|
3534
|
-
* @param params.propertyValues - The property values to query by
|
|
3535
|
-
* @param params.page - The page number (1-indexed)
|
|
3536
|
-
* @param params.pageSize - The number of items per page
|
|
3537
|
-
* @param params.itemCategory - The category of the items to fetch
|
|
3538
|
-
* @param params.includeChildItems - Whether to include child items of the same category
|
|
3539
|
-
* @param categoryParams - The category parameters for the fetch
|
|
3540
|
-
* @param categoryParams.category - The category of the items to fetch
|
|
3541
|
-
* @param categoryParams.itemCategories - The categories of the items to fetch
|
|
3542
|
-
* @param options - Options for the fetch
|
|
3543
|
-
* @param options.fetch - The fetch function to use
|
|
3544
|
-
* @param options.version - The version of the OCHRE API to use
|
|
3545
|
-
* @returns The parsed items by property values or null if the fetch/parse fails
|
|
3489
|
+
* @param elementResource - Raw element resource data in OCHRE format
|
|
3490
|
+
* @returns Parsed text WebElement with items array
|
|
3546
3491
|
*/
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
const
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
if (Array.isArray(data.result) || Object.keys(data.result.ochre).length === 0) throw new Error("No items found");
|
|
3566
|
-
if (category != null && data.result.ochre.items[category] == null) throw new Error(`No items found for category: ${category}`);
|
|
3567
|
-
if ((category === "set" || category === "tree") && itemCategories != null && "items" in data.result.ochre.items[category] && Array.isArray(data.result.ochre.items[category].items) && data.result.ochre.items[category].items.every((item) => item.category !== itemCategories)) throw new Error(`No items found for category: ${category} and item categories: ${itemCategories}`);
|
|
3568
|
-
const items = [];
|
|
3569
|
-
if ("resource" in data.result.ochre.items && data.result.ochre.items.resource != null) {
|
|
3570
|
-
const resources = parseResources(Array.isArray(data.result.ochre.items.resource) ? data.result.ochre.items.resource : [data.result.ochre.items.resource]);
|
|
3571
|
-
items.push(...resources);
|
|
3572
|
-
}
|
|
3573
|
-
if ("spatialUnit" in data.result.ochre.items && data.result.ochre.items.spatialUnit != null) {
|
|
3574
|
-
const spatialUnits = parseSpatialUnits(Array.isArray(data.result.ochre.items.spatialUnit) ? data.result.ochre.items.spatialUnit : [data.result.ochre.items.spatialUnit]);
|
|
3575
|
-
items.push(...spatialUnits);
|
|
3576
|
-
}
|
|
3577
|
-
if ("concept" in data.result.ochre.items && data.result.ochre.items.concept != null) {
|
|
3578
|
-
const concepts = parseConcepts(Array.isArray(data.result.ochre.items.concept) ? data.result.ochre.items.concept : [data.result.ochre.items.concept]);
|
|
3579
|
-
items.push(...concepts);
|
|
3580
|
-
}
|
|
3581
|
-
if ("period" in data.result.ochre.items && data.result.ochre.items.period != null) {
|
|
3582
|
-
const periods = parsePeriods(Array.isArray(data.result.ochre.items.period) ? data.result.ochre.items.period : [data.result.ochre.items.period]);
|
|
3583
|
-
items.push(...periods);
|
|
3584
|
-
}
|
|
3585
|
-
if ("bibliography" in data.result.ochre.items && data.result.ochre.items.bibliography != null) {
|
|
3586
|
-
const bibliographies = parseBibliographies(Array.isArray(data.result.ochre.items.bibliography) ? data.result.ochre.items.bibliography : [data.result.ochre.items.bibliography]);
|
|
3587
|
-
items.push(...bibliographies);
|
|
3588
|
-
}
|
|
3589
|
-
if ("person" in data.result.ochre.items && data.result.ochre.items.person != null) {
|
|
3590
|
-
const persons = parsePersons(Array.isArray(data.result.ochre.items.person) ? data.result.ochre.items.person : [data.result.ochre.items.person]);
|
|
3591
|
-
items.push(...persons);
|
|
3592
|
-
}
|
|
3593
|
-
if ("propertyVariable" in data.result.ochre.items && data.result.ochre.items.propertyVariable != null) {
|
|
3594
|
-
const propertyVariables = parsePropertyVariables(Array.isArray(data.result.ochre.items.propertyVariable) ? data.result.ochre.items.propertyVariable : [data.result.ochre.items.propertyVariable]);
|
|
3595
|
-
items.push(...propertyVariables);
|
|
3596
|
-
}
|
|
3597
|
-
if ("propertyValue" in data.result.ochre.items && data.result.ochre.items.propertyValue != null) {
|
|
3598
|
-
const propertyValues = parsePropertyValues(Array.isArray(data.result.ochre.items.propertyValue) ? data.result.ochre.items.propertyValue : [data.result.ochre.items.propertyValue]);
|
|
3599
|
-
items.push(...propertyValues);
|
|
3600
|
-
}
|
|
3601
|
-
if ("text" in data.result.ochre.items && data.result.ochre.items.text != null) {
|
|
3602
|
-
const texts = parseTexts(Array.isArray(data.result.ochre.items.text) ? data.result.ochre.items.text : [data.result.ochre.items.text]);
|
|
3603
|
-
items.push(...texts);
|
|
3604
|
-
}
|
|
3605
|
-
if ("set" in data.result.ochre.items && data.result.ochre.items.set != null) {
|
|
3606
|
-
const sets = parseSets(Array.isArray(data.result.ochre.items.set) ? data.result.ochre.items.set : [data.result.ochre.items.set]);
|
|
3607
|
-
items.push(...sets);
|
|
3608
|
-
}
|
|
3609
|
-
if ("tree" in data.result.ochre.items && data.result.ochre.items.tree != null) {
|
|
3610
|
-
const trees = parseTrees(Array.isArray(data.result.ochre.items.tree) ? data.result.ochre.items.tree : [data.result.ochre.items.tree]);
|
|
3611
|
-
items.push(...trees);
|
|
3492
|
+
function parseWebElementForAccordion(elementResource) {
|
|
3493
|
+
const textElement = parseWebElement(elementResource);
|
|
3494
|
+
const childResources = elementResource.resource ? ensureArray(elementResource.resource) : [];
|
|
3495
|
+
const items = [];
|
|
3496
|
+
for (const resource of childResources) {
|
|
3497
|
+
const resourceType = getPropertyValueByLabel(resource.properties ? parseProperties(ensureArray(resource.properties.property)) : [], "presentation");
|
|
3498
|
+
if (resourceType === null) continue;
|
|
3499
|
+
switch (resourceType) {
|
|
3500
|
+
case "element": {
|
|
3501
|
+
const element = parseWebElement(resource);
|
|
3502
|
+
items.push(element);
|
|
3503
|
+
break;
|
|
3504
|
+
}
|
|
3505
|
+
case "block": {
|
|
3506
|
+
const block = parseWebBlock(resource);
|
|
3507
|
+
if (block) items.push(block);
|
|
3508
|
+
break;
|
|
3509
|
+
}
|
|
3612
3510
|
}
|
|
3613
|
-
return {
|
|
3614
|
-
totalCount: data.result.ochre.items.totalCount,
|
|
3615
|
-
page: data.result.ochre.items.page,
|
|
3616
|
-
pageSize: data.result.ochre.items.pageSize,
|
|
3617
|
-
items,
|
|
3618
|
-
error: null
|
|
3619
|
-
};
|
|
3620
|
-
} catch (error) {
|
|
3621
|
-
console.error(error);
|
|
3622
|
-
return {
|
|
3623
|
-
totalCount: null,
|
|
3624
|
-
page: null,
|
|
3625
|
-
pageSize: null,
|
|
3626
|
-
items: null,
|
|
3627
|
-
error: error instanceof Error ? error.message : "Failed to fetch items by property values"
|
|
3628
|
-
};
|
|
3629
3511
|
}
|
|
3512
|
+
return {
|
|
3513
|
+
...textElement,
|
|
3514
|
+
items
|
|
3515
|
+
};
|
|
3630
3516
|
}
|
|
3631
|
-
|
|
3632
|
-
//#endregion
|
|
3633
|
-
//#region src/utils/fetchers/property-values-by-property-variables.ts
|
|
3634
3517
|
/**
|
|
3635
|
-
*
|
|
3518
|
+
* Parses raw block data into a standardized WebBlock structure
|
|
3519
|
+
*
|
|
3520
|
+
* @param blockResource - Raw block resource data in OCHRE format
|
|
3521
|
+
* @returns Parsed WebBlock object
|
|
3636
3522
|
*/
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
label: null
|
|
3523
|
+
function parseWebBlock(blockResource) {
|
|
3524
|
+
const blockProperties = blockResource.properties ? parseProperties(ensureArray(blockResource.properties.property)) : [];
|
|
3525
|
+
const returnBlock = {
|
|
3526
|
+
uuid: blockResource.uuid,
|
|
3527
|
+
type: "block",
|
|
3528
|
+
title: parseWebTitle(blockProperties, parseIdentification(blockResource.identification)),
|
|
3529
|
+
items: [],
|
|
3530
|
+
properties: {
|
|
3531
|
+
default: {
|
|
3532
|
+
layout: "vertical",
|
|
3533
|
+
spacing: null,
|
|
3534
|
+
gap: null
|
|
3535
|
+
},
|
|
3536
|
+
mobile: null,
|
|
3537
|
+
tablet: null
|
|
3538
|
+
},
|
|
3539
|
+
cssStyles: {
|
|
3540
|
+
default: [],
|
|
3541
|
+
tablet: [],
|
|
3542
|
+
mobile: []
|
|
3543
|
+
}
|
|
3659
3544
|
};
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3545
|
+
const blockMainProperties = getPropertyByLabelAndValue(blockProperties, "presentation", "block")?.properties ?? [];
|
|
3546
|
+
if (blockMainProperties.length > 0) {
|
|
3547
|
+
returnBlock.properties.default.layout = getPropertyValueByLabel(blockMainProperties, "layout") ?? "vertical";
|
|
3548
|
+
if (returnBlock.properties.default.layout === "accordion") {
|
|
3549
|
+
returnBlock.properties.default.isAccordionEnabled = getPropertyValueByLabel(blockMainProperties, "accordion-enabled") ?? true;
|
|
3550
|
+
returnBlock.properties.default.isAccordionExpandedByDefault = getPropertyValueByLabel(blockMainProperties, "accordion-expanded") ?? true;
|
|
3551
|
+
returnBlock.properties.default.isAccordionSidebarDisplayed = getPropertyValueByLabel(blockMainProperties, "accordion-sidebar-displayed") ?? false;
|
|
3552
|
+
}
|
|
3553
|
+
returnBlock.properties.default.spacing = getPropertyValueByLabel(blockMainProperties, "spacing") ?? null;
|
|
3554
|
+
returnBlock.properties.default.gap = getPropertyValueByLabel(blockMainProperties, "gap") ?? null;
|
|
3555
|
+
const tabletOverwriteProperty = getPropertyByLabel(blockMainProperties, "overwrite-tablet");
|
|
3556
|
+
if (tabletOverwriteProperty !== null) {
|
|
3557
|
+
const tabletOverwriteProperties = tabletOverwriteProperty.properties;
|
|
3558
|
+
const propertiesTablet = {
|
|
3559
|
+
layout: getPropertyValueByLabel(tabletOverwriteProperties, "layout") ?? void 0,
|
|
3560
|
+
spacing: getPropertyValueByLabel(tabletOverwriteProperties, "spacing") ?? void 0,
|
|
3561
|
+
gap: getPropertyValueByLabel(tabletOverwriteProperties, "gap") ?? void 0,
|
|
3562
|
+
isAccordionEnabled: void 0,
|
|
3563
|
+
isAccordionExpandedByDefault: void 0,
|
|
3564
|
+
isAccordionSidebarDisplayed: void 0
|
|
3565
|
+
};
|
|
3566
|
+
if (propertiesTablet.layout === "accordion" || returnBlock.properties.default.layout === "accordion") {
|
|
3567
|
+
propertiesTablet.isAccordionEnabled = getPropertyValueByLabel(tabletOverwriteProperties, "accordion-enabled") ?? void 0;
|
|
3568
|
+
propertiesTablet.isAccordionExpandedByDefault = getPropertyValueByLabel(tabletOverwriteProperties, "accordion-expanded") ?? void 0;
|
|
3569
|
+
propertiesTablet.isAccordionSidebarDisplayed = getPropertyValueByLabel(tabletOverwriteProperties, "accordion-sidebar-displayed") ?? void 0;
|
|
3570
|
+
}
|
|
3571
|
+
if (Object.values(propertiesTablet).every((value) => value != null)) returnBlock.properties.tablet = propertiesTablet;
|
|
3572
|
+
}
|
|
3573
|
+
const mobileOverwriteProperty = getPropertyByLabel(blockMainProperties, "overwrite-tablet");
|
|
3574
|
+
if (mobileOverwriteProperty !== null) {
|
|
3575
|
+
const mobileOverwriteProperties = mobileOverwriteProperty.properties;
|
|
3576
|
+
const propertiesMobile = {
|
|
3577
|
+
layout: getPropertyValueByLabel(mobileOverwriteProperties, "layout") ?? void 0,
|
|
3578
|
+
spacing: getPropertyValueByLabel(mobileOverwriteProperties, "spacing") ?? void 0,
|
|
3579
|
+
gap: getPropertyValueByLabel(mobileOverwriteProperties, "gap") ?? void 0,
|
|
3580
|
+
isAccordionEnabled: void 0,
|
|
3581
|
+
isAccordionExpandedByDefault: void 0,
|
|
3582
|
+
isAccordionSidebarDisplayed: void 0
|
|
3583
|
+
};
|
|
3584
|
+
if (propertiesMobile.layout === "accordion" || returnBlock.properties.default.layout === "accordion") {
|
|
3585
|
+
propertiesMobile.isAccordionEnabled = getPropertyValueByLabel(mobileOverwriteProperties, "accordion-enabled") ?? void 0;
|
|
3586
|
+
propertiesMobile.isAccordionExpandedByDefault = getPropertyValueByLabel(mobileOverwriteProperties, "accordion-expanded") ?? void 0;
|
|
3587
|
+
propertiesMobile.isAccordionSidebarDisplayed = getPropertyValueByLabel(mobileOverwriteProperties, "accordion-sidebar-displayed") ?? void 0;
|
|
3588
|
+
}
|
|
3589
|
+
if (Object.values(propertiesMobile).every((value) => value != null)) returnBlock.properties.mobile = propertiesMobile;
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
const blockResources = blockResource.resource ? ensureArray(blockResource.resource) : [];
|
|
3593
|
+
if (returnBlock.properties.default.layout === "accordion") {
|
|
3594
|
+
const accordionItems = [];
|
|
3595
|
+
for (const resource of blockResources) {
|
|
3596
|
+
const resourceProperties = resource.properties ? parseProperties(ensureArray(resource.properties.property)) : [];
|
|
3597
|
+
const resourceType = getPropertyValueByLabel(resourceProperties, "presentation");
|
|
3598
|
+
if (resourceType !== "element") throw new Error(`Accordion only accepts elements, but got “${resourceType}” for the following resource: “${parseStringContent(resource.identification.label)}”`);
|
|
3599
|
+
const componentType = getPropertyValueByLabel(getPropertyByLabel(resourceProperties, "presentation")?.properties ?? [], "component");
|
|
3600
|
+
if (componentType !== "text") throw new Error(`Accordion only accepts text components, but got “${componentType}” for the following resource: “${parseStringContent(resource.identification.label)}”`);
|
|
3601
|
+
const element = parseWebElementForAccordion(resource);
|
|
3602
|
+
accordionItems.push(element);
|
|
3603
|
+
}
|
|
3604
|
+
returnBlock.items = accordionItems;
|
|
3605
|
+
} else {
|
|
3606
|
+
const blockItems = [];
|
|
3607
|
+
for (const resource of blockResources) {
|
|
3608
|
+
const resourceType = getPropertyValueByLabel(resource.properties ? parseProperties(ensureArray(resource.properties.property)) : [], "presentation");
|
|
3609
|
+
if (resourceType === null) continue;
|
|
3610
|
+
switch (resourceType) {
|
|
3611
|
+
case "element": {
|
|
3612
|
+
const element = parseWebElement(resource);
|
|
3613
|
+
blockItems.push(element);
|
|
3614
|
+
break;
|
|
3615
|
+
}
|
|
3616
|
+
case "block": {
|
|
3617
|
+
const block = parseWebBlock(resource);
|
|
3618
|
+
if (block) blockItems.push(block);
|
|
3619
|
+
break;
|
|
3620
|
+
}
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
returnBlock.items = blockItems;
|
|
3679
3624
|
}
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
/**
|
|
3683
|
-
* Schema for the property values by property variables OCHRE API response
|
|
3684
|
-
*/
|
|
3685
|
-
const responseSchema = z.object({ result: z.union([z.object({ ochre: z.object({ propertyValue: z.union([propertyValueQueryItemSchema, z.array(propertyValueQueryItemSchema)]) }) }), z.array(z.unknown()).length(0)]) });
|
|
3686
|
-
/**
|
|
3687
|
-
* Build an XQuery string to fetch property values by property variables from the OCHRE API
|
|
3688
|
-
* @param params - The parameters for the fetch
|
|
3689
|
-
* @param params.projectScopeUuid - The UUID of the project scope
|
|
3690
|
-
* @param params.belongsToCollectionScopeUuids - An array of collection scope UUIDs to filter by
|
|
3691
|
-
* @param params.propertyVariableUuids - An array of property variable UUIDs to fetch
|
|
3692
|
-
* @param options - Options for the fetch
|
|
3693
|
-
* @param options.version - The version of the OCHRE API to use
|
|
3694
|
-
* @returns An XQuery string
|
|
3695
|
-
*/
|
|
3696
|
-
function buildXQuery(params, options) {
|
|
3697
|
-
const version = options?.version ?? DEFAULT_API_VERSION;
|
|
3698
|
-
const { projectScopeUuid, belongsToCollectionScopeUuids, propertyVariableUuids } = params;
|
|
3699
|
-
let collectionScopeFilter = "";
|
|
3700
|
-
if (belongsToCollectionScopeUuids.length > 0) collectionScopeFilter = `//properties[property[label/@uuid="${BELONGS_TO_COLLECTION_UUID}" and value/(${belongsToCollectionScopeUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ")})]]`;
|
|
3701
|
-
const propertyVariableFilters = propertyVariableUuids.map((uuid) => `@uuid="${uuid}"`).join(" or ");
|
|
3702
|
-
return `<ochre>{${`let $matching-props := ${version === 2 ? "doc()" : "input()"}/ochre[@uuidBelongsTo="${projectScopeUuid}"]
|
|
3703
|
-
${collectionScopeFilter}
|
|
3704
|
-
/*[not(self::set)]
|
|
3705
|
-
//property[label/(${propertyVariableFilters})]
|
|
3706
|
-
|
|
3707
|
-
for $v in $matching-props/value
|
|
3708
|
-
let $item-uuid := $v/ancestor::*[parent::ochre]/@uuid
|
|
3709
|
-
return <propertyValue uuid="{$v/@uuid}" rawValue="{$v/@rawValue}" dataType="{$v/@dataType}" itemUuid="{$item-uuid}">{
|
|
3710
|
-
if ($v/content) then $v/content else $v/text()
|
|
3711
|
-
}</propertyValue>`}}</ochre>`;
|
|
3625
|
+
returnBlock.cssStyles = parseResponsiveCssStyles(blockProperties);
|
|
3626
|
+
return returnBlock;
|
|
3712
3627
|
}
|
|
3713
3628
|
/**
|
|
3714
|
-
*
|
|
3629
|
+
* Parses raw website properties into a standardized Website properties structure
|
|
3715
3630
|
*
|
|
3716
|
-
* @param
|
|
3717
|
-
* @
|
|
3718
|
-
* @param params.belongsToCollectionScopeUuids - The collection scope UUIDs to filter by
|
|
3719
|
-
* @param params.propertyVariableUuids - The property variable UUIDs to query by
|
|
3720
|
-
* @param options - Options for the fetch
|
|
3721
|
-
* @param options.fetch - The fetch function to use
|
|
3722
|
-
* @param options.version - The version of the OCHRE API to use
|
|
3723
|
-
* @returns The parsed property values by property variables or null if the fetch/parse fails
|
|
3631
|
+
* @param properties - Array of raw website properties in OCHRE format
|
|
3632
|
+
* @returns Parsed WebsiteProperties object
|
|
3724
3633
|
*/
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3634
|
+
function parseWebsiteProperties(properties, websiteTree, sidebar) {
|
|
3635
|
+
const websiteProperties = getPropertyByLabel(parseProperties(properties), "presentation")?.properties ?? [];
|
|
3636
|
+
let type = getPropertyValueByLabel(websiteProperties, "webUI");
|
|
3637
|
+
type ??= "traditional";
|
|
3638
|
+
let status = getPropertyValueByLabel(websiteProperties, "status");
|
|
3639
|
+
status ??= "development";
|
|
3640
|
+
let privacy = getPropertyValueByLabel(websiteProperties, "privacy");
|
|
3641
|
+
privacy ??= "public";
|
|
3642
|
+
const returnProperties = {
|
|
3643
|
+
type,
|
|
3644
|
+
status,
|
|
3645
|
+
privacy,
|
|
3646
|
+
contact: null,
|
|
3647
|
+
theme: {
|
|
3648
|
+
isThemeToggleDisplayed: true,
|
|
3649
|
+
defaultTheme: "system"
|
|
3650
|
+
},
|
|
3651
|
+
icon: {
|
|
3652
|
+
logoUuid: null,
|
|
3653
|
+
faviconUuid: null,
|
|
3654
|
+
appleTouchIconUuid: null
|
|
3655
|
+
},
|
|
3656
|
+
navbar: {
|
|
3657
|
+
isDisplayed: true,
|
|
3658
|
+
variant: "default",
|
|
3659
|
+
alignment: "start",
|
|
3660
|
+
isProjectDisplayed: true,
|
|
3661
|
+
searchBarBoundElementUuid: null,
|
|
3662
|
+
items: null
|
|
3663
|
+
},
|
|
3664
|
+
footer: {
|
|
3665
|
+
isDisplayed: true,
|
|
3666
|
+
items: null
|
|
3667
|
+
},
|
|
3668
|
+
sidebar,
|
|
3669
|
+
itemPage: {
|
|
3670
|
+
isMainContentDisplayed: true,
|
|
3671
|
+
isDescriptionDisplayed: true,
|
|
3672
|
+
isDocumentDisplayed: true,
|
|
3673
|
+
isNotesDisplayed: true,
|
|
3674
|
+
isEventsDisplayed: true,
|
|
3675
|
+
isPeriodsDisplayed: true,
|
|
3676
|
+
isPropertiesDisplayed: true,
|
|
3677
|
+
isBibliographyDisplayed: true,
|
|
3678
|
+
isPropertyValuesGrouped: true,
|
|
3679
|
+
iiifViewer: "universal-viewer"
|
|
3680
|
+
},
|
|
3681
|
+
options: {
|
|
3682
|
+
contexts: null,
|
|
3683
|
+
scopes: null,
|
|
3684
|
+
labels: { title: null }
|
|
3750
3685
|
}
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
return {
|
|
3759
|
-
items: groupedItems.toSorted((a, b) => {
|
|
3760
|
-
if (a.count !== b.count) return b.count - a.count;
|
|
3761
|
-
if (a.label !== b.label) return a.label?.localeCompare(b.label ?? "") ?? 0;
|
|
3762
|
-
return a.content?.toString().localeCompare(b.content?.toString() ?? "") ?? 0;
|
|
3763
|
-
}),
|
|
3764
|
-
error: null
|
|
3765
|
-
};
|
|
3766
|
-
} catch (error) {
|
|
3767
|
-
console.error(error);
|
|
3768
|
-
return {
|
|
3769
|
-
items: null,
|
|
3770
|
-
error: error instanceof Error ? error.message : "Failed to fetch property values by property variables"
|
|
3686
|
+
};
|
|
3687
|
+
const contactProperty = getPropertyByLabel(websiteProperties, "contact");
|
|
3688
|
+
if (contactProperty !== null) {
|
|
3689
|
+
const contactContent = contactProperty.values[0]?.content?.toString().split(";");
|
|
3690
|
+
if (contactContent?.length === 2) returnProperties.contact = {
|
|
3691
|
+
name: contactContent[0],
|
|
3692
|
+
email: contactContent[1] ?? null
|
|
3771
3693
|
};
|
|
3694
|
+
else throw new Error(`Contact property must be in the format “name;email”, but got “${contactProperty.values[0]?.content}”`);
|
|
3695
|
+
}
|
|
3696
|
+
returnProperties.theme.isThemeToggleDisplayed = getPropertyValueByLabel(websiteProperties, "supports-theme-toggle") ?? true;
|
|
3697
|
+
returnProperties.theme.defaultTheme = getPropertyValueByLabel(websiteProperties, "default-theme") ?? "system";
|
|
3698
|
+
returnProperties.icon.logoUuid = getPropertyByLabel(websiteProperties, "logo")?.values[0]?.uuid ?? null;
|
|
3699
|
+
returnProperties.navbar.isDisplayed = getPropertyValueByLabel(websiteProperties, "navbar-displayed") ?? true;
|
|
3700
|
+
returnProperties.navbar.variant = getPropertyValueByLabel(websiteProperties, "navbar-variant") ?? "default";
|
|
3701
|
+
returnProperties.navbar.alignment = getPropertyValueByLabel(websiteProperties, "navbar-alignment") ?? "start";
|
|
3702
|
+
returnProperties.navbar.isProjectDisplayed = getPropertyValueByLabel(websiteProperties, "navbar-project-displayed") ?? true;
|
|
3703
|
+
returnProperties.navbar.searchBarBoundElementUuid = getPropertyByLabel(websiteProperties, "bound-element-navbar-search-bar")?.values[0]?.uuid ?? null;
|
|
3704
|
+
returnProperties.footer.isDisplayed = getPropertyValueByLabel(websiteProperties, "footer-displayed") ?? true;
|
|
3705
|
+
const itemPageTypeProperty = getPropertyByLabelAndValue(websiteProperties, "page-type", "item-page");
|
|
3706
|
+
if (itemPageTypeProperty !== null) {
|
|
3707
|
+
returnProperties.itemPage.isMainContentDisplayed = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-main-content-displayed") ?? true;
|
|
3708
|
+
returnProperties.itemPage.isDescriptionDisplayed = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-description-displayed") ?? true;
|
|
3709
|
+
returnProperties.itemPage.isDocumentDisplayed = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-document-displayed") ?? true;
|
|
3710
|
+
returnProperties.itemPage.isNotesDisplayed = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-notes-displayed") ?? true;
|
|
3711
|
+
returnProperties.itemPage.isEventsDisplayed = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-events-displayed") ?? true;
|
|
3712
|
+
returnProperties.itemPage.isPeriodsDisplayed = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-periods-displayed") ?? true;
|
|
3713
|
+
returnProperties.itemPage.isPropertiesDisplayed = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-properties-displayed") ?? true;
|
|
3714
|
+
returnProperties.itemPage.isBibliographyDisplayed = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-bibliography-displayed") ?? true;
|
|
3715
|
+
returnProperties.itemPage.isPropertyValuesGrouped = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-property-values-grouped") ?? true;
|
|
3716
|
+
returnProperties.itemPage.iiifViewer = getPropertyValueByLabel(itemPageTypeProperty.properties, "item-page-iiif-viewer") ?? "universal-viewer";
|
|
3772
3717
|
}
|
|
3718
|
+
if ("options" in websiteTree && websiteTree.options) {
|
|
3719
|
+
returnProperties.options.scopes = websiteTree.options.scopes != null ? ensureArray(websiteTree.options.scopes.scope).map((scope) => ({
|
|
3720
|
+
uuid: scope.uuid.content,
|
|
3721
|
+
type: scope.uuid.type,
|
|
3722
|
+
identification: parseIdentification(scope.identification)
|
|
3723
|
+
})) : null;
|
|
3724
|
+
returnProperties.options.contexts = parseAllOptionContexts(websiteTree.options);
|
|
3725
|
+
if ("notes" in websiteTree.options && websiteTree.options.notes != null) {
|
|
3726
|
+
const labelNotes = parseNotes(ensureArray(websiteTree.options.notes.note));
|
|
3727
|
+
returnProperties.options.labels.title = labelNotes.find((note) => note.title === "Title label")?.content ?? null;
|
|
3728
|
+
}
|
|
3729
|
+
}
|
|
3730
|
+
return returnProperties;
|
|
3731
|
+
}
|
|
3732
|
+
function parseContexts(contexts) {
|
|
3733
|
+
const contextsParsed = [];
|
|
3734
|
+
for (const mainContext of contexts) for (const contextItemToParse of ensureArray(mainContext.context)) {
|
|
3735
|
+
const levelsToParse = ensureArray(contextItemToParse.levels.level);
|
|
3736
|
+
let type = "";
|
|
3737
|
+
const levels = levelsToParse.map((level) => {
|
|
3738
|
+
let variableUuid = "";
|
|
3739
|
+
let valueUuid = null;
|
|
3740
|
+
if (typeof level === "string") {
|
|
3741
|
+
const splitLevel = level.split(", ");
|
|
3742
|
+
variableUuid = splitLevel[0];
|
|
3743
|
+
valueUuid = splitLevel[1] === "null" ? null : splitLevel[1];
|
|
3744
|
+
} else {
|
|
3745
|
+
const splitLevel = level.content.split(", ");
|
|
3746
|
+
type = level.dataType;
|
|
3747
|
+
variableUuid = splitLevel[0];
|
|
3748
|
+
valueUuid = splitLevel[1] === "null" ? null : splitLevel[1];
|
|
3749
|
+
}
|
|
3750
|
+
return {
|
|
3751
|
+
variableUuid,
|
|
3752
|
+
valueUuid
|
|
3753
|
+
};
|
|
3754
|
+
});
|
|
3755
|
+
contextsParsed.push({
|
|
3756
|
+
context: levels,
|
|
3757
|
+
type,
|
|
3758
|
+
identification: parseIdentification(contextItemToParse.identification)
|
|
3759
|
+
});
|
|
3760
|
+
}
|
|
3761
|
+
return contextsParsed;
|
|
3762
|
+
}
|
|
3763
|
+
function parseWebsite(websiteTree, metadata, belongsTo, { version = DEFAULT_API_VERSION } = {}) {
|
|
3764
|
+
if (!websiteTree.properties) throw new Error("Website properties not found");
|
|
3765
|
+
if (typeof websiteTree.items === "string" || !("resource" in websiteTree.items)) throw new Error("Website pages not found");
|
|
3766
|
+
const resources = ensureArray(websiteTree.items.resource);
|
|
3767
|
+
const items = [...parseWebpages(resources), ...parseSegments(resources)];
|
|
3768
|
+
const sidebar = parseSidebar(resources);
|
|
3769
|
+
const properties = parseWebsiteProperties(ensureArray(websiteTree.properties.property), websiteTree, sidebar);
|
|
3770
|
+
return {
|
|
3771
|
+
uuid: websiteTree.uuid,
|
|
3772
|
+
version,
|
|
3773
|
+
belongsTo: belongsTo ?? null,
|
|
3774
|
+
metadata: parseMetadata(metadata),
|
|
3775
|
+
publicationDateTime: parseOptionalDate(websiteTree.publicationDateTime),
|
|
3776
|
+
identification: parseIdentification(websiteTree.identification),
|
|
3777
|
+
creators: websiteTree.creators ? parsePersons(ensureArray(websiteTree.creators.creator)) : [],
|
|
3778
|
+
license: parseLicense(websiteTree.availability),
|
|
3779
|
+
items,
|
|
3780
|
+
properties
|
|
3781
|
+
};
|
|
3773
3782
|
}
|
|
3774
3783
|
|
|
3775
3784
|
//#endregion
|