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