commonmeta-ruby 3.7.1 → 3.7.3

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/publish.yml +23 -0
  3. data/Gemfile.lock +26 -26
  4. data/bin/commonmeta +1 -1
  5. data/docs/.gitignore +1 -0
  6. data/docs/_publish.yml +4 -0
  7. data/docs/_quarto.yml +46 -0
  8. data/docs/_site/favicon.ico +0 -0
  9. data/docs/_site/images/icon.png +0 -0
  10. data/docs/_site/index.html +947 -0
  11. data/docs/_site/readers/bibtex_reader.html +802 -0
  12. data/docs/_site/search.json +74 -0
  13. data/docs/_site/site_libs/bootstrap/bootstrap-dark.min.css +9 -0
  14. data/docs/_site/site_libs/bootstrap/bootstrap-icons.css +2078 -0
  15. data/docs/_site/site_libs/bootstrap/bootstrap-icons.woff +0 -0
  16. data/docs/_site/site_libs/bootstrap/bootstrap.min.css +9 -0
  17. data/docs/_site/site_libs/bootstrap/bootstrap.min.js +7 -0
  18. data/docs/_site/site_libs/clipboard/clipboard.min.js +7 -0
  19. data/docs/_site/site_libs/quarto-html/anchor.min.js +9 -0
  20. data/docs/_site/site_libs/quarto-html/popper.min.js +6 -0
  21. data/docs/_site/site_libs/quarto-html/quarto-syntax-highlighting-dark.css +179 -0
  22. data/docs/_site/site_libs/quarto-html/quarto-syntax-highlighting.css +179 -0
  23. data/docs/_site/site_libs/quarto-html/quarto.js +889 -0
  24. data/docs/_site/site_libs/quarto-html/tippy.css +1 -0
  25. data/docs/_site/site_libs/quarto-html/tippy.umd.min.js +2 -0
  26. data/docs/_site/site_libs/quarto-nav/headroom.min.js +7 -0
  27. data/docs/_site/site_libs/quarto-nav/quarto-nav.js +288 -0
  28. data/docs/_site/site_libs/quarto-search/autocomplete.umd.js +3 -0
  29. data/docs/_site/site_libs/quarto-search/fuse.min.js +9 -0
  30. data/docs/_site/site_libs/quarto-search/quarto-search.js +1241 -0
  31. data/docs/_site/utils/doi_utils.html +787 -0
  32. data/docs/_site/writers/bibtex_writer.html +803 -0
  33. data/docs/favicon.ico +0 -0
  34. data/docs/images/icon.png +0 -0
  35. data/docs/index.qmd +83 -0
  36. data/docs/readers/bibtex_reader.ipynb +37 -0
  37. data/docs/theme.scss +7 -0
  38. data/docs/utils/doi_utils.ipynb +28 -0
  39. data/docs/writers/bibtex_writer.ipynb +39 -0
  40. data/lib/commonmeta/cli.rb +8 -8
  41. data/lib/commonmeta/readers/json_feed_reader.rb +6 -6
  42. data/lib/commonmeta/utils.rb +2 -2
  43. data/lib/commonmeta/version.rb +1 -1
  44. data/spec/cli_spec.rb +5 -5
  45. data/spec/fixtures/vcr_cassettes/Commonmeta_CLI/json_feed/json_feed_blog_slug.yml +71 -0
  46. data/spec/fixtures/vcr_cassettes/Commonmeta_CLI/json_feed/json_feed_updated.yml +186 -0
  47. data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_json_feed/updated_posts.yml +186 -0
  48. data/spec/readers/json_feed_reader_spec.rb +5 -5
  49. metadata +42 -5
  50. data/spec/fixtures/vcr_cassettes/Commonmeta_CLI/json_feed/json_feed_not_indexed.yml +0 -37
  51. data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_json_feed/not_indexed_posts.yml +0 -37
@@ -0,0 +1,889 @@
1
+ const sectionChanged = new CustomEvent("quarto-sectionChanged", {
2
+ detail: {},
3
+ bubbles: true,
4
+ cancelable: false,
5
+ composed: false,
6
+ });
7
+
8
+ const layoutMarginEls = () => {
9
+ // Find any conflicting margin elements and add margins to the
10
+ // top to prevent overlap
11
+ const marginChildren = window.document.querySelectorAll(
12
+ ".column-margin.column-container > * "
13
+ );
14
+
15
+ let lastBottom = 0;
16
+ for (const marginChild of marginChildren) {
17
+ if (marginChild.offsetParent !== null) {
18
+ // clear the top margin so we recompute it
19
+ marginChild.style.marginTop = null;
20
+ const top = marginChild.getBoundingClientRect().top + window.scrollY;
21
+ if (top < lastBottom) {
22
+ const margin = lastBottom - top;
23
+ marginChild.style.marginTop = `${margin}px`;
24
+ }
25
+ const styles = window.getComputedStyle(marginChild);
26
+ const marginTop = parseFloat(styles["marginTop"]);
27
+ lastBottom = top + marginChild.getBoundingClientRect().height + marginTop;
28
+ }
29
+ }
30
+ };
31
+
32
+ window.document.addEventListener("DOMContentLoaded", function (_event) {
33
+ // Recompute the position of margin elements anytime the body size changes
34
+ if (window.ResizeObserver) {
35
+ const resizeObserver = new window.ResizeObserver(
36
+ throttle(layoutMarginEls, 50)
37
+ );
38
+ resizeObserver.observe(window.document.body);
39
+ }
40
+
41
+ const tocEl = window.document.querySelector('nav.toc-active[role="doc-toc"]');
42
+ const sidebarEl = window.document.getElementById("quarto-sidebar");
43
+ const leftTocEl = window.document.getElementById("quarto-sidebar-toc-left");
44
+ const marginSidebarEl = window.document.getElementById(
45
+ "quarto-margin-sidebar"
46
+ );
47
+ // function to determine whether the element has a previous sibling that is active
48
+ const prevSiblingIsActiveLink = (el) => {
49
+ const sibling = el.previousElementSibling;
50
+ if (sibling && sibling.tagName === "A") {
51
+ return sibling.classList.contains("active");
52
+ } else {
53
+ return false;
54
+ }
55
+ };
56
+
57
+ // fire slideEnter for bootstrap tab activations (for htmlwidget resize behavior)
58
+ function fireSlideEnter(e) {
59
+ const event = window.document.createEvent("Event");
60
+ event.initEvent("slideenter", true, true);
61
+ window.document.dispatchEvent(event);
62
+ }
63
+ const tabs = window.document.querySelectorAll('a[data-bs-toggle="tab"]');
64
+ tabs.forEach((tab) => {
65
+ tab.addEventListener("shown.bs.tab", fireSlideEnter);
66
+ });
67
+
68
+ // fire slideEnter for tabby tab activations (for htmlwidget resize behavior)
69
+ document.addEventListener("tabby", fireSlideEnter, false);
70
+
71
+ // Track scrolling and mark TOC links as active
72
+ // get table of contents and sidebar (bail if we don't have at least one)
73
+ const tocLinks = tocEl
74
+ ? [...tocEl.querySelectorAll("a[data-scroll-target]")]
75
+ : [];
76
+ const makeActive = (link) => tocLinks[link].classList.add("active");
77
+ const removeActive = (link) => tocLinks[link].classList.remove("active");
78
+ const removeAllActive = () =>
79
+ [...Array(tocLinks.length).keys()].forEach((link) => removeActive(link));
80
+
81
+ // activate the anchor for a section associated with this TOC entry
82
+ tocLinks.forEach((link) => {
83
+ link.addEventListener("click", () => {
84
+ if (link.href.indexOf("#") !== -1) {
85
+ const anchor = link.href.split("#")[1];
86
+ const heading = window.document.querySelector(
87
+ `[data-anchor-id=${anchor}]`
88
+ );
89
+ if (heading) {
90
+ // Add the class
91
+ heading.classList.add("reveal-anchorjs-link");
92
+
93
+ // function to show the anchor
94
+ const handleMouseout = () => {
95
+ heading.classList.remove("reveal-anchorjs-link");
96
+ heading.removeEventListener("mouseout", handleMouseout);
97
+ };
98
+
99
+ // add a function to clear the anchor when the user mouses out of it
100
+ heading.addEventListener("mouseout", handleMouseout);
101
+ }
102
+ }
103
+ });
104
+ });
105
+
106
+ const sections = tocLinks.map((link) => {
107
+ const target = link.getAttribute("data-scroll-target");
108
+ if (target.startsWith("#")) {
109
+ return window.document.getElementById(decodeURI(`${target.slice(1)}`));
110
+ } else {
111
+ return window.document.querySelector(decodeURI(`${target}`));
112
+ }
113
+ });
114
+
115
+ const sectionMargin = 200;
116
+ let currentActive = 0;
117
+ // track whether we've initialized state the first time
118
+ let init = false;
119
+
120
+ const updateActiveLink = () => {
121
+ // The index from bottom to top (e.g. reversed list)
122
+ let sectionIndex = -1;
123
+ if (
124
+ window.innerHeight + window.pageYOffset >=
125
+ window.document.body.offsetHeight
126
+ ) {
127
+ sectionIndex = 0;
128
+ } else {
129
+ sectionIndex = [...sections].reverse().findIndex((section) => {
130
+ if (section) {
131
+ return window.pageYOffset >= section.offsetTop - sectionMargin;
132
+ } else {
133
+ return false;
134
+ }
135
+ });
136
+ }
137
+ if (sectionIndex > -1) {
138
+ const current = sections.length - sectionIndex - 1;
139
+ if (current !== currentActive) {
140
+ removeAllActive();
141
+ currentActive = current;
142
+ makeActive(current);
143
+ if (init) {
144
+ window.dispatchEvent(sectionChanged);
145
+ }
146
+ init = true;
147
+ }
148
+ }
149
+ };
150
+
151
+ const inHiddenRegion = (top, bottom, hiddenRegions) => {
152
+ for (const region of hiddenRegions) {
153
+ if (top <= region.bottom && bottom >= region.top) {
154
+ return true;
155
+ }
156
+ }
157
+ return false;
158
+ };
159
+
160
+ const categorySelector = "header.quarto-title-block .quarto-category";
161
+ const activateCategories = (href) => {
162
+ // Find any categories
163
+ // Surround them with a link pointing back to:
164
+ // #category=Authoring
165
+ try {
166
+ const categoryEls = window.document.querySelectorAll(categorySelector);
167
+ for (const categoryEl of categoryEls) {
168
+ const categoryText = categoryEl.textContent;
169
+ if (categoryText) {
170
+ const link = `${href}#category=${encodeURIComponent(categoryText)}`;
171
+ const linkEl = window.document.createElement("a");
172
+ linkEl.setAttribute("href", link);
173
+ for (const child of categoryEl.childNodes) {
174
+ linkEl.append(child);
175
+ }
176
+ categoryEl.appendChild(linkEl);
177
+ }
178
+ }
179
+ } catch {
180
+ // Ignore errors
181
+ }
182
+ };
183
+ function hasTitleCategories() {
184
+ return window.document.querySelector(categorySelector) !== null;
185
+ }
186
+
187
+ function offsetRelativeUrl(url) {
188
+ const offset = getMeta("quarto:offset");
189
+ return offset ? offset + url : url;
190
+ }
191
+
192
+ function offsetAbsoluteUrl(url) {
193
+ const offset = getMeta("quarto:offset");
194
+ const baseUrl = new URL(offset, window.location);
195
+
196
+ const projRelativeUrl = url.replace(baseUrl, "");
197
+ if (projRelativeUrl.startsWith("/")) {
198
+ return projRelativeUrl;
199
+ } else {
200
+ return "/" + projRelativeUrl;
201
+ }
202
+ }
203
+
204
+ // read a meta tag value
205
+ function getMeta(metaName) {
206
+ const metas = window.document.getElementsByTagName("meta");
207
+ for (let i = 0; i < metas.length; i++) {
208
+ if (metas[i].getAttribute("name") === metaName) {
209
+ return metas[i].getAttribute("content");
210
+ }
211
+ }
212
+ return "";
213
+ }
214
+
215
+ async function findAndActivateCategories() {
216
+ const currentPagePath = offsetAbsoluteUrl(window.location.href);
217
+ const response = await fetch(offsetRelativeUrl("listings.json"));
218
+ if (response.status == 200) {
219
+ return response.json().then(function (listingPaths) {
220
+ const listingHrefs = [];
221
+ for (const listingPath of listingPaths) {
222
+ const pathWithoutLeadingSlash = listingPath.listing.substring(1);
223
+ for (const item of listingPath.items) {
224
+ if (
225
+ item === currentPagePath ||
226
+ item === currentPagePath + "index.html"
227
+ ) {
228
+ // Resolve this path against the offset to be sure
229
+ // we already are using the correct path to the listing
230
+ // (this adjusts the listing urls to be rooted against
231
+ // whatever root the page is actually running against)
232
+ const relative = offsetRelativeUrl(pathWithoutLeadingSlash);
233
+ const baseUrl = window.location;
234
+ const resolvedPath = new URL(relative, baseUrl);
235
+ listingHrefs.push(resolvedPath.pathname);
236
+ break;
237
+ }
238
+ }
239
+ }
240
+
241
+ // Look up the tree for a nearby linting and use that if we find one
242
+ const nearestListing = findNearestParentListing(
243
+ offsetAbsoluteUrl(window.location.pathname),
244
+ listingHrefs
245
+ );
246
+ if (nearestListing) {
247
+ activateCategories(nearestListing);
248
+ } else {
249
+ // See if the referrer is a listing page for this item
250
+ const referredRelativePath = offsetAbsoluteUrl(document.referrer);
251
+ const referrerListing = listingHrefs.find((listingHref) => {
252
+ const isListingReferrer =
253
+ listingHref === referredRelativePath ||
254
+ listingHref === referredRelativePath + "index.html";
255
+ return isListingReferrer;
256
+ });
257
+
258
+ if (referrerListing) {
259
+ // Try to use the referrer if possible
260
+ activateCategories(referrerListing);
261
+ } else if (listingHrefs.length > 0) {
262
+ // Otherwise, just fall back to the first listing
263
+ activateCategories(listingHrefs[0]);
264
+ }
265
+ }
266
+ });
267
+ }
268
+ }
269
+ if (hasTitleCategories()) {
270
+ findAndActivateCategories();
271
+ }
272
+
273
+ const findNearestParentListing = (href, listingHrefs) => {
274
+ if (!href || !listingHrefs) {
275
+ return undefined;
276
+ }
277
+ // Look up the tree for a nearby linting and use that if we find one
278
+ const relativeParts = href.substring(1).split("/");
279
+ while (relativeParts.length > 0) {
280
+ const path = relativeParts.join("/");
281
+ for (const listingHref of listingHrefs) {
282
+ if (listingHref.startsWith(path)) {
283
+ return listingHref;
284
+ }
285
+ }
286
+ relativeParts.pop();
287
+ }
288
+
289
+ return undefined;
290
+ };
291
+
292
+ const manageSidebarVisiblity = (el, placeholderDescriptor) => {
293
+ let isVisible = true;
294
+ let elRect;
295
+
296
+ return (hiddenRegions) => {
297
+ if (el === null) {
298
+ return;
299
+ }
300
+
301
+ // Find the last element of the TOC
302
+ const lastChildEl = el.lastElementChild;
303
+
304
+ if (lastChildEl) {
305
+ // Converts the sidebar to a menu
306
+ const convertToMenu = () => {
307
+ for (const child of el.children) {
308
+ child.style.opacity = 0;
309
+ child.style.overflow = "hidden";
310
+ }
311
+
312
+ nexttick(() => {
313
+ const toggleContainer = window.document.createElement("div");
314
+ toggleContainer.style.width = "100%";
315
+ toggleContainer.classList.add("zindex-over-content");
316
+ toggleContainer.classList.add("quarto-sidebar-toggle");
317
+ toggleContainer.classList.add("headroom-target"); // Marks this to be managed by headeroom
318
+ toggleContainer.id = placeholderDescriptor.id;
319
+ toggleContainer.style.position = "fixed";
320
+
321
+ const toggleIcon = window.document.createElement("i");
322
+ toggleIcon.classList.add("quarto-sidebar-toggle-icon");
323
+ toggleIcon.classList.add("bi");
324
+ toggleIcon.classList.add("bi-caret-down-fill");
325
+
326
+ const toggleTitle = window.document.createElement("div");
327
+ const titleEl = window.document.body.querySelector(
328
+ placeholderDescriptor.titleSelector
329
+ );
330
+ if (titleEl) {
331
+ toggleTitle.append(
332
+ titleEl.textContent || titleEl.innerText,
333
+ toggleIcon
334
+ );
335
+ }
336
+ toggleTitle.classList.add("zindex-over-content");
337
+ toggleTitle.classList.add("quarto-sidebar-toggle-title");
338
+ toggleContainer.append(toggleTitle);
339
+
340
+ const toggleContents = window.document.createElement("div");
341
+ toggleContents.classList = el.classList;
342
+ toggleContents.classList.add("zindex-over-content");
343
+ toggleContents.classList.add("quarto-sidebar-toggle-contents");
344
+ for (const child of el.children) {
345
+ if (child.id === "toc-title") {
346
+ continue;
347
+ }
348
+
349
+ const clone = child.cloneNode(true);
350
+ clone.style.opacity = 1;
351
+ clone.style.display = null;
352
+ toggleContents.append(clone);
353
+ }
354
+ toggleContents.style.height = "0px";
355
+ const positionToggle = () => {
356
+ // position the element (top left of parent, same width as parent)
357
+ if (!elRect) {
358
+ elRect = el.getBoundingClientRect();
359
+ }
360
+ toggleContainer.style.left = `${elRect.left}px`;
361
+ toggleContainer.style.top = `${elRect.top}px`;
362
+ toggleContainer.style.width = `${elRect.width}px`;
363
+ };
364
+ positionToggle();
365
+
366
+ toggleContainer.append(toggleContents);
367
+ el.parentElement.prepend(toggleContainer);
368
+
369
+ // Process clicks
370
+ let tocShowing = false;
371
+ // Allow the caller to control whether this is dismissed
372
+ // when it is clicked (e.g. sidebar navigation supports
373
+ // opening and closing the nav tree, so don't dismiss on click)
374
+ const clickEl = placeholderDescriptor.dismissOnClick
375
+ ? toggleContainer
376
+ : toggleTitle;
377
+
378
+ const closeToggle = () => {
379
+ if (tocShowing) {
380
+ toggleContainer.classList.remove("expanded");
381
+ toggleContents.style.height = "0px";
382
+ tocShowing = false;
383
+ }
384
+ };
385
+
386
+ // Get rid of any expanded toggle if the user scrolls
387
+ window.document.addEventListener(
388
+ "scroll",
389
+ throttle(() => {
390
+ closeToggle();
391
+ }, 50)
392
+ );
393
+
394
+ // Handle positioning of the toggle
395
+ window.addEventListener(
396
+ "resize",
397
+ throttle(() => {
398
+ elRect = undefined;
399
+ positionToggle();
400
+ }, 50)
401
+ );
402
+
403
+ window.addEventListener("quarto-hrChanged", () => {
404
+ elRect = undefined;
405
+ });
406
+
407
+ // Process the click
408
+ clickEl.onclick = () => {
409
+ if (!tocShowing) {
410
+ toggleContainer.classList.add("expanded");
411
+ toggleContents.style.height = null;
412
+ tocShowing = true;
413
+ } else {
414
+ closeToggle();
415
+ }
416
+ };
417
+ });
418
+ };
419
+
420
+ // Converts a sidebar from a menu back to a sidebar
421
+ const convertToSidebar = () => {
422
+ for (const child of el.children) {
423
+ child.style.opacity = 1;
424
+ child.style.overflow = null;
425
+ }
426
+
427
+ const placeholderEl = window.document.getElementById(
428
+ placeholderDescriptor.id
429
+ );
430
+ if (placeholderEl) {
431
+ placeholderEl.remove();
432
+ }
433
+
434
+ el.classList.remove("rollup");
435
+ };
436
+
437
+ if (isReaderMode()) {
438
+ convertToMenu();
439
+ isVisible = false;
440
+ } else {
441
+ // Find the top and bottom o the element that is being managed
442
+ const elTop = el.offsetTop;
443
+ const elBottom =
444
+ elTop + lastChildEl.offsetTop + lastChildEl.offsetHeight;
445
+
446
+ if (!isVisible) {
447
+ // If the element is current not visible reveal if there are
448
+ // no conflicts with overlay regions
449
+ if (!inHiddenRegion(elTop, elBottom, hiddenRegions)) {
450
+ convertToSidebar();
451
+ isVisible = true;
452
+ }
453
+ } else {
454
+ // If the element is visible, hide it if it conflicts with overlay regions
455
+ // and insert a placeholder toggle (or if we're in reader mode)
456
+ if (inHiddenRegion(elTop, elBottom, hiddenRegions)) {
457
+ convertToMenu();
458
+ isVisible = false;
459
+ }
460
+ }
461
+ }
462
+ }
463
+ };
464
+ };
465
+
466
+ const tabEls = document.querySelectorAll('a[data-bs-toggle="tab"]');
467
+ for (const tabEl of tabEls) {
468
+ const id = tabEl.getAttribute("data-bs-target");
469
+ if (id) {
470
+ const columnEl = document.querySelector(
471
+ `${id} .column-margin, .tabset-margin-content`
472
+ );
473
+ if (columnEl)
474
+ tabEl.addEventListener("shown.bs.tab", function (event) {
475
+ const el = event.srcElement;
476
+ if (el) {
477
+ const visibleCls = `${el.id}-margin-content`;
478
+ // walk up until we find a parent tabset
479
+ let panelTabsetEl = el.parentElement;
480
+ while (panelTabsetEl) {
481
+ if (panelTabsetEl.classList.contains("panel-tabset")) {
482
+ break;
483
+ }
484
+ panelTabsetEl = panelTabsetEl.parentElement;
485
+ }
486
+
487
+ if (panelTabsetEl) {
488
+ const prevSib = panelTabsetEl.previousElementSibling;
489
+ if (
490
+ prevSib &&
491
+ prevSib.classList.contains("tabset-margin-container")
492
+ ) {
493
+ const childNodes = prevSib.querySelectorAll(
494
+ ".tabset-margin-content"
495
+ );
496
+ for (const childEl of childNodes) {
497
+ if (childEl.classList.contains(visibleCls)) {
498
+ childEl.classList.remove("collapse");
499
+ } else {
500
+ childEl.classList.add("collapse");
501
+ }
502
+ }
503
+ }
504
+ }
505
+ }
506
+
507
+ layoutMarginEls();
508
+ });
509
+ }
510
+ }
511
+
512
+ // Manage the visibility of the toc and the sidebar
513
+ const marginScrollVisibility = manageSidebarVisiblity(marginSidebarEl, {
514
+ id: "quarto-toc-toggle",
515
+ titleSelector: "#toc-title",
516
+ dismissOnClick: true,
517
+ });
518
+ const sidebarScrollVisiblity = manageSidebarVisiblity(sidebarEl, {
519
+ id: "quarto-sidebarnav-toggle",
520
+ titleSelector: ".title",
521
+ dismissOnClick: false,
522
+ });
523
+ let tocLeftScrollVisibility;
524
+ if (leftTocEl) {
525
+ tocLeftScrollVisibility = manageSidebarVisiblity(leftTocEl, {
526
+ id: "quarto-lefttoc-toggle",
527
+ titleSelector: "#toc-title",
528
+ dismissOnClick: true,
529
+ });
530
+ }
531
+
532
+ // Find the first element that uses formatting in special columns
533
+ const conflictingEls = window.document.body.querySelectorAll(
534
+ '[class^="column-"], [class*=" column-"], aside, [class*="margin-caption"], [class*=" margin-caption"], [class*="margin-ref"], [class*=" margin-ref"]'
535
+ );
536
+
537
+ // Filter all the possibly conflicting elements into ones
538
+ // the do conflict on the left or ride side
539
+ const arrConflictingEls = Array.from(conflictingEls);
540
+ const leftSideConflictEls = arrConflictingEls.filter((el) => {
541
+ if (el.tagName === "ASIDE") {
542
+ return false;
543
+ }
544
+ return Array.from(el.classList).find((className) => {
545
+ return (
546
+ className !== "column-body" &&
547
+ className.startsWith("column-") &&
548
+ !className.endsWith("right") &&
549
+ !className.endsWith("container") &&
550
+ className !== "column-margin"
551
+ );
552
+ });
553
+ });
554
+ const rightSideConflictEls = arrConflictingEls.filter((el) => {
555
+ if (el.tagName === "ASIDE") {
556
+ return true;
557
+ }
558
+
559
+ const hasMarginCaption = Array.from(el.classList).find((className) => {
560
+ return className == "margin-caption";
561
+ });
562
+ if (hasMarginCaption) {
563
+ return true;
564
+ }
565
+
566
+ return Array.from(el.classList).find((className) => {
567
+ return (
568
+ className !== "column-body" &&
569
+ !className.endsWith("container") &&
570
+ className.startsWith("column-") &&
571
+ !className.endsWith("left")
572
+ );
573
+ });
574
+ });
575
+
576
+ const kOverlapPaddingSize = 10;
577
+ function toRegions(els) {
578
+ return els.map((el) => {
579
+ const boundRect = el.getBoundingClientRect();
580
+ const top =
581
+ boundRect.top +
582
+ document.documentElement.scrollTop -
583
+ kOverlapPaddingSize;
584
+ return {
585
+ top,
586
+ bottom: top + el.scrollHeight + 2 * kOverlapPaddingSize,
587
+ };
588
+ });
589
+ }
590
+
591
+ let hasObserved = false;
592
+ const visibleItemObserver = (els) => {
593
+ let visibleElements = [...els];
594
+ const intersectionObserver = new IntersectionObserver(
595
+ (entries, _observer) => {
596
+ entries.forEach((entry) => {
597
+ if (entry.isIntersecting) {
598
+ if (visibleElements.indexOf(entry.target) === -1) {
599
+ visibleElements.push(entry.target);
600
+ }
601
+ } else {
602
+ visibleElements = visibleElements.filter((visibleEntry) => {
603
+ return visibleEntry !== entry;
604
+ });
605
+ }
606
+ });
607
+
608
+ if (!hasObserved) {
609
+ hideOverlappedSidebars();
610
+ }
611
+ hasObserved = true;
612
+ },
613
+ {}
614
+ );
615
+ els.forEach((el) => {
616
+ intersectionObserver.observe(el);
617
+ });
618
+
619
+ return {
620
+ getVisibleEntries: () => {
621
+ return visibleElements;
622
+ },
623
+ };
624
+ };
625
+
626
+ const rightElementObserver = visibleItemObserver(rightSideConflictEls);
627
+ const leftElementObserver = visibleItemObserver(leftSideConflictEls);
628
+
629
+ const hideOverlappedSidebars = () => {
630
+ marginScrollVisibility(toRegions(rightElementObserver.getVisibleEntries()));
631
+ sidebarScrollVisiblity(toRegions(leftElementObserver.getVisibleEntries()));
632
+ if (tocLeftScrollVisibility) {
633
+ tocLeftScrollVisibility(
634
+ toRegions(leftElementObserver.getVisibleEntries())
635
+ );
636
+ }
637
+ };
638
+
639
+ window.quartoToggleReader = () => {
640
+ // Applies a slow class (or removes it)
641
+ // to update the transition speed
642
+ const slowTransition = (slow) => {
643
+ const manageTransition = (id, slow) => {
644
+ const el = document.getElementById(id);
645
+ if (el) {
646
+ if (slow) {
647
+ el.classList.add("slow");
648
+ } else {
649
+ el.classList.remove("slow");
650
+ }
651
+ }
652
+ };
653
+
654
+ manageTransition("TOC", slow);
655
+ manageTransition("quarto-sidebar", slow);
656
+ };
657
+ const readerMode = !isReaderMode();
658
+ setReaderModeValue(readerMode);
659
+
660
+ // If we're entering reader mode, slow the transition
661
+ if (readerMode) {
662
+ slowTransition(readerMode);
663
+ }
664
+ highlightReaderToggle(readerMode);
665
+ hideOverlappedSidebars();
666
+
667
+ // If we're exiting reader mode, restore the non-slow transition
668
+ if (!readerMode) {
669
+ slowTransition(!readerMode);
670
+ }
671
+ };
672
+
673
+ const highlightReaderToggle = (readerMode) => {
674
+ const els = document.querySelectorAll(".quarto-reader-toggle");
675
+ if (els) {
676
+ els.forEach((el) => {
677
+ if (readerMode) {
678
+ el.classList.add("reader");
679
+ } else {
680
+ el.classList.remove("reader");
681
+ }
682
+ });
683
+ }
684
+ };
685
+
686
+ const setReaderModeValue = (val) => {
687
+ if (window.location.protocol !== "file:") {
688
+ window.localStorage.setItem("quarto-reader-mode", val);
689
+ } else {
690
+ localReaderMode = val;
691
+ }
692
+ };
693
+
694
+ const isReaderMode = () => {
695
+ if (window.location.protocol !== "file:") {
696
+ return window.localStorage.getItem("quarto-reader-mode") === "true";
697
+ } else {
698
+ return localReaderMode;
699
+ }
700
+ };
701
+ let localReaderMode = null;
702
+
703
+ const tocOpenDepthStr = tocEl?.getAttribute("data-toc-expanded");
704
+ const tocOpenDepth = tocOpenDepthStr ? Number(tocOpenDepthStr) : 1;
705
+
706
+ // Walk the TOC and collapse/expand nodes
707
+ // Nodes are expanded if:
708
+ // - they are top level
709
+ // - they have children that are 'active' links
710
+ // - they are directly below an link that is 'active'
711
+ const walk = (el, depth) => {
712
+ // Tick depth when we enter a UL
713
+ if (el.tagName === "UL") {
714
+ depth = depth + 1;
715
+ }
716
+
717
+ // It this is active link
718
+ let isActiveNode = false;
719
+ if (el.tagName === "A" && el.classList.contains("active")) {
720
+ isActiveNode = true;
721
+ }
722
+
723
+ // See if there is an active child to this element
724
+ let hasActiveChild = false;
725
+ for (child of el.children) {
726
+ hasActiveChild = walk(child, depth) || hasActiveChild;
727
+ }
728
+
729
+ // Process the collapse state if this is an UL
730
+ if (el.tagName === "UL") {
731
+ if (tocOpenDepth === -1 && depth > 1) {
732
+ el.classList.add("collapse");
733
+ } else if (
734
+ depth <= tocOpenDepth ||
735
+ hasActiveChild ||
736
+ prevSiblingIsActiveLink(el)
737
+ ) {
738
+ el.classList.remove("collapse");
739
+ } else {
740
+ el.classList.add("collapse");
741
+ }
742
+
743
+ // untick depth when we leave a UL
744
+ depth = depth - 1;
745
+ }
746
+ return hasActiveChild || isActiveNode;
747
+ };
748
+
749
+ // walk the TOC and expand / collapse any items that should be shown
750
+
751
+ if (tocEl) {
752
+ walk(tocEl, 0);
753
+ updateActiveLink();
754
+ }
755
+
756
+ // Throttle the scroll event and walk peridiocally
757
+ window.document.addEventListener(
758
+ "scroll",
759
+ throttle(() => {
760
+ if (tocEl) {
761
+ updateActiveLink();
762
+ walk(tocEl, 0);
763
+ }
764
+ if (!isReaderMode()) {
765
+ hideOverlappedSidebars();
766
+ }
767
+ }, 5)
768
+ );
769
+ window.addEventListener(
770
+ "resize",
771
+ throttle(() => {
772
+ if (!isReaderMode()) {
773
+ hideOverlappedSidebars();
774
+ }
775
+ }, 10)
776
+ );
777
+ hideOverlappedSidebars();
778
+ highlightReaderToggle(isReaderMode());
779
+ });
780
+
781
+ // grouped tabsets
782
+ window.addEventListener("pageshow", (_event) => {
783
+ function getTabSettings() {
784
+ const data = localStorage.getItem("quarto-persistent-tabsets-data");
785
+ if (!data) {
786
+ localStorage.setItem("quarto-persistent-tabsets-data", "{}");
787
+ return {};
788
+ }
789
+ if (data) {
790
+ return JSON.parse(data);
791
+ }
792
+ }
793
+
794
+ function setTabSettings(data) {
795
+ localStorage.setItem(
796
+ "quarto-persistent-tabsets-data",
797
+ JSON.stringify(data)
798
+ );
799
+ }
800
+
801
+ function setTabState(groupName, groupValue) {
802
+ const data = getTabSettings();
803
+ data[groupName] = groupValue;
804
+ setTabSettings(data);
805
+ }
806
+
807
+ function toggleTab(tab, active) {
808
+ const tabPanelId = tab.getAttribute("aria-controls");
809
+ const tabPanel = document.getElementById(tabPanelId);
810
+ if (active) {
811
+ tab.classList.add("active");
812
+ tabPanel.classList.add("active");
813
+ } else {
814
+ tab.classList.remove("active");
815
+ tabPanel.classList.remove("active");
816
+ }
817
+ }
818
+
819
+ function toggleAll(selectedGroup, selectorsToSync) {
820
+ for (const [thisGroup, tabs] of Object.entries(selectorsToSync)) {
821
+ const active = selectedGroup === thisGroup;
822
+ for (const tab of tabs) {
823
+ toggleTab(tab, active);
824
+ }
825
+ }
826
+ }
827
+
828
+ function findSelectorsToSyncByLanguage() {
829
+ const result = {};
830
+ const tabs = Array.from(
831
+ document.querySelectorAll(`div[data-group] a[id^='tabset-']`)
832
+ );
833
+ for (const item of tabs) {
834
+ const div = item.parentElement.parentElement.parentElement;
835
+ const group = div.getAttribute("data-group");
836
+ if (!result[group]) {
837
+ result[group] = {};
838
+ }
839
+ const selectorsToSync = result[group];
840
+ const value = item.innerHTML;
841
+ if (!selectorsToSync[value]) {
842
+ selectorsToSync[value] = [];
843
+ }
844
+ selectorsToSync[value].push(item);
845
+ }
846
+ return result;
847
+ }
848
+
849
+ function setupSelectorSync() {
850
+ const selectorsToSync = findSelectorsToSyncByLanguage();
851
+ Object.entries(selectorsToSync).forEach(([group, tabSetsByValue]) => {
852
+ Object.entries(tabSetsByValue).forEach(([value, items]) => {
853
+ items.forEach((item) => {
854
+ item.addEventListener("click", (_event) => {
855
+ setTabState(group, value);
856
+ toggleAll(value, selectorsToSync[group]);
857
+ });
858
+ });
859
+ });
860
+ });
861
+ return selectorsToSync;
862
+ }
863
+
864
+ const selectorsToSync = setupSelectorSync();
865
+ for (const [group, selectedName] of Object.entries(getTabSettings())) {
866
+ const selectors = selectorsToSync[group];
867
+ // it's possible that stale state gives us empty selections, so we explicitly check here.
868
+ if (selectors) {
869
+ toggleAll(selectedName, selectors);
870
+ }
871
+ }
872
+ });
873
+
874
+ function throttle(func, wait) {
875
+ let waiting = false;
876
+ return function () {
877
+ if (!waiting) {
878
+ func.apply(this, arguments);
879
+ waiting = true;
880
+ setTimeout(function () {
881
+ waiting = false;
882
+ }, wait);
883
+ }
884
+ };
885
+ }
886
+
887
+ function nexttick(func) {
888
+ return setTimeout(func, 0);
889
+ }