about summary refs log tree commit diff
path: root/src/doc/rustc-dev-guide/pagetoc.js
blob: 927a5b10749b5cbf87caee8b0c318746da2a357a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// Inspired by https://github.com/JorelAli/mdBook-pagetoc/tree/98ee241 (under WTFPL)

let activeHref = location.href;
function updatePageToc(elem = undefined) {
    let selectedPageTocElem = elem;
    const pagetoc = document.getElementById("pagetoc");

    function getRect(element) {
        return element.getBoundingClientRect();
    }

    function overflowTop(container, element) {
        return getRect(container).top - getRect(element).top;
    }

    function overflowBottom(container, element) {
        return getRect(container).bottom - getRect(element).bottom;
    }

    // We've not selected a heading to highlight, and the URL needs updating
    // so we need to find a heading based on the URL
    if (selectedPageTocElem === undefined && location.href !== activeHref) {
        activeHref = location.href;
        for (const pageTocElement of pagetoc.children) {
            if (pageTocElement.href === activeHref) {
                selectedPageTocElem = pageTocElement;
            }
        }
    }

    // We still don't have a selected heading, let's try and find the most
    // suitable heading based on the scroll position
    if (selectedPageTocElem === undefined) {
        const margin = window.innerHeight / 3;

        const headers = document.getElementsByClassName("header");
        for (let i = 0; i < headers.length; i++) {
            const header = headers[i];
            if (selectedPageTocElem === undefined && getRect(header).top >= 0) {
                if (getRect(header).top < margin) {
                    selectedPageTocElem = header;
                } else {
                    selectedPageTocElem = headers[Math.max(0, i - 1)];
                }
            }
            // a very long last section's heading is over the screen
            if (selectedPageTocElem === undefined && i === headers.length - 1) {
                selectedPageTocElem = header;
            }
        }
    }

    // Remove the active flag from all pagetoc elements
    for (const pageTocElement of pagetoc.children) {
        pageTocElement.classList.remove("active");
    }

    // If we have a selected heading, set it to active and scroll to it
    if (selectedPageTocElem !== undefined) {
        for (const pageTocElement of pagetoc.children) {
            if (selectedPageTocElem.href.localeCompare(pageTocElement.href) === 0) {
                pageTocElement.classList.add("active");
                if (overflowTop(pagetoc, pageTocElement) > 0) {
                    pagetoc.scrollTop = pageTocElement.offsetTop;
                }
                if (overflowBottom(pagetoc, pageTocElement) < 0) {
                    pagetoc.scrollTop -= overflowBottom(pagetoc, pageTocElement);
                }
            }
        }
    }
}

if (document.getElementById("sidetoc") === null &&
    document.getElementsByClassName("header").length > 0) {
    // The sidetoc element doesn't exist yet, let's create it

    // Create the empty sidetoc and pagetoc elements
    const sidetoc = document.createElement("div");
    const pagetoc = document.createElement("div");
    sidetoc.id = "sidetoc";
    pagetoc.id = "pagetoc";
    sidetoc.appendChild(pagetoc);

    // And append them to the current DOM
    const main = document.querySelector('main');
    main.insertBefore(sidetoc, main.firstChild);

    // Populate sidebar on load
    window.addEventListener("load", () => {
        for (const header of document.getElementsByClassName("header")) {
            const link = document.createElement("a");
            link.innerHTML = header.innerHTML;
            link.href = header.hash;
            link.classList.add("pagetoc-" + header.parentElement.tagName);
            document.getElementById("pagetoc").appendChild(link);
            link.onclick = () => updatePageToc(link);
        }
        updatePageToc();
    });

    // Update page table of contents selected heading on scroll
    window.addEventListener("scroll", () => updatePageToc());
}