UNPKG

print-toc

Version:

Generates a table of contents for a printed HTML document.

133 lines (132 loc) 5.31 kB
"use strict"; 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;