128 lines
3.2 KiB
JavaScript
128 lines
3.2 KiB
JavaScript
/**
|
|
* @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<ElementContent>} */
|
|
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<ElementContent>} */
|
|
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'}
|
|
]
|
|
}
|
|
}
|