codexparser 0.1.52 → 0.1.54

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/CodexParser.js +199 -168
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexparser",
3
- "version": "0.1.52",
3
+ "version": "0.1.54",
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": {
@@ -186,148 +186,119 @@ class CodexParser {
186
186
  * @returns {object} An object with the parsed passage.
187
187
  */
188
188
  parse(reference) {
189
- this.scan(reference) // Call scan to populate this.found
189
+ this.scan(reference) // Populate this.found
190
190
 
191
191
  this.passages = this.found.map((passage) => {
192
192
  const book = this.bookify(passage.book)
193
- const testament = this.bible.old.find((bible) => bible === book) ? "old" : "new"
194
-
195
- // Initialize the parsed passage object
193
+ const testament = this.bible.old.includes(book) ? "old" : "new"
194
+ const singleChapterBook = this.singleChapterBook.find((b) => Object.keys(b)[0] === book)
196
195
  const parsedPassage = {
197
196
  original: passage.book + " " + passage.reference,
198
197
  book: book,
199
198
  chapter: null,
200
- verses: [], // Verses stored as an array
201
- type: passage.type, // Set type based on reference
199
+ verses: [],
200
+ type: passage.type,
202
201
  testament: testament,
203
202
  index: passage.index,
204
203
  version: this._handleVersion(passage.version, testament),
205
204
  }
206
205
 
207
- // Split reference by commas to handle multiple ranges or verses (e.g., "Isaiah 8:22-9:1,5")
208
- let parts = passage.reference.split(",")
209
-
210
- // Check for single-chapter books
211
- const singleChapterBook = this.singleChapterBook.find((bible) => bible[book])
206
+ // Split reference into parts (e.g., "2 John 1", "2 John 2", "2 John 1:1-3,5")
207
+ const parts = passage.reference.split(",")
212
208
 
213
209
  parts.forEach((part, partIndex) => {
214
- part = part.trim() // Clean up spaces
215
- const separator = part.includes(":") ? ":" : "."
216
-
217
- if (part.includes("-")) {
218
- // Handle ranges (e.g., "8:22-9:1")
219
- let [start, end] = part.split("-")
220
-
221
- // Handle the starting part
222
- let [startChapter, startVerse] = start.includes(separator)
223
- ? start.split(separator)
224
- : [parsedPassage.chapter, start] // Default to same chapter if no chapter is provided
225
-
226
- parsedPassage.chapter = Number(startChapter) // Set the chapter
227
-
228
- // Multi-chapter verse range logic
229
- if (start.includes(separator) && end.includes(separator)) {
230
- let [endChapter, endVerse] = end.split(separator)
231
-
232
- parsedPassage.verses = this.chapterVerses[book][startChapter].slice(
233
- this.chapterVerses[book][startChapter].indexOf(Number(startVerse))
234
- )
235
-
236
- if (Number(endChapter) !== Number(startChapter)) {
237
- const endVerses = this.chapterVerses[book][endChapter].slice(
238
- 0,
239
- this.chapterVerses[book][endChapter].indexOf(Number(endVerse)) + 1
240
- )
241
- // Construct ranges for the `to` object
242
- parsedPassage.to = {
243
- book: book,
244
- chapter: Number(endChapter),
245
- verses: [endVerse],
246
- }
210
+ part = part.trim()
247
211
 
248
- parsedPassage.type = "multi_chapter_verse_range"
249
- } else {
250
- parsedPassage.verses.push(`${startVerse}-${endVerse}`)
251
- }
252
- } else {
253
- if (separator !== ":") {
254
- // then this is a chapter_range
255
- let [startChapter, endChapter] = part.split("-")
256
- parsedPassage.chapter = Number(startChapter)
257
- parsedPassage.verses = [
258
- `${this.chapterVerses[book][parsedPassage.chapter][0]}-${
259
- this.chapterVerses[book][parsedPassage.chapter][
260
- this.chapterVerses[book][parsedPassage.chapter].length - 1
261
- ]
262
- }`,
263
- ]
264
- parsedPassage.to = {
265
- book: book,
266
- chapter: Number(endChapter),
267
- verses: [
268
- `${this.chapterVerses[book][endChapter][0]}-${
269
- this.chapterVerses[book][endChapter][
270
- this.chapterVerses[book][endChapter].length - 1
271
- ]
272
- }`,
273
- ],
274
- }
275
- } else {
276
- // Handles when verses are present.
277
- parsedPassage.verses = [part.split(separator)[1]]
278
- }
212
+ if (part.includes(":")) {
213
+ // Explicit chapter:verse (e.g., "1:1-3")
214
+ const [chapterPart, versePart] = part.split(":")
215
+ if (partIndex === 0) {
216
+ parsedPassage.chapter = Number(chapterPart) // Set chapter only on first part
279
217
  }
280
- } else {
281
- // Handle individual chapter:verse references (e.g., "9:5" in "8:22-9:1,5")
282
-
283
- let [chapterPart, versePart] = part.includes(separator)
284
- ? part.split(separator)
285
- : [parsedPassage.chapter, part]
286
218
 
287
- if (separator.trim() !== ":" && !parsedPassage.chapter) {
288
- if (singleChapterBook) {
219
+ if (versePart.includes("-")) {
220
+ parsedPassage.verses.push(versePart) // Add range (e.g., "1-3")
221
+ } else {
222
+ parsedPassage.verses.push(Number(versePart)) // Add single verse
223
+ }
224
+ parsedPassage.type = versePart.includes("-") ? "chapter_verse_range" : "chapter_verse"
225
+ } else if (singleChapterBook) {
226
+ // Handle single-chapter books
227
+ const verseCount = singleChapterBook[book][1].length
228
+ if (part === "1" && parts.length === 1 && partIndex === 0) {
229
+ // "2 John 1" means the whole chapter
230
+ parsedPassage.chapter = 1
231
+ parsedPassage.type = "single_chapter"
232
+ parsedPassage.verses = [`1-${verseCount}`] // e.g., "1-13"
233
+ } else if (part.includes("-")) {
234
+ // "2 John 3-5" → "2 John 1:3-5"
235
+ parsedPassage.chapter = 1
236
+ parsedPassage.verses.push(part) // e.g., "3-5"
237
+ parsedPassage.type = "chapter_verse_range"
238
+ } else {
239
+ // "2 John 2" → "2 John 1:2"
240
+ const num = Number(part)
241
+ if (num > 1 || (num === 1 && parts.length > 1)) {
289
242
  parsedPassage.chapter = 1
290
- parsedPassage.verses.push(versePart) // Add single verse to array
291
- } else {
292
- parsedPassage.chapter = Number(part)
293
- parsedPassage.verses = [
294
- `${this.chapterVerses[book][parsedPassage.chapter][0]}-${
295
- this.chapterVerses[book][parsedPassage.chapter][
296
- this.chapterVerses[book][parsedPassage.chapter].length - 1
297
- ]
298
- }`,
299
- ]
243
+ parsedPassage.verses.push(num) // Treat as verse number
244
+ parsedPassage.type = "chapter_verse"
300
245
  }
246
+ }
247
+ } else if (part.includes("-") && !parsedPassage.chapter) {
248
+ // Range without chapter for multi-chapter books (e.g., "Isaiah 3-5")
249
+ const [start, end] = part.split("-").map(Number)
250
+ parsedPassage.chapter = start
251
+ parsedPassage.verses = [
252
+ `${this.chapterVerses[book][start][0]}-${this.chapterVerses[book][start].slice(-1)[0]}`,
253
+ ]
254
+ parsedPassage.to = {
255
+ book,
256
+ chapter: end,
257
+ verses: [`${this.chapterVerses[book][end][0]}-${this.chapterVerses[book][end].slice(-1)[0]}`],
258
+ }
259
+ parsedPassage.type = "chapter_range"
260
+ } else if (part.includes("-")) {
261
+ // Verse range in current chapter (e.g., "8-9" after "40:3-5")
262
+ parsedPassage.verses.push(part)
263
+ parsedPassage.type = "chapter_verse_range"
264
+ } else {
265
+ // Single number (chapter or verse) for multi-chapter books
266
+ if (partIndex === 0 && !parsedPassage.chapter) {
267
+ parsedPassage.chapter = Number(part)
268
+ parsedPassage.type = "single_chapter"
301
269
  } else {
302
- if (chapterPart) {
303
- if (partIndex === parts.length - 1 && parsedPassage.to) {
304
- // Add additional verses to the last chapter in multi-chapter range
305
- if (!parsedPassage.to.verses) parsedPassage.to.verses = []
306
- parsedPassage.to.verses.push(Number(versePart))
307
- } else {
308
- parsedPassage.chapter = Number(chapterPart)
309
- parsedPassage.verses.push(Number(versePart))
310
- }
311
- }
270
+ parsedPassage.verses.push(Number(part))
271
+ parsedPassage.type = "comma_separated_verses"
312
272
  }
313
273
  }
314
-
315
- parsedPassage.passages = this.populate(parsedPassage)
316
- parsedPassage.scripture = this.scripturize(parsedPassage)
317
274
  })
318
275
 
319
- // Handle range merging in `to` object
320
- if (parsedPassage.to && Array.isArray(parsedPassage.to.verses)) {
321
- parsedPassage.to.verses = this.mergeRanges(parsedPassage.to.verses)
322
- }
323
-
276
+ // Populate passages and scripture after processing all parts
277
+ parsedPassage.passages = this.populate(parsedPassage)
278
+ parsedPassage.scripture = this.scripturize(parsedPassage)
324
279
  parsedPassage.valid = this._isValid(parsedPassage, passage.reference)
325
280
 
281
+ // Handle multi-chapter range if applicable
282
+ if (
283
+ parsedPassage.type === "multi_chapter_verse_range" &&
284
+ parts.some((p) => p.includes(":") && p.split(":")[0] !== String(parsedPassage.chapter))
285
+ ) {
286
+ const lastPart = parts[parts.length - 1]
287
+ const [endChapter, endVerse] = lastPart.split(":")
288
+ parsedPassage.to = {
289
+ book: book,
290
+ chapter: Number(endChapter),
291
+ verses: endVerse.includes("-") ? [endVerse] : [Number(endVerse)],
292
+ }
293
+ } else {
294
+ delete parsedPassage.to // Remove erroneous 'to' property
295
+ }
296
+
326
297
  return parsedPassage
327
298
  })
328
299
 
329
300
  this.versification()
330
- return this // Return this instance
301
+ return this
331
302
  }
332
303
  /**
333
304
  * Generates an array of numbers representing a range from start to end, inclusive.
@@ -405,14 +376,6 @@ class CodexParser {
405
376
  })
406
377
  }
407
378
 
408
- /**
409
- * Populate a Set of passages from entities.start.v to entities.end.v,
410
- * and then add any additional verses from the verses array.
411
- *
412
- * @param {Object} entities - Entities object from the bible-passage-reference-parser
413
- * @param {Array} verses - Array of verse numbers to add to the set of passages
414
- * @return {Array} Array of passage objects
415
- */
416
379
  /**
417
380
  * Populate all verses from a parsed passage, including all verses in ranges or chapters.
418
381
  *
@@ -421,26 +384,40 @@ class CodexParser {
421
384
  */
422
385
  populate(parsedPassage) {
423
386
  const passages = []
424
-
425
387
  const { book, chapter, verses, type, to } = parsedPassage
426
388
  const version = parsedPassage.version ? parsedPassage.version.abbreviation : "eng"
427
389
  this._setVersion(book, chapter, version) // Set version data if needed
428
390
 
391
+ const singleChapterBook = this.singleChapterBook.find((b) => Object.keys(b)[0] === book)
392
+
429
393
  if (type === "single_chapter") {
430
- // Handle entire chapter references
431
- if (this.chapterVerses[book] && this.chapterVerses[book][chapter]) {
394
+ // Handle entire chapter references (e.g., "Isaiah 40" or "2 John 1")
395
+ if (singleChapterBook) {
396
+ // Single-chapter book: populate all verses from singleChapterBook
397
+ const verseCount = singleChapterBook[book][1].length
398
+ for (let verse = 1; verse <= verseCount; verse++) {
399
+ passages.push({ book, chapter: 1, verse })
400
+ }
401
+ } else if (this.chapterVerses[book] && this.chapterVerses[book][chapter]) {
402
+ // Multi-chapter book: populate all verses in the chapter
432
403
  this.chapterVerses[book][chapter].forEach((verse) => {
433
404
  passages.push({ book, chapter: Number(chapter), verse: Number(verse) })
434
405
  })
435
406
  }
436
- } else if (type === "comma_separated_verses") {
437
- // Handle explicitly mentioned verses (e.g., 3:1,3,6)
438
-
407
+ } else if (type === "comma_separated_verses" || type === "chapter_verse_range") {
408
+ // Handle comma-separated verses or single-chapter verse ranges (e.g., "Isaiah 40:3-5,8-9" or "2 John 1:1-3")
439
409
  verses.forEach((verse) => {
440
- passages.push({ book, chapter: Number(chapter), verse: Number(verse) })
410
+ if (typeof verse === "string" && verse.includes("-")) {
411
+ const [start, end] = verse.split("-").map(Number)
412
+ for (let i = start; i <= end; i++) {
413
+ passages.push({ book, chapter: Number(chapter), verse: i })
414
+ }
415
+ } else {
416
+ passages.push({ book, chapter: Number(chapter), verse: Number(verse) })
417
+ }
441
418
  })
442
419
  } else if (type === "chapter_range") {
443
- // Handle ranges of chapters (e.g., 3-5)
420
+ // Handle ranges of chapters (e.g., "Isaiah 3-5")
444
421
  for (let currentChapter = chapter; currentChapter <= to.chapter; currentChapter++) {
445
422
  if (this.chapterVerses[book] && this.chapterVerses[book][currentChapter]) {
446
423
  this.chapterVerses[book][currentChapter].forEach((verse) => {
@@ -449,18 +426,17 @@ class CodexParser {
449
426
  }
450
427
  }
451
428
  } else if (type === "multi_chapter_verse_range") {
452
- // Handle multi-chapter verse ranges (e.g., 3:1-5:6)
429
+ // Handle multi-chapter verse ranges (e.g., "Isaiah 3:1-5:6")
453
430
  const startChapter = chapter
454
- const startVerse = verses[0]
431
+ const startVerse = verses[0].includes("-") ? Number(verses[0].split("-")[0]) : Number(verses[0])
455
432
  const endChapter = to.chapter
456
- const endVerse = to.verses[to.verses.length - 1]
433
+ const endVerse = to.verses[0].includes("-") ? Number(to.verses[0].split("-")[1]) : Number(to.verses[0])
457
434
 
458
435
  for (let currentChapter = startChapter; currentChapter <= endChapter; currentChapter++) {
459
436
  const chapterVerses = this.chapterVerses[book][currentChapter]
460
437
  if (!chapterVerses) continue
461
438
 
462
- // Determine start and end verses for each chapter
463
- const chapterStartVerse = currentChapter === startChapter ? startVerse : 1
439
+ const chapterStartVerse = currentChapter === startChapter ? startVerse : chapterVerses[0]
464
440
  const chapterEndVerse =
465
441
  currentChapter === endChapter ? endVerse : chapterVerses[chapterVerses.length - 1]
466
442
 
@@ -468,22 +444,13 @@ class CodexParser {
468
444
  passages.push({ book, chapter: currentChapter, verse })
469
445
  }
470
446
  }
471
- } else if (type === "chapter_verse" || type === "chapter_verse_range") {
472
- // Handle single chapter:verse or chapter:verse ranges (e.g., 3:1 or 3:1-5)
473
- if (this.chapterVerses[book] && this.chapterVerses[book][chapter]) {
474
- verses.forEach((verse) => {
475
- if (typeof verse === "string" && verse.includes("-")) {
476
- const [start, end] = verse.split("-").map(Number)
477
- for (let i = start; i <= end; i++) {
478
- passages.push({ book, chapter: Number(chapter), verse: i })
479
- }
480
- } else {
481
- passages.push({ book, chapter: Number(chapter), verse: Number(verse) })
482
- }
483
- })
484
- }
447
+ } else if (type === "chapter_verse") {
448
+ // Handle single chapter:verse references (e.g., "2 John 1:1")
449
+ verses.forEach((verse) => {
450
+ passages.push({ book, chapter: Number(chapter), verse: Number(verse) })
451
+ })
485
452
  } else if (type === "single_chapter_book_verse_range") {
486
- // Handle ranges in single-chapter books (e.g., Jude 5-7)
453
+ // Handle ranges in single-chapter books (e.g., "Jude 5-7")
487
454
  const [startVerse, endVerse] = verses[0].split("-").map(Number)
488
455
  for (let i = startVerse; i <= endVerse; i++) {
489
456
  passages.push({ book, chapter: 1, verse: i })
@@ -786,31 +753,35 @@ class CodexParser {
786
753
  return orderedToc
787
754
  }
788
755
 
756
+ /**
757
+ * Validates a parsed passage to ensure the chapter and verses exist.
758
+ *
759
+ * @param {Object} passage - The parsed passage object to validate.
760
+ * @param {string} reference - The original reference string for error messaging.
761
+ * @return {boolean|Object} True if valid, or an error object if invalid.
762
+ */
789
763
  _isValid(passage, reference) {
790
- const singleChapterBook = this.singleChapterBook.find((bible) => bible[passage.book])
791
- if (!passage.verses) {
792
- return {
793
- error: true,
794
- code: 101,
795
- message: {
796
- chapter_exists: false,
797
- content: "Possible invalid chapter: " + reference,
798
- },
799
- }
800
- }
801
- if (!singleChapterBook) {
802
- if (!this.chapterVerses[passage.book][passage.chapter]) {
764
+ const singleChapterBook = this.singleChapterBook.find((b) => Object.keys(b)[0] === passage.book)
765
+
766
+ // Check if verses exist at all
767
+ if (!passage.verses || passage.verses.length === 0) {
768
+ if (passage.type !== "single_chapter") {
803
769
  return {
804
770
  error: true,
805
- code: 102,
771
+ code: 101,
806
772
  message: {
807
773
  chapter_exists: false,
808
- content: `Chapter ${passage.chapter} does not exist in ${passage.book}`,
774
+ content: "Possible invalid chapter: " + reference,
809
775
  },
810
776
  }
811
777
  }
812
- } else {
813
- if (!singleChapterBook[passage.book][passage.chapter]) {
778
+ }
779
+
780
+ // Handle single-chapter books
781
+ if (singleChapterBook) {
782
+ const verseCount = singleChapterBook[passage.book][1].length
783
+
784
+ if (passage.chapter !== 1) {
814
785
  return {
815
786
  error: true,
816
787
  code: 103,
@@ -820,19 +791,78 @@ class CodexParser {
820
791
  },
821
792
  }
822
793
  }
794
+
795
+ if (passage.type === "single_chapter") {
796
+ // For "2 John 1", validate the full range
797
+ const [range] = passage.verses // e.g., "1-13"
798
+ if (range) {
799
+ const [start, end] = range.split("-").map(Number)
800
+ if (start < 1 || end > verseCount) {
801
+ return {
802
+ error: true,
803
+ code: 104,
804
+ message: {
805
+ verse_exists: false,
806
+ content: `Verse range ${start}-${end} exceeds available verses (1-${verseCount}) in ${passage.book} 1`,
807
+ },
808
+ }
809
+ }
810
+ }
811
+ return true // If no specific verses or range matches, it’s valid
812
+ }
813
+
814
+ // For specific verses in single-chapter books (e.g., "2 John 1:1-3")
815
+ for (let i = 0; i < passage.verses.length; i++) {
816
+ const verseRange = String(passage.verses[i])
817
+ let versesToCheck = verseRange.includes("-") ? verseRange.split("-").map(Number) : [Number(verseRange)]
818
+
819
+ if (versesToCheck.length === 2) {
820
+ const [start, end] = versesToCheck
821
+ versesToCheck = Array.from({ length: end - start + 1 }, (_, idx) => start + idx)
822
+ }
823
+
824
+ for (const verse of versesToCheck) {
825
+ if (verse < 1 || verse > verseCount) {
826
+ return {
827
+ error: true,
828
+ code: 104,
829
+ message: {
830
+ verse_exists: false,
831
+ content: `Verse number ${verse} does not exist in ${passage.book} 1`,
832
+ },
833
+ }
834
+ }
835
+ }
836
+ }
837
+ return true
838
+ }
839
+
840
+ // Handle multi-chapter books
841
+ if (!this.chapterVerses[passage.book] || !this.chapterVerses[passage.book][passage.chapter]) {
842
+ return {
843
+ error: true,
844
+ code: 102,
845
+ message: {
846
+ chapter_exists: false,
847
+ content: `Chapter ${passage.chapter} does not exist in ${passage.book}`,
848
+ },
849
+ }
850
+ }
851
+
852
+ if (passage.type === "single_chapter") {
853
+ return true // For multi-chapter books, whole chapter is valid if it exists
823
854
  }
855
+
824
856
  for (let i = 0; i < passage.verses.length; i++) {
825
857
  const passageVerses = String(passage.verses[i])
826
- let verses = passageVerses.split("-").map(Number)
858
+ let verses = passageVerses.includes("-") ? passageVerses.split("-").map(Number) : [Number(passageVerses)]
827
859
 
828
860
  if (verses.length === 2) {
829
861
  // Expand the range if there are two numbers
830
862
  verses = Array.from({ length: verses[1] - verses[0] + 1 }, (_, index) => verses[0] + index)
831
863
  }
832
864
 
833
- // The verses array now contains all values between the range or remains as a single value
834
865
  for (const verse of verses) {
835
- // Check if the verse exists in the chapterVerses structure
836
866
  const isValidVerse =
837
867
  this.chapterVerses[passage.book] &&
838
868
  this.chapterVerses[passage.book][passage.chapter] &&
@@ -850,6 +880,7 @@ class CodexParser {
850
880
  }
851
881
  }
852
882
  }
883
+
853
884
  return true
854
885
  }
855
886
  _handleVersion(version, testament) {