asciidoctor-chunker
Version:
Creates chunked (multi-page) HTML from Asciidoctor's single HTML file with supporting the fine-tuned splits in chapters, sections and any depth of subsections.
163 lines (153 loc) • 5.56 kB
JavaScript
/*
* This file is a part of Asciidoctor Chunker project.
* Copyright (c) 2022 Wataru Shito (@waterloo_jp)
*/
;
import { pipe } from './FP.mjs';
import { getContentNode } from './Page.mjs';
/**
* Returns the Set instance which contains all the footnote
* ids defined in the page.
*
* @param {Node} footnotesNode the node of `div#footnotes`
* which is the container of all the footnotes defined in
* the page.
* @returns {Set<string>} The Set instance with footnote ids.
*/
const getFootnoteDefIds = (footnotesNode) => {
const fnoteDefIds = new Set();
footnotesNode.find('div.footnote').each((ele, i) => {
fnoteDefIds.add(ele.getAttr('id'));
});
return fnoteDefIds;
};
/**
* Removes the unreferred footnotes from the page and returns
* only the referer nodes, i.e. the anchor a.footnote, validly
* referring the footnotes within the page. The returned valid
* referer nodes are encapsulated in the Node instance as the
* selections.
*
* @param {Set<string>} footnoteDefIds The set of all the
* footnote def ids within the page. This Set is not
* modified in this function.
* @param {Node} footnotesNode The Node instance of current
* page's #footnotes nodes of which un-referred nodes will
* be removed as a side effect.
* @param {Node} referers The Node intance which holds all
* the referer anchors in the page. The node is modified
* in this function by removing referers which do not belong
* this page.
* @return {Node} the instance of valid referers that belongs
* this page which means they refer the footnotes defined
* in this page.
*/
const keepReferredFootnotes$ = (footnoteDefIds) =>
(footnotesNode) => (referers) => {
if (referers.length === 0) {
footnotesNode.empty$();
return referers;
}
const removingFootnotes = new Set([...footnoteDefIds]);
// console.log("before removing", removingFootnotes.size);
// console.log("Referers length", referers.length);
referers.each((ele, i) => {
removingFootnotes.delete(ele.getAttr('href').substring(1));
});
// console.log("after removing", removingFootnotes.size);
removingFootnotes.forEach(id => {
// console.log("removing", id);
footnotesNode.remove$(`#${id}`);
});
return referers;
};
/**
* The main function which handles the footnotes and their
* referers that belong the given page.
*
* @param {Function} referredFootnotesKeeper$ the curried
* functon of `keepReferredFootnotes$(footnoteDefIds: Map<string>)`.
* @param {Node} node The node of the chunked page.
*/
const updateFootnotes = (referredFootnotesKeeper$) => (node) => {
// each footnote definition has `<div id='_footnotedef_4' class='footnote'>`
// the referer has
// `<a id='_footnoteref_4' href='#_footnotedef_4' class='footnote'>
// multiply used footnote's referer does not have id as
// `<a href='#_footnotedef_4' class='footnote'>
//
// if a[href='#_footnotedef_4'] is whithin the page,
// div#_footnotedef_4 should be kept, and
// and id='_footnoteref_4' should be added to the first
// a[href='#_footnotedef_4'] in the page
// see if there are referers]
const rootNode = node.root();
pipe(
getContentNode,
// (a) => { console.log("here1"); return a },
_findFootnoteReferers,
referredFootnotesKeeper$,
_updateRefererId$,
)(rootNode);
return rootNode;
}
/**
* Returns Cheerio instance of selections of footnote referers anchor elements.
*
* @param {Node} contentNode The `div#content` node.
* @returns {Node} the Node instance of selections of footnote
* referers anchor elements.
*/
const _findFootnoteReferers = (contentNode) => contentNode.find('a.footnote');
/**
* Converts the url `_footnotedef_4` to `_footnoteref_4`
* where `#_footnotedef_4` is the hash link of the footnote
* and `#_footnoteref_4` is the corresponding refererer.
*
* @param {string} defURL The hash link to the footnote
* definition as `_footnotedef_4`.
* @returns corresponding referer's ID such as `_footnoteref_4`
*/
const _makeFootnoteRefId = (defURL) => `_footnoteref${defURL.substring(defURL.lastIndexOf('_'))}`;
/**
* Each footnote has an anchor that links back to its refererer.
* When a footnote is multiply referred, the footnote links back
* to only its first referer. Other referers should not have
* referer ID. This function appropriately sets the
* first referer's ID URL to the footnote's anchor.
*
* This has side effect that modifies some of referers anchor's
* id attribute.
*
* @param {Node} referers The Node instance with the selection of
* referer nodes in the current page. The selection possibly
* contains nodes referring different footnotes as well as
* nodes referring the same footnote.
*/
const _updateRefererId$ = (referers) => {
if (referers.length === 0) return referers;
const linkedBackReferers = new Set();
referers.each((a, i) => {
// if already has an id, this is the first referer
// in the original single html page
const id = a.getAttr('id');
const url = a.getAttr('href')
if (id) {
linkedBackReferers.add(url);
return;
}
if (linkedBackReferers.has(url)) return;
linkedBackReferers.add(url);
const refID = _makeFootnoteRefId(url);
a.setAttr$('id', refID);
});
return referers;
};
export {
getFootnoteDefIds,
keepReferredFootnotes$,
updateFootnotes,
_findFootnoteReferers, // exporting for testing
_makeFootnoteRefId, // exporting for testing
_updateRefererId$, // exporting for testing
};