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;
}
}