123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564 |
- <!DOCTYPE html><!-- This is a valid HTML5 document. -->
- <!-- Screen readers, SEO, extensions and so on. -->
- <html lang="fr">
- <!-- Has to be within the first 1024 bytes, hence before the `title` element
- See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
- <meta charset="utf-8">
- <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
- <!-- The viewport meta is quite crowded and we are responsible for that.
- See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
- <meta name="viewport" content="width=device-width,initial-scale=1">
- <!-- Required to make a valid HTML5 document. -->
- <title>
- GPX Viewer
- — David Larlet</title>
- <script>
- function toggleTheme(themeName) {
- document.documentElement.classList.toggle(
- 'forced-dark',
- themeName === 'dark'
- )
- document.documentElement.classList.toggle(
- 'forced-light',
- themeName === 'light'
- )
- }
- const selectedTheme = localStorage.getItem('theme')
- if (selectedTheme !== 'undefined') {
- toggleTheme(selectedTheme)
- }
- </script>
- <!-- Documented, feel free to shoot an email. -->
- <link rel="stylesheet" href="/static/david/css/style_2024-03-09.css">
- <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
- <link rel="preload"
- href="/static/david/css/fonts/century_supra_ot_a_regular.woff2"
- as="font"
- type="font/woff2"
- media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
- crossorigin>
- <link rel="preload"
- href="/static/david/css/fonts/century_supra_ot_a_bold.woff2"
- as="font"
- type="font/woff2"
- media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
- crossorigin>
- <link rel="preload"
- href="/static/david/css/fonts/century_supra_ot_a_italic.woff2"
- as="font"
- type="font/woff2"
- media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"
- crossorigin>
- <link rel="preload"
- href="/static/david/css/fonts/century_supra_ot_b_regular.woff2"
- as="font"
- type="font/woff2"
- media="(prefers-color-scheme: dark)"
- crossorigin>
- <link rel="preload"
- href="/static/david/css/fonts/century_supra_ot_b_bold.woff2"
- as="font"
- type="font/woff2"
- media="(prefers-color-scheme: dark)"
- crossorigin>
- <link rel="preload"
- href="/static/david/css/fonts/century_supra_ot_b_italic.woff2"
- as="font"
- type="font/woff2"
- media="(prefers-color-scheme: dark)"
- crossorigin>
- <meta name="description" content="Suite de mes expérimentations de la veille à la fois en cartographie et en web components. Je me suis demandé s’il était possible de faire un composant qui ne dépende pas de uMap pour afficher une trace GPX. Le fait d’avoir passé une heure à trouver la bonne CSP pour arriver à afficher une carte m’a d’une certaine manière motivé (c’était une journée galère).">
- <!-- That good ol' feed, subscribe :). -->
- <link rel="alternate"
- type="application/atom+xml"
- title="Feed"
- href="/david/log/">
- <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
- <link rel="apple-touch-icon"
- sizes="180x180"
- href="/static/david/icons2/apple-touch-icon.png">
- <link rel="icon"
- type="image/png"
- sizes="32x32"
- href="/static/david/icons2/favicon-32x32.png">
- <link rel="icon"
- type="image/png"
- sizes="16x16"
- href="/static/david/icons2/favicon-16x16.png">
- <link rel="manifest" href="/static/david/icons2/site.webmanifest">
- <link rel="mask-icon"
- href="/static/david/icons2/safari-pinned-tab.svg"
- color="#07486c">
- <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
- <meta name="msapplication-TileColor" content="#f7f7f7">
- <meta name="msapplication-config"
- content="/static/david/icons2/browserconfig.xml">
- <meta name="theme-color"
- content="#f7f7f7"
- media="(prefers-color-scheme: light)">
- <meta name="theme-color"
- content="#272727"
- media="(prefers-color-scheme: dark)">
- <!-- Is that even respected? Retrospectively? What a shAItshow…
- https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ -->
- <meta name="robots" content="noai, noimageai">
- <!-- To get attribution when linking on mastodon. -->
- <meta name="fediverse:creator" content="@david@larlet.fr">
-
- <style type="text/css">
- .tippy-content {
- min-width: 280px;
- padding: .5rem;
- font-size: calc(var(--fluid-0) * 0.8);
- font-family: var(--labor-font);
- letter-spacing: initial;
- text-align: left;
- }
- .tippy-content h3 {
- margin-top: 0;
- }
- .tippy-content h3 img {
- max-width: 2rem;
- max-height: 2rem;
- display: inline-block;
- }
- .tippy-content .tippy-links {
- display: flex;
- justify-content: space-around;
- }
- .tippy-content a {
- padding: .4rem;
- color: #F06048;
- }
- </style>
-
- <body data-instant-intensity="viewport-all">
- <article>
-
- <header>
- <hgroup>
- <h1>GPX Viewer</h1>
- <p>Le <time datetime="2024-03-26">26 mars 2024</time></p>
- </hgroup>
- </header>
- <nav>
- <p>
-
- <a rel="prev"
- href="/david/2024/03/25/"
- title="Publication précédente : Inclusion">← Précédent</a> •
-
- <a href="/david/" title="Aller à l’accueil" rel="up">Accueil</a>
- •
- <a href="/david/recherche/"
- title="Aller à la page de recherche"
- rel="search" data-no-instant>Recherche</a>
-
- • <a rel="next"
- href="/david/2024/03/28/"
- title="Publication suivante : Collectif">Suivant →</a>
-
- </p>
- </nav>
-
- <p><a href="/david/2024/03/25/" title="Inclusion">Suite de mes expérimentations</a> de la veille à la fois en cartographie et en <em>web components</em>. Je me suis demandé s’il était possible de faire un composant qui ne dépende pas de uMap pour afficher une trace GPX. Le fait d’avoir passé une heure à trouver la bonne <a data-link-domain="MDN" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP">CSP</a> pour arriver à afficher une carte m’a d’une certaine manière motivé (c’était une journée galère).</p>
- <p>Il se trouve qu’il existe <a data-link-domain="github.com" href="https://github.com/mpetazzoni/leaflet-gpx">leaflet-gpx</a> qui fait exactement ce que je voulais. Il ne me restait « plus qu’à » en faire un composant web, ce que j’avais déjà <a data-link-domain="gitlab.com" href="https://gitlab.com/umap-project/leaflet-webcomponents/">fait par ailleurs</a> :</p>
- <gpx-viewer data-height="500px" data-width="800px">
- <p>
- Vous devriez voir s’afficher une carte de mon « Grand Tour de la forêt de Ouareau »,
- qui contient <a data-gpx href="/static/david/2024/grand_tour_de_la_foret_de_ouareau.gpx">cette trace GPX</a>,
- centrée sur <span data-latitude>46.2117</span>, <span data-longitude>-73.9335</span>
- avec un zoom de <span data-zoom>12</span>.
- Il est probable que cela ne s’exécute pas dans un agrégateur par exemple.
- </p>
- </gpx-viewer>
-
- <script type="module">
- import * as L from '/static/david/2024/leaflet.1.9.4/leaflet.1.9.4.js'
- window.L = L // Hack for leaflet-gpx, youpi.
- </script>
- <script type="module" nonce="oembed-web-component">
- class GPXViewer extends HTMLElement {
- static tagName = 'gpx-viewer'
-
- static register(tagName, registry) {
- if(!registry && ('customElements' in globalThis)) {
- registry = globalThis.customElements
- }
- registry?.define(tagName || this.tagName, this)
- }
-
- #attachCSS(path) {
- const linkElement = document.createElement('link')
- linkElement.setAttribute('rel', 'stylesheet')
- linkElement.setAttribute('href', path)
- this.shadowRoot.appendChild(linkElement)
- }
-
- #computeDimensions(mapContainer) {
- // There has to be a better way but I feel lazy tonight.
- let height = this.dataset.height
- let width = this.dataset.width
- // Size is in px so we strip these chars and convert to int.
- const heightValue = Number(height.slice(0, -2))
- const widthValue = Number(width.slice(0, -2))
- const mediaQueryMiddle = window.matchMedia(`(max-width: ${widthValue}px)`)
- const mediaQuerySmall = window.matchMedia(`(max-width: ${widthValue / 2}px)`)
- if (mediaQueryMiddle.matches) {
- height = `${heightValue / 2}px`
- width = `${widthValue / 2}px`
- }
- if (mediaQuerySmall.matches) {
- height = `${heightValue / 3}px`
- width = `${widthValue / 3}px`
- }
- mapContainer.style.height = height
- mapContainer.style.width = width
- }
-
- #createMapContainer() {
- const mapContainer = document.createElement('div')
- this.#computeDimensions(mapContainer)
- this.shadowRoot.appendChild(mapContainer)
- return mapContainer
- }
-
- #createMap(mapContainer) {
- const map = L.map(mapContainer).setView(
- [
- this.querySelector('[data-latitude]').textContent,
- this.querySelector('[data-longitude]').textContent,
- ],
- this.querySelector('[data-zoom]').textContent
- )
- L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
- attribution:
- '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
- }).addTo(map)
- return map
- }
-
- #attachGPX(map) {
- const gpxUrl = this.querySelector('[data-gpx]').href
- new GPX(gpxUrl, {
- async: true,
- marker_options: {
- startIconUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-icon-start.png',
- endIconUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-icon-end.png',
- shadowUrl: '/static/david/2024/leaflet-gpx.1.7.0-custom/pin-shadow.png'
- }
- }).on('loaded', (e) => {
- map.fitBounds(e.target.getBounds())
- }).addTo(map)
- }
-
- constructor() {
- super()
- this.attachShadow({ mode: 'open' })
- }
-
- async connectedCallback() {
- this.#attachCSS('/static/david/2024/leaflet.1.9.4/leaflet.css')
- const mapContainer = this.#createMapContainer()
- const map = this.#createMap(mapContainer)
- this.#attachGPX(map)
- }
-
- }
-
- import GPX from '/static/david/2024/leaflet-gpx.1.7.0-custom/gpx.1.7.0-custom.js'
-
- GPXViewer.register()
- </script>
-
- <p>Alors en fait, comme rien n’est jamais simple en JS, il a fallu que j’adapte le plugin qui n’était pas compatible avec les modules JS et que je fasse des galipettes pour que ça finisse par tomber en marche 🤸. Cet écosystème est merveilleux (quel métier !). J’ai quand même <a data-link-domain="github.com" href="https://github.com/mpetazzoni/leaflet-gpx/issues/153">remonté le problème</a>.</p>
- <p>Au passage, inspiré par <a data-link-domain="blog.k-nut.eu" href="https://blog.k-nut.eu/leaflet-microdata-html-webcomponent" hreflang="en"
- title="Consultation de l’article (anglais)">le travail de Knut Hühne</a>
- <a href="/david/cache/2024/65fba9cd025cd2403f932cb2c928cf14/" hreflang="en"
- data-tippy data-description="An example of using schema.org microdata to build a HTML Webcomponent for Leaflet"
- data-source="https://blog.k-nut.eu/leaflet-microdata-html-webcomponent"
- data-date="2024-03-25"
- data-favicon="https://blog.k-nut.eu/favicon-32x32.png"
- data-domain="blog.k-nut.eu"
- ><svg xmlns="http://www.w3.org/2000/svg"
- width="24" height="24" viewBox="0 0 24 24" fill="none"
- stroke="currentColor" stroke-width="2" stroke-linecap="square"
- stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle>
- <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
- <line x1="12" y1="17" x2="12.01" y2="17"></line>
- </svg>
- <span class="sr-only">[archive]</span></a> sur la sémantique des données d’une carte, je me suis demandé si j’allais définir un <a data-link-domain="schema.org" href="https://schema.org/itinerary">itinerary</a> pour un <a data-link-domain="schema.org" href="https://schema.org/Trip">Trip</a> avec des <a data-link-domain="schema.org" href="https://schema.org/GeoCoordinates">GeoCoordinates</a> mais ça m’a fait tirer un fil beaucoup trop long. Je garde l’idée sous le coude <a data-link-domain="umap-project.gitlab.io" href="https://umap-project.gitlab.io/leaflet-webcomponents/">pour mes propres recherches</a> dans le domaine cela dit.</p>
- <p>Un composant de plus, je ne suis pas très satisfait de celui-ci car j’avais l’espoir d’arriver à quelque chose de facilement réutilisable. J’aurais au moins commencé à mettre les doigts dans le XML du format GPX. Il est possible de définir plusieurs segments au sein d’une même trace. Il peut y avoir pas mal de métadonnées, notamment de mise en forme de la trace mais aussi de caractéristiques (biologiques, physiques, etc).</p>
- <a href="#hr-138" title="Lien vers cette section de la page"><hr id="hr-138" /></a>
-
- <blockquote lang="en">
- <p>The fact is that we can’t rely on any single website to hold the whole world’s knowledge, because it can be corrupted sooner or later. The only solution is a distributed architecture, with many smaller websites connecting with each other and sharing information. This is where ActivityPub comes in, the protocol used by Mastodon, Lemmy, Peertube and many other federated social media projects.</p>
- <p>I have worked on Lemmy for the past four years, bringing it from a prototype to a fully functional Reddit alternative. I wrote the entire federation code and became very familiar with the protocol. <mark>I realized that the same technology easily be used to create a federated encyclopedia.</mark> As no one else took up such a project, I finally decided to do it on my own and create Ibis. Thanks to my previous experience with the tech stack and the ActivityPub library I created, I was able to complete a proof of concept in a relatively short time of four months.</p>
- <p><cite><em><a data-link-domain="ibis.wiki" href="https://ibis.wiki/article/Announcing_Ibis,_the_federated_Wikipedia_Alternative@ibis.wiki" hreflang="en"
- title="Consultation de l’article (anglais)">Announcing Ibis, the federated Wikipedia Alternative</a>
- <a href="/david/cache/2024/691120ebac09f68413501b7f5daa2db7/" hreflang="en"
- data-tippy data-description=""
- data-source="https://ibis.wiki/article/Announcing_Ibis,_the_federated_Wikipedia_Alternative@ibis.wiki"
- data-date="2024-03-26"
- data-favicon=""
- data-domain="ibis.wiki"
- ><svg xmlns="http://www.w3.org/2000/svg"
- width="24" height="24" viewBox="0 0 24 24" fill="none"
- stroke="currentColor" stroke-width="2" stroke-linecap="square"
- stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle>
- <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
- <line x1="12" y1="17" x2="12.01" y2="17"></line>
- </svg>
- <span class="sr-only">[archive]</span></a></em></cite></p>
- </blockquote>
- <p>Wikipedia décentralisé, je dis oui ! Merci <a data-link-domain="alexsirac.com" href="https://alexsirac.com/activitypub-powered-wikis/" hreflang="fr"
- title="Consultation de l’article">Alex</a>
- <a href="/david/cache/2024/55c19feff784a41d2527b5f1589d931d/" hreflang="fr"
- data-tippy data-description=""
- data-source="https://alexsirac.com/activitypub-powered-wikis/"
- data-date="2024-03-26"
- data-favicon="https://alexsirac.com/wp-content/uploads/2023/01/cropped-cropped-portraitplante-32x32.webp"
- data-domain="alexsirac.com"
- ><svg xmlns="http://www.w3.org/2000/svg"
- width="24" height="24" viewBox="0 0 24 24" fill="none"
- stroke="currentColor" stroke-width="2" stroke-linecap="square"
- stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle>
- <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
- <line x1="12" y1="17" x2="12.01" y2="17"></line>
- </svg>
- <span class="sr-only">[archive]</span></a>.</p>
- <p>C’est important pour <a href="/david/2024/02/29/" title="Wikipédia">diverses</a> <a href="/david/2024/03/07/" title="Sondages">raisons</a>.</p>
- <a href="#hr-139" title="Lien vers cette section de la page"><hr id="hr-139" /></a>
-
- <blockquote lang="en">
- <p>I think it’s time to admit that my hobby is buying stuff for my new hobby.</p>
- <p><cite><em>Chris Hallbeck</em>, <a data-link-domain="mastodon.social" href="https://mastodon.social/@Chrishallbeck/112153815963114685">sur masto</a></cite></p>
- </blockquote>
- <p><em>Touché.</em></p>
-
-
- <nav>
- <p>
-
- <a href="/david/2024/apprentissage/"
- title="Liste de tous les articles 2024 associés à cette étiquette"
- rel="tag">#apprentissage</a>
-
- <a href="/david/2024/technique/"
- title="Liste de tous les articles 2024 associés à cette étiquette"
- rel="tag">#technique</a>
-
- <a href="/david/2024/web/"
- title="Liste de tous les articles 2024 associés à cette étiquette"
- rel="tag">#web</a>
-
- <a href="/david/2024/#tags" title="Liste de toutes les étiquettes 2024">tous ?</a>
- </p>
- </nav>
-
- <nav>
- <p>
-
- <a rel="prev"
- href="/david/2024/03/25/"
- title="Publication précédente : Inclusion">← Précédent</a> •
-
- <a href="/david/2024/" title="Liste des publications récentes">↑ En 2024</a>
-
- • <a rel="next"
- href="/david/2024/03/28/"
- title="Publication suivante : Collectif">Suivant →</a>
-
- </p>
- </nav>
-
-
- <form action="/david/recherche/" method="get">
- <fieldset>
- <legend>Recherche</legend>
- <label for="input-search">Termes de votre recherche :</label>
- <input id="input-search" type="search" name="s" aria-describedby="indexation-infos" required>
- <input type="submit" value="Chercher">
- <p id="indexation-infos">
- <small>
- Seuls les contenus de ces 8 dernières années sont indexés.
- </small>
- </p>
- </fieldset>
- </form>
-
- <aside>
- <theme-toggle></theme-toggle>
- </aside>
- </article>
- <hr>
- <footer>
- <p>
- <a href="/david/" title="Aller à l’accueil">Accueil</a>
- •
- <a href="/david/log/" title="Accès au flux RSS">Suivre</a>
- •
- <a href="http://larlet.com"
- title="Go to my English profile"
- data-instant>Pro</a>
- •
- <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">Email</a>
- •
- <abbr title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340">Légal</abbr>
- </p>
- <template id="theme-selector">
- <form>
- <style type="text/css">
- fieldset div {
- text-align: center;
- }
- </style>
- <fieldset>
- <legend>Thème</legend>
- <div>
- <label>
- <input type="radio" value="auto" name="chosen-color-scheme" checked>
- Auto
- </label>
- <label>
- <input type="radio" value="dark" name="chosen-color-scheme">
- Foncé
- </label>
- <label>
- <input type="radio" value="light" name="chosen-color-scheme">
- Clair
- </label>
- </div>
- </fieldset>
- </form>
- </template>
- </footer>
- <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
- <script>
- class ThemeToggle extends HTMLElement {
- constructor() {
- super()
- const themeSelectorTemplate = document.querySelector('#theme-selector')
- const form = themeSelectorTemplate.content.firstElementChild
- this.attachShadow({ mode: 'open' })
- this.shadowRoot.appendChild(form.cloneNode(true))
- }
-
- connectedCallback() {
- const form = this.shadowRoot.querySelector('form')
- form.addEventListener('change', (e) => {
- const chosenColorScheme = e.target.value
- localStorage.setItem('theme', chosenColorScheme)
- toggleTheme(chosenColorScheme)
- })
-
- const selectedTheme = localStorage.getItem('theme')
- if (selectedTheme && selectedTheme !== 'undefined') {
- form.querySelector(`[value="${selectedTheme}"]`).checked = true
- }
- }
- }
-
- const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
- window.addEventListener('load', () => {
- let colorsLayer = undefined
- let hasDarkRules = false
- for (const styleSheet of Array.from(document.styleSheets)) {
- let mediaRules = []
- for (const layerRule of styleSheet.cssRules) {
- if (!(layerRule instanceof CSSLayerBlockRule)) {
- continue
- }
- if (layerRule.name === 'colors') {
- colorsLayer = layerRule
- }
- for (const cssRule of layerRule.cssRules) {
- if (cssRule.type !== CSSRule.MEDIA_RULE) {
- continue
- }
- // WARNING: Safari does not have/supports `conditionText`.
- if (cssRule.conditionText) {
- if (cssRule.conditionText !== prefersColorSchemeDark) {
- continue
- }
- } else {
- if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
- continue
- }
- }
- mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
- }
- }
-
- // WARNING: do not try to insert a Rule to a styleSheet you are
- // currently iterating on, otherwise the browser will be stuck
- // in a infinite loop…
- for (const mediaRule of mediaRules) {
- // Safari requires the `0` second parameter (even if default).
- colorsLayer.insertRule(mediaRule.cssText, 0)
- hasDarkRules = true
- }
- }
-
- if (hasDarkRules) {
- if ('customElements' in window && !customElements.get('theme-toggle')) {
- customElements.define('theme-toggle', ThemeToggle)
- }
- }
- })
- </script>
-
- <script src="/static/david/js/popper-2.11.8.min.js"></script>
- <script src="/static/david/js/tippy-bundle-6.3.7.umd.min.js"></script>
- <script>
- tippy('[data-tippy]', {
- content(reference) {
- reference.addEventListener('click', (e) => e.preventDefault())
- return `
- <h3 lang="fr">
- <img src="${reference.dataset.favicon}" loading="lazy">
- <a href="${reference.dataset.source}"
- >Article sur ${reference.dataset.domain}</a></h3>
- <p lang="${reference.hreflang}"><em>${reference.dataset.description}</em></p>
- <div class="tippy-links" lang="fr">
- <a href="${reference.href}">Archive au ${reference.dataset.date}</a>
- </div>
- `
- },
- allowHTML: true,
- interactive: true,
- delay: [150, 700],
- hideOnClick: false
- })
- </script>
- <script type="module">
- import { annotate } from '/static/david/js/rough-notation-0.5.1.esm.min.js'
-
- const markObserver = new IntersectionObserver((entries, observer) => {
- const computedStyle = getComputedStyle(document.documentElement)
- const markBackground = computedStyle.getPropertyValue('--mark-background')
- for (const entry of entries) {
- if (entry.intersectionRatio === 0) continue
- const markElement = entry.target
- markElement.style.backgroundColor = 'inherit'
- const annotation = annotate(
- markElement, {
- type: 'highlight',
- multiline: true,
- color: markBackground,
- // animate: !window.matchMedia('(prefers-reduced-motion: reduce)').matches
- animate: false
- }
- )
- annotation.show()
- observer.unobserve(markElement)
- }
- }, {threshold: 1.0})
-
- for (const markElement of document.querySelectorAll('mark')) {
- markObserver.observe(markElement)
- }
- </script>
-
- </body>
- </html>
|