PageSourceSearch

https://chirimen.org/pizero/toc-8d876478.js

js chirimen.org collected 2026-10-02 07:52:33 UTC 35,226 bytes, 455 lines download raw bytes

1// Populate the sidebar
2//
3// This is a script, and not included directly in the page, to control the total size of the book.
4// The TOC contains an entry for each page, so if each page includes a copy of the TOC,
5// the total size of the page becomes O(n**2).
6class MDBookSidebarScrollbox extends HTMLElement {
7    constructor() {
8        super();
9    }
10    connectedCallback() {
11        this.innerHTML = '<ol class="chapter"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="index.html"><strong aria-hidden="true">1.</strong> チュートリアル概要</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_1-1.html"><strong aria-hidden="true">1.1.</strong> CHIRIMEN について</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_1-1-1.html"><strong aria-hidden="true">1.1.1.</strong> CHIRIMEN コミュニティ</a></span></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_1-2.html"><strong aria-hidden="true">1.2.</strong> Raspberry Pi について</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_1-3.html"><strong aria-hidden="true">1.3.</strong> JavaScript の基礎</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_1-4.html"><strong aria-hidden="true">1.4.</strong> よく使う情報へのリンク</a></span></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_2.html"><strong aria-hidden="true">2.</strong> 機材準備と Wi-Fi への接続</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_2-1.html"><strong aria-hidden="true">2.1.</strong> ステップ0(機材準備)</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_2-2.html"><strong aria-hidden="true">2.2.</strong> ステップ1(ターミナル接続)</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_2-2-1.html"><strong aria-hidden="true">2.2.1.</strong> Note. CLI操作について</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_2-2-2.html"><strong aria-hidden="true">2.2.2.</strong> Note. pi4 の利用方法</a></span></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_2-3.html"><strong aria-hidden="true">2.3.</strong> ステップ2(Wi-Fi設定)</a></span></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_3.html"><strong aria-hidden="true">3.</strong> Hello Real World(Lチカを実行する)</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_3-1.html"><strong aria-hidden="true">3.1.</strong> 配線</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_3-2.html"><strong aria-hidden="true">3.2.</strong> プログラムを書く</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_3-3.html"><strong aria-hidden="true">3.3.</strong> 実行する</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_3-4.html"><strong aria-hidden="true">3.4.</strong> コードを読む</a></span></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_4.html"><strong aria-hidden="true">4.</strong> GPIOを試す</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_4-1.html"><strong aria-hidden="true">4.1.</strong> GPIOを理解する</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_4-2.html"><strong aria-hidden="true">4.2.</strong> GPIOを出力</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_4-3.html"><strong aria-hidden="true">4.3.</strong>
11 GPIOを入力(onchange)</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_4-3-1.html"><strong aria-hidden="true">4.3.1.</strong> onchange コードを読む</a></span></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_4-4.html"><strong aria-hidden="true">4.4.</strong> GPIOを入力(ポーリング)</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_4-4-1.html"><strong aria-hidden="true">4.4.1.</strong> ポーリングコードを読む</a></span></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_4-5.html"><strong aria-hidden="true">4.5.</strong> GPIOセンサーを複数同時に使う</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_4-5-1.html"><strong aria-hidden="true">4.5.1.</strong> Lチカのコードを書き換える</a></span></li></ol></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_5.html"><strong aria-hidden="true">5.</strong> I2Cデバイスを試す</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_5-1.html"><strong aria-hidden="true">5.1.</strong> I2Cを理解する</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_5-2.html"><strong aria-hidden="true">5.2.</strong> SHT30編</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_5-2-1.html"><strong aria-hidden="true">5.2.1.</strong> I2Cセンサー(SHT30)の認識を確認する</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_5-2-2.html"><strong aria-hidden="true">5.2.2.</strong> SHT30 のコード取得と実行</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_5-2-3.html"><strong aria-hidden="true">5.2.3.</strong> SHT30 のコードを読む</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_5-2-4.html"><strong aria-hidden="true">5.2.4.</strong> SHT30 のコードの詳細解説</a></span></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_5-3.html"><strong aria-hidden="true">5.3.</strong> ADT7410編</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_5-4.html"><strong aria-hidden="true">5.4.</strong> GPIO と I2Cセンサーを組み合わせる</a></span></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_6.html"><strong aria-hidden="true">6.</strong> IoTを試す</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_6-1.html"><strong aria-hidden="true">6.1.</strong> 遠隔LEDコントロール</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_6-2.html"><strong aria-hidden="true">6.2.</strong> PiZero サンプルコードの実行</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_6-3.html"><strong aria-hidden="true">6.3.</strong> PC サンプルコードの実行</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_6-4.html"><strong aria-hidden="true">6.4.</strong> 自分専用チャンネルで制御</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_6-5.html"><strong aria-hidden="true">6.5.</strong> PiZero 側のコードを読む</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_6-6.html"><strong aria-hidden="true">6.6.</strong> PC 側のコードを読む</a></span></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_7.html"><strong aria-hidden="true">7.</strong> 常駐プログラム化する</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="using-ai-assistant.html"><strong aria-hidden="true">8.</strong> AIアシスタントを活用する</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_8.html"><strong aria-hidden="true">9.</strong> 他のいろいろなデバイスを試してみる</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_8-1.html"><strong aria-hidden="true">9.1.</strong> 応用センサーキットの使い方</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_8-1-1.html"><strong aria-hidden="true">9.1.1.</strong> 単体で動作確認できるセンサー</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_8-1-2.html"><strong aria-hidden="true">9.1.2.</strong> 人感センサー</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_8-1-3.html"><strong aria-hidden="true">9.1.3.</strong> Neopixel LED</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_8-1-4.html"><strong aria-hidden="true">9.1.4.</strong> アナログセンサー</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_8-1-5.html"><strong aria-hidden="true">
119.1.5.</strong> サーボモーター</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_8-1-6.html"><strong aria-hidden="true">9.1.6.</strong> DCモーター(MX1508利用)</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_8-1-7.html"><strong aria-hidden="true">9.1.7.</strong> DCモーター(PWM駆動)</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_8-1-8.html"><strong aria-hidden="true">9.1.8.</strong> RaspberryPi のカメラの注意点</a></span></li></ol></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_9.html"><strong aria-hidden="true">10.</strong> 付録(予備知識)</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_9-1.html"><strong aria-hidden="true">10.1.</strong> CHIRIMEN ブラウザー版との差異</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_9-2.html"><strong aria-hidden="true">10.2.</strong> CHIRIMEN環境の任意のディレクトリへのセットアップ</a></span></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10.html"><strong aria-hidden="true">11.</strong> 共通資料集</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-1.html"><strong aria-hidden="true">11.1.</strong> CHIRIMEN</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-2.html"><strong aria-hidden="true">11.2.</strong> ハードウェア・デバイス</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-2-1.html"><strong aria-hidden="true">11.2.1.</strong> LED</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-2-2.html"><strong aria-hidden="true">11.2.2.</strong> ブレッドボード</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-2-3.html"><strong aria-hidden="true">11.2.3.</strong> 抵抗値の読み方</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-2-4.html"><strong aria-hidden="true">11.2.4.</strong> MOSFETによる大電力制御</a></span></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-3.html"><strong aria-hidden="true">11.3.</strong> JavaScript</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-3-1.html"><strong aria-hidden="true">11.3.1.</strong> JavaScriptコードとライブラリの読み込み</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-3-2.html"><strong aria-hidden="true">11.3.2.</strong> 非同期処理</a></span></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-4.html"><strong aria-hidden="true">11.4.</strong> GPIO</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-4-1.html"><strong aria-hidden="true">11.4.1.</strong> GPIOポートの初期化と出力処理</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-4-2.html"><strong aria-hidden="true">11.4.2.</strong> GPIOPortの入力処理(onchange)</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-4-3.html"><strong aria-hidden="true">11.4.3.</strong> GPIOPortの入力処理(ポーリング)</a></span></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-5.html"><strong aria-hidden="true">11.5.</strong> I2C</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-5-1.html"><strong aria-hidden="true">11.5.1.</strong>
11 各種ボードの I2C端子</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-5-2.html"><strong aria-hidden="true">11.5.2.</strong> I2Cの詳細情報とポイント</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-5-3.html"><strong aria-hidden="true">11.5.3.</strong> WebI2C とデバイスドライバ</a></span></li></ol><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-6.html"><strong aria-hidden="true">11.6.</strong> IoT</a></span><ol class="section"><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-6-1.html"><strong aria-hidden="true">11.6.1.</strong> webSocketとpub/sub services</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-6-2.html"><strong aria-hidden="true">11.6.2.</strong> relayService</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-6-3.html"><strong aria-hidden="true">11.6.3.</strong> リアルタイム性</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-6-4.html"><strong aria-hidden="true">11.6.4.</strong> relayServer.js</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-6-5.html"><strong aria-hidden="true">11.6.5.</strong> プログラムの流れ</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-6-6.html"><strong aria-hidden="true">11.6.6.</strong> セキュリティ</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-6-7.html"><strong aria-hidden="true">11.6.7.</strong> Node.jsでの利用</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-6-8.html"><strong aria-hidden="true">11.6.8.</strong> Webhooks</a></span></li><li class="chapter-item expanded "><span class="chapter-link-wrapper"><a href="chapter_10-6-9.html"><strong aria-hidden="true">11.6.9.</strong> IoTクラウドサービス</a></span></li></ol></li></ol></li></ol>';
12        // Set the current, active page, and reveal it if it's hidden
13        let current_page = document.location.href.toString().split('#')[0].split('?')[0];
14        if (current_page.endsWith('/')) {
15            current_page += 'index.html';
16        }
17        const links = Array.prototype.slice.call(this.querySelectorAll('a'));
18        const l = links.length;
19        for (let i = 0; i < l; ++i) {
20            const link = links[i];
21            const href = link.getAttribute('href');
22            if (href && !href.startsWith('#') && !/^(?:[a-z+]+:)?\/\//.test(href)) {
23                link.href = path_to_root + href;
24            }
25            // The 'index' page is supposed to alias the first chapter in the book.
26            // Check both with and without the '.html' suffix to be robust against pretty URLs
27            if (link.href.replace(/\.html$/, '') === current_page.replace(/\.html$/, '')
28                || i === 0
29                && path_to_root === ''
30                && current_page.endsWith('/index.html')) {
31                link.classList.add('active');
32                let parent = link.parentElement;
33                while (parent) {
34                    if (parent.tagName === 'LI' && parent.classList.contains('chapter-item')) {
35                        parent.classList.add('expanded');
36                    }
37                    parent = parent.parentElement;
38                }
39            }
40        }
41        // Track and set sidebar scroll position
42        this.addEventListener('click', e => {
43            if (e.target.tagName === 'A') {
44                const clientRect = e.target.getBoundingClientRect();
45                const sidebarRect = this.getBoundingClientRect();
46                sessionStorage.setItem('sidebar-scroll-offset', clientRect.top - sidebarRect.top);
47            }
48        }, { passive: true });
49        const sidebarScrollOffset = sessionStorage.getItem('sidebar-scroll-offset');
50        sessionStorage.removeItem('sidebar-scroll-offset');
51        if (sidebarScrollOffset !== null) {
52            // preserve sidebar scroll position when navigating via links within sidebar
53            const activeSection = this.querySelector('.active');
54            if (activeSection) {
55                const clientRect = activeSection.getBoundingClientRect();
56                const sidebarRect = this.getBoundingClientRect();
57                const currentOffset = clientRect.top - sidebarRect.top;
58                this.scrollTop += currentOffset - parseFloat(sidebarScrollOffset);
59            }
60        } else {
61            // scroll sidebar to current active section when navigating via
62            // 'next/previous chapter' buttons
63            const activeSection = document.querySelector('#mdbook-sidebar .active');
64            if (activeSection) {
65                activeSection.scrollIntoView({ block: 'center' });
66            }
67        }
68        // Toggle buttons
69        const sidebarAnchorToggles = document.querySelectorAll('.chapter-fold-toggle');
70        function toggleSection(ev) {
71            ev.currentTarget.parentElement.parentElement.classList.toggle('expanded');
72        }
73        Array.from(sidebarAnchorToggles).forEach(el => {
74            el.addEventListener('click', toggleSection);
75        });
76    }
77}
78window.customElements.define('mdbook-sidebar-scrollbox', MDBookSidebarScrollbox);
79
80
81// ---------------------------------------------------------------------------
82// Support for dynamically adding headers to the sidebar.
83
84(function() {
85    // This is used to detect which direction the page has scrolled since the
86    // last scroll event.
87    let lastKnownScrollPosition = 0;
88    // This is the threshold in px from the top of the screen where it will
89    // consider a header the "current" header when scrolling down.
90    const defaultDownThreshold = 150;
91    // Same as defaultDownThreshold, except when scrolling up.
92    const defaultUpThreshold = 300;
93    // The threshold is a virtual horizontal line on the screen where it
94    // considers the "current" header to be above the line. The threshold is
95    // modified dynamically to handle headers that are near the bottom of the
96    // screen, and to slightly offset the behavior when scrolling up vs down.
97    let threshold = defaultDownThreshold;
98    // This is used to disable updates while scrolling. This is needed when
99    // clicking the header in the sidebar, which triggers a scroll event. It
100    // is somewhat finicky to detect when the scroll has finished, so this
101    // uses a relatively dumb system of disabling scroll updates for a short
102    // time after the click.
103    let disableScroll = false;
104    // Array of header elements on the page.
105    let headers;
106    // Array of li elements that are initially collapsed headers in the sidebar.
107    // I'm not sure why eslint seems to have a false positive here.
108    // eslint-disable-next-line prefer-const
109    let headerToggles = [];
110    // This is a debugging tool for the threshold which you can enable in the console.
111    let thresholdDebug = false;
112
113    // Updates the threshold based on the scroll position.
114    function updateThreshold() {
115        const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
116        const windowHeight = window.innerHeight;
117        const documentHeight = document.documentElement.scrollHeight;
118
119        // The number of pixels below the viewport, at most documentHeight.
120        // This is used to push the threshold down to the bottom of the page
121        // as the user scrolls towards the bottom.
122        const pixelsBelow = Math.max(0, documentHeight - (scrollTop + windowHeight));
123        // The number of pixels above the viewport, at least defaultDownThreshold.
124        // Similar to pixelsBelow, this is used to push the threshold back towards
125        // the top when reaching the top of the page.
126        const pixelsAbove = Math.max(0, defaultDownThreshold - scrollTop);
127        // How much the threshold should be offset once it gets close to the
128        // bottom of the page.
129        const bottomAdd = Math.max(0, windowHeight - pixelsBelow - defaultDownThreshold);
130        let adjustedBottomAdd = bottomAdd;
131
132        // Adjusts bottomAdd for a small document. The calculation above
133        // assumes the document is at least twice the windowheight in size. If
134        // it is less than that, then bottomAdd needs to be shrunk
135        // proportional to the difference in size.
136        if (documentHeight < windowHeight * 2) {
137            const maxPixelsBelow = documentHeight - windowHeight;
138            const t = 1 - pixelsBelow / Math.max(1, maxPixelsBelow);
139            const clamp = Math.max(0, Math.min(1, t));
140            adjustedBottomAdd *= clamp;
141        }
142
143        let scrollingDown = true;
144        if (scrollTop < lastKnownScrollPosition) {
145            scrollingDown = false;
146        }
147
148        if (scrollingDown) {
149            // When scrolling down, move the threshold up towards the default
150            // downwards threshold position. If near the bottom of the page,
151            // adjustedBottomAdd will offset the threshold towards the bottom
152            // of the page.
153            const amountScrolledDown = scrollTop - lastKnownScrollPosition;
154            const adjustedDefault = defaultDownThreshold + adjustedBottomAdd;
155            threshold = Math.max(adjustedDefault, threshold - amountScrolledDown);
156        } else {
157            // When scrolling up, move the threshold down towards the default
158            // upwards threshold position. If near the bottom of the page,
159            // quickly transition the threshold back up where it normally
160            // belongs.
161            const amountScrolledUp = lastKnownScrollPosition - scrollTop;
162            const adjustedDefault = defaultUpThreshold - pixelsAbove
163                + Math.max(0, adjustedBottomAdd - defaultDownThreshold);
164            threshold = Math.min(adjustedDefault, threshold + amountScrolledUp);
165        }
166
167        if (documentHeight <= windowHeight) {
168            threshold = 0;
169        }
170
171        if (thresholdDebug) {
172            const id = 'mdbook-threshold-debug-data';
173            let data = document.getElementById(id);
174            if (data === null) {
175                data = document.createElement('div');
176                data.id = id;
177                data.style.cssText = `
178                    position: fixed;
179                    top: 50px;
180                    right: 10px;
181                    background-color: 0xeeeeee;
182                    z-index: 9999;
183                    pointer-events: none;
184                `;
185                document.body.appendChild(data);
186            }
187            data.innerHTML = `
188                <table>
189                  <tr><td>documentHeight</td><td>${documentHeight.toFixed(1)}</td></tr>
190                  <tr><td>windowHeight</td><td>${windowHeight.toFixed(1)}</td></tr>
191                  <tr><td>scrollTop</td><td>${scrollTop.toFixed(1)}</td></tr>
192                  <tr><td>pixelsAbove</td><td>${pixelsAbove.toFixed(1)}</td></tr>
193                  <tr><td>pixelsBelow</td><td>${pixelsBelow.toFixed(1)}</td></tr>
194                  <tr><td>bottomAdd</td><td>${bottomAdd.toFixed(1)}</td></tr>
195                  <tr><td>adjustedBottomAdd</td><td>${adjustedBottomAdd.toFixed(1)}</td></tr>
196                  <tr><td>scrollingDown</td><td>${scrollingDown}</td></tr>
197                  <tr><td>threshold</td><td>${threshold.toFixed(1)}</td></tr>
198                </table>
199            `;
200            drawDebugLine();
201        }
202
203        lastKnownScrollPosition = scrollTop;
204    }
205
206    function drawDebugLine() {
207        if (!document.body) {
208            return;
209        }
210        const id = 'mdbook-threshold-debug-line';
211        const existingLine = document.getElementById(id);
212        if (existingLine) {
213            existingLine.remove();
214        }
215        const line = document.createElement('div');
216        line.id = id;
217        line.style.cssText = `
218            position: fixed;
219            top: ${threshold}px;
220            left: 0;
221            width: 100vw;
222            height: 2px;
223            background-color: red;
224            z-index: 9999;
225            pointer-events: none;
226        `;
227        document.body.appendChild(line);
228    }
229
230    function mdbookEnableThresholdDebug() {
231        thresholdDebug = true;
232        updateThreshold();
233        drawDebugLine();
234    }
235
236    window.mdbookEnableThresholdDebug = mdbookEnableThresholdDebug;
237
238    // Updates which headers in the sidebar should be expanded. If the current
239    // header is inside a collapsed group, then it, and all its parents should
240    // be expanded.
241    function updateHeaderExpanded(currentA) {
242        // Add expanded to all header-item li ancestors.
243        let current = currentA.parentElement;
244        while (current) {
245            if (current.tagName === 'LI' && current.classList.contains('header-item')) {
246                current.classList.add('expanded');
247            }
248            current = current.parentElement;
249        }
250    }
251
252    // Updates which header is marked as the "current" header in the sidebar.
253    // This is done with a virtual Y threshold, where headers at or below
254    // that line will be considered the current one.
255    function updateCurrentHeader() {
256        if (!headers || !headers.length) {
257            return;
258        }
259
260        // Reset the classes, which will be rebuilt below.
261        const els = document.getElementsByClassName('current-header');
262        for (const el of els) {
263            el.classList.remove('current-header');
264        }
265        for (const toggle of headerToggles) {
266            toggle.classList.remove('expanded');
267        }
268
269        // Find the last header that is above the threshold.
270        let lastHeader = null;
271        for (const header of headers) {
272            const rect = header.getBoundingClientRect();
273            if (rect.top <= threshold) {
274                lastHeader = header;
275            } else {
276                break;
277            }
278        }
279        if (lastHeader === null) {
280            lastHeader = headers[0];
281            const rect = lastHeader.getBoundingClientRect();
282            const windowHeight = window.innerHeight;
283            if (rect.top >= windowHeight) {
284                return;
285            }
286        }
287
288        // Get the anchor in the summary.
289        const href = '#' + lastHeader.id;
290        const a = [...document.querySelectorAll('.header-in-summary')]
291            .find(element => element.getAttribute('href') === href);
292        if (!a) {
293            return;
294        }
295
296        a.classList.add('current-header');
297
298        updateHeaderExpanded(a);
299    }
300
301    // Updates which header is "current" based on the threshold line.
302    function reloadCurrentHeader() {
303        if (disableScroll) {
304            return;
305        }
306        updateThreshold();
307        updateCurrentHeader();
308    }
309
310
311    // When clicking on a header in the sidebar, this adjusts the threshold so
312    // that it is located next to the header. This is so that header becomes
313    // "current".
314    function headerThresholdClick(event) {
315        // See disableScroll description why this is done.
316        disableScroll = true;
317        setTimeout(() => {
318            disableScroll = false;
319        }, 100);
320        // requestAnimationFrame is used to delay the update of the "current"
321        // header until after the scroll is done, and the header is in the new
322        // position.
323        requestAnimationFrame(() => {
324            requestAnimationFrame(() => {
325                // Closest is needed because if it has child elements like <code>.
326                const a = event.target.closest('a');
327                const href = a.getAttribute('href');
328                const targetId = href.substring(1);
329                const targetElement = document.getElementById(targetId);
330                if (targetElement) {
331                    threshold = targetElement.getBoundingClientRect().bottom;
332                    updateCurrentHeader();
333                }
334            });
335        });
336    }
337
338    // Takes the nodes from the given head and copies them over to the
339    // destination, along with some filtering.
340    function filterHeader(source, dest) {
341        const clone = source.cloneNode(true);
342        clone.querySelectorAll('mark').forEach(mark => {
343            mark.replaceWith(...mark.childNodes);
344        });
345        dest.append(...clone.childNodes);
346    }
347
348    // Scans page for headers and adds them to the sidebar.
349    document.addEventListener('DOMContentLoaded', function() {
350        const activeSection = document.querySelector('#mdbook-sidebar .active');
351        if (activeSection === null) {
352            return;
353        }
354
355        const main = document.getElementsByTagName('main')[0];
356        headers = Array.from(main.querySelectorAll('h2, h3, h4, h5, h6'))
357            .filter(h => h.id !== '' && h.children.length && h.children[0].tagName === 'A');
358
359        if (headers.length === 0) {
360            return;
361        }
362
363        // Build a tree of headers in the sidebar.
364
365        const stack = [];
366
367        const firstLevel = parseInt(headers[0].tagName.charAt(1));
368        for (let i = 1; i < firstLevel; i++) {
369            const ol = document.createElement('ol');
370            ol.classList.add('section');
371            if (stack.length > 0) {
372                stack[stack.length - 1].ol.appendChild(ol);
373            }
374            stack.push({level: i + 1, ol: ol});
375        }
376
377        // The level where it will start folding deeply nested headers.
378        const foldLevel = 3;
379
380        for (let i = 0; i < headers.length; i++) {
381            const header = headers[i];
382            const level = parseInt(header.tagName.charAt(1));
383
384            const currentLevel = stack[stack.length - 1].level;
385            if (level > currentLevel) {
386                // Begin nesting to this level.
387                for (let nextLevel = currentLevel + 1; nextLevel <= level; nextLevel++) {
388                    const ol = document.createElement('ol');
389                    ol.classList.add('section');
390                    const last = stack[stack.length - 1];
391                    const lastChild = last.ol.lastChild;
392                    // Handle the case where jumping more than one nesting
393                    // level, which doesn't have a list item to place this new
394                    // list inside of.
395                    if (lastChild) {
396                        lastChild.appendChild(ol);
397                    } else {
398                        last.ol.appendChild(ol);
399                    }
400                    stack.push({level: nextLevel, ol: ol});
401                }
402            } else if (level < currentLevel) {
403                while (stack.length > 1 && stack[stack.length - 1].level > level) {
404                    stack.pop();
405                }
406            }
407
408            const li = document.createElement('li');
409            li.classList.add('header-item');
410            li.classList.add('expanded');
411            if (level < foldLevel) {
412                li.classList.add('expanded');
413            }
414            const span = document.createElement('span');
415            span.classList.add('chapter-link-wrapper');
416            const a = document.createElement('a');
417            span.appendChild(a);
418            a.href = '#' + header.id;
419            a.classList.add('header-in-summary');
420            filterHeader(header.children[0], a);
421            a.addEventListener('click', headerThresholdClick);
422            const nextHeader = headers[i + 1];
423            if (nextHeader !== undefined) {
424                const nextLevel = parseInt(nextHeader.tagName.charAt(1));
425                if (nextLevel > level && level >= foldLevel) {
426                    const toggle = document.createElement('a');
427                    toggle.classList.add('chapter-fold-toggle');
428                    toggle.classList.add('header-toggle');
429                    toggle.addEventListener('click', () => {
430                        li.classList.toggle('expanded');
431                    });
432                    const toggleDiv = document.createElement('div');
433                    toggleDiv.textContent = '❱';
434                    toggle.appendChild(toggleDiv);
435                    span.appendChild(toggle);
436                    headerToggles.push(li);
437                }
438            }
439            li.appendChild(span);
440
441            const currentParent = stack[stack.length - 1];
442            currentParent.ol.appendChild(li);
443        }
444
445        const onThisPage = document.createElement('div');
446        onThisPage.classList.add('on-this-page');
447        onThisPage.append(stack[0].ol);
448        const activeItemSpan = activeSection.parentElement;
449        activeItemSpan.after(onThisPage);
450    });
451
452    document.addEventListener('DOMContentLoaded', reloadCurrentHeader);
453    document.addEventListener('scroll', reloadCurrentHeader, { passive: true });
454})();
455

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.