@segnavia/html 0.37.0 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/book/book-contents.js +33 -0
- package/dist/book/book-document-nodes.js +37 -0
- package/dist/book/book-document.d.ts +2 -0
- package/dist/book/book-document.js +178 -155
- package/dist/book/book-flow.js +23 -7
- package/dist/book/book-held.d.ts +4 -0
- package/dist/book/book-held.js +60 -7
- package/dist/book/book-issues.js +42 -0
- package/dist/book/book-page-reference.d.ts +7 -0
- package/dist/book/book-page-reference.js +19 -0
- package/dist/book/book-part-build.js +50 -0
- package/dist/book/dom.d.ts +1 -0
- package/dist/book/notes.d.ts +2 -0
- package/dist/book/notes.js +117 -51
- package/dist/book/paginate-book.d.ts +2 -1
- package/dist/book/paginate-book.js +34 -26
- package/dist/book/paginate-notes.js +19 -7
- package/dist/book/paginate-sequence.d.ts +1 -1
- package/dist/book/paginate-sequence.js +60 -28
- package/dist/book/paginate-types.d.ts +18 -5
- package/dist/book/region-fit.d.ts +16 -0
- package/dist/book/region-fit.js +111 -0
- package/dist/book/types.d.ts +5 -2
- package/dist/book/types.js +2 -2
- package/dist/font-css.js +13 -2
- package/dist/font-format.js +2 -6
- package/dist/index.d.ts +9 -6
- package/dist/index.js +7 -4
- package/dist/motifs.d.ts +2 -0
- package/dist/motifs.js +5 -2
- package/dist/placements.d.ts +89 -0
- package/dist/placements.js +189 -0
- package/dist/printed-pages.d.ts +2 -7
- package/dist/printed-pages.js +1 -1
- package/dist/sanitize.d.ts +2 -0
- package/dist/sanitize.js +82 -44
- package/package.json +2 -2
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { element, linkChildren, text } from "./dom.js";
|
|
2
|
+
import { isTag } from "domhandler";
|
|
3
|
+
import { resolveDirection } from "@segnavia/format";
|
|
4
|
+
//#region src/book/book-contents.ts
|
|
5
|
+
function contentsList(book, headings, options) {
|
|
6
|
+
const entries = headings.map((heading) => {
|
|
7
|
+
const page = book.contents.pageNumbers ? options.pageNumbers?.[heading.id] ?? "" : "";
|
|
8
|
+
const level = heading.level + (heading.depth ?? 0);
|
|
9
|
+
const attributes = {
|
|
10
|
+
class: "segnavia-contents-entry",
|
|
11
|
+
"data-level": String(level),
|
|
12
|
+
...heading.depth === void 0 ? {} : { "data-depth": String(heading.depth) }
|
|
13
|
+
};
|
|
14
|
+
return element("li", attributes, [element("a", {
|
|
15
|
+
href: options.crossLinks ? `segnavia:target/${heading.id}` : `#${heading.id}`,
|
|
16
|
+
dir: resolveDirection(book.dir, book.lang, heading.text)
|
|
17
|
+
}, [text(heading.text)]), book.contents.pageNumbers ? element("a", {
|
|
18
|
+
class: "segnavia-contents-page",
|
|
19
|
+
href: `#${heading.id}`,
|
|
20
|
+
"data-ref": "page"
|
|
21
|
+
}, [text(page)]) : element("span", { class: "segnavia-contents-page" }, [])]);
|
|
22
|
+
});
|
|
23
|
+
return element("ol", { class: "segnavia-contents-list" }, entries);
|
|
24
|
+
}
|
|
25
|
+
function addContents(book, section, headings, options) {
|
|
26
|
+
const title = section.info.title;
|
|
27
|
+
const titleNode = section.node.children.find((child) => isTag(child) && child.name === "h1");
|
|
28
|
+
if (title !== void 0 && titleNode && isTag(titleNode)) titleNode.attribs.dir = resolveDirection(book.dir, book.lang, title);
|
|
29
|
+
section.node.children.push(contentsList(book, headings, options));
|
|
30
|
+
linkChildren(section.node);
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { addContents };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { elements } from "../sanitize.js";
|
|
2
|
+
import { element, text } from "./dom.js";
|
|
3
|
+
//#region src/book/book-document-nodes.ts
|
|
4
|
+
function targetLinks(sections) {
|
|
5
|
+
const ids = sections.flatMap((section) => [section.info.id, ...elements(section.node.children).flatMap((node) => node.attribs.id ? [node.attribs.id] : [])]);
|
|
6
|
+
return element("div", {
|
|
7
|
+
class: "segnavia-targets",
|
|
8
|
+
"aria-hidden": "true"
|
|
9
|
+
}, [...new Set(ids)].map((id) => element("a", {
|
|
10
|
+
href: `#${id}`,
|
|
11
|
+
tabindex: "-1"
|
|
12
|
+
}, [text("")])));
|
|
13
|
+
}
|
|
14
|
+
function notesGalley(series, paragraphs) {
|
|
15
|
+
const notes = paragraphs.map((paragraph) => element("div", { "data-segnavia-note": paragraph.attribs.id ?? "" }, [paragraph]));
|
|
16
|
+
return element("aside", {
|
|
17
|
+
"data-segnavia-notes-galley": "",
|
|
18
|
+
"data-segnavia-note-series": series,
|
|
19
|
+
"aria-hidden": "true"
|
|
20
|
+
}, notes);
|
|
21
|
+
}
|
|
22
|
+
function notesGalleys(series, paragraphs) {
|
|
23
|
+
return series.flatMap((item) => {
|
|
24
|
+
const held = paragraphs.get(item.name);
|
|
25
|
+
return item.placement === "page" && held?.length ? [notesGalley(item.name, held)] : [];
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function withBlankPages(sections, blankBefore) {
|
|
29
|
+
const counts = /* @__PURE__ */ new Map();
|
|
30
|
+
for (const id of blankBefore ?? []) counts.set(id, (counts.get(id) ?? 0) + 1);
|
|
31
|
+
return sections.flatMap((section) => [...Array.from({ length: counts.get(section.info.id) ?? 0 }, () => element("div", {
|
|
32
|
+
class: "segnavia-blank",
|
|
33
|
+
"aria-hidden": "true"
|
|
34
|
+
})), section.node]);
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { notesGalleys, targetLinks, withBlankPages };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { BookDocument, BookDocumentOptions } from "./types.js";
|
|
2
|
+
import "./book-held.js";
|
|
3
|
+
import "./notes.js";
|
|
2
4
|
import { Book } from "@segnavia/format";
|
|
3
5
|
//#region src/book/book-document.d.ts
|
|
4
6
|
export declare function bookDocument(book: Book, options?: BookDocumentOptions): BookDocument;
|
|
@@ -1,29 +1,16 @@
|
|
|
1
1
|
import { elements, parse, serialize } from "../sanitize.js";
|
|
2
|
-
import { bookDocumentCss, bookFlowPageGeometry, markBlockPositions, resolveBookBuild } from "./book-flow.js";
|
|
3
2
|
import { element, linkChildren, text } from "./dom.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { addContents } from "./book-contents.js";
|
|
4
|
+
import { notesGalleys, targetLinks, withBlankPages } from "./book-document-nodes.js";
|
|
5
|
+
import { bookDocumentCss, bookFlowPageGeometry, markBlockPositions, resolveBookBuild } from "./book-flow.js";
|
|
6
|
+
import { authoredSection, collectHeadings, designedSection, flowAttribs, markPlacementIssues, sectionNode, templateHolders, templateRegionIssues, templatedSection, templatedSectionIssues } from "./book-held.js";
|
|
7
|
+
import { pageReferenceNodes, pageReferenceTarget, pageReferenceTargets } from "./book-page-reference.js";
|
|
8
|
+
import { duplicateIdIssues, pageReferenceIssues, reservedIdIssues } from "./book-issues.js";
|
|
9
|
+
import { addPart, attachPendingPartHeadings, partNames } from "./book-part-build.js";
|
|
10
|
+
import { noteState, transformNotes } from "./notes.js";
|
|
6
11
|
import { bookPrintPart } from "./types.js";
|
|
7
|
-
import {
|
|
8
|
-
import { isBookContents, isDesignedSection, isTemplatedSection, noteMarkerIssues, resolveDirection } from "@segnavia/format";
|
|
12
|
+
import { bookSections, isBookContents, isDesignedSection, isPart, isTemplatedSection, noteMarkerIssues, resolveDirection } from "@segnavia/format";
|
|
9
13
|
//#region src/book/book-document.ts
|
|
10
|
-
function direction(book, value) {
|
|
11
|
-
return resolveDirection(book.dir, book.lang, value);
|
|
12
|
-
}
|
|
13
|
-
function authoredSection(section, id, build) {
|
|
14
|
-
const document = parse(section.html);
|
|
15
|
-
const runningHead = section.title;
|
|
16
|
-
const node = sectionNode("front", id, document.children, build, runningHead, flowAttribs(section.opens, section.parents));
|
|
17
|
-
return {
|
|
18
|
-
info: {
|
|
19
|
-
id,
|
|
20
|
-
kind: "front",
|
|
21
|
-
title: section.title,
|
|
22
|
-
runningHead
|
|
23
|
-
},
|
|
24
|
-
node
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
14
|
function contentsSection(entry, build) {
|
|
28
15
|
const id = "segnavia-contents";
|
|
29
16
|
const title = entry.title;
|
|
@@ -35,13 +22,14 @@ function contentsSection(entry, build) {
|
|
|
35
22
|
info: {
|
|
36
23
|
id,
|
|
37
24
|
kind: "contents",
|
|
25
|
+
division: "front",
|
|
38
26
|
title,
|
|
39
27
|
runningHead: title
|
|
40
28
|
},
|
|
41
29
|
node: sectionNode("contents", id, children, build, title, flowAttribs(entry.opens, entry.parents))
|
|
42
30
|
};
|
|
43
31
|
}
|
|
44
|
-
function chapterSection(chapter, index, build) {
|
|
32
|
+
function chapterSection(chapter, index, division, build) {
|
|
45
33
|
const id = `segnavia-chapter-${index}`;
|
|
46
34
|
const document = parse(chapter.html);
|
|
47
35
|
const runningHead = chapter.runningHead ?? chapter.title;
|
|
@@ -49,80 +37,40 @@ function chapterSection(chapter, index, build) {
|
|
|
49
37
|
info: {
|
|
50
38
|
id,
|
|
51
39
|
kind: "chapter",
|
|
40
|
+
division,
|
|
52
41
|
title: chapter.title,
|
|
53
42
|
runningHead
|
|
54
43
|
},
|
|
55
44
|
node: sectionNode("chapter", id, document.children, build, runningHead, flowAttribs(chapter.opens, chapter.parents))
|
|
56
45
|
};
|
|
57
46
|
}
|
|
58
|
-
function
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
47
|
+
function appendDesigned(entry, id, kind, book, build, sectionEntry) {
|
|
48
|
+
const held = designedSection(entry, id, kind, sectionEntry.division, build.build, book.contents.depth, build.build === "flow" ? bookFlowPageGeometry(book) : void 0);
|
|
49
|
+
build.sections.push(held.built);
|
|
50
|
+
decorateSection(sectionEntry, build);
|
|
51
|
+
build.issues.push(...reservedIdIssues(held.built), ...columnIssues(held.built));
|
|
52
|
+
build.headings.push(...withSectionDepth(held.headings, sectionEntry.depth));
|
|
53
|
+
const context = noteContext(sectionEntry, held.built.info.id, 0, entry.title, build);
|
|
54
|
+
transformNotes(held.built.node, book, context, build.notes, build.build === "galley");
|
|
63
55
|
}
|
|
64
|
-
function
|
|
65
|
-
const held =
|
|
66
|
-
|
|
56
|
+
function appendTemplated(entry, id, kind, book, build, sectionEntry) {
|
|
57
|
+
const held = templatedSection(entry, id, kind, sectionEntry.division, build.build, book.contents.depth, build.templates, build.build === "flow" ? bookFlowPageGeometry(book) : void 0);
|
|
58
|
+
build.sections.push(held.built);
|
|
59
|
+
decorateSection(sectionEntry, build);
|
|
60
|
+
build.issues.push(...reservedIdIssues(held.built), ...columnIssues(held.built), ...templatedSectionIssues(entry, id, build.templates));
|
|
61
|
+
build.headings.push(...withSectionDepth(held.headings, sectionEntry.depth));
|
|
62
|
+
const context = noteContext(sectionEntry, held.built.info.id, 0, entry.title, build);
|
|
63
|
+
transformNotes(held.built.node, book, context, build.notes, build.build === "galley");
|
|
67
64
|
}
|
|
68
|
-
function
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
function withSectionDepth(headings, depth) {
|
|
66
|
+
return depth > 0 ? headings.map((heading) => ({
|
|
67
|
+
...heading,
|
|
68
|
+
depth
|
|
69
|
+
})) : headings;
|
|
71
70
|
}
|
|
72
71
|
function chapterHeadings(section, chapter, depth) {
|
|
73
72
|
return collectHeadings(section.node.children, section.info.id, depth, 2, (n) => `segnavia-h-${chapter}-${n}`, { n: 0 });
|
|
74
73
|
}
|
|
75
|
-
function contentsList(book, headings, options) {
|
|
76
|
-
const entries = headings.map((heading) => {
|
|
77
|
-
const page = book.contents.pageNumbers ? options.pageNumbers?.[heading.id] ?? "" : "";
|
|
78
|
-
return element("li", {
|
|
79
|
-
class: "segnavia-contents-entry",
|
|
80
|
-
"data-level": String(heading.level)
|
|
81
|
-
}, [element("a", {
|
|
82
|
-
href: options.crossLinks ? `segnavia:target/${heading.id}` : `#${heading.id}`,
|
|
83
|
-
dir: direction(book, heading.text)
|
|
84
|
-
}, [text(heading.text)]), element("span", {
|
|
85
|
-
class: "segnavia-contents-page",
|
|
86
|
-
"data-target": heading.id,
|
|
87
|
-
dir: direction(book, page)
|
|
88
|
-
}, [text(page)])]);
|
|
89
|
-
});
|
|
90
|
-
return element("ol", { class: "segnavia-contents-list" }, entries);
|
|
91
|
-
}
|
|
92
|
-
function addContents(book, section, headings, options) {
|
|
93
|
-
const title = section.info.title;
|
|
94
|
-
const titleNode = section.node.children.find((child) => isTag(child) && child.name === "h1");
|
|
95
|
-
if (title !== void 0 && titleNode && isTag(titleNode)) titleNode.attribs.dir = direction(book, title);
|
|
96
|
-
section.node.children.push(contentsList(book, headings, options));
|
|
97
|
-
linkChildren(section.node);
|
|
98
|
-
}
|
|
99
|
-
function targetLinks(sections) {
|
|
100
|
-
const ids = sections.flatMap((section) => [section.info.id, ...elements(section.node.children).flatMap((node) => node.attribs.id ? [node.attribs.id] : [])]);
|
|
101
|
-
return element("div", {
|
|
102
|
-
class: "segnavia-targets",
|
|
103
|
-
"aria-hidden": "true"
|
|
104
|
-
}, [...new Set(ids)].map((id) => element("a", { href: `#${id}` }, [text("")])));
|
|
105
|
-
}
|
|
106
|
-
function duplicateIdIssues(sections, headings) {
|
|
107
|
-
const listed = new Set(headings.map((heading) => heading.id));
|
|
108
|
-
const occurrences = /* @__PURE__ */ new Map();
|
|
109
|
-
for (const section of sections) {
|
|
110
|
-
const ids = [section.info.id, ...elements(section.node.children).flatMap((node) => node.attribs.id ? [node.attribs.id] : [])];
|
|
111
|
-
for (const id of ids) occurrences.set(id, [...occurrences.get(id) ?? [], section.info]);
|
|
112
|
-
}
|
|
113
|
-
return [...occurrences].flatMap(([id, owners]) => {
|
|
114
|
-
if (!listed.has(id) || owners.length < 2) return [];
|
|
115
|
-
const named = owners.slice(0, 2).map((owner) => `${owner.title ?? owner.id} (${owner.id})`);
|
|
116
|
-
return [`destination id ${id} is used in both ${named[0]} and ${named[1]}; listed heading and generated destination ids must be unique`];
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
function reservedIdIssues(section) {
|
|
120
|
-
return elements(section.node.children).flatMap((node) => {
|
|
121
|
-
const id = node.attribs.id;
|
|
122
|
-
if (!id?.startsWith("segnavia-")) return [];
|
|
123
|
-
return [`authored id ${id} in ${section.info.title ?? section.info.id} (${section.info.id}) uses the reserved segnavia- prefix`];
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
74
|
function columnsValueOk(value) {
|
|
127
75
|
if (!/^[0-9]+$/.test(value)) return false;
|
|
128
76
|
const count = Number(value);
|
|
@@ -136,112 +84,171 @@ function columnIssues(section) {
|
|
|
136
84
|
return [`The ${node.name} in ${where} could not establish data-columns ${JSON.stringify(value)}; set it to an integer from 2 to 4 to clear this`];
|
|
137
85
|
});
|
|
138
86
|
}
|
|
139
|
-
function
|
|
140
|
-
const titleValue = book.endnotes.title;
|
|
87
|
+
function notesHeading(book, notes) {
|
|
141
88
|
return element("h1", {
|
|
142
|
-
class:
|
|
143
|
-
dir:
|
|
144
|
-
}, [text(
|
|
89
|
+
class: notes.series.titleClass,
|
|
90
|
+
dir: resolveDirection(book.dir, book.lang, notes.series.title)
|
|
91
|
+
}, [text(notes.series.title)]);
|
|
145
92
|
}
|
|
146
|
-
function
|
|
93
|
+
function notesSection(book, notes, build, id, runningHead, parts, opens) {
|
|
94
|
+
const scope = notes.scopeTitle !== void 0 && (notes.series.placement === "chapter" || notes.series.placement === "part") ? [element("p", {
|
|
95
|
+
class: "segnavia-notes-scope",
|
|
96
|
+
dir: resolveDirection(book.dir, book.lang, notes.scopeTitle)
|
|
97
|
+
}, [text(notes.scopeTitle)])] : [];
|
|
147
98
|
return {
|
|
148
99
|
info: {
|
|
149
100
|
id,
|
|
150
101
|
kind: "endnotes",
|
|
151
|
-
|
|
102
|
+
division: notes.targetId === "book" ? "body" : notes.division,
|
|
103
|
+
title: notes.series.title,
|
|
152
104
|
runningHead
|
|
153
105
|
},
|
|
154
|
-
node: sectionNode("endnotes", id, [
|
|
106
|
+
node: sectionNode("endnotes", id, [
|
|
107
|
+
notesHeading(book, notes),
|
|
108
|
+
...scope,
|
|
109
|
+
...notes.paragraphs
|
|
110
|
+
], build, runningHead, {
|
|
111
|
+
...opens ? flowAttribs(opens) : {},
|
|
112
|
+
...parts ? { "data-segnavia-parts": JSON.stringify(parts) } : {}
|
|
113
|
+
})
|
|
155
114
|
};
|
|
156
115
|
}
|
|
157
|
-
function
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}, notes);
|
|
163
|
-
}
|
|
164
|
-
function withBlankPages(sections, blankBefore) {
|
|
165
|
-
const counts = /* @__PURE__ */ new Map();
|
|
166
|
-
for (const id of blankBefore ?? []) counts.set(id, (counts.get(id) ?? 0) + 1);
|
|
167
|
-
return sections.flatMap((section) => [...Array.from({ length: counts.get(section.info.id) ?? 0 }, () => element("div", {
|
|
168
|
-
class: "segnavia-blank",
|
|
169
|
-
"aria-hidden": "true"
|
|
170
|
-
})), section.node]);
|
|
171
|
-
}
|
|
172
|
-
function takeHeldNotesOwner(build) {
|
|
173
|
-
build.heldNotesOwner -= 1;
|
|
174
|
-
return build.heldNotesOwner;
|
|
116
|
+
function decorateSection(sectionEntry, build) {
|
|
117
|
+
const section = build.sections.at(-1);
|
|
118
|
+
if (!section) return;
|
|
119
|
+
if (sectionEntry.ancestry.length > 0) section.node.attribs["data-segnavia-parts"] = JSON.stringify(partNames(sectionEntry.ancestry));
|
|
120
|
+
attachPendingPartHeadings(build, sectionEntry, section.info.id);
|
|
175
121
|
}
|
|
176
|
-
function
|
|
177
|
-
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
122
|
+
function noteContext(sectionEntry, targetId, chapter, title, build) {
|
|
123
|
+
const ancestry = sectionEntry.ancestry.at(-1);
|
|
124
|
+
for (const part of sectionEntry.ancestry) build.partTargets.set(part.id, targetId);
|
|
125
|
+
return {
|
|
126
|
+
chapter,
|
|
127
|
+
title,
|
|
128
|
+
division: sectionEntry.division,
|
|
129
|
+
targetId,
|
|
130
|
+
...chapter === 0 && sectionEntry.division !== "front" ? { placementKey: targetId.replace(/^segnavia-/, "") } : {},
|
|
131
|
+
...ancestry ? { part: {
|
|
132
|
+
id: ancestry.id,
|
|
133
|
+
title: ancestry.title
|
|
134
|
+
} } : {}
|
|
135
|
+
};
|
|
182
136
|
}
|
|
183
137
|
function addFrontMatter(book, build) {
|
|
184
138
|
let contents;
|
|
185
139
|
let frontNumber = 0;
|
|
186
|
-
for (const
|
|
140
|
+
for (const sectionEntry of bookSections(book)) {
|
|
141
|
+
if (sectionEntry.division !== "front") continue;
|
|
142
|
+
const { entry } = sectionEntry;
|
|
143
|
+
if (isPart(entry)) {
|
|
144
|
+
addPart(sectionEntry, build);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
187
147
|
if (isBookContents(entry)) {
|
|
188
148
|
contents = contentsSection(entry, build.build);
|
|
189
149
|
build.sections.push(contents);
|
|
150
|
+
decorateSection(sectionEntry, build);
|
|
190
151
|
continue;
|
|
191
152
|
}
|
|
192
153
|
if (isDesignedSection(entry)) {
|
|
193
154
|
build.designedNumber += 1;
|
|
194
|
-
appendDesigned(entry, `segnavia-designed-${build.designedNumber}`, "front", book, build
|
|
155
|
+
appendDesigned(entry, `segnavia-designed-${build.designedNumber}`, "front", book, build, sectionEntry);
|
|
195
156
|
continue;
|
|
196
157
|
}
|
|
197
158
|
if (isTemplatedSection(entry)) {
|
|
198
159
|
build.templatedNumber += 1;
|
|
199
|
-
appendTemplated(entry, `segnavia-templated-${build.templatedNumber}`, "front", book, build
|
|
160
|
+
appendTemplated(entry, `segnavia-templated-${build.templatedNumber}`, "front", book, build, sectionEntry);
|
|
200
161
|
continue;
|
|
201
162
|
}
|
|
163
|
+
if (!("html" in entry)) continue;
|
|
202
164
|
frontNumber += 1;
|
|
203
165
|
const section = authoredSection(entry, `segnavia-front-${frontNumber}`, build.build);
|
|
204
166
|
build.sections.push(section);
|
|
167
|
+
decorateSection(sectionEntry, build);
|
|
205
168
|
build.issues.push(...reservedIdIssues(section), ...columnIssues(section));
|
|
206
|
-
if (entry.contents && entry.title !== void 0) build.headings.push({
|
|
169
|
+
if ("contents" in entry && entry.contents && entry.title !== void 0) build.headings.push(...withSectionDepth([{
|
|
207
170
|
id: section.info.id,
|
|
208
171
|
level: 1,
|
|
209
172
|
text: entry.title,
|
|
210
173
|
sectionId: section.info.id
|
|
211
|
-
});
|
|
212
|
-
transformNotes(section.node, book, 0, build.notes, build.build === "galley");
|
|
174
|
+
}], sectionEntry.depth));
|
|
175
|
+
transformNotes(section.node, book, noteContext(sectionEntry, section.info.id, 0, entry.title, build), build.notes, build.build === "galley");
|
|
213
176
|
}
|
|
214
177
|
return contents;
|
|
215
178
|
}
|
|
216
|
-
function addChapters(book, build) {
|
|
217
|
-
|
|
218
|
-
|
|
179
|
+
function addChapters(book, build, division = "body") {
|
|
180
|
+
for (const sectionEntry of bookSections(book)) {
|
|
181
|
+
if (sectionEntry.division !== division) continue;
|
|
182
|
+
const { entry } = sectionEntry;
|
|
183
|
+
if (isPart(entry)) {
|
|
184
|
+
addPart(sectionEntry, build);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (isBookContents(entry)) continue;
|
|
219
188
|
if (isDesignedSection(entry)) {
|
|
220
189
|
build.designedNumber += 1;
|
|
221
|
-
|
|
222
|
-
appendDesigned(entry, `segnavia-designed-${build.designedNumber}`, "designed", book, build.build, build.notes, build.sections, build.headings, build.issues, owner);
|
|
223
|
-
pushChapterEndnotes(book, build, `segnavia-endnotes-designed-${build.designedNumber}`, owner);
|
|
190
|
+
appendDesigned(entry, `segnavia-designed-${build.designedNumber}`, "designed", book, build, sectionEntry);
|
|
224
191
|
continue;
|
|
225
192
|
}
|
|
226
193
|
if (isTemplatedSection(entry)) {
|
|
227
194
|
build.templatedNumber += 1;
|
|
228
|
-
|
|
229
|
-
appendTemplated(entry, `segnavia-templated-${build.templatedNumber}`, "templated", book, build.build, build.notes, build.templates, build.sections, build.headings, build.issues, owner);
|
|
230
|
-
pushChapterEndnotes(book, build, `segnavia-endnotes-templated-${build.templatedNumber}`, owner);
|
|
195
|
+
appendTemplated(entry, `segnavia-templated-${build.templatedNumber}`, "templated", book, build, sectionEntry);
|
|
231
196
|
continue;
|
|
232
197
|
}
|
|
233
|
-
|
|
234
|
-
|
|
198
|
+
if (!("html" in entry)) continue;
|
|
199
|
+
build.chapterNumber += 1;
|
|
200
|
+
const section = chapterSection(entry, build.chapterNumber, sectionEntry.division, build.build);
|
|
235
201
|
build.sections.push(section);
|
|
202
|
+
decorateSection(sectionEntry, build);
|
|
236
203
|
build.issues.push(...reservedIdIssues(section), ...columnIssues(section));
|
|
237
|
-
build.headings.push({
|
|
204
|
+
if (entry.title !== void 0) build.headings.push(...withSectionDepth([{
|
|
238
205
|
id: section.info.id,
|
|
239
206
|
level: 1,
|
|
240
207
|
text: entry.title,
|
|
241
208
|
sectionId: section.info.id
|
|
242
|
-
},
|
|
243
|
-
|
|
244
|
-
|
|
209
|
+
}], sectionEntry.depth));
|
|
210
|
+
build.headings.push(...withSectionDepth(chapterHeadings(section, build.chapterNumber, book.contents.depth), sectionEntry.depth));
|
|
211
|
+
transformNotes(section.node, book, noteContext(sectionEntry, section.info.id, build.chapterNumber, entry.title, build), build.notes, build.build === "galley");
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function collectedSectionId(notes) {
|
|
215
|
+
const prefix = notes.series.sectionIdPrefix;
|
|
216
|
+
if (notes.targetId === "book") return prefix;
|
|
217
|
+
if (notes.series.placement === "part") return `${prefix}-${notes.placementKey.replace(/^segnavia-/, "")}`;
|
|
218
|
+
if (notes.series.placement === "chapter" && notes.targetId.startsWith("segnavia-chapter-")) return `${prefix}-${notes.targetId.slice(17)}`;
|
|
219
|
+
return `${prefix}-${notes.targetId.replace(/^segnavia-/, "")}`;
|
|
220
|
+
}
|
|
221
|
+
function insertCollectedSections(book, build) {
|
|
222
|
+
const order = new Map(book.notes.map((series, index) => [series.name, index]));
|
|
223
|
+
const collected = [...build.notes.collected.values()].sort((left, right) => (order.get(left.series.name) ?? 0) - (order.get(right.series.name) ?? 0));
|
|
224
|
+
for (const notes of [...collected].reverse()) {
|
|
225
|
+
if (notes.targetId === "book") continue;
|
|
226
|
+
const targetId = notes.series.placement === "part" ? build.partTargets.get(notes.placementKey) ?? notes.targetId : notes.targetId;
|
|
227
|
+
notes.targetId = targetId;
|
|
228
|
+
const index = build.sections.findIndex(({ info }) => info.id === targetId);
|
|
229
|
+
if (index < 0) {
|
|
230
|
+
build.issues.push(`note series ${notes.series.name} could not find placement target ${targetId}; keep the owning section in the book`);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const owner = build.sections[index];
|
|
234
|
+
const parts = owner?.node.attribs["data-segnavia-parts"];
|
|
235
|
+
build.sections.splice(index + 1, 0, notesSection(book, notes, build.build, collectedSectionId(notes), owner?.info.runningHead, parts ? JSON.parse(parts) : void 0, "any"));
|
|
236
|
+
}
|
|
237
|
+
for (const notes of collected) {
|
|
238
|
+
if (notes.targetId !== "book") continue;
|
|
239
|
+
const index = build.sections.findIndex(({ info }) => info.division === "end");
|
|
240
|
+
build.sections.splice(index < 0 ? build.sections.length : index, 0, notesSection(book, notes, build.build, collectedSectionId(notes), notes.series.title));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function writePageReferenceNumbers(book, roots, options) {
|
|
244
|
+
for (const node of pageReferenceNodes(roots)) {
|
|
245
|
+
const id = pageReferenceTarget(node.attribs);
|
|
246
|
+
if (!id) continue;
|
|
247
|
+
const page = options.pageNumbers?.[id] ?? "";
|
|
248
|
+
node.children = [text(page)];
|
|
249
|
+
node.attribs.dir = resolveDirection(book.dir, book.lang, page);
|
|
250
|
+
if (options.crossLinks) node.attribs.href = `segnavia:target/${id}`;
|
|
251
|
+
linkChildren(node);
|
|
245
252
|
}
|
|
246
253
|
}
|
|
247
254
|
function bookDocument(book, options = {}) {
|
|
@@ -255,24 +262,40 @@ function bookDocument(book, options = {}) {
|
|
|
255
262
|
templates: new Map(book.templates.map((template) => [template.name, template])),
|
|
256
263
|
designedNumber: 0,
|
|
257
264
|
templatedNumber: 0,
|
|
258
|
-
|
|
265
|
+
chapterNumber: 0,
|
|
266
|
+
partTargets: /* @__PURE__ */ new Map(),
|
|
267
|
+
pendingPartHeadings: []
|
|
259
268
|
};
|
|
260
269
|
const contents = addFrontMatter(book, build);
|
|
261
270
|
addChapters(book, build);
|
|
271
|
+
addChapters(book, build, "end");
|
|
262
272
|
const { notes, sections, headings, issues } = build;
|
|
263
|
-
|
|
264
|
-
const leftover = notes.chapterEndnotes.get(0) ?? [];
|
|
265
|
-
if (leftover.length > 0) sections.push(endnotesSection(book, leftover, requestedBuild, "segnavia-endnotes", book.endnotes.title));
|
|
266
|
-
} else if (notes.endnotes) sections.push(endnotesSection(book, endnotesInOrder(notes), requestedBuild, "segnavia-endnotes", book.endnotes.title));
|
|
273
|
+
insertCollectedSections(book, build);
|
|
267
274
|
if (contents) addContents(book, contents, headings, options);
|
|
275
|
+
const noteGalleys = notesGalleys(book.notes, notes.pageParagraphs);
|
|
276
|
+
const scopes = [...sections.map((section) => ({
|
|
277
|
+
owner: `${section.info.title ?? section.info.id} (${section.info.id})`,
|
|
278
|
+
roots: [section.node]
|
|
279
|
+
})), ...noteGalleys.map((galley) => ({
|
|
280
|
+
owner: `page-placed note series ${galley.attribs["data-segnavia-note-series"] ?? ""}`,
|
|
281
|
+
roots: [galley]
|
|
282
|
+
}))];
|
|
283
|
+
const referenceIssues = pageReferenceIssues(sections, scopes);
|
|
284
|
+
const referenceRoots = scopes.flatMap((scope) => scope.roots);
|
|
285
|
+
const referenceTargets = pageReferenceTargets(referenceRoots);
|
|
286
|
+
writePageReferenceNumbers(book, referenceRoots, options);
|
|
268
287
|
if (requestedBuild !== "print") markBlockPositions(sections);
|
|
269
|
-
const visible = sections.filter((section) => options.part === void 0 || bookPrintPart(section.info.
|
|
288
|
+
const visible = sections.filter((section) => options.part === void 0 || bookPrintPart(section.info.division) === options.part);
|
|
270
289
|
const document = parse("");
|
|
271
|
-
document.children = requestedBuild === "galley" ? [
|
|
272
|
-
|
|
273
|
-
...
|
|
274
|
-
|
|
275
|
-
|
|
290
|
+
document.children = requestedBuild === "galley" ? [
|
|
291
|
+
element("style", {}, [text(bookDocumentCss(book, requestedBuild))]),
|
|
292
|
+
...options.targets ? [targetLinks(visible)] : [],
|
|
293
|
+
element("div", { "data-segnavia-galleys": "" }, [
|
|
294
|
+
...templateHolders(book.templates),
|
|
295
|
+
...visible.map((section) => section.node),
|
|
296
|
+
...noteGalleys
|
|
297
|
+
])
|
|
298
|
+
] : [
|
|
276
299
|
element("style", {}, [text(bookDocumentCss(book, requestedBuild))]),
|
|
277
300
|
...options.targets ? [targetLinks(visible)] : [],
|
|
278
301
|
...withBlankPages(visible, options.blankBefore)
|
|
@@ -282,16 +305,16 @@ function bookDocument(book, options = {}) {
|
|
|
282
305
|
html: serialize(document),
|
|
283
306
|
sections: sections.map((section) => section.info),
|
|
284
307
|
headings,
|
|
308
|
+
pageReferenceTargets: referenceTargets,
|
|
285
309
|
issues: [
|
|
286
310
|
...buildIssue ? [buildIssue] : [],
|
|
287
|
-
...noteMarkerIssues(book,
|
|
288
|
-
|
|
289
|
-
endnotes: notes.endnotes
|
|
290
|
-
}),
|
|
311
|
+
...noteMarkerIssues(book.notes, notes.used),
|
|
312
|
+
...notes.issues,
|
|
291
313
|
...markPlacementIssues(book.parentPages),
|
|
292
|
-
...
|
|
314
|
+
...templateRegionIssues(book.templates),
|
|
293
315
|
...issues,
|
|
294
|
-
...duplicateIdIssues(sections, headings)
|
|
316
|
+
...duplicateIdIssues(sections, headings),
|
|
317
|
+
...referenceIssues
|
|
295
318
|
]
|
|
296
319
|
};
|
|
297
320
|
}
|
package/dist/book/book-flow.js
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import { isTag } from "domhandler";
|
|
2
|
-
import { PX_PER_MM, sheetMm } from "@segnavia/format";
|
|
2
|
+
import { MAX_PART_DEPTH, PX_PER_MM, bookSections, isPart, sheetMm } from "@segnavia/format";
|
|
3
3
|
//#region src/book/book-flow.ts
|
|
4
4
|
const BOOK_FLOW_COLUMN_MIN_PX = 240;
|
|
5
5
|
const BOOK_COLUMN_MIN = 2;
|
|
6
6
|
const BOOK_COLUMN_MAX = 4;
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
7
|
+
const CONTENTS_INDENTS = Array.from({ length: 5 }, (_, index) => index + 2).map((level) => `:where(.segnavia-contents-entry[data-level="${level}"]){padding-inline-start:${level - 1}em}`).join("");
|
|
8
|
+
const DEEP_CONTENTS_INDENTS = Array.from({ length: MAX_PART_DEPTH }, (_, index) => index + 7).map((level) => `:where(.segnavia-contents-entry[data-level="${level}"]){padding-inline-start:${level - 1}em}`).join("");
|
|
9
|
+
const CONTENTS_PREFIX = `:where(.segnavia-contents-list){list-style:none;padding:0;margin:0}:where(.segnavia-contents-entry){display:flex}:where(.segnavia-contents-page){margin-inline-start:auto}:where([data-ref="page"]){unicode-bidi:isolate}${CONTENTS_INDENTS}`;
|
|
10
|
+
const CONTENTS_SUFFIX = ":where(.segnavia-targets){position:absolute;inline-size:1px;block-size:1px;overflow:hidden;clip-path:inset(50%)}";
|
|
11
|
+
const GALLEY_SUFFIX = ":where([data-segnavia-galleys]){position:relative}:where([data-segnavia-flow]){orphans:inherit;widows:inherit}:where([data-segnavia-kind=\"fixed\"],[data-segnavia-kind=\"templated\"],[data-segnavia-template]){display:none}:where([data-segnavia-notes-galley]){border-block-start:1px solid;padding-block-start:8px}:where([data-segnavia-note]){break-inside:avoid}:where([data-segnavia-galleys] img,[data-segnavia-window] img,[data-segnavia-page-note] img){display:block;max-block-size:var(--segnavia-text-height);max-inline-size:100%;block-size:auto;inline-size:auto}";
|
|
12
|
+
function hasParts(book) {
|
|
13
|
+
return bookSections(book).some(({ entry }) => isPart(entry));
|
|
14
|
+
}
|
|
15
|
+
function contentsCss(book) {
|
|
16
|
+
return `${CONTENTS_PREFIX}${hasParts(book) ? DEEP_CONTENTS_INDENTS : ""}${CONTENTS_SUFFIX}`;
|
|
17
|
+
}
|
|
18
|
+
function printCss(book) {
|
|
19
|
+
const breaks = hasParts(book) ? ".segnavia-part,.segnavia-chapter,.segnavia-endnotes,.segnavia-blank" : ".segnavia-chapter,.segnavia-endnotes,.segnavia-blank";
|
|
20
|
+
return `${contentsCss(book)}@media print{:where(.segnavia-front,.segnavia-contents)~:where(.segnavia-front,.segnavia-contents),:where(${breaks}){break-before:page}:where(.segnavia-blank){break-after:page}}`;
|
|
21
|
+
}
|
|
10
22
|
function bookFlowPageGeometry(book) {
|
|
11
23
|
if (!book.page) return void 0;
|
|
12
24
|
const paper = sheetMm(book.page);
|
|
@@ -28,7 +40,7 @@ function columnFallbacks(gap) {
|
|
|
28
40
|
function flowCss(book) {
|
|
29
41
|
const gap = (book.page?.columnGap ?? 0) * PX_PER_MM;
|
|
30
42
|
const columns = Array.from({ length: 3 }, (_, index) => index + 2).map((count) => `:where([data-columns="${count}"]){column-count:${count}}`).join("");
|
|
31
|
-
return `${
|
|
43
|
+
return `${contentsCss(book)}:where([data-segnavia-flow]){container-type:inline-size}:where([data-columns]){column-gap:${gap}px}${columns}:where([data-segnavia-flow-page]){inline-size:100%;container-type:inline-size;margin-inline:0}:where([data-segnavia-flow-page-size]){position:relative;inline-size:100cqi;block-size:calc(100cqi * var(--segnavia-paper-ratio));overflow:hidden}:where([data-segnavia-flow-paper]){position:absolute;inset-block-start:0;inset-inline-start:0;inline-size:var(--segnavia-paper-inline);block-size:var(--segnavia-paper-block);overflow:hidden;zoom:var(--segnavia-flow-scale,calc(100cqi / var(--segnavia-paper-inline)))}:where([data-segnavia-flow-text]){position:absolute;inset-block-start:var(--segnavia-text-block-start);inset-inline-start:var(--segnavia-text-inline-start);inline-size:var(--segnavia-text-inline);block-size:var(--segnavia-text-block);container-type:inline-size}${columnFallbacks(gap)}`;
|
|
32
44
|
}
|
|
33
45
|
function resolveBookBuild(book, requested) {
|
|
34
46
|
const build = requested ?? (book.mode === "paginated" ? "galley" : "print");
|
|
@@ -39,8 +51,8 @@ function resolveBookBuild(book, requested) {
|
|
|
39
51
|
};
|
|
40
52
|
}
|
|
41
53
|
function bookDocumentCss(book, build) {
|
|
42
|
-
if (build === "galley") return
|
|
43
|
-
return build === "flow" ? flowCss(book) :
|
|
54
|
+
if (build === "galley") return `${contentsCss(book)}${GALLEY_SUFFIX}`;
|
|
55
|
+
return build === "flow" ? flowCss(book) : printCss(book);
|
|
44
56
|
}
|
|
45
57
|
function flowPageStyle(geometry, recto) {
|
|
46
58
|
const marginInlineStart = recto ? geometry.marginInlineStartRecto : geometry.marginInlineStartVerso;
|
|
@@ -61,12 +73,16 @@ function parseBookPositionKey(value) {
|
|
|
61
73
|
function isFootnoteBlock(node) {
|
|
62
74
|
return (node.attribs.class ?? "").split(/\s+/).includes("segnavia-footnotes");
|
|
63
75
|
}
|
|
76
|
+
function isNotesScope(node) {
|
|
77
|
+
return (node.attribs.class ?? "").split(/\s+/).includes("segnavia-notes-scope");
|
|
78
|
+
}
|
|
64
79
|
function markBlockPositions(sections) {
|
|
65
80
|
for (const section of sections) {
|
|
66
81
|
let position = 0;
|
|
67
82
|
for (const child of section.node.children) {
|
|
68
83
|
if (!isTag(child)) continue;
|
|
69
84
|
if (child.name === "style" || child.name === "script") continue;
|
|
85
|
+
if (isNotesScope(child)) continue;
|
|
70
86
|
if (isFootnoteBlock(child)) {
|
|
71
87
|
child.attribs["data-segnavia-position"] = bookBlockPosition(section.info.id, position, "footnotes");
|
|
72
88
|
continue;
|