export function prepTemplate()

in pxtlib/docsrender.ts [135:403]


    export function prepTemplate(d: RenderData) {
        let boxes = U.clone(stdboxes)
        let macros = U.clone(stdmacros)
        let settings = U.clone(stdsettings)
        let menus: Map<string> = {}
        let toc: Map<string> = {}
        let params = d.params
        let theme = d.theme

        d.boxes = boxes
        d.macros = macros
        d.settings = settings

        d.html = d.html.replace(/<aside\s+([^<>]+)>([^]*?)<\/aside>/g, (full, attrsStr, body) => {
            let attrs = parseHtmlAttrs(attrsStr)
            let name = attrs["data-name"] || attrs["id"]

            if (!name)
                return error("id or data-name missing on macro")
            if (/box/.test(attrs["class"])) {
                boxes[name] = body
            } else if (/aside/.test(attrs["class"])) {
                boxes[name] = `<!-- BEGIN-ASIDE ${name} -->${body}<!-- END-ASIDE -->`
            } else if (/setting/.test(attrs["class"])) {
                settings[name] = body
            } else if (/menu/.test(attrs["class"])) {
                menus[name] = body
            } else if (/toc/.test(attrs["class"])) {
                toc[name] = body
            } else {
                macros[name] = body
            }
            return `<!-- macro ${name} -->`
        })

        let recMenu = (m: DocMenuEntry, lev: number) => {
            let templ = menus["item"]
            let mparams: Map<string> = {
                NAME: m.name,
            }
            if (m.subitems) {
                if (!!menus["toc-dropdown"]) {
                    templ = menus["toc-dropdown"]
                }
                else {
                    /** TODO: when all targets bumped to include https://github.com/microsoft/pxt/pull/6058,
                     * swap templ assignments below with the commented out version, and remove
                     * top-dropdown, top-dropdown-noheading, inner-dropdown, and nested-dropdown from
                     * docfiles/macros.html **/
                    if (lev == 0) templ = menus["top-dropdown"]
                    else templ = menus["inner-dropdown"]
                }
                mparams["ITEMS"] = m.subitems.map(e => recMenu(e, lev + 1)).join("\n")
            } else {
                if (/^-+$/.test(m.name)) {
                    templ = menus["divider"]
                }
                if (m.path && !/^(https?:|\/)/.test(m.path))
                    return error("Invalid link: " + m.path)
                mparams["LINK"] = m.path
            }
            return injectHtml(templ, mparams, ["ITEMS"])
        }

        let breadcrumb: BreadcrumbEntry[] = [{
            name: lf("Docs"),
            href: "/docs"
        }]

        const TOC = d.TOC || theme.TOC || [];
        let tocPath: TOCMenuEntry[] = []
        let isCurrentTOC = (m: TOCMenuEntry) => {
            for (let c of m.subitems || []) {
                if (isCurrentTOC(c)) {
                    tocPath.push(m)
                    return true
                }
            }
            if (d.filepath && !!m.path && d.filepath.indexOf(m.path) == 0) {
                tocPath.push(m)
                return true
            }
            return false
        };
        TOC.forEach(isCurrentTOC)

        let recTOC = (m: TOCMenuEntry, lev: number) => {
            let templ = toc["item"]
            let mparams: Map<string> = {
                NAME: m.name,
            }
            if (m.path && !/^(https?:|\/)/.test(m.path))
                return error("Invalid link: " + m.path)
            if (/^\//.test(m.path) && d.versionPath) m.path = `/${d.versionPath}${m.path}`;
            mparams["LINK"] = m.path
            if (tocPath.indexOf(m) >= 0) {
                mparams["ACTIVE"] = 'active';
                mparams["EXPANDED"] = 'true';
                breadcrumb.push({
                    name: m.name,
                    href: m.path
                })
            } else {
                mparams["EXPANDED"] = 'false';
            }
            if (m.subitems && m.subitems.length > 0) {
                if (!!toc["toc-dropdown"]) {
                    // if macros support "toc-*", use them
                    if (m.name !== "") {
                        templ = toc["toc-dropdown"]
                    } else {
                        templ = toc["toc-dropdown-noLink"]
                    }
                }
                else {
                    // if macros don't support "toc-*"
                    /** TODO: when all targets bumped to include https://github.com/microsoft/pxt/pull/6058,
                     * delete this else branch, and remove
                     * top-dropdown, top-dropdown-noheading, inner-dropdown, and nested-dropdown from
                     * docfiles/macros.html **/
                    if (lev == 0) {
                        if (m.name !== "") {
                            templ = toc["top-dropdown"]
                        } else {
                            templ = toc["top-dropdown-noHeading"]
                        }
                    } else if (lev == 1) templ = toc["inner-dropdown"]
                    else templ = toc["nested-dropdown"]
                }
                mparams["ITEMS"] = m.subitems.map(e => recTOC(e, lev + 1)).join("\n")
            } else {
                if (/^-+$/.test(m.name)) {
                    templ = toc["divider"]
                }
            }
            return injectHtml(templ, mparams, ["ITEMS"])
        }

        params["menu"] = (theme.docMenu || []).map(e => recMenu(e, 0)).join("\n")
        params["TOC"] = TOC.map(e => recTOC(e, 0)).join("\n")

        if (theme.appStoreID)
            params["appstoremeta"] = `<meta name="apple-itunes-app" content="app-id=${U.htmlEscape(theme.appStoreID)}"/>`

        let breadcrumbHtml = '';
        if (breadcrumb.length > 1) {
            breadcrumbHtml = `
            <nav class="ui breadcrumb" aria-label="${lf("Breadcrumb")}">
                ${breadcrumb.map((b, i) =>
                `<a class="${i == breadcrumb.length - 1 ? "active" : ""} section"
                        href="${html2Quote(b.href)}" aria-current="${i == breadcrumb.length - 1 ? "page" : ""}">${html2Quote(b.name)}</a>`)
                    .join('<i class="right chevron icon divider"></i>')}
            </nav>`;
        }

        params["breadcrumb"] = breadcrumbHtml;

        if (theme.boardName)
            params["boardname"] = html2Quote(theme.boardName);
        if (theme.boardNickname)
            params["boardnickname"] = html2Quote(theme.boardNickname);
        if (theme.driveDisplayName)
            params["drivename"] = html2Quote(theme.driveDisplayName);
        if (theme.homeUrl)
            params["homeurl"] = html2Quote(theme.homeUrl);


        params["targetid"] = theme.id || "???";
        params["targetname"] = theme.name || "Microsoft MakeCode";
        params["docsheader"] = theme.docsHeader || "Documentation";
        params["orgtitle"] = "MakeCode";

        const docsLogo = theme.docsLogo && U.htmlEscape(theme.docsLogo);
        const orgLogo = (theme.organizationWideLogo || theme.organizationLogo) && U.htmlEscape(theme.organizationWideLogo || theme.organizationLogo);
        const orglogomobile = theme.organizationLogo && U.htmlEscape(theme.organizationLogo)
        params["targetlogo"] = docsLogo ? `<img aria-hidden="true" role="presentation" class="ui ${theme.logoWide ? "small" : "mini"} image" src="${docsLogo}" />` : ""
        params["orglogo"] = orgLogo ? `<img aria-hidden="true" role="presentation" class="ui image" src="${orgLogo}" />` : ""
        params["orglogomobile"] = orglogomobile ? `<img aria-hidden="true" role="presentation" class="ui image" src="${orglogomobile}" />` : ""

        let ghURLs = d.ghEditURLs || []
        if (ghURLs.length) {
            let ghText = `<p style="margin-top:1em">\n`
            let linkLabel = lf("Edit this page on GitHub")
            for (let u of ghURLs) {
                ghText += `<a href="${u}"><i class="write icon"></i>${linkLabel}</a><br>\n`;
                linkLabel = lf("Edit template of this page on GitHub")
            }
            ghText += `</p>\n`
            params["github"] = ghText
        } else {
            params["github"] = "";
        }

        // Add accessiblity menu
        const accMenuHtml = `
            <a href="#maincontent" class="ui item link" tabindex="0" role="menuitem">${lf("Skip to main content")}</a>
        `
        params['accMenu'] = accMenuHtml;

        const printButtonTitleText = lf("Print this page")
        // Add print button
        const printBtnHtml = `
            <button id="printbtn" class="circular ui icon right floated button hideprint" title="${printButtonTitleText}" aria-label="${printButtonTitleText}">
                <i class="icon print"></i>
            </button>
        `
        params['printBtn'] = printBtnHtml;

        // Add sidebar toggle
        const sidebarToggleHtml = `
            <a id="togglesidebar" class="launch icon item" tabindex="0" title="Side menu" aria-label="${lf("Side menu")}" role="menuitem" aria-expanded="false">
                <i class="content icon"></i>
            </a>
        `
        params['sidebarToggle'] = sidebarToggleHtml;

        // Add search bars
        const searchBarIds = ['tocsearch1', 'tocsearch2']
        const searchBarsHtml = searchBarIds.map((searchBarId) => {
            return `
                <input type="search" name="q" placeholder="${lf("Search...")}" aria-label="${lf("Search Documentation")}">
                <i onclick="document.getElementById('${searchBarId}').submit();" tabindex="0" class="search link icon" aria-label="${lf("Search")}" role="button"></i>
            `;
        })
        params["searchBar1"] = searchBarsHtml[0];
        params["searchBar2"] = searchBarsHtml[1];

        let style = '';
        if (theme.accentColor) style += `
.ui.accent { color: ${theme.accentColor}; }
.ui.inverted.accent { background: ${theme.accentColor}; }
`
        params["targetstyle"] = style;
        params["tocclass"] = theme.lightToc ? "lighttoc" : "inverted";

        for (let k of Object.keys(theme)) {
            let v = (theme as any)[k]
            if (params[k] === undefined && typeof v == "string")
                params[k] = v
        }

        d.finish = () => injectHtml(d.html, params, [
            "body",
            "menu",
            "accMenu",
            "TOC",
            "prev",
            "next",
            "printBtn",
            "breadcrumb",
            "targetlogo",
            "orglogo",
            "orglogomobile",
            "github",
            "JSON",
            "appstoremeta",
            "sidebarToggle",
            "searchBar1",
            "searchBar2"
        ])

        // Normalize any path URL with any version path in the current URL
        function normalizeUrl(href: string) {
            if (!href) return href;
            const relative = href.indexOf('/') == 0;
            if (relative && d.versionPath) href = `/${d.versionPath}${href}`;
            return href;
        }
    }