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.10.18"> 6 7<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> 8 9 10<title>David Sanson</title> 11<style> 12/* Default styles provided by pandoc. 13** See https://pandoc.org/MANUAL.html#variables-for-html for config info. 14*/ 15span.smallcaps{font-variant: small-caps;} 16div.columns{display: flex; gap: 1.5em;} 17div.column{flex: auto;} 18@media screen { 19div.columns{gap: min(4vw, 1.5em);} 20div.column{overflow-x: auto;} 21} 22div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} 23ul.task-list{list-style: none;} 24ul.task-list li input[type="checkbox"] { 25 width: 0.8em; 26 margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */ 27 vertical-align: middle; 28} 29</style> 30 31
32<script src="site_libs/quarto-nav/quarto-nav.js"></script>
vendor: 1 bytes, line 32
32
33<script src="site_libs/quarto-nav/headroom.min.js"></script>
vendor: 1 bytes, line 33
33
34<script src="site_libs/clipboard/clipboard.min.js"></script>
vendor: 1 bytes, line 34
34
35<script src="site_libs/quarto-search/autocomplete.umd.js"></script>
vendor: 1 bytes, line 35
35
36<script src="site_libs/quarto-search/fuse.min.js"></script>
vendor: 1 bytes, line 36
36
37<script src="site_libs/quarto-search/quarto-search.js"></script>
37 38<meta name="quarto:offset" content="./"> 39<link href="./media/favicon.png" rel="icon" type="image/png">
40<script src="site_libs/quarto-html/quarto.js" type="module"></script>
vendor: 1 bytes, line 40
40
41<script src="site_libs/quarto-html/tabsets/tabsets.js" type="module"></script>
vendor: 1 bytes, line 41
41
42<script src="site_libs/quarto-html/popper.min.js"></script>
vendor: 1 bytes, line 42
42
43<script src="site_libs/quarto-html/tippy.umd.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-3419ea62b3ca2d77d3d547f86109e84e.css" rel="stylesheet" class="quarto-color-scheme" id="quarto-text-highlighting-styles"> 46<link href="site_libs/quarto-html/quarto-syntax-highlighting-dark-eaeb381c6b7494c50009502241a4985c.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-3419ea62b3ca2d77d3d547f86109e84e.css" rel="stylesheet" class="quarto-color-scheme-extra" id="quarto-text-highlighting-styles">
48<script src="site_libs/quarto-axe/axe.min.js"></script>
vendor: 1 bytes, line 48
48
49<script src="site_libs/quarto-axe/axe-check.js" type="module"></script>
vendor: 1 bytes, line 49
49
50<script id="quarto-axe-checker-options" type="text/plain">dHJ1ZQ==</script>
vendor: 1 bytes, line 50
50
51<script src="site_libs/bootstrap/bootstrap.min.js"></script>
51 52<link href="site_libs/bootstrap/bootstrap-icons.css" rel="stylesheet"> 53<link href="site_libs/bootstrap/bootstrap-6cb147f6c25978a29d143570bd7ff446.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme" id="quarto-bootstrap" data-mode="light"> 54<link href="site_libs/bootstrap/bootstrap-dark-7676e85f6f2407f7e4fb494747d0185a.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme quarto-color-alternate" id="quarto-bootstrap" data-mode="dark"> 55<link href="site_libs/bootstrap/bootstrap-6cb147f6c25978a29d143570bd7ff446.min.css" rel="stylesheet" append-hash="true" class="quarto-color-scheme-extra" id="quarto-bootstrap" data-mode="light">
56<script src="site_libs/quarto-contrib/iconify-3.0.0/iconify-icon.min.js"></script>
vendor: 1 bytes, line 56
56
57<script src="site_libs/quarto-contrib/glightbox/glightbox.min.js"></script>
57 58<link href="site_libs/quarto-contrib/glightbox/glightbox.min.css" rel="stylesheet"> 59<link href="site_libs/quarto-contrib/glightbox/lightbox.css" rel="stylesheet">
60<script id="quarto-search-options" type="application/json">{ 61 "location": "navbar", 62 "copy-button": false, 63 "collapse-after": 3, 64 "panel-placement": "end", 65 "type": "overlay", 66 "limit": 50, 67 "keyboard-shortcut": [ 68 "f", 69 "/", 70 "s" 71 ], 72 "show-item-context": false, 73 "language": { 74 "search-no-results-text": "No results", 75 "search-matching-documents-text": "matching documents", 76 "search-copy-link-title": "Copy link to search", 77 "search-hide-matches-text": "Hide additional matches", 78 "search-more-match-text": "more match in this document", 79 "search-more-matches-text": "more matches in this document", 80 "search-clear-button-title": "Clear", 81 "search-text-placeholder": "", 82 "search-detached-cancel-button-title": "Cancel", 83 "search-submit-button-title": "Submit", 84 "search-label": "Search" 85 } 86}</script>
86 87 88 89<link rel="stylesheet" href="styles.css"> 90<meta property="og:title" content="David Sanson"> 91<meta property="og:image" content="https://www.davidsanson.com/media/david.jpg"> 92<meta property="og:site_name" content="David Sanson"> 93<meta property="og:image:alt" content="Me and my dog"> 94<meta name="twitter:title" content="David Sanson"> 95<meta name="twitter:image" content="https://www.davidsanson.com/media/david.jpg"> 96<meta name="twitter:image:alt" content="Me and my dog"> 97<meta name="twitter:card" content="summary_large_image"> 98</head> 99 100<body class="nav-fixed fullcontent quarto-light">
100<script id="quarto-html-before-body" type="application/javascript"> 101 const toggleBodyColorMode = (bsSheetEl) => { 102 const mode = bsSheetEl.getAttribute("data-mode"); 103 const bodyEl = window.document.querySelector("body"); 104 if (mode === "dark") { 105 bodyEl.classList.add("quarto-dark"); 106 bodyEl.classList.remove("quarto-light"); 107 } else { 108 bodyEl.classList.add("quarto-light"); 109 bodyEl.classList.remove("quarto-dark"); 110 } 111 } 112 const toggleBodyColorPrimary = () => { 113 const bsSheetEl = window.document.querySelector("link#quarto-bootstrap:not([rel=disabled-stylesheet])"); 114 if (bsSheetEl) { 115 toggleBodyColorMode(bsSheetEl); 116 } 117 } 118 const setColorSchemeToggle = (alternate) => { 119 const toggles = window.document.querySelectorAll('.quarto-color-scheme-toggle'); 120 for (let i=0; i < toggles.length; i++) { 121 const toggle = toggles[i]; 122 if (toggle) { 123 if (alternate) { 124 toggle.classList.add("alternate"); 125 } else { 126 toggle.classList.remove("alternate"); 127 } 128 } 129 } 130 }; 131 const toggleColorMode = (alternate) => { 132 // Switch the stylesheets 133 const primaryStylesheets = window.document.querySelectorAll('link.quarto-color-scheme:not(.quarto-color-alternate)'); 134 const alternateStylesheets = window.document.querySelectorAll('link.quarto-color-scheme.quarto-color-alternate'); 135 manageTransitions('#quarto-margin-sidebar .nav-link', false); 136 if (alternate) { 137 // note: dark is layered on light, we don't disable primary! 138 enableStylesheet(alternateStylesheets); 139 for (const sheetNode of alternateStylesheets) { 140 if (sheetNode.id === "quarto-bootstrap") { 141 toggleBodyColorMode(sheetNode); 142 } 143 } 144 } else { 145 disableStylesheet(alternateStylesheets); 146 enableStylesheet(primaryStylesheets) 147 toggleBodyColorPrimary(); 148 } 149 manageTransitions('#quarto-margin-sidebar .nav-link', true); 150 // Switch the toggles 151 setColorSchemeToggle(alternate) 152 // Hack to workaround the fact that safari doesn't 153 // properly recolor the scrollbar when toggling (#1455) 154 if (navigator.userAgent.indexOf('Safari') > 0 && navigator.userAgent.indexOf('Chrome') == -1) { 155 manageTransitions("body", false); 156 window.scrollTo(0, 1); 157 setTimeout(() => { 158 window.scrollTo(0, 0); 159 manageTransitions("body", true); 160 }, 40); 161 } 162 } 163 const disableStylesheet = (stylesheets) => { 164 for (let i=0; i < stylesheets.length; i++) { 165 const stylesheet = stylesheets[i]; 166 stylesheet.rel = 'disabled-stylesheet'; 167 } 168 } 169 const enableStylesheet = (stylesheets) => { 170 for (let i=0; i < stylesheets.length; i++) { 171 const stylesheet = stylesheets[i]; 172 if(stylesheet.rel !== 'stylesheet') { // for Chrome, which will still FOUC without this check 173 stylesheet.rel = 'stylesheet'; 174 } 175 } 176 } 177 const manageTransitions = (selector, allowTransitions) => { 178 const els = window.document.querySelectorAll(selector); 179 for (let i=0; i < els.length; i++) { 180 const el = els[i]; 181 if (allowTransitions) { 182 el.classList.remove('notransition'); 183 } else { 184 el.classList.add('notransition'); 185 } 186 } 187 } 188 const isFileUrl = () => { 189 return window.location.protocol === 'file:'; 190 } 191 const hasAlternateSentinel = () => { 192 let styleSentinel = getColorSchemeSentinel(); 193 if (styleSentinel !== null) { 194 return styleSentinel === "alternate"; 195 } else { 196 return false; 197 } 198 } 199 const setStyleSentinel = (alternate) => { 200 const value = alternate ? "alternate" : "default"; 201 if (!isFileUrl()) { 202 window.localStorage.setItem("quarto-color-scheme", value); 203 } else { 204 localAlternateSentinel = value; 205 } 206 } 207 const getColorSchemeSentinel = () => { 208 if (!isFileUrl()) { 209 const storageValue = window.localStorage.getItem("quarto-color-scheme"); 210 return storageValue != null ? storageValue : localAlternateSentinel; 211 } else { 212 return localAlternateSentinel; 213 } 214 } 215 const toggleGiscusIfUsed = (isAlternate, darkModeDefault) => { 216 const baseTheme = document.querySelector('#giscus-base-theme')?.value ?? 'light'; 217 const alternateTheme = document.querySelector('#giscus-alt-theme')?.value ?? 'dark'; 218 let newTheme = ''; 219 if(authorPrefersDark) { 220 newTheme = isAlternate ? baseTheme : alternateTheme; 221 } else { 222 newTheme = isAlternate ? alternateTheme : baseTheme; 223 } 224 const changeGiscusTheme = () => { 225 // From: https://github.com/giscus/giscus/issues/336 226 const sendMessage = (message) => { 227 const iframe = document.querySelector('iframe.giscus-frame'); 228 if (!iframe) return; 229 iframe.contentWindow.postMessage({ giscus: message }, 'https://giscus.app'); 230 } 231 sendMessage({ 232 setConfig: { 233 theme: newTheme 234 } 235 }); 236 } 237 const isGiscussLoaded = window.document.querySelector('iframe.giscus-frame') !== null; 238 if (isGiscussLoaded) { 239 changeGiscusTheme(); 240 } 241 }; 242 const authorPrefersDark = false;
243 const queryPrefersDark = window.matchMedia('(prefers-color-scheme: dark)'); 244 const darkModeDefault = queryPrefersDark.matches; 245 document.querySelector('link#quarto-text-highlighting-styles.quarto-color-scheme-extra').rel = 'disabled-stylesheet'; 246 document.querySelector('link#quarto-bootstrap.quarto-color-scheme-extra').rel = 'disabled-stylesheet'; 247 let localAlternateSentinel = darkModeDefault ? 'alternate' : 'default'; 248 // Dark / light mode switch 249 window.quartoToggleColorScheme = () => { 250 // Read the current dark / light value 251 let toAlternate = !hasAlternateSentinel(); 252 toggleColorMode(toAlternate); 253 setStyleSentinel(toAlternate); 254 toggleGiscusIfUsed(toAlternate, darkModeDefault); 255 window.dispatchEvent(new Event('resize')); 256 }; 257 queryPrefersDark.addEventListener("change", e => { 258 if(window.localStorage.getItem("quarto-color-scheme") !== null) 259 return; 260 const alternate = e.matches 261 toggleColorMode(alternate); 262 localAlternateSentinel = e.matches ? 'alternate' : 'default'; // this is used alongside local storage! 263 toggleGiscusIfUsed(alternate, darkModeDefault); 264 }); 265 // Switch to dark mode if need be 266 if (hasAlternateSentinel()) { 267 toggleColorMode(true); 268 } else { 269 toggleColorMode(false); 270 } 271 </script>
271 272 273<div id="quarto-search-results"></div> 274 <header id="quarto-header" class="headroom fixed-top"> 275 <nav class="navbar navbar-expand-lg " data-bs-theme="dark"> 276 <div class="navbar-container container-fluid"> 277 <div class="navbar-brand-container mx-auto"> 278 <a class="navbar-brand" href="./index.html"> 279 <span class="navbar-title">David Sanson</span> 280 </a> 281 </div> 282 <div id="quarto-search" class="" title="Search"></div> 283 <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(); }"> 284 <span class="navbar-toggler-icon"></span> 285</button> 286 <div class="collapse navbar-collapse" id="navbarCollapse"> 287 <ul class="navbar-nav navbar-nav-scroll me-auto"> 288 <li class="nav-item"> 289 <a class="nav-link active" href="./index.html" aria-current="page"> 290<span class="menu-text">Home</span></a> 291 </li> 292 <li class="nav-item"> 293 <a class="nav-link" href="./research.html"> 294<span class="menu-text">Research</span></a> 295 </li> 296 <li class="nav-item"> 297 <a class="nav-link" href="./teaching.html"> 298<span class="menu-text">Teaching</span></a> 299 </li> 300 <li class="nav-item"> 301 <a class="nav-link" href="./stuff.html"> 302<span class="menu-text">Other Stuff</span></a> 303 </li> 304 <li class="nav-item"> 305 <a class="nav-link" href="./cv.html"> 306<span class="menu-text">CV</span></a> 307 </li> 308</ul> 309 </div> <!-- /navcollapse --> 310 <div class="quarto-navbar-tools"> 311 <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> 312</div> 313 </div> <!-- /container-fluid --> 314 </nav> 315</header> 316<!-- content --> 317<div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-article page-navbar"> 318<!-- sidebar --> 319<!-- margin-sidebar --> 320 321<!-- main --> 322<main class="content" id="quarto-document-content"> 323 324 325 326 327 328<div class="quarto-about-trestles"> 329 <div class="about-entity"> 330 <img src="media/david.jpg" alt="Me and my dog" class="about-image 331 rounded " style="width: 20em;"> 332<header id="title-block-header" class="quarto-title-block default"> 333<div class="quarto-title"> 334<h1 class="title">David Sanson</h1> 335</div> 336<div class="quarto-title-meta"> 337 </div> 338</header> 339 <div class="about-links"> 340 <a href="https://philpeople.org/profiles/david-sanson" class="about-link" rel="" target=""> 341 <span class="about-link-text"><iconify-icon role="img" inline="" icon="academicons:philpapers" aria-label="Icon philpapers from academicons Iconify.design set." title="Icon philpapers from academicons Iconify.design set."></iconify-icon> PhilPeople</span> 342 </a> 343 <a href="https://github.com/dsanson" class="about-link" rel="" target=""> 344 <i class="bi bi-github"></i> 345 <span class="about-link-text">Github</span> 346 </a> 347 <a href="/[email protected]" class="about-link" rel="" target=""> 348 <i class="bi bi-envelope"></i> 349 <span class="about-link-text">Email</span> 350 </a> 351</div> 352 </div> 353 <div class="about-contents"><div id="about-me"> 354<p>I am an associate professor in the <a href="http://philosophy.illinoisstate.edu">Philosophy Department</a> at <a href="http://illinoisstate.edu">Illinois State University</a>.</p> 355<p>I have research interests in metaphysics, philosophy of language, the history of logic, medieval philosophy, and the philosophy of art. I have published work on the history of the Liar Paradox, time and change, essence and modality, and the ontological status of fictional characters.</p> 356<p><a href="https://www.pitzer.edu/ahmed-alwishah">Ahmed Alwishah</a> and I wrote a book on the history of the Liar Paradox, <em>The Final Word: al-DawÄnÄ« and the Liar Paradox in the Islamic World</em>. You can read the first 20 pages or so for free on <a href="https://www.google.com/books/edition/The_Final_Word/bl2m0QEACAAJ">Google Books</a>. You can find it in a library using <a href="https://search.worldcat.org/title/The-final-word-:-al-Dawani-and-the-Liar-Paradox-in-the-Islamic-world/oclc/1551490690">WorldCat</a>. You can buy it from <a href="https://global.oup.com/academic/product/the-final-word-9780197609941">Oxford University Press</a>, <a href="https://bookshop.org/p/books/the-final-word-al-dawani-and-the-liar-paradox-in-the-islamic-world-associate-professor-of-philosophy-david-sanson/40159cb5696aae38">Bookshop.org</a>, <a href="https://www.barnesandnoble.com/w/the-final-word-ahmed-alwishah/1148562805">Barnes & Noble</a>, or <a href="https://www.amazon.com/Final-Word-al-Dawani-Paradox-Islamic/dp/0197609945">Amazon</a>.</p> 357<div class="quarto-figure quarto-figure-center"> 358<figure class="figure"> 359<p><a href="media/final-word-cover.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="media/final-word-cover.jpg" class="img-fluid quarto-figure quarto-figure-center figure-img" style="width:15em" alt="Cover of our book, The Final Word"></a></p> 360</figure> 361</div> 362<p>We also have an entry on the topic in the <em><a href="https://plato.stanford.edu/entries/arabic-islamic-liar/">Stanford Encyclopedia of Philosophy</a></em>.</p> 363<div class="line-block">341 Stevenson Hall<br> 364Department of Philosophy (4540)<br> 365Illinois State University<br> 366Normal, IL 61790</div> 367</div></div> 368</div> 369 370 371 372 373 374</main> <!-- /main -->
375<script id="quarto-html-after-body" type="application/javascript"> 376 window.document.addEventListener("DOMContentLoaded", function (event) { 377 // Ensure there is a toggle, if there isn't float one in the top right 378 if (window.document.querySelector('.quarto-color-scheme-toggle') === null) { 379 const a = window.document.createElement('a'); 380 a.classList.add('top-right'); 381 a.classList.add('quarto-color-scheme-toggle'); 382 a.href = ""; 383 a.onclick = function() { try { window.quartoToggleColorScheme(); } catch {} return false; }; 384 const i = window.document.createElement("i"); 385 i.classList.add('bi'); 386 a.appendChild(i); 387 window.document.body.appendChild(a); 388 } 389 setColorSchemeToggle(hasAlternateSentinel()) 390 const isCodeAnnotation = (el) => { 391 for (const clz of el.classList) { 392 if (clz.startsWith('code-annotation-')) { 393 return true; 394 } 395 } 396 return false; 397 } 398 const onCopySuccess = function(e) { 399 // button target 400 const button = e.trigger; 401 // don't keep focus 402 button.blur(); 403 // flash "checked" 404 button.classList.add('code-copy-button-checked'); 405 var currentTitle = button.getAttribute("title"); 406 button.setAttribute("title", "Copied!"); 407 let tooltip; 408 if (window.bootstrap) { 409 button.setAttribute("data-bs-toggle", "tooltip"); 410 button.setAttribute("data-bs-placement", "left"); 411 button.setAttribute("data-bs-title", "Copied!"); 412 tooltip = new bootstrap.Tooltip(button, 413 { trigger: "manual", 414 customClass: "code-copy-button-tooltip", 415 offset: [0, -8]}); 416 tooltip.show(); 417 } 418 setTimeout(function() { 419 if (tooltip) { 420 tooltip.hide(); 421 button.removeAttribute("data-bs-title"); 422 button.removeAttribute("data-bs-toggle"); 423 button.removeAttribute("data-bs-placement"); 424 } 425 button.setAttribute("title", currentTitle); 426 button.classList.remove('code-copy-button-checked'); 427 }, 1000); 428 // clear code selection 429 e.clearSelection(); 430 } 431 const getTextToCopy = function(trigger) { 432 const outerScaffold = trigger.parentElement.cloneNode(true); 433 const codeEl = outerScaffold.querySelector('code'); 434 for (const childEl of codeEl.children) { 435 if (isCodeAnnotation(childEl)) { 436 childEl.remove(); 437 } 438 } 439 return codeEl.innerText; 440 } 441 const clipboard = new window.ClipboardJS('.code-copy-button:not([data-in-quarto-modal])', { 442 text: getTextToCopy 443 }); 444 clipboard.on('success', onCopySuccess); 445 if (window.document.getElementById('quarto-embedded-source-code-modal')) { 446 const clipboardModal = new window.ClipboardJS('.code-copy-button[data-in-quarto-modal]', { 447 text: getTextToCopy, 448 container: window.document.getElementById('quarto-embedded-source-code-modal') 449 }); 450 clipboardModal.on('success', onCopySuccess); 451 } 452 var localhostRegex = new RegExp(/^(?:http|https):\/\/localhost\:?[0-9]*\//); 453 var mailtoRegex = new RegExp(/^mailto:/); 454 var filterRegex = new RegExp("https:\/\/www\.davidsanson\.com"); 455 var isInternal = (href) => { 456 return filterRegex.test(href) || localhostRegex.test(href) || mailtoRegex.test(href); 457 } 458 // Inspect non-navigation links and adorn them if external 459 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)'); 460 for (var i=0; i<links.length; i++) { 461 const link = links[i]; 462 if (!isInternal(link.href)) { 463 // undo the damage that might have been done by quarto-nav.js in the case of 464 // links that we want to consider external 465 if (link.dataset.originalHref !== undefined) { 466 link.href = link.dataset.originalHref; 467 } 468 // target, if specified 469 link.setAttribute("target", "_blank"); 470 if (link.getAttribute("rel") === null) { 471 link.setAttribute("rel", "noopener"); 472 } 473 // default icon 474 link.classList.add("external"); 475 } 476 } 477 function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) { 478 const config = { 479 allowHTML: true, 480 maxWidth: 500, 481 delay: 100,
482 arrow: false, 483 appendTo: function(el) { 484 return el.parentElement; 485 }, 486 interactive: true, 487 interactiveBorder: 10, 488 theme: 'quarto', 489 placement: 'bottom-start', 490 }; 491 if (contentFn) { 492 config.content = contentFn; 493 } 494 if (onTriggerFn) { 495 config.onTrigger = onTriggerFn; 496 } 497 if (onUntriggerFn) { 498 config.onUntrigger = onUntriggerFn; 499 } 500 window.tippy(el, config); 501 } 502 const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]'); 503 for (var i=0; i<noterefs.length; i++) { 504 const ref = noterefs[i]; 505 tippyHover(ref, function() { 506 // use id or data attribute instead here 507 let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href'); 508 try { href = new URL(href).hash; } catch {} 509 const id = href.replace(/^#\/?/, ""); 510 const note = window.document.getElementById(id); 511 if (note) { 512 return note.innerHTML; 513 } else { 514 return ""; 515 } 516 }); 517 } 518 const xrefs = window.document.querySelectorAll('a.quarto-xref'); 519 const processXRef = (id, note) => { 520 // Strip column container classes 521 const stripColumnClz = (el) => { 522 el.classList.remove("page-full", "page-columns"); 523 if (el.children) { 524 for (const child of el.children) { 525 stripColumnClz(child); 526 } 527 } 528 } 529 stripColumnClz(note) 530 if (id === null || id.startsWith('sec-')) { 531 // Special case sections, only their first couple elements 532 const container = document.createElement("div"); 533 if (note.children && note.children.length > 2) { 534 container.appendChild(note.children[0].cloneNode(true)); 535 for (let i = 1; i < note.children.length; i++) { 536 const child = note.children[i]; 537 if (child.tagName === "P" && child.innerText === "") { 538 continue; 539 } else { 540 container.appendChild(child.cloneNode(true)); 541 break; 542 } 543 } 544 if (window.Quarto?.typesetMath) { 545 window.Quarto.typesetMath(container); 546 } 547 return container.innerHTML 548 } else { 549 if (window.Quarto?.typesetMath) { 550 window.Quarto.typesetMath(note); 551 } 552 return note.innerHTML; 553 } 554 } else { 555 // Remove any anchor links if they are present 556 const anchorLink = note.querySelector('a.anchorjs-link'); 557 if (anchorLink) { 558 anchorLink.remove(); 559 } 560 if (window.Quarto?.typesetMath) { 561 window.Quarto.typesetMath(note); 562 } 563 if (note.classList.contains("callout")) { 564 return note.outerHTML; 565 } else { 566 return note.innerHTML; 567 } 568 } 569 } 570 for (var i=0; i<xrefs.length; i++) { 571 const xref = xrefs[i]; 572 tippyHover(xref, undefined, function(instance) { 573 instance.disable(); 574 let url = xref.getAttribute('href'); 575 let hash = undefined; 576 if (url.startsWith('#')) { 577 hash = url; 578 } else { 579 try { hash = new URL(url).hash; } catch {} 580 } 581 if (hash) { 582 const id = hash.replace(/^#\/?/, ""); 583 const note = window.document.getElementById(id); 584 if (note !== null) { 585 try { 586 const html = processXRef(id, note.cloneNode(true)); 587 instance.setContent(html); 588 } finally { 589 instance.enable(); 590 instance.show(); 591 } 592 } else { 593 // See if we can fetch this 594 fetch(url.split('#')[0]) 595 .then(res => res.text()) 596 .then(html => { 597 const parser = new DOMParser(); 598 const htmlDoc = parser.parseFromString(html, "text/html"); 599 const note = htmlDoc.getElementById(id); 600 if (note !== null) { 601 const html = processXRef(id, note); 602 instance.setContent(html); 603 } 604 }).finally(() => { 605 instance.enable(); 606 instance.show(); 607 }); 608 } 609 } else { 610 // See if we can fetch a full url (with no hash to target)
611 // This is a special case and we should probably do some content thinning / targeting 612 fetch(url) 613 .then(res => res.text()) 614 .then(html => { 615 const parser = new DOMParser(); 616 const htmlDoc = parser.parseFromString(html, "text/html"); 617 const note = htmlDoc.querySelector('main.content'); 618 if (note !== null) { 619 // This should only happen for chapter cross references 620 // (since there is no id in the URL) 621 // remove the first header 622 if (note.children.length > 0 && note.children[0].tagName === "HEADER") { 623 note.children[0].remove(); 624 } 625 const html = processXRef(null, note); 626 instance.setContent(html); 627 } 628 }).finally(() => { 629 instance.enable(); 630 instance.show(); 631 }); 632 } 633 }, function(instance) { 634 }); 635 } 636 let selectedAnnoteEl; 637 const selectorForAnnotation = ( cell, annotation) => { 638 let cellAttr = 'data-code-cell="' + cell + '"'; 639 let lineAttr = 'data-code-annotation="' + annotation + '"'; 640 const selector = 'span[' + cellAttr + '][' + lineAttr + ']'; 641 return selector; 642 } 643 const selectCodeLines = (annoteEl) => { 644 const doc = window.document; 645 const targetCell = annoteEl.getAttribute("data-target-cell"); 646 const targetAnnotation = annoteEl.getAttribute("data-target-annotation"); 647 const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation)); 648 const lines = annoteSpan.getAttribute("data-code-lines").split(","); 649 const lineIds = lines.map((line) => { 650 return targetCell + "-" + line; 651 }) 652 let top = null; 653 let height = null; 654 let parent = null; 655 if (lineIds.length > 0) { 656 //compute the position of the single el (top and bottom and make a div) 657 const el = window.document.getElementById(lineIds[0]); 658 top = el.offsetTop; 659 height = el.offsetHeight; 660 parent = el.parentElement.parentElement; 661 if (lineIds.length > 1) { 662 const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]); 663 const bottom = lastEl.offsetTop + lastEl.offsetHeight; 664 height = bottom - top; 665 } 666 if (top !== null && height !== null && parent !== null) { 667 // cook up a div (if necessary) and position it 668 let div = window.document.getElementById("code-annotation-line-highlight"); 669 if (div === null) { 670 div = window.document.createElement("div"); 671 div.setAttribute("id", "code-annotation-line-highlight"); 672 div.style.position = 'absolute'; 673 parent.appendChild(div); 674 } 675 div.style.top = top - 2 + "px"; 676 div.style.height = height + 4 + "px"; 677 div.style.left = 0; 678 let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter"); 679 if (gutterDiv === null) { 680 gutterDiv = window.document.createElement("div"); 681 gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter"); 682 gutterDiv.style.position = 'absolute'; 683 const codeCell = window.document.getElementById(targetCell); 684 const gutter = codeCell.querySelector('.code-annotation-gutter'); 685 gutter.appendChild(gutterDiv); 686 } 687 gutterDiv.style.top = top - 2 + "px"; 688 gutterDiv.style.height = height + 4 + "px"; 689 } 690 selectedAnnoteEl = annoteEl; 691 } 692 }; 693 const unselectCodeLines = () => { 694 const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"]; 695 elementsIds.forEach((elId) => { 696 const div = window.document.getElementById(elId); 697 if (div) { 698 div.remove(); 699 } 700 }); 701 selectedAnnoteEl = undefined; 702 }; 703 // Handle positioning of the toggle 704 window.addEventListener( 705 "resize", 706 throttle(() => { 707 elRect = undefined; 708 if (selectedAnnoteEl) { 709 selectCodeLines(selectedAnnoteEl); 710 } 711 }, 10) 712 ); 713 function throttle(fn, ms) { 714 let throttle = false;
715 let timer; 716 return (...args) => { 717 if(!throttle) { // first call gets through 718 fn.apply(this, args); 719 throttle = true; 720 } else { // all the others get throttled 721 if(timer) clearTimeout(timer); // cancel #2 722 timer = setTimeout(() => { 723 fn.apply(this, args); 724 timer = throttle = false; 725 }, ms); 726 } 727 }; 728 } 729 // Attach click handler to the DT 730 const annoteDls = window.document.querySelectorAll('dt[data-target-cell]'); 731 for (const annoteDlNode of annoteDls) { 732 annoteDlNode.addEventListener('click', (event) => { 733 const clickedEl = event.target; 734 if (clickedEl !== selectedAnnoteEl) { 735 unselectCodeLines(); 736 const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active'); 737 if (activeEl) { 738 activeEl.classList.remove('code-annotation-active'); 739 } 740 selectCodeLines(clickedEl); 741 clickedEl.classList.add('code-annotation-active'); 742 } else { 743 // Unselect the line 744 unselectCodeLines(); 745 clickedEl.classList.remove('code-annotation-active'); 746 } 747 }); 748 } 749 const findCites = (el) => { 750 const parentEl = el.parentElement; 751 if (parentEl) { 752 const cites = parentEl.dataset.cites; 753 if (cites) { 754 return { 755 el, 756 cites: cites.split(' ') 757 }; 758 } else { 759 return findCites(el.parentElement) 760 } 761 } else { 762 return undefined; 763 } 764 }; 765 var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]'); 766 for (var i=0; i<bibliorefs.length; i++) { 767 const ref = bibliorefs[i]; 768 const citeInfo = findCites(ref); 769 if (citeInfo) { 770 tippyHover(citeInfo.el, function() { 771 var popup = window.document.createElement('div'); 772 citeInfo.cites.forEach(function(cite) { 773 var citeDiv = window.document.createElement('div'); 774 citeDiv.classList.add('hanging-indent'); 775 citeDiv.classList.add('csl-entry'); 776 var biblioDiv = window.document.getElementById('ref-' + cite); 777 if (biblioDiv) { 778 citeDiv.innerHTML = biblioDiv.innerHTML; 779 } 780 popup.appendChild(citeDiv); 781 }); 782 return popup.innerHTML; 783 }); 784 } 785 } 786 }); 787 </script>
787 788</div> <!-- /content --> 789<footer class="footer"> 790 <div class="nav-footer"> 791 <div class="nav-footer-left"> 792 793 </div> 794 <div class="nav-footer-center"> 795<p>© 2026 David Sanson. Created using <a href="https://quarto.org/">Quarto</a>.</p> 796<div class="toc-actions"><ul><li><a href="https://github.com/dsanson/dsanson.github.io/blob/main/index.md" class="toc-action"><i class="bi bi-github"></i>View source</a></li><li><a href="https://github.com/dsanson/dsanson.github.io/issues/new" class="toc-action"><i class="bi empty"></i>Report an issue</a></li></ul></div></div> 797 <div class="nav-footer-right"> 798 799 </div> 800 </div> 801</footer>
802<script>var lightboxQuarto = GLightbox({"closeEffect":"zoom","descPosition":"bottom","loop":false,"openEffect":"zoom","selector":".lightbox"}); 803(function() { 804 let previousOnload = window.onload; 805 window.onload = () => { 806 if (previousOnload) { 807 previousOnload(); 808 } 809 lightboxQuarto.on('slide_before_load', (data) => { 810 const { slideIndex, slideNode, slideConfig, player, trigger } = data; 811 const href = trigger.getAttribute('href'); 812 if (href !== null) { 813 const imgEl = window.document.querySelector(`a[href="${href}"] img`); 814 if (imgEl !== null) { 815 const srcAttr = imgEl.getAttribute("src"); 816 if (srcAttr && srcAttr.startsWith("data:")) { 817 slideConfig.href = srcAttr; 818 } 819 } 820 } 821 }); 822 823 lightboxQuarto.on('slide_after_load', (data) => { 824 const { slideIndex, slideNode, slideConfig, player, trigger } = data; 825 if (window.Quarto?.typesetMath) { 826 window.Quarto.typesetMath(slideNode); 827 } 828 }); 829 830 }; 831 832})(); 833 </script>
833 834 835 836 837 838</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.