/** * @typedef {import('hast').Element} Element * @typedef {import('hast').ElementContent} ElementContent * * @typedef {import('./state.js').State} State */ import {normalizeUri} from 'micromark-util-sanitize-uri' /** * Generate a hast footer for called footnote definitions. * * @param {State} state * Info passed around. * @returns {Element | undefined} * `section` element or `undefined`. */ export function footer(state) { /** @type {Array} */ const listItems = [] let index = -1 while (++index < state.footnoteOrder.length) { const def = state.footnoteById[state.footnoteOrder[index]] if (!def) { continue } const content = state.all(def) const id = String(def.identifier).toUpperCase() const safeId = normalizeUri(id.toLowerCase()) let referenceIndex = 0 /** @type {Array} */ const backReferences = [] while (++referenceIndex <= state.footnoteCounts[id]) { /** @type {Element} */ const backReference = { type: 'element', tagName: 'a', properties: { href: '#' + state.clobberPrefix + 'fnref-' + safeId + (referenceIndex > 1 ? '-' + referenceIndex : ''), dataFootnoteBackref: true, className: ['data-footnote-backref'], ariaLabel: state.footnoteBackLabel }, children: [{type: 'text', value: '↩'}] } if (referenceIndex > 1) { backReference.children.push({ type: 'element', tagName: 'sup', children: [{type: 'text', value: String(referenceIndex)}] }) } if (backReferences.length > 0) { backReferences.push({type: 'text', value: ' '}) } backReferences.push(backReference) } const tail = content[content.length - 1] if (tail && tail.type === 'element' && tail.tagName === 'p') { const tailTail = tail.children[tail.children.length - 1] if (tailTail && tailTail.type === 'text') { tailTail.value += ' ' } else { tail.children.push({type: 'text', value: ' '}) } tail.children.push(...backReferences) } else { content.push(...backReferences) } /** @type {Element} */ const listItem = { type: 'element', tagName: 'li', properties: {id: state.clobberPrefix + 'fn-' + safeId}, children: state.wrap(content, true) } state.patch(def, listItem) listItems.push(listItem) } if (listItems.length === 0) { return } return { type: 'element', tagName: 'section', properties: {dataFootnotes: true, className: ['footnotes']}, children: [ { type: 'element', tagName: state.footnoteLabelTagName, properties: { // To do: use structured clone. ...JSON.parse(JSON.stringify(state.footnoteLabelProperties)), id: 'footnote-label' }, children: [{type: 'text', value: state.footnoteLabel}] }, {type: 'text', value: '\n'}, { type: 'element', tagName: 'ol', properties: {}, children: state.wrap(listItems, true) }, {type: 'text', value: '\n'} ] } }