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.7.33"> 6 7<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> 8 9 10<title>Workshops â WALKER DATA</title> 11<style> 12code{white-space: pre-wrap;} 13span.smallcaps{font-variant: small-caps;} 14div.columns{display: flex; gap: min(4vw, 1.5em);} 15div.column{flex: auto; overflow-x: auto;} 16div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} 17ul.task-list{list-style: none;} 18ul.task-list li input[type="checkbox"] { 19 width: 0.8em; 20 margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */ 21 vertical-align: middle; 22} 23</style> 24 25
26<script src="site_libs/quarto-nav/quarto-nav.js"></script>
vendor: 1 bytes, line 26
26
27<script src="site_libs/quarto-nav/headroom.min.js"></script>
vendor: 1 bytes, line 27
27
28<script src="site_libs/clipboard/clipboard.min.js"></script>
vendor: 1 bytes, line 28
28
29<script src="site_libs/quarto-search/autocomplete.umd.js"></script>
vendor: 1 bytes, line 29
29
30<script src="site_libs/quarto-search/fuse.min.js"></script>
vendor: 1 bytes, line 30
30
31<script src="site_libs/quarto-search/quarto-search.js"></script>
31 32<meta name="quarto:offset" content="./"> 33<link href="./favicon.png" rel="icon" type="image/png">
34<script src="site_libs/quarto-html/quarto.js" type="module"></script>
vendor: 1 bytes, line 34
34
35<script src="site_libs/quarto-html/tabsets/tabsets.js" type="module"></script>
vendor: 1 bytes, line 35
35
36<script src="site_libs/quarto-html/popper.min.js"></script>
vendor: 1 bytes, line 36
36
37<script src="site_libs/quarto-html/tippy.umd.min.js"></script>
vendor: 1 bytes, line 37
37
38<script src="site_libs/quarto-html/anchor.min.js"></script>
38 39<link href="site_libs/quarto-html/tippy.css" rel="stylesheet"> 40<link href="site_libs/quarto-html/quarto-syntax-highlighting-ea385d0e468b0dd5ea5bf0780b1290d9.css" rel="stylesheet" id="quarto-text-highlighting-styles">
41<script src="site_libs/bootstrap/bootstrap.min.js"></script>
41 42<link href="site_libs/bootstrap/bootstrap-icons.css" rel="stylesheet"> 43<link href="site_libs/bootstrap/bootstrap-c1fac2584b48ed01fb6e278e36375074.min.css" rel="stylesheet" append-hash="true" id="quarto-bootstrap" data-mode="light">
44<script id="quarto-search-options" type="application/json">{ 45 "location": "navbar", 46 "copy-button": false, 47 "collapse-after": 3, 48 "panel-placement": "end", 49 "type": "overlay", 50 "limit": 50, 51 "keyboard-shortcut": [ 52 "f", 53 "/", 54 "s" 55 ], 56 "show-item-context": false, 57 "language": { 58 "search-no-results-text": "No results", 59 "search-matching-documents-text": "matching documents", 60 "search-copy-link-title": "Copy link to search", 61 "search-hide-matches-text": "Hide additional matches", 62 "search-more-match-text": "more match in this document", 63 "search-more-matches-text": "more matches in this document", 64 "search-clear-button-title": "Clear", 65 "search-text-placeholder": "", 66 "search-detached-cancel-button-title": "Cancel", 67 "search-submit-button-title": "Submit", 68 "search-label": "Search" 69 } 70}</script>
70 71 72 73<link rel="stylesheet" href="styles.css"> 74<meta name="twitter:title" content="Workshops â WALKER DATA"> 75<meta name="twitter:description" content="GIS, demographics, and data science consulting"> 76<meta name="twitter:image" content="https://walker-data.com/img/dfw_3d.png"> 77<meta name="twitter:image-height" content="965"> 78<meta name="twitter:image-width" content="1893"> 79<meta name="twitter:card" content="summary_large_image"> 80</head> 81 82<body class="nav-fixed quarto-light"> 83 84<div id="quarto-search-results"></div> 85 <header id="quarto-header" class="headroom fixed-top"> 86 <nav class="navbar navbar-expand-lg " data-bs-theme="dark"> 87 <div class="navbar-container container-fluid"> 88 <div class="navbar-brand-container mx-auto"> 89 <a class="navbar-brand" href="./index.html"> 90 <span class="navbar-title">WALKER DATA</span> 91 </a> 92 </div> 93 <div id="quarto-search" class="" title="Search"></div> 94 <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(); }"> 95 <span class="navbar-toggler-icon"></span> 96</button> 97 <div class="collapse navbar-collapse" id="navbarCollapse"> 98 <ul class="navbar-nav navbar-nav-scroll ms-auto"> 99 <li class="nav-item"> 100 <a class="nav-link" href="./about.html"> 101<span class="menu-text">About</span></a> 102 </li> 103 <li class="nav-item"> 104 <a class="nav-link" href="./blog.html"> 105<span class="menu-text">Blog</span></a> 106 </li> 107 <li class="nav-item"> 108 <a class="nav-link active" href="./workshops.html" aria-current="page"> 109<span class="menu-text">Workshops</span></a> 110 </li> 111 <li class="nav-item compact"> 112 <a class="nav-link" href="https://github.com/walkerke"> <i class="bi bi-github" role="img"> 113</i> 114<span class="menu-text"></span></a> 115 </li> 116 <li class="nav-item compact"> 117 <a class="nav-link" href="https://twitter.com/kyle_e_walker"> <i class="bi bi-twitter" role="img"> 118</i> 119<span class="menu-text"></span></a> 120 </li> 121</ul> 122 </div> <!-- /navcollapse --> 123 <div class="quarto-navbar-tools"> 124</div> 125 </div> <!-- /container-fluid --> 126 </nav> 127</header> 128<!-- content --> 129<div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-article page-navbar"> 130<!-- sidebar --> 131<!-- margin-sidebar --> 132 <div id="quarto-margin-sidebar" class="sidebar margin-sidebar"> 133 <nav id="TOC" role="doc-toc" class="toc-active"> 134 <h2 id="toc-title">On this page</h2> 135 136 <ul> 137 <li><a href="#mapgl-workshop-series" id="toc-mapgl-workshop-series" class="nav-link active" data-scroll-target="#mapgl-workshop-series">mapgl Workshop Series</a></li> 138 <li><a href="#location-intelligence-and-web-apps-with-shiny-workshops" id="toc-location-intelligence-and-web-apps-with-shiny-workshops" class="nav-link" data-scroll-target="#location-intelligence-and-web-apps-with-shiny-workshops">Location Intelligence and Web Apps with Shiny Workshops</a></li> 139 <li><a href="#gis-and-interactive-mapping-workshops" id="toc-gis-and-interactive-mapping-workshops" class="nav-link" data-scroll-target="#gis-and-interactive-mapping-workshops">GIS and Interactive Mapping Workshops</a></li> 140 <li><a href="#census-workshops" id="toc-census-workshops" class="nav-link" data-scroll-target="#census-workshops">2020 Census workshops</a></li> 141 </ul> 142</nav> 143 </div> 144<!-- main --> 145<main class="content page-columns page-full" id="quarto-document-content"> 146 147<header id="title-block-header" class="quarto-title-block default"> 148<div class="quarto-title"> 149<h1 class="title">Workshops</h1> 150</div> 151 152 153 154<div class="quarto-title-meta"> 155 156 157 158 159 </div> 160 161 162 163</header> 164 165 166<p><strong>WALKER DATA</strong> offers custom data science workshops tailored to the needs of your organization as well as online workshops available for purchase.</p> 167<p>Check out these testimonials from workshop participants:</p> 168<blockquote class="blockquote"> 169<p>I wanted to thank you for the amazing R mapping trainings you provided. They were extremely helpful and sparked some very exciting ideas about how our
169center can use these resources.</p> 170</blockquote> 171<blockquote class="blockquote"> 172<p>One thing that really stood out was how you shared your own workflow through real-life examples. Seeing the direct application of tidycensus to our day-to-day tasks was a game-changer. Itâs safe to say that weâre all feeling a lot more confident about integrating these techniques into our work.</p> 173</blockquote> 174<blockquote class="blockquote"> 175<p>Your assistance in streamlining some of our existing workflows was a huge bonus. Itâs not just the âhow-toâ thatâs valuable, but also the practical insights you provided.</p> 176</blockquote> 177<blockquote class="blockquote"> 178<p>Your excitement about the whole topic was infectious. Itâs not easy to keep a group engaged all day, but you nailed it. Your enthusiasm made a huge difference.</p> 179</blockquote> 180<p>Reach out to <a href="mailto:[email protected]" class="email">[email protected]</a> to discuss your workshop needs today, or purchase a recorded workshop from the links below.</p> 181<section id="mapgl-workshop-series" class="level2"> 182<h2 class="anchored" data-anchor-id="mapgl-workshop-series">mapgl Workshop Series</h2> 183<p>The brand-new <strong>mapgl</strong> package brings the powerful web mapping libraries Mapbox GL JS and MapLibre GL JS to R - and this workshop series is the best place to learn it. Buy the workshop bundle for 7.5 hours of workshop recordings and three step-by-step annotated tutorials to help you make stunning interactive maps with your data in R.</p> 184<p><img src="img/dfw_3d.png" class="img-fluid"></p> 185<p>Youâll get the following three workshops:</p> 186<ul> 187<li>Make Stunning Web Maps in R with the mapgl Package</li> 188<li>Dynamic Business Intelligence Apps in R with Shiny and mapgl</li> 189<li>Visualize US Census Data in R with tidycensus and mapgl</li> 190</ul>
191<script src="https://gumroad.com/js/gumroad.js"></script>
191 192 193<div class="center"> 194 <a href="https://walkerdata.gumroad.com/l/mapgl-bundle"><button class="custom-button">Buy the bundle on Gumroad</button></a> 195</div> 196<br> 197<p>Donât need all three? Each individual workshop is available for purchase as well.</p> 198</section> 199<section id="location-intelligence-and-web-apps-with-shiny-workshops" class="level2 page-columns page-full"> 200<h2 class="anchored" data-anchor-id="location-intelligence-and-web-apps-with-shiny-workshops">Location Intelligence and Web Apps with Shiny Workshops</h2> 201<p>Learn essential location intelligence skills like geocoding, routing, and travel-time analytics in R, and display your results in a dynamic web mapping application! This bundle gets you five hours of custom workshops and two annotated tutorials not found anywhere else.</p>
202<script src="https://gumroad.com/js/gumroad.js"></script>
202 203 204<div class="center"> 205 <a href="https://walkerdata.gumroad.com/l/li-shiny-bundle"><button class="custom-button">Buy the bundle on Gumroad</button></a> 206</div> 207<br> 208<p><br></p> 209<p><strong>Workshop 1: Location Intelligence with R and Mapbox</strong></p> 210 211<div class="no-row-height column-margin column-container"><div class=""> 212<div class="quarto-figure quarto-figure-center"> 213<figure class="figure"> 214<p><img src="img/isochrones2.gif" class="img-fluid figure-img"></p> 215<figcaption>Animated isochrones around Nationals Park in Washington, DC</figcaption> 216</figure> 217</div> 218</div></div><p>This 2.5 hour workshop is designed to help you build skills in location intelligence to benefit your business or organization. Youâll use analytical tools like R and Mapbox to identify customer and store locations, perform travel-time analytics, build sales territories, and much more.</p> 219<div class="center"> 220 <a href="https://walkerdata.gumroad.com/l/location-intelligence"><button class="custom-button">Buy on Gumroad</button></a> 221</div> 222<br> 223<p><br></p> 224<p><strong>Workshop 2: Building Web Mapping Applications with R and Shiny</strong></p> 225 226<div class="no-row-height column-margin column-container"><div class=""> 227<div class="quarto-figure quarto-figure-center"> 228<figure class="figure"> 229<p><img src="img/geocoding_app.png" class="img-fluid figure-img"></p> 230<figcaption>A geocoding app youâll learn to build</figcaption> 231</figure> 232</div> 233</div></div><p>This 2.5 hour workshop will teach you the fundamentals of building interactive web mapping applications using R and Shiny, Râs powerful framework for app development. By the end of the workshop, youâll have the skills to create your own reactive mapping applications that include charts, custom design components, and more.</p> 234<div class="center"> 235 <a href="https://walkerdata.gumroad.com/l/web-mapping-shiny"><button class="custom-button">Buy on Gumroad</button></a> 236</div> 237<br> 238<p><br></p> 239</section> 240<section id="gis-and-interactive-mapping-workshops" class="level2 page-columns page-full"> 241<h2 class="anchored" data-anchor-id="gis-and-interactive-mapping-workshops">GIS and Interactive Mapping Workshops</h2> 242<p>Get started with Geographic Infomation Systems, spatial data analysis, and interactive mapping in R in 5 hours of custom workshops. The workshops are available for purchase separately or as a bundle for a $100 discount!</p>
243<script src="https://gumroad.com/js/gumroad.js"></script>
243 244 245<div class="center"> 246 <a href="https://walkerdata.gumroad.com/l/spatial-workshop-bundle"><button class="custom-button">Buy the bundle on Gumroad</button></a> 247</div> 248<br> 249<p><br></p> 250<p><strong>Workshop 1: Getting Started With Geographic Information Systems in R</strong></p> 251 252<div class="no-row-height column-margin column-container"><div class=""> 253<div class="quarto-figure quarto-figure-center"> 254<figure class="figure"> 255<p><img src="img/arcgis.jpeg" class="img-fluid figure-img"></p> 256<figcaption>Census data from R shown in ArcGIS Pro</figcaption> 257</figure> 258</div> 259</div></div><p>In this 2.5 hour workshop, you will gain the skills you need to get started with Geographic Information Systems using the R programming language. Youâll learn the fundamentals of doing GIS in R, gain experience with Râs powerful toolset for spatial data analysis, and learn how to connect R to desktop GIS tools like ArcGIS and QGIS.</p> 260<div class="center"> 261 <a href="https://walkerdata.gumroad.com/l/gis_in_r"><button class="custom-button">Buy on Gumroad</button></a> 262</div> 263<br> 264<p><br></p> 265<p><strong>Workshop 2: Interactive Mapping with R</strong></p> 266 267<div class="no-row-height column-margin column-container"><div class=""> 268<div class="quarto-figure quarto-figure-center"> 269<figure class="figure"> 270<p><img src="img/linked_brushing_724.gif" class="img-fluid figure-img"></p> 271<figcaption>A linked interactive map and chart with ggiraph - no Shiny required!</figcaption> 272</figure> 273</div> 274</div></div><p>In this workshop, you will learn how to make elegant interactive maps with minimal code using the R programming language. Youâll gain experience with a range of libraries for interactive mapping in R; learn how to customize and share interactive maps; and dynamically link maps and charts using R.</p> 275<div class="center"> 276 <a href="https://walkerdata.gumroad.com/l/interactive-mapping"><button class="custom-button">Buy on Gumroad</button></a> 277</div> 278<br> 279<p><br></p> 280</section> 281<section id="census-workshops" class="level2 page-columns page-full"> 282<h2 class="anchored" data-anchor-id="census-workshops">2020 Census workshops</h2> 283<p>Learn all about using the new 2020 Decennial Census data with R and tidycensus in 5 hours of custom workshops. You can purchase each workshop separately, or buy the workshop bundle and save $100!</p> 284<div class="center"> 285 <a href="https://walkerdata.gumroad.com/l/census-workshop-bundle"><button class="custom-button">Buy the bundle on Gumroad</button></a> 286</div> 287<br> 288<p><br></p> 289<p>Workshop 1: <strong>Access and Analyze the New 2020 Decennial Census Data</strong></p> 290 291<div class="no-row-height column-margin column-container"><div class=""> 292<div class="quarto-figure quarto-figure-center"> 293<figure class="figure"> 294<p><img src="img/image1.png" class="img-fluid figure-img"></p> 295<figcaption>Population pyramid for Lane County, OR</figcaption> 296</figure> 297</div> 298</div></div><p>In this workshop, youâll learn how to analyze the 2020 Decennial Censusâs Demographic and Housing Characteristics file using R and the tidycensus R package. Topics covered will include brand-new functionality in tidycensus to acquire and process data from the 2020 DHC file to help you analyze the data right away. Youâll gain experience with the tidyverse ecosystem of tools to reveal insights in the new data, and youâll learn how to create stunning data visualizations to communicate those insights.</p> 299<div class="center"> 300 <a href="https://walkerdata.gumroad.com/l/analyze-2020-census"><button class="custom-button">Buy on Gumroad</button></a> 301</div> 302<br> 303<p><br></p> 304<p>Workshop 2: <strong>Mapping and Spatial Analysis with the 2020 Decennial Census</strong></p> 305 306<div class="no-row-height column-margin column-container"><div class=""> 307<div class="quarto-figure quarto-figure-center"> 308<figure class="figure"> 309<p><img src="img/image2.png" class="img-fluid figure-img"></p> 310<figcaption>Map of same-sex households in San Francisco</figcaption> 311</figure> 312</div> 313</div></div><p>Maps are powerful tools for communicating insights in the 2020 DHC data. In this workshop, youâll learn how to use R and tidycensus to get spatial Census data seamlessly, and visualize the new data with a range of cartographic tools. Youâll also learn all about Census geography and how to perform powerful spatial analyses with 2020 Census data.</p> 314<div class="center"> 315 <a href="https://walkerdata.gumroad.com/l/mapping-2020-census"><button class="custom-button">Buy on Gumroad</button></a> 316</div> 317<br> 318<p><br></p> 319<p>You get the following materials with your workshop purchase:</p> 320<ul> 321<li><p>Unlimited access to the workshop videos</p></li> 322<li><p>Custom tutorials on working with Census data not posted anywhere else</p></li> 323</ul> 324<p>For updates on these workshops and more data analysis content, subscribe to the Walker Data mailing list:</p> 325<!-- Begin MailChimp Signup Form --> 326<link href="https://cdn-images.mailchimp.com/embedcode/slim-10_7.css" rel="stylesheet" type="text/css"> 327<style type="text/css"> 328#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; } 329/* Add your own MailChimp form style overrides in your site stylesheet or in this style block. 330We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */ 331</style> 332<div id="mc_embed_signup"> 333<form action="https://github.us15.list-manage.com/subscribe/post?u=1829a68a5eda3d301119fdcd6&id=c4a53d2961" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate=""> 334<div id="mc_embed_signup_scroll"> 335 336<input type="email" value="" name="EMAIL" class="email" id="mce-EMAIL" placeholder="email address" required=""> 337<!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups--> 338<div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_1829a68a5eda3d301119fdcd6_c4a53d2961" tabindex="-1" value=""></div> 339<div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div> 340</div> 341</form> 342</div> 343 344 345<!--End mc_embed_signup--> 346<style> 347 a { 348 color: #1a730f; 349 } 350 351 .btn-outline-dark:hover { 352 color: #fff; 353 background-color: #1a730f; 354 border-color: #343a40; 355 } 356</style> 357 358 359</section> 360 361</main> <!-- /main -->
362<script id="quarto-html-after-body" type="application/javascript"> 363 window.document.addEventListener("DOMContentLoaded", function (event) { 364 const icon = "î§"; 365 const anchorJS = new window.AnchorJS(); 366 anchorJS.options = { 367 placement: 'right', 368 icon: icon 369 }; 370 anchorJS.add('.anchored'); 371 const isCodeAnnotation = (el) => { 372 for (const clz of el.classList) { 373 if (clz.startsWith('code-annotation-')) { 374 return true; 375 } 376 } 377 return false; 378 } 379 const onCopySuccess = function(e) { 380 // button target 381 const button = e.trigger; 382 // don't keep focus 383 button.blur(); 384 // flash "checked" 385 button.classList.add('code-copy-button-checked'); 386 var currentTitle = button.getAttribute("title"); 387 button.setAttribute("title", "Copied!"); 388 let tooltip; 389 if (window.bootstrap) { 390 button.setAttribute("data-bs-toggle", "tooltip"); 391 button.setAttribute("data-bs-placement", "left"); 392 button.setAttribute("data-bs-title", "Copied!"); 393 tooltip = new bootstrap.Tooltip(button, 394 { trigger: "manual", 395 customClass: "code-copy-button-tooltip", 396 offset: [0, -8]}); 397 tooltip.show(); 398 } 399 setTimeout(function() { 400 if (tooltip) { 401 tooltip.hide(); 402 button.removeAttribute("data-bs-title"); 403 button.removeAttribute("data-bs-toggle"); 404 button.removeAttribute("data-bs-placement"); 405 } 406 button.setAttribute("title", currentTitle); 407 button.classList.remove('code-copy-button-checked'); 408 }, 1000); 409 // clear code selection 410 e.clearSelection(); 411 } 412 const getTextToCopy = function(trigger) { 413 const codeEl = trigger.previousElementSibling.cloneNode(true); 414 for (const childEl of codeEl.children) { 415 if (isCodeAnnotation(childEl)) { 416 childEl.remove(); 417 } 418 } 419 return codeEl.innerText; 420 } 421 const clipboard = new window.ClipboardJS('.code-copy-button:not([data-in-quarto-modal])', { 422 text: getTextToCopy 423 }); 424 clipboard.on('success', onCopySuccess); 425 if (window.document.getElementById('quarto-embedded-source-code-modal')) { 426 const clipboardModal = new window.ClipboardJS('.code-copy-button[data-in-quarto-modal]', { 427 text: getTextToCopy, 428 container: window.document.getElementById('quarto-embedded-source-code-modal') 429 }); 430 clipboardModal.on('success', onCopySuccess); 431 } 432 var localhostRegex = new RegExp(/^(?:http|https):\/\/localhost\:?[0-9]*\//); 433 var mailtoRegex = new RegExp(/^mailto:/); 434 var filterRegex = new RegExp("https:\/\/walker-data\.com"); 435 var isInternal = (href) => { 436 return filterRegex.test(href) || localhostRegex.test(href) || mailtoRegex.test(href); 437 } 438 // Inspect non-navigation links and adorn them if external 439 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)'); 440 for (var i=0; i<links.length; i++) { 441 const link = links[i]; 442 if (!isInternal(link.href)) { 443 // undo the damage that might have been done by quarto-nav.js in the case of 444 // links that we want to consider external 445 if (link.dataset.originalHref !== undefined) { 446 link.href = link.dataset.originalHref; 447 } 448 } 449 } 450 function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) { 451 const config = { 452 allowHTML: true, 453 maxWidth: 500, 454 delay: 100, 455 arrow: false, 456 appendTo: function(el) { 457 return el.parentElement; 458 }, 459 interactive: true, 460 interactiveBorder: 10, 461 theme: 'quarto', 462 placement: 'bottom-start', 463 }; 464 if (contentFn) { 465 config.content = contentFn; 466 } 467 if (onTriggerFn) { 468 config.onTrigger = onTriggerFn; 469 } 470 if (onUntriggerFn) { 471 config.onUntrigger = onUntriggerFn; 472 } 473 window.tippy(el, config); 474 } 475 const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]'); 476 for (var i=0; i<noterefs.length; i++) { 477 const ref = noterefs[i]; 478 tippyHover(ref, function() { 479 // use id or data attribute instead here 480 let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href'); 481 try { href = new URL(href).hash; } catch {} 482 const id = href.replace(/^#\/?/, ""); 483 const note = window.document.getElementById(id); 484 if (note) { 485 return note.innerHTML; 486 } else { 487 return ""; 488 } 489 }); 490 } 491 const xrefs = window.document.querySelectorAll('a.quarto-xref'); 492 const processXRef = (id, note) => { 493 // Strip column container classes 494 const stripColumnClz = (el) => { 495 el.classList.remove("page-full", "page-columns"); 496 if (el.children) { 497 for (const child of el.children) {
498 stripColumnClz(child); 499 } 500 } 501 } 502 stripColumnClz(note) 503 if (id === null || id.startsWith('sec-')) { 504 // Special case sections, only their first couple elements 505 const container = document.createElement("div"); 506 if (note.children && note.children.length > 2) { 507 container.appendChild(note.children[0].cloneNode(true)); 508 for (let i = 1; i < note.children.length; i++) { 509 const child = note.children[i]; 510 if (child.tagName === "P" && child.innerText === "") { 511 continue; 512 } else { 513 container.appendChild(child.cloneNode(true)); 514 break; 515 } 516 } 517 if (window.Quarto?.typesetMath) { 518 window.Quarto.typesetMath(container); 519 } 520 return container.innerHTML 521 } else { 522 if (window.Quarto?.typesetMath) { 523 window.Quarto.typesetMath(note); 524 } 525 return note.innerHTML; 526 } 527 } else { 528 // Remove any anchor links if they are present 529 const anchorLink = note.querySelector('a.anchorjs-link'); 530 if (anchorLink) { 531 anchorLink.remove(); 532 } 533 if (window.Quarto?.typesetMath) { 534 window.Quarto.typesetMath(note); 535 } 536 if (note.classList.contains("callout")) { 537 return note.outerHTML; 538 } else { 539 return note.innerHTML; 540 } 541 } 542 } 543 for (var i=0; i<xrefs.length; i++) { 544 const xref = xrefs[i]; 545 tippyHover(xref, undefined, function(instance) { 546 instance.disable(); 547 let url = xref.getAttribute('href'); 548 let hash = undefined; 549 if (url.startsWith('#')) { 550 hash = url; 551 } else { 552 try { hash = new URL(url).hash; } catch {} 553 } 554 if (hash) { 555 const id = hash.replace(/^#\/?/, ""); 556 const note = window.document.getElementById(id); 557 if (note !== null) { 558 try { 559 const html = processXRef(id, note.cloneNode(true)); 560 instance.setContent(html); 561 } finally { 562 instance.enable(); 563 instance.show(); 564 } 565 } else { 566 // See if we can fetch this 567 fetch(url.split('#')[0]) 568 .then(res => res.text()) 569 .then(html => { 570 const parser = new DOMParser(); 571 const htmlDoc = parser.parseFromString(html, "text/html"); 572 const note = htmlDoc.getElementById(id); 573 if (note !== null) { 574 const html = processXRef(id, note); 575 instance.setContent(html); 576 } 577 }).finally(() => { 578 instance.enable(); 579 instance.show(); 580 }); 581 } 582 } else { 583 // See if we can fetch a full url (with no hash to target) 584 // This is a special case and we should probably do some content thinning / targeting 585 fetch(url) 586 .then(res => res.text()) 587 .then(html => { 588 const parser = new DOMParser(); 589 const htmlDoc = parser.parseFromString(html, "text/html"); 590 const note = htmlDoc.querySelector('main.content'); 591 if (note !== null) { 592 // This should only happen for chapter cross references 593 // (since there is no id in the URL) 594 // remove the first header 595 if (note.children.length > 0 && note.children[0].tagName === "HEADER") { 596 note.children[0].remove(); 597 } 598 const html = processXRef(null, note); 599 instance.setContent(html); 600 } 601 }).finally(() => { 602 instance.enable(); 603 instance.show(); 604 }); 605 } 606 }, function(instance) { 607 }); 608 } 609 let selectedAnnoteEl; 610 const selectorForAnnotation = ( cell, annotation) => { 611 let cellAttr = 'data-code-cell="' + cell + '"'; 612 let lineAttr = 'data-code-annotation="' + annotation + '"'; 613 const selector = 'span[' + cellAttr + '][' + lineAttr + ']'; 614 return selector; 615 } 616 const selectCodeLines = (annoteEl) => { 617 const doc = window.document; 618 const targetCell = annoteEl.getAttribute("data-target-cell"); 619 const targetAnnotation = annoteEl.getAttribute("data-target-annotation"); 620 const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation)); 621 const lines = annoteSpan.getAttribute("data-code-lines").split(","); 622 const lineIds = lines.map((line) => { 623 return targetCell + "-" + line; 624 }) 625 let top = null; 626 let height = null; 627 let parent = null; 628 if (lineIds.length > 0) { 629 //compute the position of the single el (top and bottom and make a div) 630 const el = window.document.getElementById(lineIds[0]); 631 top = el.offsetTop; 632 height = el.offsetHeight; 633 parent = el.parentElement.parentElement; 634 if (lineIds.length > 1) { 635 const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]); 636 const bottom = lastEl.offsetTop + lastEl.offsetHeight; 637 height = bottom - top; 638 } 639 if (top !== null && height !== null && parent !== null) { 640 // cook up a div (if necessary) and position it 641 let div = window.document.getElementById("code-annotation-line-highlight"); 642 if (div === null) { 643 div = window.document.createElement("div"); 644 div.setAttribute("id", "code-annotation-line-highlight"); 645 div.style.position = 'absolute'; 646 parent.appendChild(div); 647 } 648 div.style.top = top - 2 + "px"; 649 div.style.height = height + 4 + "px"; 650 div.style.left = 0; 651 let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter"); 652 if (gutterDiv === null) { 653 gutterDiv = window.document.createElement("div"); 654 gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter"); 655 gutterDiv.style.position = 'absolute'; 656 const codeCell = window.document.getElementById(targetCell); 657 const gutter = codeCell.querySelector('.code-annotation-gutter'); 658 gutter.appendChild(gutterDiv); 659 } 660 gutterDiv.style.top = top - 2 + "px"; 661 gutterDiv.style.height = height + 4 + "px"; 662 } 663 selectedAnnoteEl = annoteEl; 664 } 665 }; 666 const unselectCodeLines = () => { 667 const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"]; 668 elementsIds.forEach((elId) => { 669 const div = window.document.getElementById(elId); 670 if (div) { 671 div.remove(); 672 } 673 }); 674 selectedAnnoteEl = undefined; 675 }; 676 // Handle positioning of the toggle 677 window.addEventListener( 678 "resize", 679 throttle(() => { 680 elRect = undefined; 681 if (selectedAnnoteEl) { 682 selectCodeLines(selectedAnnoteEl); 683 } 684 }, 10) 685 ); 686 function throttle(fn, ms) { 687 let throttle = false;
688 let timer; 689 return (...args) => { 690 if(!throttle) { // first call gets through 691 fn.apply(this, args); 692 throttle = true; 693 } else { // all the others get throttled 694 if(timer) clearTimeout(timer); // cancel #2 695 timer = setTimeout(() => { 696 fn.apply(this, args); 697 timer = throttle = false; 698 }, ms); 699 } 700 }; 701 } 702 // Attach click handler to the DT 703 const annoteDls = window.document.querySelectorAll('dt[data-target-cell]'); 704 for (const annoteDlNode of annoteDls) { 705 annoteDlNode.addEventListener('click', (event) => { 706 const clickedEl = event.target; 707 if (clickedEl !== selectedAnnoteEl) { 708 unselectCodeLines(); 709 const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active'); 710 if (activeEl) { 711 activeEl.classList.remove('code-annotation-active'); 712 } 713 selectCodeLines(clickedEl); 714 clickedEl.classList.add('code-annotation-active'); 715 } else { 716 // Unselect the line 717 unselectCodeLines(); 718 clickedEl.classList.remove('code-annotation-active'); 719 } 720 }); 721 } 722 const findCites = (el) => { 723 const parentEl = el.parentElement; 724 if (parentEl) { 725 const cites = parentEl.dataset.cites; 726 if (cites) { 727 return { 728 el, 729 cites: cites.split(' ') 730 }; 731 } else { 732 return findCites(el.parentElement) 733 } 734 } else { 735 return undefined; 736 } 737 }; 738 var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]'); 739 for (var i=0; i<bibliorefs.length; i++) { 740 const ref = bibliorefs[i]; 741 const citeInfo = findCites(ref); 742 if (citeInfo) { 743 tippyHover(citeInfo.el, function() { 744 var popup = window.document.createElement('div'); 745 citeInfo.cites.forEach(function(cite) { 746 var citeDiv = window.document.createElement('div'); 747 citeDiv.classList.add('hanging-indent'); 748 citeDiv.classList.add('csl-entry'); 749 var biblioDiv = window.document.getElementById('ref-' + cite); 750 if (biblioDiv) { 751 citeDiv.innerHTML = biblioDiv.innerHTML; 752 } 753 popup.appendChild(citeDiv); 754 }); 755 return popup.innerHTML; 756 }); 757 } 758 } 759 }); 760 </script>
760 761</div> <!-- /content --> 762 763 764 765 766</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.