mdbook/crates/mdbook-html/front-end/js/book.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

855 lines
30 KiB
JavaScript
Raw Normal View History

2025-02-21 22:55:23 +01:00
'use strict';
/* global default_theme, default_dark_theme, default_light_theme, hljs, ClipboardJS */
// Fix back button cache problem
2025-02-21 22:55:23 +01:00
window.onunload = function() { };
2018-01-12 19:44:13 +01:00
// Global variable, shared between modules
2022-12-13 15:22:03 -08:00
function playground_text(playground, hidden = true) {
2025-02-21 22:55:23 +01:00
const code_block = playground.querySelector('code');
2025-02-21 22:55:23 +01:00
if (window.ace && code_block.classList.contains('editable')) {
const editor = window.ace.edit(code_block);
return editor.getValue();
2022-12-13 15:22:03 -08:00
} else if (hidden) {
return code_block.textContent;
2017-06-29 00:35:20 -04:00
} else {
2022-10-14 16:53:59 -07:00
return code_block.innerText;
2017-06-29 00:35:20 -04:00
}
}
/**
* Helper for global keypress handlers so they don't trigger when certain elements are active.
* @returns {boolean} True if the keypress handler should be skipped.
*/
function mdbook_something_else_has_focus(e) {
return /^(?:input|select|textarea)$/i.test(e.target.nodeName);
}
(function codeSnippets() {
function fetch_with_timeout(url, options, timeout = 6000) {
return Promise.race([
fetch(url, options),
2025-02-21 22:55:23 +01:00
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)),
]);
}
2025-02-21 22:55:23 +01:00
const playgrounds = Array.from(document.querySelectorAll('.playground'));
if (playgrounds.length > 0) {
2025-02-21 22:55:23 +01:00
fetch_with_timeout('https://play.rust-lang.org/meta/crates', {
headers: {
2025-02-21 22:55:23 +01:00
'Content-Type': 'application/json',
},
method: 'POST',
mode: 'cors',
})
2025-02-21 22:55:23 +01:00
.then(response => response.json())
.then(response => {
// get list of crates available in the rust playground
2025-02-21 22:55:23 +01:00
const playground_crates = response.crates.map(item => item['id']);
playgrounds.forEach(block => handle_crate_list_update(block, playground_crates));
});
}
function handle_crate_list_update(playground_block, playground_crates) {
// update the play buttons after receiving the response
update_play_button(playground_block, playground_crates);
// and install on change listener to dynamically update ACE editors
if (window.ace) {
2025-02-21 22:55:23 +01:00
const code_block = playground_block.querySelector('code');
if (code_block.classList.contains('editable')) {
const editor = window.ace.edit(code_block);
editor.addEventListener('change', () => {
update_play_button(playground_block, playground_crates);
});
2019-10-11 21:16:06 +09:00
// add Ctrl-Enter command to execute rust code
editor.commands.addCommand({
2025-02-21 22:55:23 +01:00
name: 'run',
2019-10-11 21:16:06 +09:00
bindKey: {
2025-02-21 22:55:23 +01:00
win: 'Ctrl-Enter',
mac: 'Ctrl-Enter',
2019-10-11 21:16:06 +09:00
},
2025-02-21 22:55:23 +01:00
exec: _editor => run_rust_code(playground_block),
2019-10-11 21:16:06 +09:00
});
}
}
}
// updates the visibility of play button based on `no_run` class and
// used crates vs ones available on https://play.rust-lang.org
function update_play_button(pre_block, playground_crates) {
2025-02-21 22:55:23 +01:00
const play_button = pre_block.querySelector('.play-button');
// skip if code is `no_run`
2025-02-21 22:55:23 +01:00
if (pre_block.querySelector('code').classList.contains('no_run')) {
play_button.classList.add('hidden');
return;
}
// get list of `extern crate`'s from snippet
2025-02-21 22:55:23 +01:00
const txt = playground_text(pre_block);
const re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g;
const snippet_crates = [];
let item;
while (item = re.exec(txt)) {
snippet_crates.push(item[1]);
}
// check if all used crates are available on play.rust-lang.org
2025-02-21 22:55:23 +01:00
const all_available = snippet_crates.every(function(elem) {
return playground_crates.indexOf(elem) > -1;
});
if (all_available) {
2025-02-21 22:55:23 +01:00
play_button.classList.remove('hidden');
play_button.hidden = false;
2015-09-11 01:16:29 +02:00
} else {
2025-02-21 22:55:23 +01:00
play_button.classList.add('hidden');
2015-09-11 01:16:29 +02:00
}
}
2015-09-11 01:16:29 +02:00
function run_rust_code(code_block) {
2025-02-21 22:55:23 +01:00
let result_block = code_block.querySelector('.result');
if (!result_block) {
result_block = document.createElement('code');
result_block.className = 'result hljs language-bash';
Improve accessibility (#535) * fix(theme/index): Use nav element for Table of Content * fix(renderer/html_handlebars/helpers/toc): Use ol instead of ul Chapters and sections are ordered, so we should use the appropriate HTML tag. * fix(renderer/html_handlebars/helpers/toc): Hide section number from screen readers Screen readers have this functionality build-in, no need to present this. Ideally, this should not even be in the DOM tree, since the numbers can be shown by using CSS. * fix(theme/index): Remove tabIndex="-1" from .page Divs are not focusable by default * fix(theme): Make sidebar accessible Using aria-hidden (together with tabIndex) takes the links out of the tab order. http://heydonworks.com/practical_aria_examples/#progressive-collapsibles * fix(theme/index): Wrap content inside main tag The main tag helps users skip additional content on the page. * fix(theme/book): Don't focus .page on page load The main content is identified by the main tag, not by auto-focusing it on page load. * fix(theme/index): Make page controls accessible * fix: Make theme selector accessible - Use ul and li (since it is a list) - Add aria-expanded and aria-haspopup to the toggle button - Use button instead of div (buttons are accessible by default) - Handle Esc key (close popup) - Adjust CSS to keep same visual style * fix(theme/stylus/sidebar): Make link clickable area wider Links now expand to fill the entire row. * fix(theme): Wrap header buttons and improve animation performance Previously, the header had a fixed height, which meant that sometimes the print button was not visible. Animating the left property is expensive, which lead to laggy animations - transform is much cheaper and has the same effect. * fix(theme/stylus/theme-popup): Theme button inherits color Bug introduced while making the popup accessible * fix(theme/book): Handle edge case when toggling sidebar Bug introduced when switching from animating left to using transform.
2018-01-15 14:26:53 +01:00
code_block.append(result_block);
}
2015-09-11 01:16:29 +02:00
2025-02-21 22:55:23 +01:00
const text = playground_text(code_block);
const classes = code_block.querySelector('code').classList;
let edition = '2015';
2025-02-26 05:50:25 -08:00
classes.forEach(className => {
2025-02-21 22:55:23 +01:00
if (className.startsWith('edition')) {
2025-02-26 05:50:25 -08:00
edition = className.slice(7);
}
});
2025-02-21 22:55:23 +01:00
const params = {
version: 'stable',
optimize: '0',
code: text,
2025-02-21 22:55:23 +01:00
edition: edition,
2018-07-30 18:37:45 -05:00
};
2021-07-04 20:25:04 -07:00
2025-02-21 22:55:23 +01:00
if (text.indexOf('#![feature') !== -1) {
params.version = 'nightly';
}
2025-02-21 22:55:23 +01:00
result_block.innerText = 'Running...';
2025-02-21 22:55:23 +01:00
fetch_with_timeout('https://play.rust-lang.org/evaluate.json', {
headers: {
2025-02-21 22:55:23 +01:00
'Content-Type': 'application/json',
},
method: 'POST',
mode: 'cors',
2025-02-21 22:55:23 +01:00
body: JSON.stringify(params),
})
2025-02-21 22:55:23 +01:00
.then(response => response.json())
.then(response => {
if (response.result.trim() === '') {
result_block.innerText = 'No output';
result_block.classList.add('result-no-output');
} else {
result_block.innerText = response.result;
result_block.classList.remove('result-no-output');
}
})
.catch(error => result_block.innerText = 'Playground Communication: ' + error.message);
}
// Syntax highlighting Configuration
hljs.configure({
tabReplace: ' ', // 4 spaces
2025-02-21 22:55:23 +01:00
languages: [], // Languages used for auto-detection
});
2025-02-21 22:55:23 +01:00
const code_nodes = Array
.from(document.querySelectorAll('code'))
// Don't highlight `inline code` blocks in headers.
2025-02-21 22:55:23 +01:00
.filter(function(node) {
return !node.parentElement.classList.contains('header');
});
if (window.ace) {
// language-rust class needs to be removed for editable
// blocks or highlightjs will capture events
code_nodes
2025-02-21 22:55:23 +01:00
.filter(function(node) {
return node.classList.contains('editable');
})
.forEach(function(block) {
block.classList.remove('language-rust');
});
code_nodes
2025-02-21 22:55:23 +01:00
.filter(function(node) {
return !node.classList.contains('editable');
})
.forEach(function(block) {
hljs.highlightBlock(block);
});
} else {
2025-02-21 22:55:23 +01:00
code_nodes.forEach(function(block) {
hljs.highlightBlock(block);
});
}
// Adding the hljs class gives code blocks the color css
// even if highlighting doesn't apply
2025-02-21 22:55:23 +01:00
code_nodes.forEach(function(block) {
block.classList.add('hljs');
});
2025-02-21 22:55:23 +01:00
Array.from(document.querySelectorAll('code.hljs')).forEach(function(block) {
2025-02-21 22:55:23 +01:00
const lines = Array.from(block.querySelectorAll('.boring'));
// If no lines were hidden, return
2025-02-21 22:55:23 +01:00
if (!lines.length) {
return;
}
block.classList.add('hide-boring');
2025-02-21 22:55:23 +01:00
const buttons = document.createElement('div');
2018-01-12 19:44:13 +01:00
buttons.className = 'buttons';
buttons.innerHTML = '<button title="Show hidden lines" \
2025-02-21 22:55:23 +01:00
aria-label="Show hidden lines"></button>';
buttons.firstChild.innerHTML = document.getElementById('fa-eye').innerHTML;
2018-01-12 19:44:13 +01:00
// add expand button
2025-02-21 22:55:23 +01:00
const pre_block = block.parentNode;
pre_block.insertBefore(buttons, pre_block.firstChild);
2018-01-12 19:44:13 +01:00
buttons.firstChild.addEventListener('click', function(e) {
if (this.title === 'Show hidden lines') {
this.innerHTML = document.getElementById('fa-eye-slash').innerHTML;
this.title = 'Hide lines';
this.setAttribute('aria-label', e.target.title);
2018-01-12 19:44:13 +01:00
block.classList.remove('hide-boring');
} else if (this.title === 'Hide lines') {
this.innerHTML = document.getElementById('fa-eye').innerHTML;
this.title = 'Show hidden lines';
this.setAttribute('aria-label', e.target.title);
2018-01-12 19:44:13 +01:00
block.classList.add('hide-boring');
}
});
});
2018-01-12 19:44:13 +01:00
if (window.playground_copyable) {
2025-02-21 22:55:23 +01:00
Array.from(document.querySelectorAll('pre code')).forEach(function(block) {
const pre_block = block.parentNode;
if (!pre_block.classList.contains('playground')) {
2025-02-21 22:55:23 +01:00
let buttons = pre_block.querySelector('.buttons');
if (!buttons) {
buttons = document.createElement('div');
buttons.className = 'buttons';
pre_block.insertBefore(buttons, pre_block.firstChild);
}
2025-02-21 22:55:23 +01:00
const clipButton = document.createElement('button');
clipButton.className = 'clip-button';
clipButton.title = 'Copy to clipboard';
clipButton.setAttribute('aria-label', clipButton.title);
2025-02-21 22:55:23 +01:00
clipButton.innerHTML = '<i class="tooltiptext"></i>';
buttons.insertBefore(clipButton, buttons.firstChild);
}
});
}
// Process playground code blocks
2025-02-21 22:55:23 +01:00
Array.from(document.querySelectorAll('.playground')).forEach(function(pre_block) {
// Add play button
2025-02-21 22:55:23 +01:00
let buttons = pre_block.querySelector('.buttons');
if (!buttons) {
2018-01-12 19:44:13 +01:00
buttons = document.createElement('div');
buttons.className = 'buttons';
pre_block.insertBefore(buttons, pre_block.firstChild);
}
2025-02-21 22:55:23 +01:00
const runCodeButton = document.createElement('button');
runCodeButton.className = 'play-button';
2018-01-12 19:44:13 +01:00
runCodeButton.hidden = true;
runCodeButton.title = 'Run this code';
runCodeButton.setAttribute('aria-label', runCodeButton.title);
runCodeButton.innerHTML = document.getElementById('fa-play').innerHTML;
2018-01-12 19:44:13 +01:00
buttons.insertBefore(runCodeButton, buttons.firstChild);
2025-02-21 22:55:23 +01:00
runCodeButton.addEventListener('click', () => {
run_rust_code(pre_block);
});
2018-01-12 19:44:13 +01:00
if (window.playground_copyable) {
2025-02-21 22:55:23 +01:00
const copyCodeClipboardButton = document.createElement('button');
copyCodeClipboardButton.className = 'clip-button';
copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>';
copyCodeClipboardButton.title = 'Copy to clipboard';
copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
}
2025-02-21 22:55:23 +01:00
const code_block = pre_block.querySelector('code');
if (window.ace && code_block.classList.contains('editable')) {
const undoChangesButton = document.createElement('button');
undoChangesButton.className = 'reset-button';
2018-01-12 19:44:13 +01:00
undoChangesButton.title = 'Undo changes';
undoChangesButton.setAttribute('aria-label', undoChangesButton.title);
undoChangesButton.innerHTML +=
document.getElementById('fa-clock-rotate-left').innerHTML;
2018-01-12 19:44:13 +01:00
buttons.insertBefore(undoChangesButton, buttons.firstChild);
2018-01-12 19:44:13 +01:00
2025-02-21 22:55:23 +01:00
undoChangesButton.addEventListener('click', function() {
const editor = window.ace.edit(code_block);
2018-01-12 19:44:13 +01:00
editor.setValue(editor.originalCode);
editor.clearSelection();
});
}
});
})();
(function themes() {
2025-02-21 22:55:23 +01:00
const html = document.querySelector('html');
const themeToggleButton = document.getElementById('mdbook-theme-toggle');
const themePopup = document.getElementById('mdbook-theme-list');
2025-02-21 22:55:23 +01:00
const themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
const themeIds = [];
themePopup.querySelectorAll('button.theme').forEach(function(el) {
themeIds.push(el.id);
});
2025-02-21 22:55:23 +01:00
const stylesheets = {
ayuHighlight: document.querySelector('#mdbook-ayu-highlight-css'),
tomorrowNight: document.querySelector('#mdbook-tomorrow-night-css'),
highlight: document.querySelector('#mdbook-highlight-css'),
};
function showThemes() {
themePopup.style.display = 'block';
themeToggleButton.setAttribute('aria-expanded', true);
themePopup.querySelector('button#mdbook-theme-' + get_theme()).focus();
}
2022-11-21 15:27:39 -08:00
function updateThemeSelected() {
2025-02-21 22:55:23 +01:00
themePopup.querySelectorAll('.theme-selected').forEach(function(el) {
2022-11-21 15:27:39 -08:00
el.classList.remove('theme-selected');
});
const selected = get_saved_theme() ?? 'default_theme';
let element = themePopup.querySelector('button#mdbook-theme-' + selected);
if (element === null) {
// Fall back in case there is no "Default" item.
element = themePopup.querySelector('button#mdbook-theme-' + get_theme());
}
element.classList.add('theme-selected');
2022-11-21 15:27:39 -08:00
}
function hideThemes() {
themePopup.style.display = 'none';
themeToggleButton.setAttribute('aria-expanded', false);
themeToggleButton.focus();
}
function get_saved_theme() {
let theme = null;
2025-02-21 22:55:23 +01:00
try {
theme = localStorage.getItem('mdbook-theme');
} catch {
2025-02-21 22:55:23 +01:00
// ignore error.
}
return theme;
}
function delete_saved_theme() {
localStorage.removeItem('mdbook-theme');
}
function get_theme() {
const theme = get_saved_theme();
if (theme === null || theme === undefined || !themeIds.includes('mdbook-theme-' + theme)) {
if (typeof default_dark_theme === 'undefined') {
// A customized index.hbs might not define this, so fall back to
// old behavior of determining the default on page load.
return default_theme;
}
return window.matchMedia('(prefers-color-scheme: dark)').matches
? default_dark_theme
: default_light_theme;
2020-03-20 11:10:50 -07:00
} else {
return theme;
}
}
let previousTheme = default_theme;
function set_theme(theme, store = true) {
let ace_theme;
2025-02-21 22:55:23 +01:00
if (theme === 'coal' || theme === 'navy') {
stylesheets.ayuHighlight.disabled = true;
stylesheets.tomorrowNight.disabled = false;
stylesheets.highlight.disabled = true;
2025-02-21 22:55:23 +01:00
ace_theme = 'ace/theme/tomorrow_night';
} else if (theme === 'ayu') {
stylesheets.ayuHighlight.disabled = false;
stylesheets.tomorrowNight.disabled = true;
stylesheets.highlight.disabled = true;
2025-02-21 22:55:23 +01:00
ace_theme = 'ace/theme/tomorrow_night';
} else {
stylesheets.ayuHighlight.disabled = true;
stylesheets.tomorrowNight.disabled = true;
stylesheets.highlight.disabled = false;
2025-02-21 22:55:23 +01:00
ace_theme = 'ace/theme/dawn';
}
2025-02-21 22:55:23 +01:00
setTimeout(function() {
themeColorMetaTag.content = getComputedStyle(document.documentElement).backgroundColor;
}, 1);
if (window.ace && window.editors) {
2025-02-21 22:55:23 +01:00
window.editors.forEach(function(editor) {
editor.setTheme(ace_theme);
});
}
if (store) {
2025-02-21 22:55:23 +01:00
try {
localStorage.setItem('mdbook-theme', theme);
} catch {
2025-02-21 22:55:23 +01:00
// ignore error.
}
}
html.classList.remove(previousTheme);
html.classList.add(theme);
previousTheme = theme;
2022-11-21 15:27:39 -08:00
updateThemeSelected();
}
const query = window.matchMedia('(prefers-color-scheme: dark)');
query.onchange = function() {
set_theme(get_theme(), false);
};
// Set theme.
set_theme(get_theme(), false);
2025-02-21 22:55:23 +01:00
themeToggleButton.addEventListener('click', function() {
if (themePopup.style.display === 'block') {
hideThemes();
} else {
showThemes();
}
});
2025-02-21 22:55:23 +01:00
themePopup.addEventListener('click', function(e) {
let theme;
if (e.target.className === 'theme') {
theme = e.target.id;
2025-02-21 22:55:23 +01:00
} else if (e.target.parentElement.className === 'theme') {
theme = e.target.parentElement.id;
} else {
return;
}
theme = theme.replace(/^mdbook-theme-/, '');
if (theme === 'default_theme' || theme === null) {
delete_saved_theme();
set_theme(get_theme(), false);
} else {
set_theme(theme);
}
});
themePopup.addEventListener('focusout', function(e) {
// e.relatedTarget is null in Safari and Firefox on macOS (see workaround below)
2025-02-21 22:55:23 +01:00
if (!!e.relatedTarget &&
!themeToggleButton.contains(e.relatedTarget) &&
!themePopup.contains(e.relatedTarget)
) {
hideThemes();
}
});
2025-02-21 22:55:23 +01:00
// Should not be needed, but it works around an issue on macOS & iOS:
// https://github.com/rust-lang/mdBook/issues/628
document.addEventListener('click', function(e) {
2025-02-21 22:55:23 +01:00
if (themePopup.style.display === 'block' &&
!themeToggleButton.contains(e.target) &&
!themePopup.contains(e.target)
) {
hideThemes();
}
});
2025-02-21 22:55:23 +01:00
document.addEventListener('keydown', function(e) {
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) {
return;
}
if (!themePopup.contains(e.target)) {
return;
}
2025-02-21 22:55:23 +01:00
let li;
switch (e.key) {
2025-02-21 22:55:23 +01:00
case 'Escape':
e.preventDefault();
hideThemes();
break;
case 'ArrowUp':
e.preventDefault();
li = document.activeElement.parentElement;
if (li && li.previousElementSibling) {
li.previousElementSibling.querySelector('button').focus();
}
break;
case 'ArrowDown':
e.preventDefault();
li = document.activeElement.parentElement;
if (li && li.nextElementSibling) {
li.nextElementSibling.querySelector('button').focus();
}
break;
case 'Home':
e.preventDefault();
themePopup.querySelector('li:first-child button').focus();
break;
case 'End':
e.preventDefault();
themePopup.querySelector('li:last-child button').focus();
break;
}
});
})();
(function sidebar() {
const sidebar = document.getElementById('mdbook-sidebar');
const sidebarLinks = document.querySelectorAll('#mdbook-sidebar a');
const sidebarToggleButton = document.getElementById('mdbook-sidebar-toggle');
const sidebarResizeHandle = document.getElementById('mdbook-sidebar-resize-handle');
const sidebarCheckbox = document.getElementById('mdbook-sidebar-toggle-anchor');
2025-02-21 22:55:23 +01:00
let firstContact = null;
/* Because we cannot change the `display` using only CSS after/before the transition, we
need JS to do it. We change the display to prevent the browsers search to find text inside
the collapsed sidebar. */
if (!document.documentElement.classList.contains('sidebar-visible')) {
sidebar.style.display = 'none';
}
sidebar.addEventListener('transitionend', () => {
/* We only change the display to "none" if we're collapsing the sidebar. */
if (!sidebarCheckbox.checked) {
sidebar.style.display = 'none';
}
});
sidebarToggleButton.addEventListener('click', () => {
/* To allow the sidebar expansion animation, we first need to put back the display. */
if (!sidebarCheckbox.checked) {
sidebar.style.display = '';
// Workaround for Safari skipping the animation when changing
// `display` and a transform in the same event loop. This forces a
// reflow after updating the display.
sidebar.offsetHeight;
}
});
function showSidebar() {
document.documentElement.classList.add('sidebar-visible');
2025-02-21 22:55:23 +01:00
Array.from(sidebarLinks).forEach(function(link) {
link.setAttribute('tabIndex', 0);
});
sidebarToggleButton.setAttribute('aria-expanded', true);
sidebar.setAttribute('aria-hidden', false);
2025-02-21 22:55:23 +01:00
try {
localStorage.setItem('mdbook-sidebar', 'visible');
} catch {
2025-02-21 22:55:23 +01:00
// Ignore error.
}
}
function hideSidebar() {
document.documentElement.classList.remove('sidebar-visible');
2025-02-21 22:55:23 +01:00
Array.from(sidebarLinks).forEach(function(link) {
link.setAttribute('tabIndex', -1);
});
sidebarToggleButton.setAttribute('aria-expanded', false);
sidebar.setAttribute('aria-hidden', true);
2025-02-21 22:55:23 +01:00
try {
localStorage.setItem('mdbook-sidebar', 'hidden');
} catch {
2025-02-21 22:55:23 +01:00
// Ignore error.
}
}
// Toggle sidebar
sidebarCheckbox.addEventListener('change', function sidebarToggle() {
if (sidebarCheckbox.checked) {
2025-02-21 22:55:23 +01:00
const current_width = parseInt(
document.documentElement.style.getPropertyValue('--sidebar-target-width'), 10);
if (current_width < 150) {
document.documentElement.style.setProperty('--sidebar-target-width', '150px');
}
showSidebar();
} else {
hideSidebar();
}
});
2018-12-12 21:31:01 +02:00
sidebarResizeHandle.addEventListener('mousedown', initResize, false);
2025-02-21 22:55:23 +01:00
function initResize() {
2018-12-12 21:31:01 +02:00
window.addEventListener('mousemove', resize, false);
window.addEventListener('mouseup', stopResize, false);
document.documentElement.classList.add('sidebar-resizing');
2018-12-12 21:31:01 +02:00
}
function resize(e) {
2025-02-21 22:55:23 +01:00
let pos = e.clientX - sidebar.offsetLeft;
if (pos < 20) {
hideSidebar();
} else {
if (!document.documentElement.classList.contains('sidebar-visible')) {
showSidebar();
}
pos = Math.min(pos, window.innerWidth - 100);
document.documentElement.style.setProperty('--sidebar-target-width', pos + 'px');
}
2018-12-12 21:31:01 +02:00
}
//on mouseup remove windows functions mousemove & mouseup
2025-02-21 22:55:23 +01:00
function stopResize() {
document.documentElement.classList.remove('sidebar-resizing');
2018-12-12 21:31:01 +02:00
window.removeEventListener('mousemove', resize, false);
window.removeEventListener('mouseup', stopResize, false);
}
2025-02-21 22:55:23 +01:00
document.addEventListener('touchstart', function(e) {
firstContact = {
x: e.touches[0].clientX,
2025-02-21 22:55:23 +01:00
time: Date.now(),
};
}, { passive: true });
2025-02-21 22:55:23 +01:00
document.addEventListener('touchmove', function(e) {
if (!firstContact) {
return;
2025-02-21 22:55:23 +01:00
}
2025-02-21 22:55:23 +01:00
const curX = e.touches[0].clientX;
const xDiff = curX - firstContact.x,
tDiff = Date.now() - firstContact.time;
if (tDiff < 250 && Math.abs(xDiff) >= 150) {
2025-02-21 22:55:23 +01:00
if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300)) {
showSidebar();
2025-02-21 22:55:23 +01:00
} else if (xDiff < 0 && curX < 300) {
hideSidebar();
2025-02-21 22:55:23 +01:00
}
firstContact = null;
}
}, { passive: true });
})();
(function chapterNavigation() {
2025-02-21 22:55:23 +01:00
document.addEventListener('keydown', function(e) {
if (e.altKey ||
e.ctrlKey ||
e.metaKey ||
window.search && window.search.hasFocus() ||
mdbook_something_else_has_focus(e)
) {
2025-02-21 22:55:23 +01:00
return;
}
2025-02-21 22:55:23 +01:00
const html = document.querySelector('html');
function next() {
2025-02-21 22:55:23 +01:00
const nextButton = document.querySelector('.nav-chapters.next');
if (nextButton) {
window.location.href = nextButton.href;
}
}
function prev() {
2025-02-21 22:55:23 +01:00
const previousButton = document.querySelector('.nav-chapters.previous');
if (previousButton) {
window.location.href = previousButton.href;
}
}
function showHelp() {
const container = document.getElementById('mdbook-help-container');
const overlay = document.getElementById('mdbook-help-popup');
container.style.display = 'flex';
2025-04-02 18:48:25 +03:00
// Clicking outside the popup will dismiss it.
const mouseHandler = event => {
if (overlay.contains(event.target)) {
2025-04-02 18:48:25 +03:00
return;
}
if (event.button !== 0) {
return;
}
event.preventDefault();
event.stopPropagation();
document.removeEventListener('mousedown', mouseHandler);
hideHelp();
};
// Pressing esc will dismiss the popup.
const escapeKeyHandler = event => {
if (event.key === 'Escape') {
event.preventDefault();
event.stopPropagation();
document.removeEventListener('keydown', escapeKeyHandler, true);
2025-04-02 18:48:25 +03:00
hideHelp();
}
};
document.addEventListener('keydown', escapeKeyHandler, true);
document.getElementById('mdbook-help-container')
.addEventListener('mousedown', mouseHandler);
}
function hideHelp() {
document.getElementById('mdbook-help-container').style.display = 'none';
}
// Usually needs the Shift key to be pressed
switch (e.key) {
2025-04-02 18:48:25 +03:00
case '?':
e.preventDefault();
showHelp();
break;
}
// Rest of the keys are only active when the Shift key is not pressed
if (e.shiftKey) {
return;
}
switch (e.key) {
2025-02-21 22:55:23 +01:00
case 'ArrowRight':
e.preventDefault();
if (html.dir === 'rtl') {
prev();
} else {
next();
}
break;
case 'ArrowLeft':
e.preventDefault();
if (html.dir === 'rtl') {
next();
} else {
prev();
}
break;
}
2018-01-12 19:44:13 +01:00
});
})();
(function clipboard() {
2025-02-21 22:55:23 +01:00
const clipButtons = document.querySelectorAll('.clip-button');
2018-01-12 19:44:13 +01:00
function hideTooltip(elem) {
2025-02-21 22:55:23 +01:00
elem.firstChild.innerText = '';
elem.className = 'clip-button';
}
function showTooltip(elem, msg) {
elem.firstChild.innerText = msg;
elem.className = 'clip-button tooltipped';
}
2025-02-21 22:55:23 +01:00
const clipboardSnippets = new ClipboardJS('.clip-button', {
text: function(trigger) {
hideTooltip(trigger);
2025-02-21 22:55:23 +01:00
const playground = trigger.closest('pre');
2022-12-13 15:22:03 -08:00
return playground_text(playground, false);
2025-02-21 22:55:23 +01:00
},
});
2025-02-21 22:55:23 +01:00
Array.from(clipButtons).forEach(function(clipButton) {
clipButton.addEventListener('mouseout', function(e) {
hideTooltip(e.currentTarget);
});
});
2018-01-12 19:44:13 +01:00
2025-02-21 22:55:23 +01:00
clipboardSnippets.on('success', function(e) {
e.clearSelection();
2025-02-21 22:55:23 +01:00
showTooltip(e.trigger, 'Copied!');
});
2018-01-12 19:44:13 +01:00
2025-02-21 22:55:23 +01:00
clipboardSnippets.on('error', function(e) {
showTooltip(e.trigger, 'Clipboard error!');
});
})();
2025-02-21 22:55:23 +01:00
(function scrollToTop() {
const menuTitle = document.querySelector('.menu-title');
2025-02-21 22:55:23 +01:00
menuTitle.addEventListener('click', function() {
document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' });
});
})();
(function controllMenu() {
const menu = document.getElementById('mdbook-menu-bar');
(function controllPosition() {
2025-02-21 22:55:23 +01:00
let scrollTop = document.scrollingElement.scrollTop;
let prevScrollTop = scrollTop;
const minMenuY = -menu.clientHeight - 50;
// When the script loads, the page can be at any scroll (e.g. if you refresh it).
menu.style.top = scrollTop + 'px';
// Same as parseInt(menu.style.top.slice(0, -2), but faster
2025-02-21 22:55:23 +01:00
let topCache = menu.style.top.slice(0, -2);
menu.classList.remove('sticky');
2025-02-21 22:55:23 +01:00
let stickyCache = false; // Same as menu.classList.contains('sticky'), but faster
document.addEventListener('scroll', function() {
scrollTop = Math.max(document.scrollingElement.scrollTop, 0);
// `null` means that it doesn't need to be updated
2025-02-21 22:55:23 +01:00
let nextSticky = null;
let nextTop = null;
const scrollDown = scrollTop > prevScrollTop;
const menuPosAbsoluteY = topCache - scrollTop;
if (scrollDown) {
nextSticky = false;
if (menuPosAbsoluteY > 0) {
nextTop = prevScrollTop;
}
} else {
if (menuPosAbsoluteY > 0) {
nextSticky = true;
} else if (menuPosAbsoluteY < minMenuY) {
nextTop = prevScrollTop + minMenuY;
}
}
if (nextSticky === true && stickyCache === false) {
menu.classList.add('sticky');
stickyCache = true;
} else if (nextSticky === false && stickyCache === true) {
menu.classList.remove('sticky');
stickyCache = false;
}
if (nextTop !== null) {
menu.style.top = nextTop + 'px';
topCache = nextTop;
}
prevScrollTop = scrollTop;
}, { passive: true });
})();
(function controllBorder() {
function updateBorder() {
if (menu.offsetTop === 0) {
menu.classList.remove('bordered');
} else {
menu.classList.add('bordered');
}
}
updateBorder();
document.addEventListener('scroll', updateBorder, { passive: true });
})();
})();