@canopy-iiif/app 1.3.6 → 1.4.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/lib/build/iiif.js CHANGED
@@ -20,6 +20,7 @@ const {log, logLine, logResponse} = require("./log");
20
20
  const { getPageContext } = require("../page-context");
21
21
  const PageContext = getPageContext();
22
22
  const referenced = require("../components/referenced");
23
+ const navPlace = require("../components/nav-place");
23
24
  const {
24
25
  getThumbnail,
25
26
  getRepresentativeImage,
@@ -1387,6 +1388,7 @@ async function buildIiifCollectionPages(CONFIG) {
1387
1388
  );
1388
1389
  } catch (_) {}
1389
1390
  const iiifRecords = [];
1391
+ const navPlaceRecords = [];
1390
1392
  const {size: thumbSize, unsafe: unsafeThumbs} = resolveThumbnailPreferences();
1391
1393
 
1392
1394
  // Compile the works layout component once per run
@@ -1543,9 +1545,13 @@ async function buildIiifCollectionPages(CONFIG) {
1543
1545
  try {
1544
1546
  let components = {};
1545
1547
  try {
1546
- components = await import("@canopy-iiif/app/ui");
1548
+ components = await import("@canopy-iiif/app/ui/server");
1547
1549
  } catch (_) {
1548
- components = {};
1550
+ try {
1551
+ components = await import("@canopy-iiif/app/ui");
1552
+ } catch (_) {
1553
+ components = {};
1554
+ }
1549
1555
  }
1550
1556
  const {withBase} = require("../common");
1551
1557
  const Anchor = function A(props) {
@@ -1658,6 +1664,7 @@ async function buildIiifCollectionPages(CONFIG) {
1658
1664
  const needsRelated = body.includes("data-canopy-related-items");
1659
1665
  const needsHeroSlider = body.includes("data-canopy-hero-slider");
1660
1666
  const needsTimeline = body.includes("data-canopy-timeline");
1667
+ const needsMap = body.includes("data-canopy-map");
1661
1668
  const needsSearchForm = body.includes("data-canopy-search-form");
1662
1669
  const needsHydrate =
1663
1670
  body.includes("data-canopy-hydrate") ||
@@ -1692,6 +1699,24 @@ async function buildIiifCollectionPages(CONFIG) {
1692
1699
  .split(path.sep)
1693
1700
  .join("/")
1694
1701
  : null;
1702
+ const mapRel = needsMap
1703
+ ? path
1704
+ .relative(
1705
+ path.dirname(outPath),
1706
+ path.join(OUT_DIR, "scripts", "canopy-map.js")
1707
+ )
1708
+ .split(path.sep)
1709
+ .join("/")
1710
+ : null;
1711
+ const mapCssRel = needsMap
1712
+ ? path
1713
+ .relative(
1714
+ path.dirname(outPath),
1715
+ path.join(OUT_DIR, "scripts", "canopy-map.css")
1716
+ )
1717
+ .split(path.sep)
1718
+ .join("/")
1719
+ : null;
1695
1720
  const heroRel = needsHeroSlider
1696
1721
  ? path
1697
1722
  .relative(
@@ -1727,6 +1752,7 @@ async function buildIiifCollectionPages(CONFIG) {
1727
1752
  if (heroRel) primaryClassicScripts.push(heroRel);
1728
1753
  if (relatedRel) primaryClassicScripts.push(relatedRel);
1729
1754
  if (timelineRel) primaryClassicScripts.push(timelineRel);
1755
+ if (mapRel) primaryClassicScripts.push(mapRel);
1730
1756
  const secondaryClassicScripts = [];
1731
1757
  if (searchFormRel) secondaryClassicScripts.push(searchFormRel);
1732
1758
  let jsRel = null;
@@ -1741,7 +1767,8 @@ async function buildIiifCollectionPages(CONFIG) {
1741
1767
  const needsReact = !!(
1742
1768
  needsHydrateViewer ||
1743
1769
  needsRelated ||
1744
- needsTimeline
1770
+ needsTimeline ||
1771
+ needsMap
1745
1772
  );
1746
1773
  let vendorTag = "";
1747
1774
  if (needsReact) {
@@ -1782,6 +1809,17 @@ async function buildIiifCollectionPages(CONFIG) {
1782
1809
  )}</script>` + vendorTag;
1783
1810
  } catch (_) {}
1784
1811
  let pageBody = body;
1812
+ const extraStyles = [];
1813
+ if (mapCssRel) {
1814
+ let rel = mapCssRel;
1815
+ try {
1816
+ const mapCssAbs = path.join(OUT_DIR, "scripts", "canopy-map.css");
1817
+ const st = fs.statSync(mapCssAbs);
1818
+ rel += `?v=${Math.floor(st.mtimeMs || Date.now())}`;
1819
+ } catch (_) {}
1820
+ extraStyles.push(`<link rel="stylesheet" href="${rel}">`);
1821
+ }
1822
+ if (extraStyles.length) headSegments.push(extraStyles.join(""));
1785
1823
  if (vendorTag) headSegments.push(vendorTag);
1786
1824
  if (extraScripts.length) headSegments.push(extraScripts.join(""));
1787
1825
  const headExtra = headSegments.join("");
@@ -1878,10 +1916,36 @@ async function buildIiifCollectionPages(CONFIG) {
1878
1916
  annotationValue = "";
1879
1917
  }
1880
1918
  }
1919
+ const navThumbnail =
1920
+ thumbUrl || (heroMedia && heroMedia.heroThumbnail) || "";
1921
+ const navThumbWidth =
1922
+ typeof thumbWidth === "number"
1923
+ ? thumbWidth
1924
+ : heroMedia && typeof heroMedia.heroThumbnailWidth === "number"
1925
+ ? heroMedia.heroThumbnailWidth
1926
+ : undefined;
1927
+ const navThumbHeight =
1928
+ typeof thumbHeight === "number"
1929
+ ? thumbHeight
1930
+ : heroMedia && typeof heroMedia.heroThumbnailHeight === "number"
1931
+ ? heroMedia.heroThumbnailHeight
1932
+ : undefined;
1933
+ const navRecord = navPlace.buildManifestNavPlaceRecord({
1934
+ manifest,
1935
+ slug,
1936
+ href: pageHref,
1937
+ title,
1938
+ summary: summaryRaw,
1939
+ thumbnail: navThumbnail,
1940
+ thumbnailWidth: navThumbWidth,
1941
+ thumbnailHeight: navThumbHeight,
1942
+ });
1943
+ if (navRecord) navPlaceRecords.push(navRecord);
1944
+
1881
1945
  iiifRecords.push({
1882
1946
  id: String(manifest.id || id),
1883
1947
  title,
1884
- href: rootRelativeHref(href.split(path.sep).join("/")),
1948
+ href: pageHref,
1885
1949
  type: "work",
1886
1950
  thumbnail: thumbUrl || undefined,
1887
1951
  thumbnailWidth:
@@ -1915,6 +1979,16 @@ async function buildIiifCollectionPages(CONFIG) {
1915
1979
  );
1916
1980
  await Promise.all(workers);
1917
1981
  }
1982
+ try {
1983
+ await navPlace.writeNavPlaceDataset(navPlaceRecords);
1984
+ } catch (error) {
1985
+ try {
1986
+ console.warn(
1987
+ '[canopy][navPlace] failed to write dataset:',
1988
+ error && error.message ? error.message : error
1989
+ );
1990
+ } catch (_) {}
1991
+ }
1918
1992
  return {iiifRecords};
1919
1993
  }
1920
1994
 
package/lib/build/mdx.js CHANGED
@@ -1739,6 +1739,128 @@ async function ensureTimelineRuntime() {
1739
1739
  logLevel: "silent",
1740
1740
  minify: true,
1741
1741
  plugins: [plugin],
1742
+ loader: {
1743
+ ".png": "dataurl",
1744
+ ".svg": "dataurl",
1745
+ ".gif": "dataurl",
1746
+ },
1747
+ });
1748
+ try {
1749
+ const {logLine} = require("./log");
1750
+ let size = 0;
1751
+ try {
1752
+ const st = fs.statSync(outFile);
1753
+ size = (st && st.size) || 0;
1754
+ } catch (_) {}
1755
+ const kb = size ? ` (${(size / 1024).toFixed(1)} KB)` : "";
1756
+ const rel = path.relative(process.cwd(), outFile).split(path.sep).join("/");
1757
+ logLine(`✓ Wrote ${rel}${kb}`, "cyan");
1758
+ } catch (_) {}
1759
+ }
1760
+
1761
+ async function ensureMapRuntime() {
1762
+ let esbuild = null;
1763
+ try {
1764
+ esbuild = require("../ui/node_modules/esbuild");
1765
+ } catch (_) {
1766
+ try {
1767
+ esbuild = require("esbuild");
1768
+ } catch (_) {}
1769
+ }
1770
+ if (!esbuild)
1771
+ throw new Error(
1772
+ "Map runtime bundling requires esbuild. Install dependencies before building."
1773
+ );
1774
+ ensureDirSync(OUT_DIR);
1775
+ const scriptsDir = path.join(OUT_DIR, "scripts");
1776
+ ensureDirSync(scriptsDir);
1777
+ const outFile = path.join(scriptsDir, "canopy-map.js");
1778
+ const entryFile = path.join(
1779
+ __dirname,
1780
+ "..",
1781
+ "components",
1782
+ "map-runtime.js"
1783
+ );
1784
+ const reactShim = `
1785
+ const React = (typeof window !== 'undefined' && window.React) || {};
1786
+ export default React;
1787
+ export const Children = React.Children;
1788
+ export const Component = React.Component;
1789
+ export const Fragment = React.Fragment;
1790
+ export const createElement = React.createElement;
1791
+ export const cloneElement = React.cloneElement;
1792
+ export const createContext = React.createContext;
1793
+ export const forwardRef = React.forwardRef;
1794
+ export const memo = React.memo;
1795
+ export const startTransition = React.startTransition;
1796
+ export const isValidElement = React.isValidElement;
1797
+ export const useEffect = React.useEffect;
1798
+ export const useLayoutEffect = React.useLayoutEffect;
1799
+ export const useMemo = React.useMemo;
1800
+ export const useState = React.useState;
1801
+ export const useRef = React.useRef;
1802
+ export const useCallback = React.useCallback;
1803
+ export const useContext = React.useContext;
1804
+ export const useReducer = React.useReducer;
1805
+ export const useId = React.useId;
1806
+ `;
1807
+ const rdomShim = `
1808
+ const ReactDOM = (typeof window !== 'undefined' && window.ReactDOM) || {};
1809
+ export default ReactDOM;
1810
+ export const render = ReactDOM.render;
1811
+ export const hydrate = ReactDOM.hydrate;
1812
+ export const findDOMNode = ReactDOM.findDOMNode;
1813
+ export const unmountComponentAtNode = ReactDOM.unmountComponentAtNode;
1814
+ export const createPortal = ReactDOM.createPortal;
1815
+ export const flushSync = ReactDOM.flushSync;
1816
+ export const unstable_batchedUpdates = ReactDOM.unstable_batchedUpdates;
1817
+ export const unstable_renderSubtreeIntoContainer = ReactDOM.unstable_renderSubtreeIntoContainer;
1818
+ `;
1819
+ const rdomClientShim = `
1820
+ const RDC = (typeof window !== 'undefined' && window.ReactDOMClient) || {};
1821
+ export const createRoot = RDC.createRoot;
1822
+ export const hydrateRoot = RDC.hydrateRoot;
1823
+ `;
1824
+ const plugin = {
1825
+ name: "canopy-react-shims-map",
1826
+ setup(build) {
1827
+ const ns = "canopy-map-shim";
1828
+ build.onResolve({filter: /^react$/}, () => ({path: "react", namespace: ns}));
1829
+ build.onResolve({filter: /^react-dom$/}, () => ({path: "react-dom", namespace: ns}));
1830
+ build.onResolve({filter: /^react-dom\/client$/}, () => ({
1831
+ path: "react-dom-client",
1832
+ namespace: ns,
1833
+ }));
1834
+ build.onLoad({filter: /^react$/, namespace: ns}, () => ({
1835
+ contents: reactShim,
1836
+ loader: "js",
1837
+ }));
1838
+ build.onLoad({filter: /^react-dom$/, namespace: ns}, () => ({
1839
+ contents: rdomShim,
1840
+ loader: "js",
1841
+ }));
1842
+ build.onLoad({filter: /^react-dom-client$/, namespace: ns}, () => ({
1843
+ contents: rdomClientShim,
1844
+ loader: "js",
1845
+ }));
1846
+ },
1847
+ };
1848
+ await esbuild.build({
1849
+ entryPoints: [entryFile],
1850
+ outfile: outFile,
1851
+ platform: "browser",
1852
+ format: "iife",
1853
+ bundle: true,
1854
+ sourcemap: false,
1855
+ target: ["es2018"],
1856
+ logLevel: "silent",
1857
+ minify: true,
1858
+ plugins: [plugin],
1859
+ loader: {
1860
+ ".png": "dataurl",
1861
+ ".svg": "dataurl",
1862
+ ".gif": "dataurl",
1863
+ },
1742
1864
  });
1743
1865
  try {
1744
1866
  const {logLine} = require("./log");
@@ -1768,6 +1890,7 @@ module.exports = {
1768
1890
  ensureClientRuntime,
1769
1891
  ensureSliderRuntime,
1770
1892
  ensureTimelineRuntime,
1893
+ ensureMapRuntime,
1771
1894
  ensureHeroRuntime,
1772
1895
  ensureFacetsRuntime,
1773
1896
  ensureReactGlobals,
@@ -176,6 +176,7 @@ async function renderContentMdxToHtml(filePath, outPath, extraProps = {}, source
176
176
  const needsHydrateSlider = body.includes('data-canopy-slider');
177
177
  const needsHeroSlider = body.includes('data-canopy-hero-slider');
178
178
  const needsTimeline = body.includes('data-canopy-timeline');
179
+ const needsMap = body.includes('data-canopy-map');
179
180
  const needsSearchForm = true; // search form runtime is global
180
181
  const needsFacets = body.includes('data-canopy-related-items');
181
182
  const needsCustomClients = body.includes('data-canopy-client-component');
@@ -194,6 +195,12 @@ async function renderContentMdxToHtml(filePath, outPath, extraProps = {}, source
194
195
  const timelineRel = needsTimeline
195
196
  ? path.relative(path.dirname(outPath), path.join(OUT_DIR, 'scripts', 'canopy-timeline.js')).split(path.sep).join('/')
196
197
  : null;
198
+ const mapRel = needsMap
199
+ ? path.relative(path.dirname(outPath), path.join(OUT_DIR, 'scripts', 'canopy-map.js')).split(path.sep).join('/')
200
+ : null;
201
+ const mapCssRel = needsMap
202
+ ? path.relative(path.dirname(outPath), path.join(OUT_DIR, 'scripts', 'canopy-map.css')).split(path.sep).join('/')
203
+ : null;
197
204
  const facetsRel = needsFacets
198
205
  ? path.relative(path.dirname(outPath), path.join(OUT_DIR, 'scripts', 'canopy-related-items.js')).split(path.sep).join('/')
199
206
  : null;
@@ -226,6 +233,7 @@ async function renderContentMdxToHtml(filePath, outPath, extraProps = {}, source
226
233
  const primaryClassicScripts = [];
227
234
  if (heroRel) primaryClassicScripts.push(heroRel);
228
235
  if (timelineRel) primaryClassicScripts.push(timelineRel);
236
+ if (mapRel) primaryClassicScripts.push(mapRel);
229
237
  if (facetsRel) primaryClassicScripts.push(facetsRel);
230
238
  const secondaryClassicScripts = [];
231
239
  if (searchFormRel) secondaryClassicScripts.push(searchFormRel);
@@ -239,6 +247,7 @@ async function renderContentMdxToHtml(filePath, outPath, extraProps = {}, source
239
247
  needsHydrateSlider ||
240
248
  needsFacets ||
241
249
  needsTimeline ||
250
+ needsMap ||
242
251
  (customClientRel && needsCustomClients)
243
252
  );
244
253
  let vendorTag = '';
@@ -279,6 +288,15 @@ async function renderContentMdxToHtml(filePath, outPath, extraProps = {}, source
279
288
  } catch (_) {}
280
289
  extraStyles.push(`<link rel="stylesheet" href="${rel}">`);
281
290
  }
291
+ if (mapCssRel) {
292
+ let rel = mapCssRel;
293
+ try {
294
+ const mapCssAbs = path.join(OUT_DIR, 'scripts', 'canopy-map.css');
295
+ const st = fs.statSync(mapCssAbs);
296
+ rel += `?v=${Math.floor(st.mtimeMs || Date.now())}`;
297
+ } catch (_) {}
298
+ extraStyles.push(`<link rel="stylesheet" href="${rel}">`);
299
+ }
282
300
  if (extraStyles.length) headSegments.push(extraStyles.join(''));
283
301
  if (vendorTag) headSegments.push(vendorTag);
284
302
  if (extraScripts.length) headSegments.push(extraScripts.join(''));
@@ -5,6 +5,7 @@ async function prepareAllRuntimes() {
5
5
  const mdx = require('./mdx');
6
6
  try { await mdx.ensureClientRuntime(); } catch (_) {}
7
7
  try { if (typeof mdx.ensureTimelineRuntime === 'function') await mdx.ensureTimelineRuntime(); } catch (_) {}
8
+ try { if (typeof mdx.ensureMapRuntime === 'function') await mdx.ensureMapRuntime(); } catch (_) {}
8
9
  try { if (typeof mdx.ensureHeroRuntime === 'function') await mdx.ensureHeroRuntime(); } catch (_) {}
9
10
  try { if (typeof mdx.ensureFacetsRuntime === 'function') await mdx.ensureFacetsRuntime(); } catch (_) {}
10
11
  try { if (typeof mdx.ensureReactGlobals === 'function') await mdx.ensureReactGlobals(); } catch (_) {}
@@ -106,10 +106,68 @@ async function cleanupLegacySitemaps() {
106
106
  await Promise.all(deletions);
107
107
  }
108
108
 
109
+ async function ensureNoExtensionGuards(fileNames) {
110
+ const guards = new Map();
111
+ (Array.isArray(fileNames) ? fileNames : []).forEach((file) => {
112
+ const raw = typeof file === 'string' ? file.trim() : '';
113
+ if (!raw || !/\.xml$/i.test(raw)) return;
114
+ const base = raw.replace(/\.xml$/i, '');
115
+ if (!base || base === raw) return;
116
+ guards.set(base, raw);
117
+ });
118
+
119
+ let entries;
120
+ try {
121
+ entries = await fsp.readdir(OUT_DIR, { withFileTypes: true });
122
+ } catch (_) {
123
+ entries = [];
124
+ }
125
+
126
+ const markerName = '.canopy-xml-guard';
127
+ const staleRemovals = [];
128
+ for (const entry of entries) {
129
+ if (!entry || !entry.isDirectory()) continue;
130
+ const dirName = entry.name;
131
+ const dirPath = path.join(OUT_DIR, dirName);
132
+ const markerPath = path.join(dirPath, markerName);
133
+ let hasMarker = false;
134
+ try {
135
+ const stat = await fsp.stat(markerPath);
136
+ hasMarker = stat.isFile();
137
+ } catch (_) {}
138
+ if (!hasMarker) continue;
139
+ if (guards.has(dirName)) {
140
+ guards.delete(dirName);
141
+ continue;
142
+ }
143
+ staleRemovals.push(
144
+ fsp
145
+ .rm(dirPath, { recursive: true, force: true })
146
+ .catch(() => {})
147
+ );
148
+ }
149
+ await Promise.all(staleRemovals);
150
+
151
+ if (!guards.size) return;
152
+
153
+ const creations = [];
154
+ for (const base of guards.keys()) {
155
+ const dirPath = path.join(OUT_DIR, base);
156
+ const markerPath = path.join(dirPath, markerName);
157
+ creations.push(
158
+ fsp
159
+ .mkdir(dirPath, { recursive: true })
160
+ .then(() => fsp.writeFile(markerPath, '', 'utf8').catch(() => {}))
161
+ );
162
+ }
163
+ await Promise.all(creations);
164
+ }
165
+
109
166
  async function writeSitemap(iiifRecords, pageRecords) {
110
167
  const urls = collectAbsoluteUrls(iiifRecords, pageRecords);
111
168
  if (!urls.length) {
112
169
  await cleanupLegacySitemaps();
170
+ await ensureNoExtensionGuards([]);
113
171
  logLine('• No URLs to write to sitemap', 'yellow');
114
172
  return;
115
173
  }
@@ -117,15 +175,19 @@ async function writeSitemap(iiifRecords, pageRecords) {
117
175
 
118
176
  const chunks = chunkList(urls, MAX_URLS_PER_SITEMAP);
119
177
  const indexEntries = [];
178
+ const writtenFiles = [];
120
179
  for (let i = 0; i < chunks.length; i += 1) {
121
180
  const fileName = `sitemap-${i + 1}.xml`;
122
181
  const dest = path.join(OUT_DIR, fileName);
123
182
  await fsp.writeFile(dest, buildUrlsetXml(chunks[i]), 'utf8');
124
183
  indexEntries.push({ loc: absoluteUrl(fileName) });
184
+ writtenFiles.push(fileName);
125
185
  }
126
186
 
127
187
  const indexDest = path.join(OUT_DIR, SITEMAP_INDEX_BASENAME);
128
188
  await fsp.writeFile(indexDest, buildSitemapIndexXml(indexEntries), 'utf8');
189
+ writtenFiles.push(SITEMAP_INDEX_BASENAME);
190
+ await ensureNoExtensionGuards(writtenFiles);
129
191
  logLine(
130
192
  `✓ Wrote sitemap index (${chunks.length} files, ${urls.length} urls total)`,
131
193
  'cyan'
@@ -0,0 +1,114 @@
1
+ import React from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import { Map } from '../../ui/dist/index.mjs';
4
+ import Leaflet from 'leaflet';
5
+ import 'leaflet/dist/leaflet.css';
6
+ import 'leaflet.markercluster';
7
+ import 'leaflet.markercluster/dist/MarkerCluster.css';
8
+ import 'leaflet.markercluster/dist/MarkerCluster.Default.css';
9
+
10
+ function broadcastLeafletReady() {
11
+ let updated = false;
12
+ try {
13
+ if (typeof globalThis !== 'undefined' && !globalThis.L) {
14
+ globalThis.L = Leaflet;
15
+ updated = true;
16
+ }
17
+ } catch (_) {}
18
+ try {
19
+ if (typeof window !== 'undefined' && !window.L) {
20
+ window.L = Leaflet;
21
+ updated = true;
22
+ }
23
+ } catch (_) {}
24
+ if (updated && typeof document !== 'undefined') {
25
+ try {
26
+ document.dispatchEvent(new CustomEvent('canopy:leaflet-ready'));
27
+ } catch (_) {
28
+ const evt = document.createEvent('CustomEvent');
29
+ evt.initCustomEvent('canopy:leaflet-ready', false, false, {});
30
+ document.dispatchEvent(evt);
31
+ }
32
+ }
33
+ }
34
+
35
+ broadcastLeafletReady();
36
+
37
+ function ready(fn) {
38
+ if (typeof document === 'undefined') return;
39
+ if (document.readyState === 'loading') {
40
+ document.addEventListener('DOMContentLoaded', fn, { once: true });
41
+ } else {
42
+ fn();
43
+ }
44
+ }
45
+
46
+ function parseProps(el) {
47
+ try {
48
+ const script = el.querySelector('script[type="application/json"]');
49
+ if (script) return JSON.parse(script.textContent || '{}');
50
+ const raw = el.getAttribute('data-props') || '{}';
51
+ return JSON.parse(raw);
52
+ } catch (_) {
53
+ return {};
54
+ }
55
+ }
56
+
57
+ function mount(el) {
58
+ try {
59
+ if (!el || el.getAttribute('data-canopy-map-mounted') === '1') return;
60
+ const props = parseProps(el);
61
+ const script = el.querySelector('script[type="application/json"]');
62
+ if (script && script.parentNode === el) {
63
+ script.parentNode.removeChild(script);
64
+ }
65
+ while (el.firstChild) {
66
+ el.removeChild(el.firstChild);
67
+ }
68
+ const root = createRoot(el);
69
+ root.render(React.createElement(Map, props));
70
+ el.setAttribute('data-canopy-map-mounted', '1');
71
+ } catch (error) {
72
+ try {
73
+ console.warn('[canopy][map] failed to mount map', error);
74
+ } catch (_) {}
75
+ }
76
+ }
77
+
78
+ function scan() {
79
+ try {
80
+ document
81
+ .querySelectorAll('[data-canopy-map]:not([data-canopy-map-mounted="1"])')
82
+ .forEach(mount);
83
+ } catch (_) {}
84
+ }
85
+
86
+ function observe() {
87
+ try {
88
+ const obs = new MutationObserver((mutations) => {
89
+ const toMount = [];
90
+ mutations.forEach((mutation) => {
91
+ mutation.addedNodes &&
92
+ mutation.addedNodes.forEach((node) => {
93
+ if (!(node instanceof Element)) return;
94
+ if (node.matches && node.matches('[data-canopy-map]')) toMount.push(node);
95
+ const inner = node.querySelectorAll
96
+ ? node.querySelectorAll('[data-canopy-map]')
97
+ : [];
98
+ inner && inner.forEach && inner.forEach((el) => toMount.push(el));
99
+ });
100
+ });
101
+ if (toMount.length) Promise.resolve().then(() => toMount.forEach(mount));
102
+ });
103
+ obs.observe(document.documentElement || document.body, {
104
+ childList: true,
105
+ subtree: true,
106
+ });
107
+ } catch (_) {}
108
+ }
109
+
110
+ ready(() => {
111
+ if (typeof document === 'undefined') return;
112
+ scan();
113
+ observe();
114
+ });