small updates and corrections

This commit is contained in:
Jeremy Kidwell 2024-02-13 10:59:42 +00:00
parent f0c39da8b9
commit d7eb7814d5
14 changed files with 484 additions and 131 deletions

View file

@ -6,8 +6,13 @@ You can view a live demo of the book here: [https://kidwellj.github.io/hacking_r
The course here has been compiled with [quarto](https://quarto.org/), and so the live instance of the course is compiled from openly accessible resources located in this repository. If you're interested in doing something similar, there are a number of other options, some of which have lamentably turned commercial, including: [bookdown](https://github.com/rstudio/bookdown), [gitbook](https://docs.gitbook.com/), [mkdocs](https://www.mkdocs.org/), [readthedocs](https://readthedocs.org) which technically uses [Sphinx](http://www.sphinx-doc.org/en/master/) or [daux](https://daux.io/). The course here has been compiled with [quarto](https://quarto.org/), and so the live instance of the course is compiled from openly accessible resources located in this repository. If you're interested in doing something similar, there are a number of other options, some of which have lamentably turned commercial, including: [bookdown](https://github.com/rstudio/bookdown), [gitbook](https://docs.gitbook.com/), [mkdocs](https://www.mkdocs.org/), [readthedocs](https://readthedocs.org) which technically uses [Sphinx](http://www.sphinx-doc.org/en/master/) or [daux](https://daux.io/).
Directory structure includes: Top level files and directories in this repository are:
* `README.md` this README file displayed on Github * `README.md` this README file displayed on Github
* `hacking_religion` a folder containing the quarto code and accompanying files which are used to render the book in html and .pdf formats
* `docs` a rendered sample version of the most recent version of the book (used for )
Directory structure includes:
* `_book` a folder containing the compiled book in html and .pdf formats * `_book` a folder containing the compiled book in html and .pdf formats
* `references.bib` a bibliography of items used for the course in [BibTeX format](http://www.bibtex.org/Format/) * `references.bib` a bibliography of items used for the course in [BibTeX format](http://www.bibtex.org/Format/)
* `preface.qmd` Preface * `preface.qmd` Preface
@ -22,8 +27,13 @@ Directory structure includes:
1. clone this repository using `git clone https://github.com/kidwellj/hacking_religion_textbook.git` ([install git](https://git-scm.com/downloads) if you haven't already) 1. clone this repository using `git clone https://github.com/kidwellj/hacking_religion_textbook.git` ([install git](https://git-scm.com/downloads) if you haven't already)
2. [install quarto](https://quarto.org/docs/get-started/) 2. [install quarto](https://quarto.org/docs/get-started/)
3. change to the `hacking_religion` subdirectory and run `quarto preview` 3. install the necessary pre-requisite R libraries, including `knitr` and `rmarkdown`. This can be done by running the following commands in an RStudio console:
4. alternatively you can render a copy of the book using `quarto render` ```{r}
install.packages("knitr")
install.packages("rmarkdown")
```
4. change to the `hacking_religion` subdirectory and run `quarto preview` to get a temporary preview of the book as a website running on your local PC.
5. alternatively you can render a copy of the book using `quarto render`.
# Cookbook # Cookbook

View file

@ -2,7 +2,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="generator" content="quarto-1.3.450"> <meta name="generator" content="quarto-1.4.549">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
@ -48,7 +48,13 @@ ul.task-list li input[type="checkbox"] {
"collapse-after": 3, "collapse-after": 3,
"panel-placement": "start", "panel-placement": "start",
"type": "textbox", "type": "textbox",
"limit": 20, "limit": 50,
"keyboard-shortcut": [
"f",
"/",
"s"
],
"show-item-context": false,
"language": { "language": {
"search-no-results-text": "No results", "search-no-results-text": "No results",
"search-matching-documents-text": "matching documents", "search-matching-documents-text": "matching documents",
@ -57,6 +63,7 @@ ul.task-list li input[type="checkbox"] {
"search-more-match-text": "more match in this document", "search-more-match-text": "more match in this document",
"search-more-matches-text": "more matches in this document", "search-more-matches-text": "more matches in this document",
"search-clear-button-title": "Clear", "search-clear-button-title": "Clear",
"search-text-placeholder": "",
"search-detached-cancel-button-title": "Cancel", "search-detached-cancel-button-title": "Cancel",
"search-submit-button-title": "Submit", "search-submit-button-title": "Submit",
"search-label": "Search" "search-label": "Search"
@ -72,11 +79,11 @@ ul.task-list li input[type="checkbox"] {
<header id="quarto-header" class="headroom fixed-top"> <header id="quarto-header" class="headroom fixed-top">
<nav class="quarto-secondary-nav"> <nav class="quarto-secondary-nav">
<div class="container-fluid d-flex"> <div class="container-fluid d-flex">
<button type="button" class="quarto-btn-toggle btn" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar,#quarto-sidebar-glass" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }"> <button type="button" class="quarto-btn-toggle btn" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<i class="bi bi-layout-text-sidebar-reverse"></i> <i class="bi bi-layout-text-sidebar-reverse"></i>
</button> </button>
<nav class="quarto-page-breadcrumbs" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="./index.html">Introduction: Hacking Religion</a></li></ol></nav> <nav class="quarto-page-breadcrumbs" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="./index.html">Introduction: Hacking Religion</a></li></ol></nav>
<a class="flex-grow-1" role="button" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar,#quarto-sidebar-glass" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }"> <a class="flex-grow-1" role="button" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
</a> </a>
<button type="button" class="btn quarto-search-button" aria-label="" onclick="window.quartoOpenSearch();"> <button type="button" class="btn quarto-search-button" aria-label="" onclick="window.quartoOpenSearch();">
<i class="bi bi-search"></i> <i class="bi bi-search"></i>
@ -87,7 +94,7 @@ ul.task-list li input[type="checkbox"] {
<!-- content --> <!-- content -->
<div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-article"> <div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-article">
<!-- sidebar --> <!-- sidebar -->
<nav id="quarto-sidebar" class="sidebar collapse collapse-horizontal sidebar-navigation floating overflow-auto"> <nav id="quarto-sidebar" class="sidebar collapse collapse-horizontal quarto-sidebar-collapse-item sidebar-navigation floating overflow-auto">
<div class="pt-lg-2 mt-2 text-left sidebar-header"> <div class="pt-lg-2 mt-2 text-left sidebar-header">
<div class="sidebar-title mb-0 py-0"> <div class="sidebar-title mb-0 py-0">
<a href="./">Hacking Religion: TRS &amp; Data Science in Action</a> <a href="./">Hacking Religion: TRS &amp; Data Science in Action</a>
@ -151,7 +158,7 @@ ul.task-list li input[type="checkbox"] {
</ul> </ul>
</div> </div>
</nav> </nav>
<div id="quarto-sidebar-glass" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar,#quarto-sidebar-glass"></div> <div id="quarto-sidebar-glass" class="quarto-sidebar-collapse-item" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item"></div>
<!-- margin-sidebar --> <!-- margin-sidebar -->
<div id="quarto-margin-sidebar" class="sidebar margin-sidebar"> <div id="quarto-margin-sidebar" class="sidebar margin-sidebar">
<nav id="TOC" role="doc-toc" class="toc-active"> <nav id="TOC" role="doc-toc" class="toc-active">
@ -198,8 +205,10 @@ ul.task-list li input[type="checkbox"] {
</div> </div>
</header> </header>
<section id="introduction-hacking-religion" class="level1 unnumbered"> <section id="introduction-hacking-religion" class="level1 unnumbered">
<h1 class="unnumbered">Introduction: Hacking Religion</h1> <h1 class="unnumbered">Introduction: Hacking Religion</h1>
<section id="why-this-book" class="level2"> <section id="why-this-book" class="level2">
@ -318,10 +327,9 @@ window.document.addEventListener("DOMContentLoaded", function (event) {
// clear code selection // clear code selection
e.clearSelection(); e.clearSelection();
}); });
function tippyHover(el, contentFn) { function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) {
const config = { const config = {
allowHTML: true, allowHTML: true,
content: contentFn,
maxWidth: 500, maxWidth: 500,
delay: 100, delay: 100,
arrow: false, arrow: false,
@ -331,8 +339,17 @@ window.document.addEventListener("DOMContentLoaded", function (event) {
interactive: true, interactive: true,
interactiveBorder: 10, interactiveBorder: 10,
theme: 'quarto', theme: 'quarto',
placement: 'bottom-start' placement: 'bottom-start',
}; };
if (contentFn) {
config.content = contentFn;
}
if (onTriggerFn) {
config.onTrigger = onTriggerFn;
}
if (onUntriggerFn) {
config.onUntrigger = onUntriggerFn;
}
window.tippy(el, config); window.tippy(el, config);
} }
const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]'); const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
@ -346,6 +363,125 @@ window.document.addEventListener("DOMContentLoaded", function (event) {
const note = window.document.getElementById(id); const note = window.document.getElementById(id);
return note.innerHTML; return note.innerHTML;
}); });
}
const xrefs = window.document.querySelectorAll('a.quarto-xref');
const processXRef = (id, note) => {
// Strip column container classes
const stripColumnClz = (el) => {
el.classList.remove("page-full", "page-columns");
if (el.children) {
for (const child of el.children) {
stripColumnClz(child);
}
}
}
stripColumnClz(note)
if (id === null || id.startsWith('sec-')) {
// Special case sections, only their first couple elements
const container = document.createElement("div");
if (note.children && note.children.length > 2) {
container.appendChild(note.children[0].cloneNode(true));
for (let i = 1; i < note.children.length; i++) {
const child = note.children[i];
if (child.tagName === "P" && child.innerText === "") {
continue;
} else {
container.appendChild(child.cloneNode(true));
break;
}
}
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(container);
}
return container.innerHTML
} else {
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(note);
}
return note.innerHTML;
}
} else {
// Remove any anchor links if they are present
const anchorLink = note.querySelector('a.anchorjs-link');
if (anchorLink) {
anchorLink.remove();
}
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(note);
}
// TODO in 1.5, we should make sure this works without a callout special case
if (note.classList.contains("callout")) {
return note.outerHTML;
} else {
return note.innerHTML;
}
}
}
for (var i=0; i<xrefs.length; i++) {
const xref = xrefs[i];
tippyHover(xref, undefined, function(instance) {
instance.disable();
let url = xref.getAttribute('href');
let hash = undefined;
if (url.startsWith('#')) {
hash = url;
} else {
try { hash = new URL(url).hash; } catch {}
}
if (hash) {
const id = hash.replace(/^#\/?/, "");
const note = window.document.getElementById(id);
if (note !== null) {
try {
const html = processXRef(id, note.cloneNode(true));
instance.setContent(html);
} finally {
instance.enable();
instance.show();
}
} else {
// See if we can fetch this
fetch(url.split('#')[0])
.then(res => res.text())
.then(html => {
const parser = new DOMParser();
const htmlDoc = parser.parseFromString(html, "text/html");
const note = htmlDoc.getElementById(id);
if (note !== null) {
const html = processXRef(id, note);
instance.setContent(html);
}
}).finally(() => {
instance.enable();
instance.show();
});
}
} else {
// See if we can fetch a full url (with no hash to target)
// This is a special case and we should probably do some content thinning / targeting
fetch(url)
.then(res => res.text())
.then(html => {
const parser = new DOMParser();
const htmlDoc = parser.parseFromString(html, "text/html");
const note = htmlDoc.querySelector('main.content');
if (note !== null) {
// This should only happen for chapter cross references
// (since there is no id in the URL)
// remove the first header
if (note.children.length > 0 && note.children[0].tagName === "HEADER") {
note.children[0].remove();
}
const html = processXRef(null, note);
instance.setContent(html);
}
}).finally(() => {
instance.enable();
instance.show();
});
}
}, function(instance) {
});
} }
let selectedAnnoteEl; let selectedAnnoteEl;
const selectorForAnnotation = ( cell, annotation) => { const selectorForAnnotation = ( cell, annotation) => {
@ -388,6 +524,7 @@ window.document.addEventListener("DOMContentLoaded", function (event) {
} }
div.style.top = top - 2 + "px"; div.style.top = top - 2 + "px";
div.style.height = height + 4 + "px"; div.style.height = height + 4 + "px";
div.style.left = 0;
let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter"); let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
if (gutterDiv === null) { if (gutterDiv === null) {
gutterDiv = window.document.createElement("div"); gutterDiv = window.document.createElement("div");
@ -413,6 +550,32 @@ window.document.addEventListener("DOMContentLoaded", function (event) {
}); });
selectedAnnoteEl = undefined; selectedAnnoteEl = undefined;
}; };
// Handle positioning of the toggle
window.addEventListener(
"resize",
throttle(() => {
elRect = undefined;
if (selectedAnnoteEl) {
selectCodeLines(selectedAnnoteEl);
}
}, 10)
);
function throttle(fn, ms) {
let throttle = false;
let timer;
return (...args) => {
if(!throttle) { // first call gets through
fn.apply(this, args);
throttle = true;
} else { // all the others get throttled
if(timer) clearTimeout(timer); // cancel #2
timer = setTimeout(() => {
fn.apply(this, args);
timer = throttle = false;
}, ms);
}
};
}
// Attach click handler to the DT // Attach click handler to the DT
const annoteDls = window.document.querySelectorAll('dt[data-target-cell]'); const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
for (const annoteDlNode of annoteDls) { for (const annoteDlNode of annoteDls) {
@ -476,7 +639,7 @@ window.document.addEventListener("DOMContentLoaded", function (event) {
<div class="nav-page nav-page-previous"> <div class="nav-page nav-page-previous">
</div> </div>
<div class="nav-page nav-page-next"> <div class="nav-page nav-page-next">
<a href="./chapter_1.html" class="pagination-link"> <a href="./chapter_1.html" class="pagination-link" aria-label="<span class='chapter-number'>1</span>&nbsp; <span class='chapter-title'>The 2021 UK Census</span>">
<span class="nav-page-text"><span class="chapter-number">1</span>&nbsp; <span class="chapter-title">The 2021 UK Census</span></span> <i class="bi bi-arrow-right-short"></i> <span class="nav-page-text"><span class="chapter-number">1</span>&nbsp; <span class="chapter-title">The 2021 UK Census</span></span> <i class="bi bi-arrow-right-short"></i>
</a> </a>
</div> </div>
@ -485,4 +648,5 @@ window.document.addEventListener("DOMContentLoaded", function (event) {
</body></html> </body></html>

View file

@ -1,8 +1,14 @@
/*!
* Bootstrap Icons v1.11.1 (https://icons.getbootstrap.com/)
* Copyright 2019-2023 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE)
*/
@font-face { @font-face {
font-display: block; font-display: block;
font-family: "bootstrap-icons"; font-family: "bootstrap-icons";
src: src:
url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff"); url("./bootstrap-icons.woff?2820a3852bdb9a5832199cc61cec4e65") format("woff");
} }
.bi::before, .bi::before,
@ -441,7 +447,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-cloud-fog2::before { content: "\f2a2"; } .bi-cloud-fog2::before { content: "\f2a2"; }
.bi-cloud-hail-fill::before { content: "\f2a3"; } .bi-cloud-hail-fill::before { content: "\f2a3"; }
.bi-cloud-hail::before { content: "\f2a4"; } .bi-cloud-hail::before { content: "\f2a4"; }
.bi-cloud-haze-1::before { content: "\f2a5"; }
.bi-cloud-haze-fill::before { content: "\f2a6"; } .bi-cloud-haze-fill::before { content: "\f2a6"; }
.bi-cloud-haze::before { content: "\f2a7"; } .bi-cloud-haze::before { content: "\f2a7"; }
.bi-cloud-haze2-fill::before { content: "\f2a8"; } .bi-cloud-haze2-fill::before { content: "\f2a8"; }
@ -1437,21 +1442,16 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-dpad::before { content: "\f687"; } .bi-dpad::before { content: "\f687"; }
.bi-ear-fill::before { content: "\f688"; } .bi-ear-fill::before { content: "\f688"; }
.bi-ear::before { content: "\f689"; } .bi-ear::before { content: "\f689"; }
.bi-envelope-check-1::before { content: "\f68a"; }
.bi-envelope-check-fill::before { content: "\f68b"; } .bi-envelope-check-fill::before { content: "\f68b"; }
.bi-envelope-check::before { content: "\f68c"; } .bi-envelope-check::before { content: "\f68c"; }
.bi-envelope-dash-1::before { content: "\f68d"; }
.bi-envelope-dash-fill::before { content: "\f68e"; } .bi-envelope-dash-fill::before { content: "\f68e"; }
.bi-envelope-dash::before { content: "\f68f"; } .bi-envelope-dash::before { content: "\f68f"; }
.bi-envelope-exclamation-1::before { content: "\f690"; }
.bi-envelope-exclamation-fill::before { content: "\f691"; } .bi-envelope-exclamation-fill::before { content: "\f691"; }
.bi-envelope-exclamation::before { content: "\f692"; } .bi-envelope-exclamation::before { content: "\f692"; }
.bi-envelope-plus-fill::before { content: "\f693"; } .bi-envelope-plus-fill::before { content: "\f693"; }
.bi-envelope-plus::before { content: "\f694"; } .bi-envelope-plus::before { content: "\f694"; }
.bi-envelope-slash-1::before { content: "\f695"; }
.bi-envelope-slash-fill::before { content: "\f696"; } .bi-envelope-slash-fill::before { content: "\f696"; }
.bi-envelope-slash::before { content: "\f697"; } .bi-envelope-slash::before { content: "\f697"; }
.bi-envelope-x-1::before { content: "\f698"; }
.bi-envelope-x-fill::before { content: "\f699"; } .bi-envelope-x-fill::before { content: "\f699"; }
.bi-envelope-x::before { content: "\f69a"; } .bi-envelope-x::before { content: "\f69a"; }
.bi-explicit-fill::before { content: "\f69b"; } .bi-explicit-fill::before { content: "\f69b"; }
@ -1461,8 +1461,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-list-columns-reverse::before { content: "\f69f"; } .bi-list-columns-reverse::before { content: "\f69f"; }
.bi-list-columns::before { content: "\f6a0"; } .bi-list-columns::before { content: "\f6a0"; }
.bi-meta::before { content: "\f6a1"; } .bi-meta::before { content: "\f6a1"; }
.bi-mortorboard-fill::before { content: "\f6a2"; }
.bi-mortorboard::before { content: "\f6a3"; }
.bi-nintendo-switch::before { content: "\f6a4"; } .bi-nintendo-switch::before { content: "\f6a4"; }
.bi-pc-display-horizontal::before { content: "\f6a5"; } .bi-pc-display-horizontal::before { content: "\f6a5"; }
.bi-pc-display::before { content: "\f6a6"; } .bi-pc-display::before { content: "\f6a6"; }
@ -1481,7 +1479,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-send-check::before { content: "\f6b3"; } .bi-send-check::before { content: "\f6b3"; }
.bi-send-dash-fill::before { content: "\f6b4"; } .bi-send-dash-fill::before { content: "\f6b4"; }
.bi-send-dash::before { content: "\f6b5"; } .bi-send-dash::before { content: "\f6b5"; }
.bi-send-exclamation-1::before { content: "\f6b6"; }
.bi-send-exclamation-fill::before { content: "\f6b7"; } .bi-send-exclamation-fill::before { content: "\f6b7"; }
.bi-send-exclamation::before { content: "\f6b8"; } .bi-send-exclamation::before { content: "\f6b8"; }
.bi-send-fill::before { content: "\f6b9"; } .bi-send-fill::before { content: "\f6b9"; }
@ -1493,7 +1490,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-send-x::before { content: "\f6bf"; } .bi-send-x::before { content: "\f6bf"; }
.bi-send::before { content: "\f6c0"; } .bi-send::before { content: "\f6c0"; }
.bi-steam::before { content: "\f6c1"; } .bi-steam::before { content: "\f6c1"; }
.bi-terminal-dash-1::before { content: "\f6c2"; }
.bi-terminal-dash::before { content: "\f6c3"; } .bi-terminal-dash::before { content: "\f6c3"; }
.bi-terminal-plus::before { content: "\f6c4"; } .bi-terminal-plus::before { content: "\f6c4"; }
.bi-terminal-split::before { content: "\f6c5"; } .bi-terminal-split::before { content: "\f6c5"; }
@ -1523,7 +1519,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-usb-symbol::before { content: "\f6dd"; } .bi-usb-symbol::before { content: "\f6dd"; }
.bi-usb::before { content: "\f6de"; } .bi-usb::before { content: "\f6de"; }
.bi-boombox-fill::before { content: "\f6df"; } .bi-boombox-fill::before { content: "\f6df"; }
.bi-displayport-1::before { content: "\f6e0"; }
.bi-displayport::before { content: "\f6e1"; } .bi-displayport::before { content: "\f6e1"; }
.bi-gpu-card::before { content: "\f6e2"; } .bi-gpu-card::before { content: "\f6e2"; }
.bi-memory::before { content: "\f6e3"; } .bi-memory::before { content: "\f6e3"; }
@ -1536,8 +1531,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-pci-card::before { content: "\f6ea"; } .bi-pci-card::before { content: "\f6ea"; }
.bi-router-fill::before { content: "\f6eb"; } .bi-router-fill::before { content: "\f6eb"; }
.bi-router::before { content: "\f6ec"; } .bi-router::before { content: "\f6ec"; }
.bi-ssd-fill::before { content: "\f6ed"; }
.bi-ssd::before { content: "\f6ee"; }
.bi-thunderbolt-fill::before { content: "\f6ef"; } .bi-thunderbolt-fill::before { content: "\f6ef"; }
.bi-thunderbolt::before { content: "\f6f0"; } .bi-thunderbolt::before { content: "\f6f0"; }
.bi-usb-drive-fill::before { content: "\f6f1"; } .bi-usb-drive-fill::before { content: "\f6f1"; }
@ -1644,7 +1637,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-filetype-pdf::before { content: "\f756"; } .bi-filetype-pdf::before { content: "\f756"; }
.bi-filetype-php::before { content: "\f757"; } .bi-filetype-php::before { content: "\f757"; }
.bi-filetype-png::before { content: "\f758"; } .bi-filetype-png::before { content: "\f758"; }
.bi-filetype-ppt-1::before { content: "\f759"; }
.bi-filetype-ppt::before { content: "\f75a"; } .bi-filetype-ppt::before { content: "\f75a"; }
.bi-filetype-psd::before { content: "\f75b"; } .bi-filetype-psd::before { content: "\f75b"; }
.bi-filetype-py::before { content: "\f75c"; } .bi-filetype-py::before { content: "\f75c"; }
@ -1660,7 +1652,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-filetype-txt::before { content: "\f766"; } .bi-filetype-txt::before { content: "\f766"; }
.bi-filetype-wav::before { content: "\f767"; } .bi-filetype-wav::before { content: "\f767"; }
.bi-filetype-woff::before { content: "\f768"; } .bi-filetype-woff::before { content: "\f768"; }
.bi-filetype-xls-1::before { content: "\f769"; }
.bi-filetype-xls::before { content: "\f76a"; } .bi-filetype-xls::before { content: "\f76a"; }
.bi-filetype-xml::before { content: "\f76b"; } .bi-filetype-xml::before { content: "\f76b"; }
.bi-filetype-yml::before { content: "\f76c"; } .bi-filetype-yml::before { content: "\f76c"; }
@ -1703,56 +1694,38 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-filetype-json::before { content: "\f791"; } .bi-filetype-json::before { content: "\f791"; }
.bi-filetype-pptx::before { content: "\f792"; } .bi-filetype-pptx::before { content: "\f792"; }
.bi-filetype-xlsx::before { content: "\f793"; } .bi-filetype-xlsx::before { content: "\f793"; }
.bi-1-circle-1::before { content: "\f794"; }
.bi-1-circle-fill-1::before { content: "\f795"; }
.bi-1-circle-fill::before { content: "\f796"; } .bi-1-circle-fill::before { content: "\f796"; }
.bi-1-circle::before { content: "\f797"; } .bi-1-circle::before { content: "\f797"; }
.bi-1-square-fill::before { content: "\f798"; } .bi-1-square-fill::before { content: "\f798"; }
.bi-1-square::before { content: "\f799"; } .bi-1-square::before { content: "\f799"; }
.bi-2-circle-1::before { content: "\f79a"; }
.bi-2-circle-fill-1::before { content: "\f79b"; }
.bi-2-circle-fill::before { content: "\f79c"; } .bi-2-circle-fill::before { content: "\f79c"; }
.bi-2-circle::before { content: "\f79d"; } .bi-2-circle::before { content: "\f79d"; }
.bi-2-square-fill::before { content: "\f79e"; } .bi-2-square-fill::before { content: "\f79e"; }
.bi-2-square::before { content: "\f79f"; } .bi-2-square::before { content: "\f79f"; }
.bi-3-circle-1::before { content: "\f7a0"; }
.bi-3-circle-fill-1::before { content: "\f7a1"; }
.bi-3-circle-fill::before { content: "\f7a2"; } .bi-3-circle-fill::before { content: "\f7a2"; }
.bi-3-circle::before { content: "\f7a3"; } .bi-3-circle::before { content: "\f7a3"; }
.bi-3-square-fill::before { content: "\f7a4"; } .bi-3-square-fill::before { content: "\f7a4"; }
.bi-3-square::before { content: "\f7a5"; } .bi-3-square::before { content: "\f7a5"; }
.bi-4-circle-1::before { content: "\f7a6"; }
.bi-4-circle-fill-1::before { content: "\f7a7"; }
.bi-4-circle-fill::before { content: "\f7a8"; } .bi-4-circle-fill::before { content: "\f7a8"; }
.bi-4-circle::before { content: "\f7a9"; } .bi-4-circle::before { content: "\f7a9"; }
.bi-4-square-fill::before { content: "\f7aa"; } .bi-4-square-fill::before { content: "\f7aa"; }
.bi-4-square::before { content: "\f7ab"; } .bi-4-square::before { content: "\f7ab"; }
.bi-5-circle-1::before { content: "\f7ac"; }
.bi-5-circle-fill-1::before { content: "\f7ad"; }
.bi-5-circle-fill::before { content: "\f7ae"; } .bi-5-circle-fill::before { content: "\f7ae"; }
.bi-5-circle::before { content: "\f7af"; } .bi-5-circle::before { content: "\f7af"; }
.bi-5-square-fill::before { content: "\f7b0"; } .bi-5-square-fill::before { content: "\f7b0"; }
.bi-5-square::before { content: "\f7b1"; } .bi-5-square::before { content: "\f7b1"; }
.bi-6-circle-1::before { content: "\f7b2"; }
.bi-6-circle-fill-1::before { content: "\f7b3"; }
.bi-6-circle-fill::before { content: "\f7b4"; } .bi-6-circle-fill::before { content: "\f7b4"; }
.bi-6-circle::before { content: "\f7b5"; } .bi-6-circle::before { content: "\f7b5"; }
.bi-6-square-fill::before { content: "\f7b6"; } .bi-6-square-fill::before { content: "\f7b6"; }
.bi-6-square::before { content: "\f7b7"; } .bi-6-square::before { content: "\f7b7"; }
.bi-7-circle-1::before { content: "\f7b8"; }
.bi-7-circle-fill-1::before { content: "\f7b9"; }
.bi-7-circle-fill::before { content: "\f7ba"; } .bi-7-circle-fill::before { content: "\f7ba"; }
.bi-7-circle::before { content: "\f7bb"; } .bi-7-circle::before { content: "\f7bb"; }
.bi-7-square-fill::before { content: "\f7bc"; } .bi-7-square-fill::before { content: "\f7bc"; }
.bi-7-square::before { content: "\f7bd"; } .bi-7-square::before { content: "\f7bd"; }
.bi-8-circle-1::before { content: "\f7be"; }
.bi-8-circle-fill-1::before { content: "\f7bf"; }
.bi-8-circle-fill::before { content: "\f7c0"; } .bi-8-circle-fill::before { content: "\f7c0"; }
.bi-8-circle::before { content: "\f7c1"; } .bi-8-circle::before { content: "\f7c1"; }
.bi-8-square-fill::before { content: "\f7c2"; } .bi-8-square-fill::before { content: "\f7c2"; }
.bi-8-square::before { content: "\f7c3"; } .bi-8-square::before { content: "\f7c3"; }
.bi-9-circle-1::before { content: "\f7c4"; }
.bi-9-circle-fill-1::before { content: "\f7c5"; }
.bi-9-circle-fill::before { content: "\f7c6"; } .bi-9-circle-fill::before { content: "\f7c6"; }
.bi-9-circle::before { content: "\f7c7"; } .bi-9-circle::before { content: "\f7c7"; }
.bi-9-square-fill::before { content: "\f7c8"; } .bi-9-square-fill::before { content: "\f7c8"; }
@ -1771,8 +1744,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-browser-edge::before { content: "\f7d5"; } .bi-browser-edge::before { content: "\f7d5"; }
.bi-browser-firefox::before { content: "\f7d6"; } .bi-browser-firefox::before { content: "\f7d6"; }
.bi-browser-safari::before { content: "\f7d7"; } .bi-browser-safari::before { content: "\f7d7"; }
.bi-c-circle-1::before { content: "\f7d8"; }
.bi-c-circle-fill-1::before { content: "\f7d9"; }
.bi-c-circle-fill::before { content: "\f7da"; } .bi-c-circle-fill::before { content: "\f7da"; }
.bi-c-circle::before { content: "\f7db"; } .bi-c-circle::before { content: "\f7db"; }
.bi-c-square-fill::before { content: "\f7dc"; } .bi-c-square-fill::before { content: "\f7dc"; }
@ -1783,8 +1754,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-car-front::before { content: "\f7e1"; } .bi-car-front::before { content: "\f7e1"; }
.bi-cassette-fill::before { content: "\f7e2"; } .bi-cassette-fill::before { content: "\f7e2"; }
.bi-cassette::before { content: "\f7e3"; } .bi-cassette::before { content: "\f7e3"; }
.bi-cc-circle-1::before { content: "\f7e4"; }
.bi-cc-circle-fill-1::before { content: "\f7e5"; }
.bi-cc-circle-fill::before { content: "\f7e6"; } .bi-cc-circle-fill::before { content: "\f7e6"; }
.bi-cc-circle::before { content: "\f7e7"; } .bi-cc-circle::before { content: "\f7e7"; }
.bi-cc-square-fill::before { content: "\f7e8"; } .bi-cc-square-fill::before { content: "\f7e8"; }
@ -1803,8 +1772,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-filetype-sql::before { content: "\f7f5"; } .bi-filetype-sql::before { content: "\f7f5"; }
.bi-fire::before { content: "\f7f6"; } .bi-fire::before { content: "\f7f6"; }
.bi-google-play::before { content: "\f7f7"; } .bi-google-play::before { content: "\f7f7"; }
.bi-h-circle-1::before { content: "\f7f8"; }
.bi-h-circle-fill-1::before { content: "\f7f9"; }
.bi-h-circle-fill::before { content: "\f7fa"; } .bi-h-circle-fill::before { content: "\f7fa"; }
.bi-h-circle::before { content: "\f7fb"; } .bi-h-circle::before { content: "\f7fb"; }
.bi-h-square-fill::before { content: "\f7fc"; } .bi-h-square-fill::before { content: "\f7fc"; }
@ -1813,8 +1780,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-lungs-fill::before { content: "\f7ff"; } .bi-lungs-fill::before { content: "\f7ff"; }
.bi-lungs::before { content: "\f800"; } .bi-lungs::before { content: "\f800"; }
.bi-microsoft-teams::before { content: "\f801"; } .bi-microsoft-teams::before { content: "\f801"; }
.bi-p-circle-1::before { content: "\f802"; }
.bi-p-circle-fill-1::before { content: "\f803"; }
.bi-p-circle-fill::before { content: "\f804"; } .bi-p-circle-fill::before { content: "\f804"; }
.bi-p-circle::before { content: "\f805"; } .bi-p-circle::before { content: "\f805"; }
.bi-p-square-fill::before { content: "\f806"; } .bi-p-square-fill::before { content: "\f806"; }
@ -1823,8 +1788,6 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-pass::before { content: "\f809"; } .bi-pass::before { content: "\f809"; }
.bi-prescription::before { content: "\f80a"; } .bi-prescription::before { content: "\f80a"; }
.bi-prescription2::before { content: "\f80b"; } .bi-prescription2::before { content: "\f80b"; }
.bi-r-circle-1::before { content: "\f80c"; }
.bi-r-circle-fill-1::before { content: "\f80d"; }
.bi-r-circle-fill::before { content: "\f80e"; } .bi-r-circle-fill::before { content: "\f80e"; }
.bi-r-circle::before { content: "\f80f"; } .bi-r-circle::before { content: "\f80f"; }
.bi-r-square-fill::before { content: "\f810"; } .bi-r-square-fill::before { content: "\f810"; }
@ -2016,3 +1979,100 @@ url("./bootstrap-icons.woff?2ab2cbbe07fcebb53bdaa7313bb290f2") format("woff");
.bi-sina-weibo::before { content: "\f8ca"; } .bi-sina-weibo::before { content: "\f8ca"; }
.bi-tencent-qq::before { content: "\f8cb"; } .bi-tencent-qq::before { content: "\f8cb"; }
.bi-wikipedia::before { content: "\f8cc"; } .bi-wikipedia::before { content: "\f8cc"; }
.bi-alphabet-uppercase::before { content: "\f2a5"; }
.bi-alphabet::before { content: "\f68a"; }
.bi-amazon::before { content: "\f68d"; }
.bi-arrows-collapse-vertical::before { content: "\f690"; }
.bi-arrows-expand-vertical::before { content: "\f695"; }
.bi-arrows-vertical::before { content: "\f698"; }
.bi-arrows::before { content: "\f6a2"; }
.bi-ban-fill::before { content: "\f6a3"; }
.bi-ban::before { content: "\f6b6"; }
.bi-bing::before { content: "\f6c2"; }
.bi-cake::before { content: "\f6e0"; }
.bi-cake2::before { content: "\f6ed"; }
.bi-cookie::before { content: "\f6ee"; }
.bi-copy::before { content: "\f759"; }
.bi-crosshair::before { content: "\f769"; }
.bi-crosshair2::before { content: "\f794"; }
.bi-emoji-astonished-fill::before { content: "\f795"; }
.bi-emoji-astonished::before { content: "\f79a"; }
.bi-emoji-grimace-fill::before { content: "\f79b"; }
.bi-emoji-grimace::before { content: "\f7a0"; }
.bi-emoji-grin-fill::before { content: "\f7a1"; }
.bi-emoji-grin::before { content: "\f7a6"; }
.bi-emoji-surprise-fill::before { content: "\f7a7"; }
.bi-emoji-surprise::before { content: "\f7ac"; }
.bi-emoji-tear-fill::before { content: "\f7ad"; }
.bi-emoji-tear::before { content: "\f7b2"; }
.bi-envelope-arrow-down-fill::before { content: "\f7b3"; }
.bi-envelope-arrow-down::before { content: "\f7b8"; }
.bi-envelope-arrow-up-fill::before { content: "\f7b9"; }
.bi-envelope-arrow-up::before { content: "\f7be"; }
.bi-feather::before { content: "\f7bf"; }
.bi-feather2::before { content: "\f7c4"; }
.bi-floppy-fill::before { content: "\f7c5"; }
.bi-floppy::before { content: "\f7d8"; }
.bi-floppy2-fill::before { content: "\f7d9"; }
.bi-floppy2::before { content: "\f7e4"; }
.bi-gitlab::before { content: "\f7e5"; }
.bi-highlighter::before { content: "\f7f8"; }
.bi-marker-tip::before { content: "\f802"; }
.bi-nvme-fill::before { content: "\f803"; }
.bi-nvme::before { content: "\f80c"; }
.bi-opencollective::before { content: "\f80d"; }
.bi-pci-card-network::before { content: "\f8cd"; }
.bi-pci-card-sound::before { content: "\f8ce"; }
.bi-radar::before { content: "\f8cf"; }
.bi-send-arrow-down-fill::before { content: "\f8d0"; }
.bi-send-arrow-down::before { content: "\f8d1"; }
.bi-send-arrow-up-fill::before { content: "\f8d2"; }
.bi-send-arrow-up::before { content: "\f8d3"; }
.bi-sim-slash-fill::before { content: "\f8d4"; }
.bi-sim-slash::before { content: "\f8d5"; }
.bi-sourceforge::before { content: "\f8d6"; }
.bi-substack::before { content: "\f8d7"; }
.bi-threads-fill::before { content: "\f8d8"; }
.bi-threads::before { content: "\f8d9"; }
.bi-transparency::before { content: "\f8da"; }
.bi-twitter-x::before { content: "\f8db"; }
.bi-type-h4::before { content: "\f8dc"; }
.bi-type-h5::before { content: "\f8dd"; }
.bi-type-h6::before { content: "\f8de"; }
.bi-backpack-fill::before { content: "\f8df"; }
.bi-backpack::before { content: "\f8e0"; }
.bi-backpack2-fill::before { content: "\f8e1"; }
.bi-backpack2::before { content: "\f8e2"; }
.bi-backpack3-fill::before { content: "\f8e3"; }
.bi-backpack3::before { content: "\f8e4"; }
.bi-backpack4-fill::before { content: "\f8e5"; }
.bi-backpack4::before { content: "\f8e6"; }
.bi-brilliance::before { content: "\f8e7"; }
.bi-cake-fill::before { content: "\f8e8"; }
.bi-cake2-fill::before { content: "\f8e9"; }
.bi-duffle-fill::before { content: "\f8ea"; }
.bi-duffle::before { content: "\f8eb"; }
.bi-exposure::before { content: "\f8ec"; }
.bi-gender-neuter::before { content: "\f8ed"; }
.bi-highlights::before { content: "\f8ee"; }
.bi-luggage-fill::before { content: "\f8ef"; }
.bi-luggage::before { content: "\f8f0"; }
.bi-mailbox-flag::before { content: "\f8f1"; }
.bi-mailbox2-flag::before { content: "\f8f2"; }
.bi-noise-reduction::before { content: "\f8f3"; }
.bi-passport-fill::before { content: "\f8f4"; }
.bi-passport::before { content: "\f8f5"; }
.bi-person-arms-up::before { content: "\f8f6"; }
.bi-person-raised-hand::before { content: "\f8f7"; }
.bi-person-standing-dress::before { content: "\f8f8"; }
.bi-person-standing::before { content: "\f8f9"; }
.bi-person-walking::before { content: "\f8fa"; }
.bi-person-wheelchair::before { content: "\f8fb"; }
.bi-shadows::before { content: "\f8fc"; }
.bi-suitcase-fill::before { content: "\f8fd"; }
.bi-suitcase-lg-fill::before { content: "\f8fe"; }
.bi-suitcase-lg::before { content: "\f8ff"; }
.bi-suitcase::before { content: "\f900"; }
.bi-suitcase2-fill::before { content: "\f901"; }
.bi-suitcase2::before { content: "\f902"; }
.bi-vignette::before { content: "\f903"; }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -9,7 +9,7 @@ const layoutMarginEls = () => {
// Find any conflicting margin elements and add margins to the // Find any conflicting margin elements and add margins to the
// top to prevent overlap // top to prevent overlap
const marginChildren = window.document.querySelectorAll( const marginChildren = window.document.querySelectorAll(
".column-margin.column-container > * " ".column-margin.column-container > *, .margin-caption, .aside"
); );
let lastBottom = 0; let lastBottom = 0;
@ -18,25 +18,14 @@ const layoutMarginEls = () => {
// clear the top margin so we recompute it // clear the top margin so we recompute it
marginChild.style.marginTop = null; marginChild.style.marginTop = null;
const top = marginChild.getBoundingClientRect().top + window.scrollY; const top = marginChild.getBoundingClientRect().top + window.scrollY;
console.log({
childtop: marginChild.getBoundingClientRect().top,
scroll: window.scrollY,
top,
lastBottom,
});
if (top < lastBottom) { if (top < lastBottom) {
const margin = lastBottom - top; const marginChildStyle = window.getComputedStyle(marginChild);
const marginBottom = parseFloat(marginChildStyle["marginBottom"]);
const margin = lastBottom - top + marginBottom;
marginChild.style.marginTop = `${margin}px`; marginChild.style.marginTop = `${margin}px`;
} }
const styles = window.getComputedStyle(marginChild); const styles = window.getComputedStyle(marginChild);
const marginTop = parseFloat(styles["marginTop"]); const marginTop = parseFloat(styles["marginTop"]);
console.log({
top,
height: marginChild.getBoundingClientRect().height,
marginTop,
total: top + marginChild.getBoundingClientRect().height + marginTop,
});
lastBottom = top + marginChild.getBoundingClientRect().height + marginTop; lastBottom = top + marginChild.getBoundingClientRect().height + marginTop;
} }
} }
@ -46,7 +35,15 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
// Recompute the position of margin elements anytime the body size changes // Recompute the position of margin elements anytime the body size changes
if (window.ResizeObserver) { if (window.ResizeObserver) {
const resizeObserver = new window.ResizeObserver( const resizeObserver = new window.ResizeObserver(
throttle(layoutMarginEls, 50) throttle(() => {
layoutMarginEls();
if (
window.document.body.getBoundingClientRect().width < 990 &&
isReaderMode()
) {
quartoToggleReader();
}
}, 50)
); );
resizeObserver.observe(window.document.body); resizeObserver.observe(window.document.body);
} }

View file

@ -85,6 +85,17 @@ window.document.addEventListener("DOMContentLoaded", function () {
} }
} }
function dashboardOffset() {
const dashboardNavEl = window.document.getElementById(
"quarto-dashboard-header"
);
if (dashboardNavEl !== null) {
return dashboardNavEl.clientHeight;
} else {
return 0;
}
}
function updateDocumentOffsetWithoutAnimation() { function updateDocumentOffsetWithoutAnimation() {
updateDocumentOffset(false); updateDocumentOffset(false);
} }
@ -92,7 +103,7 @@ window.document.addEventListener("DOMContentLoaded", function () {
function updateDocumentOffset(animated) { function updateDocumentOffset(animated) {
// set body offset // set body offset
const topOffset = headerOffset(); const topOffset = headerOffset();
const bodyOffset = topOffset + footerOffset(); const bodyOffset = topOffset + footerOffset() + dashboardOffset();
const bodyEl = window.document.body; const bodyEl = window.document.body;
bodyEl.setAttribute("data-bs-offset", topOffset); bodyEl.setAttribute("data-bs-offset", topOffset);
bodyEl.style.paddingTop = topOffset + "px"; bodyEl.style.paddingTop = topOffset + "px";
@ -205,9 +216,9 @@ window.document.addEventListener("DOMContentLoaded", function () {
// Observe size changed for the header // Observe size changed for the header
const headerEl = window.document.querySelector("header.fixed-top"); const headerEl = window.document.querySelector("header.fixed-top");
if (headerEl && window.ResizeObserver) { if (headerEl && window.ResizeObserver) {
const observer = new window.ResizeObserver( const observer = new window.ResizeObserver(() => {
updateDocumentOffsetWithoutAnimation setTimeout(updateDocumentOffsetWithoutAnimation, 0);
); });
observer.observe(headerEl, { observer.observe(headerEl, {
attributes: true, attributes: true,
childList: true, childList: true,
@ -233,7 +244,7 @@ window.document.addEventListener("DOMContentLoaded", function () {
// Fixup any sharing links that require urls // Fixup any sharing links that require urls
// Append url to any sharing urls // Append url to any sharing urls
const sharingLinks = window.document.querySelectorAll( const sharingLinks = window.document.querySelectorAll(
"a.sidebar-tools-main-item" "a.sidebar-tools-main-item, a.quarto-navigation-tool, a.quarto-navbar-tools, a.quarto-navbar-tools-item"
); );
for (let i = 0; i < sharingLinks.length; i++) { for (let i = 0; i < sharingLinks.length; i++) {
const sharingLink = sharingLinks[i]; const sharingLink = sharingLinks[i];

File diff suppressed because one or more lines are too long

View file

@ -43,7 +43,7 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
const mainEl = window.document.querySelector("main"); const mainEl = window.document.querySelector("main");
// highlight matches on the page // highlight matches on the page
if (query !== null && mainEl) { if (query && mainEl) {
// perform any highlighting // perform any highlighting
highlight(escapeRegExp(query), mainEl); highlight(escapeRegExp(query), mainEl);
@ -57,7 +57,7 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
// (e.g. if the user edits the query or clears it) // (e.g. if the user edits the query or clears it)
let highlighting = true; let highlighting = true;
const resetHighlighting = (searchTerm) => { const resetHighlighting = (searchTerm) => {
if (mainEl && highlighting && query !== null && searchTerm !== query) { if (mainEl && highlighting && query && searchTerm !== query) {
clearHighlight(query, mainEl); clearHighlight(query, mainEl);
highlighting = false; highlighting = false;
} }
@ -98,6 +98,7 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
classNames: { classNames: {
form: "d-flex", form: "d-flex",
}, },
placeholder: language["search-text-placeholder"],
translations: { translations: {
clearButtonTitle: language["search-clear-button-title"], clearButtonTitle: language["search-clear-button-title"],
detachedCancelButtonText: language["search-detached-cancel-button-title"], detachedCancelButtonText: language["search-detached-cancel-button-title"],
@ -110,6 +111,8 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
return item.href; return item.href;
}, },
onStateChange({ state }) { onStateChange({ state }) {
// If this is a file URL, note that
// Perhaps reset highlighting // Perhaps reset highlighting
resetHighlighting(state.query); resetHighlighting(state.query);
@ -359,7 +362,8 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
state, state,
setActiveItemId, setActiveItemId,
setContext, setContext,
refresh refresh,
quartoSearchOptions
); );
}, },
}, },
@ -374,6 +378,32 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
focusSearchInput(); focusSearchInput();
}; };
document.addEventListener("keyup", (event) => {
const { key } = event;
const kbds = quartoSearchOptions["keyboard-shortcut"];
const focusedEl = document.activeElement;
const isFormElFocused = [
"input",
"select",
"textarea",
"button",
"option",
].find((tag) => {
return focusedEl.tagName.toLowerCase() === tag;
});
if (
kbds &&
kbds.includes(key) &&
!isFormElFocused &&
!document.activeElement.isContentEditable
) {
event.preventDefault();
window.quartoOpenSearch();
}
});
// Remove the labeleledby attribute since it is pointing // Remove the labeleledby attribute since it is pointing
// to a non-existent label // to a non-existent label
if (quartoSearchOptions.type === "overlay") { if (quartoSearchOptions.type === "overlay") {
@ -385,11 +415,30 @@ window.document.addEventListener("DOMContentLoaded", function (_event) {
} }
} }
function throttle(func, wait) {
let waiting = false;
return function () {
if (!waiting) {
func.apply(this, arguments);
waiting = true;
setTimeout(function () {
waiting = false;
}, wait);
}
};
}
// If the main document scrolls dismiss the search results // If the main document scrolls dismiss the search results
// (otherwise, since they're floating in the document they can scroll with the document) // (otherwise, since they're floating in the document they can scroll with the document)
window.document.body.onscroll = () => { window.document.body.onscroll = throttle(() => {
// Only do this if we're not detached
// Bug #7117
// This will happen when the keyboard is shown on ios (resulting in a scroll)
// which then closed the search UI
if (!window.matchMedia(detachedMediaQuery).matches) {
setIsOpen(false); setIsOpen(false);
}; }
}, 50);
if (showSearchResults) { if (showSearchResults) {
setIsOpen(true); setIsOpen(true);
@ -429,15 +478,27 @@ function configurePlugins(quartoSearchOptions) {
const algoliaInsightsPlugin = createAlgoliaInsightsPlugin({ const algoliaInsightsPlugin = createAlgoliaInsightsPlugin({
insightsClient: window.aa, insightsClient: window.aa,
onItemsChange({ insights, insightsEvents }) { onItemsChange({ insights, insightsEvents }) {
const events = insightsEvents.map((event) => { const events = insightsEvents.flatMap((event) => {
const maxEvents = event.objectIDs.slice(0, 20); // This API limits the number of items per event to 20
const chunkSize = 20;
const itemChunks = [];
const eventItems = event.items;
for (let i = 0; i < eventItems.length; i += chunkSize) {
itemChunks.push(eventItems.slice(i, i + chunkSize));
}
// Split the items into multiple events that can be sent
const events = itemChunks.map((items) => {
return { return {
...event, ...event,
objectIDs: maxEvents, items,
}; };
}); });
return events;
});
insights.viewedObjectIDs(...events); for (const event of events) {
insights.viewedObjectIDs(event);
}
}, },
}); });
return algoliaInsightsPlugin; return algoliaInsightsPlugin;
@ -613,9 +674,17 @@ function showCopyLink(query, options) {
/* Search Index Handling */ /* Search Index Handling */
// create the index // create the index
var fuseIndex = undefined; var fuseIndex = undefined;
var shownWarning = false;
async function readSearchData() { async function readSearchData() {
// Initialize the search index on demand // Initialize the search index on demand
if (fuseIndex === undefined) { if (fuseIndex === undefined) {
if (window.location.protocol === "file:" && !shownWarning) {
window.alert(
"Search requires JavaScript features disabled when running in file://... URLs. In order to use search, please run this document in a web server."
);
shownWarning = true;
return;
}
// create fuse index // create fuse index
const options = { const options = {
keys: [ keys: [
@ -646,6 +715,7 @@ async function readSearchData() {
); );
} }
} }
return fuseIndex; return fuseIndex;
} }
@ -674,7 +744,8 @@ function renderItem(
state, state,
setActiveItemId, setActiveItemId,
setContext, setContext,
refresh refresh,
quartoSearchOptions
) { ) {
switch (item.type) { switch (item.type) {
case kItemTypeDoc: case kItemTypeDoc:
@ -684,7 +755,9 @@ function renderItem(
item.title, item.title,
item.section, item.section,
item.text, item.text,
item.href item.href,
item.crumbs,
quartoSearchOptions
); );
case kItemTypeMore: case kItemTypeMore:
return createMoreCard( return createMoreCard(
@ -709,15 +782,46 @@ function renderItem(
} }
} }
function createDocumentCard(createElement, icon, title, section, text, href) { function createDocumentCard(
createElement,
icon,
title,
section,
text,
href,
crumbs,
quartoSearchOptions
) {
const iconEl = createElement("i", { const iconEl = createElement("i", {
class: `bi bi-${icon} search-result-icon`, class: `bi bi-${icon} search-result-icon`,
}); });
const titleEl = createElement("p", { class: "search-result-title" }, title); const titleEl = createElement("p", { class: "search-result-title" }, title);
const titleContents = [iconEl, titleEl];
const showParent = quartoSearchOptions["show-item-context"];
if (crumbs && showParent) {
let crumbsOut = undefined;
const crumbClz = ["search-result-crumbs"];
if (showParent === "root") {
crumbsOut = crumbs.length > 1 ? crumbs[0] : undefined;
} else if (showParent === "parent") {
crumbsOut = crumbs.length > 1 ? crumbs[crumbs.length - 2] : undefined;
} else {
crumbsOut = crumbs.length > 1 ? crumbs.join(" > ") : undefined;
crumbClz.push("search-result-crumbs-wrap");
}
const crumbEl = createElement(
"p",
{ class: crumbClz.join(" ") },
crumbsOut
);
titleContents.push(crumbEl);
}
const titleContainerEl = createElement( const titleContainerEl = createElement(
"div", "div",
{ class: "search-result-title-container" }, { class: "search-result-title-container" },
[iconEl, titleEl] titleContents
); );
const textEls = []; const textEls = [];
@ -1099,7 +1203,8 @@ function algoliaSearch(query, limit, algoliaOptions) {
const remappedHits = response.hits.map((hit) => { const remappedHits = response.hits.map((hit) => {
return hit.map((item) => { return hit.map((item) => {
const newItem = { ...item }; const newItem = { ...item };
["href", "section", "title", "text"].forEach((keyName) => { ["href", "section", "title", "text", "crumbs"].forEach(
(keyName) => {
const mappedName = indexFields[keyName]; const mappedName = indexFields[keyName];
if ( if (
mappedName && mappedName &&
@ -1109,7 +1214,8 @@ function algoliaSearch(query, limit, algoliaOptions) {
newItem[keyName] = item[mappedName]; newItem[keyName] = item[mappedName];
delete newItem[mappedName]; delete newItem[mappedName];
} }
}); }
);
newItem.text = highlightMatch(query, newItem.text); newItem.text = highlightMatch(query, newItem.text);
return newItem; return newItem;
}); });
@ -1135,6 +1241,7 @@ function fuseSearch(query, fuse, fuseOptions) {
section: result.item.section, section: result.item.section,
href: addParam(result.item.href, kQueryArg, query), href: addParam(result.item.href, kQueryArg, query),
text: highlightMatch(query, result.item.text), text: highlightMatch(query, result.item.text),
crumbs: result.item.crumbs,
}; };
}); });
} }

View file

@ -208,17 +208,17 @@ One element of R data analysis that can get really interesting is working with m
For the UK, census data is made available for programmatic research like this via an organisation called NOMIS. Luckily for us, there is an R library you can use to access nomis directly which greatly simplifies the process of pulling data down from the platform. It's worth noting that if you're not in the UK, there are similar options for other countries. Nearly every R textbook I've ever seen works with USA census data, so you'll find plenty of documentation available on the tools you can use for US Census data. Similarly for the EU, Canada, Austrailia etc. For the UK, census data is made available for programmatic research like this via an organisation called NOMIS. Luckily for us, there is an R library you can use to access nomis directly which greatly simplifies the process of pulling data down from the platform. It's worth noting that if you're not in the UK, there are similar options for other countries. Nearly every R textbook I've ever seen works with USA census data, so you'll find plenty of documentation available on the tools you can use for US Census data. Similarly for the EU, Canada, Austrailia etc.
If you want to draw some data from the nomis platform yourself in R, have a look at the [companion cookbook repository](https://github.com/kidwellj/hacking_religion_cookbook/blob/main/nomis.R). If you want to draw some data from the nomis platform yourself in R, have a look at the nomis script in our [companion cookbook repository](https://github.com/kidwellj/hacking_religion_cookbook/blob/main/nomis.R). For now, we'll provide some data extracts for you to use.
::: :::
```{r} ```{r}
# Get table of Census 2011 religion data from nomis # Get table of Census 2011 religion data from nomis
z <- readRDS(file = (here("example_data", "z.rds"))) nomis_extract_census2011a <- readRDS(file = (here("example_data", "nomis_extract_census2011a.rds")))
# Filter down to simplified dataset with England / Wales and percentages without totals # Filter down to simplified dataset with England / Wales and percentages without totals
uk_census_2011_religion <- filter(z, GEOGRAPHY_NAME=="England and Wales" & RURAL_URBAN_NAME=="Total" & C_RELPUK11_NAME != "All categories: Religion") uk_census_2011_religion <- filter(nomis_extract_census2011a, GEOGRAPHY_NAME=="England and Wales" & RURAL_URBAN_NAME=="Total" & C_RELPUK11_NAME != "All categories: Religion")
# Drop unnecessary columns # Drop unnecessary columns
uk_census_2011_religion <- select(uk_census_2011_religion, C_RELPUK11_NAME, OBS_VALUE) uk_census_2011_religion <- select(uk_census_2011_religion, C_RELPUK11_NAME, OBS_VALUE)
# Plot results # Plot results
@ -226,30 +226,30 @@ plot1 <- ggplot(uk_census_2011_religion, aes(x = C_RELPUK11_NAME, y = OBS_VALUE)
# ggsave(filename = "plot.png", plot = plot1) # ggsave(filename = "plot.png", plot = plot1)
# grab daata from nomis for 2001 census religion / ethnicity # grab daata from nomis for 2001 census religion / ethnicity
z0 <- readRDS(file = (here("example_data", "z0.rds"))) nomis_extract_census2001 <- readRDS(file = (here("example_data", "nomis_extract_census2001.rds")))
# select relevant columns # select relevant columns
uk_census_2001_religion_ethnicity <- select(z0, GEOGRAPHY_NAME, C_RELPUK11_NAME, C_ETHHUK11_NAME, OBS_VALUE) uk_census_2001_religion_ethnicity <- select(nomis_extract_census2001, GEOGRAPHY_NAME, C_RELPUK11_NAME, C_ETHHUK11_NAME, OBS_VALUE)
# Filter down to simplified dataset with England / Wales and percentages without totals # Filter down to simplified dataset with England / Wales and percentages without totals
uk_census_2001_religion_ethnicity <- filter(uk_census_2001_religion_ethnicity, GEOGRAPHY_NAME=="England and Wales" & C_RELPUK11_NAME != "All categories: Religion") uk_census_2001_religion_ethnicity <- filter(uk_census_2001_religion_ethnicity, GEOGRAPHY_NAME=="England and Wales" & C_RELPUK11_NAME != "All categories: Religion")
# Simplify data to only include general totals and omit subcategories # Simplify data to only include general totals and omit subcategories
uk_census_2001_religion_ethnicity <- uk_census_2001_religion_ethnicity %>% filter(grepl('Total', C_ETHHUK11_NAME)) uk_census_2001_religion_ethnicity <- uk_census_2001_religion_ethnicity %>% filter(grepl('Total', C_ETHHUK11_NAME))
# grab data from nomis for 2011 census religion / ethnicity table # grab data from nomis for 2011 census religion / ethnicity table
z1 <- readRDS(file = (here("example_data", "z1.rds"))) nomis_extract_census2011b <- readRDS(file = (here("example_data", "nomis_extract_census2011b.rds")))
# select relevant columns # select relevant columns
uk_census_2011_religion_ethnicity <- select(z1, GEOGRAPHY_NAME, C_RELPUK11_NAME, C_ETHPUK11_NAME, OBS_VALUE) uk_census_2011_religion_ethnicity <- select(nomis_extract_census2011b, GEOGRAPHY_NAME, C_RELPUK11_NAME, C_ETHPUK11_NAME, OBS_VALUE)
# Filter down to simplified dataset with England / Wales and percentages without totals # Filter down to simplified dataset with England / Wales and percentages without totals
uk_census_2011_religion_ethnicity <- filter(uk_census_2011_religion_ethnicity, GEOGRAPHY_NAME=="England and Wales" & C_RELPUK11_NAME != "All categories: Religion" & C_ETHPUK11_NAME != "All categories: Ethnic group") uk_census_2011_religion_ethnicity <- filter(uk_census_2011_religion_ethnicity, GEOGRAPHY_NAME=="England and Wales" & C_RELPUK11_NAME != "All categories: Religion" & C_ETHPUK11_NAME != "All categories: Ethnic group")
# Simplify data to only include general totals and omit subcategories # Simplify data to only include general totals and omit subcategories
uk_census_2011_religion_ethnicity <- uk_census_2011_religion_ethnicity %>% filter(grepl('Total', C_ETHPUK11_NAME)) uk_census_2011_religion_ethnicity <- uk_census_2011_religion_ethnicity %>% filter(grepl('Total', C_ETHPUK11_NAME))
# grab data from nomis for 2021 census religion / ethnicity table # grab data from nomis for 2021 census religion / ethnicity table
z2 <- readRDS(file = (here("example_data", "z2.rds"))) nomis_extract_census2021 <- readRDS(file = (here("example_data", "nomis_extract_census2021.rds")))
# select relevant columns # select relevant columns
uk_census_2021_religion_ethnicity <- select(z2, GEOGRAPHY_NAME, C2021_RELIGION_10_NAME, C2021_ETH_8_NAME, OBS_VALUE) uk_census_2021_religion_ethnicity <- select(nomis_extract_census2021, GEOGRAPHY_NAME, C2021_RELIGION_10_NAME, C2021_ETH_8_NAME, OBS_VALUE)
# Filter down to simplified dataset with England / Wales and percentages without totals # Filter down to simplified dataset with England / Wales and percentages without totals
uk_census_2021_religion_ethnicity <- filter(uk_census_2021_religion_ethnicity, GEOGRAPHY_NAME=="England and Wales" & C2021_RELIGION_10_NAME != "Total" & C2021_ETH_8_NAME != "Total") uk_census_2021_religion_ethnicity <- filter(uk_census_2021_religion_ethnicity, GEOGRAPHY_NAME=="England and Wales" & C2021_RELIGION_10_NAME != "Total" & C2021_ETH_8_NAME != "Total")
# 2021 census includes white sub-groups so we need to omit those so we just have totals: # 2021 census includes white sub-groups so we need to omit those so we just have totals:

View file

@ -210,6 +210,8 @@ ggplot(religiosity_sums, aes(x = response, y = n, color=response)) + geom_col(co
Or, you might try the well-crafted typgraphy from `hbrthemes` in the `theme_ipsum_pub` theme: Or, you might try the well-crafted typgraphy from `hbrthemes` in the `theme_ipsum_pub` theme:
Note: this library will expect your system to have certain fonts installed and available for RStudio to use. You may want to run the following command to import your system fonts to R so they are available: `extrafont::font_import()`. This will take a bit of time to run. The package will also save fonts to a folder on your PC so you can install them if you don't already have them, you can run `import_public_sans()` to get the path for these files and install in R.
```{r} ```{r}
library(hrbrthemes) |> suppressPackageStartupMessages() library(hrbrthemes) |> suppressPackageStartupMessages()
ggplot(religiosity_sums, aes(x = response, y = n, color=response)) + geom_col(colour = "white", aes(fill = response)) + coord_flip() + theme(plot.margin = margin(rep(15, 4))) + labs(caption = caption) + ggplot(religiosity_sums, aes(x = response, y = n, color=response)) + geom_col(colour = "white", aes(fill = response)) + coord_flip() + theme(plot.margin = margin(rep(15, 4))) + labs(caption = caption) +