Artifact d81bb60a0eadbb04671a2e368b67713ca55c4c8eeacc4769d636946056a85948:


// toc.js
// Generate a ToC for a Fossil wiki page.  This code should work in old
// browsers.
// Copyright (c) 2020, 2021 D. Bohdan.
// License: MIT.

var tocGen = {};

// Returns an array of all h1-h6 headings in the wiki page.
tocGen.headings = function() {
    var md = document.querySelector('.markdown');
    if (!md) {
        return [];
    }

    return Array.prototype.slice.call(
        md.querySelectorAll('h1, h2, h3, h4, h5, h6')
    );
};

// Returns the element after which we append the ToC.
tocGen.contentsHeading = function(headings) {
    return headings.filter(function(el) {
        return el.innerText === 'Contents';
    })[0];
};

// Transform a string into a form suitable for a fragment identifier.
tocGen.slugify = function(s) {
    return s.toLowerCase().match(/\w+/g).join('-');
};

// Returns an <li><a href>text</a></li> node.
tocGen.createLiA = function(href, text) {
    var li = document.createElement('li');
    var a = document.createElement('a');
    a.href = href;
    a.innerText = text;
    li.appendChild(a);
    
    return li;
};

// Set unique fragement ids on headings and return an element containing the
// ToC.  The ToC consists of nested <ul>s.
tocGen.createToC = function(headings, skipH1sInSlug) {
    var prevLevel = 0;
    var stack = [null, null, null, null, null, null];

    var f = function(heading) {
        var level = +heading.tagName[1];
        var i, ul;

        // Nest the lists.
        for (i = prevLevel; i < level; i++) {
            ul = document.createElement('ul');

            if (level > 1) {
                var li = document.createElement('li');
                li.style.listStyle = 'none';
                li.appendChild(ul);
                stack[i - 1].list.appendChild(li);
            }

            stack[i] = {'list': ul, 'text': ''};
        }
        stack[level - 1].text = heading.innerText;
       
        var slug = stack.slice(1 * skipH1sInSlug, level).map(function(x) {
            return tocGen.slugify(x.text);
        }).join('--');
        heading.id = slug;        
        var liA = tocGen.createLiA(
            location.href.split('#')[0] + '#' + slug,
            heading.innerText
        );

        stack[level - 1].list.appendChild(liA);

        prevLevel = level;
    };

    headings.forEach(f);

    return stack[0].list;
};

// Insert el into the document after ref.
tocGen.insertAfter = function(ref, el) {
    ref.parentNode.insertBefore(el, ref.nextSibling);
}

// Add a ToC to the current wiki page.
tocGen.addToC = function(skipH1sInSlug) {
    var headings = tocGen.headings();

    var contentsHeading = tocGen.contentsHeading(headings);
    if (!contentsHeading) return;

    // Do not link to the ToC itself.
    headings = headings.filter(function (heading) {
        return heading !== contentsHeading;
    });

    var toc = tocGen.createToC(headings, skipH1sInSlug);
    // Remove the top-level <h1> from the ToC if there is only one.
    if (toc.childNodes.length === 2) {
        // toc consists of <ul><li>Foo</li><li><ul>...</ul></li></ul>.
        toc = toc.childNodes[1].childNodes[0];
    };
    
    tocGen.insertAfter(contentsHeading, toc);
};

// If the document contains an element with a list of wiki page tags like
// "<p><a href="/foo">Tags</a>: bar, baz.</p>", transform each individual
// tag into a link to "/foo#tags--" + slugify(tag).  Makes "Tags" itself
// no longer a link.
tocGen.linkifyTagList = function (tagPageHref) {
    var tagPageLinks = document.querySelectorAll(
        // Select with $= to match links on /draft[1-9]/ pages.
        'a[href$="' + tagPageHref + '"]'
    );
    if (tagPageLinks.length === 0) return;
    var tagPageLink = tagPageLinks[tagPageLinks.length - 1];

    var p = tagPageLink.parentNode;
    var tagListNode = p.childNodes[1];
    var tags = tagListNode
        .textContent
        .replace(/^:\s*/, '')
        .replace(/\s*\.$/, '')
        .split(/,\s*/);

    var links = tags.map(function(tag) {
        var a = document.createElement('a');
        var slug = tocGen.slugify(tag);
        a.href = tagPageHref + '#tags--' + slug;
        a.appendChild(document.createTextNode(tag));

        return a;
    });

    links.forEach(function(link, i) {
        p.appendChild(link);
        p.appendChild(document.createTextNode(
            i === links.length - 1 ? '. ' : ', '
        ));
    });

    tagListNode.textContent = 'Tags: ';

    p.removeChild(tagPageLink);
};