@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
package/dist/book/book-held.js
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
import { elements, parse } from "../sanitize.js";
|
|
2
|
-
import { flowPageStyle } from "./book-flow.js";
|
|
3
2
|
import { element, linkChildren, textContent } from "./dom.js";
|
|
3
|
+
import { flowPageStyle } from "./book-flow.js";
|
|
4
|
+
import { namedRegionFitIssues, templateRegionFitIssues } from "./region-fit.js";
|
|
4
5
|
import { BOOK_FURNITURE, MOTIF_ATTRIBUTE, REGION_NAME, motifFieldAttribute } from "@segnavia/format";
|
|
5
6
|
//#region src/book/book-held.ts
|
|
7
|
+
function authoredSection(section, id, build) {
|
|
8
|
+
const document = parse(section.html);
|
|
9
|
+
const runningHead = section.title;
|
|
10
|
+
const node = sectionNode("front", id, document.children, build, runningHead, flowAttribs(section.opens, section.parents));
|
|
11
|
+
return {
|
|
12
|
+
info: {
|
|
13
|
+
id,
|
|
14
|
+
kind: "front",
|
|
15
|
+
division: "front",
|
|
16
|
+
title: section.title,
|
|
17
|
+
runningHead
|
|
18
|
+
},
|
|
19
|
+
node
|
|
20
|
+
};
|
|
21
|
+
}
|
|
6
22
|
function flowAttribs(opens, parents, extra = {}) {
|
|
7
23
|
const attribs = { ...extra };
|
|
8
24
|
if (opens) attribs["data-opens"] = opens;
|
|
@@ -29,6 +45,25 @@ function sectionNode(kind, id, children = [], build, runningHead, extra = {}) {
|
|
|
29
45
|
if (build === "galley" && runningHead !== void 0) attribs["data-running-head"] = runningHead;
|
|
30
46
|
return element("section", attribs, children);
|
|
31
47
|
}
|
|
48
|
+
function partSection(entry, id, division, build, parts) {
|
|
49
|
+
const children = entry.html === void 0 ? [] : parse(entry.html).children;
|
|
50
|
+
const runningHead = entry.runningHead ?? entry.title;
|
|
51
|
+
const extra = flowAttribs(entry.opens, entry.parents, {
|
|
52
|
+
"data-segnavia-kind": "part",
|
|
53
|
+
"data-segnavia-parts": JSON.stringify(parts),
|
|
54
|
+
...division === "front" ? { "data-sequence": "front" } : {}
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
info: {
|
|
58
|
+
id,
|
|
59
|
+
kind: "part",
|
|
60
|
+
division,
|
|
61
|
+
title: entry.title,
|
|
62
|
+
runningHead
|
|
63
|
+
},
|
|
64
|
+
node: sectionNode("part", id, children, build, runningHead, extra)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
32
67
|
function flowPage(page, kind, children, geometry, recto) {
|
|
33
68
|
const paper = element("div", { "data-segnavia-flow-paper": "" }, children);
|
|
34
69
|
return element("figure", {
|
|
@@ -57,7 +92,7 @@ function collectHeadings(children, sectionId, depth, firstLevel, idFor, generate
|
|
|
57
92
|
function heldHeadings(children, sectionId, depth, generated) {
|
|
58
93
|
return collectHeadings(children, sectionId, depth, 1, (n) => `segnavia-h-${sectionId.replace(/^segnavia-/, "")}-${n}`, generated);
|
|
59
94
|
}
|
|
60
|
-
function designedSection(entry, id, kind, build, depth, geometry) {
|
|
95
|
+
function designedSection(entry, id, kind, division, build, depth, geometry) {
|
|
61
96
|
const extra = flowAttribs(entry.opens, entry.parents, {
|
|
62
97
|
"data-segnavia-kind": "fixed",
|
|
63
98
|
...kind === "front" ? { "data-sequence": "front" } : {}
|
|
@@ -85,6 +120,7 @@ function designedSection(entry, id, kind, build, depth, geometry) {
|
|
|
85
120
|
info: {
|
|
86
121
|
id,
|
|
87
122
|
kind,
|
|
123
|
+
division,
|
|
88
124
|
title: entry.title,
|
|
89
125
|
runningHead
|
|
90
126
|
},
|
|
@@ -93,12 +129,25 @@ function designedSection(entry, id, kind, build, depth, geometry) {
|
|
|
93
129
|
headings
|
|
94
130
|
};
|
|
95
131
|
}
|
|
132
|
+
function templateRegionElements(html) {
|
|
133
|
+
return elements(parse(html).children).filter((node) => node.attribs["data-region"]);
|
|
134
|
+
}
|
|
96
135
|
function declaredRegions(html) {
|
|
97
|
-
return new Set(
|
|
136
|
+
return new Set(templateRegionElements(html).flatMap((node) => node.attribs["data-region"] ? [node.attribs["data-region"]] : []));
|
|
98
137
|
}
|
|
99
138
|
const regionNameClear = "use a letter followed by letters, digits, hyphens or underscores to clear this refusal";
|
|
100
|
-
function templateRegionNameIssues(
|
|
101
|
-
return
|
|
139
|
+
function templateRegionNameIssues(template, regions) {
|
|
140
|
+
return [...new Set(regions.flatMap((node) => node.attribs["data-region"] ? [node.attribs["data-region"]] : []))].flatMap((key) => REGION_NAME.test(key) ? [] : [`template "${template}" marks region "${key}" which is not an HTML name token; ${regionNameClear}`]);
|
|
141
|
+
}
|
|
142
|
+
function templateRegionIssues(templates) {
|
|
143
|
+
return templates.flatMap((template) => {
|
|
144
|
+
const regions = templateRegionElements(template.html);
|
|
145
|
+
return [...templateRegionNameIssues(template.name, regions), ...templateRegionFitIssues(template.name, regions)];
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
function templatedPageRegionFitIssues(entry, id) {
|
|
149
|
+
const label = entry.title ?? id;
|
|
150
|
+
return entry.pages.flatMap((page, index) => Object.values(page.regions).flatMap((html) => namedRegionFitIssues(`section ${label} page ${index + 1}`, templateRegionElements(html))));
|
|
102
151
|
}
|
|
103
152
|
const markFurniture = BOOK_FURNITURE.find((item) => item.key === "segnavia:mark");
|
|
104
153
|
const markFields = new Set(markFurniture.fields.map((field) => motifFieldAttribute(field.key)));
|
|
@@ -137,7 +186,10 @@ function undeclaredRegionIssues(entry, id, templates) {
|
|
|
137
186
|
return Object.keys(page.regions).flatMap((region) => declared.has(region) ? [] : [`section ${label} page ${index + 1} fills region "${region}" which template "${name}" does not declare; add that region to the template or remove the fill to clear this refusal`]);
|
|
138
187
|
});
|
|
139
188
|
}
|
|
140
|
-
function
|
|
189
|
+
function templatedSectionIssues(entry, id, templates) {
|
|
190
|
+
return [...undeclaredRegionIssues(entry, id, templates), ...templatedPageRegionFitIssues(entry, id)];
|
|
191
|
+
}
|
|
192
|
+
function templatedSection(entry, id, kind, division, build, depth, templates, geometry) {
|
|
141
193
|
const extra = flowAttribs(entry.opens, entry.parents, {
|
|
142
194
|
"data-segnavia-kind": "templated",
|
|
143
195
|
"data-template": entry.template,
|
|
@@ -191,6 +243,7 @@ function templatedSection(entry, id, kind, build, depth, templates, geometry) {
|
|
|
191
243
|
info: {
|
|
192
244
|
id,
|
|
193
245
|
kind,
|
|
246
|
+
division,
|
|
194
247
|
title: entry.title,
|
|
195
248
|
runningHead
|
|
196
249
|
},
|
|
@@ -207,4 +260,4 @@ function templateHolders(templates) {
|
|
|
207
260
|
}, parse(template.html).children));
|
|
208
261
|
}
|
|
209
262
|
//#endregion
|
|
210
|
-
export { collectHeadings, designedSection, flowAttribs, markPlacementIssues, sectionNode, templateHolders, templateRegionNameIssues, templatedSection,
|
|
263
|
+
export { authoredSection, collectHeadings, designedSection, flowAttribs, markPlacementIssues, partSection, sectionNode, templateHolders, templateRegionElements, templateRegionIssues, templateRegionNameIssues, templatedSection, templatedSectionIssues };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { elements } from "../sanitize.js";
|
|
2
|
+
import { pageReferenceNodes, pageReferenceTarget } from "./book-page-reference.js";
|
|
3
|
+
//#region src/book/book-issues.ts
|
|
4
|
+
function sectionIds(section) {
|
|
5
|
+
return [section.info.id, ...elements(section.node.children).flatMap((node) => node.attribs.id ? [node.attribs.id] : [])];
|
|
6
|
+
}
|
|
7
|
+
function sectionName(section) {
|
|
8
|
+
return `${section.title ?? section.id} (${section.id})`;
|
|
9
|
+
}
|
|
10
|
+
function ownersById(sections) {
|
|
11
|
+
const occurrences = /* @__PURE__ */ new Map();
|
|
12
|
+
for (const section of sections) for (const id of sectionIds(section)) occurrences.set(id, [...occurrences.get(id) ?? [], section.info]);
|
|
13
|
+
return occurrences;
|
|
14
|
+
}
|
|
15
|
+
function duplicateIdIssues(sections, headings) {
|
|
16
|
+
const listed = new Set(headings.map((heading) => heading.id));
|
|
17
|
+
return [...ownersById(sections)].flatMap(([id, owners]) => {
|
|
18
|
+
if (!listed.has(id) || owners.length < 2) return [];
|
|
19
|
+
const named = owners.slice(0, 2).map(sectionName);
|
|
20
|
+
return [`destination id ${id} is used in both ${named[0]} and ${named[1]}; listed heading and generated destination ids must be unique`];
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function pageReferenceIssues(sections, scopes) {
|
|
24
|
+
const targets = ownersById(sections);
|
|
25
|
+
return scopes.flatMap((scope) => pageReferenceNodes(scope.roots).flatMap((node) => {
|
|
26
|
+
const id = pageReferenceTarget(node.attribs);
|
|
27
|
+
if (!id) return [];
|
|
28
|
+
const owners = targets.get(id) ?? [];
|
|
29
|
+
if (owners.length === 0) return [`page reference to ${id} in ${scope.owner} has no target; add the id or remove the reference`];
|
|
30
|
+
if (owners.length === 1) return [];
|
|
31
|
+
return [`page reference target ${id} is used in ${owners.length} sections: ${owners.map(sectionName).join(", ")}; make the target id unique to clear this refusal`];
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
function reservedIdIssues(section) {
|
|
35
|
+
return elements(section.node.children).flatMap((node) => {
|
|
36
|
+
const id = node.attribs.id;
|
|
37
|
+
if (!id?.startsWith("segnavia-")) return [];
|
|
38
|
+
return [`authored id ${id} in ${sectionName(section.info)} uses the reserved segnavia- prefix`];
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { duplicateIdIssues, pageReferenceIssues, reservedIdIssues };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Element } from "domhandler";
|
|
2
|
+
//#region src/book/book-page-reference.d.ts
|
|
3
|
+
export declare const PAGE_REFERENCE_ATTRIBUTE = "data-ref";
|
|
4
|
+
export declare const PAGE_REFERENCE_VALUE = "page";
|
|
5
|
+
export declare const PAGE_REFERENCE_SELECTOR = "a[data-ref=\"page\"]";
|
|
6
|
+
export declare function pageReferenceTarget(attribs: Record<string, string>): string | undefined;
|
|
7
|
+
//#endregion
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { elements } from "../sanitize.js";
|
|
2
|
+
//#region src/book/book-page-reference.ts
|
|
3
|
+
const PAGE_REFERENCE_ATTRIBUTE = "data-ref";
|
|
4
|
+
const PAGE_REFERENCE_VALUE = "page";
|
|
5
|
+
const PAGE_REFERENCE_SELECTOR = "a[data-ref=\"page\"]";
|
|
6
|
+
function pageReferenceTarget(attribs) {
|
|
7
|
+
if (attribs["data-ref"] !== "page") return void 0;
|
|
8
|
+
const href = attribs.href;
|
|
9
|
+
if (href?.startsWith("#") && href.length > 1) return href.slice(1);
|
|
10
|
+
return href?.startsWith("segnavia:target/") && href.length > 16 ? href.slice(16) : void 0;
|
|
11
|
+
}
|
|
12
|
+
function pageReferenceNodes(roots) {
|
|
13
|
+
return roots.flatMap((root) => [root, ...elements(root.children)]).filter((node) => node.name === "a" && pageReferenceTarget(node.attribs) !== void 0);
|
|
14
|
+
}
|
|
15
|
+
function pageReferenceTargets(roots) {
|
|
16
|
+
return [...new Set(pageReferenceNodes(roots).flatMap((node) => pageReferenceTarget(node.attribs) ?? []))];
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { PAGE_REFERENCE_ATTRIBUTE, PAGE_REFERENCE_SELECTOR, PAGE_REFERENCE_VALUE, pageReferenceNodes, pageReferenceTarget, pageReferenceTargets };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { partSection } from "./book-held.js";
|
|
2
|
+
import { isPart } from "@segnavia/format";
|
|
3
|
+
//#region src/book/book-part-build.ts
|
|
4
|
+
function partNames(ancestry, own) {
|
|
5
|
+
return Object.fromEntries([...ancestry, ...own ? [own] : []].map((part) => [part.name, part.runningHead ?? part.title ?? ""]));
|
|
6
|
+
}
|
|
7
|
+
function addPart(sectionEntry, build) {
|
|
8
|
+
const { entry } = sectionEntry;
|
|
9
|
+
if (!isPart(entry)) return false;
|
|
10
|
+
const id = sectionEntry.id ?? "";
|
|
11
|
+
if (entry.html !== void 0) {
|
|
12
|
+
const parts = partNames(sectionEntry.ancestry, {
|
|
13
|
+
name: entry.name,
|
|
14
|
+
title: entry.title,
|
|
15
|
+
runningHead: entry.runningHead,
|
|
16
|
+
id
|
|
17
|
+
});
|
|
18
|
+
build.sections.push(partSection(entry, id, sectionEntry.division, build.build, parts));
|
|
19
|
+
attachPendingPartHeadings(build, sectionEntry, id);
|
|
20
|
+
}
|
|
21
|
+
if (entry.contents && entry.title !== void 0) {
|
|
22
|
+
const heading = {
|
|
23
|
+
level: 1,
|
|
24
|
+
depth: sectionEntry.depth,
|
|
25
|
+
text: entry.title
|
|
26
|
+
};
|
|
27
|
+
if (entry.html === void 0) build.pendingPartHeadings.push({
|
|
28
|
+
...heading,
|
|
29
|
+
partId: id
|
|
30
|
+
});
|
|
31
|
+
else build.headings.push({
|
|
32
|
+
...heading,
|
|
33
|
+
id,
|
|
34
|
+
sectionId: id
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
function attachPendingPartHeadings(build, sectionEntry, id) {
|
|
40
|
+
const ancestryIds = new Set(sectionEntry.ancestry.map((part) => part.id));
|
|
41
|
+
const attached = build.pendingPartHeadings.filter((heading) => ancestryIds.has(heading.partId));
|
|
42
|
+
build.headings.push(...attached.map(({ partId: _partId, ...heading }) => ({
|
|
43
|
+
...heading,
|
|
44
|
+
id,
|
|
45
|
+
sectionId: id
|
|
46
|
+
})));
|
|
47
|
+
build.pendingPartHeadings = build.pendingPartHeadings.filter((heading) => !ancestryIds.has(heading.partId));
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
export { addPart, attachPendingPartHeadings, partNames };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { Element, Text } from "domhandler";
|
package/dist/book/notes.js
CHANGED
|
@@ -32,18 +32,15 @@ const INSIDE_BLOCKS = /* @__PURE__ */ new Set([
|
|
|
32
32
|
]);
|
|
33
33
|
function noteState() {
|
|
34
34
|
return {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
used: /* @__PURE__ */ new Set(),
|
|
36
|
+
counters: /* @__PURE__ */ new Map(),
|
|
37
|
+
collected: /* @__PURE__ */ new Map(),
|
|
38
|
+
pageParagraphs: /* @__PURE__ */ new Map(),
|
|
39
|
+
authored: /* @__PURE__ */ new Map(),
|
|
40
|
+
repeatCount: 0,
|
|
41
|
+
issues: []
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
-
function endnotesInOrder(state) {
|
|
45
|
-
return [...state.chapterEndnotes.values()].flat();
|
|
46
|
-
}
|
|
47
44
|
function nearestBlock(note) {
|
|
48
45
|
for (let parent = note.parent; parent; parent = parent.parent) if (isTag(parent) && BLOCKS.has(parent.name)) return parent;
|
|
49
46
|
}
|
|
@@ -63,59 +60,128 @@ function noteParagraph(className, noteId, refId, marker, content) {
|
|
|
63
60
|
...content
|
|
64
61
|
]);
|
|
65
62
|
}
|
|
66
|
-
function
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return
|
|
63
|
+
function scopeKey(series, context) {
|
|
64
|
+
if (series.numbering === "book") return "book";
|
|
65
|
+
if (series.numbering === "part") return context.part?.id ?? "book";
|
|
66
|
+
return String(context.chapter);
|
|
67
|
+
}
|
|
68
|
+
function placementKey(series, context) {
|
|
69
|
+
if (series.placement === "book") return "book";
|
|
70
|
+
if (series.placement === "part") return context.part?.id ?? "book";
|
|
71
|
+
return context.placementKey ?? String(context.chapter);
|
|
72
|
+
}
|
|
73
|
+
function nextNumber(series, context, state) {
|
|
74
|
+
const scope = scopeKey(series, context);
|
|
75
|
+
const key = `${series.name}:${scope}`;
|
|
76
|
+
const number = (state.counters.get(key) ?? 0) + 1;
|
|
77
|
+
state.counters.set(key, number);
|
|
78
|
+
return {
|
|
79
|
+
number,
|
|
80
|
+
scope
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function noteIds(series, scope, chapter, number) {
|
|
84
|
+
const scopePart = series.idScope === "chapter" ? `-${chapter}` : series.numbering === "book" ? "" : `-${scope}`;
|
|
85
|
+
return {
|
|
86
|
+
noteId: `segnavia-${series.idStem}${scopePart}-${number}`,
|
|
87
|
+
refId: `segnavia-${series.idStem}ref${scopePart}-${number}`
|
|
88
|
+
};
|
|
74
89
|
}
|
|
75
|
-
function
|
|
76
|
-
const
|
|
90
|
+
function addPageBlock(block, paragraph, blocks, series, seriesOrder) {
|
|
91
|
+
const blockSeries = blocks.get(block) ?? /* @__PURE__ */ new Map();
|
|
92
|
+
const existing = blockSeries.get(series.name);
|
|
77
93
|
if (existing) {
|
|
78
94
|
append(existing, paragraph);
|
|
79
95
|
return;
|
|
80
96
|
}
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
97
|
+
const attributes = {
|
|
98
|
+
class: series.blockClass,
|
|
99
|
+
...series.markBlockSeries ? { "data-segnavia-note-series": series.name } : {}
|
|
100
|
+
};
|
|
101
|
+
const notes = element("div", attributes, [paragraph]);
|
|
102
|
+
const index = seriesOrder.get(series.name) ?? seriesOrder.size;
|
|
103
|
+
const next = [...blockSeries.entries()].filter(([name]) => (seriesOrder.get(name) ?? seriesOrder.size) > index).sort(([left], [right]) => (seriesOrder.get(left) ?? seriesOrder.size) - (seriesOrder.get(right) ?? seriesOrder.size))[0]?.[1];
|
|
104
|
+
if (next?.prev) insertAfter(next.prev, notes);
|
|
105
|
+
else if (INSIDE_BLOCKS.has(block.name)) append(block, notes);
|
|
106
|
+
else {
|
|
107
|
+
const previous = [...blockSeries.entries()].filter(([name]) => (seriesOrder.get(name) ?? seriesOrder.size) < index).sort(([left], [right]) => (seriesOrder.get(right) ?? seriesOrder.size) - (seriesOrder.get(left) ?? seriesOrder.size))[0]?.[1];
|
|
108
|
+
insertAfter(previous ?? block, notes);
|
|
109
|
+
}
|
|
110
|
+
blockSeries.set(series.name, notes);
|
|
111
|
+
blocks.set(block, blockSeries);
|
|
85
112
|
}
|
|
86
|
-
function
|
|
113
|
+
function collectedNotes(series, context, state) {
|
|
114
|
+
const key = placementKey(series, context);
|
|
115
|
+
const mapKey = `${series.name}:${key}`;
|
|
116
|
+
const existing = state.collected.get(mapKey);
|
|
117
|
+
if (existing) return existing;
|
|
118
|
+
const scopeTitle = series.placement === "part" ? context.part?.title : series.placement === "chapter" ? context.title : void 0;
|
|
119
|
+
const collected = {
|
|
120
|
+
series,
|
|
121
|
+
placementKey: key,
|
|
122
|
+
targetId: series.placement === "book" || series.placement === "part" && context.part === void 0 || series.placement === "chapter" && context.division === "front" ? "book" : series.placement === "part" ? context.part?.id ?? context.targetId : context.targetId,
|
|
123
|
+
scopeTitle,
|
|
124
|
+
division: context.division,
|
|
125
|
+
paragraphs: []
|
|
126
|
+
};
|
|
127
|
+
state.collected.set(mapKey, collected);
|
|
128
|
+
return collected;
|
|
129
|
+
}
|
|
130
|
+
function transformAuthoredNote(note, series, context, state, blocks, seriesOrder, galley) {
|
|
87
131
|
const block = nearestBlock(note);
|
|
88
132
|
if (!block) return;
|
|
89
|
-
const number =
|
|
90
|
-
const noteId =
|
|
91
|
-
const
|
|
92
|
-
const
|
|
133
|
+
const { number, scope } = nextNumber(series, context, state);
|
|
134
|
+
const { noteId, refId } = noteIds(series, scope, context.chapter, number);
|
|
135
|
+
const marker = noteMarker(number, series.marker);
|
|
136
|
+
const authoredId = note.attribs.id;
|
|
93
137
|
const content = [...note.children];
|
|
94
138
|
replace(note, element("sup", { class: "segnavia-note-ref" }, [markerLink(refId, noteId, marker)]));
|
|
95
|
-
const paragraph = noteParagraph(
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
139
|
+
const paragraph = noteParagraph(series.bodyClass, noteId, refId, marker, content);
|
|
140
|
+
if (series.placement === "page") {
|
|
141
|
+
if (galley) {
|
|
142
|
+
const paragraphs = state.pageParagraphs.get(series.name) ?? [];
|
|
143
|
+
paragraphs.push(paragraph);
|
|
144
|
+
state.pageParagraphs.set(series.name, paragraphs);
|
|
145
|
+
} else addPageBlock(block, paragraph, blocks, series, seriesOrder);
|
|
146
|
+
} else collectedNotes(series, context, state).paragraphs.push(paragraph);
|
|
147
|
+
if (authoredId) state.authored.set(authoredId, {
|
|
148
|
+
noteId,
|
|
149
|
+
refId,
|
|
150
|
+
marker
|
|
151
|
+
});
|
|
152
|
+
state.used.add(series.name);
|
|
99
153
|
}
|
|
100
|
-
function
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
state.
|
|
112
|
-
state.endnotes = true;
|
|
154
|
+
function transformRepeat(node, state) {
|
|
155
|
+
const authoredId = node.attribs["data-note-ref"] ?? "";
|
|
156
|
+
const note = state.authored.get(authoredId);
|
|
157
|
+
if (!note) {
|
|
158
|
+
state.issues.push(`data-note-ref ${authoredId} names no earlier note; place the note before its repeated call`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
state.repeatCount += 1;
|
|
162
|
+
replace(node, element("sup", {
|
|
163
|
+
class: "segnavia-note-ref",
|
|
164
|
+
"data-segnavia-note-repeat": ""
|
|
165
|
+
}, [markerLink(`${note.refId}-repeat-${state.repeatCount}`, note.noteId, note.marker)]));
|
|
113
166
|
}
|
|
114
|
-
function transformNotes(root, book,
|
|
167
|
+
function transformNotes(root, book, context, state, galley = false) {
|
|
115
168
|
const blocks = /* @__PURE__ */ new Map();
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
169
|
+
const byName = new Map(book.notes.map((series) => [series.name, series]));
|
|
170
|
+
const seriesOrder = new Map(book.notes.map((series, index) => [series.name, index]));
|
|
171
|
+
const nodes = elements(root.children).filter((node) => node.name === "span" && node.attribs["data-note"] !== void 0 || node.name === "a" && node.attribs["data-note-ref"] !== void 0);
|
|
172
|
+
for (const node of nodes) {
|
|
173
|
+
if (node.name === "a") {
|
|
174
|
+
transformRepeat(node, state);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const name = node.attribs["data-note"] ?? "";
|
|
178
|
+
const series = byName.get(name);
|
|
179
|
+
if (!series) {
|
|
180
|
+
state.issues.push(`data-note names unknown note series ${name}; declare the series in notes or use a declared series name`);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
transformAuthoredNote(node, series, context, state, blocks, seriesOrder, galley);
|
|
184
|
+
}
|
|
119
185
|
}
|
|
120
186
|
//#endregion
|
|
121
|
-
export {
|
|
187
|
+
export { noteState, transformNotes };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PaginateBookInput, PaginateBookResult } from "./paginate-types.js";
|
|
2
|
+
import { printedPageAt, selectBookParent } from "../printed-pages.js";
|
|
2
3
|
import { paginateColumns } from "./paginate-columns.js";
|
|
3
4
|
import { paginateKeeps } from "./paginate-keeps.js";
|
|
4
5
|
import { paginateMarks } from "./paginate-marks.js";
|
|
@@ -6,6 +7,6 @@ import { paginateNotes } from "./paginate-notes.js";
|
|
|
6
7
|
import { paginateSequence } from "./paginate-sequence.js";
|
|
7
8
|
//#region src/book/paginate-book.d.ts
|
|
8
9
|
export declare function paginateBook(input: PaginateBookInput): PaginateBookResult;
|
|
9
|
-
export declare const PAGINATE_FUNCTIONS: (typeof paginateColumns | typeof paginateNotes | typeof paginateKeeps | typeof paginateMarks | typeof paginateSequence)[];
|
|
10
|
+
export declare const PAGINATE_FUNCTIONS: (typeof printedPageAt | typeof selectBookParent | typeof paginateColumns | typeof paginateNotes | typeof paginateKeeps | typeof paginateMarks | typeof paginateSequence)[];
|
|
10
11
|
export declare function paginateScript(): string;
|
|
11
12
|
//#endregion
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { printedPageAt, selectBookParent } from "../printed-pages.js";
|
|
1
2
|
import { paginateColumns } from "./paginate-columns.js";
|
|
2
3
|
import { paginateKeeps } from "./paginate-keeps.js";
|
|
3
4
|
import { paginateMarks } from "./paginate-marks.js";
|
|
@@ -5,31 +6,36 @@ import { paginateNotes } from "./paginate-notes.js";
|
|
|
5
6
|
import { paginateSequence } from "./paginate-sequence.js";
|
|
6
7
|
//#region src/book/paginate-book.ts
|
|
7
8
|
function paginateBook(input) {
|
|
8
|
-
const textHeight = input.geometry.textHeight;
|
|
9
9
|
const orphans = input.settings.orphans ?? 2;
|
|
10
10
|
const widows = input.settings.widows ?? 2;
|
|
11
|
-
function used(placed, fragments) {
|
|
11
|
+
function used(textHeight, placed, fragments) {
|
|
12
12
|
return paginateColumns(placed).height + paginateNotes({
|
|
13
13
|
op: "space",
|
|
14
14
|
notes: input.notes,
|
|
15
|
-
geometry:
|
|
15
|
+
geometry: {
|
|
16
|
+
...input.geometry,
|
|
17
|
+
textHeight
|
|
18
|
+
},
|
|
16
19
|
fragments
|
|
17
20
|
});
|
|
18
21
|
}
|
|
19
|
-
function withNotes(extra) {
|
|
22
|
+
function withNotes(textHeight, extra) {
|
|
20
23
|
return {
|
|
21
24
|
notes: input.notes,
|
|
22
|
-
geometry:
|
|
23
|
-
|
|
25
|
+
geometry: {
|
|
26
|
+
...input.geometry,
|
|
27
|
+
textHeight
|
|
28
|
+
},
|
|
29
|
+
used: (placed, fragments) => used(textHeight, placed, fragments),
|
|
24
30
|
...extra
|
|
25
31
|
};
|
|
26
32
|
}
|
|
27
|
-
function refusal(block) {
|
|
33
|
+
function refusal(block, textArea = "the text area") {
|
|
28
34
|
const position = `chapter ${block.position.chapter} block ${block.position.block}`;
|
|
29
35
|
return {
|
|
30
36
|
tag: block.tag,
|
|
31
37
|
position: block.position,
|
|
32
|
-
message: `The ${block.tag} at ${position} could not establish a fit in
|
|
38
|
+
message: `The ${block.tag} at ${position} could not establish a fit in ${textArea}; shorten it or increase the text-area height to clear this refusal.`
|
|
33
39
|
};
|
|
34
40
|
}
|
|
35
41
|
function blockForNote(id) {
|
|
@@ -52,16 +58,16 @@ function paginateBook(input) {
|
|
|
52
58
|
message: `The ${found.tag} at ${where} could not establish a fit for a note line in the text area; ${clears}`
|
|
53
59
|
};
|
|
54
60
|
}
|
|
55
|
-
function lineTooTall(unit) {
|
|
61
|
+
function lineTooTall(unit, textHeight) {
|
|
56
62
|
return unit.bottom - unit.top > textHeight;
|
|
57
63
|
}
|
|
58
|
-
function greedyFill(placed, fragments, units, index) {
|
|
64
|
+
function greedyFill(placed, fragments, units, index, textHeight) {
|
|
59
65
|
let next = index;
|
|
60
66
|
let notes = fragments;
|
|
61
67
|
while (next < units.length) {
|
|
62
68
|
const unit = units[next];
|
|
63
|
-
if (!unit || lineTooTall(unit)) break;
|
|
64
|
-
const allocated = paginateNotes(withNotes({
|
|
69
|
+
if (!unit || lineTooTall(unit, textHeight)) break;
|
|
70
|
+
const allocated = paginateNotes(withNotes(textHeight, {
|
|
65
71
|
op: "allocate",
|
|
66
72
|
placed,
|
|
67
73
|
fragments: notes,
|
|
@@ -78,15 +84,15 @@ function paginateBook(input) {
|
|
|
78
84
|
index: next
|
|
79
85
|
};
|
|
80
86
|
}
|
|
81
|
-
function forcePlace(unit) {
|
|
82
|
-
if (lineTooTall(unit)) return {
|
|
87
|
+
function forcePlace(unit, textHeight, textArea) {
|
|
88
|
+
if (lineTooTall(unit, textHeight)) return {
|
|
83
89
|
placed: [],
|
|
84
90
|
fragments: [],
|
|
85
91
|
index: 0,
|
|
86
92
|
pending: [],
|
|
87
|
-
refusal: refusal(unit.block)
|
|
93
|
+
refusal: refusal(unit.block, textArea)
|
|
88
94
|
};
|
|
89
|
-
const allocated = paginateNotes(withNotes({
|
|
95
|
+
const allocated = paginateNotes(withNotes(textHeight, {
|
|
90
96
|
op: "allocate",
|
|
91
97
|
placed: [],
|
|
92
98
|
fragments: [],
|
|
@@ -97,30 +103,30 @@ function paginateBook(input) {
|
|
|
97
103
|
fragments: [],
|
|
98
104
|
index: 0,
|
|
99
105
|
pending: [],
|
|
100
|
-
refusal: refusal(unit.block)
|
|
106
|
+
refusal: refusal(unit.block, textArea)
|
|
101
107
|
};
|
|
102
108
|
return {
|
|
103
109
|
placed: [unit],
|
|
104
110
|
fragments: allocated,
|
|
105
111
|
index: 0,
|
|
106
|
-
pending: paginateNotes(withNotes({
|
|
112
|
+
pending: paginateNotes(withNotes(textHeight, {
|
|
107
113
|
op: "leftover",
|
|
108
114
|
fragments: allocated
|
|
109
115
|
}))
|
|
110
116
|
};
|
|
111
117
|
}
|
|
112
|
-
function fillPage(units, index, pending) {
|
|
113
|
-
const held = paginateNotes(withNotes({
|
|
118
|
+
function fillPage(units, index, pending, textHeight, textArea) {
|
|
119
|
+
const held = paginateNotes(withNotes(textHeight, {
|
|
114
120
|
op: "continue",
|
|
115
121
|
pending
|
|
116
122
|
}));
|
|
117
123
|
const incoming = units[index];
|
|
118
|
-
if (held.leftover.length === 0 && incoming && lineTooTall(incoming)) return {
|
|
124
|
+
if (held.leftover.length === 0 && incoming && lineTooTall(incoming, textHeight)) return {
|
|
119
125
|
placed: [],
|
|
120
126
|
fragments: [],
|
|
121
127
|
index,
|
|
122
128
|
pending,
|
|
123
|
-
refusal: refusal(incoming.block)
|
|
129
|
+
refusal: refusal(incoming.block, textArea)
|
|
124
130
|
};
|
|
125
131
|
const stuck = held.fragments.length === 0 ? held.leftover[0] : void 0;
|
|
126
132
|
if (stuck) return {
|
|
@@ -136,10 +142,10 @@ function paginateBook(input) {
|
|
|
136
142
|
index,
|
|
137
143
|
pending: held.leftover
|
|
138
144
|
};
|
|
139
|
-
const filled = greedyFill([], held.fragments, units, index);
|
|
145
|
+
const filled = greedyFill([], held.fragments, units, index, textHeight);
|
|
140
146
|
const placed = paginateKeeps(filled.placed, units[filled.index], textHeight, orphans, widows, held.fragments.length > 0);
|
|
141
147
|
const nextIndex = filled.index - (filled.placed.length - placed.length);
|
|
142
|
-
const fragments = paginateNotes(withNotes({
|
|
148
|
+
const fragments = paginateNotes(withNotes(textHeight, {
|
|
143
149
|
op: "recompute",
|
|
144
150
|
placed,
|
|
145
151
|
seed: held.fragments
|
|
@@ -148,7 +154,7 @@ function paginateBook(input) {
|
|
|
148
154
|
placed,
|
|
149
155
|
fragments,
|
|
150
156
|
index: nextIndex,
|
|
151
|
-
pending: paginateNotes(withNotes({
|
|
157
|
+
pending: paginateNotes(withNotes(textHeight, {
|
|
152
158
|
op: "leftover",
|
|
153
159
|
fragments
|
|
154
160
|
}))
|
|
@@ -169,7 +175,7 @@ function paginateBook(input) {
|
|
|
169
175
|
pending,
|
|
170
176
|
done: true
|
|
171
177
|
};
|
|
172
|
-
const forced = forcePlace(unit);
|
|
178
|
+
const forced = forcePlace(unit, textHeight, textArea);
|
|
173
179
|
if (forced.refusal) return forced;
|
|
174
180
|
return {
|
|
175
181
|
placed: forced.placed,
|
|
@@ -181,6 +187,8 @@ function paginateBook(input) {
|
|
|
181
187
|
return paginateSequence(input, fillPage);
|
|
182
188
|
}
|
|
183
189
|
const PAGINATE_FUNCTIONS = [
|
|
190
|
+
printedPageAt,
|
|
191
|
+
selectBookParent,
|
|
184
192
|
paginateColumns,
|
|
185
193
|
paginateNotes,
|
|
186
194
|
paginateKeeps,
|