diff --git a/assets/images/site/rendered-footnote.png b/assets/images/site/rendered-footnote.png new file mode 100644 index 0000000000..964cf53ff7 Binary files /dev/null and b/assets/images/site/rendered-footnote.png differ diff --git a/components/lib/events.ts b/components/lib/events.ts index 751fdaf94f..03d0236b37 100644 --- a/components/lib/events.ts +++ b/components/lib/events.ts @@ -172,9 +172,13 @@ function trackScroll() { if (scrollPosition > maxScrollY) maxScrollY = scrollPosition } +function sendPage() { + const pageEvent = sendEvent({ type: EventType.page }) + pageEventId = pageEvent?.context?.event_id +} + function sendExit() { if (sentExit) return - if (document.visibilityState !== 'hidden') return sentExit = true const { render, firstContentfulPaint, domInteractive, domComplete } = getPerformance() return sendEvent({ @@ -188,9 +192,27 @@ function sendExit() { }) } -function initPageEvent() { - const pageEvent = sendEvent({ type: EventType.page }) - pageEventId = pageEvent?.context?.event_id +function initPageAndExitEvent() { + sendPage() // Initial page hit + + // Regular page exits + window.addEventListener('scroll', trackScroll) + document.addEventListener('visibilitychange', () => { + if (document.visibilityState === 'hidden') { + sendExit() + } + }) + + // Client-side routing + const pushState = history.pushState + history.pushState = function (...args) { + sendExit() + const result = pushState.call(history, ...args) + sendPage() + sentExit = false + maxScrollY = 0 + return result + } } function initClipboardEvent() { @@ -213,11 +235,6 @@ function initLinkEvent() { }) } -function initExitEvent() { - window.addEventListener('scroll', trackScroll) - document.addEventListener('visibilitychange', sendExit) -} - function initPrintEvent() { window.addEventListener('beforeprint', () => { sendEvent({ type: EventType.print }) @@ -225,8 +242,7 @@ function initPrintEvent() { } export default function initializeEvents() { - initPageEvent() // must come first - initExitEvent() + initPageAndExitEvent() // must come first initLinkEvent() initClipboardEvent() initPrintEvent() diff --git a/content/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax.md b/content/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax.md index fb73845730..ad860c2c4e 100644 --- a/content/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax.md +++ b/content/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax.md @@ -261,6 +261,22 @@ For a full list of available emoji and codes, check out [the Emoji-Cheat-Sheet]( You can create a new paragraph by leaving a blank line between lines of text. +{% ifversion fpt or ghae-next or ghes > 3.3 %} +## Footnotes + +You can add footnotes to your content by using this bracket syntax: + +``` +Here is a simple footnote[^1]. + +[^1]: My reference. +``` + +The footnote will render like this: + +![Rendered footnote](/assets/images/site/rendered-footnote.png) +{% endif %} + ## Ignoring Markdown formatting You can tell {% data variables.product.product_name %} to ignore (or escape) Markdown formatting by using `\` before the Markdown character. diff --git a/middleware/contextualizers/breadcrumbs.js b/middleware/contextualizers/breadcrumbs.js index 1b1870564b..35a69d487f 100644 --- a/middleware/contextualizers/breadcrumbs.js +++ b/middleware/contextualizers/breadcrumbs.js @@ -11,37 +11,66 @@ export default async function breadcrumbs(req, res, next) { const currentSiteTree = req.context.siteTree[req.context.currentLanguage][req.context.currentVersion] + const fallbackSiteTree = req.context.siteTree.en[req.context.currentVersion] - await createBreadcrumb( + req.context.breadcrumbs = await getBreadcrumbs( // Array of child pages on the root, i.e., the product level. currentSiteTree.childPages, - req.context + fallbackSiteTree.childPages, + req.context.currentPath.slice(3), + req.context.currentLanguage ) return next() } -async function createBreadcrumb(pageArray, context) { - // Find each page in the siteTree's array of child pages that starts with the requested path. - let childPage = pageArray.find((page) => context.currentPath.startsWith(page.href)) +async function getBreadcrumbs( + pageArray, + fallbackPageArray, + currentPathWithoutLanguage, + intendedLanguage +) { + // Find the page that starts with the requested path + let childPage = pageArray.find((page) => + currentPathWithoutLanguage.startsWith(page.href.slice(3)) + ) - // Fall back to English if needed - if (!childPage) { - childPage = pageArray.find((page) => - context.currentPath.startsWith(page.href.replace(`/${context.currentLanguage}`, '/en')) - ) - if (!childPage) return + // Find the page in the fallback page array (likely the English sub-tree) + const fallbackChildPage = + (fallbackPageArray || []).find((page) => { + return currentPathWithoutLanguage.startsWith(page.href.slice(3)) + }) || childPage + + // No matches, we bail + if (!childPage && !fallbackChildPage) { + return [] } - context.breadcrumbs.push({ - documentType: childPage.page.documentType, - href: childPage.href, - title: childPage.renderedShortTitle || childPage.renderedFullTitle, - }) + // Didn't find the intended page, but found the fallback + if (!childPage) { + childPage = fallbackChildPage + } - // Recursively loop through the siteTree and create each breadcrumb, until we reach the + const breadcrumb = { + documentType: childPage.page.documentType, + // give the breadcrumb the intendedLanguage, so nav through breadcrumbs doesn't inadvertantly change the user's selected language + href: `/${intendedLanguage}/${childPage.href.slice(4)}`, + title: childPage.renderedShortTitle || childPage.renderedFullTitle, + } + + // Recursively loop through the childPages and create each breadcrumb, until we reach the // point where the current siteTree page is the same as the requested page. Then stop. - if (childPage.childPages && context.currentPath !== childPage.href) { - createBreadcrumb(childPage.childPages, context) + if (childPage.childPages && currentPathWithoutLanguage !== childPage.href.slice(3)) { + return [ + breadcrumb, + ...(await getBreadcrumbs( + childPage.childPages, + fallbackChildPage.childPages, + currentPathWithoutLanguage, + intendedLanguage + )), + ] + } else { + return [breadcrumb] } }