PageSourceSearch

https://www.davidsanson.com/

html davidsanson.com collected 2026-10-03 20:45:27 UTC 35,017 bytes, 838 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.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 &amp; 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      &nbsp;
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      &nbsp;
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.