codexparser 0.1.36 → 0.1.37
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/package.json +1 -1
- package/src/CodexParser.js +213 -235
- package/src/versifications/isaiah.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codexparser",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.37",
|
|
4
4
|
"description": "This is a Javascript Bible parser and text scanner. It will search through texts and collate all scripture references into an array and parse them into objects, and it will parse passages into objects by book, chapter, verse, and testament. ",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
package/src/CodexParser.js
CHANGED
|
@@ -50,182 +50,150 @@ class CodexParser {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
* Scans the given text for Bible references
|
|
54
|
-
*
|
|
55
|
-
* @
|
|
53
|
+
* Scans the given text for Bible references and stores all found references in the `found` property of the instance.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} text - The text to scan for Bible references.
|
|
56
|
+
* @return {CodexParser} - Returns the instance itself, enabling method chaining.
|
|
56
57
|
*/
|
|
57
58
|
scan(text) {
|
|
58
|
-
|
|
59
|
-
const abbreviations = Object.keys(this.abbreviations) // Abbreviations for Bible books
|
|
60
|
-
|
|
59
|
+
// Initialize the `found` property as an empty array to store matched references.
|
|
61
60
|
this.found = []
|
|
62
61
|
|
|
63
|
-
//
|
|
62
|
+
// Retrieve the full names of Bible books (both Old and New Testament).
|
|
63
|
+
const fullNames = [...this.bible.old, ...this.bible.new]
|
|
64
|
+
|
|
65
|
+
// Retrieve the abbreviations for Bible books from the `abbreviations` object.
|
|
66
|
+
const abbreviations = Object.keys(this.abbreviations)
|
|
67
|
+
|
|
68
|
+
// Convert all Bible book names and abbreviations to lowercase for case-insensitive matching.
|
|
64
69
|
const lowercaseBibleFullNames = fullNames.map((book) => book.toLowerCase())
|
|
65
70
|
const lowercaseBibleAbbreviations = abbreviations.map((abbr) => abbr.toLowerCase())
|
|
71
|
+
|
|
72
|
+
// Convert the input text to lowercase for consistent comparison.
|
|
66
73
|
const lowerCaseText = text.toLowerCase()
|
|
67
74
|
|
|
75
|
+
// Initialize an index pointer `i` to iterate through the text.
|
|
68
76
|
let i = 0
|
|
69
77
|
|
|
70
|
-
//
|
|
71
|
-
const isValidChapterVerseChar = (char) =>
|
|
72
|
-
return /[^A-Za-z]/.test(char) // Allow any non-word characters
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Function to check if a character at a given index is non-alphabetic or at the boundary of the text
|
|
76
|
-
const isBoundaryOrNonAlphabetic = (index, text) => {
|
|
77
|
-
return index < 0 || index >= text.length || /[^a-z]/i.test(text[index])
|
|
78
|
-
}
|
|
78
|
+
// Helper function to check if a character is valid for chapter or verse notation.
|
|
79
|
+
const isValidChapterVerseChar = (char) => /[^A-Za-z]/.test(char)
|
|
79
80
|
|
|
80
|
-
//
|
|
81
|
+
// Helper function to determine if the next sequence in the text matches a Bible book name or abbreviation.
|
|
81
82
|
const isNextBibleBook = (startIndex) => {
|
|
82
83
|
const textAfterCurrentPosition = lowerCaseText.substring(startIndex).trim()
|
|
83
84
|
|
|
84
|
-
// Check if the
|
|
85
|
-
for (
|
|
86
|
-
if (textAfterCurrentPosition.startsWith(
|
|
87
|
-
return true // Found another Bible book
|
|
88
|
-
}
|
|
85
|
+
// Check if the upcoming text starts with any full Bible book name.
|
|
86
|
+
for (const book of lowercaseBibleFullNames) {
|
|
87
|
+
if (textAfterCurrentPosition.startsWith(book)) return true
|
|
89
88
|
}
|
|
90
89
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
90
|
+
// Check if the upcoming text starts with any Bible book abbreviation.
|
|
91
|
+
for (const abbr of lowercaseBibleAbbreviations) {
|
|
92
|
+
if (textAfterCurrentPosition.startsWith(abbr)) return true
|
|
95
93
|
}
|
|
96
94
|
|
|
95
|
+
// If no match is found, return false.
|
|
97
96
|
return false
|
|
98
97
|
}
|
|
99
98
|
|
|
100
|
-
//
|
|
99
|
+
// Helper function to detect suffixes like "LXX" or "MT" in the text after a given index.
|
|
101
100
|
const detectSuffix = (startIndex) => {
|
|
102
101
|
const suffixMatch = text.substring(startIndex).match(/\b(LXX|MT)\b/i)
|
|
103
102
|
return suffixMatch ? suffixMatch[0].toUpperCase() : null
|
|
104
103
|
}
|
|
105
104
|
|
|
106
|
-
//
|
|
105
|
+
// Main loop: Iterate through the input text to search for Bible references.
|
|
107
106
|
while (i < lowerCaseText.length) {
|
|
108
|
-
let foundBook = null
|
|
109
|
-
let foundIndex = -1
|
|
110
|
-
let matchedLength = 0
|
|
107
|
+
let foundBook = null // Stores the matched Bible book name.
|
|
108
|
+
let foundIndex = -1 // Tracks the index where the match starts.
|
|
109
|
+
let matchedLength = 0 // Tracks the length of the matched book name.
|
|
111
110
|
|
|
112
|
-
// Check
|
|
111
|
+
// Check for matches against full Bible book names.
|
|
113
112
|
for (let j = 0; j < lowercaseBibleFullNames.length; j++) {
|
|
114
113
|
const book = lowercaseBibleFullNames[j]
|
|
115
114
|
|
|
116
|
-
//
|
|
117
|
-
if (lowerCaseText.startsWith(book, i)) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
matchedLength = book.length // Update the length of the match
|
|
122
|
-
}
|
|
115
|
+
// If the text at the current index matches a book name and is longer than any previously matched name.
|
|
116
|
+
if (lowerCaseText.startsWith(book, i) && book.length > matchedLength) {
|
|
117
|
+
foundBook = fullNames[j] // Store the original case-sensitive book name.
|
|
118
|
+
foundIndex = i // Update the starting index of the match.
|
|
119
|
+
matchedLength = book.length // Update the length of the match.
|
|
123
120
|
}
|
|
124
121
|
}
|
|
125
122
|
|
|
126
|
-
// If no
|
|
123
|
+
// If no match was found against full names, try matching against abbreviations.
|
|
127
124
|
if (!foundBook) {
|
|
128
125
|
for (let k = 0; k < lowercaseBibleAbbreviations.length; k++) {
|
|
129
126
|
const abbreviation = lowercaseBibleAbbreviations[k]
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
isBoundaryOrNonAlphabetic(i + abbreviationWithDot.length, lowerCaseText)
|
|
137
|
-
) {
|
|
138
|
-
// Look ahead to check if a number or space + number follows the abbreviation
|
|
139
|
-
const afterAbbreviation = lowerCaseText.substring(i + abbreviationWithDot.length).trim()
|
|
140
|
-
if (/^\d+/.test(afterAbbreviation)) {
|
|
141
|
-
// Check if there is a number (chapter/verse)
|
|
142
|
-
foundBook = abbreviations[k] // Store the abbreviation without the dot
|
|
143
|
-
foundIndex = i // Record the index where the abbreviation is found
|
|
144
|
-
matchedLength = abbreviationWithDot.length // Update the length of the match to include the dot
|
|
145
|
-
break // Exit once found
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
} else if (lowerCaseText.startsWith(abbreviation, i)) {
|
|
149
|
-
if (
|
|
150
|
-
isBoundaryOrNonAlphabetic(i - 1, lowerCaseText) &&
|
|
151
|
-
isBoundaryOrNonAlphabetic(i + abbreviation.length, lowerCaseText)
|
|
152
|
-
) {
|
|
153
|
-
// Look ahead to check if a number or space + number follows the abbreviation
|
|
154
|
-
const afterAbbreviation = lowerCaseText.substring(i + abbreviation.length).trim()
|
|
155
|
-
if (/^\d+/.test(afterAbbreviation)) {
|
|
156
|
-
// Check if there is a number (chapter/verse)
|
|
157
|
-
if (abbreviation.length > matchedLength) {
|
|
158
|
-
foundBook = abbreviations[k] // Store the abbreviation without the dot
|
|
159
|
-
foundIndex = i // Record the index where the abbreviation is found
|
|
160
|
-
matchedLength = abbreviation.length // Update the length of the match
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
127
|
+
|
|
128
|
+
// If the text at the current index matches an abbreviation.
|
|
129
|
+
if (lowerCaseText.startsWith(abbreviation, i)) {
|
|
130
|
+
foundBook = abbreviations[k] // Store the original abbreviation.
|
|
131
|
+
foundIndex = i // Update the starting index of the match.
|
|
132
|
+
matchedLength = abbreviation.length // Update the length of the match.
|
|
164
133
|
}
|
|
165
134
|
}
|
|
166
135
|
}
|
|
167
136
|
|
|
168
|
-
// If a book
|
|
169
|
-
if (foundBook
|
|
170
|
-
i += matchedLength //
|
|
171
|
-
let chapterVerse = ""
|
|
172
|
-
const references = []
|
|
137
|
+
// If a Bible book is found.
|
|
138
|
+
if (foundBook) {
|
|
139
|
+
i += matchedLength // Move the pointer past the matched book name.
|
|
140
|
+
let chapterVerse = "" // Initialize a variable to accumulate chapter and verse information.
|
|
141
|
+
const references = [] // Array to store individual chapter and verse references.
|
|
173
142
|
|
|
174
|
-
//
|
|
143
|
+
// Secondary loop: Extract chapter and verse information after the book name.
|
|
175
144
|
while (i < text.length && isValidChapterVerseChar(text[i])) {
|
|
176
|
-
//
|
|
177
|
-
if (isNextBibleBook(i))
|
|
178
|
-
break // Stop adding to chapterVerse if a new Bible book is found
|
|
179
|
-
}
|
|
145
|
+
// Break if another Bible book starts at the current position.
|
|
146
|
+
if (isNextBibleBook(i)) break
|
|
180
147
|
|
|
181
|
-
//
|
|
182
|
-
// || text[i] === " "
|
|
148
|
+
// Handle semicolon-delimited references (e.g., "John 3:16; 4:5").
|
|
183
149
|
if (text[i] === ";") {
|
|
184
150
|
const formattedReference = chapterVerse
|
|
185
151
|
.trim()
|
|
186
|
-
.replace(
|
|
187
|
-
.replace(/[^a-zA-Z0-9]+$/, "")
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
i++
|
|
152
|
+
.replace(/\.+/g, ":") // Replace dots (.) with colons (:).
|
|
153
|
+
.replace(/[^a-zA-Z0-9:]+$/, "") // Remove trailing invalid characters.
|
|
154
|
+
|
|
155
|
+
if (formattedReference) references.push(formattedReference) // Add the formatted reference to the list.
|
|
156
|
+
chapterVerse = "" // Reset the chapterVerse accumulator.
|
|
157
|
+
i++ // Move past the semicolon.
|
|
193
158
|
continue
|
|
194
159
|
}
|
|
195
160
|
|
|
161
|
+
// Accumulate valid characters for the chapterVerse.
|
|
196
162
|
chapterVerse += text[i]
|
|
197
163
|
i++
|
|
198
164
|
}
|
|
199
165
|
|
|
200
|
-
//
|
|
166
|
+
// Handle any remaining chapterVerse after the loop ends.
|
|
201
167
|
if (chapterVerse.trim().length > 0) {
|
|
202
168
|
const formattedReference = chapterVerse
|
|
203
169
|
.trim()
|
|
204
|
-
.replace(
|
|
205
|
-
.replace(/[^a-zA-Z0-9]+$/, "")
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
170
|
+
.replace(/\.+/g, ":") // Replace dots (.) with colons (:).
|
|
171
|
+
.replace(/[^a-zA-Z0-9:]+$/, "") // Remove trailing invalid characters.
|
|
172
|
+
|
|
173
|
+
if (formattedReference) references.push(formattedReference) // Add the formatted reference to the list.
|
|
209
174
|
}
|
|
210
175
|
|
|
211
|
-
// Detect
|
|
176
|
+
// Detect any suffix (e.g., "LXX" or "MT") after the chapter/verse reference.
|
|
212
177
|
const suffix = detectSuffix(i)
|
|
213
178
|
|
|
214
|
-
//
|
|
179
|
+
// Process each extracted reference to classify its type and store it in the `found` array.
|
|
215
180
|
references.forEach((ref) => {
|
|
216
181
|
this.found.push({
|
|
217
|
-
book: foundBook,
|
|
218
|
-
reference: ref,
|
|
219
|
-
index: foundIndex,
|
|
220
|
-
|
|
182
|
+
book: foundBook, // The matched book name.
|
|
183
|
+
reference: ref.replace(/^:/, "").trim().replace(/\s+/gim, ""), // Format the reference.
|
|
184
|
+
index: foundIndex, // The starting index of the match in the original text.
|
|
185
|
+
type: ref.includes(":") ? "chapter_verse" : "single_chapter", // Determine the type of reference.
|
|
186
|
+
version: suffix || null, // Add detected suffix (e.g., "LXX" or "MT").
|
|
221
187
|
})
|
|
222
188
|
})
|
|
223
189
|
} else {
|
|
190
|
+
// If no book is found, move the pointer forward by one character.
|
|
224
191
|
i++
|
|
225
192
|
}
|
|
226
193
|
}
|
|
227
194
|
|
|
228
|
-
|
|
195
|
+
// Return the current instance for method chaining.
|
|
196
|
+
return this
|
|
229
197
|
}
|
|
230
198
|
|
|
231
199
|
bibleVersion(version) {
|
|
@@ -237,8 +205,6 @@ class CodexParser {
|
|
|
237
205
|
return this
|
|
238
206
|
}
|
|
239
207
|
|
|
240
|
-
//TODO: set the version and adjust the versifications
|
|
241
|
-
|
|
242
208
|
/**
|
|
243
209
|
* Parses a given reference and returns an object with the parsed passage,
|
|
244
210
|
* including book, chapter, verse, type, testament, index, and version.
|
|
@@ -247,146 +213,106 @@ class CodexParser {
|
|
|
247
213
|
* @returns {object} An object with the parsed passage.
|
|
248
214
|
*/
|
|
249
215
|
parse(reference) {
|
|
250
|
-
// Call scan to populate this.found
|
|
251
216
|
this.scan(reference)
|
|
252
217
|
|
|
253
218
|
this.passages = this.found.map((passage) => {
|
|
254
|
-
// Clean up spaces and remove any dots from the reference
|
|
255
|
-
passage.reference =
|
|
256
|
-
passage.reference.match(/[:]/g)?.length > 1 ? passage.reference.replace(/[:]/, "") : passage.reference
|
|
257
219
|
const book = this.bookify(passage.book)
|
|
258
|
-
const testament = this.bible.old.
|
|
259
|
-
// Initialize the parsed passage object
|
|
220
|
+
const testament = this.bible.old.includes(book) ? "old" : "new"
|
|
260
221
|
|
|
261
222
|
const parsedPassage = {
|
|
262
|
-
original: passage.book
|
|
263
|
-
book
|
|
223
|
+
original: `${passage.book} ${passage.reference}`,
|
|
224
|
+
book,
|
|
264
225
|
chapter: null,
|
|
265
|
-
verses: [],
|
|
266
|
-
type:
|
|
267
|
-
testament
|
|
226
|
+
verses: [],
|
|
227
|
+
type: passage.type,
|
|
228
|
+
testament,
|
|
268
229
|
index: passage.index,
|
|
269
230
|
version: this._handleVersion(passage.version, testament),
|
|
270
231
|
}
|
|
271
232
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
// Check for single chapter books
|
|
276
|
-
const singleChapterBook = this.singleChapterBook.find((bible) => bible[book])
|
|
233
|
+
const parts = passage.reference.split(",")
|
|
234
|
+
const isSingleChapter = this.singleChapterBook.some((singleChapterBook) => singleChapterBook[book])
|
|
277
235
|
|
|
278
236
|
parts.forEach((part) => {
|
|
279
|
-
part = part.trim()
|
|
280
|
-
// Detect whether it uses ":" or "." for chapter:verse separation
|
|
237
|
+
part = part.trim()
|
|
281
238
|
const separator = part.includes(":") ? ":" : "."
|
|
282
239
|
|
|
283
240
|
if (part.includes("-")) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
this.chapterVerses[book][startChapter].indexOf(Number(startVerse))
|
|
298
|
-
)
|
|
299
|
-
}
|
|
300
|
-
// Handle same-chapter ranges (e.g., "27:27-29") and multi-chapter ranges (e.g., "Ex 2:1-3:4")
|
|
301
|
-
if (end.includes(separator)) {
|
|
302
|
-
let [endChapter, endVerse] = end.split(separator)
|
|
303
|
-
if (Number(endChapter) !== Number(startChapter)) {
|
|
304
|
-
// Cross-chapter range, set 'to' property
|
|
305
|
-
parsedPassage.to = {
|
|
306
|
-
book: book,
|
|
307
|
-
chapter: Number(endChapter), // End chapter
|
|
308
|
-
}
|
|
309
|
-
if (endVerse > 1) {
|
|
310
|
-
parsedPassage.to.verses = this.chapterVerses[book][Number(endChapter)].slice(
|
|
311
|
-
0,
|
|
312
|
-
this.chapterVerses[book][Number(endChapter)].indexOf(Number(endVerse)) + 1
|
|
313
|
-
)
|
|
314
|
-
}
|
|
315
|
-
parsedPassage.type = "chapter_verse_range" // Set type to chapter range
|
|
316
|
-
} else {
|
|
317
|
-
// Same-chapter range, just add to the verse array
|
|
318
|
-
parsedPassage.verses.push(`${startVerse}-${endVerse}`)
|
|
319
|
-
}
|
|
320
|
-
} else {
|
|
321
|
-
// Single-chapter range (e.g., "27:27-29" or "39-41")
|
|
322
|
-
if (!singleChapterBook) {
|
|
323
|
-
if (!startChapter) {
|
|
324
|
-
// Then we have a chapter range with no verses
|
|
325
|
-
parsedPassage.chapter = start
|
|
326
|
-
parsedPassage.verses = this.chapterVerses[book][start]
|
|
241
|
+
if (!isSingleChapter) {
|
|
242
|
+
if (part.includes(":")) {
|
|
243
|
+
let [start, end] = part.split("-")
|
|
244
|
+
const [startChapter, startVerse] = start.includes(separator)
|
|
245
|
+
? start.split(separator).map(Number)
|
|
246
|
+
: [parsedPassage.chapter, Number(start)]
|
|
247
|
+
const [endChapter, endVerse] = end.includes(separator)
|
|
248
|
+
? end.split(separator).map(Number)
|
|
249
|
+
: [startChapter, Number(end)]
|
|
250
|
+
|
|
251
|
+
parsedPassage.chapter = startChapter
|
|
252
|
+
|
|
253
|
+
if (startChapter !== endChapter) {
|
|
327
254
|
parsedPassage.to = {
|
|
328
|
-
book
|
|
329
|
-
chapter:
|
|
330
|
-
verses:
|
|
255
|
+
book,
|
|
256
|
+
chapter: endChapter,
|
|
257
|
+
verses: [endVerse],
|
|
331
258
|
}
|
|
259
|
+
parsedPassage.verses.push(startVerse)
|
|
332
260
|
} else {
|
|
333
|
-
|
|
334
|
-
parsedPassage.verses.push(`${startVerse}-${end}`)
|
|
261
|
+
parsedPassage.verses.push(...this._generateRange(startVerse, endVerse))
|
|
335
262
|
}
|
|
336
263
|
} else {
|
|
337
|
-
|
|
338
|
-
parsedPassage.
|
|
264
|
+
const [start, end] = part.split("-")
|
|
265
|
+
parsedPassage.chapter = Number(start)
|
|
266
|
+
parsedPassage.to = {
|
|
267
|
+
book,
|
|
268
|
+
chapter: Number(end),
|
|
269
|
+
verses: [],
|
|
270
|
+
}
|
|
339
271
|
}
|
|
272
|
+
} else {
|
|
273
|
+
part = part.replace(/\d+:/gim, "")
|
|
274
|
+
const [singleChapterStartVerse, singleChapterEndVerse] = part.split("-")
|
|
275
|
+
parsedPassage.chapter = 1
|
|
276
|
+
parsedPassage.verses = [`${singleChapterStartVerse}-${singleChapterEndVerse}`]
|
|
277
|
+
parsedPassage.type = "single_chapter_book_verse_range"
|
|
340
278
|
}
|
|
279
|
+
} else if (part.includes(separator)) {
|
|
280
|
+
const [chapterPart, versePart] = part.split(separator).map(Number)
|
|
281
|
+
parsedPassage.chapter = chapterPart
|
|
282
|
+
if (versePart) parsedPassage.verses.push(versePart)
|
|
341
283
|
} else {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
if (singleChapterBook) {
|
|
347
|
-
if (!chapterPart) {
|
|
348
|
-
parsedPassage.chapter = 1
|
|
349
|
-
parsedPassage.verses.push(versePart) // Add single verse to array
|
|
350
|
-
} else {
|
|
351
|
-
parsedPassage.chapter = Number(chapterPart)
|
|
352
|
-
parsedPassage.verses.push(versePart) // Add single verse to array
|
|
284
|
+
if (!isSingleChapter) {
|
|
285
|
+
const number = Number(part)
|
|
286
|
+
if (!parsedPassage.verses.length) {
|
|
287
|
+
parsedPassage.chapter = number
|
|
353
288
|
}
|
|
289
|
+
parsedPassage.verses.push(Number(part))
|
|
354
290
|
} else {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
// verses from this.chapterVerses
|
|
358
|
-
if (chapterPart) {
|
|
359
|
-
parsedPassage.chapter = Number(chapterPart)
|
|
360
|
-
parsedPassage.verses.push(versePart) // Add single verse to array
|
|
361
|
-
} else {
|
|
362
|
-
parsedPassage.chapter = Number(versePart)
|
|
363
|
-
if (!this.chapterVerses[book][parsedPassage.chapter]) {
|
|
364
|
-
parsedPassage.valid = this._isValid(parsedPassage, passage.reference)
|
|
365
|
-
} else {
|
|
366
|
-
// Need to set the version of the passage here, i.e. LXX, MT, English
|
|
367
|
-
this._setVersion(parsedPassage)
|
|
368
|
-
parsedPassage.verses = [
|
|
369
|
-
this.chapterVerses[book][parsedPassage.chapter][0] +
|
|
370
|
-
"-" +
|
|
371
|
-
this.chapterVerses[book][parsedPassage.chapter][
|
|
372
|
-
this.chapterVerses[book][parsedPassage.chapter].length - 1
|
|
373
|
-
],
|
|
374
|
-
]
|
|
375
|
-
parsedPassage.type = "single_chapter"
|
|
376
|
-
}
|
|
377
|
-
}
|
|
291
|
+
parsedPassage.chapter = 1
|
|
292
|
+
parsedPassage.verses.push(Number(part))
|
|
378
293
|
}
|
|
379
294
|
}
|
|
380
|
-
parsedPassage.passages = this.populate(parsedPassage)
|
|
381
|
-
parsedPassage.scripture = this.scripturize(parsedPassage)
|
|
382
295
|
})
|
|
383
296
|
|
|
297
|
+
parsedPassage.passages = this.populate(parsedPassage)
|
|
298
|
+
parsedPassage.scripture = this.scripturize(parsedPassage)
|
|
384
299
|
parsedPassage.valid = this._isValid(parsedPassage, passage.reference)
|
|
385
300
|
|
|
386
301
|
return parsedPassage
|
|
387
302
|
})
|
|
303
|
+
|
|
388
304
|
this.versification()
|
|
389
|
-
return this
|
|
305
|
+
return this
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Generates an array of numbers representing a range from start to end, inclusive.
|
|
309
|
+
*/
|
|
310
|
+
_generateRange(start, end) {
|
|
311
|
+
const range = []
|
|
312
|
+
for (let i = start; i <= end; i++) {
|
|
313
|
+
range.push(i)
|
|
314
|
+
}
|
|
315
|
+
return range
|
|
390
316
|
}
|
|
391
317
|
|
|
392
318
|
_searchVersificationDifferences(passage) {
|
|
@@ -462,33 +388,84 @@ class CodexParser {
|
|
|
462
388
|
* @param {Array} verses - Array of verse numbers to add to the set of passages
|
|
463
389
|
* @return {Array} Array of passage objects
|
|
464
390
|
*/
|
|
391
|
+
/**
|
|
392
|
+
* Populate all verses from a parsed passage, including all verses in ranges or chapters.
|
|
393
|
+
*
|
|
394
|
+
* @param {Object} parsedPassage - The parsed passage object containing book, chapter, and verses information.
|
|
395
|
+
* @return {Array} An array of passage objects with individual verses.
|
|
396
|
+
*/
|
|
465
397
|
populate(parsedPassage) {
|
|
466
398
|
const passages = []
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
399
|
+
const { book, chapter, verses, type } = parsedPassage
|
|
400
|
+
if (type === "single_chapter") {
|
|
401
|
+
// Handle single chapter references
|
|
402
|
+
if (this.chapterVerses[book] && this.chapterVerses[book][chapter]) {
|
|
403
|
+
this.chapterVerses[book][chapter].forEach((verse) => {
|
|
404
|
+
passages.push({ book, chapter: Number(chapter), verse: Number(verse) })
|
|
405
|
+
})
|
|
473
406
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
passages.push({ book, chapter: Number(chapter), verse: i })
|
|
479
|
-
}
|
|
480
|
-
} else {
|
|
407
|
+
} else if (type === "comma_separated_verses") {
|
|
408
|
+
// Handle only the explicitly mentioned verses
|
|
409
|
+
if (verses && this.chapterVerses[book] && this.chapterVerses[book][chapter]) {
|
|
410
|
+
verses.forEach((verse) => {
|
|
481
411
|
passages.push({ book, chapter: Number(chapter), verse: Number(verse) })
|
|
412
|
+
})
|
|
413
|
+
}
|
|
414
|
+
} else if (type === "chapter_range") {
|
|
415
|
+
const { to } = parsedPassage
|
|
416
|
+
|
|
417
|
+
for (let i = chapter; i <= to.chapter; i++) {
|
|
418
|
+
const verses = this.chapterVerses[book][i]
|
|
419
|
+
for (let j = verses[0]; j < verses.length; j++) {
|
|
420
|
+
passages.push({
|
|
421
|
+
book,
|
|
422
|
+
chapter: i,
|
|
423
|
+
verse: j,
|
|
424
|
+
})
|
|
482
425
|
}
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
426
|
+
}
|
|
427
|
+
} else if (type === "multi_chapter_verse_range") {
|
|
428
|
+
const { to } = parsedPassage
|
|
429
|
+
|
|
430
|
+
// Create an array of reference objects for the start and end of the range
|
|
431
|
+
const refs = [
|
|
432
|
+
{
|
|
433
|
+
chapter: Number(parsedPassage.chapter),
|
|
434
|
+
verse: Number(parsedPassage.verses[0]),
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
chapter: Number(to.chapter),
|
|
438
|
+
verse: Number(to.verses[to.verses.length - 1]),
|
|
439
|
+
},
|
|
440
|
+
]
|
|
441
|
+
|
|
442
|
+
// Iterate over the range of chapters and verses
|
|
443
|
+
for (let i = refs[0].chapter; i <= refs[1].chapter; i++) {
|
|
444
|
+
const startVerse = i === refs[0].chapter ? refs[0].verse : 1
|
|
445
|
+
const endVerse = i === refs[1].chapter ? refs[1].verse : this.chapterVerses[book][i].length
|
|
446
|
+
|
|
447
|
+
for (let j = startVerse; j <= endVerse; j++) {
|
|
448
|
+
passages.push({
|
|
449
|
+
book,
|
|
450
|
+
chapter: i,
|
|
451
|
+
verse: j,
|
|
452
|
+
})
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
} else if (type === "chapter_verse" || type === "chapter_verse_range") {
|
|
456
|
+
// Handle chapter:verse or chapter:verse-range references
|
|
457
|
+
if (verses && this.chapterVerses[book] && this.chapterVerses[book][chapter]) {
|
|
458
|
+
verses.forEach((verse) => {
|
|
459
|
+
if (typeof verse === "string" && verse.includes("-")) {
|
|
460
|
+
const [start, end] = verse.split("-").map(Number)
|
|
461
|
+
for (let i = start; i <= end; i++) {
|
|
462
|
+
passages.push({ book, chapter: Number(chapter), verse: i })
|
|
463
|
+
}
|
|
464
|
+
} else {
|
|
465
|
+
passages.push({ book, chapter: Number(chapter), verse: Number(verse) })
|
|
466
|
+
}
|
|
467
|
+
})
|
|
468
|
+
}
|
|
492
469
|
}
|
|
493
470
|
|
|
494
471
|
return passages
|
|
@@ -602,6 +579,7 @@ class CodexParser {
|
|
|
602
579
|
* and builds the original and scripture properties.
|
|
603
580
|
* **This method will always combine based on English versification. LXX and MT versifications will be reflected in the combined passage.passages.versification.**
|
|
604
581
|
* This method will fail if the passages are not to the same book and chapter.
|
|
582
|
+
* TODO: Add support for MT and LXX
|
|
605
583
|
* @param {array} passages - An array of passage objects to combine.
|
|
606
584
|
* @return {object} The combined passage object.
|
|
607
585
|
*/
|