@helloao/tools 0.2.0 → 0.4.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/cjs/generation/api.cjs +2213 -216
- package/dist/cjs/generation/api.cjs.map +3 -3
- package/dist/cjs/generation/audio.cjs +5 -5
- package/dist/cjs/generation/audio.cjs.map +1 -1
- package/dist/cjs/generation/book-order.cjs.map +1 -1
- package/dist/cjs/generation/common-types.cjs +782 -14
- package/dist/cjs/generation/common-types.cjs.map +2 -2
- package/dist/cjs/generation/dataset.cjs +13 -9
- package/dist/cjs/generation/dataset.cjs.map +2 -2
- package/dist/cjs/generation/index.cjs +8 -5
- package/dist/cjs/generation/index.cjs.map +2 -2
- package/dist/cjs/generation/simple.cjs +294 -0
- package/dist/cjs/generation/simple.cjs.map +7 -0
- package/dist/cjs/generation/theographic.cjs +446 -0
- package/dist/cjs/generation/theographic.cjs.map +7 -0
- package/dist/cjs/index.cjs +4 -4
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/log.cjs.map +1 -1
- package/dist/cjs/parser/codex-parser.cjs +3 -3
- package/dist/cjs/parser/codex-parser.cjs.map +3 -3
- package/dist/cjs/parser/commentary-csv-parser.cjs +14 -3
- package/dist/cjs/parser/commentary-csv-parser.cjs.map +3 -3
- package/dist/cjs/parser/index.cjs +1 -1
- package/dist/cjs/parser/iterators.cjs.map +1 -1
- package/dist/cjs/parser/lockman-parser.cjs +1 -1
- package/dist/cjs/parser/lockman-parser.cjs.map +1 -1
- package/dist/cjs/parser/tyndale-xml-parser.cjs +1 -1
- package/dist/cjs/parser/tyndale-xml-parser.cjs.map +1 -1
- package/dist/cjs/parser/types.cjs.map +1 -1
- package/dist/cjs/parser/usfm-parser.cjs +2 -2
- package/dist/cjs/parser/usfm-parser.cjs.map +1 -1
- package/dist/cjs/parser/usx-parser.cjs +122 -26
- package/dist/cjs/parser/usx-parser.cjs.map +2 -2
- package/dist/cjs/parser/words.cjs +187 -0
- package/dist/cjs/parser/words.cjs.map +7 -0
- package/dist/cjs/utils.cjs +1 -1
- package/dist/esm/generation/api.js +2155 -214
- package/dist/esm/generation/api.js.map +3 -3
- package/dist/esm/generation/audio.js +1 -1
- package/dist/esm/generation/audio.js.map +1 -1
- package/dist/esm/generation/book-order.js.map +1 -1
- package/dist/esm/generation/common-types.js +738 -14
- package/dist/esm/generation/common-types.js.map +2 -2
- package/dist/esm/generation/dataset.js +6 -2
- package/dist/esm/generation/dataset.js.map +2 -2
- package/dist/esm/generation/index.js +2 -1
- package/dist/esm/generation/index.js.map +2 -2
- package/dist/esm/generation/simple.js +260 -0
- package/dist/esm/generation/simple.js.map +7 -0
- package/dist/esm/generation/theographic.js +412 -0
- package/dist/esm/generation/theographic.js.map +7 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/log.js.map +1 -1
- package/dist/esm/parser/codex-parser.js +3 -3
- package/dist/esm/parser/codex-parser.js.map +3 -3
- package/dist/esm/parser/commentary-csv-parser.js +3 -2
- package/dist/esm/parser/commentary-csv-parser.js.map +2 -2
- package/dist/esm/parser/iterators.js.map +1 -1
- package/dist/esm/parser/lockman-parser.js +1 -1
- package/dist/esm/parser/lockman-parser.js.map +1 -1
- package/dist/esm/parser/tyndale-xml-parser.js +1 -1
- package/dist/esm/parser/tyndale-xml-parser.js.map +1 -1
- package/dist/esm/parser/usfm-parser.js +1 -1
- package/dist/esm/parser/usfm-parser.js.map +1 -1
- package/dist/esm/parser/usx-parser.js +130 -26
- package/dist/esm/parser/usx-parser.js.map +2 -2
- package/dist/esm/parser/words.js +149 -0
- package/dist/esm/parser/words.js.map +7 -0
- package/dist/types/generation/api.d.ts +9006 -1101
- package/dist/types/generation/common-types.d.ts +1318 -106
- package/dist/types/generation/dataset.d.ts +17 -1
- package/dist/types/generation/index.d.ts +2 -1
- package/dist/types/generation/simple.d.ts +110 -0
- package/dist/types/generation/theographic.d.ts +64 -0
- package/dist/types/parser/commentary-csv-parser.d.ts +1 -1
- package/dist/types/parser/lockman-parser.d.ts +1 -1
- package/dist/types/parser/tyndale-xml-parser.d.ts +1 -1
- package/dist/types/parser/types.d.ts +63 -0
- package/dist/types/parser/usx-parser.d.ts +6 -5
- package/dist/types/parser/words.d.ts +104 -0
- package/package.json +3 -2
|
@@ -4,12 +4,21 @@ import {
|
|
|
4
4
|
children,
|
|
5
5
|
isParent
|
|
6
6
|
} from "./iterators.js";
|
|
7
|
+
import {
|
|
8
|
+
addChapterWords,
|
|
9
|
+
collapseWhitespaceMap,
|
|
10
|
+
isAnnotatedContent,
|
|
11
|
+
readWordAnnotations,
|
|
12
|
+
remapChapterWords,
|
|
13
|
+
removeChapterWordsForContent,
|
|
14
|
+
trimWordRange
|
|
15
|
+
} from "./words.js";
|
|
7
16
|
import { KNOWN_SKIPPED_VERSES } from "../utils.js";
|
|
8
17
|
var NodeType = /* @__PURE__ */ ((NodeType2) => {
|
|
9
18
|
NodeType2[NodeType2["Text"] = 3] = "Text";
|
|
10
19
|
return NodeType2;
|
|
11
20
|
})(NodeType || {});
|
|
12
|
-
export const PARSER_VERSION =
|
|
21
|
+
export const PARSER_VERSION = 4;
|
|
13
22
|
export class USXParser {
|
|
14
23
|
_domParser;
|
|
15
24
|
_noteCounter = 0;
|
|
@@ -186,12 +195,15 @@ export class USXParser {
|
|
|
186
195
|
descriptive = true;
|
|
187
196
|
}
|
|
188
197
|
}
|
|
189
|
-
for (let
|
|
198
|
+
for (let value of this.iterateNodeTextContent(
|
|
190
199
|
nodes,
|
|
191
200
|
node,
|
|
192
201
|
chapter,
|
|
193
202
|
verse
|
|
194
203
|
)) {
|
|
204
|
+
const words = isAnnotatedContent(value) ? value.words : null;
|
|
205
|
+
const content = isAnnotatedContent(value) ? value.annotatedContent : value;
|
|
206
|
+
let result = content;
|
|
195
207
|
if (poem !== null || descriptive !== null) {
|
|
196
208
|
if (typeof content === "string") {
|
|
197
209
|
let text = {
|
|
@@ -203,7 +215,7 @@ export class USXParser {
|
|
|
203
215
|
if (descriptive !== null) {
|
|
204
216
|
text.descriptive = true;
|
|
205
217
|
}
|
|
206
|
-
|
|
218
|
+
result = text;
|
|
207
219
|
} else {
|
|
208
220
|
let text = {
|
|
209
221
|
...content
|
|
@@ -216,10 +228,16 @@ export class USXParser {
|
|
|
216
228
|
text.descriptive = true;
|
|
217
229
|
}
|
|
218
230
|
}
|
|
219
|
-
|
|
231
|
+
result = text;
|
|
220
232
|
}
|
|
233
|
+
}
|
|
234
|
+
if (words) {
|
|
235
|
+
yield {
|
|
236
|
+
annotatedContent: result,
|
|
237
|
+
words
|
|
238
|
+
};
|
|
221
239
|
} else {
|
|
222
|
-
yield
|
|
240
|
+
yield result;
|
|
223
241
|
}
|
|
224
242
|
}
|
|
225
243
|
}
|
|
@@ -253,15 +271,26 @@ export class USXParser {
|
|
|
253
271
|
}
|
|
254
272
|
}
|
|
255
273
|
const previousVerse = this._currentChapter?.number === chapter.number ? this._lastVerse : null;
|
|
274
|
+
const words = [];
|
|
256
275
|
for (let content of this.iterateVerseContent(
|
|
257
276
|
chapter,
|
|
258
277
|
verse,
|
|
259
278
|
nodes,
|
|
260
279
|
previousVerse
|
|
261
280
|
)) {
|
|
262
|
-
|
|
281
|
+
if (isAnnotatedContent(content)) {
|
|
282
|
+
addOrJoin(
|
|
283
|
+
verse.content,
|
|
284
|
+
content.annotatedContent,
|
|
285
|
+
content.words,
|
|
286
|
+
words
|
|
287
|
+
);
|
|
288
|
+
} else {
|
|
289
|
+
addOrJoin(verse.content, content);
|
|
290
|
+
}
|
|
263
291
|
}
|
|
264
|
-
trimContent(verse.content);
|
|
292
|
+
trimContent(verse.content, words);
|
|
293
|
+
addChapterWords(chapter, verse.number, words);
|
|
265
294
|
this._lastVerse = verse;
|
|
266
295
|
return verse;
|
|
267
296
|
}
|
|
@@ -279,7 +308,10 @@ export class USXParser {
|
|
|
279
308
|
yield content;
|
|
280
309
|
continue;
|
|
281
310
|
}
|
|
282
|
-
addOrJoin(
|
|
311
|
+
addOrJoin(
|
|
312
|
+
subtitle.content,
|
|
313
|
+
isAnnotatedContent(content) ? content.annotatedContent : content
|
|
314
|
+
);
|
|
283
315
|
}
|
|
284
316
|
trimContent(subtitle.content);
|
|
285
317
|
if (subtitle.content.length > 0) {
|
|
@@ -365,18 +397,48 @@ export class USXParser {
|
|
|
365
397
|
*iterateChar(nodes, node) {
|
|
366
398
|
const style = node.getAttribute("style");
|
|
367
399
|
let text = "";
|
|
400
|
+
const words = [];
|
|
401
|
+
let currentWord = null;
|
|
402
|
+
let currentAnnotations = null;
|
|
403
|
+
let currentStart = 0;
|
|
404
|
+
const endCurrentWord = () => {
|
|
405
|
+
if (currentAnnotations) {
|
|
406
|
+
const range = trimWordRange(text, currentStart, text.length);
|
|
407
|
+
if (range) {
|
|
408
|
+
words.push({
|
|
409
|
+
...range,
|
|
410
|
+
annotations: currentAnnotations
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
currentWord = null;
|
|
415
|
+
currentAnnotations = null;
|
|
416
|
+
};
|
|
368
417
|
for (let char of children(nodes, node)) {
|
|
369
|
-
if (char.nodeType
|
|
370
|
-
|
|
418
|
+
if (char.nodeType !== 3 /* Text */) {
|
|
419
|
+
continue;
|
|
371
420
|
}
|
|
421
|
+
const word = wordElementFor(char, node);
|
|
422
|
+
if (word !== currentWord) {
|
|
423
|
+
endCurrentWord();
|
|
424
|
+
currentWord = word;
|
|
425
|
+
currentStart = text.length;
|
|
426
|
+
currentAnnotations = word ? readWordAnnotations(word) : null;
|
|
427
|
+
}
|
|
428
|
+
text += char.textContent || "";
|
|
372
429
|
}
|
|
373
|
-
|
|
430
|
+
endCurrentWord();
|
|
431
|
+
const content = style === "wj" ? {
|
|
432
|
+
text,
|
|
433
|
+
wordsOfJesus: true
|
|
434
|
+
} : text;
|
|
435
|
+
if (words.length > 0) {
|
|
374
436
|
yield {
|
|
375
|
-
|
|
376
|
-
|
|
437
|
+
annotatedContent: content,
|
|
438
|
+
words
|
|
377
439
|
};
|
|
378
440
|
} else {
|
|
379
|
-
yield
|
|
441
|
+
yield content;
|
|
380
442
|
}
|
|
381
443
|
}
|
|
382
444
|
}
|
|
@@ -472,40 +534,82 @@ function* iterateCharContent(char) {
|
|
|
472
534
|
function trimText(text) {
|
|
473
535
|
return text.replace(/\s+/g, " ");
|
|
474
536
|
}
|
|
475
|
-
function
|
|
537
|
+
function wordElementFor(node, root) {
|
|
538
|
+
let element = node.parentElement;
|
|
539
|
+
while (element) {
|
|
540
|
+
if (element.nodeName === "char" && element.getAttribute("style") === "w") {
|
|
541
|
+
return element;
|
|
542
|
+
}
|
|
543
|
+
if (element === root) {
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
element = element.parentElement;
|
|
547
|
+
}
|
|
548
|
+
return null;
|
|
549
|
+
}
|
|
550
|
+
function trimContent(content, words) {
|
|
476
551
|
for (let i = 0; i < content.length; i++) {
|
|
477
552
|
const value = content[i];
|
|
553
|
+
let text = null;
|
|
478
554
|
if (typeof value === "string") {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
555
|
+
const collapsed = collapseWhitespaceMap(value);
|
|
556
|
+
content[i] = collapsed.text;
|
|
557
|
+
text = collapsed.text;
|
|
558
|
+
if (words) {
|
|
559
|
+
remapChapterWords(words, i, collapsed.map);
|
|
484
560
|
}
|
|
485
561
|
} else if (isVerseText(value)) {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
562
|
+
const collapsed = collapseWhitespaceMap(value.text);
|
|
563
|
+
value.text = collapsed.text;
|
|
564
|
+
text = collapsed.text;
|
|
565
|
+
if (words) {
|
|
566
|
+
remapChapterWords(words, i, collapsed.map);
|
|
491
567
|
}
|
|
492
568
|
}
|
|
569
|
+
if (text === "") {
|
|
570
|
+
content.splice(i, 1);
|
|
571
|
+
if (words) {
|
|
572
|
+
removeChapterWordsForContent(words, i);
|
|
573
|
+
}
|
|
574
|
+
i--;
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
493
577
|
}
|
|
494
578
|
return content;
|
|
495
579
|
}
|
|
496
|
-
function addOrJoin(array, value) {
|
|
580
|
+
function addOrJoin(array, value, ranges, words) {
|
|
581
|
+
let contentIndex;
|
|
582
|
+
let offset;
|
|
497
583
|
if (array.length === 0) {
|
|
584
|
+
contentIndex = 0;
|
|
585
|
+
offset = 0;
|
|
498
586
|
array.push(value);
|
|
499
587
|
} else {
|
|
500
588
|
const last = array[array.length - 1];
|
|
501
589
|
if (typeof last === "string" && typeof value === "string") {
|
|
590
|
+
contentIndex = array.length - 1;
|
|
591
|
+
offset = last.length;
|
|
502
592
|
array[array.length - 1] = last + value;
|
|
503
593
|
} else if (isVerseText(last) && isVerseText(value) && hasSameFormatting(last, value)) {
|
|
594
|
+
contentIndex = array.length - 1;
|
|
595
|
+
offset = last.text.length;
|
|
504
596
|
last.text += value.text;
|
|
505
597
|
} else {
|
|
598
|
+
contentIndex = array.length;
|
|
599
|
+
offset = 0;
|
|
506
600
|
array.push(value);
|
|
507
601
|
}
|
|
508
602
|
}
|
|
603
|
+
if (ranges && words) {
|
|
604
|
+
for (let range of ranges) {
|
|
605
|
+
words.push({
|
|
606
|
+
contentIndex,
|
|
607
|
+
start: offset + range.start,
|
|
608
|
+
end: offset + range.end,
|
|
609
|
+
...range.annotations
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
}
|
|
509
613
|
}
|
|
510
614
|
function isVerseText(value) {
|
|
511
615
|
return typeof value === "object" && value !== null && "text" in value;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../parser/usx-parser.ts"],
|
|
4
|
-
"sourcesContent": ["import {\r\n Chapter,\r\n ChapterContent,\r\n Footnote,\r\n FootnoteReference,\r\n ParseTree,\r\n Verse,\r\n Text,\r\n HebrewSubtitle,\r\n InlineLineBreak,\r\n InlineHeading,\r\n ParseMessage,\r\n} from './types.js';\r\nimport {\r\n iterateAll,\r\n children,\r\n RewindableIterator,\r\n isParent,\r\n} from './iterators.js';\r\nimport { KNOWN_SKIPPED_VERSES } from '../utils.js';\r\n\r\nenum NodeType {\r\n Text = 3,\r\n}\r\n\r\n/**\r\n * The version of the parser.\r\n * Used to determine whether input files need to be re-parsed.\r\n */\r\nexport const PARSER_VERSION = 3;\r\n\r\n/**\r\n * Defines a class that is able to parse USX content.\r\n */\r\nexport class USXParser {\r\n private _domParser: DOMParser;\r\n private _noteCounter: number = 0;\r\n private _messages: ParseMessage[] = [];\r\n private _lastVerse: Verse | null = null;\r\n private _currentChapter: Chapter | null = null;\r\n private _bookId: string | null = null;\r\n\r\n constructor(domParser: DOMParser) {\r\n this._domParser = domParser;\r\n }\r\n\r\n /**\r\n * Parses the specified USX content.\r\n *\r\n * @param usx The USX content to parse.\r\n * @returns The parse tree that was generated.\r\n */\r\n public parse(usx: string): ParseTree {\r\n this._noteCounter = 0;\r\n this._messages = [];\r\n this._lastVerse = null;\r\n this._currentChapter = null;\r\n this._bookId = null;\r\n const parser = this._domParser;\r\n const doc = parser.parseFromString(usx, 'application/xml');\r\n const usxElement = doc.documentElement;\r\n\r\n let root: ParseTree = {\r\n type: 'root',\r\n content: [],\r\n };\r\n\r\n const bookElement = usxElement.querySelector('book[code]');\r\n\r\n if (!bookElement) {\r\n throw new Error('The USX content does not contain a book element.');\r\n }\r\n\r\n const bookCode = bookElement.getAttribute('code') || '';\r\n\r\n if (!bookCode) {\r\n throw new Error(\r\n 'The book element does not contain a code attribute.'\r\n );\r\n }\r\n\r\n this._bookId = root.id = bookCode;\r\n\r\n const header = usxElement.querySelector('para[style=\"h\"]');\r\n if (header) {\r\n root.header = header.textContent || '';\r\n }\r\n\r\n const titles = usxElement.querySelectorAll(\r\n 'para[style=\"mt1\"], para[style=\"mt2\"], para[style=\"mt3\"]'\r\n );\r\n // const title2 = usxElement.querySelector('para[style=\"mt2\"]');\r\n // const title3 = usxElement.querySelector('para[style=\"mt3\"]');\r\n\r\n if (titles.length > 0) {\r\n root.title = [...titles]\r\n .map((t) => t.textContent)\r\n .filter((t) => t)\r\n .join(' ');\r\n }\r\n\r\n for (let content of this.iterateRootContent(usxElement)) {\r\n root.content.push(content);\r\n }\r\n\r\n if (this._messages.length > 0) {\r\n root.parseMessages = this._messages.slice();\r\n }\r\n\r\n return root;\r\n }\r\n\r\n *iterateRootContent(\r\n usxElement: Element\r\n ): Generator<ParseTree['content'][0]> {\r\n const iterator = iterateAll(usxElement);\r\n while (true) {\r\n const { done, value: child } = iterator.next();\r\n if (done) {\r\n break;\r\n }\r\n\r\n if (!(child instanceof Element)) {\r\n continue;\r\n }\r\n\r\n if (child.nodeName === 'chapter') {\r\n if (child.hasAttribute('eid')) {\r\n continue;\r\n }\r\n\r\n const chapter: Chapter = {\r\n type: 'chapter',\r\n number: parseInt(child.getAttribute('number') || '0', 10),\r\n content: [],\r\n footnotes: [],\r\n };\r\n this._currentChapter = chapter;\r\n\r\n for (let content of this.iterateChapterContent(\r\n chapter,\r\n iterator\r\n )) {\r\n chapter.content.push(content);\r\n }\r\n\r\n yield chapter;\r\n } else if (child.nodeName === 'para') {\r\n const style = child.getAttribute('style');\r\n if (\r\n style === 's1' ||\r\n style === 's2' ||\r\n style === 's3' ||\r\n style === 's4'\r\n ) {\r\n yield {\r\n type: 'heading',\r\n content: child.textContent ? [child.textContent] : [],\r\n };\r\n }\r\n }\r\n }\r\n }\r\n\r\n *iterateChapterContent(\r\n chapter: Chapter,\r\n nodes: RewindableIterator<Node>\r\n ): IterableIterator<ChapterContent> {\r\n while (true) {\r\n const { done, value: element } = nodes.next();\r\n if (done) {\r\n break;\r\n }\r\n\r\n if (!(element instanceof Element)) {\r\n continue;\r\n }\r\n\r\n if (element.nodeName === 'chapter') {\r\n break;\r\n } else if (element.nodeName === 'para') {\r\n const style = element.getAttribute('style');\r\n if (\r\n style === 's1' ||\r\n style === 's2' ||\r\n style === 's3' ||\r\n style === 's4'\r\n ) {\r\n yield {\r\n type: 'heading',\r\n content: element.textContent\r\n ? [element.textContent]\r\n : [],\r\n };\r\n } else if (style === 'b') {\r\n yield {\r\n type: 'line_break',\r\n };\r\n } else if (style === 'd') {\r\n yield* this.parseHebrewSubtitle(element, chapter, nodes);\r\n }\r\n } else if (element.nodeName === 'verse') {\r\n if (element.hasAttribute('eid')) {\r\n continue;\r\n }\r\n\r\n yield this.parseVerse(element, chapter, nodes);\r\n }\r\n }\r\n }\r\n\r\n *iterateVerseContent(\r\n chapter: Chapter,\r\n verse: Verse,\r\n nodes: RewindableIterator<Node>,\r\n previousVerse?: Verse | null\r\n ): IterableIterator<string | FootnoteReference | Text | InlineLineBreak> {\r\n let lastParent: Element | null = null;\r\n while (true) {\r\n const { done, value: node } = nodes.next();\r\n if (done) {\r\n break;\r\n }\r\n\r\n if (node.nodeName === 'verse') {\r\n if (!(node instanceof Element) || !node.hasAttribute('eid')) {\r\n nodes.rewind(1);\r\n }\r\n break;\r\n }\r\n\r\n const parent = node.parentElement!;\r\n let poem: number | null = null;\r\n let descriptive: boolean | null = null;\r\n\r\n if (parent.nodeName === 'para') {\r\n const style = parent.getAttribute('style');\r\n if (\r\n style === 'q1' ||\r\n style === 'q2' ||\r\n style === 'q3' ||\r\n style === 'q4'\r\n ) {\r\n poem =\r\n style === 'q1'\r\n ? 1\r\n : style === 'q2'\r\n ? 2\r\n : style === 'q3'\r\n ? 3\r\n : 4;\r\n\r\n // Send explicit line breaks\r\n // if we are in a new paragraph but the previous paragraph had the same poem style\r\n if (parent.previousElementSibling?.nodeName === 'para') {\r\n const previousStyle =\r\n parent.previousElementSibling?.getAttribute(\r\n 'style'\r\n );\r\n if (previousStyle === style && lastParent !== parent) {\r\n lastParent = parent;\r\n\r\n const firstChild = parent.firstElementChild;\r\n const startsWithVerse =\r\n firstChild?.nodeName === 'verse' &&\r\n !firstChild.hasAttribute('eid') &&\r\n parseInt(\r\n firstChild.getAttribute('number') || '0',\r\n 10\r\n ) === verse.number;\r\n\r\n if (\r\n startsWithVerse &&\r\n previousVerse &&\r\n previousVerse.number !== verse.number\r\n ) {\r\n addOrJoin(previousVerse.content, {\r\n lineBreak: true,\r\n });\r\n } else {\r\n yield {\r\n lineBreak: true,\r\n };\r\n }\r\n }\r\n }\r\n } else if (style === 'd') {\r\n descriptive = true;\r\n }\r\n }\r\n\r\n for (let content of this.iterateNodeTextContent(\r\n nodes,\r\n node,\r\n chapter,\r\n verse\r\n )) {\r\n if (poem !== null || descriptive !== null) {\r\n if (typeof content === 'string') {\r\n let text: Text = {\r\n text: content,\r\n };\r\n\r\n if (poem !== null) {\r\n text.poem = poem;\r\n }\r\n\r\n if (descriptive !== null) {\r\n text.descriptive = true;\r\n }\r\n\r\n yield text;\r\n } else {\r\n let text:\r\n | InlineLineBreak\r\n | InlineHeading\r\n | FootnoteReference\r\n | Text = {\r\n ...content,\r\n };\r\n\r\n if ('text' in text) {\r\n if (poem !== null) {\r\n text.poem = poem;\r\n }\r\n\r\n if (descriptive !== null) {\r\n text.descriptive = true;\r\n }\r\n }\r\n yield text;\r\n }\r\n } else {\r\n yield content;\r\n }\r\n }\r\n }\r\n }\r\n\r\n parseVerse(\r\n element: Element,\r\n chapter: Chapter,\r\n nodes: RewindableIterator<Node>\r\n ): Verse {\r\n const verse: Verse = {\r\n type: 'verse',\r\n number: parseInt(element.getAttribute('number') || '0', 10),\r\n content: [],\r\n };\r\n\r\n if (this._currentChapter && this._lastVerse) {\r\n if (this._currentChapter.number === chapter.number) {\r\n // We're parsing the same chapter as the last one\r\n const delta = verse.number - this._lastVerse.number;\r\n if (delta > 1) {\r\n // Delta is greater than 1, so we might have missed some verses\r\n for (\r\n let v = this._lastVerse.number + 1;\r\n v < verse.number;\r\n v++\r\n ) {\r\n let isExpected = false;\r\n if (this._bookId) {\r\n const missingVerse = `${this._bookId} ${chapter.number}:${v}`;\r\n if (KNOWN_SKIPPED_VERSES.has(missingVerse)) {\r\n isExpected = true;\r\n }\r\n }\r\n\r\n if (!isExpected) {\r\n this._messages.push({\r\n type: 'warning',\r\n message: `Verse ${this._bookId ?? '(null)'} ${chapter.number}:${v} is missing.`,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n }\r\n const previousVerse =\r\n this._currentChapter?.number === chapter.number\r\n ? this._lastVerse\r\n : null;\r\n\r\n for (let content of this.iterateVerseContent(\r\n chapter,\r\n verse,\r\n nodes,\r\n previousVerse\r\n )) {\r\n addOrJoin(verse.content, content);\r\n }\r\n\r\n trimContent(verse.content);\r\n this._lastVerse = verse;\r\n return verse;\r\n }\r\n\r\n *parseHebrewSubtitle(\r\n para: Element,\r\n chapter: Chapter,\r\n nodes: RewindableIterator<Node>\r\n ): IterableIterator<HebrewSubtitle | Verse> {\r\n const subtitle: HebrewSubtitle = {\r\n type: 'hebrew_subtitle',\r\n content: [],\r\n };\r\n\r\n for (let content of this.iterateHebrewSubtitleContent(\r\n para,\r\n chapter,\r\n nodes\r\n )) {\r\n if (typeof content === 'object' && 'number' in content) {\r\n yield content;\r\n continue;\r\n }\r\n addOrJoin(subtitle.content, content);\r\n }\r\n\r\n trimContent(subtitle.content);\r\n if (subtitle.content.length > 0) {\r\n yield subtitle;\r\n }\r\n }\r\n\r\n *iterateHebrewSubtitleContent(\r\n element: Element,\r\n chapter: Chapter,\r\n nodes: RewindableIterator<Node>\r\n ): IterableIterator<\r\n Verse | string | Text | FootnoteReference | InlineLineBreak\r\n > {\r\n while (true) {\r\n const { done, value: node } = nodes.next();\r\n if (done) {\r\n break;\r\n }\r\n\r\n if (!isParent(node, element)) {\r\n nodes.rewind(1);\r\n break;\r\n }\r\n\r\n if (node instanceof Element && node.nodeName === 'verse') {\r\n yield this.parseVerse(node, chapter, nodes);\r\n } else {\r\n yield* this.iterateNodeTextContent(nodes, node, chapter);\r\n }\r\n }\r\n }\r\n\r\n *iterateNodeTextContent(\r\n nodes: RewindableIterator<Node>,\r\n node: Node,\r\n chapter: Chapter,\r\n verse?: Verse\r\n ): IterableIterator<string | Text | FootnoteReference | InlineLineBreak> {\r\n if (node instanceof Element && node.nodeName === 'note') {\r\n yield* this.iterateNote(nodes, node, chapter, verse);\r\n } else if (node instanceof Element && node.nodeName === 'char') {\r\n yield* this.iterateChar(nodes, node);\r\n } else if (\r\n node instanceof Element &&\r\n node.nodeName === 'para' &&\r\n node.getAttribute('style') === 'b'\r\n ) {\r\n for (let _ of children(nodes, node)) {\r\n // iterate through all the children to prevent iterating over them multiple times\r\n }\r\n yield {\r\n lineBreak: true,\r\n };\r\n } else if (node.nodeType === NodeType.Text) {\r\n yield node.textContent || '';\r\n }\r\n }\r\n\r\n *iterateCharContent(char: Element): IterableIterator<string | Text> {\r\n const style = char.getAttribute('style');\r\n const text = trimText(char.textContent || '');\r\n if (style === 'wj') {\r\n yield {\r\n text,\r\n wordsOfJesus: true,\r\n };\r\n } else {\r\n yield text;\r\n }\r\n }\r\n\r\n *iterateNote(\r\n nodes: RewindableIterator<Node>,\r\n node: Element,\r\n chapter: Chapter,\r\n verse?: Verse\r\n ): IterableIterator<FootnoteReference> {\r\n const style = node.getAttribute('style');\r\n if (style === 'f') {\r\n const verseReferenceRegex = /^[0-9]{1,3}:[0-9]{1,3}/;\r\n\r\n let text = '';\r\n for (let child of children(nodes, node)) {\r\n if (child.nodeType === NodeType.Text) {\r\n text += child.textContent || '';\r\n }\r\n }\r\n\r\n text = text.trim();\r\n if (verseReferenceRegex.test(text)) {\r\n text = text.replace(verseReferenceRegex, '').trim();\r\n }\r\n\r\n const note: Footnote = {\r\n noteId: this._noteCounter++,\r\n caller: node.getAttribute('caller') || null,\r\n text,\r\n reference: {\r\n chapter: chapter.number,\r\n verse: verse?.number ?? 0,\r\n },\r\n };\r\n\r\n chapter.footnotes.push(note);\r\n\r\n yield {\r\n noteId: note.noteId,\r\n };\r\n } else {\r\n for (let _ of children(nodes, node)) {\r\n // iterate through all the children\r\n // so that we don't end up with duplicates\r\n }\r\n }\r\n }\r\n\r\n *iterateChar(\r\n nodes: RewindableIterator<Node>,\r\n node: Element\r\n ): IterableIterator<string | Text> {\r\n const style = node.getAttribute('style');\r\n let text = '';\r\n\r\n for (let char of children(nodes, node)) {\r\n if (char.nodeType === NodeType.Text) {\r\n text += char.textContent || '';\r\n }\r\n }\r\n\r\n if (style === 'wj') {\r\n yield {\r\n text,\r\n wordsOfJesus: true,\r\n };\r\n } else {\r\n yield text;\r\n }\r\n\r\n // if (!parentChar(node) && !parentNote(node)) {\r\n // yield *iterateCharContent(node);\r\n // }\r\n }\r\n}\r\n\r\n// Taken from https://github.com/gracious-tech/fetch/blob/1576cc4eafb32bf347a09332094cf17c2231c90c/converters/usx-to-json/src/elements.ts#L16\r\nconst ignoredParaStyles = new Set([\r\n // <para> Identification [exclude all] - Running headings & table of contents\r\n 'ide', // See https://github.com/schierlm/BibleMultiConverter/issues/67\r\n 'rem', // Remarks (valid in schema though missed in docs)\r\n 'h',\r\n 'h1',\r\n 'h2',\r\n 'h3',\r\n 'h4',\r\n 'toc1',\r\n 'toc2',\r\n 'toc3',\r\n 'toca1',\r\n 'toca2',\r\n 'toca3',\r\n\r\n /* <para> Introductions [exclude all] - Introductionary (non-biblical) content\r\n Which might be helpful in a printed book, but intro material in apps is usually bad UX,\r\n and users that really care can research a translations methodology themselves\r\n */\r\n 'imt',\r\n 'imt1',\r\n 'imt2',\r\n 'imt3',\r\n 'imt4',\r\n 'is',\r\n 'is1',\r\n 'is2',\r\n 'is3',\r\n 'is4',\r\n 'ip',\r\n 'ipi',\r\n 'im',\r\n 'imi',\r\n 'ipq',\r\n 'imq',\r\n 'ipr',\r\n 'iq',\r\n 'iq1',\r\n 'iq2',\r\n 'iq3',\r\n 'iq4',\r\n 'ib',\r\n 'ili',\r\n 'ili1',\r\n 'ili2',\r\n 'ili3',\r\n 'ili4',\r\n 'iot',\r\n 'io',\r\n 'io1',\r\n 'io2',\r\n 'io3',\r\n 'io4',\r\n 'iex',\r\n 'imte',\r\n 'ie',\r\n\r\n /* <para> Headings [exclude some] - Exclude book & chapter headings but keep section headings\r\n Not excluded: ms# | mr | s# | sr | d | sp | sd#\r\n */\r\n 'mt',\r\n 'mt1',\r\n 'mt2',\r\n 'mt3',\r\n 'mt4',\r\n 'mte',\r\n 'mte1',\r\n 'mte2',\r\n 'mte3',\r\n 'mte4',\r\n 'cl',\r\n 'cd', // Non-biblical chapter summary, more than heading\r\n 'r', // Parallels to be provided by external data\r\n]);\r\n\r\nfunction* iterateCharContent(char: Element): IterableIterator<string | Text> {\r\n const style = char.getAttribute('style');\r\n const text = trimText(char.textContent || '');\r\n if (style === 'wj') {\r\n yield {\r\n text,\r\n wordsOfJesus: true,\r\n };\r\n } else {\r\n yield text;\r\n }\r\n}\r\n\r\nfunction trimText(text: string): string {\r\n return text.replace(/\\s+/g, ' ');\r\n}\r\n\r\nfunction trimContent<T extends string | unknown>(content: T[]): T[] {\r\n for (let i = 0; i < content.length; i++) {\r\n const value = content[i];\r\n if (typeof value === 'string') {\r\n content[i] = trimText(value as string).trim() as T;\r\n if (content[i] === '') {\r\n content.splice(i, 1);\r\n i--;\r\n continue;\r\n }\r\n } else if (isVerseText(value)) {\r\n value.text = trimText(value.text).trim();\r\n if (value.text === '') {\r\n content.splice(i, 1);\r\n i--;\r\n continue;\r\n }\r\n }\r\n }\r\n return content;\r\n}\r\n\r\nfunction addOrJoin(array: (string | unknown)[], value: string | unknown) {\r\n if (array.length === 0) {\r\n array.push(value);\r\n } else {\r\n const last = array[array.length - 1];\r\n if (typeof last === 'string' && typeof value === 'string') {\r\n array[array.length - 1] = last + value;\r\n } else if (\r\n isVerseText(last) &&\r\n isVerseText(value) &&\r\n hasSameFormatting(last, value)\r\n ) {\r\n last.text += value.text;\r\n } else {\r\n array.push(value);\r\n }\r\n }\r\n}\r\n\r\nfunction isVerseText(value: unknown): value is Text {\r\n return typeof value === 'object' && value !== null && 'text' in value;\r\n}\r\n\r\nfunction hasSameFormatting(a: Text, b: Text): boolean {\r\n return a.poem === b.poem && a.wordsOfJesus === b.wordsOfJesus;\r\n}\r\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import {\r\n Chapter,\r\n ChapterContent,\r\n ChapterWord,\r\n Footnote,\r\n FootnoteReference,\r\n ParseTree,\r\n Verse,\r\n Text,\r\n HebrewSubtitle,\r\n InlineLineBreak,\r\n InlineHeading,\r\n ParseMessage,\r\n} from './types.js';\r\nimport {\r\n iterateAll,\r\n children,\r\n RewindableIterator,\r\n isParent,\r\n} from './iterators.js';\r\nimport {\r\n AnnotatedContent,\r\n WordAnnotations,\r\n WordRange,\r\n addChapterWords,\r\n collapseWhitespaceMap,\r\n isAnnotatedContent,\r\n readWordAnnotations,\r\n remapChapterWords,\r\n removeChapterWordsForContent,\r\n trimWordRange,\r\n} from './words.js';\r\nimport { KNOWN_SKIPPED_VERSES } from '../utils.js';\r\n\r\nenum NodeType {\r\n Text = 3,\r\n}\r\n\r\n/**\r\n * The version of the parser.\r\n * Used to determine whether input files need to be re-parsed.\r\n */\r\nexport const PARSER_VERSION = 4;\r\n\r\n/**\r\n * Defines a class that is able to parse USX content.\r\n */\r\nexport class USXParser {\r\n private _domParser: DOMParser;\r\n private _noteCounter: number = 0;\r\n private _messages: ParseMessage[] = [];\r\n private _lastVerse: Verse | null = null;\r\n private _currentChapter: Chapter | null = null;\r\n private _bookId: string | null = null;\r\n\r\n constructor(domParser: DOMParser) {\r\n this._domParser = domParser;\r\n }\r\n\r\n /**\r\n * Parses the specified USX content.\r\n *\r\n * @param usx The USX content to parse.\r\n * @returns The parse tree that was generated.\r\n */\r\n public parse(usx: string): ParseTree {\r\n this._noteCounter = 0;\r\n this._messages = [];\r\n this._lastVerse = null;\r\n this._currentChapter = null;\r\n this._bookId = null;\r\n const parser = this._domParser;\r\n const doc = parser.parseFromString(usx, 'application/xml');\r\n const usxElement = doc.documentElement;\r\n\r\n let root: ParseTree = {\r\n type: 'root',\r\n content: [],\r\n };\r\n\r\n const bookElement = usxElement.querySelector('book[code]');\r\n\r\n if (!bookElement) {\r\n throw new Error('The USX content does not contain a book element.');\r\n }\r\n\r\n const bookCode = bookElement.getAttribute('code') || '';\r\n\r\n if (!bookCode) {\r\n throw new Error(\r\n 'The book element does not contain a code attribute.'\r\n );\r\n }\r\n\r\n this._bookId = root.id = bookCode;\r\n\r\n const header = usxElement.querySelector('para[style=\"h\"]');\r\n if (header) {\r\n root.header = header.textContent || '';\r\n }\r\n\r\n const titles = usxElement.querySelectorAll(\r\n 'para[style=\"mt1\"], para[style=\"mt2\"], para[style=\"mt3\"]'\r\n );\r\n // const title2 = usxElement.querySelector('para[style=\"mt2\"]');\r\n // const title3 = usxElement.querySelector('para[style=\"mt3\"]');\r\n\r\n if (titles.length > 0) {\r\n root.title = [...titles]\r\n .map((t) => t.textContent)\r\n .filter((t) => t)\r\n .join(' ');\r\n }\r\n\r\n for (let content of this.iterateRootContent(usxElement)) {\r\n root.content.push(content);\r\n }\r\n\r\n if (this._messages.length > 0) {\r\n root.parseMessages = this._messages.slice();\r\n }\r\n\r\n return root;\r\n }\r\n\r\n *iterateRootContent(\r\n usxElement: Element\r\n ): Generator<ParseTree['content'][0]> {\r\n const iterator = iterateAll(usxElement);\r\n while (true) {\r\n const { done, value: child } = iterator.next();\r\n if (done) {\r\n break;\r\n }\r\n\r\n if (!(child instanceof Element)) {\r\n continue;\r\n }\r\n\r\n if (child.nodeName === 'chapter') {\r\n if (child.hasAttribute('eid')) {\r\n continue;\r\n }\r\n\r\n const chapter: Chapter = {\r\n type: 'chapter',\r\n number: parseInt(child.getAttribute('number') || '0', 10),\r\n content: [],\r\n footnotes: [],\r\n };\r\n this._currentChapter = chapter;\r\n\r\n for (let content of this.iterateChapterContent(\r\n chapter,\r\n iterator\r\n )) {\r\n chapter.content.push(content);\r\n }\r\n\r\n yield chapter;\r\n } else if (child.nodeName === 'para') {\r\n const style = child.getAttribute('style');\r\n if (\r\n style === 's1' ||\r\n style === 's2' ||\r\n style === 's3' ||\r\n style === 's4'\r\n ) {\r\n yield {\r\n type: 'heading',\r\n content: child.textContent ? [child.textContent] : [],\r\n };\r\n }\r\n }\r\n }\r\n }\r\n\r\n *iterateChapterContent(\r\n chapter: Chapter,\r\n nodes: RewindableIterator<Node>\r\n ): IterableIterator<ChapterContent> {\r\n while (true) {\r\n const { done, value: element } = nodes.next();\r\n if (done) {\r\n break;\r\n }\r\n\r\n if (!(element instanceof Element)) {\r\n continue;\r\n }\r\n\r\n if (element.nodeName === 'chapter') {\r\n break;\r\n } else if (element.nodeName === 'para') {\r\n const style = element.getAttribute('style');\r\n if (\r\n style === 's1' ||\r\n style === 's2' ||\r\n style === 's3' ||\r\n style === 's4'\r\n ) {\r\n yield {\r\n type: 'heading',\r\n content: element.textContent\r\n ? [element.textContent]\r\n : [],\r\n };\r\n } else if (style === 'b') {\r\n yield {\r\n type: 'line_break',\r\n };\r\n } else if (style === 'd') {\r\n yield* this.parseHebrewSubtitle(element, chapter, nodes);\r\n }\r\n } else if (element.nodeName === 'verse') {\r\n if (element.hasAttribute('eid')) {\r\n continue;\r\n }\r\n\r\n yield this.parseVerse(element, chapter, nodes);\r\n }\r\n }\r\n }\r\n\r\n *iterateVerseContent(\r\n chapter: Chapter,\r\n verse: Verse,\r\n nodes: RewindableIterator<Node>,\r\n previousVerse?: Verse | null\r\n ): IterableIterator<\r\n | string\r\n | FootnoteReference\r\n | Text\r\n | InlineLineBreak\r\n | AnnotatedContent<string | Text>\r\n > {\r\n let lastParent: Element | null = null;\r\n while (true) {\r\n const { done, value: node } = nodes.next();\r\n if (done) {\r\n break;\r\n }\r\n\r\n if (node.nodeName === 'verse') {\r\n if (!(node instanceof Element) || !node.hasAttribute('eid')) {\r\n nodes.rewind(1);\r\n }\r\n break;\r\n }\r\n\r\n const parent = node.parentElement!;\r\n let poem: number | null = null;\r\n let descriptive: boolean | null = null;\r\n\r\n if (parent.nodeName === 'para') {\r\n const style = parent.getAttribute('style');\r\n if (\r\n style === 'q1' ||\r\n style === 'q2' ||\r\n style === 'q3' ||\r\n style === 'q4'\r\n ) {\r\n poem =\r\n style === 'q1'\r\n ? 1\r\n : style === 'q2'\r\n ? 2\r\n : style === 'q3'\r\n ? 3\r\n : 4;\r\n\r\n // Send explicit line breaks\r\n // if we are in a new paragraph but the previous paragraph had the same poem style\r\n if (parent.previousElementSibling?.nodeName === 'para') {\r\n const previousStyle =\r\n parent.previousElementSibling?.getAttribute(\r\n 'style'\r\n );\r\n if (previousStyle === style && lastParent !== parent) {\r\n lastParent = parent;\r\n\r\n const firstChild = parent.firstElementChild;\r\n const startsWithVerse =\r\n firstChild?.nodeName === 'verse' &&\r\n !firstChild.hasAttribute('eid') &&\r\n parseInt(\r\n firstChild.getAttribute('number') || '0',\r\n 10\r\n ) === verse.number;\r\n\r\n if (\r\n startsWithVerse &&\r\n previousVerse &&\r\n previousVerse.number !== verse.number\r\n ) {\r\n addOrJoin(previousVerse.content, {\r\n lineBreak: true,\r\n });\r\n } else {\r\n yield {\r\n lineBreak: true,\r\n };\r\n }\r\n }\r\n }\r\n } else if (style === 'd') {\r\n descriptive = true;\r\n }\r\n }\r\n\r\n for (let value of this.iterateNodeTextContent(\r\n nodes,\r\n node,\r\n chapter,\r\n verse\r\n )) {\r\n // Word annotations travel alongside the content that they apply\r\n // to. Unwrap them so that the content is formatted exactly the\r\n // same as content that has no annotations, then re-attach them.\r\n const words = isAnnotatedContent(value) ? value.words : null;\r\n const content = isAnnotatedContent(value)\r\n ? (value.annotatedContent as string | Text)\r\n : value;\r\n\r\n let result:\r\n | string\r\n | InlineLineBreak\r\n | InlineHeading\r\n | FootnoteReference\r\n | Text = content;\r\n\r\n if (poem !== null || descriptive !== null) {\r\n if (typeof content === 'string') {\r\n let text: Text = {\r\n text: content,\r\n };\r\n\r\n if (poem !== null) {\r\n text.poem = poem;\r\n }\r\n\r\n if (descriptive !== null) {\r\n text.descriptive = true;\r\n }\r\n\r\n result = text;\r\n } else {\r\n let text:\r\n | InlineLineBreak\r\n | InlineHeading\r\n | FootnoteReference\r\n | Text = {\r\n ...content,\r\n };\r\n\r\n if ('text' in text) {\r\n if (poem !== null) {\r\n text.poem = poem;\r\n }\r\n\r\n if (descriptive !== null) {\r\n text.descriptive = true;\r\n }\r\n }\r\n result = text;\r\n }\r\n }\r\n\r\n if (words) {\r\n yield {\r\n annotatedContent: result as string | Text,\r\n words,\r\n };\r\n } else {\r\n yield result;\r\n }\r\n }\r\n }\r\n }\r\n\r\n parseVerse(\r\n element: Element,\r\n chapter: Chapter,\r\n nodes: RewindableIterator<Node>\r\n ): Verse {\r\n const verse: Verse = {\r\n type: 'verse',\r\n number: parseInt(element.getAttribute('number') || '0', 10),\r\n content: [],\r\n };\r\n\r\n if (this._currentChapter && this._lastVerse) {\r\n if (this._currentChapter.number === chapter.number) {\r\n // We're parsing the same chapter as the last one\r\n const delta = verse.number - this._lastVerse.number;\r\n if (delta > 1) {\r\n // Delta is greater than 1, so we might have missed some verses\r\n for (\r\n let v = this._lastVerse.number + 1;\r\n v < verse.number;\r\n v++\r\n ) {\r\n let isExpected = false;\r\n if (this._bookId) {\r\n const missingVerse = `${this._bookId} ${chapter.number}:${v}`;\r\n if (KNOWN_SKIPPED_VERSES.has(missingVerse)) {\r\n isExpected = true;\r\n }\r\n }\r\n\r\n if (!isExpected) {\r\n this._messages.push({\r\n type: 'warning',\r\n message: `Verse ${this._bookId ?? '(null)'} ${chapter.number}:${v} is missing.`,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n }\r\n const previousVerse =\r\n this._currentChapter?.number === chapter.number\r\n ? this._lastVerse\r\n : null;\r\n\r\n const words: ChapterWord[] = [];\r\n\r\n for (let content of this.iterateVerseContent(\r\n chapter,\r\n verse,\r\n nodes,\r\n previousVerse\r\n )) {\r\n if (isAnnotatedContent(content)) {\r\n addOrJoin(\r\n verse.content,\r\n content.annotatedContent,\r\n content.words,\r\n words\r\n );\r\n } else {\r\n addOrJoin(verse.content, content);\r\n }\r\n }\r\n\r\n trimContent(verse.content, words);\r\n addChapterWords(chapter, verse.number, words);\r\n this._lastVerse = verse;\r\n return verse;\r\n }\r\n\r\n *parseHebrewSubtitle(\r\n para: Element,\r\n chapter: Chapter,\r\n nodes: RewindableIterator<Node>\r\n ): IterableIterator<HebrewSubtitle | Verse> {\r\n const subtitle: HebrewSubtitle = {\r\n type: 'hebrew_subtitle',\r\n content: [],\r\n };\r\n\r\n for (let content of this.iterateHebrewSubtitleContent(\r\n para,\r\n chapter,\r\n nodes\r\n )) {\r\n if (typeof content === 'object' && 'number' in content) {\r\n yield content;\r\n continue;\r\n }\r\n\r\n // Hebrew subtitles aren't verses, so there is nowhere to key word\r\n // annotations by. Keep the content and drop the annotations.\r\n addOrJoin(\r\n subtitle.content,\r\n isAnnotatedContent(content) ? content.annotatedContent : content\r\n );\r\n }\r\n\r\n trimContent(subtitle.content);\r\n if (subtitle.content.length > 0) {\r\n yield subtitle;\r\n }\r\n }\r\n\r\n *iterateHebrewSubtitleContent(\r\n element: Element,\r\n chapter: Chapter,\r\n nodes: RewindableIterator<Node>\r\n ): IterableIterator<\r\n | Verse\r\n | string\r\n | Text\r\n | FootnoteReference\r\n | InlineLineBreak\r\n | AnnotatedContent<string | Text>\r\n > {\r\n while (true) {\r\n const { done, value: node } = nodes.next();\r\n if (done) {\r\n break;\r\n }\r\n\r\n if (!isParent(node, element)) {\r\n nodes.rewind(1);\r\n break;\r\n }\r\n\r\n if (node instanceof Element && node.nodeName === 'verse') {\r\n yield this.parseVerse(node, chapter, nodes);\r\n } else {\r\n yield* this.iterateNodeTextContent(nodes, node, chapter);\r\n }\r\n }\r\n }\r\n\r\n *iterateNodeTextContent(\r\n nodes: RewindableIterator<Node>,\r\n node: Node,\r\n chapter: Chapter,\r\n verse?: Verse\r\n ): IterableIterator<\r\n | string\r\n | Text\r\n | FootnoteReference\r\n | InlineLineBreak\r\n | AnnotatedContent<string | Text>\r\n > {\r\n if (node instanceof Element && node.nodeName === 'note') {\r\n yield* this.iterateNote(nodes, node, chapter, verse);\r\n } else if (node instanceof Element && node.nodeName === 'char') {\r\n yield* this.iterateChar(nodes, node);\r\n } else if (\r\n node instanceof Element &&\r\n node.nodeName === 'para' &&\r\n node.getAttribute('style') === 'b'\r\n ) {\r\n for (let _ of children(nodes, node)) {\r\n // iterate through all the children to prevent iterating over them multiple times\r\n }\r\n yield {\r\n lineBreak: true,\r\n };\r\n } else if (node.nodeType === NodeType.Text) {\r\n yield node.textContent || '';\r\n }\r\n }\r\n\r\n *iterateCharContent(char: Element): IterableIterator<string | Text> {\r\n const style = char.getAttribute('style');\r\n const text = trimText(char.textContent || '');\r\n if (style === 'wj') {\r\n yield {\r\n text,\r\n wordsOfJesus: true,\r\n };\r\n } else {\r\n yield text;\r\n }\r\n }\r\n\r\n *iterateNote(\r\n nodes: RewindableIterator<Node>,\r\n node: Element,\r\n chapter: Chapter,\r\n verse?: Verse\r\n ): IterableIterator<FootnoteReference> {\r\n const style = node.getAttribute('style');\r\n if (style === 'f') {\r\n const verseReferenceRegex = /^[0-9]{1,3}:[0-9]{1,3}/;\r\n\r\n let text = '';\r\n for (let child of children(nodes, node)) {\r\n if (child.nodeType === NodeType.Text) {\r\n text += child.textContent || '';\r\n }\r\n }\r\n\r\n text = text.trim();\r\n if (verseReferenceRegex.test(text)) {\r\n text = text.replace(verseReferenceRegex, '').trim();\r\n }\r\n\r\n const note: Footnote = {\r\n noteId: this._noteCounter++,\r\n caller: node.getAttribute('caller') || null,\r\n text,\r\n reference: {\r\n chapter: chapter.number,\r\n verse: verse?.number ?? 0,\r\n },\r\n };\r\n\r\n chapter.footnotes.push(note);\r\n\r\n yield {\r\n noteId: note.noteId,\r\n };\r\n } else {\r\n for (let _ of children(nodes, node)) {\r\n // iterate through all the children\r\n // so that we don't end up with duplicates\r\n }\r\n }\r\n }\r\n\r\n *iterateChar(\r\n nodes: RewindableIterator<Node>,\r\n node: Element\r\n ): IterableIterator<string | Text | AnnotatedContent<string | Text>> {\r\n const style = node.getAttribute('style');\r\n let text = '';\r\n\r\n // The text of every word element that is contained in this element,\r\n // including the element itself. Words can be nested inside other chars\r\n // (words of Jesus, in particular), so the ranges are tracked while the\r\n // text is being accumulated.\r\n const words: WordRange[] = [];\r\n let currentWord: Element | null = null;\r\n let currentAnnotations: WordAnnotations | null = null;\r\n let currentStart = 0;\r\n\r\n const endCurrentWord = () => {\r\n if (currentAnnotations) {\r\n const range = trimWordRange(text, currentStart, text.length);\r\n if (range) {\r\n words.push({\r\n ...range,\r\n annotations: currentAnnotations,\r\n });\r\n }\r\n }\r\n currentWord = null;\r\n currentAnnotations = null;\r\n };\r\n\r\n for (let char of children(nodes, node)) {\r\n if (char.nodeType !== NodeType.Text) {\r\n continue;\r\n }\r\n\r\n const word = wordElementFor(char, node);\r\n if (word !== currentWord) {\r\n endCurrentWord();\r\n currentWord = word;\r\n currentStart = text.length;\r\n currentAnnotations = word ? readWordAnnotations(word) : null;\r\n }\r\n\r\n text += char.textContent || '';\r\n }\r\n endCurrentWord();\r\n\r\n const content: string | Text =\r\n style === 'wj'\r\n ? {\r\n text,\r\n wordsOfJesus: true,\r\n }\r\n : text;\r\n\r\n if (words.length > 0) {\r\n yield {\r\n annotatedContent: content,\r\n words,\r\n };\r\n } else {\r\n yield content;\r\n }\r\n\r\n // if (!parentChar(node) && !parentNote(node)) {\r\n // yield *iterateCharContent(node);\r\n // }\r\n }\r\n}\r\n\r\n// Taken from https://github.com/gracious-tech/fetch/blob/1576cc4eafb32bf347a09332094cf17c2231c90c/converters/usx-to-json/src/elements.ts#L16\r\nconst ignoredParaStyles = new Set([\r\n // <para> Identification [exclude all] - Running headings & table of contents\r\n 'ide', // See https://github.com/schierlm/BibleMultiConverter/issues/67\r\n 'rem', // Remarks (valid in schema though missed in docs)\r\n 'h',\r\n 'h1',\r\n 'h2',\r\n 'h3',\r\n 'h4',\r\n 'toc1',\r\n 'toc2',\r\n 'toc3',\r\n 'toca1',\r\n 'toca2',\r\n 'toca3',\r\n\r\n /* <para> Introductions [exclude all] - Introductionary (non-biblical) content\r\n Which might be helpful in a printed book, but intro material in apps is usually bad UX,\r\n and users that really care can research a translations methodology themselves\r\n */\r\n 'imt',\r\n 'imt1',\r\n 'imt2',\r\n 'imt3',\r\n 'imt4',\r\n 'is',\r\n 'is1',\r\n 'is2',\r\n 'is3',\r\n 'is4',\r\n 'ip',\r\n 'ipi',\r\n 'im',\r\n 'imi',\r\n 'ipq',\r\n 'imq',\r\n 'ipr',\r\n 'iq',\r\n 'iq1',\r\n 'iq2',\r\n 'iq3',\r\n 'iq4',\r\n 'ib',\r\n 'ili',\r\n 'ili1',\r\n 'ili2',\r\n 'ili3',\r\n 'ili4',\r\n 'iot',\r\n 'io',\r\n 'io1',\r\n 'io2',\r\n 'io3',\r\n 'io4',\r\n 'iex',\r\n 'imte',\r\n 'ie',\r\n\r\n /* <para> Headings [exclude some] - Exclude book & chapter headings but keep section headings\r\n Not excluded: ms# | mr | s# | sr | d | sp | sd#\r\n */\r\n 'mt',\r\n 'mt1',\r\n 'mt2',\r\n 'mt3',\r\n 'mt4',\r\n 'mte',\r\n 'mte1',\r\n 'mte2',\r\n 'mte3',\r\n 'mte4',\r\n 'cl',\r\n 'cd', // Non-biblical chapter summary, more than heading\r\n 'r', // Parallels to be provided by external data\r\n]);\r\n\r\nfunction* iterateCharContent(char: Element): IterableIterator<string | Text> {\r\n const style = char.getAttribute('style');\r\n const text = trimText(char.textContent || '');\r\n if (style === 'wj') {\r\n yield {\r\n text,\r\n wordsOfJesus: true,\r\n };\r\n } else {\r\n yield text;\r\n }\r\n}\r\n\r\nfunction trimText(text: string): string {\r\n return text.replace(/\\s+/g, ' ');\r\n}\r\n\r\n/**\r\n * Gets the word element that the given node is contained in, or null if it\r\n * isn't contained in one. Never looks above the given root element.\r\n * @param node The node to find the word element for.\r\n * @param root The element to stop the search at.\r\n */\r\nfunction wordElementFor(node: Node, root: Element): Element | null {\r\n let element: Element | null = node.parentElement;\r\n while (element) {\r\n if (\r\n element.nodeName === 'char' &&\r\n element.getAttribute('style') === 'w'\r\n ) {\r\n return element;\r\n }\r\n if (element === root) {\r\n return null;\r\n }\r\n element = element.parentElement;\r\n }\r\n return null;\r\n}\r\n\r\nfunction trimContent<T extends string | unknown>(\r\n content: T[],\r\n words?: ChapterWord[] | null\r\n): T[] {\r\n for (let i = 0; i < content.length; i++) {\r\n const value = content[i];\r\n let text: string | null = null;\r\n\r\n if (typeof value === 'string') {\r\n const collapsed = collapseWhitespaceMap(value as string);\r\n content[i] = collapsed.text as T;\r\n text = collapsed.text;\r\n if (words) {\r\n remapChapterWords(words, i, collapsed.map);\r\n }\r\n } else if (isVerseText(value)) {\r\n const collapsed = collapseWhitespaceMap(value.text);\r\n value.text = collapsed.text;\r\n text = collapsed.text;\r\n if (words) {\r\n remapChapterWords(words, i, collapsed.map);\r\n }\r\n }\r\n\r\n if (text === '') {\r\n content.splice(i, 1);\r\n if (words) {\r\n removeChapterWordsForContent(words, i);\r\n }\r\n i--;\r\n continue;\r\n }\r\n }\r\n return content;\r\n}\r\n\r\nfunction addOrJoin(\r\n array: (string | unknown)[],\r\n value: string | unknown,\r\n ranges?: WordRange[] | null,\r\n words?: ChapterWord[] | null\r\n) {\r\n // Where the value ended up in the array, and how far into that item's text\r\n // it starts. Both are needed to anchor the value's word annotations.\r\n let contentIndex: number;\r\n let offset: number;\r\n\r\n if (array.length === 0) {\r\n contentIndex = 0;\r\n offset = 0;\r\n array.push(value);\r\n } else {\r\n const last = array[array.length - 1];\r\n if (typeof last === 'string' && typeof value === 'string') {\r\n contentIndex = array.length - 1;\r\n offset = last.length;\r\n array[array.length - 1] = last + value;\r\n } else if (\r\n isVerseText(last) &&\r\n isVerseText(value) &&\r\n hasSameFormatting(last, value)\r\n ) {\r\n contentIndex = array.length - 1;\r\n offset = last.text.length;\r\n last.text += value.text;\r\n } else {\r\n contentIndex = array.length;\r\n offset = 0;\r\n array.push(value);\r\n }\r\n }\r\n\r\n if (ranges && words) {\r\n for (let range of ranges) {\r\n words.push({\r\n contentIndex,\r\n start: offset + range.start,\r\n end: offset + range.end,\r\n ...range.annotations,\r\n });\r\n }\r\n }\r\n}\r\n\r\nfunction isVerseText(value: unknown): value is Text {\r\n return typeof value === 'object' && value !== null && 'text' in value;\r\n}\r\n\r\nfunction hasSameFormatting(a: Text, b: Text): boolean {\r\n return a.poem === b.poem && a.wordsOfJesus === b.wordsOfJesus;\r\n}\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["NodeType"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
export function isAnnotatedContent(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && "annotatedContent" in value;
|
|
4
|
+
}
|
|
5
|
+
const WHITESPACE = /\s/;
|
|
6
|
+
const OCCURRENCE_ATTRIBUTES = ["x-occurrence", "x-occurence"];
|
|
7
|
+
const OCCURRENCES_ATTRIBUTES = ["x-occurrences", "x-occurences"];
|
|
8
|
+
export function readWordAnnotations(element) {
|
|
9
|
+
const annotations = {};
|
|
10
|
+
let hasAnnotations = false;
|
|
11
|
+
const strong = element.getAttribute("strong");
|
|
12
|
+
if (strong) {
|
|
13
|
+
const strongs = strong.split(",").map((s) => s.trim()).filter((s) => !!s);
|
|
14
|
+
if (strongs.length > 0) {
|
|
15
|
+
annotations.strongs = strongs;
|
|
16
|
+
hasAnnotations = true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const lemma = element.getAttribute("lemma");
|
|
20
|
+
if (lemma) {
|
|
21
|
+
annotations.lemma = lemma;
|
|
22
|
+
hasAnnotations = true;
|
|
23
|
+
}
|
|
24
|
+
const morph = element.getAttribute("x-morph");
|
|
25
|
+
if (morph) {
|
|
26
|
+
annotations.morph = morph;
|
|
27
|
+
hasAnnotations = true;
|
|
28
|
+
}
|
|
29
|
+
const srcloc = element.getAttribute("srcloc");
|
|
30
|
+
if (srcloc) {
|
|
31
|
+
annotations.srcloc = srcloc;
|
|
32
|
+
hasAnnotations = true;
|
|
33
|
+
}
|
|
34
|
+
const occurrence = parseCount(
|
|
35
|
+
firstAttribute(element, OCCURRENCE_ATTRIBUTES)
|
|
36
|
+
);
|
|
37
|
+
if (occurrence !== null) {
|
|
38
|
+
annotations.occurrence = occurrence;
|
|
39
|
+
hasAnnotations = true;
|
|
40
|
+
}
|
|
41
|
+
const occurrences = parseCount(
|
|
42
|
+
firstAttribute(element, OCCURRENCES_ATTRIBUTES)
|
|
43
|
+
);
|
|
44
|
+
if (occurrences !== null) {
|
|
45
|
+
annotations.occurrences = occurrences;
|
|
46
|
+
hasAnnotations = true;
|
|
47
|
+
}
|
|
48
|
+
return hasAnnotations ? annotations : null;
|
|
49
|
+
}
|
|
50
|
+
function firstAttribute(element, names) {
|
|
51
|
+
for (let name of names) {
|
|
52
|
+
const value = element.getAttribute(name);
|
|
53
|
+
if (value) {
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function parseCount(value) {
|
|
60
|
+
if (!value) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const parsed = parseInt(value, 10);
|
|
64
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
65
|
+
}
|
|
66
|
+
export function trimWordRange(text, start, end) {
|
|
67
|
+
let s = Math.max(0, start);
|
|
68
|
+
let e = Math.min(text.length, end);
|
|
69
|
+
while (s < e && WHITESPACE.test(text[s])) {
|
|
70
|
+
s++;
|
|
71
|
+
}
|
|
72
|
+
while (e > s && WHITESPACE.test(text[e - 1])) {
|
|
73
|
+
e--;
|
|
74
|
+
}
|
|
75
|
+
return e > s ? { start: s, end: e } : null;
|
|
76
|
+
}
|
|
77
|
+
export function collapseWhitespaceMap(text) {
|
|
78
|
+
const map = new Array(text.length + 1);
|
|
79
|
+
let collapsed = "";
|
|
80
|
+
let i = 0;
|
|
81
|
+
while (i < text.length) {
|
|
82
|
+
if (WHITESPACE.test(text[i])) {
|
|
83
|
+
const position = collapsed.length;
|
|
84
|
+
while (i < text.length && WHITESPACE.test(text[i])) {
|
|
85
|
+
map[i] = position;
|
|
86
|
+
i++;
|
|
87
|
+
}
|
|
88
|
+
collapsed += " ";
|
|
89
|
+
} else {
|
|
90
|
+
map[i] = collapsed.length;
|
|
91
|
+
collapsed += text[i];
|
|
92
|
+
i++;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
map[text.length] = collapsed.length;
|
|
96
|
+
const trimmed = collapsed.trim();
|
|
97
|
+
const offset = collapsed.length - collapsed.trimStart().length;
|
|
98
|
+
for (let j = 0; j < map.length; j++) {
|
|
99
|
+
map[j] = Math.min(Math.max(map[j] - offset, 0), trimmed.length);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
text: trimmed,
|
|
103
|
+
map
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
export function remapChapterWords(words, contentIndex, map) {
|
|
107
|
+
for (let i = 0; i < words.length; i++) {
|
|
108
|
+
const word = words[i];
|
|
109
|
+
if (word.contentIndex !== contentIndex) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const start = map[Math.min(word.start, map.length - 1)];
|
|
113
|
+
const end = map[Math.min(word.end, map.length - 1)];
|
|
114
|
+
if (end > start) {
|
|
115
|
+
word.start = start;
|
|
116
|
+
word.end = end;
|
|
117
|
+
} else {
|
|
118
|
+
words.splice(i, 1);
|
|
119
|
+
i--;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
export function removeChapterWordsForContent(words, contentIndex) {
|
|
124
|
+
for (let i = 0; i < words.length; i++) {
|
|
125
|
+
const word = words[i];
|
|
126
|
+
if (word.contentIndex === contentIndex) {
|
|
127
|
+
words.splice(i, 1);
|
|
128
|
+
i--;
|
|
129
|
+
} else if (word.contentIndex > contentIndex) {
|
|
130
|
+
word.contentIndex--;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export function addChapterWords(chapter, verseNumber, words) {
|
|
135
|
+
if (words.length === 0) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (!chapter.words) {
|
|
139
|
+
chapter.words = {};
|
|
140
|
+
}
|
|
141
|
+
const key = verseNumber.toString();
|
|
142
|
+
const existing = chapter.words[key];
|
|
143
|
+
if (existing) {
|
|
144
|
+
existing.push(...words);
|
|
145
|
+
} else {
|
|
146
|
+
chapter.words[key] = words;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=words.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../parser/words.ts"],
|
|
4
|
+
"sourcesContent": ["import { Chapter, ChapterWord } from './types.js';\r\n\r\n/**\r\n * The annotations that a source associated with a word, without any information\r\n * about where the word is located.\r\n */\r\nexport type WordAnnotations = Omit<\r\n ChapterWord,\r\n 'contentIndex' | 'start' | 'end'\r\n>;\r\n\r\n/**\r\n * Defines an interface that represents a range of characters within a single\r\n * piece of text, along with the annotations that apply to it.\r\n */\r\nexport interface WordRange {\r\n /**\r\n * The index of the first character of the word.\r\n */\r\n start: number;\r\n\r\n /**\r\n * The index after the last character of the word.\r\n */\r\n end: number;\r\n\r\n /**\r\n * The annotations that apply to the word.\r\n */\r\n annotations: WordAnnotations;\r\n}\r\n\r\n/**\r\n * Defines an interface that pairs a piece of verse content with the word\r\n * annotations that apply to it, so that the annotations can be passed along\r\n * with the content while it is being parsed.\r\n *\r\n * The ranges are relative to the text of the content, and are never included in\r\n * the final parse tree.\r\n */\r\nexport interface AnnotatedContent<T> {\r\n /**\r\n * The content that the annotations apply to.\r\n */\r\n annotatedContent: T;\r\n\r\n /**\r\n * The ranges of the content that are annotated.\r\n */\r\n words: WordRange[];\r\n}\r\n\r\n/**\r\n * Determines whether the given value is content that has word annotations\r\n * attached to it.\r\n * @param value The value to test.\r\n */\r\nexport function isAnnotatedContent(\r\n value: unknown\r\n): value is AnnotatedContent<unknown> {\r\n return (\r\n typeof value === 'object' &&\r\n value !== null &&\r\n 'annotatedContent' in value\r\n );\r\n}\r\n\r\nconst WHITESPACE = /\\s/;\r\n\r\n/**\r\n * The attributes that a word's occurrence number can be stored in.\r\n * The misspelled variants are common in real-world files.\r\n */\r\nconst OCCURRENCE_ATTRIBUTES = ['x-occurrence', 'x-occurence'];\r\nconst OCCURRENCES_ATTRIBUTES = ['x-occurrences', 'x-occurences'];\r\n\r\n/**\r\n * Reads the word annotations that are attached to the given element.\r\n * Returns null if the element doesn't have any recognized annotations.\r\n * @param element The element to read the annotations from.\r\n */\r\nexport function readWordAnnotations(element: Element): WordAnnotations | null {\r\n const annotations: WordAnnotations = {};\r\n let hasAnnotations = false;\r\n\r\n const strong = element.getAttribute('strong');\r\n if (strong) {\r\n const strongs = strong\r\n .split(',')\r\n .map((s) => s.trim())\r\n .filter((s) => !!s);\r\n\r\n if (strongs.length > 0) {\r\n annotations.strongs = strongs;\r\n hasAnnotations = true;\r\n }\r\n }\r\n\r\n const lemma = element.getAttribute('lemma');\r\n if (lemma) {\r\n annotations.lemma = lemma;\r\n hasAnnotations = true;\r\n }\r\n\r\n const morph = element.getAttribute('x-morph');\r\n if (morph) {\r\n annotations.morph = morph;\r\n hasAnnotations = true;\r\n }\r\n\r\n const srcloc = element.getAttribute('srcloc');\r\n if (srcloc) {\r\n annotations.srcloc = srcloc;\r\n hasAnnotations = true;\r\n }\r\n\r\n const occurrence = parseCount(\r\n firstAttribute(element, OCCURRENCE_ATTRIBUTES)\r\n );\r\n if (occurrence !== null) {\r\n annotations.occurrence = occurrence;\r\n hasAnnotations = true;\r\n }\r\n\r\n const occurrences = parseCount(\r\n firstAttribute(element, OCCURRENCES_ATTRIBUTES)\r\n );\r\n if (occurrences !== null) {\r\n annotations.occurrences = occurrences;\r\n hasAnnotations = true;\r\n }\r\n\r\n return hasAnnotations ? annotations : null;\r\n}\r\n\r\nfunction firstAttribute(element: Element, names: string[]): string | null {\r\n for (let name of names) {\r\n const value = element.getAttribute(name);\r\n if (value) {\r\n return value;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\nfunction parseCount(value: string | null): number | null {\r\n if (!value) {\r\n return null;\r\n }\r\n const parsed = parseInt(value, 10);\r\n return Number.isNaN(parsed) ? null : parsed;\r\n}\r\n\r\n/**\r\n * Shrinks the given range so that it doesn't include any leading or trailing\r\n * whitespace. Returns null if the range only contains whitespace.\r\n * @param text The text that the range is in.\r\n * @param start The index of the first character of the range.\r\n * @param end The index after the last character of the range.\r\n */\r\nexport function trimWordRange(\r\n text: string,\r\n start: number,\r\n end: number\r\n): { start: number; end: number } | null {\r\n let s = Math.max(0, start);\r\n let e = Math.min(text.length, end);\r\n\r\n while (s < e && WHITESPACE.test(text[s])) {\r\n s++;\r\n }\r\n while (e > s && WHITESPACE.test(text[e - 1])) {\r\n e--;\r\n }\r\n\r\n return e > s ? { start: s, end: e } : null;\r\n}\r\n\r\n/**\r\n * Collapses the whitespace in the given text in the same manner that the\r\n * parsers normalize text (each run of whitespace becomes a single space, and the\r\n * result is trimmed), and returns a map from the indexes in the given text to\r\n * the indexes in the returned text.\r\n *\r\n * The map contains an entry for every index in the given text, plus one for the\r\n * index just past the end of it, so that both ends of a range can be mapped.\r\n * @param text The text to collapse.\r\n */\r\nexport function collapseWhitespaceMap(text: string): {\r\n text: string;\r\n map: number[];\r\n} {\r\n const map = new Array<number>(text.length + 1);\r\n let collapsed = '';\r\n let i = 0;\r\n\r\n while (i < text.length) {\r\n if (WHITESPACE.test(text[i])) {\r\n // Every character of a whitespace run maps to the single space that\r\n // the run collapses into.\r\n const position = collapsed.length;\r\n while (i < text.length && WHITESPACE.test(text[i])) {\r\n map[i] = position;\r\n i++;\r\n }\r\n collapsed += ' ';\r\n } else {\r\n map[i] = collapsed.length;\r\n collapsed += text[i];\r\n i++;\r\n }\r\n }\r\n map[text.length] = collapsed.length;\r\n\r\n // Trimming can only remove the single space that a leading or trailing\r\n // whitespace run was collapsed into, so the whole map shifts by the number\r\n // of characters that were removed from the front.\r\n const trimmed = collapsed.trim();\r\n const offset = collapsed.length - collapsed.trimStart().length;\r\n\r\n for (let j = 0; j < map.length; j++) {\r\n map[j] = Math.min(Math.max(map[j] - offset, 0), trimmed.length);\r\n }\r\n\r\n return {\r\n text: trimmed,\r\n map,\r\n };\r\n}\r\n\r\n/**\r\n * Moves the words that apply to the given content item to the indexes that they\r\n * have after the item's text was collapsed with collapseWhitespaceMap().\r\n * Words that no longer cover any characters are removed.\r\n * @param words The words to remap.\r\n * @param contentIndex The index of the content item that was collapsed.\r\n * @param map The index map that collapseWhitespaceMap() returned.\r\n */\r\nexport function remapChapterWords(\r\n words: ChapterWord[],\r\n contentIndex: number,\r\n map: number[]\r\n): void {\r\n for (let i = 0; i < words.length; i++) {\r\n const word = words[i];\r\n if (word.contentIndex !== contentIndex) {\r\n continue;\r\n }\r\n\r\n const start = map[Math.min(word.start, map.length - 1)];\r\n const end = map[Math.min(word.end, map.length - 1)];\r\n\r\n if (end > start) {\r\n word.start = start;\r\n word.end = end;\r\n } else {\r\n words.splice(i, 1);\r\n i--;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Removes the words that apply to the given content item, and moves the words\r\n * that apply to later items back by one to account for the item being removed.\r\n * @param words The words to update.\r\n * @param contentIndex The index of the content item that was removed.\r\n */\r\nexport function removeChapterWordsForContent(\r\n words: ChapterWord[],\r\n contentIndex: number\r\n): void {\r\n for (let i = 0; i < words.length; i++) {\r\n const word = words[i];\r\n if (word.contentIndex === contentIndex) {\r\n words.splice(i, 1);\r\n i--;\r\n } else if (word.contentIndex > contentIndex) {\r\n word.contentIndex--;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Adds the given words to the chapter for the given verse number.\r\n * Does nothing if there are no words.\r\n * @param chapter The chapter to add the words to.\r\n * @param verseNumber The number of the verse that the words are in.\r\n * @param words The words to add.\r\n */\r\nexport function addChapterWords(\r\n chapter: Chapter,\r\n verseNumber: number,\r\n words: ChapterWord[]\r\n): void {\r\n if (words.length === 0) {\r\n return;\r\n }\r\n\r\n if (!chapter.words) {\r\n chapter.words = {};\r\n }\r\n\r\n const key = verseNumber.toString();\r\n const existing = chapter.words[key];\r\n\r\n if (existing) {\r\n existing.push(...words);\r\n } else {\r\n chapter.words[key] = words;\r\n }\r\n}\r\n"],
|
|
5
|
+
"mappings": ";AAyDO,gBAAS,mBACZ,OACkC;AAClC,SACI,OAAO,UAAU,YACjB,UAAU,QACV,sBAAsB;AAE9B;AAEA,MAAM,aAAa;AAMnB,MAAM,wBAAwB,CAAC,gBAAgB,aAAa;AAC5D,MAAM,yBAAyB,CAAC,iBAAiB,cAAc;AAOxD,gBAAS,oBAAoB,SAA0C;AAC1E,QAAM,cAA+B,CAAC;AACtC,MAAI,iBAAiB;AAErB,QAAM,SAAS,QAAQ,aAAa,QAAQ;AAC5C,MAAI,QAAQ;AACR,UAAM,UAAU,OACX,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAEtB,QAAI,QAAQ,SAAS,GAAG;AACpB,kBAAY,UAAU;AACtB,uBAAiB;AAAA,IACrB;AAAA,EACJ;AAEA,QAAM,QAAQ,QAAQ,aAAa,OAAO;AAC1C,MAAI,OAAO;AACP,gBAAY,QAAQ;AACpB,qBAAiB;AAAA,EACrB;AAEA,QAAM,QAAQ,QAAQ,aAAa,SAAS;AAC5C,MAAI,OAAO;AACP,gBAAY,QAAQ;AACpB,qBAAiB;AAAA,EACrB;AAEA,QAAM,SAAS,QAAQ,aAAa,QAAQ;AAC5C,MAAI,QAAQ;AACR,gBAAY,SAAS;AACrB,qBAAiB;AAAA,EACrB;AAEA,QAAM,aAAa;AAAA,IACf,eAAe,SAAS,qBAAqB;AAAA,EACjD;AACA,MAAI,eAAe,MAAM;AACrB,gBAAY,aAAa;AACzB,qBAAiB;AAAA,EACrB;AAEA,QAAM,cAAc;AAAA,IAChB,eAAe,SAAS,sBAAsB;AAAA,EAClD;AACA,MAAI,gBAAgB,MAAM;AACtB,gBAAY,cAAc;AAC1B,qBAAiB;AAAA,EACrB;AAEA,SAAO,iBAAiB,cAAc;AAC1C;AAEA,SAAS,eAAe,SAAkB,OAAgC;AACtE,WAAS,QAAQ,OAAO;AACpB,UAAM,QAAQ,QAAQ,aAAa,IAAI;AACvC,QAAI,OAAO;AACP,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO;AACX;AAEA,SAAS,WAAW,OAAqC;AACrD,MAAI,CAAC,OAAO;AACR,WAAO;AAAA,EACX;AACA,QAAM,SAAS,SAAS,OAAO,EAAE;AACjC,SAAO,OAAO,MAAM,MAAM,IAAI,OAAO;AACzC;AASO,gBAAS,cACZ,MACA,OACA,KACqC;AACrC,MAAI,IAAI,KAAK,IAAI,GAAG,KAAK;AACzB,MAAI,IAAI,KAAK,IAAI,KAAK,QAAQ,GAAG;AAEjC,SAAO,IAAI,KAAK,WAAW,KAAK,KAAK,CAAC,CAAC,GAAG;AACtC;AAAA,EACJ;AACA,SAAO,IAAI,KAAK,WAAW,KAAK,KAAK,IAAI,CAAC,CAAC,GAAG;AAC1C;AAAA,EACJ;AAEA,SAAO,IAAI,IAAI,EAAE,OAAO,GAAG,KAAK,EAAE,IAAI;AAC1C;AAYO,gBAAS,sBAAsB,MAGpC;AACE,QAAM,MAAM,IAAI,MAAc,KAAK,SAAS,CAAC;AAC7C,MAAI,YAAY;AAChB,MAAI,IAAI;AAER,SAAO,IAAI,KAAK,QAAQ;AACpB,QAAI,WAAW,KAAK,KAAK,CAAC,CAAC,GAAG;AAG1B,YAAM,WAAW,UAAU;AAC3B,aAAO,IAAI,KAAK,UAAU,WAAW,KAAK,KAAK,CAAC,CAAC,GAAG;AAChD,YAAI,CAAC,IAAI;AACT;AAAA,MACJ;AACA,mBAAa;AAAA,IACjB,OAAO;AACH,UAAI,CAAC,IAAI,UAAU;AACnB,mBAAa,KAAK,CAAC;AACnB;AAAA,IACJ;AAAA,EACJ;AACA,MAAI,KAAK,MAAM,IAAI,UAAU;AAK7B,QAAM,UAAU,UAAU,KAAK;AAC/B,QAAM,SAAS,UAAU,SAAS,UAAU,UAAU,EAAE;AAExD,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACjC,QAAI,CAAC,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,QAAQ,CAAC,GAAG,QAAQ,MAAM;AAAA,EAClE;AAEA,SAAO;AAAA,IACH,MAAM;AAAA,IACN;AAAA,EACJ;AACJ;AAUO,gBAAS,kBACZ,OACA,cACA,KACI;AACJ,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACnC,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,KAAK,iBAAiB,cAAc;AACpC;AAAA,IACJ;AAEA,UAAM,QAAQ,IAAI,KAAK,IAAI,KAAK,OAAO,IAAI,SAAS,CAAC,CAAC;AACtD,UAAM,MAAM,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,SAAS,CAAC,CAAC;AAElD,QAAI,MAAM,OAAO;AACb,WAAK,QAAQ;AACb,WAAK,MAAM;AAAA,IACf,OAAO;AACH,YAAM,OAAO,GAAG,CAAC;AACjB;AAAA,IACJ;AAAA,EACJ;AACJ;AAQO,gBAAS,6BACZ,OACA,cACI;AACJ,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACnC,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,KAAK,iBAAiB,cAAc;AACpC,YAAM,OAAO,GAAG,CAAC;AACjB;AAAA,IACJ,WAAW,KAAK,eAAe,cAAc;AACzC,WAAK;AAAA,IACT;AAAA,EACJ;AACJ;AASO,gBAAS,gBACZ,SACA,aACA,OACI;AACJ,MAAI,MAAM,WAAW,GAAG;AACpB;AAAA,EACJ;AAEA,MAAI,CAAC,QAAQ,OAAO;AAChB,YAAQ,QAAQ,CAAC;AAAA,EACrB;AAEA,QAAM,MAAM,YAAY,SAAS;AACjC,QAAM,WAAW,QAAQ,MAAM,GAAG;AAElC,MAAI,UAAU;AACV,aAAS,KAAK,GAAG,KAAK;AAAA,EAC1B,OAAO;AACH,YAAQ,MAAM,GAAG,IAAI;AAAA,EACzB;AACJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|