PageSourceSearch

https://dantonnoriega.gitlab.io/posts.html

html dantonnoriega.gitlab.io collected 2026-10-10 02:21:16 UTC 37,051 bytes, 942 lines download raw bytes

1<!DOCTYPE html>
2<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
3
4<meta charset="utf-8">
5<meta name="generator" content="quarto-1.9.38">
6
7<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
8
9
10<title>Posts – danton.codes</title>
11<style>
12/* Default styles provided by pandoc.
13** See https://pandoc.org/MANUAL.html#variables-for-html for config info.
14*/
15code{white-space: pre-wrap;}
16span.smallcaps{font-variant: small-caps;}
17div.columns{display: flex; gap: min(4vw, 1.5em);}
18div.column{flex: auto; overflow-x: auto;}
19div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
20ul.task-list{list-style: none;}
21ul.task-list li input[type="checkbox"] {
22  width: 0.8em;
23  margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */ 
24  vertical-align: middle;
25}
26</style>
27
28
29<script src="site_libs/quarto-nav/quarto-nav.js"></script>
vendor: 1 bytes, line 29
29
30<script src="site_libs/quarto-nav/headroom.min.js"></script>
vendor: 1 bytes, line 30
30
31<script src="site_libs/clipboard/clipboard.min.js"></script>
vendor: 1 bytes, line 31
31
32<script src="site_libs/quarto-search/autocomplete.umd.js"></script>
vendor: 1 bytes, line 32
32
33<script src="site_libs/quarto-search/fuse.min.js"></script>
vendor: 1 bytes, line 33
33
34<script src="site_libs/quarto-search/quarto-search.js"></script>
34
35<meta name="quarto:offset" content="./">
36<link href="./favicon.ico" rel="icon">
37<script src="site_libs/quarto-listing/list.min.js"></script>
vendor: 1 bytes, line 37
37
38<script src="site_libs/quarto-listing/quarto-listing.js"></script>
vendor: 1 bytes, line 38
38
39<script src="site_libs/quarto-html/quarto.js" type="module"></script>
vendor: 1 bytes, line 39
39
40<script src="site_libs/quarto-html/tabsets/tabsets.js" type="module"></script>
vendor: 1 bytes, line 40
40
41<script src="site_libs/quarto-html/popper.min.js"></script>
vendor: 1 bytes, line 41
41
42<script src="site_libs/quarto-html/tippy.umd.min.js"></script>
vendor: 1 bytes, line 42
42
43<script src="site_libs/quarto-html/anchor.min.js"></script>
43
44<link href="site_libs/quarto-html/tippy.css" rel="stylesheet">
45<link href="site_libs/quarto-html/quarto-syntax-highlighting-15634bcf2e68342d4ad2dfa704d543f6.css" rel="stylesheet" class="quarto-color-scheme" id="quarto-text-highlighting-styles">
46<link href="site_libs/quarto-html/quarto-syntax-highlighting-dark-0a6b7249c26820e032cb0bd839ff02b1.css" rel="stylesheet" class="quarto-color-scheme quarto-color-alternate" id="quarto-text-highlighting-styles">
47<link href="site_libs/quarto-html/quarto-syntax-highlighting-15634bcf2e68342d4ad2dfa704d543f6.css" rel="stylesheet" class="quarto-color-scheme-extra" id="quarto-text-highlighting-styles">
48<script src="site_libs/bootstrap/bootstrap.min.js"></script>
48
49<link href="site_libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
50<link href="site_libs/bootstrap/bootstrap-85545d89869a7d03b8d8b22d16f33e0b.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme" id="quarto-bootstrap" data-mode="light">
51<link href="site_libs/bootstrap/bootstrap-dark-f7fba41c1ef1477727021bb8767eabf3.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme quarto-color-alternate" id="quarto-bootstrap" data-mode="dark">
52<link href="site_libs/bootstrap/bootstrap-85545d89869a7d03b8d8b22d16f33e0b.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme-extra" id="quarto-bootstrap" data-mode="light">
53<script id="quarto-search-options" type="application/json">{
54  "location": "navbar",
55  "copy-button": false,
56  "collapse-after": 3,
57  "panel-placement": "end",
58  "type": "overlay",
59  "limit": 50,
60  "keyboard-shortcut": [
61    "f",
62    "/",
63    "s"
64  ],
65  "show-item-context": false,
66  "language": {
67    "search-no-results-text": "No results",
68    "search-matching-documents-text": "matching documents",
69    "search-copy-link-title": "Copy link to search",
70    "search-hide-matches-text": "Hide additional matches",
71    "search-more-match-text": "more match in this document",
72    "search-more-matches-text": "more matches in this document",
73    "search-clear-button-title": "Clear",
74    "search-text-placeholder": "",
75    "search-detached-cancel-button-title": "Cancel",
76    "search-submit-button-title": "Submit",
77    "search-label": "Search"
78  }
79}</script>
vendor: 1 bytes, line 79
79
80<script>
81
82  window.document.addEventListener("DOMContentLoaded", function (_event) {
83    const listingTargetEl = window.document.querySelector('#listing-listing .list');
84    if (!listingTargetEl) {
85      // No listing discovered, do not attach.
86      return; 
87    }
88
89    const options = {
90      valueNames: ['listing-date','listing-title','listing-author',{ data: ['index'] },{ data: ['categories'] },{ data: ['listing-date-sort'] }],
91      
92      searchColumns: ["listing-date","listing-title","listing-author","listing-image","listing-description"],
93    };
94
95    window['quarto-listings'] = window['quarto-listings'] || {};
96    window['quarto-listings']['listing-listing'] = new List('listing-listing', options);
97
98    if (window['quarto-listing-loaded']) {
99      window['quarto-listing-loaded']();
100    }
101  });
102
103  window.addEventListener('hashchange',() => {
104    if (window['quarto-listing-loaded']) {
105      window['quarto-listing-loaded']();
106    }
107  })
108  </script>
108
109
110  
110<script src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=es6"></script>
110
111  
111<script defer="" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.js" type="text/javascript"></script>
111
112
113<script type="text/javascript">
114const typesetMath = (el) => {
115  if (window.MathJax) {
116    // MathJax Typeset
117    window.MathJax.typeset([el]);
118  } else if (window.katex) {
119    // KaTeX Render
120    var mathElements = el.getElementsByClassName("math");
121    var macros = [];
122    for (var i = 0; i < mathElements.length; i++) {
123      var texText = mathElements[i].firstChild;
124      if (mathElements[i].tagName == "SPAN" && texText && texText.data) {
125        window.katex.render(texText.data, mathElements[i], {
126          displayMode: mathElements[i].classList.contains('display'),
127          throwOnError: false,
128          macros: macros,
129          fleqn: false
130        });
131      }
132    }
133  }
134}
135window.Quarto = {
136  typesetMath
137};
138</script>
138
139
140<link rel="stylesheet" href="styles.css">
141<meta property="og:title" content="Posts – danton.codes">
142<meta property="og:image" content="https://danton.codes/images/profile.jpeg">
143<meta property="og:site_name" content="danton.codes">
144<meta name="twitter:title" content="Posts – danton.codes">
145<meta name="twitter:image" content="https://danton.codes/images/profile.jpeg">
146<meta name="twitter:card" content="summary_large_image">
147</head>
148
149<body class="nav-fixed fullcontent quarto-light">
149<script id="quarto-html-before-body" type="application/javascript">
150    const toggleBodyColorMode = (bsSheetEl) => {
151      const mode = bsSheetEl.getAttribute("data-mode");
152      const bodyEl = window.document.querySelector("body");
153      if (mode === "dark") {
154        bodyEl.classList.add("quarto-dark");
155        bodyEl.classList.remove("quarto-light");
156      } else {
157        bodyEl.classList.add("quarto-light");
158        bodyEl.classList.remove("quarto-dark");
159      }
160    }
161    const toggleBodyColorPrimary = () => {
162      const bsSheetEl = window.document.querySelector("link#quarto-bootstrap:not([rel=disabled-stylesheet])");
163      if (bsSheetEl) {
164        toggleBodyColorMode(bsSheetEl);
165      }
166    }
167    const setColorSchemeToggle = (alternate) => {
168      const toggles = window.document.querySelectorAll('.quarto-color-scheme-toggle');
169      for (let i=0; i < toggles.length; i++) {
170        const toggle = toggles[i];
171        if (toggle) {
172          if (alternate) {
173            toggle.classList.add("alternate");
174          } else {
175            toggle.classList.remove("alternate");
176          }
177        }
178      }
179    };
180    const toggleColorMode = (alternate) => {
181      // Switch the stylesheets
182      const primaryStylesheets = window.document.querySelectorAll('link.quarto-color-scheme:not(.quarto-color-alternate)');
183      const alternateStylesheets = window.document.querySelectorAll('link.quarto-color-scheme.quarto-color-alternate');
184      manageTransitions('#quarto-margin-sidebar .nav-link', false);
185      if (alternate) {
186        // note: dark is layered on light, we don't disable primary!
187        enableStylesheet(alternateStylesheets);
188        for (const sheetNode of alternateStylesheets) {
189          if (sheetNode.id === "quarto-bootstrap") {
190            toggleBodyColorMode(sheetNode);
191          }
192        }
193      } else {
194        disableStylesheet(alternateStylesheets);
195        enableStylesheet(primaryStylesheets)
196        toggleBodyColorPrimary();
197      }
198      manageTransitions('#quarto-margin-sidebar .nav-link', true);
199      // Switch the toggles
200      setColorSchemeToggle(alternate)
201      // Hack to workaround the fact that safari doesn't
202      // properly recolor the scrollbar when toggling (#1455)
203      if (navigator.userAgent.indexOf('Safari') > 0 && navigator.userAgent.indexOf('Chrome') == -1) {
204        manageTransitions("body", false);
205        window.scrollTo(0, 1);
206        setTimeout(() => {
207          window.scrollTo(0, 0);
208          manageTransitions("body", true);
209        }, 40);
210      }
211    }
212    const disableStylesheet = (stylesheets) => {
213      for (let i=0; i < stylesheets.length; i++) {
214        const stylesheet = stylesheets[i];
215        stylesheet.rel = 'disabled-stylesheet';
216      }
217    }
218    const enableStylesheet = (stylesheets) => {
219      for (let i=0; i < stylesheets.length; i++) {
220        const stylesheet = stylesheets[i];
221        if(stylesheet.rel !== 'stylesheet') { // for Chrome, which will still FOUC without this check
222          stylesheet.rel = 'stylesheet';
223        }
224      }
225    }
226    const manageTransitions = (selector, allowTransitions) => {
227      const els = window.document.querySelectorAll(selector);
228      for (let i=0; i < els.length; i++) {
229        const el = els[i];
230        if (allowTransitions) {
231          el.classList.remove('notransition');
232        } else {
233          el.classList.add('notransition');
234        }
235      }
236    }
237    const isFileUrl = () => {
238      return window.location.protocol === 'file:';
239    }
240    const hasAlternateSentinel = () => {
241      let styleSentinel = getColorSchemeSentinel();
242      if (styleSentinel !== null) {
243        return styleSentinel === "alternate";
244      } else {
245        return false;
246      }
247    }
248    const setStyleSentinel = (alternate) => {
249      const value = alternate ? "alternate" : "default";
250      if (!isFileUrl()) {
251        window.localStorage.setItem("quarto-color-scheme", value);
252      } else {
253        localAlternateSentinel = value;
254      }
255    }
256    const getColorSchemeSentinel = () => {
257      if (!isFileUrl()) {
258        const storageValue = window.localStorage.getItem("quarto-color-scheme");
259        return storageValue != null ? storageValue : localAlternateSentinel;
260      } else {
261        return localAlternateSentinel;
262      }
263    }
264    const toggleGiscusIfUsed = (isAlternate, darkModeDefault) => {
265      const baseTheme = document.querySelector('#giscus-base-theme')?.value ?? 'light';
266      const alternateTheme = document.querySelector('#giscus-alt-theme')?.value ?? 'dark';
267      let newTheme = '';
268      if(authorPrefersDark) {
269        newTheme = isAlternate ? baseTheme : alternateTheme;
270      } else {
271        newTheme = isAlternate ? alternateTheme : baseTheme;
272      }
273      const changeGiscusTheme = () => {
274        // From: https://github.com/giscus/giscus/issues/336
275        const sendMessage = (message) => {
276          const iframe = document.querySelector('iframe.giscus-frame');
277          if (!iframe) return;
278          iframe.contentWindow.postMessage({ giscus: message }, 'https://giscus.app');
279        }
280        sendMessage({
281          setConfig: {
282            theme: newTheme
283          }
284        });
285      }
286      const isGiscussLoaded = window.document.querySelector('iframe.giscus-frame') !== null;
287      if (isGiscussLoaded) {
288        changeGiscusTheme();
289      }
290    };
291    const authorPrefersDark = false;
292    const darkModeDefault = authorPrefersDark;
293      document.querySelector('link#quarto-text-highlighting-styles.quarto-color-scheme-extra').rel = 'disabled-stylesheet';
294      document.querySelector('link#quarto-bootstrap.quarto-color-scheme-extra').rel = 'disabled-stylesheet';
295    let localAlternateSentinel = darkModeDefault ? 'alternate' : 'default';
296    // Dark / light mode switch
297    window.quartoToggleColorScheme = () => {
298      // Read the current dark / light value
299      let toAlternate = !hasAlternateSentinel();
300      toggleColorMode(toAlternate);
301      setStyleSentinel(toAlternate);
302      toggleGiscusIfUsed(toAlternate, darkModeDefault);
303      window.dispatchEvent(new Event('resize'));
304    };
305    // Switch to dark mode if need be
306    if (hasAlternateSentinel()) {
307      toggleColorMode(true);
308    } else {
309      toggleColorMode(false);
310    }
311  </script>
311
312
313<div id="quarto-search-results"></div>
314  <header id="quarto-header" class="headroom fixed-top">
315    <nav class="navbar navbar-expand-lg " data-bs-theme="dark">
316      <div class="navbar-container container-fluid">
317      <div class="navbar-brand-container mx-auto">
318    <a class="navbar-brand" href="./index.html">
319    <span class="navbar-title">danton.codes</span>
320    </a>
321  </div>
322            <div id="quarto-search" class="" title="Search"></div>
323          <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" role="menu" aria-expanded="false" aria-label="Toggle navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
324  <span class="navbar-toggler-icon"></span>
325</button>
326          <div class="collapse navbar-collapse" id="navbarCollapse">
327            <ul class="navbar-nav navbar-nav-scroll me-auto">
328  <li class="nav-item">
329    <a class="nav-link" href="./index.html"> 
330<span class="menu-text">About Me</span></a>
331  </li>  
332  <li class="nav-item">
333    <a class="nav-link" href="./resume.html"> 
334<span class="menu-text">Resume</span></a>
335  </li>  
336  <li class="nav-item">
337    <a class="nav-link active" href="./posts.html" aria-current="page"> 
338<span class="menu-text">Posts</span></a>
339  </li>  
340  <li class="nav-item">
341    <a class="nav-link" href="https://dantonnoriega.github.io/SR3TuringQuarto.jl/"> 
342<span class="menu-text">SR3TuringQuarto.jl</span></a>
343  </li>  
344</ul>
345          </div> <!-- /navcollapse -->
346            <div class="quarto-navbar-tools">
347    <a href="https://gitlab.com/dantonnoriega/dantonnoriega.gitlab.io" title="Source" class="quarto-navigation-tool px-1" aria-label="Source"><i class="bi bi-gitlab"></i></a>
348  <a href="" class="quarto-color-scheme-toggle quarto-navigation-tool  px-1" onclick="window.quartoToggleColorScheme(); return false;" title="Toggle dark mode"><i class="bi"></i></a>
349</div>
350      </div> <!-- /container-fluid -->
351    </nav>
352</header>
353<!-- content -->
354<div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-article page-navbar">
355<!-- sidebar -->
356<!-- margin-sidebar -->
357    
358<!-- main -->
359<main class="content" id="quarto-document-content">
360
361
362<header id="title-block-header" class="quarto-title-block default">
363<div class="quarto-title">
364<h1 class="title">Posts</h1>
365</div>
366
367
368
369<div class="quarto-title-meta">
370
371    
372  
373    
374  </div>
375  
376
377
378</header>
379
380
381
382
383
384
385<div class="quarto-listing quarto-listing-container-default" id="listing-listing">
386<div class="listing-actions-group">
387   <div class="input-group input-group-sm quarto-listing-sort">
388    <span class="input-group-text"><i class="bi bi-sort-down"></i></span>
389    <select id="listing-listing-sort" class="form-select" aria-label="Order By" onchange="window['quarto-listings']['listing-listing'].sort(this.options[this.selectedIndex].value, { order: this.options[this.selectedIndex].getAttribute('data-direction')})">
390      <option value="" disabled="" selected="" hidden="">Order By</option>
391      <option value="index" data-direction="asc">Default</option>
392      <option value="listing-date-sort" data-direction="asc">
393        Date - Oldest
394      </option>
395      <option value="listing-date-sort" data-direction="desc">
396        Date - Newest
397      </option>
398    </select>
399  </div>
400    <div class="input-group input-group-sm quarto-listing-filter">
401      <span class="input-group-text"><i class="bi bi-search"></i></span>
402      <input type="text" class="search form-control" placeholder="Filter">
403    </div>
404</div>
405<div class="list quarto-listing-default">
406<div class="quarto-post image-right" data-index="0" data-categories="UiUyQ1N0YW4=" data-listing-date-sort="1561359600000" data-listing-file-modified-sort="1694941493631" data-listing-date-modified-sort="NaN" data-listing-reading-time-sort="1" data-listing-word-count-sort="11">
407<div class="body">
408<h3 class="no-anchor listing-title">
409<a href="./posts/2019-06-24-brief-introduction-to-stan-and-multilevel-models.html" class="no-external">A Brief Introduction to Multilevel Models and Stan with R: rstanarm, brms, and rethinking</a>
410</h3>
411</div>
412<div class="metadata">
413<a href="./posts/2019-06-24-brief-introduction-to-stan-and-multilevel-models.html" class="no-external">
414<div class="listing-date">
415Jun 24, 2019
416</div>
417<div class="listing-author">
418Danton Noriega-Goodwin
419</div>
420</a>
421</div>
422</div>
423<div class="quarto-post image-right" data-index="1" data-categories="Y29tbWFuZCUyMGxpbmU=" data-listing-date-sort="1500620400000" data-listing-file-modified-sort="1695059899994" data-listing-date-modified-sort="NaN" data-listing-reading-time-sort="2" data-listing-word-count-sort="350">
424<div class="body">
425<h3 class="no-anchor listing-title">
426<a href="./posts/2017-07-21-cropping-pdfs.html" class="no-external">Cropping a Multiple Page PDF Using Command Line (macOS)</a>
427</h3>
428</div>
429<div class="metadata">
430<a href="./posts/2017-07-21-cropping-pdfs.html" class="no-external">
431<div class="listing-date">
432Jul 21, 2017
433</div>
434<div class="listing-author">
435Danton Noriega-Goodwin
436</div>
437</a>
438</div>
439</div>
440<div class="quarto-post image-right" data-index="2" data-categories="UiUyQ1hNTA==" data-listing-date-sort="1492498800000" data-listing-file-modified-sort="1694941493631" data-listing-date-modified-sort="NaN" data-listing-reading-time-sort="1" data-listing-word-count-sort="100">
441<div class="body">
442<h3 class="no-anchor listing-title">
443<a href="./posts/2017-04-18-xmltools-package.html" class="no-external">xmltools package to help convert XML data to tidy data frames</a>
444</h3>
445</div>
446<div class="metadata">
447<a href="./posts/2017-04-18-xmltools-package.html" class="no-external">
448<div class="listing-date">
449Apr 18, 2017
450</div>
451<div class="listing-author">
452Danton Noriega-Goodwin
453</div>
454</a>
455</div>
456</div>
457<div class="quarto-post image-right" data-index="3" data-categories="UiUyQ2hpZ2hjaGFydGVy" data-listing-date-sort="1491375600000" data-listing-file-modified-sort="1694941493631" data-listing-date-modified-sort="NaN" data-listing-reading-time-sort="23" data-listing-word-count-sort="4491">
458<div class="body">
459<h3 class="no-anchor listing-title">
460<a href="./posts/2017-04-05-highcharter-explainer.html" class="no-external">Thinking in <code>highcharter</code> - How to build any Highcharts plot in R</a>
461</h3>
462</div>
463<div class="metadata">
464<a href="./posts/2017-04-05-highcharter-explainer.html" class="no-external">
465<div class="listing-date">
466Apr 5, 2017
467</div>
468<div class="listing-author">
469Danton Noriega-Goodwin
470</div>
471</a>
472</div>
473</div>
474<div class="quarto-post image-right" data-index="4" data-listing-date-sort="1460617200000" data-listing-file-modified-sort="1694941493631" data-listing-date-modified-sort="NaN" data-listing-reading-time-sort="3" data-listing-word-count-sort="538">
475<div class="body">
476<h3 class="no-anchor listing-title">
477<a href="./posts/2016-04-14-rmarkdown-to-docx.html" class="no-external">RMarkdown (.Rmd) to MS Word (.docx) aka rmarkdown2docx</a>
478</h3>
479</div>
480<div class="metadata">
481<a href="./posts/2016-04-14-rmarkdown-to-docx.html" class="no-external">
482<div class="listing-date">
483Apr 14, 2016
484</div>
485<div class="listing-author">
486Danton Noriega-Goodwin
487</div>
488</a>
489</div>
490</div>
491<div class="quarto-post image-right" data-index="5" data-listing-date-sort="1459234800000" data-listing-file-modified-sort="1694941493631" data-listing-date-modified-sort="NaN" data-listing-reading-time-sort="4" data-listing-word-count-sort="682">
492<div class="body">
493<h3 class="no-anchor listing-title">
494<a href="./posts/2016-03-29-pdf-text-convert-ocr-tesseract.html" class="no-external">Converting Unsearchable PDF Files (aka PDF scans) to Raw Text Using Command Line Tools <code>convert</code> and <code>tesseract</code></a>
495</h3>
496</div>
497<div class="metadata">
498<a href="./posts/2016-03-29-pdf-text-convert-ocr-tesseract.html" class="no-external">
499<div class="listing-date">
500Mar 29, 2016
501</div>
502<div class="listing-author">
503Danton Noriega-Goodwin
504</div>
505</a>
506</div>
507</div>
508</div>
509<div class="listing-no-matching d-none">No matching items</div>
510</div></main> <!-- /main -->
511<script id="quarto-html-after-body" type="application/javascript">
512  window.document.addEventListener("DOMContentLoaded", function (event) {
513    // Ensure there is a toggle, if there isn't float one in the top right
514    if (window.document.querySelector('.quarto-color-scheme-toggle') === null) {
515      const a = window.document.createElement('a');
516      a.classList.add('top-right');
517      a.classList.add('quarto-color-scheme-toggle');
518      a.href = "";
519      a.onclick = function() { try { window.quartoToggleColorScheme(); } catch {} return false; };
520      const i = window.document.createElement("i");
521      i.classList.add('bi');
522      a.appendChild(i);
523      window.document.body.appendChild(a);
524    }
525    setColorSchemeToggle(hasAlternateSentinel())
526    const icon = "";
527    const anchorJS = new window.AnchorJS();
528    anchorJS.options = {
529      placement: 'right',
530      icon: icon
531    };
532    anchorJS.add('.anchored');
533    const isCodeAnnotation = (el) => {
534      for (const clz of el.classList) {
535        if (clz.startsWith('code-annotation-')) {                     
536          return true;
537        }
538      }
539      return false;
540    }
541    const onCopySuccess = function(e) {
542      // button target
543      const button = e.trigger;
544      // don't keep focus
545      button.blur();
546      // flash "checked"
547      button.classList.add('code-copy-button-checked');
548      var currentTitle = button.getAttribute("title");
549      button.setAttribute("title", "Copied!");
550      let tooltip;
551      if (window.bootstrap) {
552        button.setAttribute("data-bs-toggle", "tooltip");
553        button.setAttribute("data-bs-placement", "left");
554        button.setAttribute("data-bs-title", "Copied!");
555        tooltip = new bootstrap.Tooltip(button, 
556          { trigger: "manual", 
557            customClass: "code-copy-button-tooltip",
558            offset: [0, -8]});
559        tooltip.show();    
560      }
561      setTimeout(function() {
562        if (tooltip) {
563          tooltip.hide();
564          button.removeAttribute("data-bs-title");
565          button.removeAttribute("data-bs-toggle");
566          button.removeAttribute("data-bs-placement");
567        }
568        button.setAttribute("title", currentTitle);
569        button.classList.remove('code-copy-button-checked');
570      }, 1000);
571      // clear code selection
572      e.clearSelection();
573    }
574    const getTextToCopy = function(trigger) {
575      const outerScaffold = trigger.parentElement.cloneNode(true);
576      const codeEl = outerScaffold.querySelector('code');
577      for (const childEl of codeEl.children) {
578        if (isCodeAnnotation(childEl)) {
579          childEl.remove();
580        }
581      }
582      return codeEl.innerText;
583    }
584    const clipboard = new window.ClipboardJS('.code-copy-button:not([data-in-quarto-modal])', {
585      text: getTextToCopy
586    });
587    clipboard.on('success', onCopySuccess);
588    if (window.document.getElementById('quarto-embedded-source-code-modal')) {
589      const clipboardModal = new window.ClipboardJS('.code-copy-button[data-in-quarto-modal]', {
590        text: getTextToCopy,
591        container: window.document.getElementById('quarto-embedded-source-code-modal')
592      });
593      clipboardModal.on('success', onCopySuccess);
594    }
595      var localhostRegex = new RegExp(/^(?:http|https):\/\/localhost\:?[0-9]*\//);
596      var mailtoRegex = new RegExp(/^mailto:/);
597        var filterRegex = new RegExp("https:\/\/danton\.codes");
598      var isInternal = (href) => {
599          return filterRegex.test(href) || localhostRegex.test(href) || mailtoRegex.test(href);
600      }
601      // Inspect non-navigation links and adorn them if external
602     var links = window.document.querySelectorAll('a[href]:not(.nav-link):not(.navbar-brand):not(.toc-action):not(.sidebar-link):not(.sidebar-item-toggle):not(.pagination-link):not(.no-external):not([aria-hidden]):not(.dropdown-item):not(.quarto-navigation-tool):not(.about-link)');
603      for (var i=0; i<links.length; i++) {
604        const link = links[i];
605        if (!isInternal(link.href)) {
606          // undo the damage that might have been done by quarto-nav.js in the case of
607          // links that we want to consider external
608          if (link.dataset.originalHref !== undefined) {
609            link.href = link.dataset.originalHref;
610          }
611        }
612      }
613    function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) {
614      const config = {
615        allowHTML: true,
616        maxWidth: 500,
617        delay: 100,
618        arrow: false,
619        appendTo: function(el) {
620            return el.parentElement;
621        },
622        interactive: true,
623        interactiveBorder: 10,
624        theme: 'quarto',
625        placement: 'bottom-start',
626      };
627      if (contentFn) {
628        config.content = contentFn;
629      }
630      if (onTriggerFn) {
631        config.onTrigger = onTriggerFn;
632      }
633      if (onUntriggerFn) {
634        config.onUntrigger = onUntriggerFn;
635      }
636      window.tippy(el, config); 
637    }
638    const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
639    for (var i=0; i<noterefs.length; i++) {
640      const ref = noterefs[i];
641      tippyHover(ref, function() {
642        // use id or data attribute instead here
643        let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
644        try { href = new URL(href).hash; } catch {}
645        const id = href.replace(/^#\/?/, "");
646        const note = window.document.getElementById(id);
647        if (note) {
648          return note.innerHTML;
649        } else {
650          return "";
651        }
652      });
653    }
654    const xrefs = window.document.querySelectorAll('a.quarto-xref');
655    const processXRef = (id, note) => {
656      // Strip column container classes
657      const stripColumnClz = (el) => {
658        el.classList.remove("page-full", "page-columns");
659        if (el.children) {
660          for (const child of el.children) {
661            stripColumnClz(child);
662          }
663        }
664      }
665      stripColumnClz(note)
666      if (id === null || id.startsWith('sec-')) {
667        // Special case sections, only their first couple elements
668        const container = document.createElement("div");
669        if (note.children && note.children.length > 2) {
670          container.appendChild(note.children[0].cloneNode(true));
671          for (let i = 1; i < note.children.length; i++) {
672            const child = note.children[i];
673            if (child.tagName === "P" && child.innerText === "") {
674              continue;
675            } else {
676              container.appendChild(child.cloneNode(true));
677              break;
678            }
679          }
680          if (window.Quarto?.typesetMath) {
681            window.Quarto.typesetMath(container);
682          }
683          return container.innerHTML
684        } else {
685          if (window.Quarto?.typesetMath) {
686            window.Quarto.typesetMath(note);
687          }
688          return note.innerHTML;
689        }
690      } else {
691        // Remove any anchor links if they are present
692        const anchorLink = note.querySelector('a.anchorjs-link');
693        if (anchorLink) {
694          anchorLink.remove();
695        }
696        if (window.Quarto?.typesetMath) {
697          window.Quarto.typesetMath(note);
698        }
699        if (note.classList.contains("callout")) {
700          return note.outerHTML;
701        } else {
702          return note.innerHTML;
703        }
704      }
705    }
706    for (var i=0; i<xrefs.length; i++) {
707      const xref = xrefs[i];
708      tippyHover(xref, undefined, function(instance) {
709        instance.disable();
710        let url = xref.getAttribute('href');
711        let hash = undefined; 
712        if (url.startsWith('#')) {
713          hash = url;
714        } else {
715          try { hash = new URL(url).hash; } catch {}
716        }
717        if (hash) {
718          const id = hash.replace(/^#\/?/, "");
719          const note = window.document.getElementById(id);
720          if (note !== null) {
721            try {
722              const html = processXRef(id, note.cloneNode(true));
723              instance.setContent(html);
724            } finally {
725              instance.enable();
726              instance.show();
727            }
728          } else {
729            // See if we can fetch this
730            fetch(url.split('#')[0])
731            .then(res => res.text())
732            .then(html => {
733              const parser = new DOMParser();
734              const htmlDoc = parser.parseFromString(html, "text/html");
735              const note = htmlDoc.getElementById(id);
736              if (note !== null) {
737                const html = processXRef(id, note);
738                instance.setContent(html);
739              } 
740            }).finally(() => {
741              instance.enable();
742              instance.show();
743            });
744          }
745        } else {
746          // See if we can fetch a full url (with no hash to target)
747          // This is a special case and we should probably do some content thinning / targeting
748          fetch(url)
749          .then(res => res.text())
750          .then(html => {
751            const parser = new DOMParser();
752            const htmlDoc = parser.parseFromString(html, "text/html");
753            const note = htmlDoc.querySelector('main.content');
754            if (note !== null) {
755              // This should only happen for chapter cross references
756              // (since there is no id in the URL)
757              // remove the first header
758              if (note.children.length > 0 && note.children[0].tagName === "HEADER") {
759                note.children[0].remove();
760              }
761              const html = processXRef(null, note);
762              instance.setContent(html);
763            } 
764          }).finally(() => {
765            instance.enable();
766            instance.show();
767          });
768        }
769      }, function(instance) {
770      });
771    }
772        let selectedAnnoteEl;
773        const selectorForAnnotation = ( cell, annotation) => {
774          let cellAttr = 'data-code-cell="' + cell + '"';
775          let lineAttr = 'data-code-annotation="' +  annotation + '"';
776          const selector = 'span[' + cellAttr + '][' + lineAttr + ']';
777          return selector;
778        }
779        const selectCodeLines = (annoteEl) => {
780          const doc = window.document;
781          const targetCell = annoteEl.getAttribute("data-target-cell");
782          const targetAnnotation = annoteEl.getAttribute("data-target-annotation");
783          const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation));
784          const lines = annoteSpan.getAttribute("data-code-lines").split(",");
785          const lineIds = lines.map((line) => {
786            return targetCell + "-" + line;
787          })
788          let top = null;
789          let height = null;
790          let parent = null;
791          if (lineIds.length > 0) {
792              //compute the position of the single el (top and bottom and make a div)
793              const el = window.document.getElementById(lineIds[0]);
794              top = el.offsetTop;
795              height = el.offsetHeight;
796              parent = el.parentElement.parentElement;
797            if (lineIds.length > 1) {
798              const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
799              const bottom = lastEl.offsetTop + lastEl.offsetHeight;
800              height = bottom - top;
801            }
802            if (top !== null && height !== null && parent !== null) {
803              // cook up a div (if necessary) and position it 
804              let div = window.document.getElementById("code-annotation-line-highlight");
805              if (div === null) {
806                div = window.document.createElement("div");
807                div.setAttribute("id", "code-annotation-line-highlight");
808                div.style.position = 'absolute';
809                parent.appendChild(div);
810              }
811              div.style.top = top - 2 + "px";
812              div.style.height = height + 4 + "px";
813              div.style.left = 0;
814              let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
815              if (gutterDiv === null) {
816                gutterDiv = window.document.createElement("div");
817                gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
818                gutterDiv.style.position = 'absolute';
819                const codeCell = window.document.getElementById(targetCell);
820                const gutter = codeCell.querySelector('.code-annotation-gutter');
821                gutter.appendChild(gutterDiv);
822              }
823              gutterDiv.style.top = top - 2 + "px";
824              gutterDiv.style.height = height + 4 + "px";
825            }
826            selectedAnnoteEl = annoteEl;
827          }
828        };
829        const unselectCodeLines = () => {
830          const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"];
831          elementsIds.forEach((elId) => {
832            const div = window.document.getElementById(elId);
833            if (div) {
834              div.remove();
835            }
836          });
837          selectedAnnoteEl = undefined;
838        };
839          // Handle positioning of the toggle
840      window.addEventListener(
841        "resize",
842        throttle(() => {
843          elRect = undefined;
844          if (selectedAnnoteEl) {
845            selectCodeLines(selectedAnnoteEl);
846          }
847        }, 10)
848      );
849      function throttle(fn, ms) {
850      let throttle = false;
851      let timer;
852        return (...args) => {
853          if(!throttle) { // first call gets through
854              fn.apply(this, args);
855              throttle = true;
856          } else { // all the others get throttled
857              if(timer) clearTimeout(timer); // cancel #2
858              timer = setTimeout(() => {
859                fn.apply(this, args);
860                timer = throttle = false;
861              }, ms);
862          }
863        };
864      }
865        // Attach click handler to the DT
866        const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
867        for (const annoteDlNode of annoteDls) {
868          annoteDlNode.addEventListener('click', (event) => {
869            const clickedEl = event.target;
870            if (clickedEl !== selectedAnnoteEl) {
871              unselectCodeLines();
872              const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active');
873              if (activeEl) {
874                activeEl.classList.remove('code-annotation-active');
875              }
876              selectCodeLines(clickedEl);
877              clickedEl.classList.add('code-annotation-active');
878            } else {
879              // Unselect the line
880              unselectCodeLines();
881              clickedEl.classList.remove('code-annotation-active');
882            }
883          });
884        }
885    const findCites = (el) => {
886      const parentEl = el.parentElement;
887      if (parentEl) {
888        const cites = parentEl.dataset.cites;
889        if (cites) {
890          return {
891            el,
892            cites: cites.split(' ')
893          };
894        } else {
895          return findCites(el.parentElement)
896        }
897      } else {
898        return undefined;
899      }
900    };
901    var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
902    for (var i=0; i<bibliorefs.length; i++) {
903      const ref = bibliorefs[i];
904      const citeInfo = findCites(ref);
905      if (citeInfo) {
906        tippyHover(citeInfo.el, function() {
907          var popup = window.document.createElement('div');
908          citeInfo.cites.forEach(function(cite) {
909            var citeDiv = window.document.createElement('div');
910            citeDiv.classList.add('hanging-indent');
911            citeDiv.classList.add('csl-entry');
912            var biblioDiv = window.document.getElementById('ref-' + cite);
913            if (biblioDiv) {
914              citeDiv.innerHTML = biblioDiv.innerHTML;
915            }
916            popup.appendChild(citeDiv);
917          });
918          return popup.innerHTML;
919        });
920      }
921    }
922  });
923  </script>
923
924</div> <!-- /content -->
925<footer class="footer">
926  <div class="nav-footer">
927    <div class="nav-footer-left">
928      &nbsp;
929    </div>   
930    <div class="nav-footer-center">
931<p>☕️ copyright © 2026. made with ❤️ by danton.</p>
932</div>
933    <div class="nav-footer-right">
934      &nbsp;
935    </div>
936  </div>
937</footer>
938
939
940
941
942</body></html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.