codexparser 0.1.53 → 0.1.55

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 +155 -56
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexparser",
3
- "version": "0.1.53",
3
+ "version": "0.1.55",
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": {
@@ -191,6 +191,7 @@ class CodexParser {
191
191
  this.passages = this.found.map((passage) => {
192
192
  const book = this.bookify(passage.book)
193
193
  const testament = this.bible.old.includes(book) ? "old" : "new"
194
+ const singleChapterBook = this.singleChapterBook.find((b) => Object.keys(b)[0] === book)
194
195
  const parsedPassage = {
195
196
  original: passage.book + " " + passage.reference,
196
197
  book: book,
@@ -202,42 +203,81 @@ class CodexParser {
202
203
  version: this._handleVersion(passage.version, testament),
203
204
  }
204
205
 
205
- // Split reference into parts (e.g., "40:3-5,8-9" -> ["40:3-5", "8-9"])
206
+ // Split reference into parts (e.g., "Matthew 1", "2 John 2", "Matthew 1:1-3,5")
206
207
  const parts = passage.reference.split(",")
207
208
 
208
209
  parts.forEach((part, partIndex) => {
209
210
  part = part.trim()
210
211
 
211
212
  if (part.includes(":")) {
212
- // Explicit chapter:verse (e.g., "40:3-5")
213
+ // Explicit chapter:verse (e.g., "1:1-3")
213
214
  const [chapterPart, versePart] = part.split(":")
214
215
  if (partIndex === 0) {
215
216
  parsedPassage.chapter = Number(chapterPart) // Set chapter only on first part
216
217
  }
217
218
 
218
219
  if (versePart.includes("-")) {
219
- // Verse range (e.g., "3-5")
220
- parsedPassage.verses.push(versePart)
220
+ parsedPassage.verses.push(versePart) // Add range (e.g., "1-3")
221
221
  } else {
222
- parsedPassage.verses.push(Number(versePart))
222
+ parsedPassage.verses.push(Number(versePart)) // Add single verse
223
223
  }
224
- } else if (part.includes("-")) {
225
- // Verse range without chapter (e.g., "8-9")
226
- if (parsedPassage.chapter === null) {
227
- // Assume chapter 1 for single-chapter books if no chapter yet
228
- const singleChapterBook = this.singleChapterBook.find((b) => b[book])
229
- parsedPassage.chapter = singleChapterBook ? 1 : Number(part.split("-")[0])
230
- parsedPassage.verses.push(part)
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 2-5" → "2 John 1:2-5"
235
+ parsedPassage.chapter = 1
236
+ parsedPassage.verses.push(part) // e.g., "2-5"
237
+ parsedPassage.type = "chapter_verse_range"
231
238
  } else {
232
- parsedPassage.verses.push(part) // Add as verse range in current chapter
239
+ // "2 John 2" "2 John 1:2"
240
+ const num = Number(part)
241
+ if (num > 1 || (num === 1 && parts.length > 1)) {
242
+ parsedPassage.chapter = 1
243
+ parsedPassage.verses.push(num) // Treat as verse number
244
+ parsedPassage.type = "chapter_verse"
245
+ }
233
246
  }
247
+ } else if (part.includes("-") && !parsedPassage.chapter) {
248
+ // Range without chapter for multi-chapter books (e.g., "Matthew 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"
234
264
  } else {
235
- // Single verse without chapter (e.g., "8")
236
- if (parsedPassage.chapter === null) {
237
- const singleChapterBook = this.singleChapterBook.find((b) => b[book])
238
- parsedPassage.chapter = singleChapterBook ? 1 : Number(part)
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"
269
+ // For multi-chapter books, set verses to full chapter range
270
+ if (
271
+ !singleChapterBook &&
272
+ this.chapterVerses[book] &&
273
+ this.chapterVerses[book][parsedPassage.chapter]
274
+ ) {
275
+ const chapterVerses = this.chapterVerses[book][parsedPassage.chapter]
276
+ parsedPassage.verses = [`${chapterVerses[0]}-${chapterVerses[chapterVerses.length - 1]}`]
277
+ }
239
278
  } else {
240
279
  parsedPassage.verses.push(Number(part))
280
+ parsedPassage.type = "comma_separated_verses"
241
281
  }
242
282
  }
243
283
  })
@@ -260,7 +300,7 @@ class CodexParser {
260
300
  verses: endVerse.includes("-") ? [endVerse] : [Number(endVerse)],
261
301
  }
262
302
  } else {
263
- delete parsedPassage.to // Ensure no erroneous 'to' property
303
+ delete parsedPassage.to // Remove erroneous 'to' property
264
304
  }
265
305
 
266
306
  return parsedPassage
@@ -345,20 +385,6 @@ class CodexParser {
345
385
  })
346
386
  }
347
387
 
348
- /**
349
- * Populate a Set of passages from entities.start.v to entities.end.v,
350
- * and then add any additional verses from the verses array.
351
- *
352
- * @param {Object} entities - Entities object from the bible-passage-reference-parser
353
- * @param {Array} verses - Array of verse numbers to add to the set of passages
354
- * @return {Array} Array of passage objects
355
- */
356
- /**
357
- * Populate all verses from a parsed passage, including all verses in ranges or chapters.
358
- *
359
- * @param {Object} parsedPassage - The parsed passage object containing book, chapter, and verses information.
360
- * @return {Array} An array of passage objects with individual verses.
361
- */
362
388
  /**
363
389
  * Populate all verses from a parsed passage, including all verses in ranges or chapters.
364
390
  *
@@ -371,15 +397,24 @@ class CodexParser {
371
397
  const version = parsedPassage.version ? parsedPassage.version.abbreviation : "eng"
372
398
  this._setVersion(book, chapter, version) // Set version data if needed
373
399
 
400
+ const singleChapterBook = this.singleChapterBook.find((b) => Object.keys(b)[0] === book)
401
+
374
402
  if (type === "single_chapter") {
375
- // Handle entire chapter references (e.g., "Isaiah 40")
376
- if (this.chapterVerses[book] && this.chapterVerses[book][chapter]) {
403
+ // Handle entire chapter references (e.g., "Isaiah 40" or "2 John 1")
404
+ if (singleChapterBook) {
405
+ // Single-chapter book: populate all verses from singleChapterBook
406
+ const verseCount = singleChapterBook[book][1].length
407
+ for (let verse = 1; verse <= verseCount; verse++) {
408
+ passages.push({ book, chapter: 1, verse })
409
+ }
410
+ } else if (this.chapterVerses[book] && this.chapterVerses[book][chapter]) {
411
+ // Multi-chapter book: populate all verses in the chapter
377
412
  this.chapterVerses[book][chapter].forEach((verse) => {
378
413
  passages.push({ book, chapter: Number(chapter), verse: Number(verse) })
379
414
  })
380
415
  }
381
416
  } else if (type === "comma_separated_verses" || type === "chapter_verse_range") {
382
- // Handle comma-separated verses or single-chapter verse ranges (e.g., "Isaiah 40:3-5,8-9" or "Isaiah 40:3-5")
417
+ // Handle comma-separated verses or single-chapter verse ranges (e.g., "Isaiah 40:3-5,8-9" or "2 John 1:1-3")
383
418
  verses.forEach((verse) => {
384
419
  if (typeof verse === "string" && verse.includes("-")) {
385
420
  const [start, end] = verse.split("-").map(Number)
@@ -419,7 +454,7 @@ class CodexParser {
419
454
  }
420
455
  }
421
456
  } else if (type === "chapter_verse") {
422
- // Handle single chapter:verse references (e.g., "Isaiah 40:3")
457
+ // Handle single chapter:verse references (e.g., "2 John 1:1")
423
458
  verses.forEach((verse) => {
424
459
  passages.push({ book, chapter: Number(chapter), verse: Number(verse) })
425
460
  })
@@ -727,31 +762,35 @@ class CodexParser {
727
762
  return orderedToc
728
763
  }
729
764
 
765
+ /**
766
+ * Validates a parsed passage to ensure the chapter and verses exist.
767
+ *
768
+ * @param {Object} passage - The parsed passage object to validate.
769
+ * @param {string} reference - The original reference string for error messaging.
770
+ * @return {boolean|Object} True if valid, or an error object if invalid.
771
+ */
730
772
  _isValid(passage, reference) {
731
- const singleChapterBook = this.singleChapterBook.find((bible) => bible[passage.book])
732
- if (!passage.verses) {
733
- return {
734
- error: true,
735
- code: 101,
736
- message: {
737
- chapter_exists: false,
738
- content: "Possible invalid chapter: " + reference,
739
- },
740
- }
741
- }
742
- if (!singleChapterBook) {
743
- if (!this.chapterVerses[passage.book][passage.chapter]) {
773
+ const singleChapterBook = this.singleChapterBook.find((b) => Object.keys(b)[0] === passage.book)
774
+
775
+ // Check if verses exist at all
776
+ if (!passage.verses || passage.verses.length === 0) {
777
+ if (passage.type !== "single_chapter") {
744
778
  return {
745
779
  error: true,
746
- code: 102,
780
+ code: 101,
747
781
  message: {
748
782
  chapter_exists: false,
749
- content: `Chapter ${passage.chapter} does not exist in ${passage.book}`,
783
+ content: "Possible invalid chapter: " + reference,
750
784
  },
751
785
  }
752
786
  }
753
- } else {
754
- if (!singleChapterBook[passage.book][passage.chapter]) {
787
+ }
788
+
789
+ // Handle single-chapter books
790
+ if (singleChapterBook) {
791
+ const verseCount = singleChapterBook[passage.book][1].length
792
+
793
+ if (passage.chapter !== 1) {
755
794
  return {
756
795
  error: true,
757
796
  code: 103,
@@ -761,19 +800,78 @@ class CodexParser {
761
800
  },
762
801
  }
763
802
  }
803
+
804
+ if (passage.type === "single_chapter") {
805
+ // For "2 John 1", validate the full range
806
+ const [range] = passage.verses // e.g., "1-13"
807
+ if (range) {
808
+ const [start, end] = range.split("-").map(Number)
809
+ if (start < 1 || end > verseCount) {
810
+ return {
811
+ error: true,
812
+ code: 104,
813
+ message: {
814
+ verse_exists: false,
815
+ content: `Verse range ${start}-${end} exceeds available verses (1-${verseCount}) in ${passage.book} 1`,
816
+ },
817
+ }
818
+ }
819
+ }
820
+ return true // If no specific verses or range matches, it’s valid
821
+ }
822
+
823
+ // For specific verses in single-chapter books (e.g., "2 John 1:1-3")
824
+ for (let i = 0; i < passage.verses.length; i++) {
825
+ const verseRange = String(passage.verses[i])
826
+ let versesToCheck = verseRange.includes("-") ? verseRange.split("-").map(Number) : [Number(verseRange)]
827
+
828
+ if (versesToCheck.length === 2) {
829
+ const [start, end] = versesToCheck
830
+ versesToCheck = Array.from({ length: end - start + 1 }, (_, idx) => start + idx)
831
+ }
832
+
833
+ for (const verse of versesToCheck) {
834
+ if (verse < 1 || verse > verseCount) {
835
+ return {
836
+ error: true,
837
+ code: 104,
838
+ message: {
839
+ verse_exists: false,
840
+ content: `Verse number ${verse} does not exist in ${passage.book} 1`,
841
+ },
842
+ }
843
+ }
844
+ }
845
+ }
846
+ return true
847
+ }
848
+
849
+ // Handle multi-chapter books
850
+ if (!this.chapterVerses[passage.book] || !this.chapterVerses[passage.book][passage.chapter]) {
851
+ return {
852
+ error: true,
853
+ code: 102,
854
+ message: {
855
+ chapter_exists: false,
856
+ content: `Chapter ${passage.chapter} does not exist in ${passage.book}`,
857
+ },
858
+ }
764
859
  }
860
+
861
+ if (passage.type === "single_chapter") {
862
+ return true // For multi-chapter books, whole chapter is valid if it exists
863
+ }
864
+
765
865
  for (let i = 0; i < passage.verses.length; i++) {
766
866
  const passageVerses = String(passage.verses[i])
767
- let verses = passageVerses.split("-").map(Number)
867
+ let verses = passageVerses.includes("-") ? passageVerses.split("-").map(Number) : [Number(passageVerses)]
768
868
 
769
869
  if (verses.length === 2) {
770
870
  // Expand the range if there are two numbers
771
871
  verses = Array.from({ length: verses[1] - verses[0] + 1 }, (_, index) => verses[0] + index)
772
872
  }
773
873
 
774
- // The verses array now contains all values between the range or remains as a single value
775
874
  for (const verse of verses) {
776
- // Check if the verse exists in the chapterVerses structure
777
875
  const isValidVerse =
778
876
  this.chapterVerses[passage.book] &&
779
877
  this.chapterVerses[passage.book][passage.chapter] &&
@@ -791,6 +889,7 @@ class CodexParser {
791
889
  }
792
890
  }
793
891
  }
892
+
794
893
  return true
795
894
  }
796
895
  _handleVersion(version, testament) {