ochre-sdk 1.0.78 → 1.1.0
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/README.md +31 -3
- package/dist/_virtual/_rolldown/runtime.mjs +13 -0
- package/dist/categories.d.mts +249 -0
- package/dist/categories.mjs +259 -0
- package/dist/constants.d.mts +14 -0
- package/dist/constants.mjs +18 -1
- package/dist/errors.d.mts +23 -0
- package/dist/{utilities.mjs → errors.mjs} +33 -84
- package/dist/fetchers/gallery.mjs +15 -51
- package/dist/fetchers/item-children.mjs +20 -66
- package/dist/fetchers/item-links.mjs +26 -79
- package/dist/fetchers/item-ocr-data.d.mts +2 -2
- package/dist/fetchers/item-ocr-data.mjs +11 -15
- package/dist/fetchers/item.d.mts +0 -15
- package/dist/fetchers/item.mjs +20 -72
- package/dist/fetchers/request.d.mts +70 -0
- package/dist/fetchers/request.mjs +100 -0
- package/dist/fetchers/set/items.mjs +27 -73
- package/dist/fetchers/set/property-values.d.mts +2 -3
- package/dist/fetchers/set/property-values.mjs +96 -130
- package/dist/fetchers/website-metadata.mjs +35 -57
- package/dist/fetchers/website.d.mts +2 -3
- package/dist/fetchers/website.mjs +22 -31
- package/dist/getters.d.mts +78 -148
- package/dist/getters.mjs +127 -208
- package/dist/helpers.d.mts +0 -4
- package/dist/helpers.mjs +19 -6
- package/dist/index.d.mts +8 -6
- package/dist/index.mjs +6 -4
- package/dist/ocr.d.mts +37 -0
- package/dist/ocr.mjs +52 -0
- package/dist/parsers/helpers.d.mts +21 -1
- package/dist/parsers/helpers.mjs +26 -6
- package/dist/parsers/index.d.mts +0 -8
- package/dist/parsers/index.mjs +118 -259
- package/dist/parsers/languages.d.mts +72 -0
- package/dist/parsers/languages.mjs +132 -0
- package/dist/parsers/multilingual.d.mts +49 -74
- package/dist/parsers/multilingual.mjs +88 -189
- package/dist/parsers/property-token.d.mts +34 -0
- package/dist/parsers/property-token.mjs +29 -0
- package/dist/parsers/string.d.mts +19 -0
- package/dist/parsers/string.mjs +45 -25
- package/dist/parsers/website/bounds.d.mts +10 -0
- package/dist/parsers/website/bounds.mjs +28 -0
- package/dist/parsers/website/components.d.mts +91 -0
- package/dist/parsers/website/components.mjs +681 -0
- package/dist/parsers/website/index.d.mts +0 -7
- package/dist/parsers/website/index.mjs +92 -1153
- package/dist/parsers/website/links.d.mts +36 -0
- package/dist/parsers/website/links.mjs +58 -0
- package/dist/parsers/website/messages.d.mts +24 -0
- package/dist/parsers/website/messages.mjs +31 -0
- package/dist/parsers/website/options.d.mts +6 -0
- package/dist/parsers/website/options.mjs +114 -0
- package/dist/parsers/website/properties.d.mts +12 -0
- package/dist/parsers/website/properties.mjs +158 -0
- package/dist/parsers/website/reader.d.mts +54 -4
- package/dist/parsers/website/reader.mjs +65 -20
- package/dist/parsers/website/slug.d.mts +64 -0
- package/dist/parsers/website/slug.mjs +82 -0
- package/dist/parsers/website/styles.d.mts +28 -0
- package/dist/parsers/website/styles.mjs +103 -0
- package/dist/parsers/website/walk.d.mts +68 -0
- package/dist/parsers/website/walk.mjs +116 -0
- package/dist/query.d.mts +66 -18
- package/dist/query.mjs +202 -48
- package/dist/reflection.d.mts +64 -0
- package/dist/reflection.mjs +79 -0
- package/dist/schemas.d.mts +7 -0
- package/dist/schemas.mjs +12 -3
- package/dist/types/index.d.mts +1 -31
- package/dist/types/utilities.d.mts +9 -0
- package/dist/types/utilities.mjs +1 -0
- package/dist/types/website.d.mts +49 -58
- package/dist/xml/metadata.d.mts +16 -0
- package/dist/xml/metadata.mjs +32 -11
- package/dist/xml/schemas.d.mts +5970 -3
- package/dist/xml/schemas.mjs +43 -45
- package/dist/xml/types.d.mts +13 -30
- package/dist/xquery.d.mts +46 -0
- package/dist/xquery.mjs +66 -0
- package/package.json +3 -3
- package/dist/utilities.d.mts +0 -54
|
@@ -1,867 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { cleanObject, parseLicense } from "../helpers.mjs";
|
|
2
|
+
import { parseMetadataLanguages, resolveDefaultLanguage, resolveLanguages } from "../languages.mjs";
|
|
3
|
+
import { parseIdentification, parseLinks, parseMetadata, parsePersonList, parseSimplifiedProperties } from "../index.mjs";
|
|
4
|
+
import { findWebsiteLink, parseWebsiteLinkTarget, webImageFromLink } from "./links.mjs";
|
|
5
5
|
import { websitePresentationReader } from "./reader.mjs";
|
|
6
|
-
import
|
|
6
|
+
import { emptyResponsiveStyles, parseResponsiveCssStyles } from "./styles.mjs";
|
|
7
|
+
import { collectWebsitePageSlugs, normalizeWebsiteResources, readWebsitePages } from "./walk.mjs";
|
|
8
|
+
import { parseWebElement, parseWebTitle } from "./components.mjs";
|
|
9
|
+
import { parseWebsiteProperties } from "./properties.mjs";
|
|
7
10
|
//#region src/parsers/website/index.ts
|
|
8
|
-
function isWebsiteLink(link, category) {
|
|
9
|
-
return link.category === category;
|
|
10
|
-
}
|
|
11
|
-
function findWebsiteLink(links, category, isMatch) {
|
|
12
|
-
for (const link of links) if (isWebsiteLink(link, category) && (isMatch == null || isMatch(link))) return link;
|
|
13
|
-
return null;
|
|
14
|
-
}
|
|
15
|
-
function findWebsiteLinkByCategories(links, categories) {
|
|
16
|
-
for (const link of links) for (const category of categories) if (isWebsiteLink(link, category)) return link;
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
function getWebsiteLinks(links, category) {
|
|
20
|
-
const matchedLinks = [];
|
|
21
|
-
for (const link of links) if (isWebsiteLink(link, category)) matchedLinks.push(link);
|
|
22
|
-
return matchedLinks;
|
|
23
|
-
}
|
|
24
|
-
function transformPermanentIdentificationUrlToItemLink(url) {
|
|
25
|
-
return url.replace("https://pi.lib.uchicago.edu/1001/org/ochre/", "/item/");
|
|
26
|
-
}
|
|
27
|
-
function normalizeWebsiteResources(resources) {
|
|
28
|
-
const normalized = [];
|
|
29
|
-
const resourcesToNormalize = resources ?? [];
|
|
30
|
-
for (const resource of resourcesToNormalize) {
|
|
31
|
-
if ("identification" in resource) {
|
|
32
|
-
normalized.push(resource);
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
if ("resource" in resource) normalized.push(...resource.resource);
|
|
36
|
-
}
|
|
37
|
-
return normalized;
|
|
38
|
-
}
|
|
39
|
-
const SEGMENT_UNIQUE_SLUG_PREFIX_REGEX = /^\$[^-]*-/;
|
|
40
|
-
function cleanWebsitePageSlug(slug) {
|
|
41
|
-
return slug?.replace(SEGMENT_UNIQUE_SLUG_PREFIX_REGEX, "") ?? null;
|
|
42
|
-
}
|
|
43
|
-
function prefixSlug(slug, slugPrefix) {
|
|
44
|
-
if (slugPrefix === "" || slugPrefix == null) return slug;
|
|
45
|
-
if (slug === "") return slugPrefix;
|
|
46
|
-
return `${slugPrefix}/${slug}`;
|
|
47
|
-
}
|
|
48
|
-
function collectSegmentPageSlugs(trees, options, pageSlugsByUuid, segmentSlugPrefix) {
|
|
49
|
-
for (const tree of trees) {
|
|
50
|
-
const segmentSlug = tree.identification.abbreviation == null ? null : parseStringContent(tree.identification.abbreviation, options);
|
|
51
|
-
if (segmentSlug == null) throw new Error(`Slug not found for segment website (website uuid “${tree.uuid}”)`, { cause: tree });
|
|
52
|
-
collectWebsitePageSlugs(tree.items?.resource, options, prefixSlug(segmentSlug, segmentSlugPrefix), pageSlugsByUuid);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
function collectResourcePageSlug(resource, options, pageSlugsByUuid, slugPrefix) {
|
|
56
|
-
const slug = cleanWebsitePageSlug(resource.slug);
|
|
57
|
-
if (slug == null) throw new Error(`Slug not found for page (${formatXMLWebsiteResourceMetadata(resource)})`, { cause: resource });
|
|
58
|
-
const pageSlug = prefixSlug(slug, slugPrefix);
|
|
59
|
-
pageSlugsByUuid.set(resource.uuid, pageSlug);
|
|
60
|
-
collectWebsitePageSlugs(resource.resource, options, slugPrefix == null ? void 0 : pageSlug, pageSlugsByUuid, pageSlug);
|
|
61
|
-
}
|
|
62
|
-
function collectWebsitePageSlugs(resources, options, slugPrefix, pageSlugsByUuid = /* @__PURE__ */ new Map(), segmentSlugPrefix = slugPrefix) {
|
|
63
|
-
const slugResources = resources ?? [];
|
|
64
|
-
for (const resource of slugResources) {
|
|
65
|
-
if ("segments" in resource) {
|
|
66
|
-
collectSegmentPageSlugs(resource.segments.tree, options, pageSlugsByUuid, segmentSlugPrefix);
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
if (!("identification" in resource)) {
|
|
70
|
-
collectWebsitePageSlugs(resource.resource, options, slugPrefix, pageSlugsByUuid, segmentSlugPrefix);
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
const resourceProperties = parseSimplifiedProperties(resource.properties, options);
|
|
74
|
-
if (websitePresentationReader(resourceProperties).value("presentation") === "page") {
|
|
75
|
-
collectResourcePageSlug(resource, options, pageSlugsByUuid, slugPrefix);
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
collectWebsitePageSlugs(resource.resource, options, slugPrefix, pageSlugsByUuid, segmentSlugPrefix);
|
|
79
|
-
}
|
|
80
|
-
return pageSlugsByUuid;
|
|
81
|
-
}
|
|
82
|
-
function parseWebsiteLinkTarget(value, context) {
|
|
83
|
-
if (value == null) return null;
|
|
84
|
-
if (value.href != null) return transformPermanentIdentificationUrlToItemLink(value.href);
|
|
85
|
-
return (value.uuid == null ? void 0 : context.pageSlugsByUuid?.get(value.uuid)) ?? value.slug;
|
|
86
|
-
}
|
|
87
|
-
function formatXMLWebsiteResourceMetadata(resource) {
|
|
88
|
-
const metadata = [`label “${parseStringContent(resource.identification.label)}”`, `uuid “${resource.uuid}”`];
|
|
89
|
-
if (resource.slug != null) metadata.push(`slug “${resource.slug}”`);
|
|
90
|
-
if (resource.identification.abbreviation != null) metadata.push(`abbreviation “${parseStringContent(resource.identification.abbreviation)}”`);
|
|
91
|
-
return metadata.join(", ");
|
|
92
|
-
}
|
|
93
|
-
function formatComponentError(message, componentName, elementResource) {
|
|
94
|
-
return `${message} for component “${componentName ?? "(unknown)"}” (${formatXMLWebsiteResourceMetadata(elementResource)})`;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Extracts CSS style properties for a given presentation variant.
|
|
98
|
-
*
|
|
99
|
-
* @param properties - Array of properties to parse
|
|
100
|
-
* @param cssVariant - CSS variant to parse
|
|
101
|
-
* @returns Array of CSS styles
|
|
102
|
-
*/
|
|
103
|
-
function parseCssStylesFromProperties(properties, cssVariant) {
|
|
104
|
-
const label = cssVariant != null ? `css-${cssVariant}` : "css";
|
|
105
|
-
const cssProperties = websitePresentationReader(properties).nestedByValue("presentation", label).properties;
|
|
106
|
-
const styles = [];
|
|
107
|
-
for (const property of cssProperties) {
|
|
108
|
-
const value = property.values[0]?.content.toString();
|
|
109
|
-
if (value != null) styles.push({
|
|
110
|
-
label: property.variable.label,
|
|
111
|
-
value
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
return styles;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Parses responsive CSS styles (default, tablet, mobile) from properties.
|
|
118
|
-
*
|
|
119
|
-
* @param properties - Array of properties to parse
|
|
120
|
-
* @returns Object containing responsive CSS styles
|
|
121
|
-
*/
|
|
122
|
-
function parseResponsiveCssStyles(properties) {
|
|
123
|
-
return {
|
|
124
|
-
default: parseCssStylesFromProperties(properties),
|
|
125
|
-
tablet: parseCssStylesFromProperties(properties, "tablet"),
|
|
126
|
-
mobile: parseCssStylesFromProperties(properties, "mobile")
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Parses raw bounds data into a standardized bounds structure
|
|
131
|
-
*
|
|
132
|
-
* @param bounds - Raw bounds data in OCHRE format
|
|
133
|
-
* @returns Parsed bounds object
|
|
134
|
-
*/
|
|
135
|
-
function parseBounds(bounds) {
|
|
136
|
-
const [southWest, northEast] = bounds.trimStart().startsWith("[") ? parseJsonBounds(bounds) : bounds.split(";").map((pair) => pair.split(",").map((coordinate) => Number(coordinate.trim())));
|
|
137
|
-
if (southWest?.length !== 2 || northEast?.length !== 2 || southWest.some((coordinate) => Number.isNaN(coordinate)) || northEast.some((coordinate) => Number.isNaN(coordinate))) throw new Error(`Invalid bounds: ${bounds}`, { cause: bounds });
|
|
138
|
-
return [[southWest[0], southWest[1]], [northEast[0], northEast[1]]];
|
|
139
|
-
}
|
|
140
|
-
function parseJsonBounds(bounds) {
|
|
141
|
-
let parsed;
|
|
142
|
-
try {
|
|
143
|
-
parsed = JSON.parse(bounds);
|
|
144
|
-
} catch {
|
|
145
|
-
throw new Error(`Invalid bounds: ${bounds}`, { cause: bounds });
|
|
146
|
-
}
|
|
147
|
-
if (!isNumberPairArray(parsed)) throw new Error(`Invalid bounds: ${bounds}`, { cause: bounds });
|
|
148
|
-
return parsed;
|
|
149
|
-
}
|
|
150
|
-
function isNumberPairArray(value) {
|
|
151
|
-
return Array.isArray(value) && value.every((pair) => Array.isArray(pair) && pair.every((coordinate) => typeof coordinate === "number"));
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Parses all context option arrays from an options object.
|
|
155
|
-
*
|
|
156
|
-
* @param options - Options object containing context options
|
|
157
|
-
* @param options.flattenContexts - Flatten contexts
|
|
158
|
-
* @param options.suppressContexts - Suppress contexts
|
|
159
|
-
* @param options.filterContexts - Filter contexts
|
|
160
|
-
* @param options.sortContexts - Sort contexts
|
|
161
|
-
* @param options.detailContexts - Detail contexts
|
|
162
|
-
* @param options.downloadContexts - Download contexts
|
|
163
|
-
* @param options.labelContexts - Label contexts
|
|
164
|
-
* @param options.prominentContexts - Prominent contexts
|
|
165
|
-
* @returns Parsed context options
|
|
166
|
-
*/
|
|
167
|
-
function parseAllOptionContexts(options, parserOptions) {
|
|
168
|
-
function handleContexts(v) {
|
|
169
|
-
return parseContexts(v ?? [], parserOptions);
|
|
170
|
-
}
|
|
171
|
-
function handleFilterContexts(v) {
|
|
172
|
-
return parseFilterContexts(v ?? [], parserOptions);
|
|
173
|
-
}
|
|
174
|
-
return {
|
|
175
|
-
flatten: handleContexts(options.flattenContexts),
|
|
176
|
-
suppress: handleContexts(options.suppressContexts),
|
|
177
|
-
filter: handleFilterContexts(options.filterContexts),
|
|
178
|
-
sort: handleContexts(options.sortContexts),
|
|
179
|
-
detail: handleContexts(options.detailContexts),
|
|
180
|
-
download: handleContexts(options.downloadContexts),
|
|
181
|
-
label: handleContexts(options.labelContexts),
|
|
182
|
-
prominent: handleContexts(options.prominentContexts)
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
function parseWebsiteScopes(scopes, options) {
|
|
186
|
-
if (scopes == null) return null;
|
|
187
|
-
return Array.from(scopes.scope, (scope) => ({
|
|
188
|
-
uuid: scope.uuid.payload,
|
|
189
|
-
type: scope.uuid.type,
|
|
190
|
-
identification: parseIdentification(scope.identification, options)
|
|
191
|
-
}));
|
|
192
|
-
}
|
|
193
|
-
function parseWebsiteOptions(rawOptions, options) {
|
|
194
|
-
const parsedOptions = {
|
|
195
|
-
scopes: parseWebsiteScopes(rawOptions?.scopes, options),
|
|
196
|
-
contextTree: rawOptions == null ? null : parseAllOptionContexts(rawOptions, options),
|
|
197
|
-
labels: { title: null }
|
|
198
|
-
};
|
|
199
|
-
const notes = parseNotes(rawOptions?.notes, options);
|
|
200
|
-
for (const note of notes) {
|
|
201
|
-
if (note.title?.getText() !== "Title label") continue;
|
|
202
|
-
parsedOptions.labels.title = note.content;
|
|
203
|
-
break;
|
|
204
|
-
}
|
|
205
|
-
return parsedOptions;
|
|
206
|
-
}
|
|
207
|
-
function parseStylesheets(styles) {
|
|
208
|
-
const parsedStyles = [];
|
|
209
|
-
for (const style of styles) {
|
|
210
|
-
const defaultStyles = [];
|
|
211
|
-
for (const [label, value] of Object.entries(style)) {
|
|
212
|
-
if ([
|
|
213
|
-
"variableUuid",
|
|
214
|
-
"valueUuid",
|
|
215
|
-
"category",
|
|
216
|
-
"payload",
|
|
217
|
-
"content"
|
|
218
|
-
].includes(label)) continue;
|
|
219
|
-
const valueString = value?.toString();
|
|
220
|
-
if (valueString != null) defaultStyles.push({
|
|
221
|
-
label,
|
|
222
|
-
value: valueString
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
const stylesByViewport = {
|
|
226
|
-
default: defaultStyles,
|
|
227
|
-
tablet: [],
|
|
228
|
-
mobile: []
|
|
229
|
-
};
|
|
230
|
-
if (style.category === "propertyValue" || style.valueUuid != null) {
|
|
231
|
-
if (style.valueUuid == null) throw new Error(`Stylesheet property value "${style.variableUuid}" is missing a value UUID`, { cause: style });
|
|
232
|
-
parsedStyles.push({
|
|
233
|
-
uuid: style.valueUuid,
|
|
234
|
-
category: "propertyValue",
|
|
235
|
-
variableUuid: style.variableUuid,
|
|
236
|
-
icon: style.lucideIcon ?? null,
|
|
237
|
-
styles: stylesByViewport
|
|
238
|
-
});
|
|
239
|
-
continue;
|
|
240
|
-
}
|
|
241
|
-
parsedStyles.push({
|
|
242
|
-
uuid: style.variableUuid,
|
|
243
|
-
category: "propertyVariable",
|
|
244
|
-
icon: style.lucideIcon ?? null,
|
|
245
|
-
styles: stylesByViewport
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
return parsedStyles;
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Default values for a collection's display properties, shared between the
|
|
252
|
-
* "collection" component and the "query" component's collection overrides.
|
|
253
|
-
*/
|
|
254
|
-
const COLLECTION_PROPERTY_DEFAULTS = {
|
|
255
|
-
variant: "slide",
|
|
256
|
-
paginationVariant: "default",
|
|
257
|
-
loadingVariant: "skeleton",
|
|
258
|
-
minimumColumnCount: null,
|
|
259
|
-
maximumColumnCount: null,
|
|
260
|
-
expectedItemCount: null,
|
|
261
|
-
isSortDisplayed: false,
|
|
262
|
-
isUsingQueryParams: false,
|
|
263
|
-
isInteractive: true
|
|
264
|
-
};
|
|
265
|
-
/**
|
|
266
|
-
* Default values for a collection's image display properties, merged separately
|
|
267
|
-
* from {@link COLLECTION_PROPERTY_DEFAULTS} because overrides of the nested
|
|
268
|
-
* "image" object are themselves partial.
|
|
269
|
-
*/
|
|
270
|
-
const COLLECTION_IMAGE_DEFAULTS = {
|
|
271
|
-
layout: "start",
|
|
272
|
-
fit: "fit",
|
|
273
|
-
alignment: null,
|
|
274
|
-
isPlaceholderDisplayed: true
|
|
275
|
-
};
|
|
276
|
-
/**
|
|
277
|
-
* Reads the collection display properties explicitly set on a reader, omitting
|
|
278
|
-
* any that are unset. The "collection" component merges these over
|
|
279
|
-
* {@link COLLECTION_PROPERTY_DEFAULTS} and {@link COLLECTION_IMAGE_DEFAULTS},
|
|
280
|
-
* while the "query" component uses them as partial overrides for its embedded
|
|
281
|
-
* collection.
|
|
282
|
-
*/
|
|
283
|
-
function parseCollectionPropertyOverrides(reader) {
|
|
284
|
-
const overrides = {};
|
|
285
|
-
const imageOverrides = {};
|
|
286
|
-
function read(key, label) {
|
|
287
|
-
const value = reader.value(label);
|
|
288
|
-
if (value != null) overrides[key] = value;
|
|
289
|
-
}
|
|
290
|
-
function readImage(key, label) {
|
|
291
|
-
const value = reader.value(label);
|
|
292
|
-
if (value != null) imageOverrides[key] = value;
|
|
293
|
-
}
|
|
294
|
-
read("variant", "variant");
|
|
295
|
-
read("paginationVariant", "pagination-variant");
|
|
296
|
-
read("loadingVariant", "loading-variant");
|
|
297
|
-
read("minimumColumnCount", "minimum-column-count");
|
|
298
|
-
read("maximumColumnCount", "maximum-column-count");
|
|
299
|
-
read("expectedItemCount", "item-count");
|
|
300
|
-
read("isSortDisplayed", "sort-displayed");
|
|
301
|
-
read("isUsingQueryParams", "is-using-query-params");
|
|
302
|
-
read("isInteractive", "is-interactive");
|
|
303
|
-
readImage("layout", "image-layout");
|
|
304
|
-
readImage("fit", "image-fit");
|
|
305
|
-
readImage("alignment", "image-alignment");
|
|
306
|
-
readImage("isPlaceholderDisplayed", "image-placeholder-displayed");
|
|
307
|
-
return Object.keys(imageOverrides).length > 0 ? {
|
|
308
|
-
...overrides,
|
|
309
|
-
image: imageOverrides
|
|
310
|
-
} : overrides;
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Parses the "use-property" values defining which item properties a collection
|
|
314
|
-
* displays, returning `null` when none are set.
|
|
315
|
-
*/
|
|
316
|
-
function parseCollectionDisplayedProperties(reader) {
|
|
317
|
-
const property = reader.property("use-property");
|
|
318
|
-
if (property == null) return null;
|
|
319
|
-
return property.values.filter((value) => value.uuid !== null).map((value) => ({
|
|
320
|
-
uuid: value.uuid,
|
|
321
|
-
label: value.label
|
|
322
|
-
}));
|
|
323
|
-
}
|
|
324
|
-
function readStringOrNumber(reader, label) {
|
|
325
|
-
const value = reader.value(label);
|
|
326
|
-
if (typeof value === "string") return value;
|
|
327
|
-
if (typeof value === "number") return value.toString();
|
|
328
|
-
return null;
|
|
329
|
-
}
|
|
330
|
-
function parse3dViewerComponent(parameters) {
|
|
331
|
-
const { componentProperty, componentReader, elementResource, websiteLinks } = parameters;
|
|
332
|
-
const resourceLink = findWebsiteLink(websiteLinks, "resource", (link) => link.fileFormat === "model/obj");
|
|
333
|
-
if (resourceLink == null) throw new Error(formatComponentError("Resource link not found", "3d-viewer", elementResource), { cause: componentProperty });
|
|
334
|
-
const isInteractive = componentReader.valueOr("is-interactive", true);
|
|
335
|
-
const isControlsDisplayed = componentReader.valueOr("controls-displayed", true);
|
|
336
|
-
return {
|
|
337
|
-
component: "3d-viewer",
|
|
338
|
-
linkUuid: resourceLink.uuid,
|
|
339
|
-
fileSize: resourceLink.fileSize,
|
|
340
|
-
isInteractive,
|
|
341
|
-
isControlsDisplayed
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
function parseAdvancedSearchComponent(parameters) {
|
|
345
|
-
const { componentProperty, componentReader, elementResource, options, context } = parameters;
|
|
346
|
-
const boundElementPropertyUuid = componentReader.uuid("bound-element");
|
|
347
|
-
const href = parseWebsiteLinkTarget(componentReader.valueNode("link-to"), context);
|
|
348
|
-
if (boundElementPropertyUuid == null && href == null) throw new Error(formatComponentError("Bound element or href not found", "advanced-search", elementResource), { cause: componentProperty });
|
|
349
|
-
return {
|
|
350
|
-
component: "advanced-search",
|
|
351
|
-
boundElementUuid: boundElementPropertyUuid,
|
|
352
|
-
href,
|
|
353
|
-
options: parseWebsiteOptions(elementResource.options, options)
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
function parseAnnotatedDocumentComponent(parameters) {
|
|
357
|
-
const { componentProperty, elementResource, websiteLinks } = parameters;
|
|
358
|
-
const documentLink = findWebsiteLink(websiteLinks, "resource", (link) => link.type === "internalDocument");
|
|
359
|
-
if (documentLink == null) throw new Error(formatComponentError("Document link not found", "annotated-document", elementResource), { cause: componentProperty });
|
|
360
|
-
return {
|
|
361
|
-
component: "annotated-document",
|
|
362
|
-
linkUuid: documentLink.uuid
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
function parseAnnotatedImageComponent(parameters) {
|
|
366
|
-
const { componentProperty, componentReader, elementResource, websiteLinks } = parameters;
|
|
367
|
-
const imageLinks = getWebsiteLinks(websiteLinks, "resource").filter((link) => link.type === "image" || link.type === "IIIF");
|
|
368
|
-
if (imageLinks.length === 0) throw new Error(formatComponentError("Image link not found", "annotated-image", elementResource), { cause: componentProperty });
|
|
369
|
-
const isFilterInputDisplayed = componentReader.valueOr("filter-input-displayed", true);
|
|
370
|
-
const isOptionsDisplayed = componentReader.valueOr("options-displayed", true);
|
|
371
|
-
const isAnnotationHighlightsDisplayed = componentReader.valueOr("annotation-highlights-displayed", true);
|
|
372
|
-
const isAnnotationTooltipsDisplayed = componentReader.valueOr("annotation-tooltips-displayed", true);
|
|
373
|
-
return {
|
|
374
|
-
component: "annotated-image",
|
|
375
|
-
linkUuid: imageLinks[0].uuid,
|
|
376
|
-
isFilterInputDisplayed,
|
|
377
|
-
isOptionsDisplayed,
|
|
378
|
-
isAnnotationHighlightsDisplayed,
|
|
379
|
-
isAnnotationTooltipsDisplayed
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
function parseAudioPlayerComponent(parameters) {
|
|
383
|
-
const { componentProperty, componentReader, elementResource, websiteLinks } = parameters;
|
|
384
|
-
const audioLink = findWebsiteLink(websiteLinks, "resource", (link) => link.type === "audio");
|
|
385
|
-
if (audioLink == null) throw new Error(formatComponentError("Audio link not found", "audio-player", elementResource), { cause: componentProperty });
|
|
386
|
-
const isSpeedControlsDisplayed = componentReader.valueOr("speed-controls-displayed", true);
|
|
387
|
-
const isVolumeControlsDisplayed = componentReader.valueOr("volume-controls-displayed", true);
|
|
388
|
-
const isSeekBarDisplayed = componentReader.valueOr("seek-bar-displayed", true);
|
|
389
|
-
return {
|
|
390
|
-
component: "audio-player",
|
|
391
|
-
linkUuid: audioLink.uuid,
|
|
392
|
-
isSpeedControlsDisplayed,
|
|
393
|
-
isVolumeControlsDisplayed,
|
|
394
|
-
isSeekBarDisplayed
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
function parseBibliographyComponent(parameters) {
|
|
398
|
-
const { componentProperty, componentReader, elementResource, websiteLinks, options } = parameters;
|
|
399
|
-
const bibliographies = parseBibliographyList(elementResource.bibliographies, options);
|
|
400
|
-
if (websiteLinks.length === 0 && bibliographies.length === 0) throw new Error(formatComponentError("No links found", "bibliography", elementResource), { cause: componentProperty });
|
|
401
|
-
const layout = componentReader.valueOr("layout", "long");
|
|
402
|
-
const isSourceDocumentDisplayed = componentReader.valueOr("source-document-displayed", true);
|
|
403
|
-
return {
|
|
404
|
-
component: "bibliography",
|
|
405
|
-
linkUuids: websiteLinks.map((link) => link.uuid),
|
|
406
|
-
bibliographies,
|
|
407
|
-
layout,
|
|
408
|
-
isSourceDocumentDisplayed
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
function parseButtonComponent(parameters) {
|
|
412
|
-
const { componentProperty, componentReader, elementResource, websiteLinks, options, context } = parameters;
|
|
413
|
-
const variant = componentReader.valueOr("variant", "default");
|
|
414
|
-
let isExternal = false;
|
|
415
|
-
let isRelative = false;
|
|
416
|
-
let href = parseWebsiteLinkTarget(componentReader.valueNode("navigate-to"), context);
|
|
417
|
-
if (href === null) {
|
|
418
|
-
href = parseWebsiteLinkTarget(componentReader.valueNode("link-to"), context);
|
|
419
|
-
if (href === null) throw new Error(formatComponentError("Properties “navigate-to” or “link-to” not found", "button", elementResource), { cause: componentProperty });
|
|
420
|
-
isExternal = href.startsWith("http");
|
|
421
|
-
isRelative = !href.startsWith("/");
|
|
422
|
-
}
|
|
423
|
-
const startIcon = componentReader.value("start-icon");
|
|
424
|
-
const endIcon = componentReader.value("end-icon");
|
|
425
|
-
let image = null;
|
|
426
|
-
const imageLink = findWebsiteLink(websiteLinks, "resource", (link) => link.type === "image" || link.type === "IIIF");
|
|
427
|
-
if (imageLink != null) image = {
|
|
428
|
-
uuid: imageLink.uuid,
|
|
429
|
-
label: imageLink.identification.label,
|
|
430
|
-
width: imageLink.image?.width ?? 0,
|
|
431
|
-
height: imageLink.image?.height ?? 0,
|
|
432
|
-
description: imageLink.description,
|
|
433
|
-
quality: "high"
|
|
434
|
-
};
|
|
435
|
-
const childResources = normalizeWebsiteResources(elementResource.resource);
|
|
436
|
-
const elements = [];
|
|
437
|
-
for (const childResource of childResources) {
|
|
438
|
-
const childReader = websitePresentationReader(parseSimplifiedProperties(childResource.properties, options));
|
|
439
|
-
if (childReader.value("presentation") !== "element") continue;
|
|
440
|
-
if (childReader.nestedByValue("presentation", "element").value("component") === "button") continue;
|
|
441
|
-
elements.push(parseWebElement(childResource, options, context));
|
|
442
|
-
}
|
|
443
|
-
return {
|
|
444
|
-
component: "button",
|
|
445
|
-
variant,
|
|
446
|
-
href,
|
|
447
|
-
isExternal,
|
|
448
|
-
isRelative,
|
|
449
|
-
label: elementResource.document && "content" in elementResource.document ? parseXMLContent(elementResource.document, options) : null,
|
|
450
|
-
startIcon,
|
|
451
|
-
endIcon,
|
|
452
|
-
image,
|
|
453
|
-
elements
|
|
454
|
-
};
|
|
455
|
-
}
|
|
456
|
-
function parseCollectionComponent(parameters) {
|
|
457
|
-
const { componentProperty, componentReader, elementResource, websiteLinks, options } = parameters;
|
|
458
|
-
const setLinks = getWebsiteLinks(websiteLinks, "set");
|
|
459
|
-
if (setLinks.length === 0) throw new Error(formatComponentError("Set links not found", "collection", elementResource), { cause: componentProperty });
|
|
460
|
-
const isFilterResultsBarDisplayed = componentReader.valueOr("filter-results-bar-displayed", false);
|
|
461
|
-
const isFilterInputDisplayed = componentReader.valueOr("filter-input-displayed", false);
|
|
462
|
-
const isFilterLimitedToInputFilter = componentReader.valueOr("filter-limit-to-input-filter", false);
|
|
463
|
-
const isFilterLimitedToLeafPropertyValues = componentReader.valueOr("filter-limit-to-leaf-property-values", false);
|
|
464
|
-
const isFilterSidebarDisplayed = componentReader.valueOr("filter-sidebar-displayed", false);
|
|
465
|
-
const filterSidebarSort = componentReader.valueOr("filter-sidebar-sort", "default");
|
|
466
|
-
const isFilterSidebarHelpTooltipsDisplayed = componentReader.valueOr("filter-sidebar-help-tooltips-displayed", false);
|
|
467
|
-
const componentOptions = parseWebsiteOptions(elementResource.options, options);
|
|
468
|
-
const propertyOverrides = parseCollectionPropertyOverrides(componentReader);
|
|
469
|
-
return {
|
|
470
|
-
component: "collection",
|
|
471
|
-
linkUuids: setLinks.map((link) => link.uuid),
|
|
472
|
-
displayedProperties: parseCollectionDisplayedProperties(componentReader),
|
|
473
|
-
...COLLECTION_PROPERTY_DEFAULTS,
|
|
474
|
-
...propertyOverrides,
|
|
475
|
-
image: {
|
|
476
|
-
...COLLECTION_IMAGE_DEFAULTS,
|
|
477
|
-
...propertyOverrides.image
|
|
478
|
-
},
|
|
479
|
-
filter: {
|
|
480
|
-
isSidebarDisplayed: isFilterSidebarDisplayed,
|
|
481
|
-
isResultsBarDisplayed: isFilterResultsBarDisplayed,
|
|
482
|
-
isInputDisplayed: isFilterInputDisplayed,
|
|
483
|
-
isLimitedToInputFilter: isFilterLimitedToInputFilter,
|
|
484
|
-
isLimitedToLeafPropertyValues: isFilterLimitedToLeafPropertyValues,
|
|
485
|
-
sidebarSort: filterSidebarSort,
|
|
486
|
-
isSidebarHelpTooltipsDisplayed: isFilterSidebarHelpTooltipsDisplayed
|
|
487
|
-
},
|
|
488
|
-
options: componentOptions
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
function parseEmptySpaceComponent(parameters) {
|
|
492
|
-
const { componentReader } = parameters;
|
|
493
|
-
return {
|
|
494
|
-
component: "empty-space",
|
|
495
|
-
height: componentReader.stringValue("height"),
|
|
496
|
-
width: componentReader.stringValue("width")
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
function parseEntriesComponent(parameters) {
|
|
500
|
-
const { componentProperty, componentReader, elementResource, websiteLinks } = parameters;
|
|
501
|
-
const entriesLink = findWebsiteLinkByCategories(websiteLinks, ["set", "tree"]);
|
|
502
|
-
if (entriesLink == null) throw new Error(formatComponentError("Entries link not found", "entries", elementResource), { cause: componentProperty });
|
|
503
|
-
const variant = componentReader.valueOr("variant", "entry");
|
|
504
|
-
const isFilterInputDisplayed = componentReader.valueOr("filter-input-displayed", false);
|
|
505
|
-
return {
|
|
506
|
-
component: "entries",
|
|
507
|
-
linkUuid: entriesLink.uuid,
|
|
508
|
-
variant,
|
|
509
|
-
isFilterInputDisplayed
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
function parseIframeComponent(parameters) {
|
|
513
|
-
const { componentProperty, componentReader, elementResource, websiteLinks } = parameters;
|
|
514
|
-
const webpageLink = findWebsiteLink(websiteLinks, "resource", (link) => link.type === "webpage");
|
|
515
|
-
if (webpageLink?.href == null) throw new Error(formatComponentError("URL not found", "iframe", elementResource), { cause: componentProperty });
|
|
516
|
-
return {
|
|
517
|
-
component: "iframe",
|
|
518
|
-
href: transformPermanentIdentificationUrlToItemLink(webpageLink.href),
|
|
519
|
-
height: componentReader.stringValue("height"),
|
|
520
|
-
width: componentReader.stringValue("width")
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
function parseIiifViewerComponent(parameters) {
|
|
524
|
-
const { componentProperty, componentReader, elementResource, websiteLinks } = parameters;
|
|
525
|
-
const manifestLink = findWebsiteLink(websiteLinks, "resource", (link) => link.type === "IIIF");
|
|
526
|
-
if (manifestLink == null) throw new Error(formatComponentError("Manifest link not found", "iiif-viewer", elementResource), { cause: componentProperty });
|
|
527
|
-
const variant = componentReader.valueOr("variant", "universal-viewer");
|
|
528
|
-
return {
|
|
529
|
-
component: "iiif-viewer",
|
|
530
|
-
linkUuid: manifestLink.uuid,
|
|
531
|
-
variant
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
function parseImageComponent(parameters) {
|
|
535
|
-
const { componentProperty, componentReader, elementResource, websiteLinks } = parameters;
|
|
536
|
-
if (websiteLinks.length === 0) throw new Error(formatComponentError("No links found", "image", elementResource), { cause: componentProperty });
|
|
537
|
-
const imageQuality = componentReader.valueOr("image-quality", "high");
|
|
538
|
-
const images = Array.from(websiteLinks, (link) => ({
|
|
539
|
-
uuid: link.uuid,
|
|
540
|
-
label: link.identification.label,
|
|
541
|
-
width: "image" in link ? link.image?.width ?? 0 : 0,
|
|
542
|
-
height: "image" in link ? link.image?.height ?? 0 : 0,
|
|
543
|
-
description: link.description,
|
|
544
|
-
quality: imageQuality
|
|
545
|
-
}));
|
|
546
|
-
const variant = componentReader.valueOr("variant", "default");
|
|
547
|
-
const captionLayout = componentReader.valueOr("layout-caption", "bottom");
|
|
548
|
-
const width = readStringOrNumber(componentReader, "width");
|
|
549
|
-
const height = readStringOrNumber(componentReader, "height");
|
|
550
|
-
const isFullWidth = componentReader.valueOr("is-full-width", true);
|
|
551
|
-
const isFullHeight = componentReader.valueOr("is-full-height", true);
|
|
552
|
-
const captionSource = componentReader.valueOr("source-caption", "name");
|
|
553
|
-
const altTextSource = componentReader.valueOr("alt-text-source", "name");
|
|
554
|
-
const isTransparentBackground = componentReader.valueOr("is-transparent", false);
|
|
555
|
-
const isCover = componentReader.valueOr("is-cover", false);
|
|
556
|
-
const variantReader = componentReader.nested("variant");
|
|
557
|
-
let carouselOptions = null;
|
|
558
|
-
if (images.length > 1) {
|
|
559
|
-
const secondsPerImage = variant === "carousel" ? readStringOrNumber(variantReader, "seconds-per-image") : null;
|
|
560
|
-
carouselOptions = { secondsPerImage: Number(secondsPerImage ?? 5) };
|
|
561
|
-
}
|
|
562
|
-
let heroOptions = null;
|
|
563
|
-
if (variant === "hero") heroOptions = {
|
|
564
|
-
isBackgroundImageDisplayed: variantReader.valueOr("background-image-displayed", true),
|
|
565
|
-
isDocumentDisplayed: variantReader.valueOr("document-displayed", true)
|
|
566
|
-
};
|
|
567
|
-
return {
|
|
568
|
-
component: "image",
|
|
569
|
-
images,
|
|
570
|
-
variant,
|
|
571
|
-
width,
|
|
572
|
-
height,
|
|
573
|
-
isFullWidth,
|
|
574
|
-
isFullHeight,
|
|
575
|
-
imageQuality,
|
|
576
|
-
captionLayout,
|
|
577
|
-
captionSource,
|
|
578
|
-
altTextSource,
|
|
579
|
-
isTransparentBackground,
|
|
580
|
-
isCover,
|
|
581
|
-
carouselOptions,
|
|
582
|
-
heroOptions
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
|
-
function parseImageGalleryComponent(parameters) {
|
|
586
|
-
const { componentProperty, componentReader, elementResource, websiteLinks } = parameters;
|
|
587
|
-
const galleryLink = findWebsiteLinkByCategories(websiteLinks, ["set", "tree"]);
|
|
588
|
-
if (galleryLink == null) throw new Error(formatComponentError("Image gallery link not found", "image-gallery", elementResource), { cause: componentProperty });
|
|
589
|
-
const isFilterInputDisplayed = componentReader.valueOr("filter-input-displayed", true);
|
|
590
|
-
return {
|
|
591
|
-
component: "image-gallery",
|
|
592
|
-
linkUuid: galleryLink.uuid,
|
|
593
|
-
isFilterInputDisplayed
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
function parseMapComponent(parameters) {
|
|
597
|
-
const { componentProperty, componentReader, elementResource, websiteLinks } = parameters;
|
|
598
|
-
const mapLink = findWebsiteLinkByCategories(websiteLinks, ["set", "tree"]);
|
|
599
|
-
if (mapLink == null) throw new Error(formatComponentError("Map link not found", "map", elementResource), { cause: componentProperty });
|
|
600
|
-
const isInteractive = componentReader.valueOr("is-interactive", true);
|
|
601
|
-
const isClustered = componentReader.valueOr("is-clustered", false);
|
|
602
|
-
const isUsingPins = componentReader.valueOr("is-using-pins", false);
|
|
603
|
-
const customBasemap = componentReader.value("custom-basemap");
|
|
604
|
-
let initialBounds = null;
|
|
605
|
-
const initialBoundsProperty = componentReader.value("initial-bounds");
|
|
606
|
-
if (initialBoundsProperty !== null) initialBounds = parseBounds(String(initialBoundsProperty));
|
|
607
|
-
let maximumBounds = null;
|
|
608
|
-
const maximumBoundsProperty = componentReader.value("maximum-bounds");
|
|
609
|
-
if (maximumBoundsProperty !== null) maximumBounds = parseBounds(String(maximumBoundsProperty));
|
|
610
|
-
const isControlsDisplayed = componentReader.valueOr("controls-displayed", false);
|
|
611
|
-
const isFullHeight = componentReader.valueOr("is-full-height", false);
|
|
612
|
-
return {
|
|
613
|
-
component: "map",
|
|
614
|
-
linkUuid: mapLink.uuid,
|
|
615
|
-
customBasemap,
|
|
616
|
-
initialBounds,
|
|
617
|
-
maximumBounds,
|
|
618
|
-
isInteractive,
|
|
619
|
-
isClustered,
|
|
620
|
-
isUsingPins,
|
|
621
|
-
isControlsDisplayed,
|
|
622
|
-
isFullHeight
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
function parseQueryItemQueries(propertyVariables, queryLanguage, elementResource) {
|
|
626
|
-
const queries = [];
|
|
627
|
-
for (const propertyVariable of propertyVariables) {
|
|
628
|
-
if (propertyVariable.uuid === null) throw new Error(formatComponentError("Property variable UUID not found", "query", elementResource), { cause: propertyVariable });
|
|
629
|
-
const dataType = propertyVariable.dataType;
|
|
630
|
-
if (dataType === "coordinate") throw new Error(formatComponentError("Query prompts with data type \"coordinate\" are not supported", "query", elementResource), { cause: propertyVariable });
|
|
631
|
-
queries.push({
|
|
632
|
-
target: "property",
|
|
633
|
-
propertyVariable: propertyVariable.uuid,
|
|
634
|
-
dataType,
|
|
635
|
-
matchMode: "exact",
|
|
636
|
-
isCaseSensitive: true,
|
|
637
|
-
language: queryLanguage
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
return queries;
|
|
641
|
-
}
|
|
642
|
-
function parseQueryComponent(parameters) {
|
|
643
|
-
const { componentProperty, componentReader, elementResource, websiteLinks, options } = parameters;
|
|
644
|
-
const setLinks = getWebsiteLinks(websiteLinks, "set");
|
|
645
|
-
if (setLinks.length === 0) throw new Error(formatComponentError("Set links not found", "query", elementResource), { cause: componentProperty });
|
|
646
|
-
if (componentProperty.properties.length === 0) throw new Error(formatComponentError("Query properties not found", "query", elementResource), { cause: componentProperty });
|
|
647
|
-
const items = [];
|
|
648
|
-
for (const queryItem of componentProperty.properties) {
|
|
649
|
-
const queryReader = websitePresentationReader(queryItem.properties);
|
|
650
|
-
const label = queryReader.multilingualValue("query-prompt", options);
|
|
651
|
-
if (label === null) continue;
|
|
652
|
-
const propertyVariables = queryReader.values("use-property").filter((value) => value.uuid !== null);
|
|
653
|
-
const queryLanguage = options.languages[0];
|
|
654
|
-
if (queryLanguage == null) throw new Error(formatComponentError("Query language not found", "query", elementResource));
|
|
655
|
-
const queries = parseQueryItemQueries(propertyVariables, queryLanguage, elementResource);
|
|
656
|
-
const startIcon = queryReader.value("start-icon");
|
|
657
|
-
const endIcon = queryReader.value("end-icon");
|
|
658
|
-
items.push({
|
|
659
|
-
label,
|
|
660
|
-
queries,
|
|
661
|
-
startIcon,
|
|
662
|
-
endIcon
|
|
663
|
-
});
|
|
664
|
-
}
|
|
665
|
-
if (items.length === 0) throw new Error(formatComponentError("No queries found", "query", elementResource), { cause: componentProperty });
|
|
666
|
-
const componentOptions = parseWebsiteOptions(elementResource.options, options);
|
|
667
|
-
const collectionProperties = parseCollectionPropertyOverrides(componentReader.nestedByValue("sub-component-override", "collection"));
|
|
668
|
-
const displayedProperties = parseCollectionDisplayedProperties(componentReader);
|
|
669
|
-
if (displayedProperties != null) collectionProperties.displayedProperties = displayedProperties;
|
|
670
|
-
return {
|
|
671
|
-
component: "query",
|
|
672
|
-
linkUuids: setLinks.map((link) => link.uuid),
|
|
673
|
-
items,
|
|
674
|
-
options: componentOptions,
|
|
675
|
-
collectionProperties
|
|
676
|
-
};
|
|
677
|
-
}
|
|
678
|
-
function parseTableComponent(parameters) {
|
|
679
|
-
const { componentProperty, elementResource, websiteLinks } = parameters;
|
|
680
|
-
const tableLink = findWebsiteLink(websiteLinks, "set");
|
|
681
|
-
if (tableLink == null) throw new Error(formatComponentError("Table link not found", "table", elementResource), { cause: componentProperty });
|
|
682
|
-
return {
|
|
683
|
-
component: "table",
|
|
684
|
-
linkUuid: tableLink.uuid
|
|
685
|
-
};
|
|
686
|
-
}
|
|
687
|
-
function parseSearchBarComponent(parameters) {
|
|
688
|
-
const { componentProperty, componentReader, elementResource, options, context } = parameters;
|
|
689
|
-
const queryVariant = componentReader.valueOr("query-variant", "submit");
|
|
690
|
-
const boundElementUuid = componentReader.uuid("bound-element");
|
|
691
|
-
const href = parseWebsiteLinkTarget(componentReader.valueNode("link-to"), context);
|
|
692
|
-
if (boundElementUuid === null && href === null) throw new Error(formatComponentError("Bound element or href not found", "search-bar", elementResource), { cause: componentProperty });
|
|
693
|
-
return {
|
|
694
|
-
component: "search-bar",
|
|
695
|
-
queryVariant,
|
|
696
|
-
placeholder: componentReader.multilingualValue("placeholder-text", options),
|
|
697
|
-
baseFilterQueries: componentReader.value("base-filter-queries")?.replaceAll(String.raw`\{`, "{").replaceAll(String.raw`\}`, "}") ?? null,
|
|
698
|
-
boundElementUuid,
|
|
699
|
-
href
|
|
700
|
-
};
|
|
701
|
-
}
|
|
702
|
-
function parseTextComponent(parameters) {
|
|
703
|
-
const { componentProperty, componentReader, elementResource, options } = parameters;
|
|
704
|
-
const content = elementResource.document && "content" in elementResource.document ? parseXMLContent(elementResource.document, options) : null;
|
|
705
|
-
if (content == null) throw new Error(formatComponentError("Content not found", "text", elementResource), { cause: componentProperty });
|
|
706
|
-
let variantName = "block";
|
|
707
|
-
let variant;
|
|
708
|
-
const variantProperty = componentReader.property("variant");
|
|
709
|
-
if (variantProperty !== null) {
|
|
710
|
-
const variantReader = websitePresentationReader(variantProperty.properties);
|
|
711
|
-
variantName = variantProperty.values[0].content;
|
|
712
|
-
switch (variantName) {
|
|
713
|
-
case "paragraph":
|
|
714
|
-
variant = {
|
|
715
|
-
name: variantName,
|
|
716
|
-
size: variantReader.valueOr("size", "md")
|
|
717
|
-
};
|
|
718
|
-
break;
|
|
719
|
-
case "label":
|
|
720
|
-
variant = {
|
|
721
|
-
name: variantName,
|
|
722
|
-
size: variantReader.valueOr("size", "md")
|
|
723
|
-
};
|
|
724
|
-
break;
|
|
725
|
-
case "heading":
|
|
726
|
-
variant = {
|
|
727
|
-
name: variantName,
|
|
728
|
-
size: variantReader.valueOr("size", "md")
|
|
729
|
-
};
|
|
730
|
-
break;
|
|
731
|
-
case "display":
|
|
732
|
-
variant = {
|
|
733
|
-
name: variantName,
|
|
734
|
-
size: variantReader.valueOr("size", "md")
|
|
735
|
-
};
|
|
736
|
-
break;
|
|
737
|
-
default: variant = { name: variantName };
|
|
738
|
-
}
|
|
739
|
-
} else variant = { name: variantName };
|
|
740
|
-
const headingLevel = componentReader.value("heading-level");
|
|
741
|
-
return {
|
|
742
|
-
component: "text",
|
|
743
|
-
variant,
|
|
744
|
-
headingLevel,
|
|
745
|
-
content
|
|
746
|
-
};
|
|
747
|
-
}
|
|
748
|
-
function parseTimelineComponent(parameters) {
|
|
749
|
-
const { componentProperty, elementResource, websiteLinks } = parameters;
|
|
750
|
-
const timelineLink = findWebsiteLink(websiteLinks, "tree");
|
|
751
|
-
if (timelineLink == null) throw new Error(formatComponentError("Timeline link not found", "timeline", elementResource), { cause: componentProperty });
|
|
752
|
-
return {
|
|
753
|
-
component: "timeline",
|
|
754
|
-
linkUuid: timelineLink.uuid
|
|
755
|
-
};
|
|
756
|
-
}
|
|
757
|
-
function parseVideoComponent(parameters) {
|
|
758
|
-
const { componentProperty, componentReader, elementResource, websiteLinks } = parameters;
|
|
759
|
-
const videoLink = findWebsiteLink(websiteLinks, "resource", (link) => link.type === "video");
|
|
760
|
-
if (videoLink == null) throw new Error(formatComponentError("Video link not found", "video", elementResource), { cause: componentProperty });
|
|
761
|
-
const isChaptersDisplayed = componentReader.valueOr("chapters-displayed", true);
|
|
762
|
-
return {
|
|
763
|
-
component: "video",
|
|
764
|
-
linkUuid: videoLink.uuid,
|
|
765
|
-
isChaptersDisplayed
|
|
766
|
-
};
|
|
767
|
-
}
|
|
768
|
-
/**
|
|
769
|
-
* Parses raw web element properties into a standardized WebElementComponent structure
|
|
770
|
-
*
|
|
771
|
-
* @param componentProperty - Raw component property data in OCHRE format
|
|
772
|
-
* @param elementResource - Raw element resource data in OCHRE format
|
|
773
|
-
* @returns Parsed WebElementComponent object
|
|
774
|
-
*/
|
|
775
|
-
function parseWebElementProperties(componentProperty, elementResource, options, context) {
|
|
776
|
-
const unparsedComponentName = componentProperty.values[0].content;
|
|
777
|
-
const componentNameResult = v.safeParse(componentSchema, unparsedComponentName);
|
|
778
|
-
const componentName = componentNameResult.success ? componentNameResult.output : void 0;
|
|
779
|
-
const websiteLinks = parseLinks(elementResource.links, options);
|
|
780
|
-
const parameters = {
|
|
781
|
-
componentProperty,
|
|
782
|
-
componentReader: websitePresentationReader(componentProperty.properties),
|
|
783
|
-
elementResource,
|
|
784
|
-
websiteLinks,
|
|
785
|
-
options,
|
|
786
|
-
context
|
|
787
|
-
};
|
|
788
|
-
switch (componentName) {
|
|
789
|
-
case "3d-viewer": return parse3dViewerComponent(parameters);
|
|
790
|
-
case "advanced-search": return parseAdvancedSearchComponent(parameters);
|
|
791
|
-
case "annotated-document": return parseAnnotatedDocumentComponent(parameters);
|
|
792
|
-
case "annotated-image": return parseAnnotatedImageComponent(parameters);
|
|
793
|
-
case "audio-player": return parseAudioPlayerComponent(parameters);
|
|
794
|
-
case "bibliography": return parseBibliographyComponent(parameters);
|
|
795
|
-
case "button": return parseButtonComponent(parameters);
|
|
796
|
-
case "collection": return parseCollectionComponent(parameters);
|
|
797
|
-
case "empty-space": return parseEmptySpaceComponent(parameters);
|
|
798
|
-
case "entries": return parseEntriesComponent(parameters);
|
|
799
|
-
case "iframe": return parseIframeComponent(parameters);
|
|
800
|
-
case "iiif-viewer": return parseIiifViewerComponent(parameters);
|
|
801
|
-
case "image": return parseImageComponent(parameters);
|
|
802
|
-
case "image-gallery": return parseImageGalleryComponent(parameters);
|
|
803
|
-
case "map": return parseMapComponent(parameters);
|
|
804
|
-
case "query": return parseQueryComponent(parameters);
|
|
805
|
-
case "table": return parseTableComponent(parameters);
|
|
806
|
-
case "search-bar": return parseSearchBarComponent(parameters);
|
|
807
|
-
case "text": return parseTextComponent(parameters);
|
|
808
|
-
case "timeline": return parseTimelineComponent(parameters);
|
|
809
|
-
case "video": return parseVideoComponent(parameters);
|
|
810
|
-
default: throw new Error(`Invalid or non-implemented component name “${unparsedComponentName.toString()}” for the following element: “${parseStringContent(elementResource.identification.label, options)}”`);
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
function parseWebTitle(properties, identification, overrides) {
|
|
814
|
-
const title = {
|
|
815
|
-
label: identification.label,
|
|
816
|
-
variant: "default",
|
|
817
|
-
properties: {
|
|
818
|
-
isNameDisplayed: overrides?.isNameDisplayed ?? false,
|
|
819
|
-
isDescriptionDisplayed: overrides?.isDescriptionDisplayed ?? false,
|
|
820
|
-
isDateDisplayed: overrides?.isDateDisplayed ?? false,
|
|
821
|
-
isCreatorsDisplayed: overrides?.isCreatorsDisplayed ?? false,
|
|
822
|
-
isCountDisplayed: overrides?.isCountDisplayed ?? false
|
|
823
|
-
}
|
|
824
|
-
};
|
|
825
|
-
const titleReader = websitePresentationReader(properties).nestedByValue("presentation", "title");
|
|
826
|
-
if (titleReader.size > 0) {
|
|
827
|
-
title.variant = titleReader.valueOr("variant", "default");
|
|
828
|
-
title.properties.isNameDisplayed = titleReader.valueOr("name-displayed", false);
|
|
829
|
-
title.properties.isDescriptionDisplayed = titleReader.valueOr("description-displayed", false);
|
|
830
|
-
title.properties.isDateDisplayed = titleReader.valueOr("date-displayed", false);
|
|
831
|
-
title.properties.isCreatorsDisplayed = titleReader.valueOr("creators-displayed", false);
|
|
832
|
-
title.properties.isCountDisplayed = titleReader.valueOr("count-displayed", false);
|
|
833
|
-
}
|
|
834
|
-
return title;
|
|
835
|
-
}
|
|
836
|
-
/**
|
|
837
|
-
* Parses raw web element data into a standardized WebElement structure
|
|
838
|
-
*
|
|
839
|
-
* @param elementResource - Raw element resource data in OCHRE format
|
|
840
|
-
* @returns Parsed WebElement object
|
|
841
|
-
*/
|
|
842
|
-
function parseWebElement(elementResource, options, context) {
|
|
843
|
-
const identification = parseIdentification(elementResource.identification, options);
|
|
844
|
-
const elementProperties = parseSimplifiedProperties(elementResource.properties, options);
|
|
845
|
-
const presentationProperty = websitePresentationReader(elementProperties).requiredProperty("presentation", `Presentation property not found for element (${formatXMLWebsiteResourceMetadata(elementResource)})`);
|
|
846
|
-
const properties = parseWebElementProperties(websitePresentationReader(presentationProperty.properties).requiredProperty("component", `Component property not found for element (${formatXMLWebsiteResourceMetadata(elementResource)})`), elementResource, options, context);
|
|
847
|
-
const cssStyles = parseResponsiveCssStyles(elementProperties);
|
|
848
|
-
const title = parseWebTitle(elementProperties, identification, {
|
|
849
|
-
isNameDisplayed: [
|
|
850
|
-
"annotated-image",
|
|
851
|
-
"annotated-document",
|
|
852
|
-
"collection"
|
|
853
|
-
].includes(properties.component),
|
|
854
|
-
isCountDisplayed: properties.component === "collection"
|
|
855
|
-
});
|
|
856
|
-
return {
|
|
857
|
-
uuid: elementResource.uuid,
|
|
858
|
-
language: elementResource.lang ?? null,
|
|
859
|
-
type: "element",
|
|
860
|
-
title,
|
|
861
|
-
cssStyles,
|
|
862
|
-
...properties
|
|
863
|
-
};
|
|
864
|
-
}
|
|
865
11
|
function isSidebarResource(resource, options) {
|
|
866
12
|
const resourceProperties = parseSimplifiedProperties(resource.properties, options);
|
|
867
13
|
const resourceReader = websitePresentationReader(resourceProperties);
|
|
@@ -875,7 +21,7 @@ function parseWebBlockItems(resources, options, context) {
|
|
|
875
21
|
if (resourceType === null) continue;
|
|
876
22
|
switch (resourceType) {
|
|
877
23
|
case "element":
|
|
878
|
-
items.push(parseWebElement(resource, options, context));
|
|
24
|
+
items.push(parseWebElement(resource, options, context.pageSlugsByUuid));
|
|
879
25
|
break;
|
|
880
26
|
case "block": {
|
|
881
27
|
const block = parseWebBlock(resource, options, context);
|
|
@@ -887,7 +33,7 @@ function parseWebBlockItems(resources, options, context) {
|
|
|
887
33
|
return items;
|
|
888
34
|
}
|
|
889
35
|
function parseWebpageRedirect(redirectValue, context) {
|
|
890
|
-
const redirectTarget = parseWebsiteLinkTarget(redirectValue, context);
|
|
36
|
+
const redirectTarget = parseWebsiteLinkTarget(redirectValue, context.pageSlugsByUuid);
|
|
891
37
|
if (redirectTarget != null) {
|
|
892
38
|
if (redirectValue?.href == null && redirectValue?.uuid != null) return {
|
|
893
39
|
type: "page",
|
|
@@ -908,23 +54,21 @@ function parseWebpageRedirect(redirectValue, context) {
|
|
|
908
54
|
return null;
|
|
909
55
|
}
|
|
910
56
|
/**
|
|
911
|
-
* Parses
|
|
57
|
+
* Parses a walked page node into a standardized Webpage structure
|
|
912
58
|
*
|
|
913
|
-
* @param
|
|
59
|
+
* @param page - The page node, with its slug and nesting already resolved
|
|
914
60
|
* @returns Parsed Webpage object
|
|
915
61
|
*/
|
|
916
|
-
function parseWebpage(
|
|
62
|
+
function parseWebpage(page, options, context) {
|
|
63
|
+
const webpageResource = page.resource;
|
|
917
64
|
const webpageProperties = parseSimplifiedProperties(webpageResource.properties, options);
|
|
918
65
|
const webpageReader = websitePresentationReader(webpageProperties);
|
|
919
|
-
if (webpageReader.value("presentation") !== "page") return null;
|
|
920
66
|
const identification = parseIdentification(webpageResource.identification, options);
|
|
921
|
-
const slug = cleanWebsitePageSlug(webpageResource.slug);
|
|
922
|
-
if (slug == null) throw new Error(`Slug not found for page (${formatXMLWebsiteResourceMetadata(webpageResource)})`, { cause: webpageResource });
|
|
923
67
|
const returnWebpage = {
|
|
924
68
|
uuid: webpageResource.uuid,
|
|
925
69
|
type: "page",
|
|
926
70
|
title: identification.label,
|
|
927
|
-
slug:
|
|
71
|
+
slug: page.slug,
|
|
928
72
|
publicationDateTime: webpageResource.publicationDateTime ?? null,
|
|
929
73
|
items: [],
|
|
930
74
|
segments: [],
|
|
@@ -938,70 +82,51 @@ function parseWebpage(webpageResource, options, context, slugPrefix) {
|
|
|
938
82
|
redirect: null,
|
|
939
83
|
backgroundImage: null,
|
|
940
84
|
sidebar: null,
|
|
941
|
-
cssStyles:
|
|
942
|
-
default: [],
|
|
943
|
-
tablet: [],
|
|
944
|
-
mobile: []
|
|
945
|
-
}
|
|
85
|
+
cssStyles: emptyResponsiveStyles()
|
|
946
86
|
},
|
|
947
87
|
webpages: []
|
|
948
88
|
};
|
|
949
|
-
const
|
|
89
|
+
const websiteLinks = parseLinks(webpageResource.links, options);
|
|
90
|
+
const imageLink = findWebsiteLink(websiteLinks, "resource", (link) => link.type === "image" || link.type === "IIIF");
|
|
950
91
|
const webpageResources = normalizeWebsiteResources(webpageResource.resource);
|
|
951
92
|
returnWebpage.items = parseWebBlockItems(webpageResources.filter((resource) => !isSidebarResource(resource, options)), options, context);
|
|
952
|
-
returnWebpage.webpages = parseWebpages(
|
|
953
|
-
returnWebpage.segments = parseWebsiteSegments(
|
|
93
|
+
returnWebpage.webpages = parseWebpages(page.children, options, context);
|
|
94
|
+
returnWebpage.segments = parseWebsiteSegments(page.segments, options, context);
|
|
954
95
|
returnWebpage.properties.sidebar = parseSidebar(webpageResources, options, context);
|
|
955
96
|
const pageReader = webpageReader.nestedByValue("presentation", "page");
|
|
956
97
|
if (pageReader.size > 0) {
|
|
957
|
-
returnWebpage.properties
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
98
|
+
pageReader.readAll(returnWebpage.properties, {
|
|
99
|
+
isDisplayedInNavbar: "displayed-in-navbar",
|
|
100
|
+
width: "width",
|
|
101
|
+
variant: "variant",
|
|
102
|
+
isSidebarDisplayed: "sidebar-displayed",
|
|
103
|
+
isBreadcrumbsDisplayed: "breadcrumbs-displayed",
|
|
104
|
+
isNavbarSearchBarDisplayed: "navbar-search-bar-displayed"
|
|
105
|
+
});
|
|
963
106
|
returnWebpage.properties.redirect = parseWebpageRedirect(pageReader.valueNode("redirect-to"), context);
|
|
964
107
|
}
|
|
965
|
-
if (imageLink != null) returnWebpage.properties.backgroundImage =
|
|
966
|
-
uuid: imageLink.uuid,
|
|
967
|
-
label: imageLink.identification.label,
|
|
968
|
-
description: imageLink.description,
|
|
969
|
-
width: imageLink.image?.width ?? 0,
|
|
970
|
-
height: imageLink.image?.height ?? 0,
|
|
971
|
-
quality: "high"
|
|
972
|
-
};
|
|
108
|
+
if (imageLink != null) returnWebpage.properties.backgroundImage = webImageFromLink(imageLink, "high");
|
|
973
109
|
returnWebpage.properties.cssStyles = parseResponsiveCssStyles(webpageProperties);
|
|
974
110
|
return returnWebpage;
|
|
975
111
|
}
|
|
976
112
|
/**
|
|
977
|
-
* Parses
|
|
113
|
+
* Parses walked page nodes into an array of Webpage objects
|
|
978
114
|
*
|
|
979
|
-
* @param
|
|
115
|
+
* @param pages - The page nodes to parse
|
|
980
116
|
* @returns Array of parsed Webpage objects
|
|
981
117
|
*/
|
|
982
|
-
function parseWebpages(
|
|
983
|
-
|
|
984
|
-
for (const webpageResource of webpageResources) {
|
|
985
|
-
const webpage = parseWebpage(webpageResource, options, context, slugPrefix);
|
|
986
|
-
if (webpage !== null) returnPages.push(webpage);
|
|
987
|
-
}
|
|
988
|
-
return returnPages;
|
|
118
|
+
function parseWebpages(pages, options, context) {
|
|
119
|
+
return Array.from(pages, (page) => parseWebpage(page, options, context));
|
|
989
120
|
}
|
|
990
121
|
function parseWebpageView(view, options, context) {
|
|
991
|
-
|
|
122
|
+
const pages = readWebsitePages(view?.resource, options);
|
|
123
|
+
return parseWebpages(pages, options, {
|
|
124
|
+
...context,
|
|
125
|
+
pageSlugsByUuid: collectWebsitePageSlugs(pages, options)
|
|
126
|
+
})[0] ?? null;
|
|
992
127
|
}
|
|
993
|
-
function parseWebsiteSegments(
|
|
994
|
-
|
|
995
|
-
const segmentResources = resources ?? [];
|
|
996
|
-
for (const resource of segmentResources) {
|
|
997
|
-
if (!("segments" in resource)) continue;
|
|
998
|
-
for (const tree of resource.segments.tree) {
|
|
999
|
-
const segmentSlug = tree.identification.abbreviation == null ? null : parseStringContent(tree.identification.abbreviation, options);
|
|
1000
|
-
if (segmentSlug == null) throw new Error(`Slug not found for segment website (website uuid “${tree.uuid}”)`, { cause: tree });
|
|
1001
|
-
segments.push(parseWebsiteTree(tree, context, "segment", options, prefixSlug(segmentSlug, slugPrefix)));
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
return segments;
|
|
128
|
+
function parseWebsiteSegments(segments, options, context) {
|
|
129
|
+
return Array.from(segments, (segment) => parseWebsiteTree(segment.tree, options, context, "segment", segment.slugPrefix));
|
|
1005
130
|
}
|
|
1006
131
|
/**
|
|
1007
132
|
* Parses raw sidebar data into a standardized Sidebar structure
|
|
@@ -1016,17 +141,22 @@ function parseSidebar(resources, options, context) {
|
|
|
1016
141
|
const items = parseWebBlockItems(normalizeWebsiteResources(sidebarResource.resource), options, context);
|
|
1017
142
|
if (items.length === 0) return null;
|
|
1018
143
|
const sidebarReader = websitePresentationReader(sidebarBaseProperties).nestedByValue("presentation", "element").nestedByValue("component", "sidebar");
|
|
1019
|
-
|
|
144
|
+
const sidebar = {
|
|
1020
145
|
isDisplayed: true,
|
|
1021
146
|
items,
|
|
1022
147
|
title: parseWebTitle(sidebarBaseProperties, parseIdentification(sidebarResource.identification, options)),
|
|
1023
|
-
layout:
|
|
1024
|
-
mobileLayout:
|
|
148
|
+
layout: "start",
|
|
149
|
+
mobileLayout: "default",
|
|
1025
150
|
cssStyles: parseResponsiveCssStyles(sidebarBaseProperties)
|
|
1026
151
|
};
|
|
152
|
+
sidebarReader.readAll(sidebar, {
|
|
153
|
+
layout: "layout",
|
|
154
|
+
mobileLayout: "layout-mobile"
|
|
155
|
+
});
|
|
156
|
+
return sidebar;
|
|
1027
157
|
}
|
|
1028
158
|
function parseWebAccordionItem(elementResource, childResources, options, context) {
|
|
1029
|
-
const trigger = parseWebElement(elementResource, options, context);
|
|
159
|
+
const trigger = parseWebElement(elementResource, options, context.pageSlugsByUuid);
|
|
1030
160
|
const items = parseWebBlockItems(childResources, options, context);
|
|
1031
161
|
return {
|
|
1032
162
|
uuid: trigger.uuid,
|
|
@@ -1038,19 +168,25 @@ function parseWebAccordionItem(elementResource, childResources, options, context
|
|
|
1038
168
|
function parseBlockOverwrite(overwriteReader, isDefaultLayoutAccordion) {
|
|
1039
169
|
if (overwriteReader.size === 0) return null;
|
|
1040
170
|
const properties = {
|
|
1041
|
-
layout:
|
|
1042
|
-
wrap:
|
|
1043
|
-
spacing:
|
|
1044
|
-
gap:
|
|
171
|
+
layout: void 0,
|
|
172
|
+
wrap: void 0,
|
|
173
|
+
spacing: void 0,
|
|
174
|
+
gap: void 0,
|
|
1045
175
|
isAccordionEnabled: void 0,
|
|
1046
176
|
isAccordionExpandedByDefault: void 0,
|
|
1047
177
|
isAccordionSidebarDisplayed: void 0
|
|
1048
178
|
};
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
179
|
+
overwriteReader.readAll(properties, {
|
|
180
|
+
layout: "layout",
|
|
181
|
+
wrap: "wrap",
|
|
182
|
+
spacing: "spacing",
|
|
183
|
+
gap: "gap"
|
|
184
|
+
});
|
|
185
|
+
if (isDefaultLayoutAccordion || properties.layout === "accordion") overwriteReader.readAll(properties, {
|
|
186
|
+
isAccordionEnabled: "accordion-enabled",
|
|
187
|
+
isAccordionExpandedByDefault: "accordion-expanded",
|
|
188
|
+
isAccordionSidebarDisplayed: "accordion-sidebar-displayed"
|
|
189
|
+
});
|
|
1054
190
|
const cleanedProperties = cleanObject(properties);
|
|
1055
191
|
return Object.keys(cleanedProperties).length > 0 ? cleanedProperties : null;
|
|
1056
192
|
}
|
|
@@ -1065,7 +201,7 @@ function parseAccordionAwareBlockItems(blockResources, options, context, isSuppo
|
|
|
1065
201
|
case "element": {
|
|
1066
202
|
const childResources = normalizeWebsiteResources(resource.resource);
|
|
1067
203
|
const componentType = resourceReader.nestedByValue("presentation", "element").value("component");
|
|
1068
|
-
blockItems.push(isSupportingAccordionItems && componentType === "text" && childResources.length > 0 ? parseWebAccordionItem(resource, childResources, options, context) : parseWebElement(resource, options, context));
|
|
204
|
+
blockItems.push(isSupportingAccordionItems && componentType === "text" && childResources.length > 0 ? parseWebAccordionItem(resource, childResources, options, context) : parseWebElement(resource, options, context.pageSlugsByUuid));
|
|
1069
205
|
break;
|
|
1070
206
|
}
|
|
1071
207
|
case "block": {
|
|
@@ -1101,23 +237,26 @@ function parseWebBlock(blockResource, options, context) {
|
|
|
1101
237
|
mobile: null,
|
|
1102
238
|
tablet: null
|
|
1103
239
|
},
|
|
1104
|
-
cssStyles:
|
|
1105
|
-
default: [],
|
|
1106
|
-
tablet: [],
|
|
1107
|
-
mobile: []
|
|
1108
|
-
}
|
|
240
|
+
cssStyles: emptyResponsiveStyles()
|
|
1109
241
|
};
|
|
1110
242
|
const blockReader = websitePresentationReader(blockProperties).nestedByValue("presentation", "block");
|
|
1111
243
|
if (blockReader.size > 0) {
|
|
1112
|
-
returnBlock.properties.default
|
|
1113
|
-
|
|
244
|
+
blockReader.readAll(returnBlock.properties.default, {
|
|
245
|
+
layout: "layout",
|
|
246
|
+
wrap: "wrap",
|
|
247
|
+
spacing: "spacing",
|
|
248
|
+
gap: "gap"
|
|
249
|
+
});
|
|
1114
250
|
if (returnBlock.properties.default.layout === "accordion") {
|
|
1115
|
-
returnBlock.properties.default.isAccordionEnabled =
|
|
1116
|
-
returnBlock.properties.default.isAccordionExpandedByDefault =
|
|
1117
|
-
returnBlock.properties.default.isAccordionSidebarDisplayed =
|
|
251
|
+
returnBlock.properties.default.isAccordionEnabled = true;
|
|
252
|
+
returnBlock.properties.default.isAccordionExpandedByDefault = true;
|
|
253
|
+
returnBlock.properties.default.isAccordionSidebarDisplayed = false;
|
|
254
|
+
blockReader.readAll(returnBlock.properties.default, {
|
|
255
|
+
isAccordionEnabled: "accordion-enabled",
|
|
256
|
+
isAccordionExpandedByDefault: "accordion-expanded",
|
|
257
|
+
isAccordionSidebarDisplayed: "accordion-sidebar-displayed"
|
|
258
|
+
});
|
|
1118
259
|
}
|
|
1119
|
-
returnBlock.properties.default.spacing = blockReader.valueOr("spacing", null);
|
|
1120
|
-
returnBlock.properties.default.gap = blockReader.valueOr("gap", null);
|
|
1121
260
|
const isDefaultLayoutAccordion = returnBlock.properties.default.layout === "accordion";
|
|
1122
261
|
returnBlock.properties.tablet = parseBlockOverwrite(blockReader.nested("overwrite-tablet"), isDefaultLayoutAccordion);
|
|
1123
262
|
returnBlock.properties.mobile = parseBlockOverwrite(blockReader.nested("overwrite-mobile"), isDefaultLayoutAccordion);
|
|
@@ -1126,215 +265,12 @@ function parseWebBlock(blockResource, options, context) {
|
|
|
1126
265
|
returnBlock.cssStyles = parseResponsiveCssStyles(blockProperties);
|
|
1127
266
|
return returnBlock;
|
|
1128
267
|
}
|
|
1129
|
-
|
|
1130
|
-
* Parses raw website properties into a standardized Website properties structure
|
|
1131
|
-
*
|
|
1132
|
-
* @param properties - Array of raw website properties in OCHRE format
|
|
1133
|
-
* @returns Parsed WebsiteProperties object
|
|
1134
|
-
*/
|
|
1135
|
-
function parseWebsiteProperties(properties, websiteTree, sidebar, options, parent) {
|
|
1136
|
-
const mainProperties = parseSimplifiedProperties({ property: properties }, options);
|
|
1137
|
-
const websiteReader = websitePresentationReader(mainProperties).nested("presentation");
|
|
1138
|
-
const returnProperties = {
|
|
1139
|
-
type: websiteReader.valueOr("webUI", parent?.type ?? "traditional"),
|
|
1140
|
-
status: websiteReader.valueOr("status", parent?.status ?? "development"),
|
|
1141
|
-
versionLabel: websiteReader.valueOr("version-label", parent?.versionLabel ?? "release"),
|
|
1142
|
-
privacy: websiteReader.valueOr("privacy", parent?.privacy ?? "public"),
|
|
1143
|
-
contact: parent?.contact ?? null,
|
|
1144
|
-
loadingVariant: "spinner",
|
|
1145
|
-
theme: {
|
|
1146
|
-
isThemeToggleDisplayed: true,
|
|
1147
|
-
defaultTheme: "system"
|
|
1148
|
-
},
|
|
1149
|
-
icon: {
|
|
1150
|
-
logoUuid: null,
|
|
1151
|
-
faviconUuid: null,
|
|
1152
|
-
appleTouchIconUuid: null
|
|
1153
|
-
},
|
|
1154
|
-
navbar: {
|
|
1155
|
-
isDisplayed: true,
|
|
1156
|
-
variant: "default",
|
|
1157
|
-
alignment: "start",
|
|
1158
|
-
isProjectDisplayed: true,
|
|
1159
|
-
searchBarBoundElementUuid: null,
|
|
1160
|
-
items: parent?.navbar.items ?? null
|
|
1161
|
-
},
|
|
1162
|
-
footer: {
|
|
1163
|
-
isDisplayed: true,
|
|
1164
|
-
logoUuid: null,
|
|
1165
|
-
items: parent?.footer.items ?? null
|
|
1166
|
-
},
|
|
1167
|
-
sidebar: sidebar ?? parent?.sidebar ?? null,
|
|
1168
|
-
itemPage: {
|
|
1169
|
-
isMainContentDisplayed: parent?.itemPage.isMainContentDisplayed ?? true,
|
|
1170
|
-
description: {
|
|
1171
|
-
isDisplayed: parent?.itemPage.description.isDisplayed ?? true,
|
|
1172
|
-
isHeaderDisplayed: parent?.itemPage.description.isHeaderDisplayed ?? true
|
|
1173
|
-
},
|
|
1174
|
-
document: {
|
|
1175
|
-
isDisplayed: parent?.itemPage.document.isDisplayed ?? true,
|
|
1176
|
-
isHeaderDisplayed: parent?.itemPage.document.isHeaderDisplayed ?? true
|
|
1177
|
-
},
|
|
1178
|
-
notes: {
|
|
1179
|
-
isDisplayed: parent?.itemPage.notes.isDisplayed ?? true,
|
|
1180
|
-
isHeaderDisplayed: parent?.itemPage.notes.isHeaderDisplayed ?? true,
|
|
1181
|
-
variant: parent?.itemPage.notes.variant ?? "discrete"
|
|
1182
|
-
},
|
|
1183
|
-
events: {
|
|
1184
|
-
isDisplayed: parent?.itemPage.events.isDisplayed ?? true,
|
|
1185
|
-
isHeaderDisplayed: parent?.itemPage.events.isHeaderDisplayed ?? true,
|
|
1186
|
-
variant: parent?.itemPage.events.variant ?? "tabular"
|
|
1187
|
-
},
|
|
1188
|
-
periods: {
|
|
1189
|
-
isDisplayed: parent?.itemPage.periods.isDisplayed ?? true,
|
|
1190
|
-
isHeaderDisplayed: parent?.itemPage.periods.isHeaderDisplayed ?? true
|
|
1191
|
-
},
|
|
1192
|
-
isPropertiesDisplayed: parent?.itemPage.isPropertiesDisplayed ?? true,
|
|
1193
|
-
bibliography: {
|
|
1194
|
-
isDisplayed: parent?.itemPage.bibliography.isDisplayed ?? true,
|
|
1195
|
-
isHeaderDisplayed: parent?.itemPage.bibliography.isHeaderDisplayed ?? true
|
|
1196
|
-
},
|
|
1197
|
-
isPropertyValuesGrouped: parent?.itemPage.isPropertyValuesGrouped ?? true,
|
|
1198
|
-
isPublicationDateTimeDisplayed: parent?.itemPage.isPublicationDateTimeDisplayed ?? true,
|
|
1199
|
-
isPersistentIdentifierDisplayed: parent?.itemPage.isPersistentIdentifierDisplayed ?? true,
|
|
1200
|
-
iiifViewer: parent?.itemPage.iiifViewer ?? "universal-viewer"
|
|
1201
|
-
},
|
|
1202
|
-
options: {
|
|
1203
|
-
contextTree: parent?.options.contextTree ?? null,
|
|
1204
|
-
scopes: parent?.options.scopes ?? null,
|
|
1205
|
-
labels: { title: parent?.options.labels.title ?? null },
|
|
1206
|
-
stylesheets: { properties: parent?.options.stylesheets.properties ?? [] }
|
|
1207
|
-
}
|
|
1208
|
-
};
|
|
1209
|
-
const contactProperty = websiteReader.property("contact");
|
|
1210
|
-
if (contactProperty !== null) {
|
|
1211
|
-
const contactContent = contactProperty.values[0]?.content.toString().split(";") ?? [];
|
|
1212
|
-
if (contactContent.length === 2) returnProperties.contact = {
|
|
1213
|
-
name: contactContent[0],
|
|
1214
|
-
email: contactContent[1] ?? null
|
|
1215
|
-
};
|
|
1216
|
-
else throw new Error(`Contact property must use “name;email”, got “${contactProperty.values[0]?.content}” (website uuid “${websiteTree.uuid}”)`, { cause: websiteTree });
|
|
1217
|
-
}
|
|
1218
|
-
returnProperties.loadingVariant = websiteReader.valueOr("loading-variant", parent?.loadingVariant ?? "spinner");
|
|
1219
|
-
returnProperties.theme.isThemeToggleDisplayed = websiteReader.valueOr("supports-theme-toggle", parent?.theme.isThemeToggleDisplayed ?? true);
|
|
1220
|
-
returnProperties.theme.defaultTheme = websiteReader.valueOr("default-theme", parent?.theme.defaultTheme ?? "system");
|
|
1221
|
-
returnProperties.icon.logoUuid = websiteReader.uuid("navbar-logo") ?? parent?.icon.logoUuid ?? null;
|
|
1222
|
-
returnProperties.icon.faviconUuid = websiteReader.uuid("favicon-ico") ?? parent?.icon.faviconUuid ?? null;
|
|
1223
|
-
returnProperties.icon.appleTouchIconUuid = websiteReader.uuid("favicon-img") ?? parent?.icon.appleTouchIconUuid ?? null;
|
|
1224
|
-
returnProperties.navbar.isDisplayed = websiteReader.valueOr("navbar-displayed", parent?.navbar.isDisplayed ?? true);
|
|
1225
|
-
returnProperties.navbar.variant = websiteReader.valueOr("navbar-variant", parent?.navbar.variant ?? "default");
|
|
1226
|
-
returnProperties.navbar.alignment = websiteReader.valueOr("navbar-alignment", parent?.navbar.alignment ?? "start");
|
|
1227
|
-
returnProperties.navbar.isProjectDisplayed = websiteReader.valueOr("navbar-project-displayed", parent?.navbar.isProjectDisplayed ?? true);
|
|
1228
|
-
returnProperties.navbar.searchBarBoundElementUuid = websiteReader.uuid("bound-element-navbar-search-bar") ?? parent?.navbar.searchBarBoundElementUuid ?? null;
|
|
1229
|
-
returnProperties.footer.isDisplayed = websiteReader.valueOr("footer-displayed", parent?.footer.isDisplayed ?? true);
|
|
1230
|
-
returnProperties.footer.logoUuid = websiteReader.uuid("footer-logo") ?? parent?.footer.logoUuid ?? null;
|
|
1231
|
-
const itemPageReader = websiteReader.nestedByValue("page-type", "item-page");
|
|
1232
|
-
if (itemPageReader.size > 0) {
|
|
1233
|
-
for (const [key, slug] of [
|
|
1234
|
-
["description", "description"],
|
|
1235
|
-
["document", "document"],
|
|
1236
|
-
["notes", "notes"],
|
|
1237
|
-
["events", "events"],
|
|
1238
|
-
["periods", "periods"],
|
|
1239
|
-
["bibliography", "bibliography"]
|
|
1240
|
-
]) {
|
|
1241
|
-
const section = returnProperties.itemPage[key];
|
|
1242
|
-
section.isDisplayed = itemPageReader.valueOr(`item-page-${slug}-displayed`, section.isDisplayed);
|
|
1243
|
-
section.isHeaderDisplayed = itemPageReader.valueOr(`item-page-${slug}-header-displayed`, section.isHeaderDisplayed);
|
|
1244
|
-
}
|
|
1245
|
-
returnProperties.itemPage.notes.variant = itemPageReader.valueOr("item-page-notes-display-variant", returnProperties.itemPage.notes.variant);
|
|
1246
|
-
returnProperties.itemPage.events.variant = itemPageReader.valueOr("item-page-events-display-variant", returnProperties.itemPage.events.variant);
|
|
1247
|
-
returnProperties.itemPage.isPropertyValuesGrouped = itemPageReader.valueOr("item-page-property-values-grouped", returnProperties.itemPage.isPropertyValuesGrouped);
|
|
1248
|
-
returnProperties.itemPage.isPublicationDateTimeDisplayed = itemPageReader.valueOr("item-page-publication-date-time-displayed", returnProperties.itemPage.isPublicationDateTimeDisplayed);
|
|
1249
|
-
returnProperties.itemPage.isPersistentIdentifierDisplayed = itemPageReader.valueOr("item-page-persistent-identifier-displayed", returnProperties.itemPage.isPersistentIdentifierDisplayed);
|
|
1250
|
-
returnProperties.itemPage.iiifViewer = itemPageReader.valueOr("item-page-iiif-viewer", returnProperties.itemPage.iiifViewer);
|
|
1251
|
-
}
|
|
1252
|
-
if (websiteTree.options != null) {
|
|
1253
|
-
const parsedOptions = parseWebsiteOptions(websiteTree.options, options);
|
|
1254
|
-
returnProperties.options.scopes = (parsedOptions.scopes != null && parsedOptions.scopes.length > 0 ? parsedOptions : returnProperties.options).scopes;
|
|
1255
|
-
returnProperties.options.contextTree = parsedOptions.contextTree ?? returnProperties.options.contextTree;
|
|
1256
|
-
returnProperties.options.labels = { title: parsedOptions.labels.title ?? returnProperties.options.labels.title };
|
|
1257
|
-
}
|
|
1258
|
-
if ("styleOptions" in websiteTree && websiteTree.styleOptions != null) {
|
|
1259
|
-
const stylesheetProperties = parseStylesheets(websiteTree.styleOptions.style);
|
|
1260
|
-
if (stylesheetProperties.length > 0) returnProperties.options.stylesheets.properties = stylesheetProperties;
|
|
1261
|
-
}
|
|
1262
|
-
return returnProperties;
|
|
1263
|
-
}
|
|
1264
|
-
function parseContextItem(contextItemToParse, options) {
|
|
1265
|
-
let type = "";
|
|
1266
|
-
const levels = [];
|
|
1267
|
-
const levelsToParse = contextItemToParse.levels?.level ?? [];
|
|
1268
|
-
for (const level of levelsToParse) {
|
|
1269
|
-
const [rawVariableUuid = "", rawValueUuid] = level.payload.split(",", 2);
|
|
1270
|
-
const valueUuid = rawValueUuid == null || rawValueUuid.trim() === "null" ? null : rawValueUuid.trim();
|
|
1271
|
-
type = level.dataType ?? type;
|
|
1272
|
-
levels.push({
|
|
1273
|
-
variableUuid: rawVariableUuid.trim(),
|
|
1274
|
-
valueUuid
|
|
1275
|
-
});
|
|
1276
|
-
}
|
|
1277
|
-
return {
|
|
1278
|
-
context: levels,
|
|
1279
|
-
type,
|
|
1280
|
-
identification: parseIdentification(contextItemToParse.identification, options),
|
|
1281
|
-
description: parseContentLike(contextItemToParse.description, options)
|
|
1282
|
-
};
|
|
1283
|
-
}
|
|
1284
|
-
function parseFilterContextDisplay(filterOption) {
|
|
1285
|
-
switch (filterOption) {
|
|
1286
|
-
case "inline-displayed": return {
|
|
1287
|
-
isInlineDisplayed: true,
|
|
1288
|
-
isSidebarDisplayed: false,
|
|
1289
|
-
isSidebarOpen: false
|
|
1290
|
-
};
|
|
1291
|
-
case "inline-sidebar-displayed-closed": return {
|
|
1292
|
-
isInlineDisplayed: true,
|
|
1293
|
-
isSidebarDisplayed: true,
|
|
1294
|
-
isSidebarOpen: false
|
|
1295
|
-
};
|
|
1296
|
-
case "inline-sidebar-displayed-open": return {
|
|
1297
|
-
isInlineDisplayed: true,
|
|
1298
|
-
isSidebarDisplayed: true,
|
|
1299
|
-
isSidebarOpen: true
|
|
1300
|
-
};
|
|
1301
|
-
case "sidebar-displayed-closed": return {
|
|
1302
|
-
isInlineDisplayed: false,
|
|
1303
|
-
isSidebarDisplayed: true,
|
|
1304
|
-
isSidebarOpen: false
|
|
1305
|
-
};
|
|
1306
|
-
case "sidebar-displayed-open": return {
|
|
1307
|
-
isInlineDisplayed: false,
|
|
1308
|
-
isSidebarDisplayed: true,
|
|
1309
|
-
isSidebarOpen: true
|
|
1310
|
-
};
|
|
1311
|
-
default: return {
|
|
1312
|
-
isInlineDisplayed: false,
|
|
1313
|
-
isSidebarDisplayed: false,
|
|
1314
|
-
isSidebarOpen: false
|
|
1315
|
-
};
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
function parseContexts(contextLevels, options) {
|
|
1319
|
-
const contextTreeLevels = [];
|
|
1320
|
-
for (const contextLevel of contextLevels) for (const contextItemToParse of contextLevel.context) contextTreeLevels.push(parseContextItem(contextItemToParse, options));
|
|
1321
|
-
return contextTreeLevels;
|
|
1322
|
-
}
|
|
1323
|
-
function parseFilterContexts(filterContextLevels, options) {
|
|
1324
|
-
const filterContextTreeLevels = [];
|
|
1325
|
-
for (const filterContextLevel of filterContextLevels) for (const contextItemToParse of filterContextLevel.context) filterContextTreeLevels.push({
|
|
1326
|
-
...parseContextItem(contextItemToParse, options),
|
|
1327
|
-
filterType: contextItemToParse.filterType ?? "property",
|
|
1328
|
-
filterVariant: contextItemToParse.filterVariant ?? null,
|
|
1329
|
-
...parseFilterContextDisplay(contextItemToParse.filterOption)
|
|
1330
|
-
});
|
|
1331
|
-
return filterContextTreeLevels;
|
|
1332
|
-
}
|
|
1333
|
-
function parseWebsiteTree(websiteTree, context, type, options, slugPrefix) {
|
|
268
|
+
function parseWebsiteTree(websiteTree, options, context, type, slugPrefix) {
|
|
1334
269
|
if (!websiteTree.properties) throw new Error(`Website properties not found (website uuid “${websiteTree.uuid}”)`, { cause: websiteTree });
|
|
1335
270
|
if (type === "website" && websiteTree.items?.resource == null) throw new Error(`Website pages not found (website uuid “${websiteTree.uuid}”)`, { cause: websiteTree });
|
|
1336
271
|
const resources = normalizeWebsiteResources(websiteTree.items?.resource);
|
|
1337
|
-
const
|
|
272
|
+
const pages = readWebsitePages(websiteTree.items?.resource, options, slugPrefix);
|
|
273
|
+
const pageSlugsByUuid = context.pageSlugsByUuid ?? collectWebsitePageSlugs(pages, options);
|
|
1338
274
|
const treeContext = {
|
|
1339
275
|
...context,
|
|
1340
276
|
pageSlugsByUuid
|
|
@@ -1351,11 +287,11 @@ function parseWebsiteTree(websiteTree, context, type, options, slugPrefix) {
|
|
|
1351
287
|
identification: parseIdentification(websiteTree.identification, options),
|
|
1352
288
|
creators: websiteTree.creators ? parsePersonList(websiteTree.creators.creator, options) : [],
|
|
1353
289
|
license,
|
|
1354
|
-
items: parseWebpages(
|
|
290
|
+
items: parseWebpages(pages, options, {
|
|
1355
291
|
...treeContext,
|
|
1356
292
|
parentProperties: properties,
|
|
1357
293
|
parentLicense: license
|
|
1358
|
-
}
|
|
294
|
+
}),
|
|
1359
295
|
properties
|
|
1360
296
|
};
|
|
1361
297
|
}
|
|
@@ -1363,17 +299,20 @@ function parseWebsite(data, options) {
|
|
|
1363
299
|
const rawOchre = data.result.ochre;
|
|
1364
300
|
const metadataLanguages = parseMetadataLanguages(rawOchre);
|
|
1365
301
|
const languages = resolveLanguages(options?.languages, metadataLanguages);
|
|
1366
|
-
const parserOptions = { languages };
|
|
1367
302
|
const defaultLanguage = resolveDefaultLanguage(rawOchre, languages);
|
|
303
|
+
const parserOptions = {
|
|
304
|
+
languages,
|
|
305
|
+
defaultLanguage
|
|
306
|
+
};
|
|
1368
307
|
const websiteTree = rawOchre.tree[0];
|
|
1369
308
|
if (websiteTree == null) throw new Error("Website tree not found", { cause: data });
|
|
1370
|
-
return parseWebsiteTree(websiteTree, {
|
|
309
|
+
return parseWebsiteTree(websiteTree, parserOptions, {
|
|
1371
310
|
belongsTo: {
|
|
1372
311
|
uuid: rawOchre.uuidBelongsTo,
|
|
1373
312
|
abbreviation: rawOchre.belongsTo
|
|
1374
313
|
},
|
|
1375
314
|
metadata: parseMetadata(rawOchre, parserOptions, defaultLanguage)
|
|
1376
|
-
}, "website"
|
|
315
|
+
}, "website");
|
|
1377
316
|
}
|
|
1378
317
|
//#endregion
|
|
1379
|
-
export {
|
|
318
|
+
export { parseWebpageView, parseWebsite };
|