codexparser 0.1.42 → 0.1.43

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 +110 -79
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexparser",
3
- "version": "0.1.42",
3
+ "version": "0.1.43",
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": {
@@ -147,16 +147,19 @@ class CodexParser {
147
147
 
148
148
  references.forEach((ref) => {
149
149
  let type
150
-
151
150
  if (ref.includes(":")) {
152
151
  if (ref.includes("-")) {
153
152
  const [start, end] = ref.split("-")
154
153
  const startParts = start.split(":")
155
154
  const endParts = end.split(":")
155
+
156
+ // Determine type based on the chapter (startParts[0] and endParts[0])
156
157
  type =
158
+ startParts.length > 1 &&
159
+ endParts.length > 1 &&
157
160
  startParts[0].trim() !== endParts[0].trim()
158
- ? "multi_chapter_verse_range"
159
- : "chapter_verse_range"
161
+ ? "multi_chapter_verse_range" // Chapters differ
162
+ : "chapter_verse_range" // Same chapter
160
163
  } else if (ref.includes(",")) {
161
164
  type = "comma_separated_verses"
162
165
  } else {
@@ -242,12 +245,14 @@ class CodexParser {
242
245
  // Checks to see if we are in a multi chapter verse range, if so, include only relevant verses from the this.chapterVerse to
243
246
  // the end of the chapter.
244
247
  if (start.includes(separator) && end.includes(separator)) {
248
+ // TODO: Need to update versification and version here.
249
+ this._setVersion(book, startChapter, passage.version)
245
250
  parsedPassage.verses = this.chapterVerses[book][startChapter].slice(
246
251
  this.chapterVerses[book][startChapter].indexOf(Number(startVerse))
247
252
  )
248
253
  }
249
254
 
250
- // Handle same-chapter ranges (e.g., "27:27-29") and multi-chapter ranges (e.g., "Ex 2:1-3:4")
255
+ // Handle chapter ranges (e.g., "27:27-29") and multi-chapter ranges (e.g., "Ex 2:1-3:4")
251
256
  if (end.includes(separator)) {
252
257
  let [endChapter, endVerse] = end.split(separator)
253
258
  if (Number(endChapter) !== Number(startChapter)) {
@@ -256,11 +261,14 @@ class CodexParser {
256
261
  book: book,
257
262
  chapter: Number(endChapter), // End chapter
258
263
  }
264
+
259
265
  if (endVerse > 1) {
260
266
  parsedPassage.to.verses = this.chapterVerses[book][Number(endChapter)].slice(
261
267
  0,
262
268
  this.chapterVerses[book][Number(endChapter)].indexOf(Number(endVerse)) + 1
263
269
  )
270
+ } else {
271
+ parsedPassage.to.verses = [endVerse]
264
272
  }
265
273
  parsedPassage.type =
266
274
  endChapter !== startChapter ? "multi_chapter_verse_range" : "chapter_verse_range" // Set type to chapter range
@@ -350,18 +358,17 @@ class CodexParser {
350
358
  return range
351
359
  }
352
360
 
353
- _searchVersificationDifferences(passage) {
354
- const { book, chapter, version } = passage
361
+ _searchVersificationDifferences(book, chapter, version) {
362
+ version = version.toLowerCase()
355
363
  if (!this.chapterVerses[book][chapter]) return
356
-
357
364
  // Loop through each key-value pair in the dictionary
358
365
  for (const [key, value] of Object.entries(this.versificationDifferences[book])) {
359
366
  // Check if the key starts with the desired chapter
360
- if (value[version.abbreviation].startsWith(`${chapter}:`)) {
367
+ if (value[version].startsWith(`${chapter}:`)) {
361
368
  // Ensure the version exists in the value object
362
- if (value[version.abbreviation]) {
369
+ if (value[version]) {
363
370
  // Extract the verse number from the value
364
- const verse = value[version.abbreviation].split(":")[1]
371
+ const verse = value[version].split(":")[1]
365
372
  this.chapterVerses[book][chapter].push(Number(verse))
366
373
  }
367
374
  }
@@ -370,11 +377,11 @@ class CodexParser {
370
377
  return this.chapterVerses // Return the array of verses
371
378
  }
372
379
 
373
- _setVersion(passage) {
374
- this.version = passage.version ? passage.version.abbreviation : "eng"
380
+ _setVersion(book, chapter, version) {
381
+ this.version = version ? version : "eng"
375
382
 
376
383
  if (this.version !== "eng") {
377
- this._searchVersificationDifferences(passage)
384
+ this._searchVersificationDifferences(book, chapter, version)
378
385
  }
379
386
  }
380
387
 
@@ -432,8 +439,8 @@ class CodexParser {
432
439
  populate(parsedPassage) {
433
440
  const passages = []
434
441
  const { book, chapter, verses, type, to } = parsedPassage
435
-
436
- this._setVersion(parsedPassage) // Set version data if needed
442
+ const version = parsedPassage.version ? parsedPassage.version.abbreviation : "eng"
443
+ this._setVersion(book, chapter, version) // Set version data if needed
437
444
 
438
445
  if (type === "single_chapter") {
439
446
  // Handle entire chapter references
@@ -460,7 +467,6 @@ class CodexParser {
460
467
  }
461
468
  } else if (type === "multi_chapter_verse_range") {
462
469
  // Handle multi-chapter verse ranges (e.g., 3:1-5:6)
463
-
464
470
  const startChapter = chapter
465
471
  const startVerse = verses[0]
466
472
  const endChapter = to.chapter
@@ -648,7 +654,6 @@ class CodexParser {
648
654
  const noDuplicates2 = [...new Set(newPassages)]
649
655
 
650
656
  const parsed = this.parse(noDuplicates2.join(" // ")).getPassages()
651
-
652
657
  return this.join(parsed)
653
658
  }
654
659
 
@@ -660,86 +665,100 @@ class CodexParser {
660
665
  * @return {object} The combined passage object.
661
666
  */
662
667
  join(passages) {
663
- const newObject = { ...passages[0] } // Start with the first passage
664
-
665
- const chapters = {} // Store verses by chapters
666
- const uniquePassages = new Set() // Track unique passages to prevent duplicates
667
- // Add initial passages to the unique set to avoid duplication
668
- newObject.passages.forEach((p) => {
669
- const passageKey = `${p.book}-${p.chapter}-${p.verse}`
670
- uniquePassages.add(passageKey)
671
- })
668
+ if (!passages || passages.length === 0) {
669
+ throw new Error("No passages provided to join.")
670
+ }
672
671
 
673
- // Iterate through all the passages and group verses by chapter
674
- passages.forEach((passage) => {
675
- if (!chapters[passage.chapter]) {
676
- chapters[passage.chapter] = new Set() // Use Set to avoid duplicates
677
- }
672
+ // Ensure all passages are from the same book
673
+ const uniqueBooks = [...new Set(passages.map((p) => p.book))]
674
+ if (uniqueBooks.length > 1) {
675
+ throw new Error("Passages must be from the same book to join.")
676
+ }
678
677
 
679
- // Add verses to their corresponding chapter
680
- passage.passages.forEach((p) => {
681
- chapters[p.chapter].add(p.verse)
678
+ // Start with the base object
679
+ const combined = {
680
+ ...passages[0],
681
+ verses: [],
682
+ passages: [],
683
+ to: null,
684
+ scripture: {},
685
+ type: null,
686
+ }
682
687
 
683
- // Create a unique key for each passage (book-chapter-verse)
684
- const passageKey = `${p.book}-${p.chapter}-${p.verse}`
688
+ const chapterVerses = {}
689
+ let firstChapter = null
690
+ let lastChapter = null
685
691
 
686
- // Add to the passages array if it hasn't been added yet
687
- if (!uniquePassages.has(passageKey)) {
688
- newObject.passages.push(p) // Add the passage
689
- uniquePassages.add(passageKey) // Mark it as added
692
+ // Collect all verses and passages, grouped by chapter
693
+ passages.forEach((passage) => {
694
+ passage.passages.forEach((p) => {
695
+ if (!chapterVerses[p.chapter]) {
696
+ chapterVerses[p.chapter] = new Set()
690
697
  }
698
+ chapterVerses[p.chapter].add(p.verse)
699
+ combined.passages.push(p) // Add individual passage
691
700
  })
692
- })
693
701
 
694
- // Sort the newObject.passages array by chapter first, then by verse
695
- newObject.passages.sort((a, b) => {
696
- if (a.chapter !== b.chapter) {
697
- return a.chapter - b.chapter // Sort by chapter
702
+ // Track first and last chapters
703
+ const chapters = passage.passages.map((p) => p.chapter)
704
+ if (!firstChapter || Math.min(...chapters) < firstChapter) {
705
+ firstChapter = Math.min(...chapters)
706
+ }
707
+ if (!lastChapter || Math.max(...chapters) > lastChapter) {
708
+ lastChapter = Math.max(...chapters)
698
709
  }
699
- return a.verse - b.verse // Sort by verse within the same chapter
700
710
  })
701
711
 
702
- // Prepare to build the final result
712
+ // Ensure unique and sorted passages
713
+ combined.passages = Array.from(new Set(combined.passages.map(JSON.stringify))).map(JSON.parse)
714
+
715
+ // Process chapter and verse data
703
716
  const chapterStrings = []
704
- let firstChapter = null
705
- let lastChapter = null
717
+ const sortedChapters = Object.keys(chapterVerses)
718
+ .map(Number)
719
+ .sort((a, b) => a - b)
706
720
 
707
- for (const chapter in chapters) {
708
- const verses = Array.from(chapters[chapter]).sort((a, b) => a - b)
709
- const mergedVerses = this.mergeRanges(verses) // Merge adjacent verses into ranges
721
+ sortedChapters.forEach((chapter) => {
722
+ const verses = Array.from(chapterVerses[chapter]).sort((a, b) => a - b)
723
+ const mergedVerses = this.mergeRanges(verses)
710
724
  chapterStrings.push(`${chapter}:${mergedVerses.join(",")}`)
711
-
712
- // Track the first and last chapters for the 'to' key
713
- if (!firstChapter) firstChapter = Number(chapter) // Ensure chapter is a number
714
- lastChapter = Number(chapter) // Always update to the current chapter as a number
715
-
716
- // Update the newObject.verses with the merged ranges for the current chapter
717
- if (Number(chapter) === firstChapter) {
718
- newObject.verses = mergedVerses
725
+ if (chapter === firstChapter) {
726
+ combined.verses = mergedVerses // First chapter's verses
719
727
  }
720
- }
721
-
722
- // Build the final combined object with `to` key for multi-chapter passages
723
- newObject.original = `${newObject.book} ${firstChapter}:${newObject.verses.join(",")}`
728
+ })
724
729
 
730
+ // Handle multi-chapter ranges
725
731
  if (firstChapter !== lastChapter) {
726
- newObject.to = {
727
- book: newObject.book,
732
+ combined.type = "multi_chapter_verse_range"
733
+ combined.to = {
734
+ book: combined.book,
728
735
  chapter: lastChapter,
729
- verses: this.mergeRanges(Array.from(chapters[lastChapter])), // Ensure merged range
736
+ verses: this.mergeRanges(Array.from(chapterVerses[lastChapter])),
737
+ }
738
+ combined.original = `${combined.book} ${firstChapter}:${combined.verses.join(
739
+ ","
740
+ )}-${lastChapter}:${combined.to.verses.join(",")}`
741
+ } else {
742
+ // Single-chapter range or comma-separated
743
+ if (combined.verses.length > 1) {
744
+ combined.type = "chapter_verse_range"
745
+ } else {
746
+ combined.type = "chapter_verse"
730
747
  }
748
+ combined.original = `${combined.book} ${firstChapter}:${combined.verses.join(",")}`
731
749
  }
732
750
 
733
- // Build the scripture string with combined chapters (without spaces after commas)
734
- const chapterString = chapterStrings.join(",") // No space after comma
735
- newObject.scripture = {
736
- passage: `${newObject.book} ${chapterString}`,
751
+ // Build the scripture property
752
+ const chapterString = chapterStrings.join(",")
753
+ combined.scripture = {
754
+ passage: `${combined.book} ${chapterString}`,
737
755
  cv: chapterString,
738
- hash: `${newObject.book.toLowerCase()}_${chapterString.replace(/:/g, ".").replace(/,/g, ".")}`,
756
+ hash: `${combined.book.toLowerCase()}_${chapterString.replace(/:/g, ".").replace(/,/g, ".")}`,
739
757
  }
740
758
 
741
- return newObject
759
+ return combined
742
760
  }
761
+
743
762
  mergeRanges(verses) {
744
763
  const sortedVerses = [...new Set(verses)].sort((a, b) => a - b)
745
764
  const merged = []
@@ -750,11 +769,9 @@ class CodexParser {
750
769
  if (sortedVerses[i] === end + 1) {
751
770
  end = sortedVerses[i]
752
771
  } else {
753
- // Push the current range if it's more than 2 consecutive numbers, otherwise separate by commas
772
+ // Push range or single verse
754
773
  if (start === end) {
755
774
  merged.push(`${start}`)
756
- } else if (end === start + 1) {
757
- merged.push(`${start},${end}`)
758
775
  } else {
759
776
  merged.push(`${start}-${end}`)
760
777
  }
@@ -763,11 +780,9 @@ class CodexParser {
763
780
  }
764
781
  }
765
782
 
766
- // Push the final range or pair
783
+ // Push the final range or single verse
767
784
  if (start === end) {
768
785
  merged.push(`${start}`)
769
- } else if (end === start + 1) {
770
- merged.push(`${start},${end}`)
771
786
  } else {
772
787
  merged.push(`${start}-${end}`)
773
788
  }
@@ -849,6 +864,22 @@ class CodexParser {
849
864
  }
850
865
  }
851
866
  }
867
+ for (let i = 0; i < passage.verses.length; i++) {
868
+ let verse = passage.verses[i]
869
+ const searchForVerse = this.chapterVerses[passage.book][passage.chapter].find(
870
+ (v) => Number(v) === Number(verse)
871
+ )
872
+ if (!searchForVerse) {
873
+ return {
874
+ error: true,
875
+ code: 104,
876
+ message: {
877
+ verse_exists: false,
878
+ content: `Verse number ${verse} does not exist in ${passage.book} ${passage.chapter}`,
879
+ },
880
+ }
881
+ }
882
+ }
852
883
  return true
853
884
  }
854
885
  _handleVersion(version, testament) {