codexparser 0.1.29 → 0.1.31

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 +101 -71
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexparser",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
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": {
@@ -476,7 +476,20 @@ class CodexParser {
476
476
  * @return {array} The passages stored in the object.
477
477
  */
478
478
  getPassages() {
479
- return this.passages
479
+ // Return the array of passages and add a custom first() method to it
480
+ const passagesArray = [...this.passages] // Clone the array to avoid mutation
481
+
482
+ // Add first() method directly to the array
483
+ passagesArray.first = function () {
484
+ return this.length > 0 ? this[0] : null
485
+ }
486
+
487
+ return passagesArray
488
+ }
489
+
490
+ // New first() method that can be chained after getPassages()
491
+ first() {
492
+ return this.passages.length > 0 ? this.passages[0] : null
480
493
  }
481
494
 
482
495
  /**
@@ -572,96 +585,113 @@ class CodexParser {
572
585
  * @return {object} The combined passage object.
573
586
  */
574
587
  join(passages) {
575
- const newObject = { ...passages[0] }
576
-
577
- for (let i = 1; i < passages.length; i++) {
578
- // Combine passages, but check for duplicates
579
- passages[i].passages.forEach((passage) => {
580
- // Add passage only if it's not already in newObject.passages
581
- const isDuplicate = newObject.passages.some(
582
- (p) => p.book === passage.book && p.chapter === passage.chapter && p.verse === passage.verse
583
- )
584
- if (!isDuplicate) {
585
- newObject.passages = newObject.passages.concat(passage)
586
- }
587
- })
588
- const verses = passages[i].verses
589
- for (let j = 0; j < verses.length; j++) {
590
- const verse = verses[j]
591
- newObject.verses = newObject.verses.concat(verse)
592
- }
593
- }
588
+ const newObject = { ...passages[0] } // Start with the first passage
594
589
 
595
- // Remove lone numbers that are part of a range
596
- newObject.verses = newObject.verses.filter((v, i, arr) => {
597
- if (!v.includes("-")) {
598
- const num = parseInt(v, 10)
599
- return !arr.some((item) => {
600
- if (item.includes("-")) {
601
- const [start, end] = item.split("-").map(Number)
602
- return num >= start && num <= end
603
- }
604
- return false
605
- })
606
- }
607
- return true // Keep ranges
590
+ const chapters = {} // Store verses by chapters
591
+ const uniquePassages = new Set() // Track unique passages to prevent duplicates
592
+
593
+ // Add initial passages to the unique set to avoid duplication
594
+ newObject.passages.forEach((p) => {
595
+ const passageKey = `${p.book}-${p.chapter}-${p.verse}`
596
+ uniquePassages.add(passageKey)
608
597
  })
609
598
 
610
- // Convert to a Set to remove duplicates
611
- newObject.verses = [...new Set(newObject.verses)]
599
+ // Iterate through all the passages and group verses by chapter
600
+ passages.forEach((passage) => {
601
+ if (!chapters[passage.chapter]) {
602
+ chapters[passage.chapter] = new Set() // Use Set to avoid duplicates
603
+ }
604
+
605
+ // Add verses to their corresponding chapter
606
+ passage.passages.forEach((p) => {
607
+ chapters[p.chapter].add(p.verse)
612
608
 
613
- // Helper function to merge overlapping or adjacent ranges
614
- const mergeRanges = (verses) => {
615
- const rangeObjects = verses.map((v) => {
616
- if (v.includes("-")) {
617
- const [start, end] = v.split("-").map(Number)
618
- return { start, end }
619
- } else {
620
- const num = Number(v)
621
- return { start: num, end: num }
609
+ // Create a unique key for each passage (book-chapter-verse)
610
+ const passageKey = `${p.book}-${p.chapter}-${p.verse}`
611
+
612
+ // Add to the passages array if it hasn't been added yet
613
+ if (!uniquePassages.has(passageKey)) {
614
+ newObject.passages.push(p) // Add the passage
615
+ uniquePassages.add(passageKey) // Mark it as added
622
616
  }
623
617
  })
618
+ })
624
619
 
625
- // Sort the ranges by starting number
626
- rangeObjects.sort((a, b) => a.start - b.start)
620
+ // Prepare to build the final result
621
+ const chapterStrings = []
622
+ let firstChapter = null
623
+ let lastChapter = null
627
624
 
628
- const merged = []
629
- let currentRange = rangeObjects[0]
625
+ for (const chapter in chapters) {
626
+ const verses = Array.from(chapters[chapter]).sort((a, b) => a - b)
627
+ const mergedVerses = this.mergeRanges(verses) // Merge adjacent verses into ranges
628
+ chapterStrings.push(`${chapter}:${mergedVerses.join(",")}`)
630
629
 
631
- for (let i = 1; i < rangeObjects.length; i++) {
632
- const nextRange = rangeObjects[i]
630
+ // Track the first and last chapters for the 'to' key
631
+ if (!firstChapter) firstChapter = Number(chapter) // Ensure chapter is a number
632
+ lastChapter = Number(chapter) // Always update to the current chapter as a number
633
633
 
634
- if (currentRange.end >= nextRange.start - 1) {
635
- // Overlapping or adjacent, merge the ranges
636
- currentRange.end = Math.max(currentRange.end, nextRange.end)
637
- } else {
638
- // No overlap, push the current range and start a new one
639
- merged.push(currentRange)
640
- currentRange = nextRange
641
- }
634
+ // Update the newObject.verses with the merged ranges for the current chapter
635
+ if (Number(chapter) === firstChapter) {
636
+ newObject.verses = mergedVerses
642
637
  }
638
+ }
643
639
 
644
- // Push the last range
645
- merged.push(currentRange)
640
+ // Build the final combined object with `to` key for multi-chapter passages
641
+ newObject.original = `${newObject.book} ${firstChapter}:${newObject.verses.join(",")}`
646
642
 
647
- // Convert merged ranges back to strings
648
- return merged.map(({ start, end }) => (start === end ? `${start}` : `${start}-${end}`))
643
+ if (firstChapter !== lastChapter) {
644
+ newObject.to = {
645
+ book: newObject.book,
646
+ chapter: lastChapter,
647
+ verses: this.mergeRanges(Array.from(chapters[lastChapter])), // Ensure merged range
648
+ }
649
649
  }
650
650
 
651
- // Merge and sort the ranges
652
- newObject.verses = mergeRanges(newObject.verses)
653
-
654
- // Build the original and scripture properties
655
- newObject.original = newObject.book + " " + newObject.chapter + ":" + newObject.verses.join(",")
651
+ // Build the scripture string with combined chapters (without spaces after commas)
652
+ const chapterString = chapterStrings.join(",") // No space after comma
656
653
  newObject.scripture = {
657
- passage: newObject.book + " " + newObject.chapter + ":" + newObject.verses.join(","),
658
- cv: newObject.chapter + ":" + newObject.verses.join(","),
659
- hash: newObject.book + "_" + newObject.chapter + "." + newObject.verses.join("."),
654
+ passage: `${newObject.book} ${chapterString}`,
655
+ cv: chapterString,
656
+ hash: `${newObject.book.toLowerCase()}_${chapterString.replace(/:/g, ".").replace(/,/g, ".")}`,
660
657
  }
661
- newObject.scripture.hash = newObject.scripture.hash.toLowerCase()
662
658
 
663
659
  return newObject
664
660
  }
661
+ mergeRanges(verses) {
662
+ const sortedVerses = [...new Set(verses)].sort((a, b) => a - b)
663
+ const merged = []
664
+ let start = sortedVerses[0]
665
+ let end = sortedVerses[0]
666
+
667
+ for (let i = 1; i < sortedVerses.length; i++) {
668
+ if (sortedVerses[i] === end + 1) {
669
+ end = sortedVerses[i]
670
+ } else {
671
+ // Push the current range if it's more than 2 consecutive numbers, otherwise separate by commas
672
+ if (start === end) {
673
+ merged.push(`${start}`)
674
+ } else if (end === start + 1) {
675
+ merged.push(`${start},${end}`)
676
+ } else {
677
+ merged.push(`${start}-${end}`)
678
+ }
679
+ start = sortedVerses[i]
680
+ end = sortedVerses[i]
681
+ }
682
+ }
683
+
684
+ // Push the final range or pair
685
+ if (start === end) {
686
+ merged.push(`${start}`)
687
+ } else if (end === start + 1) {
688
+ merged.push(`${start},${end}`)
689
+ } else {
690
+ merged.push(`${start}-${end}`)
691
+ }
692
+
693
+ return merged
694
+ }
665
695
 
666
696
  _isValid(passage, reference) {
667
697
  const singleChapterBook = this.singleChapterBook.find((bible) => bible[passage.book])