print-toc
Version:
Generates a table of contents for a printed HTML document.
133 lines (132 loc) • 5.31 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
var pageHeightPx = 1134;
var pageWidthPx = 754;
var DEBUG = false;
var STYLES = "\n .page, .break {\n padding-top: 1px;\n margin-top: -1px;\n }\n .page {\n position: relative;\n page-break-after: auto;\n page-break-inside: avoid;\n display: block;\n }\n .break {\n page-break-before: always;\n }\n .debug_x {\n height: 1px;\n background: red;\n position: absolute;\n left: 0; \n }\n";
function comparePosition(a, b) {
var position = a.compareDocumentPosition(b);
if (position & Node.DOCUMENT_POSITION_FOLLOWING)
return "after";
if (position & Node.DOCUMENT_POSITION_PRECEDING)
return "before";
if (position & Node.DOCUMENT_POSITION_CONTAINS)
return "contains";
return "";
}
function offset(el) {
var rect = el.getBoundingClientRect();
var _a = document.body, scrollLeft = _a.scrollLeft, scrollTop = _a.scrollTop;
return {
top: rect.top + scrollTop,
bottom: rect.bottom + scrollTop,
left: rect.left + scrollLeft,
right: rect.right + scrollLeft,
};
}
function addStyle(styles) {
var css = document.createElement("style");
css.type = "text/css";
css.appendChild(document.createTextNode(styles));
document.getElementsByTagName("head")[0].appendChild(css);
}
function getVisibleBreaks() {
return Array.from(document.querySelectorAll(".break, .page")).filter(function (el) { return getComputedStyle(el).display !== "none"; });
}
function getBreakOffset(breakEl, yOffset) {
if (yOffset === void 0) { yOffset = 0; }
var className = breakEl.className;
var _a = offset(breakEl), top = _a.top, bottom = _a.bottom;
var offsetTop = yOffset + top;
var newYOffset = pageHeightPx - (offsetTop % pageHeightPx);
var isBroken = Math.floor(offsetTop / pageHeightPx) !==
Math.floor((bottom + yOffset) / pageHeightPx);
if (breakEl.className.includes("break") ||
(className.includes("page") && isBroken)) {
return newYOffset;
}
else {
return 0;
}
}
function findPageNumber(element) {
var precedingBreaks = getVisibleBreaks().filter(function (node) {
return ["before", "contains"].includes(comparePosition(element, node));
});
var yOffset = 0;
for (var _i = 0, precedingBreaks_1 = precedingBreaks; _i < precedingBreaks_1.length; _i++) {
var breakEl = precedingBreaks_1[_i];
yOffset += getBreakOffset(breakEl, yOffset);
}
var yPosition = offset(element).top + yOffset;
var pageNum = Math.floor(yPosition / pageHeightPx) + 1;
if (DEBUG) {
return pageNum + " (" + precedingBreaks.length + " breaks totalling " + Math.round(yOffset) + "px)";
}
return pageNum.toString();
}
function mockBreaks() {
getVisibleBreaks().forEach(function (breakEl) {
var marginTop = breakEl.style.marginTop;
if (!marginTop || marginTop === "-1px") {
breakEl.style.marginTop = getBreakOffset(breakEl) + "px";
}
});
}
function drawTOC(element) {
element.innerHTML = "\n <ol>\n " + Array.from(document.querySelectorAll("[data-section]"))
.map(function (el) {
var _a;
var id = el.dataset.section ||
((_a = el.textContent) === null || _a === void 0 ? void 0 : _a.toLowerCase().replace(/\s/g, "-"));
if (!id) {
throw new Error("You must provide a section title in the element, or an id in the data-section attribute.");
}
el.id = id;
return "\n <li>\n <a href=\"#" + id + "\">" + el.textContent + "</a>\n <span class=\"page-number\" data-page=\"" + id + "\"></span>\n </li>\n ";
})
.join("") + "\n </ol>\n ";
}
function drawDebug() {
mockBreaks();
var numberOfPages = Math.floor(document.body.scrollHeight / pageHeightPx);
for (var i = 0; i < numberOfPages; i++) {
var el = document.createElement("DIV");
el.style.width = pageWidthPx + "px";
el.style.top = (i + 1) * pageHeightPx + "px";
el.className = "debug_x";
document.body.appendChild(el);
}
window.addEventListener("beforeprint", function () {
getVisibleBreaks().forEach(function (breakEl) {
breakEl.style.marginTop = "-1px";
});
});
window.addEventListener("afterprint", function () {
mockBreaks();
});
}
function generateTOC(debug) {
if (debug === void 0) { debug = false; }
DEBUG = debug;
var toc = document.getElementById("table-of-contents");
if (!toc)
return;
addStyle(STYLES);
document.body.style.width = pageWidthPx + "px";
drawTOC(toc);
var pageNumbers = document.querySelectorAll(".page-number[data-page]");
pageNumbers.forEach(function (element) {
var page = element.dataset.page;
if (typeof page === "undefined")
throw new Error("Provide a value to the data-page attribute.");
var reference = document.getElementById(page);
if (!reference)
throw new Error("Table of contents scaffolding failed.");
element.textContent = findPageNumber(reference);
});
if (DEBUG) {
drawDebug();
}
}
exports.default = generateTOC;
;