@segnavia/html 0.37.0 → 0.38.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 +30 -0
- package/dist/book/book-document.d.ts +2 -0
- package/dist/book/book-document.js +150 -154
- 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 +24 -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 +101 -47
- package/dist/book/paginate-book.d.ts +2 -1
- package/dist/book/paginate-book.js +34 -26
- package/dist/book/paginate-sequence.d.ts +1 -1
- package/dist/book/paginate-sequence.js +60 -28
- package/dist/book/paginate-types.d.ts +10 -5
- package/dist/book/region-fit.d.ts +16 -0
- package/dist/book/region-fit.js +111 -0
- package/dist/book/types.d.ts +4 -2
- package/dist/book/types.js +2 -2
- package/dist/index.d.ts +5 -4
- package/dist/index.js +4 -3
- package/dist/motifs.d.ts +2 -0
- package/dist/motifs.js +5 -2
- package/dist/printed-pages.d.ts +2 -7
- package/dist/printed-pages.js +1 -1
- package/dist/sanitize.d.ts +1 -0
- package/dist/sanitize.js +78 -43
- 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)]), element("span", {
|
|
18
|
+
class: "segnavia-contents-page",
|
|
19
|
+
"data-target": heading.id,
|
|
20
|
+
dir: resolveDirection(book.dir, book.lang, page)
|
|
21
|
+
}, [text(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,30 @@
|
|
|
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(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
|
+
"aria-hidden": "true"
|
|
19
|
+
}, notes);
|
|
20
|
+
}
|
|
21
|
+
function withBlankPages(sections, blankBefore) {
|
|
22
|
+
const counts = /* @__PURE__ */ new Map();
|
|
23
|
+
for (const id of blankBefore ?? []) counts.set(id, (counts.get(id) ?? 0) + 1);
|
|
24
|
+
return sections.flatMap((section) => [...Array.from({ length: counts.get(section.info.id) ?? 0 }, () => element("div", {
|
|
25
|
+
class: "segnavia-blank",
|
|
26
|
+
"aria-hidden": "true"
|
|
27
|
+
})), section.node]);
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { notesGalley, 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,15 @@
|
|
|
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 { notesGalley, 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 { duplicateIdIssues, reservedIdIssues } from "./book-issues.js";
|
|
8
|
+
import { addPart, attachPendingPartHeadings, partNames } from "./book-part-build.js";
|
|
9
|
+
import { noteState, transformNotes } from "./notes.js";
|
|
6
10
|
import { bookPrintPart } from "./types.js";
|
|
7
|
-
import {
|
|
8
|
-
import { isBookContents, isDesignedSection, isTemplatedSection, noteMarkerIssues, resolveDirection } from "@segnavia/format";
|
|
11
|
+
import { bookSections, isBookContents, isDesignedSection, isPart, isTemplatedSection, noteMarkerIssues, resolveDirection } from "@segnavia/format";
|
|
9
12
|
//#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
13
|
function contentsSection(entry, build) {
|
|
28
14
|
const id = "segnavia-contents";
|
|
29
15
|
const title = entry.title;
|
|
@@ -35,13 +21,14 @@ function contentsSection(entry, build) {
|
|
|
35
21
|
info: {
|
|
36
22
|
id,
|
|
37
23
|
kind: "contents",
|
|
24
|
+
division: "front",
|
|
38
25
|
title,
|
|
39
26
|
runningHead: title
|
|
40
27
|
},
|
|
41
28
|
node: sectionNode("contents", id, children, build, title, flowAttribs(entry.opens, entry.parents))
|
|
42
29
|
};
|
|
43
30
|
}
|
|
44
|
-
function chapterSection(chapter, index, build) {
|
|
31
|
+
function chapterSection(chapter, index, division, build) {
|
|
45
32
|
const id = `segnavia-chapter-${index}`;
|
|
46
33
|
const document = parse(chapter.html);
|
|
47
34
|
const runningHead = chapter.runningHead ?? chapter.title;
|
|
@@ -49,80 +36,40 @@ function chapterSection(chapter, index, build) {
|
|
|
49
36
|
info: {
|
|
50
37
|
id,
|
|
51
38
|
kind: "chapter",
|
|
39
|
+
division,
|
|
52
40
|
title: chapter.title,
|
|
53
41
|
runningHead
|
|
54
42
|
},
|
|
55
43
|
node: sectionNode("chapter", id, document.children, build, runningHead, flowAttribs(chapter.opens, chapter.parents))
|
|
56
44
|
};
|
|
57
45
|
}
|
|
58
|
-
function
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
46
|
+
function appendDesigned(entry, id, kind, book, build, sectionEntry) {
|
|
47
|
+
const held = designedSection(entry, id, kind, sectionEntry.division, build.build, book.contents.depth, build.build === "flow" ? bookFlowPageGeometry(book) : void 0);
|
|
48
|
+
build.sections.push(held.built);
|
|
49
|
+
decorateSection(sectionEntry, build);
|
|
50
|
+
build.issues.push(...reservedIdIssues(held.built), ...columnIssues(held.built));
|
|
51
|
+
build.headings.push(...withSectionDepth(held.headings, sectionEntry.depth));
|
|
52
|
+
const context = noteContext(sectionEntry, held.built.info.id, 0, entry.title, build);
|
|
53
|
+
transformNotes(held.built.node, book, context, build.notes, build.build === "galley");
|
|
63
54
|
}
|
|
64
|
-
function
|
|
65
|
-
const held =
|
|
66
|
-
|
|
55
|
+
function appendTemplated(entry, id, kind, book, build, sectionEntry) {
|
|
56
|
+
const held = templatedSection(entry, id, kind, sectionEntry.division, build.build, book.contents.depth, build.templates, build.build === "flow" ? bookFlowPageGeometry(book) : void 0);
|
|
57
|
+
build.sections.push(held.built);
|
|
58
|
+
decorateSection(sectionEntry, build);
|
|
59
|
+
build.issues.push(...reservedIdIssues(held.built), ...columnIssues(held.built), ...templatedSectionIssues(entry, id, build.templates));
|
|
60
|
+
build.headings.push(...withSectionDepth(held.headings, sectionEntry.depth));
|
|
61
|
+
const context = noteContext(sectionEntry, held.built.info.id, 0, entry.title, build);
|
|
62
|
+
transformNotes(held.built.node, book, context, build.notes, build.build === "galley");
|
|
67
63
|
}
|
|
68
|
-
function
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
function withSectionDepth(headings, depth) {
|
|
65
|
+
return depth > 0 ? headings.map((heading) => ({
|
|
66
|
+
...heading,
|
|
67
|
+
depth
|
|
68
|
+
})) : headings;
|
|
71
69
|
}
|
|
72
70
|
function chapterHeadings(section, chapter, depth) {
|
|
73
71
|
return collectHeadings(section.node.children, section.info.id, depth, 2, (n) => `segnavia-h-${chapter}-${n}`, { n: 0 });
|
|
74
72
|
}
|
|
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
73
|
function columnsValueOk(value) {
|
|
127
74
|
if (!/^[0-9]+$/.test(value)) return false;
|
|
128
75
|
const count = Number(value);
|
|
@@ -136,112 +83,159 @@ function columnIssues(section) {
|
|
|
136
83
|
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
84
|
});
|
|
138
85
|
}
|
|
139
|
-
function
|
|
140
|
-
const titleValue = book.endnotes.title;
|
|
86
|
+
function notesHeading(book, notes) {
|
|
141
87
|
return element("h1", {
|
|
142
|
-
class:
|
|
143
|
-
dir:
|
|
144
|
-
}, [text(
|
|
88
|
+
class: notes.series.titleClass,
|
|
89
|
+
dir: resolveDirection(book.dir, book.lang, notes.series.title)
|
|
90
|
+
}, [text(notes.series.title)]);
|
|
145
91
|
}
|
|
146
|
-
function
|
|
92
|
+
function notesSection(book, notes, build, id, runningHead, parts, opens) {
|
|
93
|
+
const scope = notes.scopeTitle !== void 0 && (notes.series.placement === "chapter" || notes.series.placement === "part") ? [element("p", {
|
|
94
|
+
class: "segnavia-notes-scope",
|
|
95
|
+
dir: resolveDirection(book.dir, book.lang, notes.scopeTitle)
|
|
96
|
+
}, [text(notes.scopeTitle)])] : [];
|
|
147
97
|
return {
|
|
148
98
|
info: {
|
|
149
99
|
id,
|
|
150
100
|
kind: "endnotes",
|
|
151
|
-
|
|
101
|
+
division: notes.targetId === "book" ? "body" : notes.division,
|
|
102
|
+
title: notes.series.title,
|
|
152
103
|
runningHead
|
|
153
104
|
},
|
|
154
|
-
node: sectionNode("endnotes", id, [
|
|
105
|
+
node: sectionNode("endnotes", id, [
|
|
106
|
+
notesHeading(book, notes),
|
|
107
|
+
...scope,
|
|
108
|
+
...notes.paragraphs
|
|
109
|
+
], build, runningHead, {
|
|
110
|
+
...opens ? flowAttribs(opens) : {},
|
|
111
|
+
...parts ? { "data-segnavia-parts": JSON.stringify(parts) } : {}
|
|
112
|
+
})
|
|
155
113
|
};
|
|
156
114
|
}
|
|
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]);
|
|
115
|
+
function decorateSection(sectionEntry, build) {
|
|
116
|
+
const section = build.sections.at(-1);
|
|
117
|
+
if (!section) return;
|
|
118
|
+
if (sectionEntry.ancestry.length > 0) section.node.attribs["data-segnavia-parts"] = JSON.stringify(partNames(sectionEntry.ancestry));
|
|
119
|
+
attachPendingPartHeadings(build, sectionEntry, section.info.id);
|
|
171
120
|
}
|
|
172
|
-
function
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
121
|
+
function noteContext(sectionEntry, targetId, chapter, title, build) {
|
|
122
|
+
const ancestry = sectionEntry.ancestry.at(-1);
|
|
123
|
+
for (const part of sectionEntry.ancestry) build.partTargets.set(part.id, targetId);
|
|
124
|
+
return {
|
|
125
|
+
chapter,
|
|
126
|
+
title,
|
|
127
|
+
division: sectionEntry.division,
|
|
128
|
+
targetId,
|
|
129
|
+
...chapter === 0 && sectionEntry.division !== "front" ? { placementKey: targetId.replace(/^segnavia-/, "") } : {},
|
|
130
|
+
...ancestry ? { part: {
|
|
131
|
+
id: ancestry.id,
|
|
132
|
+
title: ancestry.title
|
|
133
|
+
} } : {}
|
|
134
|
+
};
|
|
182
135
|
}
|
|
183
136
|
function addFrontMatter(book, build) {
|
|
184
137
|
let contents;
|
|
185
138
|
let frontNumber = 0;
|
|
186
|
-
for (const
|
|
139
|
+
for (const sectionEntry of bookSections(book)) {
|
|
140
|
+
if (sectionEntry.division !== "front") continue;
|
|
141
|
+
const { entry } = sectionEntry;
|
|
142
|
+
if (isPart(entry)) {
|
|
143
|
+
addPart(sectionEntry, build);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
187
146
|
if (isBookContents(entry)) {
|
|
188
147
|
contents = contentsSection(entry, build.build);
|
|
189
148
|
build.sections.push(contents);
|
|
149
|
+
decorateSection(sectionEntry, build);
|
|
190
150
|
continue;
|
|
191
151
|
}
|
|
192
152
|
if (isDesignedSection(entry)) {
|
|
193
153
|
build.designedNumber += 1;
|
|
194
|
-
appendDesigned(entry, `segnavia-designed-${build.designedNumber}`, "front", book, build
|
|
154
|
+
appendDesigned(entry, `segnavia-designed-${build.designedNumber}`, "front", book, build, sectionEntry);
|
|
195
155
|
continue;
|
|
196
156
|
}
|
|
197
157
|
if (isTemplatedSection(entry)) {
|
|
198
158
|
build.templatedNumber += 1;
|
|
199
|
-
appendTemplated(entry, `segnavia-templated-${build.templatedNumber}`, "front", book, build
|
|
159
|
+
appendTemplated(entry, `segnavia-templated-${build.templatedNumber}`, "front", book, build, sectionEntry);
|
|
200
160
|
continue;
|
|
201
161
|
}
|
|
202
162
|
frontNumber += 1;
|
|
203
163
|
const section = authoredSection(entry, `segnavia-front-${frontNumber}`, build.build);
|
|
204
164
|
build.sections.push(section);
|
|
165
|
+
decorateSection(sectionEntry, build);
|
|
205
166
|
build.issues.push(...reservedIdIssues(section), ...columnIssues(section));
|
|
206
|
-
if (entry.contents && entry.title !== void 0) build.headings.push({
|
|
167
|
+
if ("contents" in entry && entry.contents && entry.title !== void 0) build.headings.push(...withSectionDepth([{
|
|
207
168
|
id: section.info.id,
|
|
208
169
|
level: 1,
|
|
209
170
|
text: entry.title,
|
|
210
171
|
sectionId: section.info.id
|
|
211
|
-
});
|
|
212
|
-
transformNotes(section.node, book, 0, build.notes, build.build === "galley");
|
|
172
|
+
}], sectionEntry.depth));
|
|
173
|
+
transformNotes(section.node, book, noteContext(sectionEntry, section.info.id, 0, entry.title, build), build.notes, build.build === "galley");
|
|
213
174
|
}
|
|
214
175
|
return contents;
|
|
215
176
|
}
|
|
216
|
-
function addChapters(book, build) {
|
|
217
|
-
|
|
218
|
-
|
|
177
|
+
function addChapters(book, build, division = "body") {
|
|
178
|
+
for (const sectionEntry of bookSections(book)) {
|
|
179
|
+
if (sectionEntry.division !== division) continue;
|
|
180
|
+
const { entry } = sectionEntry;
|
|
181
|
+
if (isPart(entry)) {
|
|
182
|
+
addPart(sectionEntry, build);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (isBookContents(entry)) continue;
|
|
219
186
|
if (isDesignedSection(entry)) {
|
|
220
187
|
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);
|
|
188
|
+
appendDesigned(entry, `segnavia-designed-${build.designedNumber}`, "designed", book, build, sectionEntry);
|
|
224
189
|
continue;
|
|
225
190
|
}
|
|
226
191
|
if (isTemplatedSection(entry)) {
|
|
227
192
|
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);
|
|
193
|
+
appendTemplated(entry, `segnavia-templated-${build.templatedNumber}`, "templated", book, build, sectionEntry);
|
|
231
194
|
continue;
|
|
232
195
|
}
|
|
233
|
-
|
|
234
|
-
|
|
196
|
+
if (!("html" in entry)) continue;
|
|
197
|
+
build.chapterNumber += 1;
|
|
198
|
+
const section = chapterSection(entry, build.chapterNumber, sectionEntry.division, build.build);
|
|
235
199
|
build.sections.push(section);
|
|
200
|
+
decorateSection(sectionEntry, build);
|
|
236
201
|
build.issues.push(...reservedIdIssues(section), ...columnIssues(section));
|
|
237
|
-
build.headings.push({
|
|
202
|
+
if (entry.title !== void 0) build.headings.push(...withSectionDepth([{
|
|
238
203
|
id: section.info.id,
|
|
239
204
|
level: 1,
|
|
240
205
|
text: entry.title,
|
|
241
206
|
sectionId: section.info.id
|
|
242
|
-
},
|
|
243
|
-
|
|
244
|
-
|
|
207
|
+
}], sectionEntry.depth));
|
|
208
|
+
build.headings.push(...withSectionDepth(chapterHeadings(section, build.chapterNumber, book.contents.depth), sectionEntry.depth));
|
|
209
|
+
transformNotes(section.node, book, noteContext(sectionEntry, section.info.id, build.chapterNumber, entry.title, build), build.notes, build.build === "galley");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function collectedSectionId(notes) {
|
|
213
|
+
const prefix = notes.series.sectionIdPrefix;
|
|
214
|
+
if (notes.targetId === "book") return prefix;
|
|
215
|
+
if (notes.series.placement === "part") return `${prefix}-${notes.placementKey.replace(/^segnavia-/, "")}`;
|
|
216
|
+
if (notes.series.placement === "chapter" && notes.targetId.startsWith("segnavia-chapter-")) return `${prefix}-${notes.targetId.slice(17)}`;
|
|
217
|
+
return `${prefix}-${notes.targetId.replace(/^segnavia-/, "")}`;
|
|
218
|
+
}
|
|
219
|
+
function insertCollectedSections(book, build) {
|
|
220
|
+
const order = new Map(book.notes.map((series, index) => [series.name, index]));
|
|
221
|
+
const collected = [...build.notes.collected.values()].sort((left, right) => (order.get(left.series.name) ?? 0) - (order.get(right.series.name) ?? 0));
|
|
222
|
+
for (const notes of [...collected].reverse()) {
|
|
223
|
+
if (notes.targetId === "book") continue;
|
|
224
|
+
const targetId = notes.series.placement === "part" ? build.partTargets.get(notes.placementKey) ?? notes.targetId : notes.targetId;
|
|
225
|
+
notes.targetId = targetId;
|
|
226
|
+
const index = build.sections.findIndex(({ info }) => info.id === targetId);
|
|
227
|
+
if (index < 0) {
|
|
228
|
+
build.issues.push(`note series ${notes.series.name} could not find placement target ${targetId}; keep the owning section in the book`);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
const owner = build.sections[index];
|
|
232
|
+
const parts = owner?.node.attribs["data-segnavia-parts"];
|
|
233
|
+
build.sections.splice(index + 1, 0, notesSection(book, notes, build.build, collectedSectionId(notes), owner?.info.runningHead, parts ? JSON.parse(parts) : void 0, "any"));
|
|
234
|
+
}
|
|
235
|
+
for (const notes of collected) {
|
|
236
|
+
if (notes.targetId !== "book") continue;
|
|
237
|
+
const index = build.sections.findIndex(({ info }) => info.division === "end");
|
|
238
|
+
build.sections.splice(index < 0 ? build.sections.length : index, 0, notesSection(book, notes, build.build, collectedSectionId(notes), notes.series.title));
|
|
245
239
|
}
|
|
246
240
|
}
|
|
247
241
|
function bookDocument(book, options = {}) {
|
|
@@ -255,24 +249,28 @@ function bookDocument(book, options = {}) {
|
|
|
255
249
|
templates: new Map(book.templates.map((template) => [template.name, template])),
|
|
256
250
|
designedNumber: 0,
|
|
257
251
|
templatedNumber: 0,
|
|
258
|
-
|
|
252
|
+
chapterNumber: 0,
|
|
253
|
+
partTargets: /* @__PURE__ */ new Map(),
|
|
254
|
+
pendingPartHeadings: []
|
|
259
255
|
};
|
|
260
256
|
const contents = addFrontMatter(book, build);
|
|
261
257
|
addChapters(book, build);
|
|
258
|
+
addChapters(book, build, "end");
|
|
262
259
|
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));
|
|
260
|
+
insertCollectedSections(book, build);
|
|
267
261
|
if (contents) addContents(book, contents, headings, options);
|
|
268
262
|
if (requestedBuild !== "print") markBlockPositions(sections);
|
|
269
|
-
const visible = sections.filter((section) => options.part === void 0 || bookPrintPart(section.info.
|
|
263
|
+
const visible = sections.filter((section) => options.part === void 0 || bookPrintPart(section.info.division) === options.part);
|
|
270
264
|
const document = parse("");
|
|
271
|
-
document.children = requestedBuild === "galley" ? [
|
|
272
|
-
|
|
273
|
-
...
|
|
274
|
-
|
|
275
|
-
|
|
265
|
+
document.children = requestedBuild === "galley" ? [
|
|
266
|
+
element("style", {}, [text(bookDocumentCss(book, requestedBuild))]),
|
|
267
|
+
...options.targets ? [targetLinks(visible)] : [],
|
|
268
|
+
element("div", { "data-segnavia-galleys": "" }, [
|
|
269
|
+
...templateHolders(book.templates),
|
|
270
|
+
...visible.map((section) => section.node),
|
|
271
|
+
...notes.pageParagraphs.length > 0 ? [notesGalley(notes.pageParagraphs)] : []
|
|
272
|
+
])
|
|
273
|
+
] : [
|
|
276
274
|
element("style", {}, [text(bookDocumentCss(book, requestedBuild))]),
|
|
277
275
|
...options.targets ? [targetLinks(visible)] : [],
|
|
278
276
|
...withBlankPages(visible, options.blankBefore)
|
|
@@ -284,12 +282,10 @@ function bookDocument(book, options = {}) {
|
|
|
284
282
|
headings,
|
|
285
283
|
issues: [
|
|
286
284
|
...buildIssue ? [buildIssue] : [],
|
|
287
|
-
...noteMarkerIssues(book,
|
|
288
|
-
|
|
289
|
-
endnotes: notes.endnotes
|
|
290
|
-
}),
|
|
285
|
+
...noteMarkerIssues(book.notes, notes.used),
|
|
286
|
+
...notes.issues,
|
|
291
287
|
...markPlacementIssues(book.parentPages),
|
|
292
|
-
...
|
|
288
|
+
...templateRegionIssues(book.templates),
|
|
293
289
|
...issues,
|
|
294
290
|
...duplicateIdIssues(sections, headings)
|
|
295
291
|
]
|
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}${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;
|