codexparser 0.1.96 → 0.2.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/.trunk/trunk.yaml +4 -4
- package/REFACTORING.md +214 -0
- package/index.js +1 -1
- package/package.json +4 -24
- package/src/{CodexParser.js → CodexParser.js.backup} +333 -274
- package/src/core/CodexParser.js +353 -0
- package/src/core/PassageCollection.js +404 -0
- package/src/core/PassageValidator.js +107 -0
- package/src/core/ReferenceParser.js +622 -0
- package/src/core/ScriptureScanner.js +235 -0
- package/src/core/VersificationHandler.js +143 -0
- package/src/core/VersionHandler.js +77 -0
- package/src/data/toc.js +7 -0
- package/src/format/osis.js +210 -0
- package/src/index.js +12 -0
- package/src/utils/PassageUtils.js +117 -0
- package/src/utils/chapterVerseCombine.js +65 -0
- package/.babelrc +0 -5
- package/src/chapterVerseCombine.js +0 -63
- package/src/toc.js +0 -2
- package/webpack.config.js +0 -38
- /package/src/{abbr → data/abbr}/sbl.js +0 -0
- /package/src/{bible.js → data/bible.js} +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/1chronicles.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/1corinthians.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/1john.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/1kings.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/1peter.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/1samuel.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/1thessalonians.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/1timothy.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/2chronicles.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/2corinthians.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/2kings.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/2peter.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/2samuel.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/2thessalonians.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/2timothy.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/acts.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/amos.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/colossians.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/daniel.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/deuteronomy.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/ecclesiastes.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/ephesians.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/esther.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/exodus.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/ezekiel.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/ezra.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/galatians.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/genesis.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/habakkuk.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/haggai.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/hebrews.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/hosea.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/isaiah.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/james.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/jeremiah.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/job.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/joel.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/john.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/jonah.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/joshua.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/judges.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/lamentations.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/leviticus.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/luke.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/malachi.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/mark.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/matthew.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/micah.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/nahum.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/nehemiah.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/numbers.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/philippians.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/proverbs.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/psalms.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/revelation.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/romans.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/ruth.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/songs.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/titus.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/zechariah.js +0 -0
- /package/src/{chapter_verses → data/chapter_verses}/zephaniah.js +0 -0
- /package/src/{esv.js → data/esv.js} +0 -0
- /package/src/{versifications → data/versifications}/1chronicles.js +0 -0
- /package/src/{versifications → data/versifications}/1kings.js +0 -0
- /package/src/{versifications → data/versifications}/1samuel.js +0 -0
- /package/src/{versifications → data/versifications}/2chronicles.js +0 -0
- /package/src/{versifications → data/versifications}/2samuel.js +0 -0
- /package/src/{versifications → data/versifications}/daniel.js +0 -0
- /package/src/{versifications → data/versifications}/deuteronomy.js +0 -0
- /package/src/{versifications → data/versifications}/ecclesiastes.js +0 -0
- /package/src/{versifications → data/versifications}/exodus.js +0 -0
- /package/src/{versifications → data/versifications}/ezekiel.js +0 -0
- /package/src/{versifications → data/versifications}/genesis.js +0 -0
- /package/src/{versifications → data/versifications}/hosea.js +0 -0
- /package/src/{versifications → data/versifications}/isaiah.js +0 -0
- /package/src/{versifications → data/versifications}/jeremiah.js +0 -0
- /package/src/{versifications → data/versifications}/job.js +0 -0
- /package/src/{versifications → data/versifications}/joel.js +0 -0
- /package/src/{versifications → data/versifications}/jonah.js +0 -0
- /package/src/{versifications → data/versifications}/joshua.js +0 -0
- /package/src/{versifications → data/versifications}/leviticus.js +0 -0
- /package/src/{versifications → data/versifications}/malachi.js +0 -0
- /package/src/{versifications → data/versifications}/micah.js +0 -0
- /package/src/{versifications → data/versifications}/nahum.js +0 -0
- /package/src/{versifications → data/versifications}/nehemiah.js +0 -0
- /package/src/{versifications → data/versifications}/numbers.js +0 -0
- /package/src/{versifications → data/versifications}/proverbs.js +0 -0
- /package/src/{versifications → data/versifications}/psalms.js +0 -0
- /package/src/{versifications → data/versifications}/song.js +0 -0
- /package/src/{versifications → data/versifications}/zechariah.js +0 -0
- /package/src/{versified.js → data/versified.js} +0 -0
- /package/src/{abbr.js → format/abbr.js} +0 -0
- /package/src/{functions.js → utils/functions.js} +0 -0
- /package/src/{regex.js → utils/regex.js} +0 -0
|
@@ -1,303 +1,359 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CodexParser.js
|
|
3
|
-
* A class for scanning and parsing scripture references from text
|
|
4
|
-
* (
|
|
5
|
-
*
|
|
3
|
+
* A modern ES6+ class for scanning and parsing scripture references from text.
|
|
4
|
+
* Supports various formats (single verses, ranges, multi-chapter references) with
|
|
5
|
+
* validation and version-specific versification.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const chapter_verses = require("./chapterVerseCombine")
|
|
8
|
+
const bible = require("./bible");
|
|
9
|
+
const { bookRegex, chapterRegex, verseRegex, scripturesRegex } = require("./regex");
|
|
10
|
+
const ScriptureScanner = require("./ScriptureScanner");
|
|
11
|
+
const ReferenceParser = require("./ReferenceParser");
|
|
12
|
+
const VersificationHandler = require("./VersificationHandler");
|
|
13
|
+
const PassageCollection = require("./PassageCollection");
|
|
14
|
+
const PassageUtils = require("./PassageUtils");
|
|
15
|
+
const VersionHandler = require("./VersionHandler");
|
|
16
|
+
const chapter_verses = require("./chapterVerseCombine");
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* Main class for parsing and validating scripture references
|
|
20
20
|
* @class
|
|
21
21
|
*/
|
|
22
22
|
class CodexParser {
|
|
23
|
+
// Private fields using ES6+ syntax
|
|
24
|
+
#scanner;
|
|
25
|
+
#parser;
|
|
26
|
+
#versificationHandler;
|
|
27
|
+
#config;
|
|
28
|
+
#version;
|
|
29
|
+
#found;
|
|
30
|
+
#passages;
|
|
31
|
+
|
|
23
32
|
/**
|
|
24
|
-
* Initializes the parser with
|
|
33
|
+
* Initializes the parser with configuration
|
|
34
|
+
* @param {Object} config - Configuration options
|
|
25
35
|
*/
|
|
26
36
|
constructor(config = {}) {
|
|
27
|
-
this
|
|
28
|
-
this.passages = []
|
|
29
|
-
this.bible = bible
|
|
30
|
-
this.bookRegex = bookRegex
|
|
31
|
-
this.chapterRegex = chapterRegex
|
|
32
|
-
this.verseRegex = verseRegex
|
|
33
|
-
this.scripturesRegex = scripturesRegex
|
|
34
|
-
this.abbreviations = abbreviations
|
|
35
|
-
this.sblAbbreviations = sblAbbreviations
|
|
36
|
-
this.versificationDifferences = versified
|
|
37
|
-
this.singleChapterBook = [
|
|
38
|
-
sch("Jude", 25),
|
|
39
|
-
sch("2 John", 13),
|
|
40
|
-
sch("3 John", 15),
|
|
41
|
-
sch("Obadiah", 21),
|
|
42
|
-
sch("Philemon", 25),
|
|
43
|
-
]
|
|
44
|
-
this.chapterVerses = chapter_verses
|
|
45
|
-
this.error = false
|
|
46
|
-
this.version = null
|
|
47
|
-
this.SINGLE_CHAPTER = "single_chapter"
|
|
48
|
-
this.CHAPTER_VERSE = "chapter_verse"
|
|
49
|
-
this.CHAPTER_VERSE_RANGE = "chapter_verse_range"
|
|
50
|
-
this.COMMA_SEPARATED = "comma_separated_verses"
|
|
51
|
-
this.CHAPTER_RANGE = "chapter_range"
|
|
52
|
-
this.MULTI_CHAPTER_RANGE = "multi_chapter_verse_range"
|
|
53
|
-
this.config = {
|
|
37
|
+
this.#config = {
|
|
54
38
|
booksOnly: config.booksOnly ?? false,
|
|
55
39
|
invalid_sequence_strategy: config.invalid_sequence_strategy ?? "include",
|
|
56
40
|
invalid_passage_strategy: config.invalid_passage_strategy ?? "include",
|
|
57
|
-
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
this.#scanner = new ScriptureScanner(this.#config);
|
|
44
|
+
this.#parser = new ReferenceParser(this.#config);
|
|
45
|
+
this.#versificationHandler = new VersificationHandler();
|
|
46
|
+
this.#version = null;
|
|
47
|
+
this.#found = [];
|
|
48
|
+
this.#passages = [];
|
|
49
|
+
|
|
50
|
+
// Legacy public properties for backward compatibility
|
|
51
|
+
this.bible = bible;
|
|
52
|
+
this.bookRegex = bookRegex;
|
|
53
|
+
this.chapterRegex = chapterRegex;
|
|
54
|
+
this.verseRegex = verseRegex;
|
|
55
|
+
this.scripturesRegex = scripturesRegex;
|
|
56
|
+
this.chapterVerses = chapter_verses;
|
|
57
|
+
this.error = false;
|
|
58
|
+
|
|
59
|
+
// Legacy constants
|
|
60
|
+
this.SINGLE_CHAPTER = "single_chapter";
|
|
61
|
+
this.CHAPTER_VERSE = "chapter_verse";
|
|
62
|
+
this.CHAPTER_VERSE_RANGE = "chapter_verse_range";
|
|
63
|
+
this.COMMA_SEPARATED = "comma_separated_verses";
|
|
64
|
+
this.CHAPTER_RANGE = "chapter_range";
|
|
65
|
+
this.MULTI_CHAPTER_RANGE = "multi_chapter_verse_range";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Gets the found references (legacy getter)
|
|
70
|
+
*/
|
|
71
|
+
get found() {
|
|
72
|
+
return this.#found;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Sets the found references (legacy setter)
|
|
77
|
+
*/
|
|
78
|
+
set found(value) {
|
|
79
|
+
this.#found = value;
|
|
58
80
|
}
|
|
59
81
|
|
|
60
82
|
/**
|
|
61
|
-
*
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
83
|
+
* Gets the parsed passages (legacy getter)
|
|
84
|
+
*/
|
|
85
|
+
get passages() {
|
|
86
|
+
return this.#passages;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Sets the parsed passages (legacy setter)
|
|
91
|
+
*/
|
|
92
|
+
set passages(value) {
|
|
93
|
+
this.#passages = value;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Gets the current version (legacy getter)
|
|
98
|
+
*/
|
|
99
|
+
get version() {
|
|
100
|
+
return this.#version;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Sets the current version (legacy setter)
|
|
105
|
+
*/
|
|
106
|
+
set version(value) {
|
|
107
|
+
this.#version = value;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Gets the current config (legacy getter)
|
|
112
|
+
*/
|
|
113
|
+
get config() {
|
|
114
|
+
return this.#config;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Sets the current config (legacy setter)
|
|
119
|
+
*/
|
|
120
|
+
set config(value) {
|
|
121
|
+
this.#config = value;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Sets configuration options for the parser
|
|
126
|
+
* @param {Object} config - Configuration options
|
|
127
|
+
* @param {boolean} [config.booksOnly=false] - Whether to capture book names without references
|
|
128
|
+
* @returns {CodexParser} The parser instance for method chaining
|
|
65
129
|
*/
|
|
66
130
|
options(config) {
|
|
67
|
-
this
|
|
68
|
-
booksOnly: config.booksOnly ??
|
|
69
|
-
invalid_sequence_strategy: config.invalid_sequence_strategy ?? this
|
|
70
|
-
invalid_passage_strategy: config.invalid_passage_strategy ?? this
|
|
71
|
-
}
|
|
72
|
-
|
|
131
|
+
this.#config = {
|
|
132
|
+
booksOnly: config.booksOnly ?? this.#config.booksOnly,
|
|
133
|
+
invalid_sequence_strategy: config.invalid_sequence_strategy ?? this.#config.invalid_sequence_strategy,
|
|
134
|
+
invalid_passage_strategy: config.invalid_passage_strategy ?? this.#config.invalid_passage_strategy,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Update scanner and parser configs
|
|
138
|
+
this.#scanner = new ScriptureScanner(this.#config);
|
|
139
|
+
this.#parser = new ReferenceParser(this.#config);
|
|
140
|
+
|
|
141
|
+
return this;
|
|
73
142
|
}
|
|
74
143
|
|
|
75
144
|
/**
|
|
76
|
-
* Retrieves available verses for a given book and chapter
|
|
77
|
-
* @param {string} book - The book name
|
|
78
|
-
* @param {number} chapter - The chapter number
|
|
79
|
-
* @returns {number[]} Array of valid verse numbers
|
|
145
|
+
* Retrieves available verses for a given book and chapter (legacy method)
|
|
146
|
+
* @param {string} book - The book name
|
|
147
|
+
* @param {number} chapter - The chapter number
|
|
148
|
+
* @returns {number[]} Array of valid verse numbers
|
|
80
149
|
*/
|
|
81
150
|
getChapterVerses(book, chapter) {
|
|
82
|
-
|
|
83
|
-
return singleChapterBook ? singleChapterBook[book][chapter] || [] : this.chapterVerses[book]?.[chapter] || []
|
|
151
|
+
return PassageUtils.getChapterVerses(book, chapter);
|
|
84
152
|
}
|
|
85
153
|
|
|
86
154
|
/**
|
|
87
|
-
* Scans text for scripture references
|
|
88
|
-
* @param {string} text - The text to scan
|
|
89
|
-
* @returns {CodexParser} The parser instance for method chaining
|
|
155
|
+
* Scans text for scripture references
|
|
156
|
+
* @param {string} text - The text to scan
|
|
157
|
+
* @returns {CodexParser} The parser instance for method chaining
|
|
90
158
|
*/
|
|
91
159
|
scan(text) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// Minimal normalization: fix periods before numbers, remove trailing periods
|
|
96
|
-
let normalizedText = text.replace(/\.(?=\d)/g, ":").replace(/(\b[A-Za-z]+)\.(?=\s|$)/g, "$1")
|
|
97
|
-
const lowercaseBibleFullNames = fullNames.map((book) => book.toLowerCase())
|
|
98
|
-
const lowercaseBibleAbbreviations = abbreviations.map((abbr) => abbr.toLowerCase())
|
|
99
|
-
const lowerCaseText = normalizedText.toLowerCase()
|
|
100
|
-
let i = 0
|
|
101
|
-
|
|
102
|
-
const isValidChapterVerseChar = (char) => /[\d:,\-;\s]/.test(char)
|
|
103
|
-
const isNextBibleBook = (startIndex) => {
|
|
104
|
-
const textAfterCurrentPosition = lowerCaseText.substring(startIndex).trim()
|
|
105
|
-
return (
|
|
106
|
-
lowercaseBibleFullNames.some((book) => textAfterCurrentPosition.startsWith(book)) ||
|
|
107
|
-
lowercaseBibleAbbreviations.some((abbr) => textAfterCurrentPosition.startsWith(abbr))
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
const detectSuffix = (startIndex) => {
|
|
111
|
-
const suffixMatch = normalizedText.substring(startIndex).match(/\b(LXX|MT)\b/i)
|
|
112
|
-
return suffixMatch ? { suffix: suffixMatch[0].toUpperCase(), length: suffixMatch[0].length } : null
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
while (i < lowerCaseText.length) {
|
|
116
|
-
let foundBook = null
|
|
117
|
-
let bookStartIndex = -1
|
|
118
|
-
let matchedLength = 0
|
|
160
|
+
this.#found = this.#scanner.scan(text);
|
|
161
|
+
return this;
|
|
162
|
+
}
|
|
119
163
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
foundBook = fullNames[j]
|
|
130
|
-
bookStartIndex = i
|
|
131
|
-
matchedLength = book.length
|
|
132
|
-
}
|
|
133
|
-
}
|
|
164
|
+
/**
|
|
165
|
+
* Sets the Bible version for parsing
|
|
166
|
+
* @param {string} version - The version (e.g., "lxx", "mt", "eng")
|
|
167
|
+
* @returns {CodexParser} The parser instance
|
|
168
|
+
*/
|
|
169
|
+
bibleVersion(version) {
|
|
170
|
+
this.#version = VersionHandler.normalizeVersion(version);
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
134
173
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
174
|
+
/**
|
|
175
|
+
* Parses a scripture reference into structured passage objects
|
|
176
|
+
* @param {string} reference - The reference to parse (e.g., "John 3:16")
|
|
177
|
+
* @returns {CodexParser} The parser instance
|
|
178
|
+
*/
|
|
179
|
+
parse(reference) {
|
|
180
|
+
this.scan(reference);
|
|
181
|
+
this.#passages = this.#parser.parse(this.#found, this.#version);
|
|
182
|
+
this.#passages = this.#versificationHandler.applyVersification(this.#passages);
|
|
183
|
+
return this;
|
|
184
|
+
}
|
|
145
185
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Returns parsed passages with utility methods
|
|
188
|
+
* @returns {PassageCollection} Collection of passages with utility methods
|
|
189
|
+
*/
|
|
190
|
+
getPassages() {
|
|
191
|
+
return PassageCollection.from(this.#passages);
|
|
192
|
+
}
|
|
152
193
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const refEndIndex = i
|
|
161
|
-
references.push({
|
|
162
|
-
ref: formattedReference,
|
|
163
|
-
start: refStartIndex,
|
|
164
|
-
end: refEndIndex,
|
|
165
|
-
})
|
|
166
|
-
}
|
|
167
|
-
chapterVerse = ""
|
|
168
|
-
refStartIndex = i + 1
|
|
169
|
-
const semicolonIndex = text.indexOf(";", originalRefStartIndex)
|
|
170
|
-
originalRefStartIndex = semicolonIndex !== -1 ? semicolonIndex + 1 : refStartIndex
|
|
171
|
-
i++
|
|
172
|
-
continue
|
|
173
|
-
}
|
|
174
|
-
chapterVerse += normalizedText[i]
|
|
175
|
-
i++
|
|
176
|
-
}
|
|
194
|
+
/**
|
|
195
|
+
* Returns the first parsed passage
|
|
196
|
+
* @returns {Object|null} The first passage or null
|
|
197
|
+
*/
|
|
198
|
+
first() {
|
|
199
|
+
return this.#passages.length > 0 ? this.#passages[0] : null;
|
|
200
|
+
}
|
|
177
201
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
})
|
|
187
|
-
}
|
|
188
|
-
}
|
|
202
|
+
/**
|
|
203
|
+
* Normalizes book names using abbreviations or full names (legacy method)
|
|
204
|
+
* @param {string|Array} book - The book name or array
|
|
205
|
+
* @returns {string} Normalized book name
|
|
206
|
+
*/
|
|
207
|
+
bookify(book) {
|
|
208
|
+
return this.#parser.constructor.prototype.normalizeBookName?.(book) || book;
|
|
209
|
+
}
|
|
189
210
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
let type
|
|
199
|
-
if (ref.includes(":")) {
|
|
200
|
-
if (ref.includes("-")) {
|
|
201
|
-
const [start, end] = ref.split("-")
|
|
202
|
-
const startParts = start.split(":")
|
|
203
|
-
const endParts = end.split(":")
|
|
204
|
-
type =
|
|
205
|
-
startParts.length > 1 &&
|
|
206
|
-
endParts.length > 1 &&
|
|
207
|
-
startParts[0].trim() !== endParts[0].trim()
|
|
208
|
-
? "multi_chapter_verse_range"
|
|
209
|
-
: "chapter_verse_range"
|
|
210
|
-
} else if (ref.includes(",")) {
|
|
211
|
-
type = "comma_separated_verses"
|
|
212
|
-
} else {
|
|
213
|
-
type = "chapter_verse"
|
|
214
|
-
}
|
|
215
|
-
} else if (ref.includes("-")) {
|
|
216
|
-
type = "chapter_range"
|
|
217
|
-
} else {
|
|
218
|
-
type = "single_chapter"
|
|
219
|
-
}
|
|
211
|
+
/**
|
|
212
|
+
* Combines multiple passages into a single reference
|
|
213
|
+
* @param {Object[]} [passages=this.passages] - Array of passages to combine
|
|
214
|
+
* @returns {Object} Combined passage object
|
|
215
|
+
*/
|
|
216
|
+
combine(passages = this.#passages) {
|
|
217
|
+
return PassageCollection.combinePassages(passages);
|
|
218
|
+
}
|
|
220
219
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
220
|
+
/**
|
|
221
|
+
* Merges verses into ranges or comma-separated lists (legacy method)
|
|
222
|
+
* @param {number[]} verses - Array of verse numbers
|
|
223
|
+
* @returns {string[]} Array of verse strings
|
|
224
|
+
*/
|
|
225
|
+
mergeRanges(verses) {
|
|
226
|
+
return PassageUtils.mergeRanges(verses);
|
|
227
|
+
}
|
|
230
228
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
229
|
+
/**
|
|
230
|
+
* Generates a table of contents for the Bible
|
|
231
|
+
* @param {string} [version="ESV"] - The Bible version
|
|
232
|
+
* @returns {Object} TOC with book-chapter-verse mappings
|
|
233
|
+
*/
|
|
234
|
+
getToc(version = "ESV") {
|
|
235
|
+
const toc = {};
|
|
236
|
+
|
|
237
|
+
bible.old.forEach((book) => {
|
|
238
|
+
if (chapter_verses[book]) {
|
|
239
|
+
toc[book] = chapter_verses[book];
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
bible.new.forEach((book) => {
|
|
244
|
+
if (chapter_verses[book]) {
|
|
245
|
+
toc[book] = chapter_verses[book];
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
PassageUtils.SINGLE_CHAPTER_BOOKS.forEach((item) => {
|
|
250
|
+
Object.keys(item).forEach((book) => {
|
|
251
|
+
if (!toc[book]) {
|
|
252
|
+
toc[book] = item[book];
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const orderedToc = {};
|
|
258
|
+
const canonicalOrder = [...bible.old, ...bible.new];
|
|
259
|
+
canonicalOrder.forEach((book) => {
|
|
260
|
+
if (toc[book]) {
|
|
261
|
+
orderedToc[book] = toc[book];
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
return orderedToc;
|
|
266
|
+
}
|
|
236
267
|
|
|
237
|
-
|
|
238
|
-
|
|
268
|
+
/**
|
|
269
|
+
* Replaces scripture references in text with formatted references
|
|
270
|
+
* @param {string} text - The original text
|
|
271
|
+
* @param {boolean} useAbbreviations - Whether to use abbreviated book names
|
|
272
|
+
* @returns {string} Text with replaced references
|
|
273
|
+
*/
|
|
274
|
+
replace(text, useAbbreviations = true) {
|
|
275
|
+
if (!this.#passages.length) {
|
|
276
|
+
return text;
|
|
277
|
+
}
|
|
239
278
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
279
|
+
let result = text;
|
|
280
|
+
for (let i = this.#passages.length - 1; i >= 0; i--) {
|
|
281
|
+
const passage = this.#passages[i];
|
|
282
|
+
const { originalText, abbr, original } = passage;
|
|
283
|
+
const newReference = useAbbreviations ? abbr : original;
|
|
245
284
|
|
|
246
|
-
|
|
247
|
-
while (originalEndIndex > originalStartIndex && /[\s]/.test(text[originalEndIndex - 1])) {
|
|
248
|
-
originalEndIndex--
|
|
249
|
-
originalText = text.slice(originalStartIndex, originalEndIndex)
|
|
250
|
-
}
|
|
285
|
+
const regex = new RegExp(`${originalText.replace(/([.*+?^${}()|[\]\\])/g, "\\$1")}`, "g");
|
|
251
286
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
287
|
+
const matches = [...result.matchAll(regex)];
|
|
288
|
+
if (matches.length > 0) {
|
|
289
|
+
for (let j = matches.length - 1; j >= 0; j--) {
|
|
290
|
+
const match = matches[j];
|
|
291
|
+
const startIndex = match.index;
|
|
292
|
+
const endIndex = startIndex + match[0].length;
|
|
293
|
+
const leadingSpace = match[1] || "";
|
|
294
|
+
const hasOpeningParen = match[2] === "(";
|
|
295
|
+
const hasClosingParen = match[3] === ")";
|
|
296
|
+
const trailingSpace = match[4] || " ";
|
|
297
|
+
const replacement =
|
|
298
|
+
hasOpeningParen && hasClosingParen
|
|
299
|
+
? `${leadingSpace}(${newReference})${trailingSpace}`
|
|
300
|
+
: `${leadingSpace}${newReference}${trailingSpace}`;
|
|
301
|
+
result = result.slice(0, startIndex) + replacement + result.slice(endIndex);
|
|
302
|
+
}
|
|
264
303
|
}
|
|
265
304
|
}
|
|
266
305
|
|
|
267
|
-
return
|
|
306
|
+
return result;
|
|
268
307
|
}
|
|
269
308
|
|
|
270
309
|
/**
|
|
271
|
-
*
|
|
272
|
-
* @
|
|
273
|
-
* @returns {CodexParser} The parser instance.
|
|
310
|
+
* Checks if all references in the passages array are from the same book
|
|
311
|
+
* @returns {boolean} True if all passages are from the same book
|
|
274
312
|
*/
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
return this
|
|
313
|
+
same() {
|
|
314
|
+
if (this.#passages.length <= 1) {
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const firstBook = this.#passages[0].book.toLowerCase();
|
|
319
|
+
return this.#passages.every((passage) => passage.book.toLowerCase() === firstBook);
|
|
282
320
|
}
|
|
283
321
|
|
|
322
|
+
// Legacy methods for backward compatibility
|
|
323
|
+
|
|
284
324
|
/**
|
|
285
|
-
*
|
|
286
|
-
* @param {string} reference - The reference to parse (e.g., "John 3:16").
|
|
287
|
-
* @returns {CodexParser} The parser instance.
|
|
325
|
+
* Expands verse references into individual verse objects (legacy)
|
|
288
326
|
*/
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
327
|
+
expandVerses(book, chapter, verses) {
|
|
328
|
+
return PassageUtils.expandVerses(book, chapter, verses);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Populates passage with expanded verse objects (legacy)
|
|
333
|
+
*/
|
|
334
|
+
populate(passage) {
|
|
335
|
+
return this.#parser.constructor.prototype._populatePassage?.(passage) || [];
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Formats a passage into a human-readable reference (legacy)
|
|
340
|
+
*/
|
|
341
|
+
scripturize(passage) {
|
|
342
|
+
return this.#parser.constructor.prototype._formatScripture?.(passage) || {};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Applies versification differences to parsed passages (legacy)
|
|
347
|
+
*/
|
|
348
|
+
versification() {
|
|
349
|
+
this.#passages = this.#versificationHandler.applyVersification(this.#passages);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
module.exports = CodexParser;
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
|
|
301
357
|
testament,
|
|
302
358
|
startIndex: passage.startIndex,
|
|
303
359
|
endIndex: passage.endIndex,
|
|
@@ -1141,6 +1197,11 @@ class CodexParser {
|
|
|
1141
1197
|
throw new Error("Passages must be from the same book to join.")
|
|
1142
1198
|
}
|
|
1143
1199
|
|
|
1200
|
+
const versions = new Set(passages.map((p) => p.version.abbreviation))
|
|
1201
|
+
if (versions.size > 1) {
|
|
1202
|
+
throw new Error("Cannot combine passages from different versions.")
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1144
1205
|
const combined = {
|
|
1145
1206
|
...passages[0],
|
|
1146
1207
|
verses: [],
|
|
@@ -1160,35 +1221,30 @@ class CodexParser {
|
|
|
1160
1221
|
|
|
1161
1222
|
passages.forEach((passage) => {
|
|
1162
1223
|
passage.passages.forEach((p) => {
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
if (
|
|
1166
|
-
|
|
1167
|
-
normChapter = c
|
|
1168
|
-
normVerse = v
|
|
1169
|
-
}
|
|
1170
|
-
if (!chapterVerses[normChapter]) {
|
|
1171
|
-
chapterVerses[normChapter] = new Set()
|
|
1224
|
+
const chapter = p.chapter
|
|
1225
|
+
const verse = p.verse
|
|
1226
|
+
if (!chapterVerses[chapter]) {
|
|
1227
|
+
chapterVerses[chapter] = new Set()
|
|
1172
1228
|
}
|
|
1173
|
-
chapterVerses[
|
|
1229
|
+
chapterVerses[chapter].add(verse)
|
|
1174
1230
|
combined.passages.push({
|
|
1175
1231
|
book: p.book,
|
|
1176
|
-
chapter:
|
|
1177
|
-
verse:
|
|
1232
|
+
chapter: p.chapter,
|
|
1233
|
+
verse: p.verse,
|
|
1178
1234
|
versification: p.versification,
|
|
1179
1235
|
})
|
|
1180
1236
|
|
|
1181
|
-
if (firstChapter === null ||
|
|
1182
|
-
firstChapter =
|
|
1183
|
-
firstVerse =
|
|
1184
|
-
} else if (
|
|
1185
|
-
firstVerse =
|
|
1237
|
+
if (firstChapter === null || chapter < firstChapter) {
|
|
1238
|
+
firstChapter = chapter
|
|
1239
|
+
firstVerse = verse
|
|
1240
|
+
} else if (chapter === firstChapter && (firstVerse === null || verse < firstVerse)) {
|
|
1241
|
+
firstVerse = verse
|
|
1186
1242
|
}
|
|
1187
|
-
if (lastChapter === null ||
|
|
1188
|
-
lastChapter =
|
|
1189
|
-
lastVerse =
|
|
1190
|
-
} else if (
|
|
1191
|
-
lastVerse =
|
|
1243
|
+
if (lastChapter === null || chapter > lastChapter) {
|
|
1244
|
+
lastChapter = chapter
|
|
1245
|
+
lastVerse = verse
|
|
1246
|
+
} else if (chapter === lastChapter && (lastVerse === null || verse > lastVerse)) {
|
|
1247
|
+
lastVerse = verse
|
|
1192
1248
|
}
|
|
1193
1249
|
})
|
|
1194
1250
|
})
|
|
@@ -1272,18 +1328,21 @@ class CodexParser {
|
|
|
1272
1328
|
delete combined.to
|
|
1273
1329
|
}
|
|
1274
1330
|
|
|
1275
|
-
//
|
|
1276
|
-
combined.version =
|
|
1331
|
+
// Respect the common version
|
|
1332
|
+
combined.version = passages[0].version
|
|
1277
1333
|
|
|
1278
|
-
// Set abbr
|
|
1334
|
+
// Set abbr with version suffix if not ENG
|
|
1279
1335
|
const sblEntry = Object.entries(this.sblAbbreviations).find(
|
|
1280
1336
|
([key]) => key.toLowerCase() === combined.book.toLowerCase()
|
|
1281
1337
|
)
|
|
1338
|
+
const suffix = combined.version.abbreviation === "eng" ? "" : ` ${combined.version.value}`
|
|
1282
1339
|
if (sblEntry) {
|
|
1283
1340
|
const { value, abbr } = sblEntry[1]
|
|
1284
|
-
combined.abbr = abbr
|
|
1341
|
+
combined.abbr = abbr
|
|
1342
|
+
? `${value}. ${combined.scripture.cv}${suffix}`
|
|
1343
|
+
: `${value} ${combined.scripture.cv}${suffix}`
|
|
1285
1344
|
} else {
|
|
1286
|
-
combined.abbr = `${combined.book} ${combined.scripture.cv}`
|
|
1345
|
+
combined.abbr = `${combined.book} ${combined.scripture.cv}${suffix}`
|
|
1287
1346
|
}
|
|
1288
1347
|
|
|
1289
1348
|
const self = this
|