1'use strict'; 2 3/* global default_theme, default_dark_theme, default_light_theme, hljs, ClipboardJS */ 4 5// Fix back button cache problem 6window.onunload = function() { }; 7 8// Global variable, shared between modules 9function playground_text(playground, hidden = true) { 10 const code_block = playground.querySelector('code'); 11 12 if (window.ace && code_block.classList.contains('editable')) { 13 const editor = window.ace.edit(code_block); 14 return editor.getValue(); 15 } else if (hidden) { 16 return code_block.textContent; 17 } else { 18 return code_block.innerText; 19 } 20} 21 22(function codeSnippets() { 23 function fetch_with_timeout(url, options, timeout = 6000) { 24 return Promise.race([ 25 fetch(url, options), 26 new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)), 27 ]); 28 } 29 30 const playgrounds = Array.from(document.querySelectorAll('.playground')); 31 if (playgrounds.length > 0) { 32 fetch_with_timeout('https://play.rust-lang.org/meta/crates', { 33 headers: { 34 'Content-Type': 'application/json', 35 }, 36 method: 'POST', 37 mode: 'cors', 38 }) 39 .then(response => response.json()) 40 .then(response => { 41 // get list of crates available in the rust playground 42 const playground_crates = response.crates.map(item => item['id']); 43 playgrounds.forEach(block => handle_crate_list_update(block, playground_crates)); 44 }); 45 } 46 47 function handle_crate_list_update(playground_block, playground_crates) { 48 // update the play buttons after receiving the response 49 update_play_button(playground_block, playground_crates); 50 51 // and install on change listener to dynamically update ACE editors 52 if (window.ace) { 53 const code_block = playground_block.querySelector('code'); 54 if (code_block.classList.contains('editable')) { 55 const editor = window.ace.edit(code_block); 56 editor.addEventListener('change', () => { 57 update_play_button(playground_block, playground_crates); 58 }); 59 // add Ctrl-Enter command to execute rust code 60 editor.commands.addCommand({ 61 name: 'run', 62 bindKey: { 63 win: 'Ctrl-Enter', 64 mac: 'Ctrl-Enter', 65 }, 66 exec: _editor => run_rust_code(playground_block), 67 }); 68 } 69 } 70 } 71 72 // updates the visibility of play button based on `no_run` class and 73 // used crates vs ones available on https://play.rust-lang.org 74 function update_play_button(pre_block, playground_crates) { 75 const play_button = pre_block.querySelector('.play-button'); 76 77 // skip if code is `no_run` 78 if (pre_block.querySelector('code').classList.contains('no_run')) { 79 play_button.classList.add('hidden'); 80 return; 81 } 82 83 // get list of `extern crate`'s from snippet 84 const txt = playground_text(pre_block); 85 const re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g; 86 const snippet_crates = []; 87 let item; 88 while (item = re.exec(txt)) { 89 snippet_crates.push(item[1]); 90 } 91 92 // check if all used crates are available on play.rust-lang.org 93 const all_available = snippet_crates.every(function(elem) { 94 return playground_crates.indexOf(elem) > -1; 95 }); 96 97 if (all_available) { 98 play_button.classList.remove('hidden'); 99 play_button.hidden = false; 100 } else { 101 play_button.classList.add('hidden'); 102 } 103 } 104 105 function run_rust_code(code_block) { 106 let result_block = code_block.querySelector('.result'); 107 if (!result_block) { 108 result_block = document.createElement('code'); 109 result_block.className = 'result hljs language-bash'; 110 111 code_block.append(result_block); 112 } 113 114 const text = playground_text(code_block); 115 const classes = code_block.querySelector('code').classList; 116 let edition = '2015';
117 classes.forEach(className => { 118 if (className.startsWith('edition')) { 119 edition = className.slice(7); 120 } 121 }); 122 const params = { 123 version: 'stable', 124 optimize: '0', 125 code: text, 126 edition: edition, 127 }; 128 129 if (text.indexOf('#![feature') !== -1) { 130 params.version = 'nightly'; 131 } 132 133 result_block.innerText = 'Running...'; 134 135 fetch_with_timeout('https://play.rust-lang.org/evaluate.json', { 136 headers: { 137 'Content-Type': 'application/json', 138 }, 139 method: 'POST', 140 mode: 'cors', 141 body: JSON.stringify(params), 142 }) 143 .then(response => response.json()) 144 .then(response => { 145 if (response.result.trim() === '') { 146 result_block.innerText = 'No output'; 147 result_block.classList.add('result-no-output'); 148 } else { 149 result_block.innerText = response.result; 150 result_block.classList.remove('result-no-output'); 151 } 152 }) 153 .catch(error => result_block.innerText = 'Playground Communication: ' + error.message); 154 } 155 156 // Syntax highlighting Configuration 157 hljs.configure({ 158 tabReplace: ' ', // 4 spaces 159 languages: [], // Languages used for auto-detection 160 }); 161 162 const code_nodes = Array 163 .from(document.querySelectorAll('code')) 164 // Don't highlight `inline code` blocks in headers. 165 .filter(function(node) { 166 return !node.parentElement.classList.contains('header'); 167 }); 168 169 if (window.ace) { 170 // language-rust class needs to be removed for editable 171 // blocks or highlightjs will capture events 172 code_nodes 173 .filter(function(node) { 174 return node.classList.contains('editable'); 175 }) 176 .forEach(function(block) { 177 block.classList.remove('language-rust'); 178 }); 179 180 code_nodes 181 .filter(function(node) { 182 return !node.classList.contains('editable'); 183 }) 184 .forEach(function(block) { 185 hljs.highlightBlock(block); 186 }); 187 } else { 188 code_nodes.forEach(function(block) { 189 hljs.highlightBlock(block); 190 }); 191 } 192 193 // Adding the hljs class gives code blocks the color css 194 // even if highlighting doesn't apply 195 code_nodes.forEach(function(block) { 196 block.classList.add('hljs'); 197 }); 198 199 Array.from(document.querySelectorAll('code.hljs')).forEach(function(block) { 200 201 const lines = Array.from(block.querySelectorAll('.boring')); 202 // If no lines were hidden, return 203 if (!lines.length) { 204 return; 205 } 206 block.classList.add('hide-boring'); 207 208 const buttons = document.createElement('div'); 209 buttons.className = 'buttons'; 210 buttons.innerHTML = '<button title="Show hidden lines" \ 211aria-label="Show hidden lines"></button>'; 212 buttons.firstChild.innerHTML = document.getElementById('fa-eye').innerHTML; 213 214 // add expand button 215 const pre_block = block.parentNode; 216 pre_block.insertBefore(buttons, pre_block.firstChild); 217 218 buttons.firstChild.addEventListener('click', function(e) { 219 if (this.title === 'Show hidden lines') { 220 this.innerHTML = document.getElementById('fa-eye-slash').innerHTML; 221 this.title = 'Hide lines'; 222 this.setAttribute('aria-label', e.target.title); 223 224 block.classList.remove('hide-boring'); 225 } else if (this.title === 'Hide lines') { 226 this.innerHTML = document.getElementById('fa-eye').innerHTML; 227 this.title = 'Show hidden lines'; 228 this.setAttribute('aria-label', e.target.title); 229 230 block.classList.add('hide-boring'); 231 } 232 }); 233 }); 234 235 if (window.playground_copyable) { 236 Array.from(document.querySelectorAll('pre code')).forEac
236h(function(block) { 237 const pre_block = block.parentNode; 238 if (!pre_block.classList.contains('playground')) { 239 let buttons = pre_block.querySelector('.buttons'); 240 if (!buttons) { 241 buttons = document.createElement('div'); 242 buttons.className = 'buttons'; 243 pre_block.insertBefore(buttons, pre_block.firstChild); 244 } 245 246 const clipButton = document.createElement('button'); 247 clipButton.className = 'clip-button'; 248 clipButton.title = 'Copy to clipboard'; 249 clipButton.setAttribute('aria-label', clipButton.title); 250 clipButton.innerHTML = '<i class="tooltiptext"></i>'; 251 252 buttons.insertBefore(clipButton, buttons.firstChild); 253 } 254 }); 255 } 256 257 // Process playground code blocks 258 Array.from(document.querySelectorAll('.playground')).forEach(function(pre_block) { 259 // Add play button 260 let buttons = pre_block.querySelector('.buttons'); 261 if (!buttons) { 262 buttons = document.createElement('div'); 263 buttons.className = 'buttons'; 264 pre_block.insertBefore(buttons, pre_block.firstChild); 265 } 266 267 const runCodeButton = document.createElement('button'); 268 runCodeButton.className = 'play-button'; 269 runCodeButton.hidden = true; 270 runCodeButton.title = 'Run this code'; 271 runCodeButton.setAttribute('aria-label', runCodeButton.title); 272 runCodeButton.innerHTML = document.getElementById('fa-play').innerHTML; 273 274 buttons.insertBefore(runCodeButton, buttons.firstChild); 275 runCodeButton.addEventListener('click', () => { 276 run_rust_code(pre_block); 277 }); 278 279 if (window.playground_copyable) { 280 const copyCodeClipboardButton = document.createElement('button'); 281 copyCodeClipboardButton.className = 'clip-button'; 282 copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>'; 283 copyCodeClipboardButton.title = 'Copy to clipboard'; 284 copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title); 285 286 buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild); 287 } 288 289 const code_block = pre_block.querySelector('code'); 290 if (window.ace && code_block.classList.contains('editable')) { 291 const undoChangesButton = document.createElement('button'); 292 undoChangesButton.className = 'reset-button'; 293 undoChangesButton.title = 'Undo changes'; 294 undoChangesButton.setAttribute('aria-label', undoChangesButton.title); 295 undoChangesButton.innerHTML += 296 document.getElementById('fa-clock-rotate-left').innerHTML; 297 298 buttons.insertBefore(undoChangesButton, buttons.firstChild); 299 300 undoChangesButton.addEventListener('click', function() { 301 const editor = window.ace.edit(code_block); 302 editor.setValue(editor.originalCode); 303 editor.clearSelection(); 304 }); 305 } 306 }); 307})(); 308 309(function themes() { 310 const html = document.querySelector('html'); 311 const themeToggleButton = document.getElementById('mdbook-theme-toggle'); 312 const themePopup = document.getElementById('mdbook-theme-list'); 313 const themeColorMetaTag = document.querySelector('meta[name="theme-color"]'); 314 const themeIds = []; 315 themePopup.querySelectorAll('button.theme').forEach(function(el) { 316 themeIds.push(el.id); 317 }); 318 const stylesheets = { 319 ayuHighlight: document.querySelector('#mdbook-ayu-highlight-css'), 320 tomorrowNight: document.querySelector('#mdbook-tomorrow-night-css'), 321 highlight: document.querySelector('#mdbook-highlight-css'), 322 }; 323 324 function showThemes() { 325 themePopup.style.display = 'block'; 326 themeToggleButton.setAttribute('aria-expanded', true); 327 themePopup.querySelector('button#mdbook-theme-' + get_theme()).focus(); 328 } 329 330 function updateThemeSelected() { 331 themePopup.querySelectorAll('.theme-selected').forEach(function(el) { 332 el.classList.remove('theme-selected'); 333 }); 334 const selected = get_saved_theme() ?? 'default_theme';
335 let element = themePopup.querySelector('button#mdbook-theme-' + selected); 336 if (element === null) { 337 // Fall back in case there is no "Default" item. 338 element = themePopup.querySelector('button#mdbook-theme-' + get_theme()); 339 } 340 element.classList.add('theme-selected'); 341 } 342 343 function hideThemes() { 344 themePopup.style.display = 'none'; 345 themeToggleButton.setAttribute('aria-expanded', false); 346 themeToggleButton.focus(); 347 } 348 349 function get_saved_theme() { 350 let theme = null; 351 try { 352 theme = localStorage.getItem('mdbook-theme'); 353 } catch { 354 // ignore error. 355 } 356 return theme; 357 } 358 359 function delete_saved_theme() { 360 localStorage.removeItem('mdbook-theme'); 361 } 362 363 function get_theme() { 364 const theme = get_saved_theme(); 365 if (theme === null || theme === undefined || !themeIds.includes('mdbook-theme-' + theme)) { 366 if (typeof default_dark_theme === 'undefined') { 367 // A customized index.hbs might not define this, so fall back to 368 // old behavior of determining the default on page load. 369 return default_theme; 370 } 371 return window.matchMedia('(prefers-color-scheme: dark)').matches 372 ? default_dark_theme 373 : default_light_theme; 374 } else { 375 return theme; 376 } 377 } 378 379 let previousTheme = default_theme; 380 function set_theme(theme, store = true) { 381 let ace_theme; 382 383 if (theme === 'coal' || theme === 'navy') { 384 stylesheets.ayuHighlight.disabled = true; 385 stylesheets.tomorrowNight.disabled = false; 386 stylesheets.highlight.disabled = true; 387 388 ace_theme = 'ace/theme/tomorrow_night'; 389 } else if (theme === 'ayu') { 390 stylesheets.ayuHighlight.disabled = false; 391 stylesheets.tomorrowNight.disabled = true; 392 stylesheets.highlight.disabled = true; 393 ace_theme = 'ace/theme/tomorrow_night'; 394 } else { 395 stylesheets.ayuHighlight.disabled = true; 396 stylesheets.tomorrowNight.disabled = true; 397 stylesheets.highlight.disabled = false; 398 ace_theme = 'ace/theme/dawn'; 399 } 400 401 setTimeout(function() { 402 themeColorMetaTag.content = getComputedStyle(document.documentElement).backgroundColor; 403 }, 1); 404 405 if (window.ace && window.editors) { 406 window.editors.forEach(function(editor) { 407 editor.setTheme(ace_theme); 408 }); 409 } 410 411 if (store) { 412 try { 413 localStorage.setItem('mdbook-theme', theme); 414 } catch { 415 // ignore error. 416 } 417 } 418 419 html.classList.remove(previousTheme); 420 html.classList.add(theme); 421 previousTheme = theme; 422 updateThemeSelected(); 423 } 424 425 const query = window.matchMedia('(prefers-color-scheme: dark)'); 426 query.onchange = function() { 427 set_theme(get_theme(), false); 428 }; 429 430 // Set theme. 431 set_theme(get_theme(), false); 432 433 themeToggleButton.addEventListener('click', function() { 434 if (themePopup.style.display === 'block') { 435 hideThemes(); 436 } else { 437 showThemes(); 438 } 439 }); 440 441 themePopup.addEventListener('click', function(e) { 442 let theme; 443 if (e.target.className === 'theme') { 444 theme = e.target.id; 445 } else if (e.target.parentElement.className === 'theme') { 446 theme = e.target.parentElement.id; 447 } else { 448 return; 449 } 450 theme = theme.replace(/^mdbook-theme-/, ''); 451 452 if (theme === 'default_theme' || theme === null) { 453 delete_saved_theme(); 454 set_theme(get_theme(), false); 455 } else { 456 set_theme(theme); 457 } 458 }); 459 460 themePopup.addEventListener('focusout', function(e) { 461 // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below) 462 if (!!e.relatedTarget && 463 !themeToggleButton.contains(e.relatedTarget) && 464 !themePopup.contains(e.relatedTarget) 465 ) { 466 hideThemes(); 467 } 468 }); 469 470 // Should not be needed, but it works around an issue on macOS & iOS: 471 // https://github.com/rust-lang/mdBook/issues/628 472 document.addEventListener('click', function(e) { 473 if (themePopup.style.display === 'block' && 474 !themeToggleButton.contains(e.target) && 475 !themePopup.contains(e.target) 476 ) { 477 hideThemes(); 478 } 479 }); 480 481 document.addEventListener('keydown', function(e) { 482 if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { 483 return; 484 } 485 if (!themePopup.contains(e.target)) { 486 return; 487 } 488 489 let li; 490 switch (e.key) { 491 case 'Escape': 492 e.preventDefault(); 493 hideThemes(); 494 break; 495 case 'ArrowUp': 496 e.preventDefault(); 497 li = document.activeElement.parentElement; 498 if (li && li.previousElementSibling) { 499 li.previousElementSibling.querySelector('button').focus(); 500 } 501 break; 502 case 'ArrowDown': 503 e.preventDefault(); 504 li = document.activeElement.parentElement; 505 if (li && li.nextElementSibling) { 506 li.nextElementSibling.querySelector('button').focus(); 507 } 508 break; 509 case 'Home': 510 e.preventDefault(); 511 themePopup.querySelector('li:first-child button').focus(); 512 break; 513 case 'End': 514 e.preventDefault(); 515 themePopup.querySelector('li:last-child button').focus(); 516 break; 517 } 518 }); 519})(); 520 521(function sidebar() { 522 const sidebar = document.getElementById('mdbook-sidebar'); 523 const sidebarLinks = document.querySelectorAll('#mdbook-sidebar a'); 524 const sidebarToggleButton = document.getElementById('mdbook-sidebar-toggle'); 525 const sidebarResizeHandle = document.getElementById('mdbook-sidebar-resize-handle'); 526 const sidebarCheckbox = document.getElementById('mdbook-sidebar-toggle-anchor'); 527 let firstContact = null; 528 529 530 /* Because we cannot change the `display` using only CSS after/before the transition, we 531 need JS to do it. We change the display to prevent the browsers search to find text inside
532 the collapsed sidebar. */ 533 if (!document.documentElement.classList.contains('sidebar-visible')) { 534 sidebar.style.display = 'none'; 535 } 536 sidebar.addEventListener('transitionend', () => { 537 /* We only change the display to "none" if we're collapsing the sidebar. */ 538 if (!sidebarCheckbox.checked) { 539 sidebar.style.display = 'none'; 540 } 541 }); 542 sidebarToggleButton.addEventListener('click', () => { 543 /* To allow the sidebar expansion animation, we first need to put back the display. */ 544 if (!sidebarCheckbox.checked) { 545 sidebar.style.display = ''; 546 // Workaround for Safari skipping the animation when changing 547 // `display` and a transform in the same event loop. This forces a 548 // reflow after updating the display. 549 sidebar.offsetHeight; 550 } 551 }); 552 553 function showSidebar() { 554 document.documentElement.classList.add('sidebar-visible'); 555 Array.from(sidebarLinks).forEach(function(link) { 556 link.setAttribute('tabIndex', 0); 557 }); 558 sidebarToggleButton.setAttribute('aria-expanded', true); 559 sidebar.setAttribute('aria-hidden', false); 560 try { 561 localStorage.setItem('mdbook-sidebar', 'visible'); 562 } catch { 563 // Ignore error. 564 } 565 } 566 567 function hideSidebar() { 568 document.documentElement.classList.remove('sidebar-visible'); 569 Array.from(sidebarLinks).forEach(function(link) { 570 link.setAttribute('tabIndex', -1); 571 }); 572 sidebarToggleButton.setAttribute('aria-expanded', false); 573 sidebar.setAttribute('aria-hidden', true); 574 try { 575 localStorage.setItem('mdbook-sidebar', 'hidden'); 576 } catch { 577 // Ignore error. 578 } 579 } 580 581 // Toggle sidebar 582 sidebarCheckbox.addEventListener('change', function sidebarToggle() { 583 if (sidebarCheckbox.checked) { 584 const current_width = parseInt( 585 document.documentElement.style.getPropertyValue('--sidebar-target-width'), 10); 586 if (current_width < 150) { 587 document.documentElement.style.setProperty('--sidebar-target-width', '150px'); 588 } 589 showSidebar(); 590 } else { 591 hideSidebar(); 592 } 593 }); 594 595 sidebarResizeHandle.addEventListener('mousedown', initResize, false); 596 597 function initResize() { 598 window.addEventListener('mousemove', resize, false); 599 window.addEventListener('mouseup', stopResize, false); 600 document.documentElement.classList.add('sidebar-resizing'); 601 } 602 function resize(e) { 603 let pos = e.clientX - sidebar.offsetLeft; 604 if (pos < 20) { 605 hideSidebar(); 606 } else { 607 if (!document.documentElement.classList.contains('sidebar-visible')) { 608 showSidebar(); 609 } 610 pos = Math.min(pos, window.innerWidth - 100); 611 document.documentElement.style.setProperty('--sidebar-target-width', pos + 'px'); 612 } 613 } 614 //on mouseup remove windows functions mousemove & mouseup 615 function stopResize() { 616 document.documentElement.classList.remove('sidebar-resizing'); 617 window.removeEventListener('mousemove', resize, false); 618 window.removeEventListener('mouseup', stopResize, false); 619 } 620 621 document.addEventListener('touchstart', function(e) { 622 firstContact = { 623 x: e.touches[0].clientX, 624 time: Date.now(), 625 }; 626 }, { passive: true }); 627 628 document.addEventListener('touchmove', function(e) { 629 if (!firstContact) { 630 return; 631 } 632 633 const curX = e.touches[0].clientX; 634 const xDiff = curX - firstContact.x, 635 tDiff = Date.now() - firstContact.time; 636 637 if (tDiff < 250 && Math.abs(xDiff) >= 150) { 638 if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300)) { 639 showSidebar(); 640 } else if (xDiff < 0 && curX < 300) { 641 hideSidebar(); 642 } 643 644 firstContact = null; 645 } 646 }, { passive: true }); 647})(); 648 649(function chapterNavigation() { 650 document.addEventListener('keydown', function(e) { 651 if (e.altKey || e.ctrlKey || e.metaKey) { 652 return; 653 } 654 if (window.search && window.search.hasFocus()) { 655 return; 656 } 657 const html = document.querySelector('html'); 658 659 function next() { 660 const nextButton = document.querySelector('.nav-chapter
660s.next'); 661 if (nextButton) { 662 window.location.href = nextButton.href; 663 } 664 } 665 function prev() { 666 const previousButton = document.querySelector('.nav-chapters.previous'); 667 if (previousButton) { 668 window.location.href = previousButton.href; 669 } 670 } 671 function showHelp() { 672 const container = document.getElementById('mdbook-help-container'); 673 const overlay = document.getElementById('mdbook-help-popup'); 674 container.style.display = 'flex'; 675 676 // Clicking outside the popup will dismiss it. 677 const mouseHandler = event => { 678 if (overlay.contains(event.target)) { 679 return; 680 } 681 if (event.button !== 0) { 682 return; 683 } 684 event.preventDefault(); 685 event.stopPropagation(); 686 document.removeEventListener('mousedown', mouseHandler); 687 hideHelp(); 688 }; 689 690 // Pressing esc will dismiss the popup. 691 const escapeKeyHandler = event => { 692 if (event.key === 'Escape') { 693 event.preventDefault(); 694 event.stopPropagation(); 695 document.removeEventListener('keydown', escapeKeyHandler, true); 696 hideHelp(); 697 } 698 }; 699 document.addEventListener('keydown', escapeKeyHandler, true); 700 document.getElementById('mdbook-help-container') 701 .addEventListener('mousedown', mouseHandler); 702 } 703 function hideHelp() { 704 document.getElementById('mdbook-help-container').style.display = 'none'; 705 } 706 707 // Usually needs the Shift key to be pressed 708 switch (e.key) { 709 case '?': 710 e.preventDefault(); 711 showHelp(); 712 break; 713 } 714 715 // Rest of the keys are only active when the Shift key is not pressed 716 if (e.shiftKey) { 717 return; 718 } 719 720 switch (e.key) { 721 case 'ArrowRight': 722 e.preventDefault(); 723 if (html.dir === 'rtl') { 724 prev(); 725 } else { 726 next(); 727 } 728 break; 729 case 'ArrowLeft': 730 e.preventDefault(); 731 if (html.dir === 'rtl') { 732 next(); 733 } else { 734 prev(); 735 } 736 break; 737 } 738 }); 739})(); 740 741(function clipboard() { 742 const clipButtons = document.querySelectorAll('.clip-button'); 743 744 function hideTooltip(elem) { 745 elem.firstChild.innerText = ''; 746 elem.className = 'clip-button'; 747 } 748 749 function showTooltip(elem, msg) { 750 elem.firstChild.innerText = msg; 751 elem.className = 'clip-button tooltipped'; 752 } 753 754 const clipboardSnippets = new ClipboardJS('.clip-button', { 755 text: function(trigger) { 756 hideTooltip(trigger); 757 const playground = trigger.closest('pre'); 758 return playground_text(playground, false); 759 }, 760 }); 761 762 Array.from(clipButtons).forEach(function(clipButton) { 763 clipButton.addEventListener('mouseout', function(e) { 764 hideTooltip(e.currentTarget); 765 }); 766 }); 767 768 clipboardSnippets.on('success', function(e) { 769 e.clearSelection(); 770 showTooltip(e.trigger, 'Copied!'); 771 }); 772 773 clipboardSnippets.on('error', function(e) { 774 showTooltip(e.trigger, 'Clipboard error!'); 775 }); 776})(); 777 778(function scrollToTop() { 779 const menuTitle = document.querySelector('.menu-title'); 780 781 menuTitle.addEventListener('click', function() { 782 document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' }); 783 }); 784})(); 785 786(function controllMenu() { 787 const menu = document.getElementById('mdbook-menu-bar'); 788 789 (function controllPosition() { 790 let scrollTop = document.scrollingElement.scrollTop; 791 let prevScrollTop = scrollTop; 792 const minMenuY = -menu.clientHeight - 50; 793 // When the script loads, the page can be at any scroll (e.g. if you refresh it). 794 menu.style.top = scrollTop + 'px'; 795 // Same as parseInt(menu.style.top.slice(0, -2), but faster 796 let topCache = menu.style.top.slice(0, -2); 797 menu.classList.remove('sticky'); 798 let stickyCache = false; // Same as menu.classList.contains('sticky'), but faster 799 document.addEventListener('scroll', function() { 800 scrollTop = Math.max(document.scrollingElement.scrollTop, 0); 801 // `null` means that it doesn't need to be updated 802 let nextSticky = null; 803 let nextTop = null; 804 const scrollDown = scrollTop > prevScrollTop; 805 const menuPosAbsoluteY = topCache - scrollTop; 806 if (scrollDown) { 807 nextSticky = false;
808 if (menuPosAbsoluteY > 0) { 809 nextTop = prevScrollTop; 810 } 811 } else { 812 if (menuPosAbsoluteY > 0) { 813 nextSticky = true; 814 } else if (menuPosAbsoluteY < minMenuY) { 815 nextTop = prevScrollTop + minMenuY; 816 } 817 } 818 if (nextSticky === true && stickyCache === false) { 819 menu.classList.add('sticky'); 820 stickyCache = true; 821 } else if (nextSticky === false && stickyCache === true) { 822 menu.classList.remove('sticky'); 823 stickyCache = false; 824 } 825 if (nextTop !== null) { 826 menu.style.top = nextTop + 'px'; 827 topCache = nextTop; 828 } 829 prevScrollTop = scrollTop; 830 }, { passive: true }); 831 })(); 832 (function controllBorder() { 833 function updateBorder() { 834 if (menu.offsetTop === 0) { 835 menu.classList.remove('bordered'); 836 } else { 837 menu.classList.add('bordered'); 838 } 839 } 840 updateBorder(); 841 document.addEventListener('scroll', updateBorder, { passive: true }); 842 })(); 843})();
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.