ochre-sdk 1.3.17 → 1.3.19

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.
@@ -90,6 +90,16 @@ declare function local:matching-pages($resources, $target-slug, $slug-prefix) {
90
90
  )
91
91
  };
92
92
 
93
+ declare function local:oak-collection-pages($tree, $resources, $target-slug) {
94
+ if ($tree/properties/property[label/string() = "presentation"][value/string() = "website"]/property[label/string() = "webUI"]/value/string() = "oak") then
95
+ for $resource in $resources[local:presentation(.) = "element"]
96
+ let $slug := local:clean-slug($resource/@slug)
97
+ where string(($resource/properties/property[label/string() = "presentation"]/property[label/string() = "component"]/value)[1]) = "collection"
98
+ and (if ($slug != "") then $slug else string(($resource/identification/abbreviation/content/string)[1])) = $target-slug
99
+ return local:metadata-page($resource)
100
+ else ()
101
+ };
102
+
93
103
  declare function local:metadata-tree($tree, $target-slug, $slug-prefix) {
94
104
  let $resources := local:resource-items($tree/items/resource)
95
105
  let $presentation-properties := $tree/properties/property[label/string() = "presentation"][value/string() = "website"]/property[label/string() = (${Object.values(METADATA_PRESENTATION_LABELS).map((label) => stringLiteral(label)).join(", ")})]
@@ -103,7 +113,8 @@ declare function local:metadata-tree($tree, $target-slug, $slug-prefix) {
103
113
  $resources[local:presentation(.) = "page"],
104
114
  $target-slug,
105
115
  $slug-prefix
106
- )
116
+ ),
117
+ local:oak-collection-pages($tree, $resources, $target-slug)
107
118
  }
108
119
  }
109
120
  };
@@ -65,7 +65,8 @@ function parseCollectionPropertyOverrides(reader) {
65
65
  read("isSortDisplayed", "sort-displayed");
66
66
  read("isUsingQueryParams", "is-using-query-params");
67
67
  read("isInteractive", "is-interactive");
68
- readImage("layout", "image-layout");
68
+ const imageLayout = reader.value("image-layout");
69
+ if (imageLayout != null) imageOverrides.layout = imageLayout === "suppress" ? null : imageLayout;
69
70
  readImage("fit", "image-fit");
70
71
  readImage("alignment", "image-alignment");
71
72
  readImage("isPlaceholderDisplayed", "image-placeholder-displayed");
@@ -455,6 +456,24 @@ function parseMapComponent(parameters) {
455
456
  let maximumBounds = null;
456
457
  const maximumBoundsProperty = componentReader.value("maximum-bounds");
457
458
  if (maximumBoundsProperty !== null) maximumBounds = parseBounds(String(maximumBoundsProperty));
459
+ const layerSettings = [];
460
+ for (const layerReader of componentReader.nestedAllByValue("customize-special", "map-layer")) {
461
+ const uuid = layerReader.uuid("use-layer");
462
+ if (uuid == null) continue;
463
+ const opacity = layerReader.value("opacity-percent");
464
+ const filterFields = layerReader.stringValues("filter-field");
465
+ layerSettings.push({
466
+ uuid,
467
+ isBasemap: layerReader.value("is-basemap"),
468
+ isDisplayedByDefault: layerReader.value("displayed-by-default"),
469
+ opacity: typeof opacity === "number" ? Math.min(Math.max(opacity, 0), 100) / 100 : null,
470
+ ochreLinkField: layerReader.stringValue("ochre-link-field"),
471
+ periodField: layerReader.stringValue("period-field"),
472
+ periodOrder: layerReader.stringValues("period-order"),
473
+ filterFields: filterFields.length > 0 ? filterFields : null,
474
+ markerColor: layerReader.stringValue("marker-color")
475
+ });
476
+ }
458
477
  return {
459
478
  component: "map",
460
479
  linkUuids,
@@ -462,6 +481,7 @@ function parseMapComponent(parameters) {
462
481
  customBasemap,
463
482
  initialBounds,
464
483
  maximumBounds,
484
+ layerSettings,
465
485
  ...properties
466
486
  };
467
487
  }
@@ -1,9 +1,11 @@
1
- import { cleanObject, parseLicense, parseRequiredContentLike } from "../helpers.mjs";
1
+ import { cleanObject, parseLicense, parseRequiredContentLike, parseStringContent } from "../helpers.mjs";
2
2
  import { parseMetadataLanguages, resolveDefaultLanguage, resolveLanguages } from "../languages.mjs";
3
3
  import { parseIdentification, parseLinks, parseMetadata, parsePersonList, parseSimplifiedProperties } from "../index.mjs";
4
4
  import { findWebsiteLink, parseWebsiteLinkTarget, webImageFromLink } from "./links.mjs";
5
+ import { formatXMLWebsiteResourceMetadata } from "./messages.mjs";
5
6
  import { websitePresentationReader } from "./reader.mjs";
6
7
  import { emptyResponsiveStyles, parseResponsiveCssStyles } from "./styles.mjs";
8
+ import { cleanWebsitePageSlug } from "./slug.mjs";
7
9
  import { collectWebsitePageSlugs, normalizeWebsiteResources, readWebsitePages } from "./walk.mjs";
8
10
  import { parseWebElement, parseWebTitle } from "./components.mjs";
9
11
  import { parseWebsiteProperties } from "./properties.mjs";
@@ -53,23 +55,13 @@ function parseWebpageRedirect(redirectValue, context) {
53
55
  };
54
56
  return null;
55
57
  }
56
- /**
57
- * Parses a walked page node into a standardized Webpage structure
58
- *
59
- * @param page - The page node, with its slug and nesting already resolved
60
- * @returns Parsed Webpage object
61
- */
62
- function parseWebpage(page, options, context) {
63
- const webpageResource = page.resource;
64
- const webpageProperties = parseSimplifiedProperties(webpageResource.properties, options);
65
- const webpageReader = websitePresentationReader(webpageProperties);
66
- const identification = parseIdentification(webpageResource.identification, options);
67
- const returnWebpage = {
68
- uuid: webpageResource.uuid,
58
+ function createWebpage(resource, slug, options) {
59
+ return {
60
+ uuid: resource.uuid,
69
61
  type: "page",
70
- title: identification.label,
71
- slug: page.slug,
72
- publicationDateTime: webpageResource.publicationDateTime ?? null,
62
+ title: parseIdentification(resource.identification, options).label,
63
+ slug,
64
+ publicationDateTime: resource.publicationDateTime ?? null,
73
65
  items: [],
74
66
  segments: [],
75
67
  properties: {
@@ -86,6 +78,18 @@ function parseWebpage(page, options, context) {
86
78
  },
87
79
  webpages: []
88
80
  };
81
+ }
82
+ /**
83
+ * Parses a walked page node into a standardized Webpage structure
84
+ *
85
+ * @param page - The page node, with its slug and nesting already resolved
86
+ * @returns Parsed Webpage object
87
+ */
88
+ function parseWebpage(page, options, context) {
89
+ const webpageResource = page.resource;
90
+ const webpageProperties = parseSimplifiedProperties(webpageResource.properties, options);
91
+ const webpageReader = websitePresentationReader(webpageProperties);
92
+ const returnWebpage = createWebpage(webpageResource, page.slug, options);
89
93
  const websiteLinks = parseLinks(webpageResource.links, options);
90
94
  const imageLink = findWebsiteLink(websiteLinks, "resource", (link) => link.type === "image" || link.type === "IIIF");
91
95
  const webpageResources = normalizeWebsiteResources(webpageResource.resource);
@@ -265,6 +269,36 @@ function parseWebBlock(blockResource, options, context) {
265
269
  returnBlock.cssStyles = parseResponsiveCssStyles(blockProperties);
266
270
  return returnBlock;
267
271
  }
272
+ function parseOakWebsite(resources, webpages, options, context) {
273
+ const webpagesByUuid = new Map(webpages.map((webpage) => [webpage.uuid, webpage]));
274
+ const items = [];
275
+ let map = null;
276
+ for (const resource of resources) {
277
+ const webpage = webpagesByUuid.get(resource.uuid);
278
+ if (webpage != null) {
279
+ items.push(webpage);
280
+ continue;
281
+ }
282
+ const component = websitePresentationReader(parseSimplifiedProperties(resource.properties, options)).nestedByValue("presentation", "element").value("component");
283
+ if (component !== "map" && component !== "collection") continue;
284
+ const element = parseWebElement(resource, options, context.pageSlugsByUuid);
285
+ if (element.component === "map") {
286
+ map ??= element;
287
+ continue;
288
+ }
289
+ const resourceSlug = cleanWebsitePageSlug(resource.slug) ?? "";
290
+ const slug = resourceSlug === "" ? parseStringContent(resource.identification.abbreviation, options) : resourceSlug;
291
+ if (slug === "") throw new Error(`Slug not found for OAK collection (${formatXMLWebsiteResourceMetadata(resource)})`, { cause: resource });
292
+ const collectionPage = createWebpage(resource, slug, options);
293
+ collectionPage.items = [element];
294
+ collectionPage.properties.isDisplayedInNavbar = false;
295
+ items.push(collectionPage);
296
+ }
297
+ return {
298
+ items,
299
+ oak: { map }
300
+ };
301
+ }
268
302
  function parseWebsiteTree(websiteTree, options, context, type, slugPrefix) {
269
303
  if (!websiteTree.properties) throw new Error(`Website properties not found (website uuid “${websiteTree.uuid}”)`, { cause: websiteTree });
270
304
  if (type === "website" && websiteTree.items?.resource == null) throw new Error(`Website pages not found (website uuid “${websiteTree.uuid}”)`, { cause: websiteTree });
@@ -278,6 +312,15 @@ function parseWebsiteTree(websiteTree, options, context, type, slugPrefix) {
278
312
  const sidebar = parseSidebar(resources, options, treeContext);
279
313
  const properties = parseWebsiteProperties(websiteTree.properties.property, websiteTree, sidebar, options, context.parentProperties ?? null);
280
314
  const license = parseLicense(websiteTree.availability) ?? context.parentLicense ?? null;
315
+ const webpages = parseWebpages(pages, options, {
316
+ ...treeContext,
317
+ parentProperties: properties,
318
+ parentLicense: license
319
+ });
320
+ const { items, oak } = type === "website" && properties.type === "oak" ? parseOakWebsite(resources, webpages, options, treeContext) : {
321
+ items: webpages,
322
+ oak: null
323
+ };
281
324
  return {
282
325
  uuid: websiteTree.uuid,
283
326
  type,
@@ -287,12 +330,9 @@ function parseWebsiteTree(websiteTree, options, context, type, slugPrefix) {
287
330
  identification: parseIdentification(websiteTree.identification, options),
288
331
  creators: websiteTree.creators ? parsePersonList(websiteTree.creators.creator, options) : [],
289
332
  license,
290
- items: parseWebpages(pages, options, {
291
- ...treeContext,
292
- parentProperties: properties,
293
- parentLicense: license
294
- }),
295
- properties
333
+ items,
334
+ properties,
335
+ oak
296
336
  };
297
337
  }
298
338
  function parseWebsite(data, options) {
@@ -44,6 +44,7 @@ export declare class WebsitePresentationReader<T extends LanguageCodes> {
44
44
  * @returns The string, or null when the property is unset or not string-like
45
45
  */
46
46
  stringValue(label: string): string | null;
47
+ stringValues(label: string): Array<string>;
47
48
  uuid(label: string): string | null;
48
49
  multilingualValue(label: string, options: ParserOptions<T>): MultilingualString<T> | null;
49
50
  /**
@@ -63,6 +63,17 @@ var WebsitePresentationReader = class WebsitePresentationReader {
63
63
  if (typeof value === "string") return value;
64
64
  return typeof value === "number" ? value.toString() : null;
65
65
  }
66
+ stringValues(label) {
67
+ const values = [];
68
+ for (const property of this.sourceProperties) {
69
+ if (getProperty([property], { label }) == null) continue;
70
+ for (const { content } of property.values) {
71
+ const value = typeof content === "number" ? content.toString() : content;
72
+ if (typeof value === "string" && value.trim() !== "") values.push(value.trim());
73
+ }
74
+ }
75
+ return values;
76
+ }
66
77
  uuid(label) {
67
78
  return this.valueNode(label)?.uuid ?? null;
68
79
  }
@@ -219,6 +219,14 @@ export type Website<T extends LanguageCodes = LanguageCodes> = {
219
219
  };
220
220
  }>;
221
221
  };
222
+ /**
223
+ * The bare Map element of an OAK website, null for every other design. Its
224
+ * bare Collection elements are in `items` as pages hidden from the navbar,
225
+ * in tree order.
226
+ */
227
+ oak: {
228
+ map: WebElementOf<"map", T> | null;
229
+ } | null;
222
230
  };
223
231
  export type WebsiteSegment<T extends LanguageCodes = LanguageCodes> = Website<T> & {
224
232
  type: "segment";
@@ -358,7 +366,7 @@ export type WebElementComponent<T extends LanguageCodes = LanguageCodes> = {
358
366
  paginationVariant: "default" | "numeric";
359
367
  loadingVariant: WebLoadingVariant;
360
368
  image: {
361
- layout: "top" | "bottom" | "start" | "end" | null;
369
+ layout: "top" | "bottom" | "start" | "end" | "thumbnail" | null;
362
370
  fit: "fill" | "fit";
363
371
  alignment: "top" | "center" | "bottom" | null;
364
372
  isPlaceholderDisplayed: boolean;
@@ -426,6 +434,17 @@ export type WebElementComponent<T extends LanguageCodes = LanguageCodes> = {
426
434
  customBasemap: string | null;
427
435
  initialBounds: [[number, number], [number, number]] | null;
428
436
  maximumBounds: [[number, number], [number, number]] | null;
437
+ layerSettings: Array<{
438
+ uuid: string;
439
+ isBasemap: boolean | null;
440
+ isDisplayedByDefault: boolean | null;
441
+ opacity: number | null;
442
+ ochreLinkField: string | null;
443
+ periodField: string | null;
444
+ periodOrder: Array<string>;
445
+ filterFields: Array<string> | null;
446
+ markerColor: string | null;
447
+ }>;
429
448
  isControlsDisplayed: boolean;
430
449
  isInteractive: boolean;
431
450
  isClustered: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ochre-sdk",
3
- "version": "1.3.17",
3
+ "version": "1.3.19",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Node.js library for working with OCHRE (Online Cultural and Historical Research Environment) data",