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<!-- This site is hosted on Netlify. Anyone can build and deploy a site 6 like this one for free: https://netlify.new/?utm_campaign=loops&utm_source=ai-legible&utm_medium=owned&utm_content=comment&utm_id=6214997d-9e50-4e83-a6ea-306b634eeb3a 7 Netlify hosting facts for this site: static/SSR served via Netlify Edge. --> 8<meta name="generator" content="quarto-1.9.17"> 9 10<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> 11 12 13<title>Domingo López RodrÃguez</title> 14<style> 15/* Default styles provided by pandoc. 16** See https://pandoc.org/MANUAL.html#variables-for-html for config info. 17*/ 18code{white-space: pre-wrap;} 19span.smallcaps{font-variant: small-caps;} 20div.columns{display: flex; gap: min(4vw, 1.5em);} 21div.column{flex: auto; overflow-x: auto;} 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<script src="site_libs/quarto-listing/list.min.js"></script>
vendor: 1 bytes, line 39
39
40<script src="site_libs/quarto-listing/quarto-listing.js"></script>
vendor: 1 bytes, line 40
40
41<script src="site_libs/cookie-consent/cookie-consent.js"></script>
41 42<link href="site_libs/cookie-consent/cookie-consent.css" rel="stylesheet">
43<script src="site_libs/quarto-html/quarto.js" type="module"></script>
vendor: 1 bytes, line 43
43
44<script src="site_libs/quarto-html/tabsets/tabsets.js" type="module"></script>
vendor: 1 bytes, line 44
44
45<script src="site_libs/quarto-html/axe/axe-check.js" type="module"></script>
vendor: 1 bytes, line 45
45
46<script src="site_libs/quarto-html/popper.min.js"></script>
vendor: 1 bytes, line 46
46
47<script src="site_libs/quarto-html/tippy.umd.min.js"></script>
vendor: 1 bytes, line 47
47
48<script src="site_libs/quarto-html/anchor.min.js"></script>
48 49<link href="site_libs/quarto-html/tippy.css" rel="stylesheet"> 50<link href="site_libs/quarto-html/quarto-syntax-highlighting-ce4278342a015d016477ed09bce942c5.css" rel="stylesheet" id="quarto-text-highlighting-styles">
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-137ad79071c2adac38395dead4c968d4.min.css" rel="stylesheet" append-hash="true" id="quarto-bootstrap" data-mode="light">
54<script id="quarto-search-options" type="application/json">{ 55 "location": "navbar", 56 "copy-button": false, 57 "collapse-after": 3, 58 "panel-placement": "end", 59 "type": "overlay", 60 "limit": 50, 61 "keyboard-shortcut": [ 62 "f", 63 "/", 64 "s" 65 ], 66 "show-item-context": false, 67 "language": { 68 "search-no-results-text": "No results", 69 "search-matching-documents-text": "matching documents", 70 "search-copy-link-title": "Copy link to search", 71 "search-hide-matches-text": "Hide additional matches", 72 "search-more-match-text": "more match in this document", 73 "search-more-matches-text": "more matches in this document", 74 "search-clear-button-title": "Clear", 75 "search-text-placeholder": "", 76 "search-detached-cancel-button-title": "Cancel", 77 "search-submit-button-title": "Submit", 78 "search-label": "Search" 79 } 80}</script>
vendor: 1 bytes, line 80
80
81<script> 82 83 window.document.addEventListener("DOMContentLoaded", function (_event) { 84 const listingTargetEl = window.document.querySelector('#listing-posts .list'); 85 if (!listingTargetEl) { 86 // No listing discovered, do not attach. 87 return; 88 } 89 90 const options = { 91 valueNames: ['listing-date','listing-author','listing-title','listing-image','listing-description',{ data: ['index'] },{ data: ['categories'] },{ data: ['listing-date-sort'] }], 92 93 searchColumns: ["listing-title","listing-author","listing-date","listing-categories","listing-image","listing-description"], 94 }; 95 96 window['quarto-listings'] = window['quarto-listings'] || {}; 97 window['quarto-listings']['listing-posts'] = new List('listing-posts', options); 98 99 if (window['quarto-listing-loaded']) { 100 window['quarto-listing-loaded'](); 101 } 102 }); 103 104 window.addEventListener('hashchange',() => { 105 if (window['quarto-listing-loaded']) { 106 window['quarto-listing-loaded'](); 107 } 108 }) 109 </script>
vendor: 67 bytes, lines 109-110
109 110<script async="" src="https://www.googletagmanager.com/gtag/js?id=
110G-SSMR9M3FZG
vendor: 13 bytes, lines 110-111
110"></script> 111
112<script type="text/plain" cookie-consent="tracking"> 113
vendor: 127 bytes, lines 114-117
114window.dataLayer = window.dataLayer || []; 115function gtag(){dataLayer.push(arguments);} 116gtag('js', new Date()); 117gtag('config', '
117G-SSMR9M3FZG
vendor: 29 bytes, line 117
117', { 'anonymize_ip': true});
118</script>
118 119
120<script type="text/javascript" charset="UTF-8"> 121document.addEventListener('DOMContentLoaded', function () { 122cookieconsent.run({ 123 "notice_banner_type":"simple", 124 "consent_type":"express", 125 "palette":"light", 126 "language":"en", 127 "page_load_consent_levels":["strictly-necessary"], 128 "notice_banner_reject_button_hide":false, 129 "preferences_center_close_button_hide":false, 130 "website_name":"" 131 , 132"language":"en" 133 }); 134}); 135</script>
135 136 137<style>html{ scroll-behavior: smooth; }</style> 138<link href="https://fonts.googleapis.com/css?family=Fira+Sans|Merriweather" rel="stylesheet">
139<script src="https://kit.fontawesome.com/0fba9333d8.js" crossorigin="anonymous"></script>
139 140<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jpswalsh/academicons@1/css/academicons.min.css"> 141<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"> 142
143<script src="https://code.iconify.design/iconify-icon/1.0.7/iconify-icon.min.js"></script>
143 144
145<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.min.js"></script>
vendor: 1 bytes, line 145
145
146<script src="./prism.js"></script>
vendor: 1 bytes, line 146
146
147<script> 148// Carga el archivo JavaScript de resaltado de sintaxis 149// (sustituye "prism.js" por el nombre del archivo que hayas descargado) 150 151// Crea una función que muestre una ventana popup con el contenido del archivo "cite.bib" 152function showPopup() { 153 // Abre la ventana popup 154 var popupWindow = window.open("", "Popup", "height=500,width=500"); 155 156 // Lee el contenido del archivo "cite.bib" usando Ajax 157 var xhr = new XMLHttpRequest(); 158 xhr.onreadystatechange = function() { 159 if (xhr.readyState == XMLHttpRequest.DONE) { 160 // Cuando el archivo se haya cargado, muestra su contenido en la ventana popup 161 // usando resaltado de sintaxis 162 var text = xhr.responseText; 163 popupWindow.document.body.innerHTML = "<pre><code class='language-bibtex'>" + text + "</code></pre>"; 164 165 // Aplica el resaltado de sintaxis a la ventana popup 166 Prism.highlightAll(); 167 } 168 } 169 xhr.open("GET", "cite.bib", true); 170 xhr.send(); 171} 172 173document.addEventListener("DOMContentLoaded", function() { 174 // Configure PDF.js Worker 175 pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.worker.min.js'; 176 177 // Inyectar estructura del Slides Modal en el body 178 const modalDiv = document.createElement("div"); 179 modalDiv.className = "slides-modal"; 180 modalDiv.id = "global-slides-modal"; 181 modalDiv.innerHTML = ` 182 <div class="slides-modal-content animate__animated animate__fadeInUp animate__faster"> 183 <div class="slides-modal-header"> 184 <h3 class="slides-modal-title"><i class="bi bi-easel"></i> Presentation Slides</h3> 185 <button class="slides-modal-close" id="global-slides-modal-close">×</button> 186 </div> 187 188 <!-- Container for reveal.js or HTML slides --> 189 <div class="slides-modal-body" id="slides-iframe-container" style="display: none; width: 100%; height: 100%;"> 190 <iframe src="" id="global-slides-modal-iframe" allowfullscreen></iframe> 191 </div> 192 193 <!-- Container for PDF slides (Slide deck player) --> 194 <div class="slides-modal-body" id="slides-pdf-container" style="background-color: #1a1a1a; display: flex; align-items: center; justify-content: center; overflow: auto; padding: 20px; position: relative; min-height: 400px; flex: 1;"> 195 <!-- Loading spinner --> 196 <div id="slides-loading" style="color: #fff; display: flex; flex-direction: column; align-items: center; gap: 10px; position: absolute; z-index: 10;"> 197 <div class="spinner-border text-light" role="status" style="width: 2rem; height: 2rem;"></div> 198 <span style="font-weight: 500; font-size: 0.95rem;">Loading presentation...</span> 199 </div> 200 <!-- Canvas for rendering slides --> 201 <canvas id="slides-canvas" style="box-shadow: 0 8px 30px rgba(0,0,0,0.6); max-width: 100%; max-height: 100%; display: none; background-color: #fff; border-radius: 4px;"></canvas> 202 </div> 203 204 <!-- PDF Player Controls --> 205 <div class="slides-modal-footer" id="slides-pdf-controls" style="padding: 12px 20px; background-color: #f8f9fa; border-top: 1px solid #dee2e6; display: flex; align-items: center; justify-content: space-between;"> 206 <button class="slides-modal-btn" id="prev-slide-btn" style="border: none; background: #dee2e6; color: #333; padding: 6px 18px; border-radius: 20px; font-weight: 600; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; transition: all 0.2s ease;"> 207 <i class="bi bi-chevron-left"></i> Previous 208 </button> 209 <span style="font-size: 0.95rem; font-weight: 600; color: #495057; font-family: var(--font-sans);"> 210 Slide <span id="current-page-num">0</span> of <span id="total-page-count">0</span> 211 </span> 212 <button class="slides-modal-btn" id="next-slide-btn" style="border: none; background: #dee2e6; color: #333; padding: 6px 18px; border-radius: 20px; font-weight: 600; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; transition: all 0.2s ease;"> 213 Next <i class="bi bi-chevron-right"></i> 214 </button> 215 </div> 216 </div> 217 `; 218 document.body.appendChild(modalDiv); 219 220 const modal = document.getElementById("global-slides-modal"); 221 const iframe = document.getElementById("global-slides-modal-iframe"); 222 const closeBtn = document.getElementById("global-slides-modal-close"); 223 const canvas = document.getElementById("slides-canvas"); 224 const ctx = canvas.getContext("2d"); 225 226 let pdfDoc = null, 227 pageNum = 1, 228 pageRendering = false, 229 pageNumPending = null; 230 231 // Escuchar clics en los enlaces de slides 232 document.addEventListener("click", function(e) { 233 const trigger = e.target.closest(".slides-modal-trigger"); 234 if (trigger) { 235 e.preventDefault(); 236 const href = trigger.getAttribute("href"); 237 238 modal.style.display = "flex"; 239 document.body.style.overflow = "hidden"; 240 241 const isPdf = href.toLowerCase().endsWith(".pdf"); 242 243 if (isPdf) { 244 document.getElementById("slides-iframe-container").style.display = "none"; 245 document.getElementById("slides-pdf-container").style.display = "flex";
246 document.getElementById("slides-pdf-controls").style.display = "flex"; 247 document.getElementById("slides-loading").style.display = "flex"; 248 document.getElementById("slides-loading").innerHTML = ` 249 <div class="spinner-border text-light" role="status" style="width: 2rem; height: 2rem;"></div> 250 <span style="font-weight: 500; font-size: 0.95rem;">Loading presentation...</span> 251 `; 252 canvas.style.display = "none"; 253 254 pdfjsLib.getDocument(href).promise.then(function(pdfDoc_) { 255 pdfDoc = pdfDoc_; 256 document.getElementById("total-page-count").textContent = pdfDoc.numPages; 257 pageNum = 1; 258 259 document.getElementById("slides-loading").style.display = "none"; 260 canvas.style.display = "block"; 261 262 renderPage(pageNum); 263 }).catch(function(err) { 264 console.error("Error loading PDF: ", err); 265 document.getElementById("slides-loading").innerHTML = '<span style="color: #c92a2a; font-weight: 600;">Error: Failed to load slides.</span>'; 266 }); 267 } else { 268 document.getElementById("slides-pdf-container").style.display = "none"; 269 document.getElementById("slides-pdf-controls").style.display = "none"; 270 document.getElementById("slides-iframe-container").style.display = "block"; 271 iframe.src = href; 272 } 273 } 274 }); 275 276 function renderPage(num) { 277 pageRendering = true; 278 pdfDoc.getPage(num).then(function(page) { 279 // Responsive responsiveScale based on viewport size 280 let unscaledViewport = page.getViewport({scale: 1.0}); 281 let containerWidth = canvas.parentElement.clientWidth - 40; 282 let containerHeight = canvas.parentElement.clientHeight - 40; 283 284 let widthScale = containerWidth / unscaledViewport.width; 285 let heightScale = containerHeight / unscaledViewport.height; 286 let responsiveScale = Math.min(widthScale, heightScale); 287 288 // Clamp values to keep it crisp and fast 289 responsiveScale = Math.min(Math.max(responsiveScale, 0.8), 2.2); 290 291 let viewport = page.getViewport({scale: responsiveScale}); 292 canvas.height = viewport.height; 293 canvas.width = viewport.width; 294 295 let renderContext = { 296 canvasContext: ctx, 297 viewport: viewport 298 }; 299 let renderTask = page.render(renderContext); 300 301 renderTask.promise.then(function() { 302 pageRendering = false; 303 if (pageNumPending !== null) { 304 renderPage(pageNumPending); 305 pageNumPending = null; 306 } 307 }); 308 }); 309 310 document.getElementById("current-page-num").textContent = num; 311 } 312 313 function queueRenderPage(num) { 314 if (pageRendering) { 315 pageNumPending = num; 316 } else { 317 renderPage(num); 318 } 319 } 320 321 function onPrevPage() { 322 if (!pdfDoc || pageNum <= 1) return; 323 pageNum--; 324 queueRenderPage(pageNum); 325 } 326 327 function onNextPage() { 328 if (!pdfDoc || pageNum >= pdfDoc.numPages) return; 329 pageNum++; 330 queueRenderPage(pageNum); 331 } 332 333 document.getElementById("prev-slide-btn").addEventListener("click", onPrevPage); 334 document.getElementById("next-slide-btn").addEventListener("click", onNextPage); 335 336 // Keyboard navigation for PDF slide viewer 337 document.addEventListener("keydown", function(e) { 338 if (modal.style.display === "flex" && pdfDoc) { 339 if (e.key === "ArrowLeft" || e.key === "PageUp") { 340 onPrevPage(); 341 } else if (e.key === "ArrowRight" || e.key === " " || e.key === "PageDown") { 342 e.preventDefault(); 343 onNextPage(); 344 } 345 } 346 }); 347 348 // Cerrar modal 349 function closeModal() { 350 modal.style.display = "none"; 351 iframe.src = ""; 352 pdfDoc = null; 353 document.body.style.overflow = ""; // Reactivar scroll 354 } 355 356 closeBtn.addEventListener("click", closeModal); 357 modal.addEventListener("click", function(e) { 358 if (e.target === modal) { 359 closeModal(); 360 } 361 }); 362 363 // Inyectar estructura del BibTeX Modal en el body 364 const bibModalDiv = document.createElement("div"); 365 bibModalDiv.className = "bibtex-modal"; 366 bibModalDiv.id = "global-bibtex-modal"; 367 bibModalDiv.innerHTML = ` 368 <div class="bibtex-modal-content animate__animated animate__fadeInUp animate__faster"> 369 <div class="bibtex-modal-header"> 370 <h3 class="bibtex-modal-title">
370<i class="bi bi-code-slash"></i> BibTeX Citation</h3> 371 <button class="bibtex-modal-close" id="global-bibtex-modal-close">×</button> 372 </div> 373 <div class="bibtex-modal-body"> 374 <pre><code id="global-bibtex-modal-code"></code></pre> 375 </div> 376 <div class="bibtex-modal-footer"> 377 <button class="bibtex-modal-btn bibtex-modal-btn-secondary" id="global-bibtex-modal-copy"> 378 <i class="bi bi-clipboard"></i> Copy BibTeX 379 </button> 380 <button class="bibtex-modal-btn bibtex-modal-btn-primary" id="global-bibtex-modal-download"> 381 <i class="bi bi-download"></i> Download .bib 382 </button> 383 </div> 384 </div> 385 `; 386 document.body.appendChild(bibModalDiv); 387 388 const bibModal = document.getElementById("global-bibtex-modal"); 389 const bibCode = document.getElementById("global-bibtex-modal-code"); 390 const bibCloseBtn = document.getElementById("global-bibtex-modal-close"); 391 const bibCopyBtn = document.getElementById("global-bibtex-modal-copy"); 392 const bibDownloadBtn = document.getElementById("global-bibtex-modal-download"); 393 394 let currentBibtex = ""; 395 let currentFilename = "citation.bib"; 396 397 // Escuchar clics en los botones de BibTeX 398 document.addEventListener("click", function(e) { 399 const trigger = e.target.closest(".show-bibtex-btn"); 400 if (trigger) { 401 e.preventDefault(); 402 currentBibtex = trigger.getAttribute("data-bibtex") || ""; 403 currentFilename = trigger.getAttribute("data-filename") || "citation.bib"; 404 405 bibCode.textContent = currentBibtex; 406 bibModal.style.display = "flex"; 407 document.body.style.overflow = "hidden"; 408 409 // Reset copy button 410 bibCopyBtn.innerHTML = '<i class="bi bi-clipboard"></i> Copy BibTeX'; 411 bibCopyBtn.classList.remove("copied"); 412 } 413 }); 414 415 // Cerrar BibTeX modal 416 function closeBibModal() { 417 bibModal.style.display = "none"; 418 document.body.style.overflow = ""; 419 } 420 421 bibCloseBtn.addEventListener("click", closeBibModal); 422 bibModal.addEventListener("click", function(e) { 423 if (e.target === bibModal) { 424 closeBibModal(); 425 } 426 }); 427 428 // Copiar al portapapeles 429 bibCopyBtn.addEventListener("click", function() { 430 navigator.clipboard.writeText(currentBibtex).then(function() { 431 bibCopyBtn.innerHTML = '<i class="bi bi-check-lg"></i> Copied!'; 432 bibCopyBtn.classList.add("copied"); 433 setTimeout(function() { 434 bibCopyBtn.innerHTML = '<i class="bi bi-clipboard"></i> Copy BibTeX'; 435 bibCopyBtn.classList.remove("copied"); 436 }, 2000); 437 }).catch(function(err) { 438 console.error("Could not copy BibTeX: ", err); 439 }); 440 }); 441 442 // Descargar archivo .bib 443 bibDownloadBtn.addEventListener("click", function() { 444 const blob = new Blob([currentBibtex], { type: "text/plain;charset=utf-8" }); 445 const link = document.createElement("a"); 446 link.href = URL.createObjectURL(blob); 447 link.download = currentFilename; 448 document.body.appendChild(link); 449 link.click(); 450 document.body.removeChild(link); 451 }); 452 453 document.addEventListener("keydown", function(e) { 454 if (e.key === "Escape") { 455 if (modal.style.display === "flex") { 456 closeModal(); 457 } 458 if (bibModal.style.display === "flex") { 459 closeBibModal(); 460 } 461 } 462 }); 463 464 // --- PREMIUM BLOG INTERACTIVE UX --- 465 const isBlogPost = window.location.pathname.includes("/blog/") && 466 !window.location.pathname.endsWith("/blog/") && 467 !window.location.pathname.endsWith("/blog/index.html"); 468 469 if (isBlogPost) { 470 // 1. Reading progress bar 471 const progBar = document.createElement("div"); 472 progBar.id = "reading-progress-bar"; 473 progBar.style.cssText = "position: fixed; top: 0; left: 0; height: 3px; background-color: #bd666d; width: 0%; z-index: 9999; transition: width 0.1s ease;"; 474 document.body.appendChild(progBar); 475 476 window.addEventListener("scroll", function() { 477 const winScroll = document.documentElement.scrollTop || document.body.scrollTop; 478 const height = document.documentElement.scrollHeight - document.documentElement.clientHeight; 479 const scrolled = height > 0 ? (winScroll / height) * 100 : 0; 480 progBar.style.width = scrolled + "%"; 481 }); 482 483 // 2. Dynamic Share widget 484 const shareDiv = document.createElement("div"); 485 shareDiv.className = "blog-share-widget"; 486 shareDiv.innerHTML = ` 487 <a href="https://twitter.com/intent/tweet?url=${encodeURIComponent(window.location.href)}&text=${encodeURIComponent(document.title)}" target="_blank" title="Share on Twitter"><i class="fa-brands fa-twitter"></i></a> 488 <a href="https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(window.location.href)}" target="_blank" title="Share on LinkedIn"><i class="fa-brands fa-linkedin"></i></a> 489 <button id="blog-copy-link" title="Copy Link"><i class="fa-solid fa-link"></i></button> 490 `; 491 document.body.appendChild(shareDiv); 492 493 document.getElementById("blog-copy-link").addEventListener("click", function() { 494 navigator.clipboard.writeText(window.location.href).then(function() { 495 const btn = document.getElementById("blog-copy-link"); 496 btn.innerHTML = '<i class="fa-solid fa-check" style="color: #bd666d;"></i>'; 497 setTimeout(() => { 498 btn.innerHTML = '<i class="fa-solid fa-link"></i>'; 499 }, 2000); 500 }); 501 }); 502 } 503}); 504</script>
504 505 506
506<script>window.backupDefine = window.define; window.define = undefined;</script>
506<script src="https://cdn.jsdelivr.net/npm/katex@latest/dist/katex.min.js"></script>
506 507
507<script>document.addEventListener("DOMContentLoaded", function () { 508 var mathElements = document.getElementsByClassName("math"); 509 var macros = []; 510 for (var i = 0; i < mathElements.length; i++) { 511 var texText = mathElements[i].firstChild; 512 if (mathElements[i].tagName == "SPAN") { 513 katex.render(texText.data, mathElements[i], { 514 displayMode: mathElements[i].classList.contains('display'), 515 throwOnError: false, 516 macros: macros, 517 fleqn: false 518 }); 519}}}); 520 </script>
520 521
521<script>window.define = window.backupDefine; window.backupDefine = undefined;</script>
521<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@latest/dist/katex.min.css"> 522
523<script type="text/javascript"> 524const typesetMath = (el) => { 525 if (window.MathJax) { 526 // MathJax Typeset 527 window.MathJax.typeset([el]); 528 } else if (window.katex) { 529 // KaTeX Render 530 var mathElements = el.getElementsByClassName("math"); 531 var macros = []; 532 for (var i = 0; i < mathElements.length; i++) { 533 var texText = mathElements[i].firstChild; 534 if (mathElements[i].tagName == "SPAN" && texText && texText.data) { 535 window.katex.render(texText.data, mathElements[i], { 536 displayMode: mathElements[i].classList.contains('display'), 537 throwOnError: false, 538 macros: macros, 539 fleqn: false 540 }); 541 } 542 } 543 } 544} 545window.Quarto = { 546 typesetMath 547}; 548</script>
548 549 550<link rel="stylesheet" href="styles.css"> 551<meta property="og:title" content="Domingo López RodrÃguez"> 552<meta property="og:image" content="https://dominlopez.netlify.app/avatar.png"> 553<meta property="og:site_name" content="Domingo López RodrÃguez"> 554<meta property="og:image:height" content="1024"> 555<meta property="og:image:width" content="1365"> 556<meta name="twitter:title" content="Domingo López RodrÃguez"> 557<meta name="twitter:image" content="https://dominlopez.netlify.app/avatar.png"> 558<meta name="twitter:creator" content="@dominlopez"> 559<meta name="twitter:card" content="summary_large_image"> 560<meta name="twitter:image-height" content="1024"> 561<meta name="twitter:image-width" content="1365"> 562</head> 563 564<body class="nav-fixed quarto-light"> 565 566<div id="quarto-search-results"></div> 567 <header id="quarto-header" class="headroom fixed-top"> 568 <nav class="navbar navbar-expand-lg " data-bs-theme="dark"> 569 <div class="navbar-container container-fluid"> 570 <div class="navbar-brand-container mx-auto"> 571 <a class='navbar-brand' href='/'> 572 <span class="navbar-title">Domingo López RodrÃguez</span> 573 </a> 574 </div> 575 <div id="quarto-search" class="" title="Search"></div> 576 <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(); }"> 577 <span class="navbar-toggler-icon"></span> 578</button> 579 <div class="collapse navbar-collapse" id="navbarCollapse"> 580 <ul class="navbar-nav navbar-nav-scroll ms-auto"> 581 <li class="nav-item"> 582 <a class='nav-link' href='/about/'> 583<span class="menu-text">About</span></a> 584 </li> 585 <li class="nav-item"> 586 <a class='nav-link' href='/teaching/'> 587<span class="menu-text">Teaching</span></a> 588 </li> 589 <li class="nav-item dropdown "> 590 <a class="nav-link dropdown-toggle" href="#" id="nav-menu-research" role="link" data-bs-toggle="dropdown" aria-expanded="false"> 591 <span class="menu-text">Research</span> 592 </a> 593 <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="nav-menu-research"> 594 <li> 595 <a class='dropdown-item' href='/research/'> 596 <span class="dropdown-text">Research summary</span></a> 597 </li> 598 <li> 599 <a class='dropdown-item' href='/publications/'> 600 <span class="dropdown-text">Publications</span></a> 601 </li> 602 <li> 603 <a class='dropdown-item' href='/projects/'> 604 <span class="dropdown-text">Projects</span></a> 605 </li> 606 <li> 607 <a class='dropdown-item' href='/talks/'> 608 <span class="dropdown-text">Talks</span></a> 609 </li> 610 </ul> 611 </li> 612 <li class="nav-item"> 613 <a class='nav-link' href='/resources/'> 614<span class="menu-text">Resources</span></a> 615 </li> 616 <li class="nav-item"> 617 <a class='nav-link' href='/blog/'> 618<span class="menu-text">Blog</span></a> 619 </li> 620</ul> 621 </div> <!-- /navcollapse --> 622 <div class="quarto-navbar-tools"> 623</div> 624 </div> <!-- /container-fluid --> 625 </nav> 626</header> 627<!-- content --> 628<div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-full page-navbar"> 629<!-- sidebar --> 630<!-- margin-sidebar --> 631 632<!-- main --> 633<main class="content page-columns page-full column-body" id="quarto-document-content"><header id="title-block-header" class="quarto-title-block"></header> 634 635 636 637 638<div class="grid column-page"> 639<div class="g-col-12 g-col-md-4 text-center"> 640<p><img src="avatar.png" class="profile-pic img-fluid"></p> 641<div class="about-links" style="display: flex; flex-direction: column; gap: 0.5rem; align-items: center;"> 642<p><a href="https://twitter.com/dominlopez" class="btn btn-outline-secondary btn-sm rounded-pill" style="width: 100%; max-width: 200px;"><i class="bi bi-twitter"></i> Twitter</a> <a href="https://mastodon.social/" class="btn btn-outline-secondary btn-sm rounded-pill" style="width: 100%; max-width: 200px;"><i class="bi bi-mastodon"></i> Mastodon</a> <a href="https://github.com/neuroimaginador" class="btn btn-outline-secondary btn-sm rounded-pill" style="width: 100%; max-width: 200px;"><i class="bi bi-github"></i> GitHub</a> <a href="mailto:[email protected]" class="btn btn-outline-secondary btn-sm rounded-pill" style="width: 100%; max-width: 200px;"><i class="bi bi-envelope"></i> Email</a></p> 643</div> 644</div> 645<section id="domingo-lópez-rodrÃguez" class="level1 g-col-12 g-col-md-8"> 646<h1>Domingo López RodrÃguez</h1> 647<p>Professor at the Department of Applied Mathematics, University of Málaga.</p> 648<p>My recent work focuses on <strong>Formal Concept Analysis (FCA)</strong> and its applications in data science and knowledge discovery.</p> 649<p><a href='/about/'>Learn more about me â</a></p> 650</section> 651</div> 652<div class="column-page"> 653<div class="featured-grid animate__animated animate__fadeInUp" style="animation-delay: 0.2s;"> 654<div class="feature-card"> 655<div class="feature-content"> 656<div class="feature-title"> 657<i class="bi bi-box-seam"></i> fcaR Package 658</div> 659<p> 660Formal Concept Analysis in R. A comprehensive toolset for FCA including formal contexts, concept lattices, and implications. 661</p> 662</div> 663<div style="padding: 1.5rem; border-top: 1px solid #eee; background: #fafafa;"> 664<p><a href="https://github.com/neuroimaginador/fcaR" target="_blank">View on GitHub â</a></p> 665</div> 666</div> 667<div class="feature-card"> 668<div class="feature-content"> 669<div class="feature-title"> 670<i class="bi bi-easel"></i> Teaching 671</div> 672<p> 673Teaching materials and resources for undergraduate and graduate courses in Mathematics and Engineering. 674</p> 675</div> 676<div style="padding: 1.5rem; border-top: 1px solid #eee; background: #fafafa;"> 677<p><a href='/teaching/'>Go to Teaching â</a></p> 678</div> 679</div> 680<div class="feature-card"> 681<div class="feature-content"> 682<div class="feature-title"> 683<i class="bi bi-journal-text"></i> Publications 684</div> 685<p> 686A complete list of my scientific tracking, including journal papers, conference proceedings, and book chapters. 687</p> 688</div> 689<div style="padding: 1.5rem; border-top: 1px solid #eee; background: #fafafa;"> 690<p><a href='/publications/'>Browse Publications â</a></p> 691</div> 692</div> 693</div> 694<div class="latest-updates-section"> 695<h2 style="text-align: center; border-bottom: none; margin-top: 0;" class="anchored"> 696Latest Updates 697</h2> 698<div id="listing-posts" class="quarto-listing quarto-listing-container-custom"> 699<div class="list grid quarto-listing-cols-3 featured-grid animate__animated animate__fadeInUp"> 700 701 <div class="quarto-grid-item card h-100" data-index="0" data-categories="Rm9ybWFsJTIwQ29uY2VwdCUyMEFuYWx5c2lzJTJDUGFwZXIlMjBTcG90bGlnaHQ=" data-listing-date-sort="1781474400000"> 702
703 <a href="./blog/2026/EfGrBoMaFaViRiSiAl" class="card-img-top"> 704 <img src="./assets/images/latest_posts/EfGrBoMaFaViRiSiAl-cover.jpg" alt="Unlocking Hidden Patterns in Big Data: How We Scaled Boolean Matrix Factorization to Thousands of Features" style="height: 200px; width: 100%; object-fit: cover;"> 705 </a> 706 707 <div class="card-body post-contents"> 708 <h5 class="no-anchor card-title listing-title"> 709 <a href="./blog/2026/EfGrBoMaFaViRiSiAl" class="no-external"> 710 Unlocking Hidden Patterns in Big Data: How We Scaled Boolean Matrix Factorization to Thousands of Features 711 </a> 712 </h5> 713 714 <div class="card-text listing-description"> 715 Uncovering meaningful, interpretable patterns in massive binary datasets often comes with an overwhelming computational cost. We developed a novel algorithm, RSF-ES, that uses order-theoretic smart pruning to dramatically speed up Boolean Matrix Factorization without losing mathematical exactness. 716 </div> 717 718 <div class="card-attribution card-text-small justify" style="margin-top: 1rem; color: #888; font-size: 0.85em;"> 719 <span class="listing-date"><i class="bi bi-calendar"></i> 720 15 June 2026 721 </span> 722 723 </div> 724 </div> 725 </div> 726 727 <div class="quarto-grid-item card h-100" data-index="1" data-categories="Rm9ybWFsJTIwQ29uY2VwdCUyMEFuYWx5c2lzJTJDSW1wbGljYXRpb24lMjBzeXN0ZW1zJTJDU2ltcGxpZmljYXRpb24lMjBsb2dpYyUyQ1BhcGVyJTIwU3BvdGxpZ2h0" data-listing-date-sort="1781215200000"> 728 729 <a href="./blog/2026/DiOpSyAtImFuFoCoAn" class="card-img-top"> 730 <img src="./assets/images/latest_posts/DiOpSyAtImFuFoCoAn-cover.jpg" alt="Cracking the Speed Code in Fuzzy Data Reasoning: How We Made Fuzzy FCA Blazing Fast" style="height: 200px; width: 100%; object-fit: cover;"> 731 </a> 732 733 <div class="card-body post-contents"> 734 <h5 class="no-anchor card-title listing-title"> 735 <a href="./blog/2026/DiOpSyAtImFuFoCoAn" class="no-external"> 736 Cracking the Speed Code in Fuzzy Data Reasoning: How We Made Fuzzy FCA Blazing Fast 737 </a> 738 </h5> 739 740 <div class="card-text listing-description"> 741 Calculating logical closures in fuzzy data used to be a massive computational bottleneck. In our latest paper, we introduce a new framework and algorithm that makes computing direct-optimal fuzzy implicational systems orders of magnitude faster. 742 </div> 743 744 <div class="card-attribution card-text-small justify" style="margin-top: 1rem; color: #888; font-size: 0.85em;"> 745 <span class="listing-date"><i class="bi bi-calendar"></i> 746 12 June 2026 747 </span> 748 749 </div> 750 </div> 751 </div> 752 753 <div class="quarto-grid-item card h-100" data-index="2" data-categories="Rm9ybWFsJTIwY29uY2VwdCUyMGFuYWx5c2lzJTJDSW1wcmVjaXNlJTIwaW5mb3JtYXRpb24lMkNQYXBlciUyMFNwb3RsaWdodA==" data-listing-date-sort="1763161200000"> 754 755 <a href="./blog/2025/ClOnLiAlFuThEx" class="card-img-top"> 756 <img src="./assets/images/latest_posts/ClOnLiAlFuThEx-cover.jpg" alt="Teaching Algorithms to 'Speak Fuzzy' Natively: A Faster Way to Mine Nuanced Data" style="height: 200px; width: 100%; object-fit: cover;"> 757 </a> 758 759 <div class="card-body post-contents"> 760 <h5 class="no-anchor card-title listing-title"> 761 <a href="./blog/2025/ClOnLiAlFuThEx" class="no-external"> 762 Teaching Algorithms to 'Speak Fuzzy' Natively: A Faster Way to Mine Nuanced Data 763 </a> 764 </h5> 765 766 <div class="card-text listing-description"> 767 Analyzing fuzzy data shouldn't mean drowning in computational bottlenecks. By adapting the Close-by-One algorithm family to process degrees of truth natively, our research team eliminated the need for cumbersome data scaling and drastically boosted execution speeds. 768 </div> 769 770 <div class="card-attribution card-text-small justify" style="margin-top: 1rem; color: #888; font-size: 0.85em;">
771 <span class="listing-date"><i class="bi bi-calendar"></i> 772 15 November 2025 773 </span> 774 775 </div> 776 </div> 777 </div> 778 779 <div class="quarto-grid-item card h-100" data-index="3" data-categories="RkNBJTJDQWxnb3JpdGhtJTJDSW1wbGljYXRpb25zJTJDRGl2aWRlLWFuZC1jb25xdWVyJTJDS25vd2xlZGdlJTIwcmVwcmVzZW50YXRpb24=" data-listing-date-sort="1745013600000"> 780 781 <a href="./blog/2025/SyImObUsCaDeFoCo" class="card-img-top"> 782 <img src="./assets/images/placeholder.png" alt="Divide and conquer... the 'if-then' rules in your data" style="height: 200px; width: 100%; object-fit: cover;"> 783 </a> 784 785 <div class="card-body post-contents"> 786 <h5 class="no-anchor card-title listing-title"> 787 <a href="./blog/2025/SyImObUsCaDeFoCo" class="no-external"> 788 Divide and conquer... the 'if-then' rules in your data 789 </a> 790 </h5> 791 792 <div class="card-text listing-description"> 793 The 'CARVE' algorithm is a super-fast 'divide-and-conquer' method for 794finding data concepts. We upgraded it to 'CARVE+' so it can find all 795the 'if-then' logical rules at the same time, giving us the best of both worlds. 796 797 </div> 798 799 <div class="card-attribution card-text-small justify" style="margin-top: 1rem; color: #888; font-size: 0.85em;"> 800 <span class="listing-date"><i class="bi bi-calendar"></i> 801 19 April 2025 802 </span> 803 804 </div> 805 </div> 806 </div> 807 808 <div class="quarto-grid-item card h-100" data-index="4" data-categories="U29mdHdhcmUlMkNTdXN0YWluYWJpbGl0eSUyQ0RhdGElMjBtaW5pbmclMkNVcmJhbiUyMHBsYW5uaW5nJTJDR0lT" data-listing-date-sort="1738364400000"> 809 810 <a href="./blog/2025/UrSuSoDeUnArDuUrHeIsEf" class="card-img-top"> 811 <img src="./assets/images/placeholder.png" alt="We built an open-source tool to find a city's worst 'heat island' hotspots" style="height: 200px; width: 100%; object-fit: cover;"> 812 </a> 813 814 <div class="card-body post-contents"> 815 <h5 class="no-anchor card-title listing-title"> 816 <a href="./blog/2025/UrSuSoDeUnArDuUrHeIsEf" class="no-external"> 817 We built an open-source tool to find a city's worst 'heat island' hotspots 818 </a> 819 </h5> 820 821 <div class="card-text listing-description"> 822 We present URSUS_UHI, a new open-source software tool that helps urban 823planners automatically find the most unfavourable areas in a city: the places 824with the highest temperatures and the least green space. 825 826 </div> 827 828 <div class="card-attribution card-text-small justify" style="margin-top: 1rem; color: #888; font-size: 0.85em;"> 829 <span class="listing-date"><i class="bi bi-calendar"></i> 830 1 February 2025 831 </span> 832 833 </div> 834 </div> 835 </div> 836 837 <div class="quarto-grid-item card h-100" data-index="5" data-categories="RkNBJTJDTG9naWMlMkNUaGVvcmV0aWNhbCUyMENTJTJDQWxnb3JpdGhtJTJDU2ltcGxpZmljYXRpb24=" data-listing-date-sort="1737154800000"> 838 839 <a href="./blog/2025/NeSiRuDaPoNeAt" class="card-img-top"> 840 <img src="./assets/images/placeholder.png" alt="Beyond just 'correct': new logic rules to make data insights easier to read" style="height: 200px; width: 100%; object-fit: cover;"> 841 </a> 842 843 <div class="card-body post-contents"> 844 <h5 class="no-anchor card-title listing-title"> 845 <a href="./blog/2025/NeSiRuDaPoNeAt" class="no-external"> 846 Beyond just 'correct': new logic rules to make data insights easier to read 847 </a> 848 </h5> 849 850 <div class="card-text listing-description"> 851 Our previous work created a logic to find correct rules from positive and negative data. 852Now, we've added new logical equivalences to 'shrink' those rules, 853making them shorter, simpler, and more useful for humans. 854 855 </div> 856 857 <div class="card-attribution card-text-small justify" style="margin-top: 1rem; color: #888; font-size: 0.85em;">
858 <span class="listing-date"><i class="bi bi-calendar"></i> 859 18 January 2025 860 </span> 861 862 </div> 863 </div> 864 </div> 865 866</div> 867<div class="listing-no-matching d-none">No matching items</div> 868</div> 869<div style="text-align: center; margin-top: 3rem;"> 870<p><a class='btn-action' href='/blog/'>View all posts â</a></p> 871</div> 872</div> 873</div> 874 875 876 877 878</main> <!-- /main -->
879<script id="quarto-html-after-body" type="application/javascript"> 880 window.document.addEventListener("DOMContentLoaded", function (event) { 881 const icon = ""; 882 const anchorJS = new window.AnchorJS(); 883 anchorJS.options = { 884 placement: 'right', 885 icon: icon 886 }; 887 anchorJS.add('.anchored'); 888 const isCodeAnnotation = (el) => { 889 for (const clz of el.classList) { 890 if (clz.startsWith('code-annotation-')) { 891 return true; 892 } 893 } 894 return false; 895 } 896 const onCopySuccess = function(e) { 897 // button target 898 const button = e.trigger; 899 // don't keep focus 900 button.blur(); 901 // flash "checked" 902 button.classList.add('code-copy-button-checked'); 903 var currentTitle = button.getAttribute("title"); 904 button.setAttribute("title", "Copied!"); 905 let tooltip; 906 if (window.bootstrap) { 907 button.setAttribute("data-bs-toggle", "tooltip"); 908 button.setAttribute("data-bs-placement", "left"); 909 button.setAttribute("data-bs-title", "Copied!"); 910 tooltip = new bootstrap.Tooltip(button, 911 { trigger: "manual", 912 customClass: "code-copy-button-tooltip", 913 offset: [0, -8]}); 914 tooltip.show(); 915 } 916 setTimeout(function() { 917 if (tooltip) { 918 tooltip.hide(); 919 button.removeAttribute("data-bs-title"); 920 button.removeAttribute("data-bs-toggle"); 921 button.removeAttribute("data-bs-placement"); 922 } 923 button.setAttribute("title", currentTitle); 924 button.classList.remove('code-copy-button-checked'); 925 }, 1000); 926 // clear code selection 927 e.clearSelection(); 928 } 929 const getTextToCopy = function(trigger) { 930 const outerScaffold = trigger.parentElement.cloneNode(true); 931 const codeEl = outerScaffold.querySelector('code'); 932 for (const childEl of codeEl.children) { 933 if (isCodeAnnotation(childEl)) { 934 childEl.remove(); 935 } 936 } 937 return codeEl.innerText; 938 } 939 const clipboard = new window.ClipboardJS('.code-copy-button:not([data-in-quarto-modal])', { 940 text: getTextToCopy 941 }); 942 clipboard.on('success', onCopySuccess); 943 if (window.document.getElementById('quarto-embedded-source-code-modal')) { 944 const clipboardModal = new window.ClipboardJS('.code-copy-button[data-in-quarto-modal]', { 945 text: getTextToCopy, 946 container: window.document.getElementById('quarto-embedded-source-code-modal') 947 }); 948 clipboardModal.on('success', onCopySuccess); 949 } 950 var localhostRegex = new RegExp(/^(?:http|https):\/\/localhost\:?[0-9]*\//); 951 var mailtoRegex = new RegExp(/^mailto:/); 952 var filterRegex = new RegExp("https:\/\/dominlopez\.netlify\.app"); 953 var isInternal = (href) => { 954 return filterRegex.test(href) || localhostRegex.test(href) || mailtoRegex.test(href); 955 } 956 // Inspect non-navigation links and adorn them if external 957 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)'); 958 for (var i=0; i<links.length; i++) { 959 const link = links[i]; 960 if (!isInternal(link.href)) { 961 // undo the damage that might have been done by quarto-nav.js in the case of 962 // links that we want to consider external 963 if (link.dataset.originalHref !== undefined) { 964 link.href = link.dataset.originalHref; 965 } 966 } 967 } 968 function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) { 969 const config = { 970 allowHTML: true, 971 maxWidth: 500, 972 delay: 100, 973 arrow: false, 974 appendTo: function(el) { 975 return el.parentElement; 976 }, 977 interactive: true, 978 interactiveBorder: 10, 979 theme: 'quarto', 980 placement: 'bottom-start', 981 }; 982 if (contentFn) { 983 config.content = contentFn; 984 } 985 if (onTriggerFn) { 986 config.onTrigger = onTriggerFn; 987 } 988 if (onUntriggerFn) { 989 config.onUntrigger = onUntriggerFn; 990 } 991 window.tippy(el, config); 992 } 993 const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]'); 994 for (var i=0; i<noterefs.length; i++) { 995 const ref = noterefs[i]; 996 tippyHover(ref, function() { 997 // use id or data attribute instead here 998 let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href'); 999 try { href = new URL(href).hash; } catch {} 1000 const id = href.replace(/^#\/?/, ""); 1001 const note = window.document.getElementById(id); 1002 if (note) { 1003 return note.innerHTML; 1004 } else { 1005 return ""; 1006 } 1007 }); 1008 } 1009 const xrefs = window.document.querySelectorAll('a.quarto-xref'); 1010 const processXRef = (id, note) => { 1011 // Strip column container classes 1012 const stripColumnClz = (el) => { 1013 el.classList.remove("page-full", "page-columns"); 1014 if (el.children) { 1015 for (const child of el.children) {
1016 stripColumnClz(child); 1017 } 1018 } 1019 } 1020 stripColumnClz(note) 1021 if (id === null || id.startsWith('sec-')) { 1022 // Special case sections, only their first couple elements 1023 const container = document.createElement("div"); 1024 if (note.children && note.children.length > 2) { 1025 container.appendChild(note.children[0].cloneNode(true)); 1026 for (let i = 1; i < note.children.length; i++) { 1027 const child = note.children[i]; 1028 if (child.tagName === "P" && child.innerText === "") { 1029 continue; 1030 } else { 1031 container.appendChild(child.cloneNode(true)); 1032 break; 1033 } 1034 } 1035 if (window.Quarto?.typesetMath) { 1036 window.Quarto.typesetMath(container); 1037 } 1038 return container.innerHTML 1039 } else { 1040 if (window.Quarto?.typesetMath) { 1041 window.Quarto.typesetMath(note); 1042 } 1043 return note.innerHTML; 1044 } 1045 } else { 1046 // Remove any anchor links if they are present 1047 const anchorLink = note.querySelector('a.anchorjs-link'); 1048 if (anchorLink) { 1049 anchorLink.remove(); 1050 } 1051 if (window.Quarto?.typesetMath) { 1052 window.Quarto.typesetMath(note); 1053 } 1054 if (note.classList.contains("callout")) { 1055 return note.outerHTML; 1056 } else { 1057 return note.innerHTML; 1058 } 1059 } 1060 } 1061 for (var i=0; i<xrefs.length; i++) { 1062 const xref = xrefs[i]; 1063 tippyHover(xref, undefined, function(instance) { 1064 instance.disable(); 1065 let url = xref.getAttribute('href'); 1066 let hash = undefined; 1067 if (url.startsWith('#')) { 1068 hash = url; 1069 } else { 1070 try { hash = new URL(url).hash; } catch {} 1071 } 1072 if (hash) { 1073 const id = hash.replace(/^#\/?/, ""); 1074 const note = window.document.getElementById(id); 1075 if (note !== null) { 1076 try { 1077 const html = processXRef(id, note.cloneNode(true)); 1078 instance.setContent(html); 1079 } finally { 1080 instance.enable(); 1081 instance.show(); 1082 } 1083 } else { 1084 // See if we can fetch this 1085 fetch(url.split('#')[0]) 1086 .then(res => res.text()) 1087 .then(html => { 1088 const parser = new DOMParser(); 1089 const htmlDoc = parser.parseFromString(html, "text/html"); 1090 const note = htmlDoc.getElementById(id); 1091 if (note !== null) { 1092 const html = processXRef(id, note); 1093 instance.setContent(html); 1094 } 1095 }).finally(() => { 1096 instance.enable(); 1097 instance.show(); 1098 }); 1099 } 1100 } else { 1101 // See if we can fetch a full url (with no hash to target) 1102 // This is a special case and we should probably do some content thinning / targeting 1103 fetch(url) 1104 .then(res => res.text()) 1105 .then(html => { 1106 const parser = new DOMParser(); 1107 const htmlDoc = parser.parseFromString(html, "text/html"); 1108 const note = htmlDoc.querySelector('main.content'); 1109 if (note !== null) { 1110 // This should only happen for chapter cross references 1111 // (since there is no id in the URL) 1112 // remove the first header 1113 if (note.children.length > 0 && note.children[0].tagName === "HEADER") { 1114 note.children[0].remove(); 1115 } 1116 const html = processXRef(null, note); 1117 instance.setContent(html); 1118 } 1119 }).finally(() => { 1120 instance.enable(); 1121 instance.show(); 1122 }); 1123 } 1124 }, function(instance) { 1125 }); 1126 } 1127 let selectedAnnoteEl; 1128 const selectorForAnnotation = ( cell, annotation) => { 1129 let cellAttr = 'data-code-cell="' + cell + '"'; 1130 let lineAttr = 'data-code-annotation="' + annotation + '"'; 1131 const selector = 'span[' + cellAttr + '][' + lineAttr + ']'; 1132 return selector; 1133 } 1134 const selectCodeLines = (annoteEl) => { 1135 const doc = window.document; 1136 const targetCell = annoteEl.getAttribute("data-target-cell"); 1137 const targetAnnotation = annoteEl.getAttribute("data-target-annotation"); 1138 const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation)); 1139 const lines = annoteSpan.getAttribute("data-code-lines").split(","); 1140 const lineIds = lines.map((line) => { 1141 return targetCell + "-" + line; 1142 }) 1143 let top = null; 1144 let height = null; 1145 let parent = null; 1146 if (lineIds.length > 0) { 1147 //compute the position of the single el (top and bottom and make a div) 1148 const el = window.document.getElementById(lineIds[0]); 1149 top = el.offsetTop; 1150 height = el.offsetHeight; 1151 parent = el.parentElement.parentElement; 1152 if (lineIds.length > 1) { 1153 const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]); 1154 const bottom = lastEl.offsetTop + lastEl.offsetHeight; 1155 height = bottom - top; 1156 } 1157 if (top !== null && height !== null && parent !== null) { 1158 // cook up a div (if necessary) and position it 1159 let div = window.document.getElementById("code-annotation-line-highlight"); 1160 if (div === null) { 1161 div = window.document.createElement("div"); 1162 div.setAttribute("id", "code-annotation-line-highlight"); 1163 div.style.position = 'absolute'; 1164 parent.appendChild(div); 1165 } 1166 div.style.top = top - 2 + "px"; 1167 div.style.height = height + 4 + "px"; 1168 div.style.left = 0; 1169 let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter"); 1170 if (gutterDiv === null) { 1171 gutterDiv = window.document.createElement("div"); 1172 gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter"); 1173 gutterDiv.style.position = 'absolute'; 1174 const codeCell = window.document.getElementById(targetCell); 1175 const gutter = codeCell.querySelector('.code-annotation-gutter'); 1176 gutter.appendChild(gutterDiv); 1177 } 1178 gutterDiv.style.top = top - 2 + "px"; 1179 gutterDiv.style.height = height + 4 + "px"; 1180 } 1181 selectedAnnoteEl = annoteEl; 1182 } 1183 }; 1184 const unselectCodeLines = () => { 1185 const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"]; 1186 elementsIds.forEach((elId) => { 1187 const div = window.document.getElementById(elId); 1188 if (div) { 1189 div.remove(); 1190 } 1191 }); 1192 selectedAnnoteEl = undefined; 1193 }; 1194 // Handle positioning of the toggle 1195 window.addEventListener( 1196 "resize", 1197 throttle(() => { 1198 elRect = undefined; 1199 if (selectedAnnoteEl) { 1200 selectCodeLines(selectedAnnoteEl); 1201 } 1202 }, 10) 1203 ); 1204 function throttle(fn, ms) { 1205 let throttle = false;
1206 let timer; 1207 return (...args) => { 1208 if(!throttle) { // first call gets through 1209 fn.apply(this, args); 1210 throttle = true; 1211 } else { // all the others get throttled 1212 if(timer) clearTimeout(timer); // cancel #2 1213 timer = setTimeout(() => { 1214 fn.apply(this, args); 1215 timer = throttle = false; 1216 }, ms); 1217 } 1218 }; 1219 } 1220 // Attach click handler to the DT 1221 const annoteDls = window.document.querySelectorAll('dt[data-target-cell]'); 1222 for (const annoteDlNode of annoteDls) { 1223 annoteDlNode.addEventListener('click', (event) => { 1224 const clickedEl = event.target; 1225 if (clickedEl !== selectedAnnoteEl) { 1226 unselectCodeLines(); 1227 const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active'); 1228 if (activeEl) { 1229 activeEl.classList.remove('code-annotation-active'); 1230 } 1231 selectCodeLines(clickedEl); 1232 clickedEl.classList.add('code-annotation-active'); 1233 } else { 1234 // Unselect the line 1235 unselectCodeLines(); 1236 clickedEl.classList.remove('code-annotation-active'); 1237 } 1238 }); 1239 } 1240 const findCites = (el) => { 1241 const parentEl = el.parentElement; 1242 if (parentEl) { 1243 const cites = parentEl.dataset.cites; 1244 if (cites) { 1245 return { 1246 el, 1247 cites: cites.split(' ') 1248 }; 1249 } else { 1250 return findCites(el.parentElement) 1251 } 1252 } else { 1253 return undefined; 1254 } 1255 }; 1256 var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]'); 1257 for (var i=0; i<bibliorefs.length; i++) { 1258 const ref = bibliorefs[i]; 1259 const citeInfo = findCites(ref); 1260 if (citeInfo) { 1261 tippyHover(citeInfo.el, function() { 1262 var popup = window.document.createElement('div'); 1263 citeInfo.cites.forEach(function(cite) { 1264 var citeDiv = window.document.createElement('div'); 1265 citeDiv.classList.add('hanging-indent'); 1266 citeDiv.classList.add('csl-entry'); 1267 var biblioDiv = window.document.getElementById('ref-' + cite); 1268 if (biblioDiv) { 1269 citeDiv.innerHTML = biblioDiv.innerHTML; 1270 } 1271 popup.appendChild(citeDiv); 1272 }); 1273 return popup.innerHTML; 1274 }); 1275 } 1276 } 1277 }); 1278 </script>
1278 1279</div> <!-- /content --> 1280<footer class="footer"> 1281 <div class="nav-footer"> 1282 <div class="nav-footer-left"> 1283<p><strong>Domingo López-RodrÃguez</strong><br> Department of Applied Mathematics<br> University of Málaga</p> 1284</div> 1285 <div class="nav-footer-center"> 1286 <ul class="footer-items list-unstyled"> 1287 <li class="nav-item compact"> 1288 <a class="nav-link" href="https://twitter.com/dominlopez"> 1289 <i class="bi bi-twitter" role="img" aria-label="Twitter"> 1290</i> 1291 </a> 1292 </li> 1293 <li class="nav-item compact"> 1294 <a class="nav-link" href="https://mastodon.social/"> 1295 <i class="bi bi-mastodon" role="img" aria-label="Mastodon"> 1296</i> 1297 </a> 1298 </li> 1299 <li class="nav-item compact"> 1300 <a class="nav-link" href="https://github.com/neuroimaginador"> 1301 <i class="bi bi-github" role="img" aria-label="Github"> 1302</i> 1303 </a> 1304 </li> 1305 <li class="nav-item compact"> 1306 <a class="nav-link" href="mailto:[email protected]"> 1307 <i class="bi bi-envelope" role="img" aria-label="Email"> 1308</i> 1309 </a> 1310 </li> 1311</ul> 1312 <div class="cookie-consent-footer"><a href="#" id="open_preferences_center">Cookie Preferences</a></div></div> 1313 <div class="nav-footer-right"> 1314<p><a href="https://www.uma.es" target="_blank" style="color: #fdfbf7; text-decoration: underline;">University of Málaga</a><br> Built with <a href="https://quarto.org">Quarto</a></p> 1315</div> 1316 </div> 1317</footer> 1318 1319 1320 1321
1322<script src="site_libs/quarto-html/zenscroll-min.js"></script>
1322 1323</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.