@vivliostyle/vfm
Version:
Custom Markdown syntax specialized in book authoring.
96 lines (95 loc) • 2.98 kB
JavaScript
import { select, selectAll } from 'hast-util-select';
import footnotes from 'remark-footnotes';
/**
* Replace the footnote link with Pandoc format.
* @param tree Tree of Hypertext AST.
*/
const replaceFootnoteLinks = (tree) => {
const sups = selectAll('sup[id^="fnref-"]', tree).filter((node) => node.children.length === 1 && node.children[0].tagName === 'a');
for (let i = 0; i < sups.length; ++i) {
const parent = sups[i];
const refIndex = i + 1;
parent.tagName = 'a';
parent.properties = {
id: `fnref${refIndex}`,
href: `#fn${refIndex}`,
className: ['footnote-ref'],
role: 'doc-noteref',
};
const child = parent.children[0];
child.tagName = 'sup';
child.properties = {};
child.children = [{ type: 'text', value: `${refIndex}` }];
}
};
/**
* Check if it has a class name as a back reference.
* @param className Array of class names.
* @returns `true` for back reference, `false` otherwise.
*/
const hasBackReferenceClass = (className) => {
if (Array.isArray(className)) {
for (const name of className) {
if (name === 'footnote-backref') {
return true;
}
}
}
return false;
};
/**
* Replace back reference with Pandoc format.
* @param elements Children elements of footnote.
* @param index Index of footnote.
*/
const replaceBackReference = (elements, index) => {
for (const element of elements) {
if (element.type === 'element' &&
element.tagName === 'a' &&
element.properties &&
hasBackReferenceClass(element.properties.className)) {
element.properties.href = `#fnref${index}`;
element.properties.className = ['footnote-back'];
element.properties.role = 'doc-backlink';
// Back reference is only one
break;
}
}
};
/**
* Replace the footnote with Pandoc format.
* @param tree Tree of Hypertext AST.
*/
const replaceFootnotes = (tree) => {
const area = select('div.footnotes', tree);
if (area && area.properties) {
area.tagName = 'section';
area.properties.role = 'doc-endnotes';
}
else {
return;
}
const items = selectAll('section.footnotes ol li', tree);
for (let i = 0; i < items.length; ++i) {
const item = items[i];
if (!item.properties) {
continue;
}
const refIndex = i + 1;
item.properties.id = `fn${refIndex}`;
item.properties.role = 'doc-endnote';
replaceBackReference(item.children, refIndex);
}
};
/**
* Process Markdown AST.
*/
export const mdast = [footnotes, { inlineNotes: true }];
/**
* Process math related Hypertext AST.
* Resolves HTML diffs between `remark-footnotes` and Pandoc footnotes.
*/
export const hast = () => (tree) => {
replaceFootnoteLinks(tree);
replaceFootnotes(tree);
};