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 // eslint-disable-next-line no-cond-assign 89 while (item = re.exec(txt)) { 90 snippet_crates.push(item[1]); 91 } 92 93 // check if all used crates are available on play.rust-lang.org 94 const all_available = snippet_crates.every(function(elem) { 95 return playground_crates.indexOf(elem) > -1; 96 }); 97 98 if (all_available) { 99 play_button.classList.remove('hidden'); 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 class="fa fa-eye" title="Show hidden lines" \ 211aria-label="Show hidden lines"></button>'; 212 213 // add expand button 214 const pre_block = block.parentNode; 215 pre_block.insertBefore(buttons, pre_block.firstChild); 216 217 pre_block.querySelector('.buttons').addEventListener('click', function(e) { 218 if (e.target.classList.contains('fa-eye')) { 219 e.target.classList.remove('fa-eye'); 220 e.target.classList.add('fa-eye-slash'); 221 e.target.title = 'Hide lines'; 222 e.target.setAttribute('aria-label', e.target.title); 223 224 block.classList.remove('hide-boring'); 225 } else if (e.target.classList.contains('fa-eye-slash')) { 226 e.target.classList.remove('fa-eye-slash'); 227 e.target.classList.add('fa-eye'); 228 e.target.title = 'Show hidden lines'; 229 e.target.setAttribute('aria-label', e.target.title); 230 231 block.classList.add('hide-boring'); 232 } 233 }); 234 }); 235 236 if (window.playground_copyable) { 237 Array.from(document.querySelectorAll('pre code')).forEac
237h(function(block) { 238 const pre_block = block.parentNode; 239 if (!pre_block.classList.contains('playground')) { 240 let buttons = pre_block.querySelector('.buttons'); 241 if (!buttons) { 242 buttons = document.createElement('div'); 243 buttons.className = 'buttons'; 244 pre_block.insertBefore(buttons, pre_block.firstChild); 245 } 246 247 const clipButton = document.createElement('button'); 248 clipButton.className = 'clip-button'; 249 clipButton.title = 'Copy to clipboard'; 250 clipButton.setAttribute('aria-label', clipButton.title); 251 clipButton.innerHTML = '<i class="tooltiptext"></i>'; 252 253 buttons.insertBefore(clipButton, buttons.firstChild); 254 } 255 }); 256 } 257 258 // Process playground code blocks 259 Array.from(document.querySelectorAll('.playground')).forEach(function(pre_block) { 260 // Add play button 261 let buttons = pre_block.querySelector('.buttons'); 262 if (!buttons) { 263 buttons = document.createElement('div'); 264 buttons.className = 'buttons'; 265 pre_block.insertBefore(buttons, pre_block.firstChild); 266 } 267 268 const runCodeButton = document.createElement('button'); 269 runCodeButton.className = 'fa fa-play play-button'; 270 runCodeButton.hidden = true; 271 runCodeButton.title = 'Run this code'; 272 runCodeButton.setAttribute('aria-label', runCodeButton.title); 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 = 'fa fa-history reset-button'; 293 undoChangesButton.title = 'Undo changes'; 294 undoChangesButton.setAttribute('aria-label', undoChangesButton.title); 295 296 buttons.insertBefore(undoChangesButton, buttons.firstChild); 297 298 undoChangesButton.addEventListener('click', function() { 299 const editor = window.ace.edit(code_block); 300 editor.setValue(editor.originalCode); 301 editor.clearSelection(); 302 }); 303 } 304 }); 305})(); 306 307(function themes() { 308 const html = document.querySelector('html'); 309 const themeToggleButton = document.getElementById('theme-toggle'); 310 const themePopup = document.getElementById('theme-list'); 311 const themeColorMetaTag = document.querySelector('meta[name="theme-color"]'); 312 const themeIds = []; 313 themePopup.querySelectorAll('button.theme').forEach(function(el) { 314 themeIds.push(el.id); 315 }); 316 const stylesheets = { 317 ayuHighlight: document.querySelector('#ayu-highlight-css'), 318 tomorrowNight: document.querySelector('#tomorrow-night-css'), 319 highlight: document.querySelector('#highlight-css'), 320 }; 321 322 function showThemes() { 323 themePopup.style.display = 'block'; 324 themeToggleButton.setAttribute('aria-expanded', true); 325 themePopup.querySelector('button#' + get_theme()).focus(); 326 } 327 328 function updateThemeSelected() { 329 themePopup.querySelectorAll('.theme-selected').forEach(function(el) { 330 el.classList.remove('theme-selected'); 331 }); 332 const selected = get_saved_theme() ?? 'default_theme'; 333 let element = themePopup.querySelector('button#' + selected); 334 if (element === null) { 335 // Fall back in case there is no "Default" item. 336 element = themePopup.querySelector('button#' + get_theme()); 337 } 338 element.classList.add('theme-selected'); 339 } 340 341 function hideThemes() { 342 themePopup.style.display = 'none'; 343 themeToggleButton.setAttribute('aria-expanded', false); 344 themeToggleButton.focus(); 345 } 346 347 function get_saved_theme() { 348 let theme = null; 349 try { 350 theme = localStorage.getItem('mdbook-theme'); 351 } catch (e) { 352 // ignore error. 353 } 354 return theme; 355 } 356 357 function delete_saved_theme() { 358 localStorage.removeItem('mdbook-theme'); 359 } 360 361 function get_theme() { 362 const theme = get_saved_theme(); 363 if (theme === null || theme === undefined || !themeIds.includes(theme)) { 364 if (typeof default_dark_theme === 'undefined') { 365 // A customized index.hbs might not define this, so fall back to 366 // old behavior of determining the default on page load. 367 return default_theme; 368 } 369 return window.matchMedia('(prefers-color-scheme: dark)').matches 370 ? default_dark_theme 371 : default_light_theme; 372 } else { 373 return theme; 374 } 375 } 376 377 let previousTheme = default_theme; 378 function set_theme(theme, store = true) { 379 let ace_theme; 380 381 if (theme === 'coal' || theme === 'navy') { 382 stylesheets.ayuHighlight.disabled = true; 383 stylesheets.tomorrowNight.disabled = false;
384 stylesheets.highlight.disabled = true; 385 386 ace_theme = 'ace/theme/tomorrow_night'; 387 } else if (theme === 'ayu') { 388 stylesheets.ayuHighlight.disabled = false; 389 stylesheets.tomorrowNight.disabled = true; 390 stylesheets.highlight.disabled = true; 391 ace_theme = 'ace/theme/tomorrow_night'; 392 } else { 393 stylesheets.ayuHighlight.disabled = true; 394 stylesheets.tomorrowNight.disabled = true; 395 stylesheets.highlight.disabled = false; 396 ace_theme = 'ace/theme/dawn'; 397 } 398 399 setTimeout(function() { 400 themeColorMetaTag.content = getComputedStyle(document.documentElement).backgroundColor; 401 }, 1); 402 403 if (window.ace && window.editors) { 404 window.editors.forEach(function(editor) { 405 editor.setTheme(ace_theme); 406 }); 407 } 408 409 if (store) { 410 try { 411 localStorage.setItem('mdbook-theme', theme); 412 } catch (e) { 413 // ignore error. 414 } 415 } 416 417 html.classList.remove(previousTheme); 418 html.classList.add(theme); 419 previousTheme = theme; 420 updateThemeSelected(); 421 } 422 423 const query = window.matchMedia('(prefers-color-scheme: dark)'); 424 query.onchange = function() { 425 set_theme(get_theme(), false); 426 }; 427 428 // Set theme. 429 set_theme(get_theme(), false); 430 431 themeToggleButton.addEventListener('click', function() { 432 if (themePopup.style.display === 'block') { 433 hideThemes(); 434 } else { 435 showThemes(); 436 } 437 }); 438 439 themePopup.addEventListener('click', function(e) { 440 let theme; 441 if (e.target.className === 'theme') { 442 theme = e.target.id; 443 } else if (e.target.parentElement.className === 'theme') { 444 theme = e.target.parentElement.id; 445 } else { 446 return; 447 } 448 if (theme === 'default_theme' || theme === null) { 449 delete_saved_theme(); 450 set_theme(get_theme(), false); 451 } else { 452 set_theme(theme); 453 } 454 }); 455 456 themePopup.addEventListener('focusout', function(e) { 457 // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below) 458 if (!!e.relatedTarget && 459 !themeToggleButton.contains(e.relatedTarget) && 460 !themePopup.contains(e.relatedTarget) 461 ) { 462 hideThemes(); 463 } 464 }); 465 466 // Should not be needed, but it works around an issue on macOS & iOS: 467 // https://github.com/rust-lang/mdBook/issues/628 468 document.addEventListener('click', function(e) { 469 if (themePopup.style.display === 'block' && 470 !themeToggleButton.contains(e.target) && 471 !themePopup.contains(e.target) 472 ) { 473 hideThemes(); 474 } 475 }); 476 477 document.addEventListener('keydown', function(e) { 478 if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { 479 return; 480 } 481 if (!themePopup.contains(e.target)) { 482 return; 483 } 484 485 let li; 486 switch (e.key) { 487 case 'Escape': 488 e.preventDefault(); 489 hideThemes(); 490 break; 491 case 'ArrowUp': 492 e.preventDefault(); 493 li = document.activeElement.parentElement; 494 if (li && li.previousElementSibling) { 495 li.previousElementSibling.querySelector('button').focus(); 496 } 497 break; 498 case 'ArrowDown': 499 e.preventDefault(); 500 li = document.activeElement.parentElement; 501 if (li && li.nextElementSibling) { 502 li.nextElementSibling.querySelector('button').focus(); 503 } 504 break; 505 case 'Home': 506 e.preventDefault(); 507 themePopup.querySelector('li:first-child button').focus(); 508 break; 509 case 'End': 510 e.preventDefault(); 511 themePopup.querySelector('li:last-child button').focus(); 512 break; 513 } 514 }); 515})(); 516 517(function sidebar() { 518 const sidebar = document.getElementById('sidebar'); 519 const sidebarLinks = document.querySelectorAll('#sidebar a'); 520 const sidebarToggleButton = document.getElementById('sidebar-toggle'); 521 const sidebarResizeHandle = document.getElementById('sidebar-resize-handle'); 522 const sidebarCheckbox = document.getElementById('sidebar-toggle-anchor'); 523 let firstContact = null; 524 525 526 /* Because we cannot change the `display` using only CSS after/before the transition, we 527 need JS to do it. We change the display to prevent the browsers search to find text inside
528 the collapsed sidebar. */ 529 if (!document.documentElement.classList.contains('sidebar-visible')) { 530 sidebar.style.display = 'none'; 531 } 532 sidebar.addEventListener('transitionend', () => { 533 /* We only change the display to "none" if we're collapsing the sidebar. */ 534 if (!sidebarCheckbox.checked) { 535 sidebar.style.display = 'none'; 536 } 537 }); 538 sidebarToggleButton.addEventListener('click', () => { 539 /* To allow the sidebar expansion animation, we first need to put back the display. */ 540 if (!sidebarCheckbox.checked) { 541 sidebar.style.display = ''; 542 // Workaround for Safari skipping the animation when changing 543 // `display` and a transform in the same event loop. This forces a 544 // reflow after updating the display. 545 sidebar.offsetHeight; 546 } 547 }); 548 549 function showSidebar() { 550 document.documentElement.classList.add('sidebar-visible'); 551 Array.from(sidebarLinks).forEach(function(link) { 552 link.setAttribute('tabIndex', 0); 553 }); 554 sidebarToggleButton.setAttribute('aria-expanded', true); 555 sidebar.setAttribute('aria-hidden', false); 556 try { 557 localStorage.setItem('mdbook-sidebar', 'visible'); 558 } catch (e) { 559 // Ignore error. 560 } 561 } 562 563 function hideSidebar() { 564 document.documentElement.classList.remove('sidebar-visible'); 565 Array.from(sidebarLinks).forEach(function(link) { 566 link.setAttribute('tabIndex', -1); 567 }); 568 sidebarToggleButton.setAttribute('aria-expanded', false); 569 sidebar.setAttribute('aria-hidden', true); 570 try { 571 localStorage.setItem('mdbook-sidebar', 'hidden'); 572 } catch (e) { 573 // Ignore error. 574 } 575 } 576 577 // Toggle sidebar 578 sidebarCheckbox.addEventListener('change', function sidebarToggle() { 579 if (sidebarCheckbox.checked) { 580 const current_width = parseInt( 581 document.documentElement.style.getPropertyValue('--sidebar-target-width'), 10); 582 if (current_width < 150) { 583 document.documentElement.style.setProperty('--sidebar-target-width', '150px'); 584 } 585 showSidebar(); 586 } else { 587 hideSidebar(); 588 } 589 }); 590 591 sidebarResizeHandle.addEventListener('mousedown', initResize, false); 592 593 function initResize() { 594 window.addEventListener('mousemove', resize, false); 595 window.addEventListener('mouseup', stopResize, false); 596 document.documentElement.classList.add('sidebar-resizing'); 597 } 598 function resize(e) { 599 let pos = e.clientX - sidebar.offsetLeft; 600 if (pos < 20) { 601 hideSidebar(); 602 } else { 603 if (!document.documentElement.classList.contains('sidebar-visible')) { 604 showSidebar(); 605 } 606 pos = Math.min(pos, window.innerWidth - 100); 607 document.documentElement.style.setProperty('--sidebar-target-width', pos + 'px'); 608 } 609 } 610 //on mouseup remove windows functions mousemove & mouseup 611 function stopResize() { 612 document.documentElement.classList.remove('sidebar-resizing'); 613 window.removeEventListener('mousemove', resize, false); 614 window.removeEventListener('mouseup', stopResize, false); 615 } 616 617 document.addEventListener('touchstart', function(e) { 618 firstContact = { 619 x: e.touches[0].clientX, 620 time: Date.now(), 621 }; 622 }, { passive: true }); 623 624 document.addEventListener('touchmove', function(e) { 625 if (!firstContact) { 626 return; 627 } 628 629 const curX = e.touches[0].clientX; 630 const xDiff = curX - firstContact.x, 631 tDiff = Date.now() - firstContact.time; 632 633 if (tDiff < 250 && Math.abs(xDiff) >= 150) { 634 if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300)) { 635 showSidebar(); 636 } else if (xDiff < 0 && curX < 300) { 637 hideSidebar(); 638 } 639 640 firstContact = null; 641 } 642 }, { passive: true }); 643})(); 644 645(function chapterNavigation() { 646 document.addEventListener('keydown', function(e) { 647 if (e.altKey || e.ctrlKey || e.metaKey) { 648 return; 649 } 650 if (window.search && window.search.hasFocus()) { 651 return; 652 } 653 const html = document.querySelector('html'); 654 655 function next() { 656 const nextButton = document.querySelector('.nav-chapter
656s.next'); 657 if (nextButton) { 658 window.location.href = nextButton.href; 659 } 660 } 661 function prev() { 662 const previousButton = document.querySelector('.nav-chapters.previous'); 663 if (previousButton) { 664 window.location.href = previousButton.href; 665 } 666 } 667 function showHelp() { 668 const container = document.getElementById('mdbook-help-container'); 669 const overlay = document.getElementById('mdbook-help-popup'); 670 container.style.display = 'flex'; 671 672 // Clicking outside the popup will dismiss it. 673 const mouseHandler = event => { 674 if (overlay.contains(event.target)) { 675 return; 676 } 677 if (event.button !== 0) { 678 return; 679 } 680 event.preventDefault(); 681 event.stopPropagation(); 682 document.removeEventListener('mousedown', mouseHandler); 683 hideHelp(); 684 }; 685 686 // Pressing esc will dismiss the popup. 687 const escapeKeyHandler = event => { 688 if (event.key === 'Escape') { 689 event.preventDefault(); 690 event.stopPropagation(); 691 document.removeEventListener('keydown', escapeKeyHandler, true); 692 hideHelp(); 693 } 694 }; 695 document.addEventListener('keydown', escapeKeyHandler, true); 696 document.getElementById('mdbook-help-container') 697 .addEventListener('mousedown', mouseHandler); 698 } 699 function hideHelp() { 700 document.getElementById('mdbook-help-container').style.display = 'none'; 701 } 702 703 // Usually needs the Shift key to be pressed 704 switch (e.key) { 705 case '?': 706 e.preventDefault(); 707 showHelp(); 708 break; 709 } 710 711 // Rest of the keys are only active when the Shift key is not pressed 712 if (e.shiftKey) { 713 return; 714 } 715 716 switch (e.key) { 717 case 'ArrowRight': 718 e.preventDefault(); 719 if (html.dir === 'rtl') { 720 prev(); 721 } else { 722 next(); 723 } 724 break; 725 case 'ArrowLeft': 726 e.preventDefault(); 727 if (html.dir === 'rtl') { 728 next(); 729 } else { 730 prev(); 731 } 732 break; 733 } 734 }); 735})(); 736 737(function clipboard() { 738 const clipButtons = document.querySelectorAll('.clip-button'); 739 740 function hideTooltip(elem) { 741 elem.firstChild.innerText = ''; 742 elem.className = 'clip-button'; 743 } 744 745 function showTooltip(elem, msg) { 746 elem.firstChild.innerText = msg; 747 elem.className = 'clip-button tooltipped'; 748 } 749 750 const clipboardSnippets = new ClipboardJS('.clip-button', { 751 text: function(trigger) { 752 hideTooltip(trigger); 753 const playground = trigger.closest('pre'); 754 return playground_text(playground, false); 755 }, 756 }); 757 758 Array.from(clipButtons).forEach(function(clipButton) { 759 clipButton.addEventListener('mouseout', function(e) { 760 hideTooltip(e.currentTarget); 761 }); 762 }); 763 764 clipboardSnippets.on('success', function(e) { 765 e.clearSelection(); 766 showTooltip(e.trigger, 'Copied!'); 767 }); 768 769 clipboardSnippets.on('error', function(e) { 770 showTooltip(e.trigger, 'Clipboard error!'); 771 }); 772})(); 773 774(function scrollToTop() { 775 const menuTitle = document.querySelector('.menu-title'); 776 777 menuTitle.addEventListener('click', function() { 778 document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' }); 779 }); 780})(); 781 782(function controllMenu() { 783 const menu = document.getElementById('menu-bar'); 784 785 (function controllPosition() { 786 let scrollTop = document.scrollingElement.scrollTop; 787 let prevScrollTop = scrollTop; 788 const minMenuY = -menu.clientHeight - 50; 789 // When the script loads, the page can be at any scroll (e.g. if you refresh it). 790 menu.style.top = scrollTop + 'px'; 791 // Same as parseInt(menu.style.top.slice(0, -2), but faster 792 let topCache = menu.style.top.slice(0, -2); 793 menu.classList.remove('sticky'); 794 let stickyCache = false; // Same as menu.classList.contains('sticky'), but faster 795 document.addEventListener('scroll', function() { 796 scrollTop = Math.max(document.scrollingElement.scrollTop, 0); 797 // `null` means that it doesn't need to be updated 798 let nextSticky = null; 799 let nextTop = null; 800 const scrollDown = scrollTop > prevScrollTop; 801 const menuPosAbsoluteY = topCache - scrollTop; 802 if (scrollDown) { 803 nextSticky = false;
804 if (menuPosAbsoluteY > 0) { 805 nextTop = prevScrollTop; 806 } 807 } else { 808 if (menuPosAbsoluteY > 0) { 809 nextSticky = true; 810 } else if (menuPosAbsoluteY < minMenuY) { 811 nextTop = prevScrollTop + minMenuY; 812 } 813 } 814 if (nextSticky === true && stickyCache === false) { 815 menu.classList.add('sticky'); 816 stickyCache = true; 817 } else if (nextSticky === false && stickyCache === true) { 818 menu.classList.remove('sticky'); 819 stickyCache = false; 820 } 821 if (nextTop !== null) { 822 menu.style.top = nextTop + 'px'; 823 topCache = nextTop; 824 } 825 prevScrollTop = scrollTop; 826 }, { passive: true }); 827 })(); 828 (function controllBorder() { 829 function updateBorder() { 830 if (menu.offsetTop === 0) { 831 menu.classList.remove('bordered'); 832 } else { 833 menu.classList.add('bordered'); 834 } 835 } 836 updateBorder(); 837 document.addEventListener('scroll', updateBorder, { passive: true }); 838 })(); 839})();
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.